fix Ml register, clear interrupt before RETI
[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
10; r24
f180febe 11; r25
acc3a518
TG
12#define x r26 //==Xlo==Mh==a1
13#define _ r27 //==Xhi==a2
f180febe
TG
14; r28
15; r29
16; r30 Zlo
17; r31 Zhi
18; aliases:
19#define Xlo r26
20#define Xhi r27
acc3a518 21#define Ml r23 //mod3 vars
d44d4b47 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
6d672b87 55 ADC Mh, Mh ; store carry in Mh
65aa7cd6
TG
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
6d672b87
TG
115 #define a1 x
116 #define a2 _
6d672b87
TG
117 CLR a1
118
d35c3d70 119 #define tmp _
6d672b87 120 #define zero a1
65aa7cd6
TG
121 ANDI t, 0x07
122 MOV tmp, i2
123 ANDI tmp, 3
65aa7cd6
TG
124 CPSE tmp, zero
125 SUBI t, -8
6d672b87 126 #undef zero
02f61e33 127 #undef tmp
4466dd8b 128
34fa6d04 129 ;TODO: check correctness!
d44d4b47
TG
130 LDI Xhi, hi8(notes) ; hi(notes) always zero, but still need to clear the register
131 LDI Xlo, lo8(notes)
132 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
133 LD t, X
4466dd8b 134
986f12ae
TG
135 CLR a2
136
4466dd8b
TG
137 /* decision tree multiplication saves cycles and (hopefully) reduces code size
138 _xxx?
139 / \
140 _xx?0 _xx1?
141 | |
142 _x?00 _x?01
143 / \ / \
144 _?000 _?100 _?001 _?101
145 / \ / \ | / \
146 _0000 _1000 _0100 _1100 _1001 _0101 _1101
147 | | | | | | |
148 ... ... ... ... ... ... ...
149 | | | | | | |
150 B0 58 84 8C 69 75 9D */
65aa7cd6 151 test 0, m____1
4466dd8b 152 m____0: shift16
65aa7cd6 153 never 1
4466dd8b 154 m___00: shift16
65aa7cd6 155 test 2, m__100
4466dd8b 156 m__000: shift16
65aa7cd6 157 test 3, m_1000
4466dd8b 158 m_0000: shift16
65aa7cd6 159 always 4
ea40b11f 160 add16 $ shift16
65aa7cd6 161 always 5
ea40b11f 162 add8 $ shift8
65aa7cd6 163 never 6
4466dd8b 164 shift8
65aa7cd6 165 always 7
ea40b11f 166 add8 $ shift0
65aa7cd6 167 RJMP end_mul ; calc'd 0xb0
d0324785 168
ea40b11f 169 m_1000: add16 $ shift16
65aa7cd6 170 always 4
ea40b11f 171 add16 $ shift16
65aa7cd6 172 never 5
4466dd8b 173 shift8
65aa7cd6 174 always 6
ea40b11f 175 add8 $ shift8
65aa7cd6 176 never 7
4466dd8b 177 shift0
65aa7cd6 178 RJMP end_mul ; calc'd 0x58
d0324785 179
ea40b11f 180 m__100: add16 $ shift16
f8861a90
TG
181 i_test 3, m_0100
182 m_1100: add16
4466dd8b 183 m_0100: shift16
65aa7cd6 184 never 4
4466dd8b 185 shift16
65aa7cd6 186 never 5
4466dd8b 187 shift8
65aa7cd6 188 never 6
4466dd8b 189 shift8
65aa7cd6 190 always 7
ea40b11f 191 add8 $ shift0
f8861a90 192 RJMP end_mul ; calc'd 0x8c / 0x84
d0324785 193
ea40b11f 194 m____1: add16 $ shift16
65aa7cd6 195 never 1
4466dd8b 196 m___01: shift16
65aa7cd6 197 test 2, m__101
4466dd8b 198 m__001: shift16
65aa7cd6 199 always 3
ea40b11f 200 m_1001: add16 $ shift16
65aa7cd6 201 never 4
4466dd8b 202 shift16
65aa7cd6 203 always 5
ea40b11f 204 add8 $ shift8
65aa7cd6 205 always 6
ea40b11f 206 add8 $ shift8
65aa7cd6 207 never 7
4466dd8b 208 shift0
65aa7cd6 209 RJMP end_mul ; calc'd 0x69
d0324785 210
ea40b11f 211 m__101: add16 $ shift16
65aa7cd6 212 test 3, m_1101
4466dd8b 213 m_0101: shift16
65aa7cd6 214 always 4
ea40b11f 215 add16 $ shift16
65aa7cd6 216 always 5
ea40b11f 217 add8 $ shift8
65aa7cd6 218 always 6
ea40b11f 219 add8 $ shift8
65aa7cd6 220 never 7
4466dd8b 221 shift0
65aa7cd6 222 RJMP end_mul ; calc'd 0x75
d0324785 223
ea40b11f 224 m_1101: add16 $ shift16
65aa7cd6 225 always 4
ea40b11f 226 add16 $ shift16
65aa7cd6 227 never 5
4466dd8b 228 shift8
65aa7cd6 229 never 6
4466dd8b 230 shift8
65aa7cd6 231 always 7
ea40b11f 232 add8 $ shift0
65aa7cd6 233 ; calc'd 0x9d
d0324785 234
4466dd8b 235 end_mul:
65aa7cd6 236 LSR a1 ;final shift is a common operation for all
4466dd8b 237
65aa7cd6 238 MOV t, a1 ;;TODO: use a1 in main() directly
d0324785
TG
239 #undef a1
240 #undef a2
65aa7cd6 241 RET ; TODO: replace CALL/RET with IJMP?
61fab018 242
19e320a6 243main: ; setup routine
65aa7cd6
TG
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
6d672b87 250 #define zero i0
19e320a6
TG
251 #define one _
252 LDI one, 1
253 LDI x, 0x5f ; RAMEND
254 OUT SPL, x ; init stack ptr
255 OUT SPH, zero ; -"-
256 OUT PUEB, zero ; disable pullups
ea40b11f
TG
257 LDI x, 0x05
258 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
19e320a6
TG
259 LDI x, 0xd8
260 OUT CCP, x ; change protected ioregs
261 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
262 OUT WDTCSR, zero; turn off watchdog ;;TODO: incomplete - see datasheet pg48
263 ; OUT SMCR, 2 ; sleep mode 'power down' ('idle' (default) has faster response time)
264
265 ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler
266 LDI x, 0x81
267 OUT TCCR0A, x
268 LDI x, 0x09
269 OUT TCCR0B, x
270 OUT TIMSK0, one ; enable tim0_ovf
19e320a6
TG
271 SEI
272 #undef one
6d672b87 273 #undef zero
f180febe 274 RJMP sample
19e320a6
TG
275
276loop:
277 SLEEP ; wait for interrupt
278 RJMP loop
279
280sample:
34fa6d04
TG
281 ; potential TODO: softcounter in r25 to only update duty cicle every n iterations
282 ; potential TODO: save/restore status register (SREG=0x3f) (only if something in mainloop)
acc3a518 283 ; TODO: there is substantial jitter in the tim0_ovf interval -- are we clearing the interrupt flag correctly?
34fa6d04 284
19e320a6 285 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
34fa6d04 286 SBI PORTB, 2 ; to measure runtime
19e320a6 287
65aa7cd6
TG
288 MOV n, i2
289 LSL n
290 LSL n
f6ef1520 291 #define tmp _
65aa7cd6
TG
292 MOV tmp, i1
293 SWAP tmp
294 ANDI tmp, 0x0f
295 LSR tmp
296 LSR tmp
297 OR n, tmp
f6ef1520 298 #undef tmp
65aa7cd6
TG
299 MOV s, i3
300 LSR s
301 ROR s
302 ANDI s, 0x80
f6ef1520 303 #define tmp _
65aa7cd6
TG
304 MOV tmp, i2
305 LSR tmp
306 OR s, tmp
f6ef1520 307 #undef tmp
3b86ca43 308
65aa7cd6
TG
309 ; voice 1:
310 MOV t, n
311 RCALL g
312 SWAP t
313 ANDI t, 1
314 MOV acc, t
3b86ca43 315
65aa7cd6 316 ; voice 2:
f6ef1520 317 #define tmp _
65aa7cd6
TG
318 MOV tmp, i2
319 LSL tmp
320 LSL tmp
321 LSL tmp
322 MOV t, i1
323 SWAP t
324 ANDI t, 0xf
325 LSR t
326 OR t, tmp
f6ef1520 327 #undef tmp
65aa7cd6
TG
328 EOR t, n
329 RCALL g
330 LSR t
331 LSR t
332 ANDI t, 3
333 AND t, s
334 ADD acc, t
3b86ca43 335
65aa7cd6
TG
336 ; voice 3:
337 MOV Ml, i2
338 SWAP Ml
339 ANDI Ml, 0xf0
340 LSL Ml
f6ef1520 341 #define tmp _
65aa7cd6
TG
342 MOV tmp, i1
343 LSR tmp
344 LSR tmp
345 LSR tmp
346 OR Ml, tmp
f6ef1520 347 #undef tmp
65aa7cd6
TG
348 MOV Mh, i3
349 SWAP Mh
350 ANDI Mh, 0xf0
351 LSL Mh
f6ef1520 352 #define tmp _
65aa7cd6
TG
353 MOV tmp, i2
354 LSR tmp
355 LSR tmp
356 LSR tmp
357 OR Mh, tmp
f6ef1520 358 #undef tmp
65aa7cd6
TG
359 RCALL mod3
360 ADD t, n
361 RCALL g
362 LSR t
363 LSR t
364 ANDI t, 3
365 MOV x, s
366 INC x
f6ef1520 367 #define tmp _
65aa7cd6
TG
368 MOV tmp, x
369 LSR tmp
370 LSR tmp
371 ADD tmp, x
372 ROR tmp
373 LSR tmp
374 ADD tmp, x
375 ROR tmp
376 LSR tmp
377 ADD tmp, x
378 ROR tmp
379 LSR tmp
380 AND t, tmp
f6ef1520 381 #undef tmp
65aa7cd6 382 ADD acc, t
f6ef1520 383
65aa7cd6
TG
384 ; voice 4:
385 MOV Ml, i2
386 SWAP Ml
387 ANDI Ml, 0xf0
388 LSL Ml
389 LSL Ml
f6ef1520 390 #define tmp _
65aa7cd6
TG
391 MOV tmp, i1
392 LSR tmp
393 LSR tmp
394 OR Ml, tmp
f6ef1520 395 #undef tmp
65aa7cd6
TG
396 MOV Mh, i3
397 SWAP Mh
398 ANDI Mh, 0xf0
399 LSL Mh
400 LSL Mh
f6ef1520 401 #define tmp _
65aa7cd6
TG
402 MOV tmp, i2
403 LSR tmp
404 LSR tmp
405 OR Mh, tmp
f6ef1520 406 #undef tmp
65aa7cd6
TG
407 RCALL mod3
408 SUB t, n
409 NEG t
410 SUBI t, -8
411 RCALL g
412 LSR t
413 ANDI t, 3
414 INC s
f6ef1520 415 #define tmp _
65aa7cd6
TG
416 MOV tmp, s
417 LSR tmp
418 ADD tmp, s
419 ROR tmp
420 LSR tmp
421 LSR tmp
422 ADD tmp, s
423 ROR tmp
424 ADD tmp, s
425 ROR tmp
426 LSR tmp
427 LSR tmp
428 AND t, tmp
f6ef1520 429 #undef tmp
65aa7cd6 430 ADD acc, t
3b86ca43 431
19e320a6
TG
432 SWAP acc ; acc<<4, to be passed to OCR0AL
433
f6ef1520
TG
434 SUBI i0, -1
435 SBCI i1, -1
436 SBCI i2, -1
437 SBCI i3, -1
bfce2f8c 438
34fa6d04 439 CBI PORTB, 2 ; end runtime measurement
acc3a518
TG
440 LDI _, 1 ; NOTE: could use own register for speed
441 OUT TIFR0, _ ; clear pending interrupt (routine takes two intr.cycles)
19e320a6 442 RETI ; reenables interrupts
Imprint / Impressum