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