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