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