Tridora-CPU/lib/pcmaudio.s
slederer 536c0adde7 pcmaudio: set amplitude to biased zero at end
pcmtest2: small updates to the demo program
2025-10-12 22:52:17 +02:00

247 lines
3.9 KiB
ArmAsm

.EQU AUDIO_BASE $A00
.EQU IRQC_REG $980
.EQU IRQC_EN $80
; args: sample rate
START_PCMAUDIO:
; calculate clock divider
LOADCP 77000000
SWAP
LOADCP _DIV
CALL
LOADC AUDIO_BASE + 1
SWAP ; put clock divider on ToS
; LOADCP 4812 ; clock divider for 16KHz sample rate
; LOADCP 2406 ; clock divider for 32KHz sample rate
STOREI 1
LOADCP 32768 ; set amplitude to biased 0
STOREI
DROP
LOADC AUDIO_BASE
LOADC 17 ; enable channel, enable interrupt
STOREI
DROP
RET
STOP_AUDIO:
LOADC AUDIO_BASE
LOADC 0
STOREI
DROP
RET
; args: pointer to pascal string, sample rate
.EQU PS_PTR 0
.EQU PS_COUNT 4
.EQU PS_FS 12
PLAYSAMPLE:
FPADJ -PS_FS
LOADCP START_PCMAUDIO
CALL
DUP
LOADI ; get string size from header
SHR ; divide by 4 to get word count
SHR
STORE PS_COUNT
INC 8 ; skip rest of header
STORE PS_PTR ; store sample data pointer
PS_L0:
LOAD PS_PTR ; load pointer
INC.S1.X2Y 4 ; increment and keep old value
STORE PS_PTR ; store incremented value
LOADI ; load 32 bit word
DUP
BROT ; get upper 16 bit word
BROT
LOADCP $FFFF
AND
LOADCP PLAY_1SAMPLE
CALL
LOADCP $FFFF ; get lower 16 bit word
AND
LOADCP PLAY_1SAMPLE
CALL
LOAD PS_COUNT ; load word count
DEC 1 ; decrement
DUP
STORE PS_COUNT
CBRANCH.NZ PS_L0 ; loop if not zero
LOADCP STOP_AUDIO
CALL
FPADJ PS_FS
RET
; play one sample, waiting
; for the clock divider, which
; is visible via the phase flag
; args: 16-bit unsigned sample
PLAY_1SAMPLE:
PLAY1_L0:
LOADC AUDIO_BASE
LOADI
LOADC 8 ; get fifo_full flag
AND
CBRANCH.NZ PLAY1_L0 ; loop if fifo is full
LOADC AUDIO_BASE+2 ; store amplitude value
SWAP
STOREI
DROP
RET
; start interrupt-driven sample playback
; args: pointer to pascal string, sample rate
SAMPLEQSTART:
LOADCP START_PCMAUDIO
CALL
LOADCP SMPLQ_COUNT
OVER
LOADI ; get string size from header
SHR ; divide by 4 to get word count
SHR
STOREI
DROP
LOADCP SMPLQ_PTR
SWAP
INC 8 ; skip rest of header
STOREI ; store sample data pointer
DROP
LOADCP SMPLQ_ISR ; set interrupt handler
STOREREG IV
LOADC IRQC_REG ; enable irq
LOADC IRQC_EN
STOREI
DROP
RET
SAMPLEQSTOP:
LOADCP SMPLQ_PTR
LOADC 0
STOREI
DROP
LOADCP STOP_AUDIO
CALL
LOADC IRQC_REG ; disable irq
LOADC 0
STOREI
DROP
RET
SAMPLEQSIZE:
LOADCP SMPLQ_COUNT
LOADI
RET
SMPLQ_PTR: .WORD 0
SMPLQ_COUNT: .WORD 0
SMPLQ_ISR:
LOADC IRQC_REG
LOADI
LOADC 4 ; check for audio interrupt
AND
CBRANCH.Z SMPLQ_I_XT ; if flag not set, exit
SMPLQ_I_L:
LOADCP SMPLQ_PTR
LOADI ; load word pointer
DUP
CBRANCH.NZ SMPLQ_I_B ; check for null pointer
DROP
BRANCH SMPLQ_I_XT ; if null, end interrupt routine
SMPLQ_I_B:
LOADI ; load next word
DUP
BROT ; get high half-word
BROT
LOADCP $FFFF
AND
LOADC AUDIO_BASE+2
SWAP
STOREI ; write sample, keep addr
SWAP ; addr to NoS, lower halfword on ToS
LOADCP $FFFF
AND
STOREI ; write sample
DROP
; decrement word count
LOADCP SMPLQ_COUNT
LOADI.S1.X2Y ; load counter, keep addr
DEC 1
DUP
CBRANCH.Z SMPLQ_I_END ; end if zero
STOREI ; store new counter value
DROP
; increment pointer
LOADCP SMPLQ_PTR
LOADI.S1.X2Y
INC 4
STOREI
DROP
; check if fifo is full
LOADC AUDIO_BASE
LOADI
LOADC 8 ; fifo_full
AND
CBRANCH.Z SMPLQ_I_L ; next sample if not full
LOADC AUDIO_BASE
LOADC 17 ; re-enable channel interrupt
STOREI
DROP
BRANCH SMPLQ_I_XT
; end playback, set ptr and counter to zero
SMPLQ_I_END:
DROP
DROP
LOADCP SMPLQ_PTR
LOADC 0
STOREI
DROP
LOADCP SMPLQ_COUNT
LOADC 0
STOREI
DROP
; set amplitude out to zero (biased)
LOADC AUDIO_BASE+2
LOADCP 32768
STOREI
DROP
SMPLQ_I_XT:
LOADC IRQC_REG ; re-enable interrupts
LOADC IRQC_EN
STOREI
DROP
LOADREG IR ; jump via interrupt return register
JUMP