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