Sona 0.50 Source

Sona 0.50/src-z80/psg_adsr.z80

;****************************************************************************
; 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