]> git.gir.st - Chiptunes-pms150c.git/blob - bsv.S
save 1 instruction cycle
[Chiptunes-pms150c.git] / bsv.S
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 ; cycle count (worst-case)
58 ; mod3: 28
59 ; g: 81
60 ; sample: 115 + 4*g + 2*mod3 = 495
61
62 ; portA.4: audio out
63 ; portA.6: debug pin
64
65
66 .area CSEG (CODE,ABS)
67 .org 0x0000
68 GOTO init
69
70 .org 0x0020
71 GOTO interrupt
72
73 mod3:
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
106 g:
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
193 init:
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:
253 CLEAR i0
254 CLEAR i1
255 CLEAR i2
256
257 ;rom is not mmapped; must load notes into ram first
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
282 ENGINT
283
284 loop:
285 ;TODO: test i2==0x78 to enter halt()
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).
293 GOTO loop
294
295 interrupt:
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
306 ;TODO: send pwm data to timer2
307 MOV a, pwm
308 ADD a, #4
309 MOV tm2b, a
310
311 ; generate new sample:
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
423 ADD a, pwm
424
425 SWAP a
426 MOV pwm, a
427 ; next sample is now ready.
428
429 INC i0
430 ADDC i1
431 ADDC i2
432
433 SET1 pa, #6 ; debug
434 ivr_end:
435 POP af
436 RETI
Imprint / Impressum