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