Sona 0.50 Source

Sona 0.50/src-z80/pcm_output.z80

;****************************************************************************
; OutputPcm [RST $30]
; Called regularly to output PCM samples.
;----------------------------------------------------------------------------
; input carry ... set if timer A fired
; input hl' ..... must be YmAddr1
; input de' ..... must be YmData1
; input bc' ..... pointer to PCM sample to output
;----------------------------------------------------------------------------
; output bc' .... pointer to next PCM sample to output
;----------------------------------------------------------------------------
; breaks ........ a
;****************************************************************************

OutputPcm:
    ret                                 ; This instruction becomes LD A,(NNN)
    dw      YmAddr1                     ; when PCM playback is enabled. Load
    rra                                 ; the timer A flag into carry.
    
    ret     nc                          ; Return if timer A didn't fire
    exx                                 ; Switch to alternate register set
    
TimerAAck: equ $+1
    ld      a, YMACK_TIMERA             ; Acknowledge timer A
    ld      (hl), YMREG_MODE            ; Note that the YMACK_TIMERA will be
    ld      (de), a                     ; overwritten when ch3 mode changes
    
    ld      a, (bc)                     ; Retrieve PCM sample
    inc     c                           ; Advance PCM pointer
    
    ld      (hl), YMREG_DAC             ; Output PCM sample
    ld      (de), a
    
    jr      z, OutputPcmEnd             ; End of buffer?
    
    exx                                 ; Switch to normal register set
    ret                                 ; End of subroutine

;----------------------------------------------------------------------------

OutputPcmEnd:
    ld      a, b                        ; Flip to the other PCM buffer (the
    ld      (OutputPcmOutBuf), a        ; XOR trickery is to quickly change
    xor     (PcmBuffer1^PcmBuffer2)>>8  ; between both addresses)
    ld      b, a
    ld      c, PcmBuffer1&$FF
    
    exx                                 ; Switch to normal register set
                                        ; Calling PollPcm recursively now
                                        ; is safe (it'll play back the
                                        ; samples from the new buffer half)
    
    PollPcm
    
    push    bc                          ; Save all registers
    push    de                          ; We don't know what the main thread
    push    hl                          ; may have been executing (this is
                                        ; effectively like an "interrupt")
                                        ; so we can't change anything except
                                        ; A (which is already expected to be
                                        ; clobbered by PollPcm)
    
    PollPcm
    
OutputPcmOutBuf: equ $+2
    ld      de, PcmBuffer1&$FF          ; Get the address of the buffer to
                                        ; render new samples into. The high
                                        ; byte comes from the write we did
                                        ; when we flipped buffers earlier.
    
OutputPcmCall: equ $+1
    call    PcmRender0ch                ; Call the render function
                                        ; The called address gets overwritten
                                        ; as different PCM chanenls play.
    
    PollPcm
    
    pop     hl                          ; Restore all registers
    pop     de
    pop     bc
    
    PollPcm
    ret                                 ; End of subroutine