diff --git a/pcm-io.s b/pcm-io.s new file mode 100644 index 0000000..e5887aa --- /dev/null +++ b/pcm-io.s @@ -0,0 +1,171 @@ + + /* PCM channel increment */ + .equ PCM_FDL, 0xFF0005 + .equ PCM_FDH, 0xFF0007 + + /* General use timer */ + .equ TIMER, 0x8030 + .equ INT_MASK, 0x8032 + .equ _LEVEL3, 0x5F82 /* TIMER INTERRUPT jump vector */ + + + .text + .align 2 + + +| uint8_t pcm_lcf(uint8_t pan); +| Convert pan setting to simple linear crossfade volume settings + .global pcm_lcf +pcm_lcf: + move.l 4(sp),d0 /* pan */ + addq.b #8,d0 + bcc.b 0f + move.l 4(sp),d0 +0: + andi.w #0x00F0,d0 /* right pan volume nibble */ + + move.l 4(sp),d1 + not.b d1 + addq.b #8,d1 + bcc.b 1f + move.l 4(sp),d1 + not.b d1 +1: + lsr.b #4,d1 /* left pan volume nibble */ + or.b d1,d0 + rts + + +| void pcm_delay(void); +| Delay after writing PCM register + .global pcm_delay +pcm_delay: + moveq #20,d1 +0: + dbra d1,0b + rts + + +| void pcm_set_period(uint32_t period); + .global pcm_set_period +pcm_set_period: + move.l 4(sp),d1 + cmpi.l #4,d1 + blo.b 0f /* saturate increment to 65535 */ + addq.l #1,d1 + move.l #446304,d0 + divu.w d1,d0 + lsr.w #1,d0 /* incr = (446304 / Period + 1) >> 1 */ + bra.b 1f +0: + move.w #65535,d0 +1: + move.b d0,PCM_FDL.l + bsr.b pcm_delay + lsr.w #8,d0 + move.b d0,PCM_FDH.l + bra.b pcm_delay + + +| void pcm_set_freq(uint32_t freq); + .global pcm_set_freq +pcm_set_freq: + move.l 4(sp),d0 + cmpi.l #1041648,d0 + bhs.b 0f /* saturate increment to 65535 */ + lsl.l #8,d0 + lsl.l #3,d0 /* shift freq for fixed point result */ + move.w #32552,d1 + divu.w d1,d0 /* incr = (freq << 11) / 32552 */ + bra.b 1f +0: + move.w #65535,d0 +1: + move.b d0,PCM_FDL.l + bsr.b pcm_delay + lsr.w #8,d0 + move.b d0,PCM_FDH.l + bra.b pcm_delay + + +| void pcm_set_timer(uint16_t bpm); +| TIMER = 32552 / (bpm * 2) - 1 +| should be 32552 * 5 / (bpm * 2) - 1, but that's too big for TIMER, so +| we leave out the * 5 and compensate in the int handler + .global pcm_set_timer +pcm_set_timer: + move.l #32552,d0 + move.l 4(sp),d1 + add.w d1,d1 + beq.b 0f /* safety check... passing 0 will just exit */ + divu.w d1,d0 + subq.w #1,d0 + ble.b 0f /* safety check */ + move.w d0,TIMER.w +0: + rts + + +| void pcm_stop_timer(void); + .global pcm_stop_timer +pcm_stop_timer: + move #0x2700,sr /* disable interrupts */ + + move.w #0,TIMER.w /* stop General Timer */ + move.w INT_MASK.w,d0 + andi.w #0xFFF7,d0 + move.w d0,INT_MASK.w /* disable General Timer interrupt */ + + move #0x2000,sr /* enable interrupts */ + rts + + +| void pcm_start_timer(void (*callback)(void)); + .global pcm_start_timer +pcm_start_timer: + move #0x2700,sr /* disable interrupts */ + + move.l 4(sp),int3_callback /* set callback vector */ + move.w #0,int3_cntr /* clear int counter */ + + move.w #0x4EF9,_LEVEL3.w + move.l #timer_int,_LEVEL3+2.w /* set level 3 int vector for timer */ + + move.w #129,TIMER.w /* 125 BPM */ + move.w INT_MASK.w,d0 + ori.w #0x0008,d0 + move.w d0,INT_MASK.w /* enable General Timer interrupt */ + + move #0x2000,sr /* enable interrupts */ + rts + + +timer_int: + move.l d0,-(sp) + move.w int3_cntr,d0 + addq.w #1,d0 + cmpi.w #5,d0 + blo.b 0f /* once every 5 ints for actual beats per minute rate */ + + movem.l d1/a0-a1,-(sp) + movea.l int3_callback,a1 + jsr (a1) + movem.l (sp)+,d1/a0-a1 + moveq #0,d0 +0: + move.w d0,int3_cntr + move.l (sp)+,d0 + rte + + + .data + + + .align 4 + +int3_callback: + .long 0 + +int3_cntr: + .word 0 +