;****************************************************************************
; UpdatePsgAdsr
; Updates the ADSR envelopes for all PSG channels.
;----------------------------------------------------------------------------
; modifies ... a,bc,de,hl,a'
;****************************************************************************
UpdatePsgAdsr:
ld hl, PsgAdsr_Psg1 ; Pointer to 1st channel
UpdatePsgAdsrLoop:
PollPcm
call PollTimerB
ld b, (hl) ; Get current level
inc l ; Get current phase and call the
ld a, (hl) ; respective handler
ld (UpdatePsgAdsrJump), a
PollPcm
UpdatePsgAdsrJump: equ $+1
jr $
jr UpdatePsgAttack ; PSGPHASE_ATTACK
jr UpdatePsgDecay ; PSGPHASE_DECAY
jr UpdatePsgSustain ; PSGPHASE_SUSTAIN
jr UpdatePsgRelease ; PSGPHASE_RELEASE
UpdatePsgAdsrReloop:
PollPcm
ld a, l ; Move onto next envelope, if any
add SIZEOF_PSGADSR
cp PsgAdsr_End&$FF
ret z
ld l, a
jr UpdatePsgAdsrLoop
;****************************************************************************
; UpdatePsgAttack
; Handles the attack phase of a PSG ADSR envelope.
;----------------------------------------------------------------------------
; input b ..... current level
; input hl .... pointer to PSGADDR_PHASE field
;----------------------------------------------------------------------------
; output hl ... pointer to PSGADDR_LEVEL field
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;----------------------------------------------------------------------------
; continues in UpdatePsgAdsrReloop
;****************************************************************************
UpdatePsgAttack:
inc l ; Get total level (the attack ->
ld c, (hl) ; delay transition point)
inc l ; Add attack rate to the
ld a, b ; current level
add a, (hl)
jr c, UpdatePsgAttackOver ; Check if we overshot past the
cp c ; total level
jr nc, UpdatePsgAttackOver
ld b, a
PollPcm
dec l ; Store new level
dec l
dec l
ld (hl), b
jr UpdatePsgAdsrReloop ; Go process next envelope
UpdatePsgAttackOver:
PollPcm
dec l ; Move to the decay phase
dec l
ld (hl), PSGPHASE_DECAY
dec l ; Store total level as the new
ld (hl), c ; current level instead of the
; computed one, since that's where
; decay will take off from
jr UpdatePsgAdsrReloop ; Go process next envelope
;****************************************************************************
; UpdatePsgDecay
; Handles the decay phase of a PSG ADSR envelope.
;----------------------------------------------------------------------------
; input b ..... current level
; input hl .... pointer to PSGADDR_PHASE field
;----------------------------------------------------------------------------
; output hl ... pointer to PSGADDR_LEVEL field
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;----------------------------------------------------------------------------
; continues in UpdatePsgAdsrReloop
;****************************************************************************
UpdatePsgDecay:
ld a, l ; Get sustain level (the decay ->
add PSGADSR_SL-PSGADSR_PHASE ; sustain transition point)
ld l, a
ld c, (hl)
inc l ; Substract decay rate to the
ld a, b ; current level
sub (hl)
jr c, UpdatePsgDecayOver ; Check if we overshot past the
cp c ; sustain level
jr c, UpdatePsgDecayOver
ld b, a
PollPcm
ld a, l ; Store new level
sub PSGADSR_DR-PSGADSR_LEVEL
ld l, a
ld (hl), b
jr UpdatePsgAdsrReloop ; Go process next envelope
UpdatePsgDecayOver:
PollPcm
ld a, l ; Move to the sustain phase
sub PSGADSR_DR-PSGADSR_PHASE
ld l, a
ld (hl), PSGPHASE_SUSTAIN
dec l ; Store sustain level as the new
ld (hl), c ; current level instead of the
; computed one, since that's where
; sustain will take off from
jr UpdatePsgAdsrReloop ; Go process next envelope
;****************************************************************************
; UpdatePsgSustain
; Handles the sustain phase of a PSG ADSR envelope.
;----------------------------------------------------------------------------
; input b ..... current level
; input hl .... pointer to PSGADDR_PHASE field
;----------------------------------------------------------------------------
; output hl ... pointer to PSGADDR_LEVEL field
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;----------------------------------------------------------------------------
; continues in UpdatePsgAdsrReloop
;****************************************************************************
UpdatePsgSustain:
ld c, PSGADSR_SR-PSGADSR_PHASE
jr UpdatePsgSustainOrRelease
;****************************************************************************
; UpdatePsgRelease
; Handles the release phase of a PSG ADSR envelope.
;----------------------------------------------------------------------------
; input b ..... current level
; input hl .... pointer to PSGADDR_PHASE field
;----------------------------------------------------------------------------
; output hl ... pointer to PSGADDR_LEVEL field
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;----------------------------------------------------------------------------
; continues in UpdatePsgAdsrReloop
;****************************************************************************
UpdatePsgRelease:
ld c, PSGADSR_RR-PSGADSR_PHASE
; fallthrough to UpdatePsgSustainOrRelease...
;****************************************************************************
; UpdatePsgSustainOrRelease
; Common code to both sustain and release phases of a PSG ADSR envelope.
;----------------------------------------------------------------------------
; input b ..... current level
; input c ..... offset from PSGADDR_PHASE to sustain or release rate
; input hl .... pointer to PSGADDR_PHASE field
;----------------------------------------------------------------------------
; output hl ... pointer to PSGADDR_LEVEL field
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;----------------------------------------------------------------------------
; continues in UpdatePsgAdsrReloop
;****************************************************************************
UpdatePsgSustainOrRelease:
ld a, l ; Point to the sustain/release rate
add a, c
ld l, a
ld a, b ; Substract the sustain/release rate
sub (hl) ; from the current level
jr nc, UpdatePsgReleaseOk ; Check for overflow (when the
xor a ; envelope goes below mute)
UpdatePsgReleaseOk:
ld b, a
PollPcm
ld a, l ; Store the new level
inc c
sub c
ld l, a
ld (hl), b
jr UpdatePsgAdsrReloop ; Go process next envelope
;****************************************************************************
; GetPsgAdsrPtr
;
; Computes the pointer to the ADSR envelope data for a PSG channel. Note that
; it returns a pointer to the *phase* field (which is what's most commonly
; needed to manipulate), not the beginning of the channel's entry.
;----------------------------------------------------------------------------
; input b ..... channel ID (bits 1-0 only)
;----------------------------------------------------------------------------
; output de ... pointer to PSGADSR_PHASE of relevant channel
;----------------------------------------------------------------------------
; modifies .... a,de
;****************************************************************************
GetPsgAdsrPtr:
PollPcm
ld a, b ; Isolate channel ID
and $03
add a, a ; Compute pointer to the ADSR phase
add a, a ; field for this channel
add a, a
add a, (PsgAdsr_Psg1+PSGADSR_PHASE)&$FF
ld e, a
ld d, PsgAdsr_Psg1>>8
ret ; Return it