;****************************************************************************
; EntryPoint
; Where the program starts
;****************************************************************************

EntryPoint:
    ld sp, RAM_Stack            ; Init stack

    xor a                       ; Reset Echo status
    ld (RAM_Status), a

    ld hl, $7F11                ; Mute PSG
    ld (hl), $9F
    ld (hl), $BF
    ld (hl), $DF
    ld (hl), $FF
    xor a
    ld (RAM_PSGData), a
    ld (RAM_PSGData+16), a
    ld (RAM_PSGData+32), a
    ld (RAM_PSGData+48), a

    ld hl, $6000                ; Set default bank
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l
    ld (hl), l

    ld ix, $4000                ; YM2612 I/O ports base address
    ld iyh, $40

    exx                         ; Init PCM playback status
    ;ld bc, $0000                ; B = playing, C = bank
    ;ld de, $0000                ; DE = address
    ;ld hl, $6000                ; HL = always $6000
    ld b, $00                   ; Not playing
    exx

    ld (ix+0), $2B              ; Disable DAC by default
    ld (ix+1), $00

    ld e, $7F                   ; Mute all FM channels
    ld a, $40
    ld b, 4
.mutefm:
    ld (ix+0), a
    ld (ix+1), e
    ld (ix+2), a
    ld (ix+3), e
    inc a
    ld (ix+0), a
    ld (ix+1), e
    ld (ix+2), a
    ld (ix+3), e
    inc a
    ld (ix+0), a
    ld (ix+1), e
    ld (ix+2), a
    ld (ix+3), e
    inc a
    inc a
    djnz .mutefm

    ld (ix+0), $B4              ; Ensure all channels can be heard from both
    ld (ix+1), $C0              ; speakers (by default they're mute!)
    ld (ix+0), $B5
    ld (ix+1), $C0
    ld (ix+0), $B6
    ld (ix+1), $C0
    ld (ix+2), $B4
    ld (ix+3), $C0
    ld (ix+2), $B5
    ld (ix+3), $C0
    ld (ix+2), $B6
    ld (ix+3), $C0

    ld (ix+0), $24              ; Init timers
    ld (ix+1), $FE
    ld (ix+0), $25
    ld (ix+1), $03
    ld (ix+0), $26
    ld (ix+1), $C9
    ld (ix+0), $27
    ld (ix+1), $3F
    
    jp IdleLoop                 ; Go into idle loop

;****************************************************************************
; PollPCM
; Used to update PCM while not idle
;****************************************************************************

PollPCM: macro
    ld a, ($4000)
    rrca
    call c, UpdatePCM
    endm

;****************************************************************************
; RunCommand
; Checks which command to run
;****************************************************************************

RunCommand:
    dec a                       ; Command $01: load list
    jp z, LoadList
    dec a                       ; Command $02: play SFX
    jp z, PlaySFX
    dec a                       ; Command $03: stop SFX
    jp z, StopSFXCmd
    dec a                       ; Command $04: play BGM
    jp z, PlayBGM
    dec a                       ; Command $05: stop BGM
    jp z, StopBGMCmd
    dec a                       ; Command $06: resume BGM
    jp z, ResumeBGM

    PollPCM

    xor a                       ; Bad command, ignore >:(
    ld (RAM_Command), a

    PollPCM

;****************************************************************************
; IdleLoop
; Loop that runs when not processing SFX or BGM
;****************************************************************************

IdleLoop:
    ld a, (RAM_Command)         ; Look for commands
    or a
    jr nz, RunCommand

    PollPCM                     ; Poll PCM

    ld a, ($4000)               ; Tick?
    bit 1, a
    jr nz, DoTick
    bit 0, a                    ; Poll PCM again
    call nz, UpdatePCM          ; Not using macro for optimization purposes

    jp IdleLoop                 ; Keep idling

;****************************************************************************
; DoTick
; Called whenever a new tick triggers
;****************************************************************************

DoTick:
    ld a, (ix+0)
    bit 0, a
    call nz, UpdatePCM

    ld (ix+0), $27
    ld (ix+1), $2F

    PollPCM

DoTick_SFX:                     ; Process SFXs
    jp DoTick_SFXSkip
DoTick_SFXSkip:

    PollPCM

DoTick_BGM:                     ; Process BGMs
    jp DoTick_BGMSkip
DoTick_BGMSkip:

    PollPCM

    jp UpdatePSG                ; Update PSG envelopes
DoTick_PSGSkip:

    PollPCM

    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; BankSwitch
; Switches into a new bank (won't update player status!)
;
; input A .... New bank to switch into
; input HL ... Must be $6000
; breaks ..... AF
;****************************************************************************

BankSwitch: macro
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    rrca
    ld (hl), a
    ld (hl), h
    rrca
    ld (hl), a
    endm

;****************************************************************************
; LoadList [command $01]
; Loads the pointer list
;****************************************************************************

LoadList:
    ld hl, RAM_ComBank          ; Get command parameters
    ld c, (hl)
    inc l
    ld e, (hl)
    inc l
    ld d, (hl)
    ex de, hl

    xor a                       ; Command parsed
    ld (RAM_Command), a

    ld de, RAM_PointerList      ; Where the pointer list starts

.loop:
    call GetParam               ; Get high byte address
    
    ld a, b                     ; Is it the end of the list?
    or a
    jp z, .end
    
    ld (de), a                  ; Store high byte address
    inc d
    
    call GetParam               ; Get low address byte
    ld a, b
    ld (de), a
    inc d
    
    call GetParam               ; Get bank byte
    ld a, b
    ld (de), a
    
    dec d                       ; Go for next byte
    dec d
    inc e
    jp .loop

.end:
    jp IdleLoop                 ; End of subroutine

;****************************************************************************
; GetParam
; Subroutine for getting the parameter byte
;****************************************************************************

GetParam:
    ld a, (RAM_LastBank)        ; Bank switch?
    cp c
    jp z, .noswitchp
    ld a, c
    ld (RAM_LastBank), a
;    exx
;    BankSwitch
;    exx
    push hl
    ld hl, $6000
    BankSwitch
    pop hl
.noswitchp:
    ld b, (hl)                  ; Get volume

    inc l                       ; Get next address
    jp nz, .nonewbankp
    inc h
    jp nz, .nonewbankp
    ld h, $80
    inc c
.nonewbankp:

    ret                         ; End of subroutine
