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