fix melody for voices 3 and 4
[Chiptunes.git] / foo.S
CommitLineData
f180febe 1/* REGISTER NAMES */
6d672b87
TG
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
f99fd6f3
TG
10#define x r24 //==a1==Mh
11#define _ r25 //==a2
12#define Xlo r26
13#define Xhi r27
ce618731 14#define one r28
f180febe
TG
15; r29
16; r30 Zlo
17; r31 Zhi
18; aliases:
ce618731
TG
19#define Ml t //mod3 vars
20#define Mh x // -"-
21#define a1 x //mul_ vars
22#define a2 _ // -"-
da32ed67 23
f180febe
TG
24/* I/O REGISTERS */
25OCR0AL = 0x26
26DDRB = 0x01
34fa6d04 27PORTB = 0x02
f180febe
TG
28PUEB = 0x03
29SPL = 0x3D
30SPH = 0x3E
31CCP = 0x3C
32CLKPSR = 0x36
2af726bc 33OSCCAL = 0x39
19e320a6 34WDTCSR = 0x31
f180febe
TG
35SMCR = 0x3A
36TCCR0A = 0x2E
37TCCR0B = 0x2D
38TIMSK0 = 0x2B
39TIFR0 = 0x2A
f99fd6f3
TG
40RAMEND = 0x5F
41FLASHM = 0x4000
4466dd8b 42
f180febe 43.section .text
19e320a6 44.org 0x0000 ; RESET interrupt
cf36b4c3
TG
45 CLR i0
46 CLR i1
47 CLR i2
19e320a6
TG
48 RJMP main
49.org 0x0008 ; TIM0_OVF interrupt
50 RJMP sample
4466dd8b 51
f99fd6f3
TG
52notes:
53 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
54 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
55
f180febe 56mod3: ; mod3(Mh.Ml) -> t
8d8c00e4 57 #define tmp _
65aa7cd6
TG
58 ADD Ml, Mh
59 CLR Mh
6d672b87 60 ADC Mh, Mh ; store carry in Mh
65aa7cd6
TG
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
26799bab 79 BRCS skip
65aa7cd6
TG
80 SUBI Ml, 3
81 skip:
4283632d 82 RET
8d8c00e4 83 #undef tmp
f180febe
TG
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
65958d9d
TG
91 SBRC t, \_bit
92 RJMP \_jmpto
f180febe 93.endm
f8861a90
TG
94.macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
95 SBRS t, \_bit
96 RJMP \_jmpto
97.endm
f180febe
TG
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
ea40b11f 107.macro add16
f180febe
TG
108 ADD a1, i0
109 ADC a2, i1
f180febe 110.endm
ea40b11f 111.macro add8 ; ditto with carrying
f180febe
TG
112 ADD a1, i0
113.endm
114
115g: ; g(i, t) -> t
6d672b87
TG
116 CLR a1
117
d35c3d70 118 #define tmp _
6d672b87 119 #define zero a1
65aa7cd6
TG
120 ANDI t, 0x07
121 MOV tmp, i2
122 ANDI tmp, 3
65aa7cd6
TG
123 CPSE tmp, zero
124 SUBI t, -8
6d672b87 125 #undef zero
02f61e33 126 #undef tmp
4466dd8b 127
34fa6d04 128 ;TODO: check correctness!
d44d4b47
TG
129 LDI Xlo, lo8(notes)
130 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
131 LD t, X
4466dd8b 132
986f12ae
TG
133 CLR a2
134
4466dd8b
TG
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 */
65aa7cd6 149 test 0, m____1
4466dd8b 150 m____0: shift16
65aa7cd6 151 never 1
4466dd8b 152 m___00: shift16
65aa7cd6 153 test 2, m__100
4466dd8b 154 m__000: shift16
65aa7cd6 155 test 3, m_1000
4466dd8b 156 m_0000: shift16
65aa7cd6 157 always 4
ea40b11f 158 add16 $ shift16
65aa7cd6 159 always 5
ea40b11f 160 add8 $ shift8
65aa7cd6 161 never 6
4466dd8b 162 shift8
65aa7cd6 163 always 7
ea40b11f 164 add8 $ shift0
65aa7cd6 165 RJMP end_mul ; calc'd 0xb0
d0324785 166
ea40b11f 167 m_1000: add16 $ shift16
65aa7cd6 168 always 4
ea40b11f 169 add16 $ shift16
65aa7cd6 170 never 5
4466dd8b 171 shift8
65aa7cd6 172 always 6
ea40b11f 173 add8 $ shift8
65aa7cd6 174 never 7
4466dd8b 175 shift0
65aa7cd6 176 RJMP end_mul ; calc'd 0x58
d0324785 177
ea40b11f 178 m__100: add16 $ shift16
f8861a90
TG
179 i_test 3, m_0100
180 m_1100: add16
4466dd8b 181 m_0100: shift16
65aa7cd6 182 never 4
4466dd8b 183 shift16
65aa7cd6 184 never 5
4466dd8b 185 shift8
65aa7cd6 186 never 6
4466dd8b 187 shift8
65aa7cd6 188 always 7
ea40b11f 189 add8 $ shift0
f8861a90 190 RJMP end_mul ; calc'd 0x8c / 0x84
d0324785 191
ea40b11f 192 m____1: add16 $ shift16
65aa7cd6 193 never 1
4466dd8b 194 m___01: shift16
65aa7cd6 195 test 2, m__101
4466dd8b 196 m__001: shift16
65aa7cd6 197 always 3
ea40b11f 198 m_1001: add16 $ shift16
65aa7cd6 199 never 4
4466dd8b 200 shift16
65aa7cd6 201 always 5
ea40b11f 202 add8 $ shift8
65aa7cd6 203 always 6
ea40b11f 204 add8 $ shift8
65aa7cd6 205 never 7
4466dd8b 206 shift0
65aa7cd6 207 RJMP end_mul ; calc'd 0x69
d0324785 208
ea40b11f 209 m__101: add16 $ shift16
65aa7cd6 210 test 3, m_1101
4466dd8b 211 m_0101: shift16
65aa7cd6 212 always 4
ea40b11f 213 add16 $ shift16
65aa7cd6 214 always 5
ea40b11f 215 add8 $ shift8
65aa7cd6 216 always 6
ea40b11f 217 add8 $ shift8
65aa7cd6 218 never 7
4466dd8b 219 shift0
65aa7cd6 220 RJMP end_mul ; calc'd 0x75
d0324785 221
ea40b11f 222 m_1101: add16 $ shift16
65aa7cd6 223 always 4
ea40b11f 224 add16 $ shift16
65aa7cd6 225 never 5
4466dd8b 226 shift8
65aa7cd6 227 never 6
4466dd8b 228 shift8
65aa7cd6 229 always 7
ea40b11f 230 add8 $ shift0
65aa7cd6 231 ; calc'd 0x9d
d0324785 232
4466dd8b 233 end_mul:
65aa7cd6 234 LSR a1 ;final shift is a common operation for all
4466dd8b 235
2af726bc 236 MOV t, a1 ;;TODO: use a1 in loop: directly
ce618731 237 RET
61fab018 238
19e320a6 239main: ; setup routine
cf36b4c3 240 ; NOTE: clr i0..i2 moved to .ord 0x0
65aa7cd6 241 CLR i3
19e320a6 242 CLR acc ; we output a dummy sample before the actual first one
f99fd6f3 243 LDI Xhi, hi8(FLASHM + notes) ; never changes
ce618731 244 LDI one, 1 ; mostly for clearing TIM0_OVF bit
19e320a6 245
6d672b87 246 #define zero i0
f99fd6f3 247 LDI x, RAMEND
19e320a6
TG
248 OUT SPL, x ; init stack ptr
249 OUT SPH, zero ; -"-
250 OUT PUEB, zero ; disable pullups
ea40b11f
TG
251 LDI x, 0x05
252 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
19e320a6
TG
253 LDI x, 0xd8
254 OUT CCP, x ; change protected ioregs
255 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
2af726bc
TG
256 LDI x, 0xa7 ; determined by trial-and-error (->PORTB2)
257 OUT OSCCAL, x ; set oscillator calibration
ce618731 258 OUT WDTCSR, zero; turn off watchdog
19e320a6
TG
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
19e320a6 266 SEI
6d672b87 267 #undef zero
19e320a6
TG
268
269loop:
270 SLEEP ; wait for interrupt
271 RJMP loop
272
273sample:
19e320a6 274 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
ce618731 275#ifdef DEBUG
34fa6d04 276 SBI PORTB, 2 ; to measure runtime
ce618731 277#endif // DEBUG
19e320a6 278
65aa7cd6
TG
279 MOV n, i2
280 LSL n
281 LSL n
f6ef1520 282 #define tmp _
65aa7cd6
TG
283 MOV tmp, i1
284 SWAP tmp
285 ANDI tmp, 0x0f
286 LSR tmp
287 LSR tmp
288 OR n, tmp
f6ef1520 289 #undef tmp
65aa7cd6
TG
290 MOV s, i3
291 LSR s
292 ROR s
293 ANDI s, 0x80
f6ef1520 294 #define tmp _
65aa7cd6
TG
295 MOV tmp, i2
296 LSR tmp
297 OR s, tmp
f6ef1520 298 #undef tmp
3b86ca43 299
65aa7cd6
TG
300 ; voice 1:
301 MOV t, n
302 RCALL g
303 SWAP t
304 ANDI t, 1
305 MOV acc, t
3b86ca43 306
65aa7cd6 307 ; voice 2:
f6ef1520 308 #define tmp _
65aa7cd6
TG
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
f6ef1520 318 #undef tmp
65aa7cd6
TG
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
3b86ca43 326
65aa7cd6
TG
327 ; voice 3:
328 MOV Ml, i2
329 SWAP Ml
330 ANDI Ml, 0xf0
331 LSL Ml
f6ef1520 332 #define tmp _
65aa7cd6
TG
333 MOV tmp, i1
334 LSR tmp
335 LSR tmp
336 LSR tmp
337 OR Ml, tmp
f6ef1520 338 #undef tmp
65aa7cd6
TG
339 MOV Mh, i3
340 SWAP Mh
341 ANDI Mh, 0xf0
342 LSL Mh
f6ef1520 343 #define tmp _
65aa7cd6
TG
344 MOV tmp, i2
345 LSR tmp
346 LSR tmp
347 LSR tmp
348 OR Mh, tmp
f6ef1520 349 #undef tmp
65aa7cd6
TG
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
f6ef1520 358 #define tmp _
65aa7cd6
TG
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
f6ef1520 372 #undef tmp
65aa7cd6 373 ADD acc, t
f6ef1520 374
65aa7cd6
TG
375 ; voice 4:
376 MOV Ml, i2
377 SWAP Ml
378 ANDI Ml, 0xf0
379 LSL Ml
380 LSL Ml
f6ef1520 381 #define tmp _
65aa7cd6
TG
382 MOV tmp, i1
383 LSR tmp
384 LSR tmp
385 OR Ml, tmp
f6ef1520 386 #undef tmp
65aa7cd6
TG
387 MOV Mh, i3
388 SWAP Mh
389 ANDI Mh, 0xf0
390 LSL Mh
391 LSL Mh
f6ef1520 392 #define tmp _
65aa7cd6
TG
393 MOV tmp, i2
394 LSR tmp
395 LSR tmp
396 OR Mh, tmp
f6ef1520 397 #undef tmp
65aa7cd6
TG
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
f6ef1520 406 #define tmp _
65aa7cd6
TG
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
f6ef1520 420 #undef tmp
65aa7cd6 421 ADD acc, t
3b86ca43 422
19e320a6
TG
423 SWAP acc ; acc<<4, to be passed to OCR0AL
424
f6ef1520
TG
425 SUBI i0, -1
426 SBCI i1, -1
427 SBCI i2, -1
428 SBCI i3, -1
bfce2f8c 429
ce618731
TG
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)
19e320a6 434 RETI ; reenables interrupts
Imprint / Impressum