]> git.gir.st - Chiptunes-pms150c.git/blob - bsv.asm
e83e3a4cd32f32ce4043cdb1859683534424daef
[Chiptunes-pms150c.git] / bsv.asm
1 .area FUSE (ABS)
2 .org 0x3ff*2
3 .word ( 0x0260 | 1<<0 | 5<<2 | 1<<7 | 3<<10 )
4 ; reserved_bits | security_off | lvr_1v8 | io_drv_norm | boot_fast
5
6 .area OSEG (OVR,DATA)
7 notes: .ds 16 ; 0x00 .. 0x0f
8 i0: .ds 1 ; 0x10
9 i1: .ds 1 ; 0x11
10 i2: .ds 1 ; 0x12
11 n: .ds 1 ; 0x13
12 .even ; make next two bytes word-aligned
13 zero: .ds 1 ; 0x14
14 tmp_1: .ds 1 ; 0x15
15 tmp_hi: .ds 1 ; 0x16
16 tmp_lo: .ds 1 ; 0x17
17 pwm: .ds 1 ; 0x18
18
19 .even ; SP must be aligned
20 stack_start: .ds 1
21 .area SSEG
22 stack: .ds 1
23
24 ; aliases for memory locations:
25 notes_ix = tmp_1
26 t = tmp_1
27 mul2 = tmp_hi
28 mul1 = tmp_lo
29 mod3hi = tmp_hi
30 mod3lo = tmp_lo
31
32 ; io addresses
33 clkmd = 0x03
34 inten = 0x04
35 intrq = 0x05
36 tm2c = 0x1C
37 tm2b = 0x09
38 tm2s = 0x17
39 t16m = 0x06
40 eoscr = 0x0A
41 padier = 0x0D
42 pa = 0x10
43 pac = 0x11
44 paph = 0x12
45 misc = 0x1B
46 gpcc = 0x1A
47 ihrcr = 0x0B
48
49 ; Calibration Parameters:
50 ; Bitshift Variations calls for an 8kHz sample rate; with an interrupt every
51 ; 512 cycles (the next power of two above the 495 cycles the program needs for
52 ; execution), this gives us a clock speed of 512 * 8khz = 4.096MHz. The MCU
53 ; will be powered by a 3V lithium coin cell. (+10-15ish cycles ivr overhead)
54 calib_freq = 4096000 ; Hz
55 calib_vdd = 3000 ; mV
56
57 ; Clock Parameters:
58 ; during playback: IHRC/4, WDT off, keep ILRC on
59 active_clock = (( 0<<5 | 1<<4 | 0<<3 | 1<<2 | 0<<1 | 0<<0 ))
60 ; during deep-sleep: ILRC/1, WDT off
61 sleep_clock = (( 7<<5 | 1<<4 | 0<<3 | 1<<2 | 0<<1 | 0<<0 ))
62 ; for extra power saving, consider: 6<<5|0<<3 for ilrc/6, 2<<5|1<<3 for ilrc/16
63
64 ; cycle count (worst-case)
65 ; mod3: 28
66 ; g: 81
67 ; sample: 115 + 4*g + 2*mod3 = 495
68
69 ; portA.4: audio out
70 ; portA.6: debug pin
71
72
73 .area CSEG (CODE,ABS)
74 .org 0x0000
75 GOTO init
76
77 .org 0x0020
78 GOTO interrupt
79
80 mod3:
81 MOV a, mod3hi
82 ADD mod3lo, a ; mod3lo = hi+lo
83 MOV a, #0
84 ADDC a ; mod3hi, 1bit
85 SWAP a
86 MOV mod3hi, a
87
88 MOV a, mod3lo
89 SWAP a
90 AND a, #0xf ; (mod3lo>>4)
91 XCH mod3lo ; a=mod3lo, mod3lo=mod3lo>>4
92 AND a, #0xF ; a=mod3lo&0xf, mod3lo=mod3lo>>4
93 ADD a, mod3lo ; (mod3lo & 0xF)
94 ADD a, mod3hi
95 MOV mod3lo, a
96
97 AND a, #0x3 ; a = (mod3lo & 0x3)
98 SR mod3lo
99 SR mod3lo ; (mod3lo >> 2)
100 ADD a, mod3lo
101 MOV mod3lo, a
102
103 AND a, #0x3 ; a = (mod3lo & 0x3)
104 SR mod3lo
105 SR mod3lo ; (mod3lo >> 2)
106 ADD a, mod3lo
107
108 SUB a, #3
109 T0SN f, c
110 ADD a, #3
111 RET
112
113 g:
114 ; notes_ix_hi = always 0
115 AND a, #0x7
116 MOV notes_ix, a
117 ; test i2 & 3:
118 MOV a, i2
119 AND a, #3
120 T0SN f, z
121 SET1 notes_ix, #3
122 IDXM a, notes_ix
123
124 MOV t, a
125 CLEAR mul2
126 CLEAR mul1
127 ; note: LSB of result (mul0) is not needed for our purposes
128 ;;1/8:
129 SR t
130 T1SN f, c
131 GOTO skip1
132 MOV a, i0
133 ADD mul1, a
134 MOV a, i1
135 ADDC mul2, a
136 skip1: SR mul2
137 SRC mul1
138 ;;2/8:
139 SR t
140 skip2: SR mul2
141 SRC mul1
142 ;;3/8:
143 SR t
144 T1SN f, c
145 GOTO skip3
146 MOV a, i0
147 ADD mul1, a
148 MOV a, i1
149 ADDC mul2, a
150 skip3: SR mul2
151 SRC mul1
152 ;;4/8:
153 SR t
154 T1SN f, c
155 GOTO skip4
156 MOV a, i0
157 ADD mul1, a
158 MOV a, i1
159 ADDC mul2, a
160 skip4: SR mul2
161 SRC mul1
162 ;;5/8:
163 SR t
164 T1SN f, c
165 GOTO skip5
166 MOV a, i0
167 ADD mul1, a
168 MOV a, i1
169 ADDC mul2, a
170 skip5: SR mul2
171 SRC mul1
172 ;;6/8:
173 SR t
174 T1SN f, c
175 GOTO skip6
176 MOV a, i0
177 ADD mul1, a
178 skip6:
179 SRC mul1
180 ;;7/8:
181 SR t
182 T1SN f, c
183 GOTO skip7
184 MOV a, i0
185 ADD mul1, a
186 skip7:
187 SRC mul1
188 ;;8/8:
189 SR t
190 T1SN f, c
191 GOTO skip8
192 MOV a, i0
193 ADD mul1, a
194 skip8:
195 SRC mul1
196
197 MOV a, mul1
198 RET
199
200 init:
201 ; clock setup:
202 SET1 clkmd, #4 ; enable IHRC
203 MOV a, #active_clock
204 MOV clkmd, a ; switch to IHRC
205
206 ;; .org 0x46 ; comment out on 2nd iteration
207 ; calibration placeholder:
208 AND a, #'R'
209 AND a, #'C'
210 AND a, #1 ; IHRC
211 AND a, #( calib_freq )
212 AND a, #( calib_freq>>8 )
213 AND a, #( calib_freq>>16 )
214 AND a, #( calib_freq>>24 )
215 AND a, #( calib_vdd )
216 AND a, #( calib_vdd>>8 )
217 AND a, #ihrcr
218 ;; .org 0x5a
219
220 ;stack setup:
221 MOV a, #stack_start
222 MOV sp, a
223
224 ; portA setup:
225 MOV a, #0x50 ; data direction: PWM & debug output, rest input
226 MOV pac, a ; (conserves power, apparently)
227 MOV a, #(( 1<<4 ))
228 MOV padier, a ; disable pin wakeup, except on audio pin
229 MOV pa, a ; PortA data = 0
230 MOV paph, a ; disable all pull-ups
231
232 ; timer2/pwm setup:
233 ; Since (unlike in the ATTiny4 version) the interrupt timer is not tied
234 ; to the PWM frequency, we can use a much faster clock for PWM. The
235 ; highest "carrier frequency" for the PCM samples we can generate is by
236 ; setting Timer2 to 6 bit, (IHRC/1)/1 mode, giving a frequency of
237 ; (4*4.096MHz)/2^6 = 256kHz.
238 MOV pwm, a ; clear
239 MOV tm2b, a ; clear
240 MOV a, #(( 2<<4 | 3<<2 | 1<<1 | 0<<0 ))
241 MOV tm2c, a ; timer2: IHRC, PA4, PWM, not inverted
242 MOV a, #(( 0<<7 | 1<<5 | 0<<0 ))
243 MOV tm2s, a ; 8bit, /4 prescaler, divide by (0+1)
244
245 ;timer16/ivr setup
246 ;mov a, #(( 0<<0 | 1<<3 | 4<<5 )) ; ovf@bit8 (512cy; §9.2.5), clk/4, ihrc
247 MOV a, #(( 1<<0 | 1<<3 | 4<<5 )) ; ovf@bit9 (???cy; §9.2.5), clk/4, ihrc
248 ;XXX: datasheet §5.10.1 says bit8 = 256cycles, 9.2.5 says bit8=512cy
249 ; note: ovf@bit9 causes 4khz isr => we need ovf@bit8.
250 MOV t16m, a
251 MOV a, #(1<<2) ; enable timer16 int, disable all others
252 MOV inten, a
253
254 ; misc setup:
255 SET1 eoscr, #0 ; disable bandgap and lvr
256 SET0 gpcc, #7 ; disable comparator
257
258 ; memory setup:
259 CLEAR i0
260 CLEAR i1
261 CLEAR i2
262
263 ;rom is not mmapped; must load notes into ram first
264 MOV a, #0x84
265 MOV notes+0x0, a
266 MOV notes+0x5, a
267 MOV a, #0x9d
268 MOV notes+0x1, a
269 MOV notes+0x4, a
270 MOV a, #0xb0
271 MOV notes+0x2, a
272 MOV notes+0xA, a
273 MOV a, #0x69
274 MOV notes+0x3, a
275 MOV notes+0x6, a
276 MOV notes+0xB, a
277 MOV notes+0xE, a
278 MOV a, #0x58
279 MOV notes+0x7, a
280 MOV notes+0xF, a
281 MOV a, #0x75
282 MOV notes+0x8, a
283 MOV notes+0xD, a
284 MOV a, #0x8c
285 MOV notes+0x9, a
286 MOV notes+0xC, a
287
288 ENGINT
289
290 loop:
291 MOV a, i2
292 CEQSN a, #0x78 ; compare, skip next if equal
293 ; Note: usually, this is the place where the MCU is put into some
294 ; sort of low power/sleep mode. But the Padauk's stopexe instruction
295 ; causes the ISR to a) run at greatly reduced frequency (100hz vs
296 ; 1khz for timer16@bit11; probably due to slow wakeup), b)
297 ; double-fire some (20-30%) of the time, c) jitter -50% to +10%. so
298 ; we don't sleep at all between samples (which is only a short time
299 ; anyways).
300 GOTO loop
301
302 ; at this point, i2==0x78, i.e. the music is finished.
303 ; => goto halt (fallthrough)
304 halt:
305 DISGINT
306 CLEAR i2 ; clear halting signal
307
308 ; Note: disabling the timers isn't strictly necessary (as stopsys halts
309 ; all timers anyways), but I'm hoping it may reduce power consumption.
310 ; We're lucky that we only need to toggle a single bit to switch
311 ; between the required clock source and 'off' (0010xxxx->0000xxxx for
312 ; timer2, 100xxxxx->000xxxxx for timer16), so we can hack our way out
313 ; of loading an immediate each time.
314 SET0 tm2c, #5
315 SET0 t16m, #7
316
317 SET1 pa, #4 ; assert a high level on the audio pin for good measure
318 SET0 pac, #4 ; ... before setting it to input mode (optional)
319
320 ;switch to ilrc clock
321 MOV a, #sleep_clock
322 MOV clkmd, a
323 SET0 clkmd, #4 ; disable ihrc
324
325 STOPSYS
326 ; (at this point, we wait for an i/o-toggle wake up event to resume execution)
327
328 MOV a, #active_clock
329 MOV clkmd, a ; switch to IHRC again
330
331 SET1 pac, #4 ; restore output mode for audio pin
332
333 ;reenable timer16, timer2
334 SET1 tm2c, #5
335 SET1 t16m, #7
336
337 ENGINT
338 GOTO loop
339
340 interrupt:
341 PUSH af
342 T1SN intrq, #2 ; if intrq.t16 is triggered, skip next
343 GOTO ivr_end
344
345 ;clear t16int:
346 SET0 intrq, #2
347
348 SET1 pa, #6 ; debug
349
350 ; send pwm data to timer2:
351 MOV a, pwm
352 ADD a, #4
353 MOV tm2b, a
354
355 ; generate new sample:
356 MOV a, i2; "mov mem,mem"
357 MOV n, a; does not exist
358 SL n
359 SL n
360 MOV a, i1
361 SWAP a
362 AND a, #0xf
363 SR a
364 SR a
365 OR n, a
366
367 MOV a, n
368 CALL g
369 SWAP a
370 AND a, #0x1
371 MOV pwm, a
372
373 MOV a, i2
374 SL a
375 SL a
376 SL a
377 MOV tmp_1, a ; fresh tmp_1:
378 MOV a, i1
379 SWAP a
380 AND a, #0xf
381 SR a
382 OR a, tmp_1 ; tmp_1 done.
383 XOR a, n
384 CALL g
385 SR a
386 AND a, i2
387 SR a
388 AND a, #3
389 ADD pwm, a
390
391 MOV a, i2
392 MOV mod3hi, a
393 SR mod3hi
394 SR mod3hi
395 SR mod3hi
396 SWAP a
397 AND a, #0xf0
398 SL a
399 MOV mod3lo, a
400 MOV a, i1
401 SR a
402 SR a
403 SR a
404 OR mod3lo, a
405 CALL mod3
406 ADD a, n
407 CALL g
408 SR a
409 SR a
410 MOV tmp_1, a ; a saved in tmp_1; fresh a
411 MOV a, i2
412 ; shift-divide by six
413 ; note: i2 is max 0x78; so a will <= 20. (breaks vor values >=128)
414 SR a
415 ADD a, i2
416 SR a
417 SR a
418 ADD a, i2
419 SR a
420 SR a
421 ADD a, i2
422 SR a
423 SR a
424 SR a
425 ; end divide by six
426 AND a, tmp_1 ; a restored from tmp_1
427 AND a, #3
428 ADD pwm, a
429
430 MOV a, i2
431 MOV mod3hi, a
432 SR mod3hi
433 SR mod3hi
434 SWAP a
435 AND a, #0xf0
436 SL a
437 SL a
438 MOV mod3lo, a
439 MOV a, i1
440 SR a
441 SR a
442 OR mod3lo, a
443 CALL mod3
444 SUB a, n
445 SUB a, #8
446 NEG a
447 CALL g
448 SR a
449 MOV tmp_1, a ; a saved in tmp_1; fresh a
450 MOV a, i2
451 ; shift-divide by ten
452 ; note: i2 is max 0x78; so a will <= 12.
453 INC i2
454 SR a
455 ADD a, i2
456 SR a
457 SR a
458 SR a
459 ADD a, i2
460 SR a
461 ADD a, i2
462 SWAP a
463 DEC i2
464 ; end divide by ten
465 AND a, tmp_1 ; a restored from tmp_1
466 AND a, #3
467 ADD a, pwm
468
469 SWAP a
470 MOV pwm, a
471 ; next sample is now ready.
472
473 INC i0
474 ADDC i1
475 ADDC i2
476
477 SET1 pa, #6 ; debug
478 ivr_end:
479 POP af
480 RETI
Imprint / Impressum