0e3f727fbdffbd3e4c02c0af5611bdfab36c247c
[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 #define one r28
15 ; r29
16 ; r30 Zlo
17 ; r31 Zhi
18 ; aliases:
19 #define Ml t //mod3 vars
20 #define Mh x // -"-
21 #define a1 x //mul_ vars
22 #define a2 _ // -"-
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 OSCCAL = 0x39
34 WDTCSR = 0x31
35 SMCR = 0x3A
36 TCCR0A = 0x2E
37 TCCR0B = 0x2D
38 TIMSK0 = 0x2B
39 TIFR0 = 0x2A
40 PCMSK = 0x10
41 PCIFR = 0x11
42 PCICR = 0x12
43 PRR = 0x35
44 RAMEND = 0x5F
45 FLASHM = 0x4000
46
47 .section .text
48 .org 0x0000 ; RESET interrupt
49 CLR i0
50 RJMP main
51 .org 0x0004 ; PCINT0 interrupt
52 CBI PCICR, 0 ; disable interrupt
53 RJMP pcint_handler
54 .org 0x0008 ; TIM0_OVF interrupt
55 RJMP sample
56
57 notes:
58 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
59 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
60
61 mod3: ; mod3(Mh.Ml) -> t
62 #define tmp _
63 ADD Ml, Mh
64 CLR Mh
65 ADC Mh, Mh ; store carry in Mh
66 MOV tmp, Ml
67 SWAP tmp
68 ANDI tmp, 0x0f
69 SWAP Mh
70 OR tmp, Mh
71 ANDI Ml, 0x0f
72 ADD Ml, tmp
73 MOV tmp, Ml
74 LSR tmp
75 LSR tmp
76 ANDI Ml, 0x03
77 ADD Ml, tmp
78 MOV tmp, Ml
79 LSR tmp
80 LSR tmp
81 ANDI Ml, 0x03
82 ADD Ml, tmp
83 CPI Ml, 3
84 BRCS skip
85 SUBI Ml, 3
86 skip:
87 RET
88 #undef tmp
89
90 g: ; g(i, t) -> t
91 CLR a1
92
93 #define tmp _
94 #define zero a1
95 ANDI t, 0x07
96 MOV tmp, i2
97 ANDI tmp, 3
98 CPSE tmp, zero
99 SUBI t, -8
100 #undef zero
101 #undef tmp
102
103 LDI Xlo, lo8(notes)
104 ADD Xlo, t ; NOTE: notes are positioned so that hi8(notes) never changes
105 LD t, X
106
107 CLR a2
108
109 ; begin of mulitiplication:
110 LSR t
111 BRCC skip1
112 ADD a1, i0
113 ADC a2, i1
114 skip1:
115 LSR a2
116 ROR a1
117 LSR t
118 ; BRCC skip2 -- this bit is always zero
119 ; ADD a1, i0
120 ; ADC a2, i1
121 ;skip2:
122 LSR a2
123 ROR a1
124 LSR t
125 BRCC skip3
126 ADD a1, i0
127 ADC a2, i1
128 skip3:
129 LSR a2
130 ROR a1
131 LSR t
132 BRCC skip4
133 ADD a1, i0
134 ADC a2, i1
135 skip4:
136 LSR a2
137 ROR a1
138 LSR t
139 BRCC skip5
140 ADD a1, i0
141 ADC a2, i1
142 skip5:
143 LSR a2
144 ROR a1
145 LSR t
146 BRCC skip6 ;sbrc t, NNN
147 ADD a1, i0
148 skip6:
149 LSR a1
150 LSR t
151 BRCC skip7
152 ADD a1, i0
153 skip7:
154 LSR a1
155 LSR t
156 BRCC skip8
157 ADD a1, i0
158 skip8:
159 LSR a1
160
161 MOV t, a1 ;;TODO: use a1 in loop: directly
162 RET
163
164 main: ; setup routine
165 ; NOTE: clr i0 moved to .ord 0x0
166 CLR i1
167 CLR i2
168 CLR i3
169 CLR acc ; we output a dummy sample before the actual first one
170 LDI Xhi, hi8(FLASHM + notes) ; never changes
171 LDI one, 1 ; mostly for clearing TIM0_OVF bit
172
173 #define zero i0
174 LDI x, RAMEND
175 OUT SPL, x ; init stack ptr
176 OUT SPH, zero ; -"-
177 OUT PUEB, zero ; disable pullups
178 LDI x, 0x05
179 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
180 LDI x, 0xd8
181 OUT CCP, x ; change protected ioregs
182 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
183 LDI x, 0xa7 ; determined by trial-and-error (->PORTB2)
184 OUT OSCCAL, x ; set oscillator calibration
185 OUT WDTCSR, zero; turn off watchdog
186
187 ;set timer/counter0 to 8bit fastpwm, non-inverting, no prescaler
188 LDI x, 0x81
189 OUT TCCR0A, x
190 LDI x, 0x09
191 OUT TCCR0B, x
192 OUT TIMSK0, one ; enable tim0_ovf
193 SEI
194 #undef zero
195
196 loop:
197 SLEEP ; wait for interrupt
198 RJMP loop
199
200 sample:
201 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
202 #ifdef DEBUG
203 SBI PORTB, 2 ; to measure runtime
204 #endif // DEBUG
205
206 MOV n, i2
207 LSL n
208 LSL n
209 #define tmp _
210 MOV tmp, i1
211 SWAP tmp
212 ANDI tmp, 0x0f
213 LSR tmp
214 LSR tmp
215 OR n, tmp
216 #undef tmp
217 MOV s, i3
218 LSR s
219 ROR s
220 ANDI s, 0x80
221 #define tmp _
222 MOV tmp, i2
223 LSR tmp
224 OR s, tmp
225 #undef tmp
226
227 ; voice 1:
228 MOV t, n
229 RCALL g
230 SWAP t
231 ANDI t, 1
232 MOV acc, t
233
234 ; voice 2:
235 #define tmp _
236 MOV tmp, i2
237 LSL tmp
238 LSL tmp
239 LSL tmp
240 MOV t, i1
241 SWAP t
242 ANDI t, 0xf
243 LSR t
244 OR t, tmp
245 #undef tmp
246 EOR t, n
247 RCALL g
248 LSR t
249 LSR t
250 ANDI t, 3
251 AND t, s
252 ADD acc, t
253
254 ; voice 3:
255 MOV Ml, i2
256 SWAP Ml
257 ANDI Ml, 0xf0
258 LSL Ml
259 #define tmp _
260 MOV tmp, i1
261 LSR tmp
262 LSR tmp
263 LSR tmp
264 OR Ml, tmp
265 #undef tmp
266 MOV Mh, i3
267 SWAP Mh
268 ANDI Mh, 0xf0
269 LSL Mh
270 #define tmp _
271 MOV tmp, i2
272 LSR tmp
273 LSR tmp
274 LSR tmp
275 OR Mh, tmp
276 #undef tmp
277 RCALL mod3
278 ADD t, n
279 RCALL g
280 LSR t
281 LSR t
282 ANDI t, 3
283 MOV x, s
284 INC x
285 #define tmp _
286 MOV tmp, x
287 LSR tmp
288 LSR tmp
289 ADD tmp, x
290 ROR tmp
291 LSR tmp
292 ADD tmp, x
293 ROR tmp
294 LSR tmp
295 ADD tmp, x
296 ROR tmp
297 LSR tmp
298 AND t, tmp
299 #undef tmp
300 ADD acc, t
301
302 ; voice 4:
303 MOV Ml, i2
304 SWAP Ml
305 ANDI Ml, 0xf0
306 LSL Ml
307 LSL Ml
308 #define tmp _
309 MOV tmp, i1
310 LSR tmp
311 LSR tmp
312 OR Ml, tmp
313 #undef tmp
314 MOV Mh, i3
315 SWAP Mh
316 ANDI Mh, 0xf0
317 LSL Mh
318 LSL Mh
319 #define tmp _
320 MOV tmp, i2
321 LSR tmp
322 LSR tmp
323 OR Mh, tmp
324 #undef tmp
325 RCALL mod3
326 SUB t, n
327 NEG t
328 SUBI t, -8
329 RCALL g
330 LSR t
331 ANDI t, 3
332 INC s
333 #define tmp _
334 MOV tmp, s
335 LSR tmp
336 ADD tmp, s
337 ROR tmp
338 LSR tmp
339 LSR tmp
340 ADD tmp, s
341 ROR tmp
342 ADD tmp, s
343 ROR tmp
344 LSR tmp
345 LSR tmp
346 AND t, tmp
347 #undef tmp
348 ADD acc, t
349
350 SWAP acc ; acc<<4, to be passed to OCR0AL
351
352 SUBI i0, -1
353 SBCI i1, -1
354 SBCI i2, -1
355 SBCI i3, -1
356
357 CPI i2, 0x78 ; 16m23 -- one loop
358 BREQ halt
359 continue:
360
361 #ifdef DEBUG
362 CBI PORTB, 2 ; end runtime measurement
363 #endif // DEBUG
364 OUT TIFR0, one ; clear pending interrupt (routine takes two intr.cycles)
365 RETI ; reenables interrupts
366
367 ; POWER SAVING MODE
368 ;
369 ; To reduce physical size, we won't use a latching power switch. Instead,
370 ; playback will stop after a full iteration (16m23s). Then, a momentary
371 ; button will trigger a RESET, INT0 or PCINT0 interrupt to resume
372 ; playback. Meanwhile, all periphials will be shut down and the clock
373 ; turned off. The watchdog is permanently off to reduce power consumption.
374 ; Note: it is not necessary to switch the clock source from the 8MHz
375 ; oscillator to the 128kHz one, since power-down mode disables the clock
376 ; completely.
377
378 ; TODO: if we are using pcint on the audio-out pin, we could instruct the user to short the audio plug to ground to start playback (which should be triggered by a connected audio source). this would eliminate the button (and associated wiring) completely.
379
380 halt:
381 ;set all pins as input & enable pullups and disable periphials to conserve energy
382 LDI x, 0x00
383 OUT DDRB, x
384 LDI x, 0x0f
385 OUT PUEB, x
386 LDI x, 0x3 ; bit0: disable Timer0, bit1: disable attiny5/10's ADC
387 OUT PRR, x
388
389 SBI PCMSK, 0 ; enable PCINT0 on PB0 (pin1; also audio out)
390 SBI PCICR, 0 ; enable PCINT0
391
392 ;enter power-down-mode
393 LDI x, 0x05 ; power-down mode:____010_ ; enable:_______1
394 OUT SMCR, x
395 CLR x
396 SLEEP
397 OUT SMCR, x
398
399 RJMP continue
400
401
402 pcint_handler:
403 SBI PCIFR, 0 ; clear interrupt
404 LDI x, 0x05
405 OUT DDRB, x
406 LDI x, 0x00
407 OUT PUEB, x
408 LDI x, 0x02
409 OUT PRR, x ; reset prr (not using zero, so devices with adc keep it disabled)
410 ; TODO: should probably reset i0/i1/i2
411 RETI
412
413 ; XXX: 14 byte too large; should probably purge i3, rerun fakeasm-tests with length reduced to 7864320. a dedicated five-register will save 2 instructions, too.
Imprint / Impressum