;****************************************************************************
; UpdatePSG
; Updates PSG output
;****************************************************************************

UpdatePSG:
    ld hl, RAM_PSGData+48       ; PSG envelope data of *last* channel
    ld b, 3                     ; Go through all channels
.loop:
    push bc

    ld a, (hl)                  ; Get channel volume
    bit 7, a
    jr nz, .noskip
    ld b, $0F
    inc l
    jp .skip
.noskip:
    and $7F
    ld b, a

    inc l                       ; Add global volume
    ld a, (hl)
    add b
    ld b, a

    PollPCM
    push bc

    inc l                       ; Get current address of envelope
    ld c, (hl)
    inc l
    ld e, (hl)
    inc l
    ld d, (hl)
    ex de, hl

.readenv:
    PollPCM
    call GetParam               ; Get next byte
    PollPCM

    ld a, b
    cp $FE                      ; Set loop point?
    jp z, .envsetloop
    cp $FF                      ; Loop envelope?
    jp z, .envloop

    ld iyl, b                  ; Keep byte safe somewhere...
    PollPCM

    ex de, hl                   ; Store new address
    ld (hl), d
    dec l
    ld (hl), e
    dec l
    ld (hl), c
    dec l

    pop bc
    PollPCM

    db $FD,$7D ; ld a, iyl      ; Mix envelope with volume
    add b
    ld b, a

    cp $10                      ; Check for overflow
    jr c, .notmute
    ld b, $0F
.notmute:

.skip:
    PollPCM

    ld a, b                     ; Set PSG channel volume
    rlca   
    rlca
    rlca
    pop bc
    or b
    rrca
    rrca
    rrca
    or $90
    ld ($7F11), a

    ld a, l                     ; Go for next channel
    sub 16+1
    ld l, a
    PollPCM
    dec b
    jp p, .loop

    jp DoTick_PSGSkip           ; End of subroutine

.envsetloop:
    PollPCM
    inc e                       ; Where we store the loop point

    ex de, hl                   ; Store loop point
    ld (hl), c
    inc l
    ld (hl), e
    inc l
    ld (hl), d
    ex de, hl

    dec e                       ; Back to where we were...
    dec e
    dec e
    jp .readenv                 ; Go for next byte

.envloop:
    PollPCM
    inc e                       ; Where we store the loop point

    ex de, hl                   ; Retrieve loop point
    ld c, (hl)
    inc l
    ld e, (hl)
    inc l
    ld d, (hl)
    ex de, hl

    dec e                       ; Back to where we were...
    dec e
    dec e
    jp .readenv                 ; Go for next byte

;****************************************************************************
; NoteOnPSG*
; Does a "note on" for a PSG channel
;****************************************************************************

NoteOnPSGSFX:
    call NoteOnPSG              ; We're just a wrapper
    jp ProcessSFXRun            ; End of subroutine

NoteOnPSGBGM:
    ld b, a
    PollPCM
    ld a, b

    push hl
    and $03                     ; Check if channel is free
    ld hl, RAM_Locked+8
    add l
    ld l, a
    ld a, (hl)
    pop hl
    or a
    jp nz, ProcessBGMSkip1      ; Don't play if locked

    ld a, b
    call NoteOnPSG              ; We're just a wrapper
    jp ProcessBGMRun            ; End of subroutine

NoteOnPSG:
    and $03
    ld b, a
    ;push af
    ex af, af'
    PollPCM

    push hl                     ; Set channel volume
    ld h, RAM_PSGData>>8
    ld a, b
    rrca
    rrca
    rrca
    rrca
    ld l, a
    ld a, (hl)
    or $80
    ld (hl), a

    PollPCM
    push de

    inc l                       ; Now we'll reset the envelope address...
    inc l
    ld d, h
    ld a, l
    add 6
    ld e, a

    PollPCM

    ld a, (de)                  ; Reset envelope address
    ld (hl), a
    inc l
    inc e
    ld a, (de)
    ld (hl), a
    inc l
    inc e
    ld a, (de)
    ld (hl), a

    pop de
    pop hl

    PollPCM
    call GetParam               ; Get note
    PollPCM

    ;pop af
    ex af, af'
    push hl
    push de
    ld h, PSGFreqTable>>8       ; Get address of frequency data
    ld l, b
    ld de, $7F11

    rrca                        ; Set new frequency
    rrca
    rrca
    ld b, (hl)
    or b
    ld (de), a
    inc l
    ld a, (hl)
    ld (de), a
    pop de
    pop hl

    ret                         ; End of subroutine

;****************************************************************************
; NoteOnNoise*
; Does a "note on" for the noise PSG channel
;****************************************************************************

NoteOnNoiseSFX:
    call NoteOnNoise            ; We're just a wrapper
    jp ProcessSFXRun            ; End of subroutine

NoteOnNoiseBGM:
    ld a, (RAM_Locked+11)       ; Check if channel is free
    or a
    jp nz, ProcessBGMSkip1      ; Don't play if locked

    call NoteOnNoise            ; We're just a wrapper
    jp ProcessBGMRun            ; End of subroutine

NoteOnNoise:
    PollPCM
    push hl

    ld hl, RAM_PSGData+48       ; Set channel volume
    ld a, (hl)
    or $80
    ld (hl), a

    PollPCM
    push de

    inc l                       ; Now we'll reset the envelope address...
    inc l
    ld d, h
    ld a, l
    add 6
    ld e, a

    PollPCM

    ld a, (de)                  ; Reset envelope address
    ld (hl), a
    inc l
    inc e
    ld a, (de)
    ld (hl), a
    inc l
    inc e
    ld a, (de)
    ld (hl), a

    pop de
    pop hl

SetNoteNoise:
    PollPCM
    call GetParam               ; Get noise type
    PollPCM

    ld a, $E0                   ; Set new noise type
    or b
    ld ($7F11), a

    ret                         ; End of subroutine

;****************************************************************************
; NoteOffPSG*
; Does a "note off" for a PSG channel
;****************************************************************************

NoteOffPSGSFX:
    call NoteOffPSG             ; We're just a wrapper
    jp ProcessSFXRun            ; End of subroutine

NoteOffPSGBGM:
    ld b, a
    PollPCM
    ld a, b

    push hl
    and $03                     ; Check if channel is free
    ld hl, RAM_Locked+8
    add l
    ld l, a
    ld a, (hl)
    pop hl
    or a
    jp nz, ProcessBGMRun        ; Don't stop if locked

    ld a, b
    call NoteOffPSG             ; We're just a wrapper
    jp ProcessBGMRun            ; End of subroutine

NoteOffPSG:
    and $03
    ld b, a
    PollPCM

    push hl                     ; Mark channel as not playing
    ld h, RAM_PSGData>>8
    ld a, b
    rrca
    rrca
    rrca
    rrca
    ld l, a
    ld a, (hl)
    and $7F
    ld (hl), a
    pop hl

    ret                         ; End of subroutine

;****************************************************************************
; SetPSGVol*
; Sets the volume of a PSG channel
;****************************************************************************

SetPSGVolSFX:
    and $03                     ; Get channel ID

    ex af, af'
    PollPCM
    call GetParam               ; Get volume
    PollPCM
    ex af, af'

    push hl
    ld h, RAM_PSGData>>8        ; Set new volume
    rrca
    rrca
    rrca
    rrca
    ld l, a
    ld a, (hl)
    and $80
    or b
    ld (hl), a
    pop hl

    jp ProcessSFXRun            ; End of subroutine

SetPSGVolBGM:
    and $03                     ; Get channel ID

    ex af, af'
    PollPCM
    call GetParam               ; Get volume
    PollPCM
    ex af, af'

    push de
    push hl

    push af
    ld de, RAM_Locked+8         ; Check if channel is locked
    add e                         ; Keep results for later
    ld e, a
    ld a, (de)
    ld e, a

    PollPCM
    pop af

    ld h, RAM_PSGData>>8        ; Store new volume
    rrca
    rrca
    rrca
    rrca
    add 15
    ld l, a
    ld (hl), b

    PollPCM

    ld a, e                     ; Is channel locked?
    or a
    jr nz, .nosetvol

    ld a, l                     ; Set new volume
    sub 15
    ld l, a
    ld a, (hl)
    and $80
    or b
    ld (hl), a

    PollPCM

.nosetvol:
    pop hl
    pop de

    jp ProcessBGMRun            ; End of subroutine

;****************************************************************************
; LoadPSG*
; Loads a PSG instrument
;****************************************************************************

LoadPSGSFX:
    and $03                     ; Get channel number

    ex af, af'
    PollPCM
    call GetParam               ; Get instrument ID
    PollPCM
    ex af, af'

    push de
    push hl

    ld d, RAM_PointerList>>8    ; Get position in pointer list
    ld e, b

    ld h, RAM_PSGData>>8        ; Where to store address
    rrca
    rrca
    rrca
    rrca
    add 8+2+(RAM_PSGData&$FF)
    ld l, a

    PollPCM

    ld a, (de)                  ; Store PSG envelope start address
    ld (hl), a
    inc d
    dec l
    ld a, (de)
    ld (hl), a
    inc d
    dec l
    ld a, (de)
    ld (hl), a
    
    PollPCM
    
    ld a, l                     ; Reset volume
    sub 8
    ld l, a
    ld (hl), $00

    pop hl
    pop de
    jp ProcessSFXRun            ; End of subroutine

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

LoadPSGBGM:
    and $03                     ; Get channel number

    ex af, af'
    PollPCM
    call GetParam               ; Get instrument ID
    PollPCM
    ex af, af'

    push de
    push hl

    ld d, RAM_PointerList>>8    ; Get position in pointer list
    ld e, b

    ld hl, RAM_Locked+8         ; Get if channel is locked
    push af
    add l
    ld l, a
    pop af
    ld b, (hl)

    ld h, RAM_PSGData>>8        ; Where to store BGM instrument data
    rrca
    rrca
    rrca
    rrca
    add 15
    ld l, a

    PollPCM
    
    ld (hl), a                  ; Reset volume for BGM
    dec l

    ld a, (de)                  ; Store PSG envelope address for BGM
    ld (hl), a
    inc d
    dec l
    ld a, (de)
    ld (hl), a
    inc d
    dec l
    ld a, (de)
    ld (hl), a

    PollPCM

    ld a, b                     ; Don't set PSG envelope if locked
    or a
    jp z, .noloadlocked
    pop hl
    pop de
    jp ProcessBGMRun
.noloadlocked:
    PollPCM

    ld d, h                     ; Set PSG envelope
    ld a, l
    sub 12-8
    ld e, a

    ld a, (hl)
    ld (de), a
    inc l
    inc e
    ld a, (hl)
    ld (de), a
    inc l
    inc e
    ld a, (hl)
    ld (de), a

    PollPCM
    
    ld a, l                     ; Reset volume
    sub 8
    ld l, a
    ld (hl), $00

    pop hl
    pop de
    jp ProcessBGMRun            ; End of subroutine

;****************************************************************************
; SetNotePSG*
; Sets the note of a PSG channel without "note on"
;****************************************************************************

SetNotePSGSFX:
    call SetNotePSG             ; We're just a wrapper
    jp ProcessSFXRun            ; End of subroutine

SetNotePSGBGM:
    ld b, a
    PollPCM
    ld a, b

    push hl
    and $0F                     ; Check if channel is free
    ld h, RAM_Locked>>8
    add RAM_Locked&$FF
    ld l, a
    ld a, (hl)
    pop hl
    or a
    jp nz, ProcessBGMSkip2      ; Don't play if locked

    ld a, b
    call SetNotePSG             ; We're just a wrapper
    jp ProcessBGMRun            ; End of subroutine

SetNotePSG:
    and $03                     ; Get channel number

    ex af, af'
    PollPCM
    call GetParam               ; Get first byte
    PollPCM
    ex af, af'

    push de                     ; PSG port address
    ld de, $7F11

    rrca                        ; Set first frequency byte
    rrca
    rrca
    or b
    or $80
    ld (de), a

    PollPCM
    call GetParam               ; Get second byte
    PollPCM

    ex de, hl
    ld (hl), b                  ; Set second frequency byte
    ex de, hl
    pop de

    ret                         ; End of subroutine

;****************************************************************************
; SetNoteNoise*
; Sets the note of the noise PSG channel without "note on"
;****************************************************************************

SetNoteNoiseSFX:
    call SetNoteNoise           ; We're just a wrapper
    jp ProcessSFXRun            ; End of subroutine

SetNoteNoiseBGM:
    ld a, (RAM_Locked+11)       ; Check if channel is free
    or a
    jp nz, ProcessBGMSkip1      ; Don't play if locked

    call SetNoteNoise           ; We're just a wrapper
    jp ProcessBGMRun            ; End of subroutine

;****************************************************************************
; LockChannelPSG [events $E8-$EB]
; Locks a PSG channel
;****************************************************************************

LockChannelPSG:
    and $03
    ld b, a
    PollPCM
    push hl

    ld h, RAM_Locked>>8         ; Get address of channel to lock
    ld a, b
    add (RAM_Locked&$FF)+8
    ld l, a

    ld (hl), $01                ; Lock channel
    
    PollPCM
    
    ld a, b                     ; Stop channel
    rrca
    rrca
    rrca
    rrca
    ld l, a
    ld h, RAM_PSGData>>8
    ld (hl), $00

    pop hl
    jp ProcessSFXRun            ; End of subroutine
