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