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