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