fix melody for voices 3 and 4
[Chiptunes.git] / foo.S
1 /* REGISTER NAMES */
2 #define acc r16
3 #define i0 r17
4 #define i1 r18
5 #define i2 r19
6 #define i3 r20
7 #define n r21
8 #define s r22
9 #define t r23 //==Ml
10 #define x r24 //==a1==Mh
11 #define _ r25 //==a2
12 #define Xlo r26
13 #define Xhi r27
14 #define one r28
15 ; r29
16 ; r30 Zlo
17 ; r31 Zhi
18 ; aliases:
19 #define Ml t //mod3 vars
20 #define Mh x // -"-
21 #define a1 x //mul_ vars
22 #define a2 _ // -"-
23
24 /* I/O REGISTERS */
25 OCR0AL = 0x26
26 DDRB = 0x01
27 PORTB = 0x02
28 PUEB = 0x03
29 SPL = 0x3D
30 SPH = 0x3E
31 CCP = 0x3C
32 CLKPSR = 0x36
33 OSCCAL = 0x39
34 WDTCSR = 0x31
35 SMCR = 0x3A
36 TCCR0A = 0x2E
37 TCCR0B = 0x2D
38 TIMSK0 = 0x2B
39 TIFR0 = 0x2A
40 RAMEND = 0x5F
41 FLASHM = 0x4000
42
43 .section .text
44 .org 0x0000 ; RESET interrupt
45 CLR i0
46 CLR i1
47 CLR i2
48 RJMP main
49 .org 0x0008 ; TIM0_OVF interrupt
50 RJMP sample
51
52 notes:
53 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
54 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
55
56 mod3: ; mod3(Mh.Ml) -> t
57 #define tmp _
58 ADD Ml, Mh
59 CLR Mh
60 ADC Mh, Mh ; store carry in Mh
61 MOV tmp, Ml
62 SWAP tmp
63 ANDI tmp, 0x0f
64 SWAP Mh
65 OR tmp, Mh
66 ANDI Ml, 0x0f
67 ADD Ml, tmp
68 MOV tmp, Ml
69 LSR tmp
70 LSR tmp
71 ANDI Ml, 0x03
72 ADD Ml, tmp
73 MOV tmp, Ml
74 LSR tmp
75 LSR tmp
76 ANDI Ml, 0x03
77 ADD Ml, tmp
78 CPI Ml, 3
79 BRCS skip
80 SUBI Ml, 3
81 skip:
82 RET
83 #undef tmp
84
85 ; definitions to mul-tree readable:
86 .macro always _bit ; nop; for when a test() is not necessary (see tree)
87 .endm
88 .macro never _bit ; nop; for when a test() is not necessary (see tree)
89 .endm
90 .macro test _bit,_jmpto
91 SBRC t, \_bit
92 RJMP \_jmpto
93 .endm
94 .macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
95 SBRS t, \_bit
96 RJMP \_jmpto
97 .endm
98 .macro shift16
99 LSR a2
100 ROR a1
101 .endm
102 .macro shift8 ; top three bits don't need to be corrrect, so save cycles by not carrying
103 LSR a1
104 .endm
105 .macro shift0 ; nop; last shift is common
106 .endm
107 .macro add16
108 ADD a1, i0
109 ADC a2, i1
110 .endm
111 .macro add8 ; ditto with carrying
112 ADD a1, i0
113 .endm
114
115 g: ; g(i, t) -> t
116 CLR a1
117
118 #define tmp _
119 #define zero a1
120 ANDI t, 0x07
121 MOV tmp, i2
122 ANDI tmp, 3
123 CPSE tmp, zero
124 SUBI t, -8
125 #undef zero
126 #undef tmp
127
128 ;TODO: check correctness!
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
259
260 ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler
261 LDI x, 0x81
262 OUT TCCR0A, x
263 LDI x, 0x09
264 OUT TCCR0B, x
265 OUT TIMSK0, one ; enable tim0_ovf
266 SEI
267 #undef zero
268
269 loop:
270 SLEEP ; wait for interrupt
271 RJMP loop
272
273 sample:
274 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
275 #ifdef DEBUG
276 SBI PORTB, 2 ; to measure runtime
277 #endif // DEBUG
278
279 MOV n, i2
280 LSL n
281 LSL n
282 #define tmp _
283 MOV tmp, i1
284 SWAP tmp
285 ANDI tmp, 0x0f
286 LSR tmp
287 LSR tmp
288 OR n, tmp
289 #undef tmp
290 MOV s, i3
291 LSR s
292 ROR s
293 ANDI s, 0x80
294 #define tmp _
295 MOV tmp, i2
296 LSR tmp
297 OR s, tmp
298 #undef tmp
299
300 ; voice 1:
301 MOV t, n
302 RCALL g
303 SWAP t
304 ANDI t, 1
305 MOV acc, t
306
307 ; voice 2:
308 #define tmp _
309 MOV tmp, i2
310 LSL tmp
311 LSL tmp
312 LSL tmp
313 MOV t, i1
314 SWAP t
315 ANDI t, 0xf
316 LSR t
317 OR t, tmp
318 #undef tmp
319 EOR t, n
320 RCALL g
321 LSR t
322 LSR t
323 ANDI t, 3
324 AND t, s
325 ADD acc, t
326
327 ; voice 3:
328 MOV Ml, i2
329 SWAP Ml
330 ANDI Ml, 0xf0
331 LSL Ml
332 #define tmp _
333 MOV tmp, i1
334 LSR tmp
335 LSR tmp
336 LSR tmp
337 OR Ml, tmp
338 #undef tmp
339 MOV Mh, i3
340 SWAP Mh
341 ANDI Mh, 0xf0
342 LSL Mh
343 #define tmp _
344 MOV tmp, i2
345 LSR tmp
346 LSR tmp
347 LSR tmp
348 OR Mh, tmp
349 #undef tmp
350 RCALL mod3
351 ADD t, n
352 RCALL g
353 LSR t
354 LSR t
355 ANDI t, 3
356 MOV x, s
357 INC x
358 #define tmp _
359 MOV tmp, x
360 LSR tmp
361 LSR tmp
362 ADD tmp, x
363 ROR tmp
364 LSR tmp
365 ADD tmp, x
366 ROR tmp
367 LSR tmp
368 ADD tmp, x
369 ROR tmp
370 LSR tmp
371 AND t, tmp
372 #undef tmp
373 ADD acc, t
374
375 ; voice 4:
376 MOV Ml, i2
377 SWAP Ml
378 ANDI Ml, 0xf0
379 LSL Ml
380 LSL Ml
381 #define tmp _
382 MOV tmp, i1
383 LSR tmp
384 LSR tmp
385 OR Ml, tmp
386 #undef tmp
387 MOV Mh, i3
388 SWAP Mh
389 ANDI Mh, 0xf0
390 LSL Mh
391 LSL Mh
392 #define tmp _
393 MOV tmp, i2
394 LSR tmp
395 LSR tmp
396 OR Mh, tmp
397 #undef tmp
398 RCALL mod3
399 SUB t, n
400 NEG t
401 SUBI t, -8
402 RCALL g
403 LSR t
404 ANDI t, 3
405 INC s
406 #define tmp _
407 MOV tmp, s
408 LSR tmp
409 ADD tmp, s
410 ROR tmp
411 LSR tmp
412 LSR tmp
413 ADD tmp, s
414 ROR tmp
415 ADD tmp, s
416 ROR tmp
417 LSR tmp
418 LSR tmp
419 AND t, tmp
420 #undef tmp
421 ADD acc, t
422
423 SWAP acc ; acc<<4, to be passed to OCR0AL
424
425 SUBI i0, -1
426 SBCI i1, -1
427 SBCI i2, -1
428 SBCI i3, -1
429
430 #ifdef DEBUG
431 CBI PORTB, 2 ; end runtime measurement
432 #endif // DEBUG
433 OUT TIFR0, one ; clear pending interrupt (routine takes two intr.cycles)
434 RETI ; reenables interrupts
Imprint / Impressum