first test of pcint0 implementation
[Chiptunes.git] / foo.S
CommitLineData
f180febe 1/* REGISTER NAMES */
6d672b87
TG
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
f99fd6f3
TG
10#define x r24 //==a1==Mh
11#define _ r25 //==a2
12#define Xlo r26
13#define Xhi r27
ce618731 14#define one r28
f180febe
TG
15; r29
16; r30 Zlo
17; r31 Zhi
18; aliases:
ce618731
TG
19#define Ml t //mod3 vars
20#define Mh x // -"-
21#define a1 x //mul_ vars
22#define a2 _ // -"-
da32ed67 23
f180febe
TG
24/* I/O REGISTERS */
25OCR0AL = 0x26
26DDRB = 0x01
34fa6d04 27PORTB = 0x02
f180febe
TG
28PUEB = 0x03
29SPL = 0x3D
30SPH = 0x3E
31CCP = 0x3C
32CLKPSR = 0x36
2af726bc 33OSCCAL = 0x39
19e320a6 34WDTCSR = 0x31
f180febe
TG
35SMCR = 0x3A
36TCCR0A = 0x2E
37TCCR0B = 0x2D
38TIMSK0 = 0x2B
39TIFR0 = 0x2A
95d26108
TG
40PCMSK = 0x10
41PCIFR = 0x11
42PCICR = 0x12
43PRR = 0x35
f99fd6f3
TG
44RAMEND = 0x5F
45FLASHM = 0x4000
4466dd8b 46
f180febe 47.section .text
19e320a6 48.org 0x0000 ; RESET interrupt
cf36b4c3 49 CLR i0
19e320a6 50 RJMP main
95d26108
TG
51.org 0x0004 ; PCINT0 interrupt
52 CBI PCICR, 0 ; disable interrupt
53 RJMP pcint_handler
19e320a6
TG
54.org 0x0008 ; TIM0_OVF interrupt
55 RJMP sample
4466dd8b 56
f99fd6f3
TG
57notes:
58 .byte 0x84, 0x9d, 0xb0, 0x69, 0x9d, 0x84, 0x69, 0x58
59 .byte 0x75, 0x8c, 0xb0, 0x69, 0x8c, 0x75, 0x69, 0x58
60
f180febe 61mod3: ; mod3(Mh.Ml) -> t
8d8c00e4 62 #define tmp _
65aa7cd6
TG
63 ADD Ml, Mh
64 CLR Mh
6d672b87 65 ADC Mh, Mh ; store carry in Mh
65aa7cd6
TG
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
26799bab 84 BRCS skip
65aa7cd6
TG
85 SUBI Ml, 3
86 skip:
4283632d 87 RET
8d8c00e4 88 #undef tmp
f180febe 89
f180febe 90g: ; g(i, t) -> t
6d672b87
TG
91 CLR a1
92
d35c3d70 93 #define tmp _
6d672b87 94 #define zero a1
65aa7cd6
TG
95 ANDI t, 0x07
96 MOV tmp, i2
97 ANDI tmp, 3
65aa7cd6
TG
98 CPSE tmp, zero
99 SUBI t, -8
6d672b87 100 #undef zero
02f61e33 101 #undef tmp
4466dd8b 102
d44d4b47 103 LDI Xlo, lo8(notes)
95d26108 104 ADD Xlo, t ; NOTE: notes are positioned so that hi8(notes) never changes
d44d4b47 105 LD t, X
4466dd8b 106
986f12ae
TG
107 CLR a2
108
dd3193ab
TG
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
4466dd8b 160
2af726bc 161 MOV t, a1 ;;TODO: use a1 in loop: directly
ce618731 162 RET
61fab018 163
19e320a6 164main: ; setup routine
95d26108
TG
165 ; NOTE: clr i0 moved to .ord 0x0
166 CLR i1
167 CLR i2
65aa7cd6 168 CLR i3
19e320a6 169 CLR acc ; we output a dummy sample before the actual first one
f99fd6f3 170 LDI Xhi, hi8(FLASHM + notes) ; never changes
ce618731 171 LDI one, 1 ; mostly for clearing TIM0_OVF bit
19e320a6 172
6d672b87 173 #define zero i0
f99fd6f3 174 LDI x, RAMEND
19e320a6
TG
175 OUT SPL, x ; init stack ptr
176 OUT SPH, zero ; -"-
177 OUT PUEB, zero ; disable pullups
ea40b11f
TG
178 LDI x, 0x05
179 OUT DDRB, x ; PORTB0:pwm, PORTB2:debug
19e320a6
TG
180 LDI x, 0xd8
181 OUT CCP, x ; change protected ioregs
182 OUT CLKPSR, one ; clock prescaler 1/2 (4Mhz)
2af726bc
TG
183 LDI x, 0xa7 ; determined by trial-and-error (->PORTB2)
184 OUT OSCCAL, x ; set oscillator calibration
ce618731 185 OUT WDTCSR, zero; turn off watchdog
19e320a6
TG
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
19e320a6 193 SEI
6d672b87 194 #undef zero
19e320a6
TG
195
196loop:
197 SLEEP ; wait for interrupt
198 RJMP loop
199
200sample:
19e320a6 201 OUT OCR0AL, acc ; start by outputting a sample, because routine has variable runtime
ce618731 202#ifdef DEBUG
34fa6d04 203 SBI PORTB, 2 ; to measure runtime
ce618731 204#endif // DEBUG
19e320a6 205
65aa7cd6
TG
206 MOV n, i2
207 LSL n
208 LSL n
f6ef1520 209 #define tmp _
65aa7cd6
TG
210 MOV tmp, i1
211 SWAP tmp
212 ANDI tmp, 0x0f
213 LSR tmp
214 LSR tmp
215 OR n, tmp
f6ef1520 216 #undef tmp
65aa7cd6
TG
217 MOV s, i3
218 LSR s
219 ROR s
220 ANDI s, 0x80
f6ef1520 221 #define tmp _
65aa7cd6
TG
222 MOV tmp, i2
223 LSR tmp
224 OR s, tmp
f6ef1520 225 #undef tmp
3b86ca43 226
65aa7cd6
TG
227 ; voice 1:
228 MOV t, n
229 RCALL g
230 SWAP t
231 ANDI t, 1
232 MOV acc, t
3b86ca43 233
65aa7cd6 234 ; voice 2:
f6ef1520 235 #define tmp _
65aa7cd6
TG
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
f6ef1520 245 #undef tmp
65aa7cd6
TG
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
3b86ca43 253
65aa7cd6
TG
254 ; voice 3:
255 MOV Ml, i2
256 SWAP Ml
257 ANDI Ml, 0xf0
258 LSL Ml
f6ef1520 259 #define tmp _
65aa7cd6
TG
260 MOV tmp, i1
261 LSR tmp
262 LSR tmp
263 LSR tmp
264 OR Ml, tmp
f6ef1520 265 #undef tmp
65aa7cd6
TG
266 MOV Mh, i3
267 SWAP Mh
268 ANDI Mh, 0xf0
269 LSL Mh
f6ef1520 270 #define tmp _
65aa7cd6
TG
271 MOV tmp, i2
272 LSR tmp
273 LSR tmp
274 LSR tmp
275 OR Mh, tmp
f6ef1520 276 #undef tmp
65aa7cd6
TG
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
f6ef1520 285 #define tmp _
65aa7cd6
TG
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
f6ef1520 299 #undef tmp
65aa7cd6 300 ADD acc, t
f6ef1520 301
65aa7cd6
TG
302 ; voice 4:
303 MOV Ml, i2
304 SWAP Ml
305 ANDI Ml, 0xf0
306 LSL Ml
307 LSL Ml
f6ef1520 308 #define tmp _
65aa7cd6
TG
309 MOV tmp, i1
310 LSR tmp
311 LSR tmp
312 OR Ml, tmp
f6ef1520 313 #undef tmp
65aa7cd6
TG
314 MOV Mh, i3
315 SWAP Mh
316 ANDI Mh, 0xf0
317 LSL Mh
318 LSL Mh
f6ef1520 319 #define tmp _
65aa7cd6
TG
320 MOV tmp, i2
321 LSR tmp
322 LSR tmp
323 OR Mh, tmp
f6ef1520 324 #undef tmp
65aa7cd6
TG
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
f6ef1520 333 #define tmp _
65aa7cd6
TG
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
f6ef1520 347 #undef tmp
65aa7cd6 348 ADD acc, t
3b86ca43 349
19e320a6
TG
350 SWAP acc ; acc<<4, to be passed to OCR0AL
351
f6ef1520
TG
352 SUBI i0, -1
353 SBCI i1, -1
354 SBCI i2, -1
355 SBCI i3, -1
bfce2f8c 356
95d26108
TG
357 CPI i2, 0x78 ; 16m23 -- one loop
358 BREQ halt
359continue:
50cc7c09 360
ce618731
TG
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)
19e320a6 365 RETI ; reenables interrupts
50cc7c09
TG
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
95d26108 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.
50cc7c09
TG
379
380halt:
381 ;set all pins as input & enable pullups and disable periphials to conserve energy
95d26108
TG
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
50cc7c09 388
95d26108
TG
389 SBI PCMSK, 0 ; enable PCINT0 on PB0 (pin1; also audio out)
390 SBI PCICR, 0 ; enable PCINT0
391
50cc7c09 392 ;enter power-down-mode
95d26108
TG
393 LDI x, 0x05 ; power-down mode:____010_ ; enable:_______1
394 OUT SMCR, x
395 CLR x
50cc7c09 396 SLEEP
95d26108
TG
397 OUT SMCR, x
398
399 RJMP continue
400
401
402pcint_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
50cc7c09 412
95d26108 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