1f7a5c30e65af7b9df8ac97e394532af535ba49c
[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 ;TODO: check correctness!
130 LDI Xlo, lo8(notes)
131 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
132 LD t, X
133
134 CLR a2
135
136 /* decision tree multiplication saves cycles and (hopefully) reduces code size
137 _xxx?
138 / \
139 _xx?0 _xx1?
140 | |
141 _x?00 _x?01
142 / \ / \
143 _?000 _?100 _?001 _?101
144 / \ / \ | / \
145 _0000 _1000 _0100 _1100 _1001 _0101 _1101
146 | | | | | | |
147 ... ... ... ... ... ... ...
148 | | | | | | |
149 B0 58 84 8C 69 75 9D */
150 test 0, m____1
151 m____0: shift16
152 never 1
153 m___00: shift16
154 test 2, m__100
155 m__000: shift16
156 test 3, m_1000
157 m_0000: shift16
158 always 4
159 add16 $ shift16
160 always 5
161 add8 $ shift8
162 never 6
163 shift8
164 always 7
165 add8 $ shift0
166 RJMP end_mul ; calc'd 0xb0
167
168 m_1000: add16 $ shift16
169 always 4
170 add16 $ shift16
171 never 5
172 shift8
173 always 6
174 add8 $ shift8
175 never 7
176 shift0
177 RJMP end_mul ; calc'd 0x58
178
179 m__100: add16 $ shift16
180 i_test 3, m_0100
181 m_1100: add16
182 m_0100: shift16
183 never 4
184 shift16
185 never 5
186 shift8
187 never 6
188 shift8
189 always 7
190 add8 $ shift0
191 RJMP end_mul ; calc'd 0x8c / 0x84
192
193 m____1: add16 $ shift16
194 never 1
195 m___01: shift16
196 test 2, m__101
197 m__001: shift16
198 always 3
199 m_1001: add16 $ shift16
200 never 4
201 shift16
202 always 5
203 add8 $ shift8
204 always 6
205 add8 $ shift8
206 never 7
207 shift0
208 RJMP end_mul ; calc'd 0x69
209
210 m__101: add16 $ shift16
211 test 3, m_1101
212 m_0101: shift16
213 always 4
214 add16 $ shift16
215 always 5
216 add8 $ shift8
217 always 6
218 add8 $ shift8
219 never 7
220 shift0
221 RJMP end_mul ; calc'd 0x75
222
223 m_1101: add16 $ shift16
224 always 4
225 add16 $ shift16
226 never 5
227 shift8
228 never 6
229 shift8
230 always 7
231 add8 $ shift0
232 ; calc'd 0x9d
233
234 end_mul:
235 LSR a1 ;final shift is a common operation for all
236
237 MOV t, a1 ;;TODO: use a1 in loop: directly
238 RET
239
240 main: ; setup routine
241 ; NOTE: clr i0..i2 moved to .ord 0x0
242 CLR i3
243 CLR acc ; we output a dummy sample before the actual first one
244 LDI Xhi, hi8(FLASHM + notes) ; never changes
245 LDI one, 1 ; mostly for clearing TIM0_OVF bit
246
247 #define zero i0
248 LDI x, RAMEND
249 OUT SPL, x ; init stack ptr
250 OUT SPH, zero ; -"-
251 OUT PUEB, zero ; disable pullups
252 LDI x, 0x05
253 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
254 LDI x, 0xd8
255 OUT CCP, x ; change protected ioregs
256 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
257 LDI x, 0xa7 ; determined by trial-and-error (->PORTB2)
258 OUT OSCCAL, x ; set oscillator calibration
259 OUT WDTCSR, zero; turn off watchdog
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 #ifdef DEBUG
277 SBI PORTB, 2 ; to measure runtime
278 #endif // DEBUG
279
280 MOV n, i2
281 LSL n
282 LSL n
283 #define tmp _
284 MOV tmp, i1
285 SWAP tmp
286 ANDI tmp, 0x0f
287 LSR tmp
288 LSR tmp
289 OR n, tmp
290 #undef tmp
291 MOV s, i3
292 LSR s
293 ROR s
294 ANDI s, 0x80
295 #define tmp _
296 MOV tmp, i2
297 LSR tmp
298 OR s, tmp
299 #undef tmp
300
301 ; voice 1:
302 MOV t, n
303 RCALL g
304 SWAP t
305 ANDI t, 1
306 MOV acc, t
307
308 ; voice 2:
309 #define tmp _
310 MOV tmp, i2
311 LSL tmp
312 LSL tmp
313 LSL tmp
314 MOV t, i1
315 SWAP t
316 ANDI t, 0xf
317 LSR t
318 OR t, tmp
319 #undef tmp
320 EOR t, n
321 RCALL g
322 LSR t
323 LSR t
324 ANDI t, 3
325 AND t, s
326 ADD acc, t
327
328 ; voice 3:
329 MOV Ml, i2
330 SWAP Ml
331 ANDI Ml, 0xf0
332 LSL Ml
333 #define tmp _
334 MOV tmp, i1
335 LSR tmp
336 LSR tmp
337 LSR tmp
338 OR Ml, tmp
339 #undef tmp
340 MOV Mh, i3
341 SWAP Mh
342 ANDI Mh, 0xf0
343 LSL Mh
344 #define tmp _
345 MOV tmp, i2
346 LSR tmp
347 LSR tmp
348 LSR tmp
349 OR Mh, tmp
350 #undef tmp
351 RCALL mod3
352 ADD t, n
353 RCALL g
354 LSR t
355 LSR t
356 ANDI t, 3
357 MOV x, s
358 INC x
359 #define tmp _
360 MOV tmp, x
361 LSR tmp
362 LSR tmp
363 ADD tmp, x
364 ROR tmp
365 LSR tmp
366 ADD tmp, x
367 ROR tmp
368 LSR tmp
369 ADD tmp, x
370 ROR tmp
371 LSR tmp
372 AND t, tmp
373 #undef tmp
374 ADD acc, t
375
376 ; voice 4:
377 MOV Ml, i2
378 SWAP Ml
379 ANDI Ml, 0xf0
380 LSL Ml
381 LSL Ml
382 #define tmp _
383 MOV tmp, i1
384 LSR tmp
385 LSR tmp
386 OR Ml, tmp
387 #undef tmp
388 MOV Mh, i3
389 SWAP Mh
390 ANDI Mh, 0xf0
391 LSL Mh
392 LSL Mh
393 #define tmp _
394 MOV tmp, i2
395 LSR tmp
396 LSR tmp
397 OR Mh, tmp
398 #undef tmp
399 RCALL mod3
400 SUB t, n
401 NEG t
402 SUBI t, -8
403 RCALL g
404 LSR t
405 ANDI t, 3
406 INC s
407 #define tmp _
408 MOV tmp, s
409 LSR tmp
410 ADD tmp, s
411 ROR tmp
412 LSR tmp
413 LSR tmp
414 ADD tmp, s
415 ROR tmp
416 ADD tmp, s
417 ROR tmp
418 LSR tmp
419 LSR tmp
420 AND t, tmp
421 #undef tmp
422 ADD acc, t
423
424 SWAP acc ; acc<<4, to be passed to OCR0AL
425
426 SUBI i0, -1
427 SBCI i1, -1
428 SBCI i2, -1
429 SBCI i3, -1
430
431 #ifdef DEBUG
432 CBI PORTB, 2 ; end runtime measurement
433 #endif // DEBUG
434 OUT TIFR0, one ; clear pending interrupt (routine takes two intr.cycles)
435 RETI ; reenables interrupts
Imprint / Impressum