conserve 6 bytes of progmem
[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 CLR i0
44 CLR i1
45 CLR i2
46 RJMP main
47 .org 0x0008 ; TIM0_OVF interrupt
48 RJMP sample
49
50 notes:
51 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
52 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
53
54 mod3: ; mod3(Mh.Ml) -> t
55 #define tmp _
56 ADD Ml, Mh
57 CLR Mh
58 ADC Mh, Mh ; store carry in Mh
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:
80 RET
81 #undef tmp
82
83 ; definitions to mul-tree readable:
84 #define a1 x
85 #define a2 _
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
91 SBRC t, \_bit
92 RJMP \_jmpto
93 .endm
94 .macro i_test _bit,_jmpto ; inverted test (for reordered 0x8_)
95 SBRS t, \_bit
96 RJMP \_jmpto
97 .endm
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
107 .macro add16
108 ADD a1, i0
109 ADC a2, i1
110 .endm
111 .macro add8 ; ditto with carrying
112 ADD a1, i0
113 .endm
114 #undef a2
115 #undef a1
116
117 g: ; g(i, t) -> t
118 #define a1 x
119 #define a2 _
120 CLR a1
121
122 #define tmp _
123 #define zero a1
124 ANDI t, 0x07
125 MOV tmp, i2
126 ANDI tmp, 3
127 CPSE tmp, zero
128 SUBI t, -8
129 #undef zero
130 #undef tmp
131
132 ;TODO: check correctness!
133 LDI Xlo, lo8(notes)
134 ADD Xlo, t ; NOTE: can't overflow, since RAMEND == 0x5F
135 LD t, X
136
137 CLR a2
138
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 */
153 test 0, m____1
154 m____0: shift16
155 never 1
156 m___00: shift16
157 test 2, m__100
158 m__000: shift16
159 test 3, m_1000
160 m_0000: shift16
161 always 4
162 add16 $ shift16
163 always 5
164 add8 $ shift8
165 never 6
166 shift8
167 always 7
168 add8 $ shift0
169 RJMP end_mul ; calc'd 0xb0
170
171 m_1000: add16 $ shift16
172 always 4
173 add16 $ shift16
174 never 5
175 shift8
176 always 6
177 add8 $ shift8
178 never 7
179 shift0
180 RJMP end_mul ; calc'd 0x58
181
182 m__100: add16 $ shift16
183 i_test 3, m_0100
184 m_1100: add16
185 m_0100: shift16
186 never 4
187 shift16
188 never 5
189 shift8
190 never 6
191 shift8
192 always 7
193 add8 $ shift0
194 RJMP end_mul ; calc'd 0x8c / 0x84
195
196 m____1: add16 $ shift16
197 never 1
198 m___01: shift16
199 test 2, m__101
200 m__001: shift16
201 always 3
202 m_1001: add16 $ shift16
203 never 4
204 shift16
205 always 5
206 add8 $ shift8
207 always 6
208 add8 $ shift8
209 never 7
210 shift0
211 RJMP end_mul ; calc'd 0x69
212
213 m__101: add16 $ shift16
214 test 3, m_1101
215 m_0101: shift16
216 always 4
217 add16 $ shift16
218 always 5
219 add8 $ shift8
220 always 6
221 add8 $ shift8
222 never 7
223 shift0
224 RJMP end_mul ; calc'd 0x75
225
226 m_1101: add16 $ shift16
227 always 4
228 add16 $ shift16
229 never 5
230 shift8
231 never 6
232 shift8
233 always 7
234 add8 $ shift0
235 ; calc'd 0x9d
236
237 end_mul:
238 LSR a1 ;final shift is a common operation for all
239
240 MOV t, a1 ;;TODO: use a1 in loop: directly
241 #undef a1
242 #undef a2
243 RET ; TODO: replace CALL/RET with IJMP?
244
245 main: ; setup routine
246 ; NOTE: clr i0..i2 moved to .ord 0x0
247 CLR i3
248 CLR acc ; we output a dummy sample before the actual first one
249 LDI Xhi, hi8(FLASHM + notes) ; never changes
250
251 #define zero i0
252 #define one _
253 LDI one, 1
254 LDI x, 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 ; TODO: could use own register for speed
442 OUT TIFR0, _ ; clear pending interrupt (routine takes two intr.cycles)
443 RETI ; reenables interrupts
Imprint / Impressum