calibrate oscillator
[Chiptunes.git] / foo.S
1 /* REGISTER NAMES */
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
11 ; r25
12 #define x r26 //==Xlo==Mh==a1
13 #define _ r27 //==Xhi==a2
14 ; r28
15 ; r29
16 ; r30 Zlo
17 ; r31 Zhi
18 ; aliases:
19 #define Xlo r26
20 #define Xhi r27
21 #define Ml r23 //mod3 vars
22 #define Mh r26 // -"-
23
24 /* I/O REGISTERS */
25 OCR0AL = 0x26
26 DDRB = 0x01
27 PORTB = 0x02
28 PUEB = 0x03
29 SPL = 0x3D
30 SPH = 0x3E
31 CCP = 0x3C
32 CLKPSR = 0x36
33 OSCCAL = 0x39
34 WDTCSR = 0x31
35 SMCR = 0x3A
36 TCCR0A = 0x2E
37 TCCR0B = 0x2D
38 TIMSK0 = 0x2B
39 TIFR0 = 0x2A
40
41 .section .data
42 notes:
43 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
44 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
45
46 .section .text
47 .org 0x0000 ; RESET interrupt
48 RJMP main
49 .org 0x0008 ; TIM0_OVF interrupt
50 RJMP sample
51
52 mod3: ; mod3(Mh.Ml) -> t
53 #define tmp _
54 ADD Ml, Mh
55 CLR Mh
56 ADC Mh, Mh ; store carry in Mh
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:
78 RET
79 #undef tmp
80
81 ; definitions to mul-tree readable:
82 #define a1 x
83 #define a2 _
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
89 SBRC t, \_bit
90 RJMP \_jmpto
91 .endm
92 .macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
93 SBRS t, \_bit
94 RJMP \_jmpto
95 .endm
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
105 .macro add16
106 ADD a1, i0
107 ADC a2, i1
108 .endm
109 .macro add8 ; ditto with carrying
110 ADD a1, i0
111 .endm
112 #undef a2
113 #undef a1
114
115 g: ; g(i, t) -> t
116 #define a1 x
117 #define a2 _
118 CLR a1
119
120 #define tmp _
121 #define zero a1
122 ANDI t, 0x07
123 MOV tmp, i2
124 ANDI tmp, 3
125 CPSE tmp, zero
126 SUBI t, -8
127 #undef zero
128 #undef tmp
129
130 ;TODO: check correctness!
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
135
136 CLR a2
137
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 */
152 test 0, m____1
153 m____0: shift16
154 never 1
155 m___00: shift16
156 test 2, m__100
157 m__000: shift16
158 test 3, m_1000
159 m_0000: shift16
160 always 4
161 add16 $ shift16
162 always 5
163 add8 $ shift8
164 never 6
165 shift8
166 always 7
167 add8 $ shift0
168 RJMP end_mul ; calc'd 0xb0
169
170 m_1000: add16 $ shift16
171 always 4
172 add16 $ shift16
173 never 5
174 shift8
175 always 6
176 add8 $ shift8
177 never 7
178 shift0
179 RJMP end_mul ; calc'd 0x58
180
181 m__100: add16 $ shift16
182 i_test 3, m_0100
183 m_1100: add16
184 m_0100: shift16
185 never 4
186 shift16
187 never 5
188 shift8
189 never 6
190 shift8
191 always 7
192 add8 $ shift0
193 RJMP end_mul ; calc'd 0x8c / 0x84
194
195 m____1: add16 $ shift16
196 never 1
197 m___01: shift16
198 test 2, m__101
199 m__001: shift16
200 always 3
201 m_1001: add16 $ shift16
202 never 4
203 shift16
204 always 5
205 add8 $ shift8
206 always 6
207 add8 $ shift8
208 never 7
209 shift0
210 RJMP end_mul ; calc'd 0x69
211
212 m__101: add16 $ shift16
213 test 3, m_1101
214 m_0101: shift16
215 always 4
216 add16 $ shift16
217 always 5
218 add8 $ shift8
219 always 6
220 add8 $ shift8
221 never 7
222 shift0
223 RJMP end_mul ; calc'd 0x75
224
225 m_1101: add16 $ shift16
226 always 4
227 add16 $ shift16
228 never 5
229 shift8
230 never 6
231 shift8
232 always 7
233 add8 $ shift0
234 ; calc'd 0x9d
235
236 end_mul:
237 LSR a1 ;final shift is a common operation for all
238
239 MOV t, a1 ;;TODO: use a1 in loop: directly
240 #undef a1
241 #undef a2
242 RET ; TODO: replace CALL/RET with IJMP?
243
244 main: ; setup routine
245 CLR i0
246 CLR i1
247 CLR i2
248 CLR i3
249 CLR acc ; we output a dummy sample before the actual first one
250
251 #define zero i0
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
258 LDI x, 0x05
259 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
260 LDI x, 0xd8
261 OUT CCP, x ; change protected ioregs
262 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
263 LDI x, 0xa7 ; determined by trial-and-error (->PORTB2)
264 OUT OSCCAL, x ; set oscillator calibration
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
274 SEI
275 #undef one
276 #undef zero
277
278 loop:
279 SLEEP ; wait for interrupt
280 RJMP loop
281
282 sample:
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
286 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
287 SBI PORTB, 2 ; to measure runtime
288
289 MOV n, i2
290 LSL n
291 LSL n
292 #define tmp _
293 MOV tmp, i1
294 SWAP tmp
295 ANDI tmp, 0x0f
296 LSR tmp
297 LSR tmp
298 OR n, tmp
299 #undef tmp
300 MOV s, i3
301 LSR s
302 ROR s
303 ANDI s, 0x80
304 #define tmp _
305 MOV tmp, i2
306 LSR tmp
307 OR s, tmp
308 #undef tmp
309
310 ; voice 1:
311 MOV t, n
312 RCALL g
313 SWAP t
314 ANDI t, 1
315 MOV acc, t
316
317 ; voice 2:
318 #define tmp _
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
328 #undef tmp
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
336
337 ; voice 3:
338 MOV Ml, i2
339 SWAP Ml
340 ANDI Ml, 0xf0
341 LSL Ml
342 #define tmp _
343 MOV tmp, i1
344 LSR tmp
345 LSR tmp
346 LSR tmp
347 OR Ml, tmp
348 #undef tmp
349 MOV Mh, i3
350 SWAP Mh
351 ANDI Mh, 0xf0
352 LSL Mh
353 #define tmp _
354 MOV tmp, i2
355 LSR tmp
356 LSR tmp
357 LSR tmp
358 OR Mh, tmp
359 #undef tmp
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
368 #define tmp _
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
382 #undef tmp
383 ADD acc, t
384
385 ; voice 4:
386 MOV Ml, i2
387 SWAP Ml
388 ANDI Ml, 0xf0
389 LSL Ml
390 LSL Ml
391 #define tmp _
392 MOV tmp, i1
393 LSR tmp
394 LSR tmp
395 OR Ml, tmp
396 #undef tmp
397 MOV Mh, i3
398 SWAP Mh
399 ANDI Mh, 0xf0
400 LSL Mh
401 LSL Mh
402 #define tmp _
403 MOV tmp, i2
404 LSR tmp
405 LSR tmp
406 OR Mh, tmp
407 #undef tmp
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
416 #define tmp _
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
430 #undef tmp
431 ADD acc, t
432
433 SWAP acc ; acc<<4, to be passed to OCR0AL
434
435 SUBI i0, -1
436 SBCI i1, -1
437 SBCI i2, -1
438 SBCI i3, -1
439
440 CBI PORTB, 2 ; end runtime measurement
441 LDI _, 1 ; NOTE: could use own register for speed
442 OUT TIFR0, _ ; clear pending interrupt (routine takes two intr.cycles)
443 RETI ; reenables interrupts
Imprint / Impressum