calibrate oscillator
[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
2af726bc 33OSCCAL = 0x39
19e320a6 34WDTCSR = 0x31
f180febe
TG
35SMCR = 0x3A
36TCCR0A = 0x2E
37TCCR0B = 0x2D
38TIMSK0 = 0x2B
39TIFR0 = 0x2A
4466dd8b 40
f180febe 41.section .data
d44d4b47 42notes:
f180febe
TG
43 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
44 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
4466dd8b 45
f180febe 46.section .text
19e320a6
TG
47.org 0x0000 ; RESET interrupt
48 RJMP main
49.org 0x0008 ; TIM0_OVF interrupt
50 RJMP sample
4466dd8b 51
f180febe 52mod3: ; mod3(Mh.Ml) -> t
8d8c00e4 53 #define tmp _
65aa7cd6
TG
54 ADD Ml, Mh
55 CLR Mh
6d672b87 56 ADC Mh, Mh ; store carry in Mh
65aa7cd6
TG
57 MOV tmp, Ml
58 SWAP tmp
59 ANDI tmp, 0x0f
60 SWAP Mh
61 OR tmp, Mh
62 ANDI Ml, 0x0f
63 ADD Ml, tmp
64 MOV tmp, Ml
65 LSR tmp
66 LSR tmp
67 ANDI Ml, 0x03
68 ADD Ml, tmp
69 MOV tmp, Ml
70 LSR tmp
71 LSR tmp
72 ANDI Ml, 0x03
73 ADD Ml, tmp
74 CPI Ml, 3
75 BRPL skip
76 SUBI Ml, 3
77 skip:
4283632d 78 RET
8d8c00e4 79 #undef tmp
f180febe
TG
80
81; definitions to mul-tree readable:
65958d9d
TG
82#define a1 x
83#define a2 _
f180febe
TG
84.macro always _bit ; nop; for when a test() is not necessary (see tree)
85.endm
86.macro never _bit ; nop; for when a test() is not necessary (see tree)
87.endm
88.macro test _bit,_jmpto
65958d9d
TG
89 SBRC t, \_bit
90 RJMP \_jmpto
f180febe 91.endm
f8861a90
TG
92.macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
93 SBRS t, \_bit
94 RJMP \_jmpto
95.endm
f180febe
TG
96.macro shift16
97 LSR a2
98 ROR a1
99.endm
100.macro shift8 ; top three bits don't need to be corrrect, so save cycles by not carrying
101 LSR a1
102.endm
103.macro shift0 ; nop; last shift is common
104.endm
ea40b11f 105.macro add16
f180febe
TG
106 ADD a1, i0
107 ADC a2, i1
f180febe 108.endm
ea40b11f 109.macro add8 ; ditto with carrying
f180febe
TG
110 ADD a1, i0
111.endm
65958d9d
TG
112#undef a2
113#undef a1
f180febe
TG
114
115g: ; g(i, t) -> t
6d672b87
TG
116 #define a1 x
117 #define a2 _
6d672b87
TG
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
986f12ae
TG
136 CLR a2
137
4466dd8b
TG
138 /* decision tree multiplication saves cycles and (hopefully) reduces code size
139 _xxx?
140 / \
141 _xx?0 _xx1?
142 | |
143 _x?00 _x?01
144 / \ / \
145 _?000 _?100 _?001 _?101
146 / \ / \ | / \
147 _0000 _1000 _0100 _1100 _1001 _0101 _1101
148 | | | | | | |
149 ... ... ... ... ... ... ...
150 | | | | | | |
151 B0 58 84 8C 69 75 9D */
65aa7cd6 152 test 0, m____1
4466dd8b 153 m____0: shift16
65aa7cd6 154 never 1
4466dd8b 155 m___00: shift16
65aa7cd6 156 test 2, m__100
4466dd8b 157 m__000: shift16
65aa7cd6 158 test 3, m_1000
4466dd8b 159 m_0000: shift16
65aa7cd6 160 always 4
ea40b11f 161 add16 $ shift16
65aa7cd6 162 always 5
ea40b11f 163 add8 $ shift8
65aa7cd6 164 never 6
4466dd8b 165 shift8
65aa7cd6 166 always 7
ea40b11f 167 add8 $ shift0
65aa7cd6 168 RJMP end_mul ; calc'd 0xb0
d0324785 169
ea40b11f 170 m_1000: add16 $ shift16
65aa7cd6 171 always 4
ea40b11f 172 add16 $ shift16
65aa7cd6 173 never 5
4466dd8b 174 shift8
65aa7cd6 175 always 6
ea40b11f 176 add8 $ shift8
65aa7cd6 177 never 7
4466dd8b 178 shift0
65aa7cd6 179 RJMP end_mul ; calc'd 0x58
d0324785 180
ea40b11f 181 m__100: add16 $ shift16
f8861a90
TG
182 i_test 3, m_0100
183 m_1100: add16
4466dd8b 184 m_0100: shift16
65aa7cd6 185 never 4
4466dd8b 186 shift16
65aa7cd6 187 never 5
4466dd8b 188 shift8
65aa7cd6 189 never 6
4466dd8b 190 shift8
65aa7cd6 191 always 7
ea40b11f 192 add8 $ shift0
f8861a90 193 RJMP end_mul ; calc'd 0x8c / 0x84
d0324785 194
ea40b11f 195 m____1: add16 $ shift16
65aa7cd6 196 never 1
4466dd8b 197 m___01: shift16
65aa7cd6 198 test 2, m__101
4466dd8b 199 m__001: shift16
65aa7cd6 200 always 3
ea40b11f 201 m_1001: add16 $ shift16
65aa7cd6 202 never 4
4466dd8b 203 shift16
65aa7cd6 204 always 5
ea40b11f 205 add8 $ shift8
65aa7cd6 206 always 6
ea40b11f 207 add8 $ shift8
65aa7cd6 208 never 7
4466dd8b 209 shift0
65aa7cd6 210 RJMP end_mul ; calc'd 0x69
d0324785 211
ea40b11f 212 m__101: add16 $ shift16
65aa7cd6 213 test 3, m_1101
4466dd8b 214 m_0101: shift16
65aa7cd6 215 always 4
ea40b11f 216 add16 $ shift16
65aa7cd6 217 always 5
ea40b11f 218 add8 $ shift8
65aa7cd6 219 always 6
ea40b11f 220 add8 $ shift8
65aa7cd6 221 never 7
4466dd8b 222 shift0
65aa7cd6 223 RJMP end_mul ; calc'd 0x75
d0324785 224
ea40b11f 225 m_1101: add16 $ shift16
65aa7cd6 226 always 4
ea40b11f 227 add16 $ shift16
65aa7cd6 228 never 5
4466dd8b 229 shift8
65aa7cd6 230 never 6
4466dd8b 231 shift8
65aa7cd6 232 always 7
ea40b11f 233 add8 $ shift0
65aa7cd6 234 ; calc'd 0x9d
d0324785 235
4466dd8b 236 end_mul:
65aa7cd6 237 LSR a1 ;final shift is a common operation for all
4466dd8b 238
2af726bc 239 MOV t, a1 ;;TODO: use a1 in loop: directly
d0324785
TG
240 #undef a1
241 #undef a2
65aa7cd6 242 RET ; TODO: replace CALL/RET with IJMP?
61fab018 243
19e320a6 244main: ; setup routine
65aa7cd6
TG
245 CLR i0
246 CLR i1
247 CLR i2
248 CLR i3
19e320a6
TG
249 CLR acc ; we output a dummy sample before the actual first one
250
6d672b87 251 #define zero i0
19e320a6
TG
252 #define one _
253 LDI one, 1
254 LDI x, 0x5f ; RAMEND
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
acc3a518
TG
441 LDI _, 1 ; NOTE: could use own register for speed
442 OUT TIFR0, _ ; clear pending interrupt (routine takes two intr.cycles)
19e320a6 443 RETI ; reenables interrupts
Imprint / Impressum