swap registers for `t` and `_`
[Chiptunes.git] / foo.S
CommitLineData
f180febe
TG
1/* REGISTER NAMES */
2#define zero r16
3#define acc r17
4#define i0 r18
5#define i1 r19
6#define i2 r20
7#define i3 r21
8#define n r22
9#define s r23
d44d4b47 10#define t r24 //==Ml
f180febe
TG
11; r25
12#define x r26 //==Xlo==Mh
d44d4b47 13#define _ r27 //==Xhi
f180febe
TG
14; r28
15; r29
16; r30 Zlo
17; r31 Zhi
18; aliases:
19#define Xlo r26
20#define Xhi r27
d44d4b47
TG
21#define Ml r24 //mod3 vars
22#define Mh r26 // -"-
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
19e320a6 33WDTCSR = 0x31
f180febe
TG
34SMCR = 0x3A
35TCCR0A = 0x2E
36TCCR0B = 0x2D
37TIMSK0 = 0x2B
38TIFR0 = 0x2A
4466dd8b 39
f180febe 40.section .data
d44d4b47 41notes:
f180febe
TG
42 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
43 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
4466dd8b 44
f180febe 45.section .text
19e320a6
TG
46.org 0x0000 ; RESET interrupt
47 RJMP main
48.org 0x0008 ; TIM0_OVF interrupt
49 RJMP sample
4466dd8b 50
f180febe 51mod3: ; mod3(Mh.Ml) -> t
8d8c00e4 52 #define tmp _
65aa7cd6
TG
53 ADD Ml, Mh
54 CLR Mh
55 ADC Mh, zero
56 MOV tmp, Ml
57 SWAP tmp
58 ANDI tmp, 0x0f
59 SWAP Mh
60 OR tmp, Mh
61 ANDI Ml, 0x0f
62 ADD Ml, tmp
63 MOV tmp, Ml
64 LSR tmp
65 LSR tmp
66 ANDI Ml, 0x03
67 ADD Ml, tmp
68 MOV tmp, Ml
69 LSR tmp
70 LSR tmp
71 ANDI Ml, 0x03
72 ADD Ml, tmp
73 CPI Ml, 3
74 BRPL skip
75 SUBI Ml, 3
76 skip:
4283632d 77 RET
8d8c00e4 78 #undef tmp
f180febe
TG
79
80; definitions to mul-tree readable:
65958d9d
TG
81#define a1 x
82#define a2 _
f180febe
TG
83.macro always _bit ; nop; for when a test() is not necessary (see tree)
84.endm
85.macro never _bit ; nop; for when a test() is not necessary (see tree)
86.endm
87.macro test _bit,_jmpto
65958d9d
TG
88 SBRC t, \_bit
89 RJMP \_jmpto
f180febe 90.endm
f8861a90
TG
91.macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
92 SBRS t, \_bit
93 RJMP \_jmpto
94.endm
f180febe
TG
95.macro shift16
96 LSR a2
97 ROR a1
98.endm
99.macro shift8 ; top three bits don't need to be corrrect, so save cycles by not carrying
100 LSR a1
101.endm
102.macro shift0 ; nop; last shift is common
103.endm
ea40b11f 104.macro add16
f180febe
TG
105 ADD a1, i0
106 ADC a2, i1
f180febe 107.endm
ea40b11f 108.macro add8 ; ditto with carrying
f180febe
TG
109 ADD a1, i0
110.endm
65958d9d
TG
111#undef a2
112#undef a1
f180febe
TG
113
114g: ; g(i, t) -> t
d35c3d70 115 #define tmp _
65aa7cd6
TG
116 ANDI t, 0x07
117 MOV tmp, i2
118 ANDI tmp, 3
65aa7cd6
TG
119 CPSE tmp, zero
120 SUBI t, -8
02f61e33 121 #undef tmp
4466dd8b 122
34fa6d04 123 ;TODO: check correctness!
d44d4b47
TG
124 LDI Xhi, hi8(notes) ; hi(notes) always zero, but still need to clear the register
125 LDI Xlo, lo8(notes)
126 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
127 LD t, X
4466dd8b 128
cc428230
TG
129 #define a1 x
130 #define a2 _
131 #define a0 t
65aa7cd6
TG
132 CLR a2
133 CLR a1
d0324785 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
65aa7cd6 236 MOV t, a1 ;;TODO: use a1 in main() directly
d0324785
TG
237 #undef a0
238 #undef a1
239 #undef a2
65aa7cd6 240 RET ; TODO: replace CALL/RET with IJMP?
61fab018 241
19e320a6 242main: ; setup routine
65aa7cd6
TG
243 CLR zero
244 CLR i0
245 CLR i1
246 CLR i2
247 CLR i3
19e320a6
TG
248 CLR acc ; we output a dummy sample before the actual first one
249
250 #define one _
251 LDI one, 1
252 LDI x, 0x5f ; RAMEND
253 OUT SPL, x ; init stack ptr
254 OUT SPH, zero ; -"-
255 OUT PUEB, zero ; disable pullups
ea40b11f
TG
256 LDI x, 0x05
257 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
19e320a6
TG
258 LDI x, 0xd8
259 OUT CCP, x ; change protected ioregs
260 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
261 OUT WDTCSR, zero; turn off watchdog ;;TODO: incomplete - see datasheet pg48
262 ; OUT SMCR, 2 ; sleep mode 'power down' ('idle' (default) has faster response time)
263
264 ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler
265 LDI x, 0x81
266 OUT TCCR0A, x
267 LDI x, 0x09
268 OUT TCCR0B, x
269 OUT TIMSK0, one ; enable tim0_ovf
270 OUT TIFR0, one ; TODO: why?
271 SEI
272 #undef one
f180febe 273 RJMP sample
19e320a6
TG
274
275loop:
276 SLEEP ; wait for interrupt
277 RJMP loop
278
279sample:
34fa6d04
TG
280 ; potential TODO: softcounter in r25 to only update duty cicle every n iterations
281 ; potential TODO: save/restore status register (SREG=0x3f) (only if something in mainloop)
282
19e320a6 283 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
34fa6d04 284 SBI PORTB, 2 ; to measure runtime
19e320a6 285
65aa7cd6
TG
286 MOV n, i2
287 LSL n
288 LSL n
f6ef1520 289 #define tmp _
65aa7cd6
TG
290 MOV tmp, i1
291 SWAP tmp
292 ANDI tmp, 0x0f
293 LSR tmp
294 LSR tmp
295 OR n, tmp
f6ef1520 296 #undef tmp
65aa7cd6
TG
297 MOV s, i3
298 LSR s
299 ROR s
300 ANDI s, 0x80
f6ef1520 301 #define tmp _
65aa7cd6
TG
302 MOV tmp, i2
303 LSR tmp
304 OR s, tmp
f6ef1520 305 #undef tmp
3b86ca43 306
65aa7cd6
TG
307 ; voice 1:
308 MOV t, n
309 RCALL g
310 SWAP t
311 ANDI t, 1
312 MOV acc, t
3b86ca43 313
65aa7cd6 314 ; voice 2:
f6ef1520 315 #define tmp _
65aa7cd6
TG
316 MOV tmp, i2
317 LSL tmp
318 LSL tmp
319 LSL tmp
320 MOV t, i1
321 SWAP t
322 ANDI t, 0xf
323 LSR t
324 OR t, tmp
f6ef1520 325 #undef tmp
65aa7cd6
TG
326 EOR t, n
327 RCALL g
328 LSR t
329 LSR t
330 ANDI t, 3
331 AND t, s
332 ADD acc, t
3b86ca43 333
65aa7cd6
TG
334 ; voice 3:
335 MOV Ml, i2
336 SWAP Ml
337 ANDI Ml, 0xf0
338 LSL Ml
f6ef1520 339 #define tmp _
65aa7cd6
TG
340 MOV tmp, i1
341 LSR tmp
342 LSR tmp
343 LSR tmp
344 OR Ml, tmp
f6ef1520 345 #undef tmp
65aa7cd6
TG
346 MOV Mh, i3
347 SWAP Mh
348 ANDI Mh, 0xf0
349 LSL Mh
f6ef1520 350 #define tmp _
65aa7cd6
TG
351 MOV tmp, i2
352 LSR tmp
353 LSR tmp
354 LSR tmp
355 OR Mh, tmp
f6ef1520 356 #undef tmp
65aa7cd6
TG
357 RCALL mod3
358 ADD t, n
359 RCALL g
360 LSR t
361 LSR t
362 ANDI t, 3
363 MOV x, s
364 INC x
f6ef1520 365 #define tmp _
65aa7cd6
TG
366 MOV tmp, x
367 LSR tmp
368 LSR tmp
369 ADD tmp, x
370 ROR tmp
371 LSR tmp
372 ADD tmp, x
373 ROR tmp
374 LSR tmp
375 ADD tmp, x
376 ROR tmp
377 LSR tmp
378 AND t, tmp
f6ef1520 379 #undef tmp
65aa7cd6 380 ADD acc, t
f6ef1520 381
65aa7cd6
TG
382 ; voice 4:
383 MOV Ml, i2
384 SWAP Ml
385 ANDI Ml, 0xf0
386 LSL Ml
387 LSL Ml
f6ef1520 388 #define tmp _
65aa7cd6
TG
389 MOV tmp, i1
390 LSR tmp
391 LSR tmp
392 OR Ml, tmp
f6ef1520 393 #undef tmp
65aa7cd6
TG
394 MOV Mh, i3
395 SWAP Mh
396 ANDI Mh, 0xf0
397 LSL Mh
398 LSL Mh
f6ef1520 399 #define tmp _
65aa7cd6
TG
400 MOV tmp, i2
401 LSR tmp
402 LSR tmp
403 OR Mh, tmp
f6ef1520 404 #undef tmp
65aa7cd6
TG
405 RCALL mod3
406 SUB t, n
407 NEG t
408 SUBI t, -8
409 RCALL g
410 LSR t
411 ANDI t, 3
412 INC s
f6ef1520 413 #define tmp _
65aa7cd6
TG
414 MOV tmp, s
415 LSR tmp
416 ADD tmp, s
417 ROR tmp
418 LSR tmp
419 LSR tmp
420 ADD tmp, s
421 ROR tmp
422 ADD tmp, s
423 ROR tmp
424 LSR tmp
425 LSR tmp
426 AND t, tmp
f6ef1520 427 #undef tmp
65aa7cd6 428 ADD acc, t
3b86ca43 429
19e320a6
TG
430 SWAP acc ; acc<<4, to be passed to OCR0AL
431
f6ef1520
TG
432 SUBI i0, -1
433 SBCI i1, -1
434 SBCI i2, -1
435 SBCI i3, -1
bfce2f8c 436
34fa6d04
TG
437 CBI PORTB, 2 ; end runtime measurement
438 ;TODO: to reduce jitter: clear pending tim0_ovf (TIFR0[TOV0] <- 1) ?
19e320a6 439 RETI ; reenables interrupts
Imprint / Impressum