]> git.gir.st - Chiptunes.git/blob - foo.S
cleanup some TODOs, #defines
[Chiptunes.git] / foo.S
1 ; TODO: melody is still not 100% correct!
2 /* REGISTER NAMES */
3 #define acc r16
4 #define i0 r17
5 #define i1 r18
6 #define i2 r19
7 #define i3 r20
8 #define n r21
9 #define s r22
10 #define t r23 //==Ml
11 #define x r24 //==a1==Mh
12 #define _ r25 //==a2
13 #define Xlo r26
14 #define Xhi r27
15 #define one r28
16 ; r29
17 ; r30 Zlo
18 ; r31 Zhi
19 ; aliases:
20 #define Ml t //mod3 vars
21 #define Mh x // -"-
22 #define a1 x //mul_ vars
23 #define a2 _ // -"-
24
25 /* I/O REGISTERS */
26 OCR0AL = 0x26
27 DDRB = 0x01
28 PORTB = 0x02
29 PUEB = 0x03
30 SPL = 0x3D
31 SPH = 0x3E
32 CCP = 0x3C
33 CLKPSR = 0x36
34 OSCCAL = 0x39
35 WDTCSR = 0x31
36 SMCR = 0x3A
37 TCCR0A = 0x2E
38 TCCR0B = 0x2D
39 TIMSK0 = 0x2B
40 TIFR0 = 0x2A
41 RAMEND = 0x5F
42 FLASHM = 0x4000
43
44 .section .text
45 .org 0x0000 ; RESET interrupt
46 CLR i0
47 CLR i1
48 CLR i2
49 RJMP main
50 .org 0x0008 ; TIM0_OVF interrupt
51 RJMP sample
52
53 notes:
54 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
55 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
56
57 mod3: ; mod3(Mh.Ml) -> t
58 #define tmp _
59 ADD Ml, Mh
60 CLR Mh
61 ADC Mh, Mh ; store carry in Mh
62 MOV tmp, Ml
63 SWAP tmp
64 ANDI tmp, 0x0f
65 SWAP Mh
66 OR tmp, Mh
67 ANDI Ml, 0x0f
68 ADD Ml, tmp
69 MOV tmp, Ml
70 LSR tmp
71 LSR tmp
72 ANDI Ml, 0x03
73 ADD Ml, tmp
74 MOV tmp, Ml
75 LSR tmp
76 LSR tmp
77 ANDI Ml, 0x03
78 ADD Ml, tmp
79 CPI Ml, 3
80 BRPL skip
81 SUBI Ml, 3
82 skip:
83 RET
84 #undef tmp
85
86 ; definitions to mul-tree readable:
87 .macro always _bit ; nop; for when a test() is not necessary (see tree)
88 .endm
89 .macro never _bit ; nop; for when a test() is not necessary (see tree)
90 .endm
91 .macro test _bit,_jmpto
92 SBRC t, \_bit
93 RJMP \_jmpto
94 .endm
95 .macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
96 SBRS t, \_bit
97 RJMP \_jmpto
98 .endm
99 .macro shift16
100 LSR a2
101 ROR a1
102 .endm
103 .macro shift8 ; top three bits don't need to be corrrect, so save cycles by not carrying
104 LSR a1
105 .endm
106 .macro shift0 ; nop; last shift is common
107 .endm
108 .macro add16
109 ADD a1, i0
110 ADC a2, i1
111 .endm
112 .macro add8 ; ditto with carrying
113 ADD a1, i0
114 .endm
115
116 g: ; g(i, t) -> t
117 CLR a1
118
119 #define tmp _
120 #define zero a1
121 ANDI t, 0x07
122 MOV tmp, i2
123 ANDI tmp, 3
124 CPSE tmp, zero
125 SUBI t, -8
126 #undef zero
127 #undef tmp
128
129 LDI Xlo, lo8(notes)
130 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
131 LD t, X
132
133 CLR a2
134
135 /* decision tree multiplication saves cycles and (hopefully) reduces code size
136 _xxx?
137 / \
138 _xx?0 _xx1?
139 | |
140 _x?00 _x?01
141 / \ / \
142 _?000 _?100 _?001 _?101
143 / \ / \ | / \
144 _0000 _1000 _0100 _1100 _1001 _0101 _1101
145 | | | | | | |
146 ... ... ... ... ... ... ...
147 | | | | | | |
148 B0 58 84 8C 69 75 9D */
149 test 0, m____1
150 m____0: shift16
151 never 1
152 m___00: shift16
153 test 2, m__100
154 m__000: shift16
155 test 3, m_1000
156 m_0000: shift16
157 always 4
158 add16 $ shift16
159 always 5
160 add8 $ shift8
161 never 6
162 shift8
163 always 7
164 add8 $ shift0
165 RJMP end_mul ; calc'd 0xb0
166
167 m_1000: add16 $ shift16
168 always 4
169 add16 $ shift16
170 never 5
171 shift8
172 always 6
173 add8 $ shift8
174 never 7
175 shift0
176 RJMP end_mul ; calc'd 0x58
177
178 m__100: add16 $ shift16
179 i_test 3, m_0100
180 m_1100: add16
181 m_0100: shift16
182 never 4
183 shift16
184 never 5
185 shift8
186 never 6
187 shift8
188 always 7
189 add8 $ shift0
190 RJMP end_mul ; calc'd 0x8c / 0x84
191
192 m____1: add16 $ shift16
193 never 1
194 m___01: shift16
195 test 2, m__101
196 m__001: shift16
197 always 3
198 m_1001: add16 $ shift16
199 never 4
200 shift16
201 always 5
202 add8 $ shift8
203 always 6
204 add8 $ shift8
205 never 7
206 shift0
207 RJMP end_mul ; calc'd 0x69
208
209 m__101: add16 $ shift16
210 test 3, m_1101
211 m_0101: shift16
212 always 4
213 add16 $ shift16
214 always 5
215 add8 $ shift8
216 always 6
217 add8 $ shift8
218 never 7
219 shift0
220 RJMP end_mul ; calc'd 0x75
221
222 m_1101: add16 $ shift16
223 always 4
224 add16 $ shift16
225 never 5
226 shift8
227 never 6
228 shift8
229 always 7
230 add8 $ shift0
231 ; calc'd 0x9d
232
233 end_mul:
234 LSR a1 ;final shift is a common operation for all
235
236 MOV t, a1 ;;TODO: use a1 in loop: directly
237 RET
238
239 main: ; setup routine
240 ; NOTE: clr i0..i2 moved to .ord 0x0
241 CLR i3
242 CLR acc ; we output a dummy sample before the actual first one
243 LDI Xhi, hi8(FLASHM + notes) ; never changes
244 LDI one, 1 ; mostly for clearing TIM0_OVF bit
245
246 #define zero i0
247 LDI x, RAMEND
248 OUT SPL, x ; init stack ptr
249 OUT SPH, zero ; -"-
250 OUT PUEB, zero ; disable pullups
251 LDI x, 0x05
252 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
253 LDI x, 0xd8
254 OUT CCP, x ; change protected ioregs
255 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
256 LDI x, 0xa7 ; determined by trial-and-error (->PORTB2)
257 OUT OSCCAL, x ; set oscillator calibration
258 OUT WDTCSR, zero; turn off watchdog ;;TODO: incomplete - see datasheet pg48
259 ; OUT SMCR, 2 ; sleep mode 'power down' ('idle' (default) has faster response time)
260
261 ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler
262 LDI x, 0x81
263 OUT TCCR0A, x
264 LDI x, 0x09
265 OUT TCCR0B, x
266 OUT TIMSK0, one ; enable tim0_ovf
267 SEI
268 #undef zero
269
270 loop:
271 SLEEP ; wait for interrupt
272 RJMP loop
273
274 sample:
275 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
276 SBI PORTB, 2 ; to measure runtime
277
278 MOV n, i2
279 LSL n
280 LSL n
281 #define tmp _
282 MOV tmp, i1
283 SWAP tmp
284 ANDI tmp, 0x0f
285 LSR tmp
286 LSR tmp
287 OR n, tmp
288 #undef tmp
289 MOV s, i3
290 LSR s
291 ROR s
292 ANDI s, 0x80
293 #define tmp _
294 MOV tmp, i2
295 LSR tmp
296 OR s, tmp
297 #undef tmp
298
299 ; voice 1:
300 MOV t, n
301 RCALL g
302 SWAP t
303 ANDI t, 1
304 MOV acc, t
305
306 ; voice 2:
307 #define tmp _
308 MOV tmp, i2
309 LSL tmp
310 LSL tmp
311 LSL tmp
312 MOV t, i1
313 SWAP t
314 ANDI t, 0xf
315 LSR t
316 OR t, tmp
317 #undef tmp
318 EOR t, n
319 RCALL g
320 LSR t
321 LSR t
322 ANDI t, 3
323 AND t, s
324 ADD acc, t
325
326 ; voice 3:
327 MOV Ml, i2
328 SWAP Ml
329 ANDI Ml, 0xf0
330 LSL Ml
331 #define tmp _
332 MOV tmp, i1
333 LSR tmp
334 LSR tmp
335 LSR tmp
336 OR Ml, tmp
337 #undef tmp
338 MOV Mh, i3
339 SWAP Mh
340 ANDI Mh, 0xf0
341 LSL Mh
342 #define tmp _
343 MOV tmp, i2
344 LSR tmp
345 LSR tmp
346 LSR tmp
347 OR Mh, tmp
348 #undef tmp
349 RCALL mod3
350 ADD t, n
351 RCALL g
352 LSR t
353 LSR t
354 ANDI t, 3
355 MOV x, s
356 INC x
357 #define tmp _
358 MOV tmp, x
359 LSR tmp
360 LSR tmp
361 ADD tmp, x
362 ROR tmp
363 LSR tmp
364 ADD tmp, x
365 ROR tmp
366 LSR tmp
367 ADD tmp, x
368 ROR tmp
369 LSR tmp
370 AND t, tmp
371 #undef tmp
372 ADD acc, t
373
374 ; voice 4:
375 MOV Ml, i2
376 SWAP Ml
377 ANDI Ml, 0xf0
378 LSL Ml
379 LSL Ml
380 #define tmp _
381 MOV tmp, i1
382 LSR tmp
383 LSR tmp
384 OR Ml, tmp
385 #undef tmp
386 MOV Mh, i3
387 SWAP Mh
388 ANDI Mh, 0xf0
389 LSL Mh
390 LSL Mh
391 #define tmp _
392 MOV tmp, i2
393 LSR tmp
394 LSR tmp
395 OR Mh, tmp
396 #undef tmp
397 RCALL mod3
398 SUB t, n
399 NEG t
400 SUBI t, -8
401 RCALL g
402 LSR t
403 ANDI t, 3
404 INC s
405 #define tmp _
406 MOV tmp, s
407 LSR tmp
408 ADD tmp, s
409 ROR tmp
410 LSR tmp
411 LSR tmp
412 ADD tmp, s
413 ROR tmp
414 ADD tmp, s
415 ROR tmp
416 LSR tmp
417 LSR tmp
418 AND t, tmp
419 #undef tmp
420 ADD acc, t
421
422 SWAP acc ; acc<<4, to be passed to OCR0AL
423
424 SUBI i0, -1
425 SBCI i1, -1
426 SBCI i2, -1
427 SBCI i3, -1
428
429 CBI PORTB, 2 ; end runtime measurement
430 OUT TIFR0, one ; clear pending interrupt (routine takes two intr.cycles)
431 RETI ; reenables interrupts
Imprint / Impressum