remove dedicated zero register (saves 4 bytes of progmem)
[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
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
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 _
117 CLR a2
118 CLR a1
119
d35c3d70 120 #define tmp _
6d672b87 121 #define zero a1
65aa7cd6
TG
122 ANDI t, 0x07
123 MOV tmp, i2
124 ANDI tmp, 3
65aa7cd6
TG
125 CPSE tmp, zero
126 SUBI t, -8
6d672b87 127 #undef zero
02f61e33 128 #undef tmp
4466dd8b 129
34fa6d04 130 ;TODO: check correctness!
d44d4b47
TG
131 LDI Xhi, hi8(notes) ; hi(notes) always zero, but still need to clear the register
132 LDI Xlo, lo8(notes)
133 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
134 LD t, X
4466dd8b 135
4466dd8b
TG
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 */
65aa7cd6 150 test 0, m____1
4466dd8b 151 m____0: shift16
65aa7cd6 152 never 1
4466dd8b 153 m___00: shift16
65aa7cd6 154 test 2, m__100
4466dd8b 155 m__000: shift16
65aa7cd6 156 test 3, m_1000
4466dd8b 157 m_0000: shift16
65aa7cd6 158 always 4
ea40b11f 159 add16 $ shift16
65aa7cd6 160 always 5
ea40b11f 161 add8 $ shift8
65aa7cd6 162 never 6
4466dd8b 163 shift8
65aa7cd6 164 always 7
ea40b11f 165 add8 $ shift0
65aa7cd6 166 RJMP end_mul ; calc'd 0xb0
d0324785 167
ea40b11f 168 m_1000: add16 $ shift16
65aa7cd6 169 always 4
ea40b11f 170 add16 $ shift16
65aa7cd6 171 never 5
4466dd8b 172 shift8
65aa7cd6 173 always 6
ea40b11f 174 add8 $ shift8
65aa7cd6 175 never 7
4466dd8b 176 shift0
65aa7cd6 177 RJMP end_mul ; calc'd 0x58
d0324785 178
ea40b11f 179 m__100: add16 $ shift16
f8861a90
TG
180 i_test 3, m_0100
181 m_1100: add16
4466dd8b 182 m_0100: shift16
65aa7cd6 183 never 4
4466dd8b 184 shift16
65aa7cd6 185 never 5
4466dd8b 186 shift8
65aa7cd6 187 never 6
4466dd8b 188 shift8
65aa7cd6 189 always 7
ea40b11f 190 add8 $ shift0
f8861a90 191 RJMP end_mul ; calc'd 0x8c / 0x84
d0324785 192
ea40b11f 193 m____1: add16 $ shift16
65aa7cd6 194 never 1
4466dd8b 195 m___01: shift16
65aa7cd6 196 test 2, m__101
4466dd8b 197 m__001: shift16
65aa7cd6 198 always 3
ea40b11f 199 m_1001: add16 $ shift16
65aa7cd6 200 never 4
4466dd8b 201 shift16
65aa7cd6 202 always 5
ea40b11f 203 add8 $ shift8
65aa7cd6 204 always 6
ea40b11f 205 add8 $ shift8
65aa7cd6 206 never 7
4466dd8b 207 shift0
65aa7cd6 208 RJMP end_mul ; calc'd 0x69
d0324785 209
ea40b11f 210 m__101: add16 $ shift16
65aa7cd6 211 test 3, m_1101
4466dd8b 212 m_0101: shift16
65aa7cd6 213 always 4
ea40b11f 214 add16 $ shift16
65aa7cd6 215 always 5
ea40b11f 216 add8 $ shift8
65aa7cd6 217 always 6
ea40b11f 218 add8 $ shift8
65aa7cd6 219 never 7
4466dd8b 220 shift0
65aa7cd6 221 RJMP end_mul ; calc'd 0x75
d0324785 222
ea40b11f 223 m_1101: add16 $ shift16
65aa7cd6 224 always 4
ea40b11f 225 add16 $ shift16
65aa7cd6 226 never 5
4466dd8b 227 shift8
65aa7cd6 228 never 6
4466dd8b 229 shift8
65aa7cd6 230 always 7
ea40b11f 231 add8 $ shift0
65aa7cd6 232 ; calc'd 0x9d
d0324785 233
4466dd8b 234 end_mul:
65aa7cd6 235 LSR a1 ;final shift is a common operation for all
4466dd8b 236
65aa7cd6 237 MOV t, a1 ;;TODO: use a1 in main() directly
d0324785
TG
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 i0
244 CLR i1
245 CLR i2
246 CLR i3
19e320a6
TG
247 CLR acc ; we output a dummy sample before the actual first one
248
6d672b87 249 #define zero i0
19e320a6
TG
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
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)
283
19e320a6 284 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
34fa6d04 285 SBI PORTB, 2 ; to measure runtime
19e320a6 286
65aa7cd6
TG
287 MOV n, i2
288 LSL n
289 LSL n
f6ef1520 290 #define tmp _
65aa7cd6
TG
291 MOV tmp, i1
292 SWAP tmp
293 ANDI tmp, 0x0f
294 LSR tmp
295 LSR tmp
296 OR n, tmp
f6ef1520 297 #undef tmp
65aa7cd6
TG
298 MOV s, i3
299 LSR s
300 ROR s
301 ANDI s, 0x80
f6ef1520 302 #define tmp _
65aa7cd6
TG
303 MOV tmp, i2
304 LSR tmp
305 OR s, tmp
f6ef1520 306 #undef tmp
3b86ca43 307
65aa7cd6
TG
308 ; voice 1:
309 MOV t, n
310 RCALL g
311 SWAP t
312 ANDI t, 1
313 MOV acc, t
3b86ca43 314
65aa7cd6 315 ; voice 2:
f6ef1520 316 #define tmp _
65aa7cd6
TG
317 MOV tmp, i2
318 LSL tmp
319 LSL tmp
320 LSL tmp
321 MOV t, i1
322 SWAP t
323 ANDI t, 0xf
324 LSR t
325 OR t, tmp
f6ef1520 326 #undef tmp
65aa7cd6
TG
327 EOR t, n
328 RCALL g
329 LSR t
330 LSR t
331 ANDI t, 3
332 AND t, s
333 ADD acc, t
3b86ca43 334
65aa7cd6
TG
335 ; voice 3:
336 MOV Ml, i2
337 SWAP Ml
338 ANDI Ml, 0xf0
339 LSL Ml
f6ef1520 340 #define tmp _
65aa7cd6
TG
341 MOV tmp, i1
342 LSR tmp
343 LSR tmp
344 LSR tmp
345 OR Ml, tmp
f6ef1520 346 #undef tmp
65aa7cd6
TG
347 MOV Mh, i3
348 SWAP Mh
349 ANDI Mh, 0xf0
350 LSL Mh
f6ef1520 351 #define tmp _
65aa7cd6
TG
352 MOV tmp, i2
353 LSR tmp
354 LSR tmp
355 LSR tmp
356 OR Mh, tmp
f6ef1520 357 #undef tmp
65aa7cd6
TG
358 RCALL mod3
359 ADD t, n
360 RCALL g
361 LSR t
362 LSR t
363 ANDI t, 3
364 MOV x, s
365 INC x
f6ef1520 366 #define tmp _
65aa7cd6
TG
367 MOV tmp, x
368 LSR tmp
369 LSR tmp
370 ADD tmp, x
371 ROR tmp
372 LSR tmp
373 ADD tmp, x
374 ROR tmp
375 LSR tmp
376 ADD tmp, x
377 ROR tmp
378 LSR tmp
379 AND t, tmp
f6ef1520 380 #undef tmp
65aa7cd6 381 ADD acc, t
f6ef1520 382
65aa7cd6
TG
383 ; voice 4:
384 MOV Ml, i2
385 SWAP Ml
386 ANDI Ml, 0xf0
387 LSL Ml
388 LSL Ml
f6ef1520 389 #define tmp _
65aa7cd6
TG
390 MOV tmp, i1
391 LSR tmp
392 LSR tmp
393 OR Ml, tmp
f6ef1520 394 #undef tmp
65aa7cd6
TG
395 MOV Mh, i3
396 SWAP Mh
397 ANDI Mh, 0xf0
398 LSL Mh
399 LSL Mh
f6ef1520 400 #define tmp _
65aa7cd6
TG
401 MOV tmp, i2
402 LSR tmp
403 LSR tmp
404 OR Mh, tmp
f6ef1520 405 #undef tmp
65aa7cd6
TG
406 RCALL mod3
407 SUB t, n
408 NEG t
409 SUBI t, -8
410 RCALL g
411 LSR t
412 ANDI t, 3
413 INC s
f6ef1520 414 #define tmp _
65aa7cd6
TG
415 MOV tmp, s
416 LSR tmp
417 ADD tmp, s
418 ROR tmp
419 LSR tmp
420 LSR tmp
421 ADD tmp, s
422 ROR tmp
423 ADD tmp, s
424 ROR tmp
425 LSR tmp
426 LSR tmp
427 AND t, tmp
f6ef1520 428 #undef tmp
65aa7cd6 429 ADD acc, t
3b86ca43 430
19e320a6
TG
431 SWAP acc ; acc<<4, to be passed to OCR0AL
432
f6ef1520
TG
433 SUBI i0, -1
434 SBCI i1, -1
435 SBCI i2, -1
436 SBCI i3, -1
bfce2f8c 437
34fa6d04
TG
438 CBI PORTB, 2 ; end runtime measurement
439 ;TODO: to reduce jitter: clear pending tim0_ovf (TIFR0[TOV0] <- 1) ?
19e320a6 440 RETI ; reenables interrupts
Imprint / Impressum