;****************************************************************************
; PlayBGM [command $04]
; Plays a BGM
;----------------------------------------------------------------------------
; breaks: all
;****************************************************************************

PlayBGM:
    PollPCM
    call ClearBGM               ; Clear BGM resources
    PollPCM

    ld a, (RAM_Status)          ; Show BGM playback in Echo's status
    or $02
    ld (RAM_Status), a

    PollPCM

    ld hl, RAM_ComBank          ; Get command parameters
    ld c, (hl)
    inc l
    ld e, (hl)
    inc l
    ld d, (hl)

    PollPCM

    xor a                       ; Command parsed
    ld (RAM_Command), a

    ld hl, RAM_BGMData          ; Set BGM as playing
    ld (hl), $01
    inc l                       ; No delays!
    ld (hl), $01
    inc l                       ; Store BGM start bank
    ld (hl), c
    inc l                       ; Store BGM start address (low)
    ld (hl), e
    inc l                       ; Store BGM start address (high)
    ld (hl), d

    PollPCM

    ld hl, ProcessBGM           ; Tell Echo to process BGM
    ld (DoTick_BGM+1), hl
    
    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; ResumeBGM [command $06]
; Resumes a stopped BGM
;----------------------------------------------------------------------------
; breaks: all
;****************************************************************************

ResumeBGM:
    xor a                       ; Command parsed
    ld (RAM_Command), a

;----------------------------------------------------------------------------
    
    ld b, 8                     ; Restore all FM channels
    ld de, RAM_Locked+7
.restorefm:

    PollPCM

    ld a, (de)                    ; Check if this channel is locked
    or a
    jp nz, .fmlocked

    PollPCM
    
    dec b                         ; Restore FM channel
    call RestoreFM
    inc b
    
.fmlocked:
    dec e                         ; Go for next channel to restore
    dec b
    jp nz, .restorefm

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

    ld a, (RAM_Status)          ; Show BGM playback in Echo's status
    or $02
    ld (RAM_Status), a

    ld hl, RAM_BGMData          ; Set BGM as playing
    ld (hl), $01
    
    ld hl, ProcessBGM           ; Tell Echo to process BGM
    ld (DoTick_BGM+1), hl
    
    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; ProcessBGM
; Processes a tick for a BGM
;****************************************************************************

ProcessBGM:
    PollPCM

    ld hl, RAM_BGMData+1        ; BGM data address

    ld a, (hl)                  ; Delaying?
    dec a
    jp z, .nodelay
    ld (hl), a

    jp DoTick_BGMSkip           ; End of subroutine

.nodelay:
    PollPCM

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

ProcessBGMRun:
ProcessBGMSkip:
    PollPCM                     ; Fetch next event
    call GetParam
    PollPCM

    ld a, b                     ; Parse byte

    cp $08
    jp c, NoteOnFMBGM           ; Events $00-$07: note on FM
    cp $0B
    jp c, NoteOnPSGBGM          ; Events $08-$0A: note on PSG (square)
    jp z, NoteOnNoiseBGM        ; Event $0B: note on PSG (noise)
    cp $0C
    jp z, PlayPCMBGM            ; Event $0C: play PCM

    PollPCM
    ld a, b

    cp $18
    jp c, NoteOffFMBGM          ; Events $10-$17: note off FM
    cp $1C
    jp c, NoteOffPSGBGM         ; Events $18-$1B: note off PSG
    jp z, StopPCMBGM            ; Event $1C: note off PCM

    PollPCM
    ld a, b

    cp $FE
    jp z, SetDelayBGM           ; Event $FE: set delay
    cp $FF
    jp z, StopBGMEvent          ; Event $FF: stop BGM
    cp $FC
    jp z, LoopBGM               ; Event $FC: loop BGM
    cp $FD
    jp z, SetLoopBGM            ; Event $FD: set loop point
    cp $FB
    jp z, SetSyncFlag           ; Event $FB: new Sync Point 

    PollPCM
    ld a, b

    cp $28
    jp c, SetFMVolBGM           ; Events $28-$2B: set FM volume
    cp $2C
    jp c, SetPSGVolBGM          ; Events $28-$2B: set PSG volume

    PollPCM
    ld a, b

    cp $38
    jp c, SetNoteFMBGM          ; Events $30-$37: set FM note
    cp $3B
    jp c, SetNotePSGBGM         ; Events $38-$3A: set PSG note (square)
    jp z, SetNoteNoiseBGM       ; Event $3B: set PSG note (noise)

    PollPCM
    ld a, b

    cp $48
    jp c, LoadFMBGM             ; Events $40-$47: load FM instrument
    cp $4C
    jp c, LoadPSGBGM            ; Events $48-$4B: load PSG instrument

    PollPCM
    ld a, b

    cp $F8                      ; Events $F0-$F7: set FM parameters
    jp c, SetFMParamBGM

    PollPCM                     ; FFFFFFFFF bad event >:(
    jp StopBGMEvent             ; End of subroutine

ProcessBGMSkip2:                ; This is where we land after a locked event
    PollPCM                       ; that had two bytes for the parameter
    inc l                         ; Skip first byte
    jp nz, .nobankskip2
    inc h
    jp nz, .nobankskip2
    ld h, $80
    inc c
.nobankskip2:

ProcessBGMSkip1:                ; This is where we land after a locked event
    PollPCM                       ; that had one byte for the parameter
    inc l                         ; Skip byte
    jp nz, .nobankskip1
    inc h
    jp nz, .nobankskip1
    ld h, $80
    inc c
.nobankskip1:

    jp ProcessBGMRun            ; Keep processing

;****************************************************************************
; StopBGM* [command $05, event $FF]
; Stops BGM playback
;****************************************************************************

StopBGMEvent:
    call StopBGM                ; We're just a wrapper
    jp DoTick_BGMSkip           ; End of subroutine

StopBGMCmd:
    xor a                       ; Command parsed
    ld (RAM_Command), a
    call StopBGM                ; We're just a wrapper
    jp IdleLoop                 ; End of subroutine

StopBGM:
    ld a, (RAM_Status)          ; Hide BGM playback in Echo's status
    and $FD
    ld (RAM_Status), a

    PollPCM

    call ClearBGM               ; Clear BGM resources
    PollPCM

    xor a                       ; Stop playback
    ld (RAM_BGMPlaying), a
    ld hl, DoTick_BGMSkip
    ld (DoTick_BGM+1), hl

    ret                         ; End of subroutine

;****************************************************************************
; ClearBGM
; Clears BGM resources
;****************************************************************************

ClearBGM:
    ld a, (RAM_Locked+6)        ; Stop PCM playback if needed
    or a
    call z, StopPCM

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

    ld b, 4                     ; Reset all PSG channels
    ld de, RAM_PSGData+48+15
    ld hl, RAM_Locked+11
.mutepsg:
    PollPCM
    
    ld (hl), $00                ; Reset BGM volume
    
    ld a, e
    sub 15
    ld e, a
    
    ld a, (hl)                  ; Mute PSG channel if it isn't locked
    or a
    jr nz, .nopsgmute
    xor a
    ld (de), a
    
.nopsgmute:
    PollPCM
    
    dec e
    dec l
    djnz .mutepsg

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

    ld b, 8                     ; Reset all FM channels
    ld de, RAM_BGMFMVol+7
.resetfm:
    PollPCM
    
    ld a, $7F                   ; Reset BGM volume
    ld (de), a
    dec e
    
    PollPCM
    
    ld a, (hl)                  ; Kill FM channel if not locked
    or a
    jp nz, .nofmkill
    dec b
    
    ld a, b
    call KillFM
    
    PollPCM
    
    ld a, b                     ; Reset panning
    and $04
    ld iyl, a
    ld a, b
    and $03
    add $B4
    ld (iy+0), a
    ld (iy+1), $C0
    
    inc b
.nofmkill:
    dec l
    djnz .resetfm

;----------------------------------------------------------------------------
    
    ld hl, RAM_BGMFMPan         ; Reset panning status (for restoring)
    ld a, $C0
    ld b, 8
.initpanstat:
    ld (hl), a
    inc l
    djnz .initpanstat

    ret                         ; End of subroutine

;****************************************************************************
; LoopBGM [event $FC]
; Makes a BGM loop
;****************************************************************************

LoopBGM:
    PollPCM

    ex de, hl                   ; Get looping address
    inc l
    ld c, (hl)
    inc l
    ld e, (hl)
    inc l
    ld d, (hl)
    dec l
    dec l
    dec l
    ex de, hl

    jp ProcessBGMRun            ; End of subroutine

;****************************************************************************
; SetLoopBGM [event $FD]
; Sets the BGM loop point
;****************************************************************************

SetLoopBGM:
    PollPCM

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

    jp ProcessBGMRun            ; End of subroutine

SetSyncFlag:
    push hl
    ld hl, RAM_SyncFlag
    ld (hl), $FF
    pop hl
    jp ProcessBGMRun
