add debug pin, remove 2 unnecessary instructions
[Chiptunes.git] / foo.S
1 /* REGISTER NAMES */
2 #define zero r16
3 #define acc r17
4 #define i0 r18
5 #define i1 r19
6 #define i2 r20
7 #define i3 r21
8 #define n r22
9 #define s r23
10 #define _ r24
11 ; r25
12 #define x r26 //==Xlo==Mh
13 #define t r27 //==Xhi==Ml
14 ; r28
15 ; r29
16 ; r30 Zlo
17 ; r31 Zhi
18 ; aliases:
19 #define Xlo r26
20 #define Xhi r27
21 #define Mh r26 //mod3 vars
22 #define Ml r27 // -"-
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 data:
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, zero
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 shift16
92 LSR a2
93 ROR a1
94 .endm
95 .macro shift8 ; top three bits don't need to be corrrect, so save cycles by not carrying
96 LSR a1
97 .endm
98 .macro shift0 ; nop; last shift is common
99 .endm
100 .macro add_shift16
101 ADD a1, i0
102 ADC a2, i1
103 shift16
104 .endm
105 .macro add_shift8 ; ditto with carrying
106 ADD a1, i0
107 shift8
108 .endm
109 .macro add_shift0 ; last shift is common
110 ADD a1, i0
111 .endm
112 #undef a2
113 #undef a1
114
115 g: ; g(i, t) -> t
116 #define tmp _
117 ANDI t, 0x07
118 MOV tmp, i2
119 ANDI tmp, 3
120 CPSE tmp, zero
121 SUBI t, -8
122 #undef tmp
123
124 ;TODO: check correctness!
125 #define tmp _
126 MOV tmp, t ; NOTE: must move value away from `t`, as that is also hi(X)
127 LDI Xhi, hi8(data) ; hi(data) always zero, but still need to clear the register
128 LDI Xlo, lo8(data)
129 ADD Xlo, tmp ;<-- the offset (formerly `t`) into data[]
130 ;ADC Xhi, zero ; data == 0x40 t <= 0x10, so can never overflow
131 LD tmp, X
132 MOV t, tmp
133 #undef tmp
134
135 #define a1 x
136 #define a2 _
137 #define a0 t
138 CLR a2
139 CLR a1
140
141 /* decision tree multiplication saves cycles and (hopefully) reduces code size
142 _xxx?
143 / \
144 _xx?0 _xx1?
145 | |
146 _x?00 _x?01
147 / \ / \
148 _?000 _?100 _?001 _?101
149 / \ / \ | / \
150 _0000 _1000 _0100 _1100 _1001 _0101 _1101
151 | | | | | | |
152 ... ... ... ... ... ... ...
153 | | | | | | |
154 B0 58 84 8C 69 75 9D */
155 test 0, m____1
156 m____0: shift16
157 never 1
158 m___00: shift16
159 test 2, m__100
160 m__000: shift16
161 test 3, m_1000
162 m_0000: shift16
163 always 4
164 add_shift16
165 always 5
166 add_shift8
167 never 6
168 shift8
169 always 7
170 add_shift0
171 RJMP end_mul ; calc'd 0xb0
172
173 m_1000: add_shift16
174 always 4
175 add_shift16
176 never 5
177 shift8
178 always 6
179 add_shift8
180 never 7
181 shift0
182 RJMP end_mul ; calc'd 0x58
183
184 m__100: add_shift16
185 test 3, m_1100
186 m_0100: shift16
187 RJMP upper_8 ;'ll calc 0x84
188 ;TODO: combine shift16 above with add_shift16 below to save progmem
189 m_1100: add_shift16
190 upper_8: ; used twice, so deduplicated
191 never 4
192 shift16
193 never 5
194 shift8
195 never 6
196 shift8
197 always 7
198 add_shift0
199 RJMP end_mul ; calc'd 0x8c
200
201 m____1: add_shift16
202 never 1
203 m___01: shift16
204 test 2, m__101
205 m__001: shift16
206 always 3
207 m_1001: add_shift16
208 never 4
209 shift16
210 always 5
211 add_shift8
212 always 6
213 add_shift8
214 never 7
215 shift0
216 RJMP end_mul ; calc'd 0x69
217
218 m__101: add_shift16
219 test 3, m_1101
220 m_0101: shift16
221 always 4
222 add_shift16
223 always 5
224 add_shift8
225 always 6
226 add_shift8
227 never 7
228 shift0
229 RJMP end_mul ; calc'd 0x75
230
231 m_1101: add_shift16
232 always 4
233 add_shift16
234 never 5
235 shift8
236 never 6
237 shift8
238 always 7
239 add_shift0
240 ; calc'd 0x9d
241
242 end_mul:
243 LSR a1 ;final shift is a common operation for all
244
245 MOV t, a1 ;;TODO: use a1 in main() directly
246 #undef a0
247 #undef a1
248 #undef a2
249 RET ; TODO: replace CALL/RET with IJMP?
250
251 main: ; setup routine
252 CLR zero
253 CLR i0
254 CLR i1
255 CLR i2
256 CLR i3
257 CLR acc ; we output a dummy sample before the actual first one
258
259 #define one _
260 LDI one, 1
261 LDI x, 0x5f ; RAMEND
262 OUT SPL, x ; init stack ptr
263 OUT SPH, zero ; -"-
264 OUT PUEB, zero ; disable pullups
265 LDI x, 0x05 ; PORTB0:pwm, PORTB2:debug
266 OUT DDRB, x
267 LDI x, 0xd8
268 OUT CCP, x ; change protected ioregs
269 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
270 OUT WDTCSR, zero; turn off watchdog ;;TODO: incomplete - see datasheet pg48
271 ; OUT SMCR, 2 ; sleep mode 'power down' ('idle' (default) has faster response time)
272
273 ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler
274 LDI x, 0x81
275 OUT TCCR0A, x
276 LDI x, 0x09
277 OUT TCCR0B, x
278 OUT TIMSK0, one ; enable tim0_ovf
279 OUT TIFR0, one ; TODO: why?
280 SEI
281 #undef one
282 RJMP sample
283
284 loop:
285 SLEEP ; wait for interrupt
286 RJMP loop
287
288 sample:
289 ; potential TODO: softcounter in r25 to only update duty cicle every n iterations
290 ; potential TODO: save/restore status register (SREG=0x3f) (only if something in mainloop)
291
292 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
293 SBI PORTB, 2 ; to measure runtime
294
295 MOV n, i2
296 LSL n
297 LSL n
298 #define tmp _
299 MOV tmp, i1
300 SWAP tmp
301 ANDI tmp, 0x0f
302 LSR tmp
303 LSR tmp
304 OR n, tmp
305 #undef tmp
306 MOV s, i3
307 LSR s
308 ROR s
309 ANDI s, 0x80
310 #define tmp _
311 MOV tmp, i2
312 LSR tmp
313 OR s, tmp
314 #undef tmp
315
316 ; voice 1:
317 MOV t, n
318 RCALL g
319 SWAP t
320 ANDI t, 1
321 MOV acc, t
322
323 ; voice 2:
324 #define tmp _
325 MOV tmp, i2
326 LSL tmp
327 LSL tmp
328 LSL tmp
329 MOV t, i1
330 SWAP t
331 ANDI t, 0xf
332 LSR t
333 OR t, tmp
334 #undef tmp
335 EOR t, n
336 RCALL g
337 LSR t
338 LSR t
339 ANDI t, 3
340 AND t, s
341 ADD acc, t
342
343 ; voice 3:
344 MOV Ml, i2
345 SWAP Ml
346 ANDI Ml, 0xf0
347 LSL Ml
348 #define tmp _
349 MOV tmp, i1
350 LSR tmp
351 LSR tmp
352 LSR tmp
353 OR Ml, tmp
354 #undef tmp
355 MOV Mh, i3
356 SWAP Mh
357 ANDI Mh, 0xf0
358 LSL Mh
359 #define tmp _
360 MOV tmp, i2
361 LSR tmp
362 LSR tmp
363 LSR tmp
364 OR Mh, tmp
365 #undef tmp
366 RCALL mod3
367 ADD t, n
368 RCALL g
369 LSR t
370 LSR t
371 ANDI t, 3
372 MOV x, s
373 INC x
374 #define tmp _
375 MOV tmp, x
376 LSR tmp
377 LSR tmp
378 ADD tmp, x
379 ROR tmp
380 LSR tmp
381 ADD tmp, x
382 ROR tmp
383 LSR tmp
384 ADD tmp, x
385 ROR tmp
386 LSR tmp
387 AND t, tmp
388 #undef tmp
389 ADD acc, t
390
391 ; voice 4:
392 MOV Ml, i2
393 SWAP Ml
394 ANDI Ml, 0xf0
395 LSL Ml
396 LSL Ml
397 #define tmp _
398 MOV tmp, i1
399 LSR tmp
400 LSR tmp
401 OR Ml, tmp
402 #undef tmp
403 MOV Mh, i3
404 SWAP Mh
405 ANDI Mh, 0xf0
406 LSL Mh
407 LSL Mh
408 #define tmp _
409 MOV tmp, i2
410 LSR tmp
411 LSR tmp
412 OR Mh, tmp
413 #undef tmp
414 RCALL mod3
415 SUB t, n
416 NEG t
417 SUBI t, -8
418 RCALL g
419 LSR t
420 ANDI t, 3
421 INC s
422 #define tmp _
423 MOV tmp, s
424 LSR tmp
425 ADD tmp, s
426 ROR tmp
427 LSR tmp
428 LSR tmp
429 ADD tmp, s
430 ROR tmp
431 ADD tmp, s
432 ROR tmp
433 LSR tmp
434 LSR tmp
435 AND t, tmp
436 #undef tmp
437 ADD acc, t
438
439 SWAP acc ; acc<<4, to be passed to OCR0AL
440
441 SUBI i0, -1
442 SBCI i1, -1
443 SBCI i2, -1
444 SBCI i3, -1
445
446 CBI PORTB, 2 ; end runtime measurement
447 ;TODO: to reduce jitter: clear pending tim0_ovf (TIFR0[TOV0] <- 1) ?
448 RETI ; reenables interrupts
Imprint / Impressum