Sona 0.50 Source

Sona 0.50/src-z80/stream.z80

;****************************************************************************
; PlayBgmCmd [CMD_PLAYBGM]
; Starts playing new music on 68000's request.
;----------------------------------------------------------------------------
; input CmdAddr1 ... pointer to stream data (in 68000 format)
; input CmdAddr2 ... pointer to instrument list (in 68000 format)
; input CmdByte1 ... number of instruments
;----------------------------------------------------------------------------
; continues in EndOfCommand
;****************************************************************************

PlayBgmCmd:
    call    StopAllSound                ; Stop any previous music first
    
    ld      hl, (CmdAddr1+0)            ; Store the stream data's address
    ld      a, (CmdAddr1+2)             ; so we can reference it later when
    ToAddrBank                          ; computing offsets within the stream
    ld      (BgmStartAddr), hl
    ld      (BgmStartBank), a
    
    ex      de, hl
    ld      hl, StreamState             ; Set up stream #0 with the
    call    InitStream                  ; BGM's information
    
    ld      hl, BGMTEMPO_DEFAULT        ; Set up default tempo
    ld      (BgmTempo), hl
    ld      hl, 0                       ; Reset subticks (or it can mess with
    ld      (BgmTicks), hl              ; the speed playback early on)
    
    ld      hl, (CmdAddr2+0)            ; Get pointer to instrument
    ld      a, (CmdAddr2+2)             ; list for this track
    ToAddrBank
    ld      c, a
    
    ld      a, (CmdByte)                ; Get number of instruments
    ld      b, a                        ; used by this track
    
    xor     a                           ; BGMs start from instrument $00
    call    LoadInstrList               ; Go load the instrument list
    
    xor     a                           ; Reset volume values to a known
    ld      hl, BgmFmVol                ; default (for tracks that don't
    ld      b, SIZEOF_VOL_DATA          ; modify volume)
ClearVolumeLoop:
    ld      (hl), a
    inc     l
    djnz    ClearVolumeLoop
    
    ld      a, PAN_BOTH                 ; Reset panning and PMS/AMS to
    ld      hl, BgmFmParam              ; a known default (for tracks that
    ld      b, SIZEOF_PAN_DATA          ; don't modify panning)
ClearPanLoop:
    ld      (hl), a
    inc     l
    djnz    ClearPanLoop
    
    ld      b, 3                        ; Rewrite the panning registers to
    ld      de, (YMREG_PAN<<8)|PAN_BOTH ; the YM2612 so they take effect
    ld      iy, YmAddr2                 ; (reloading a FM instrument will
ResetPanLoop:                           ; not implificly reset them unlike
    ld      (ix+0), d                   ; it does with volume)
    ld      (ix+1), e                   ;
    PollPcm                             ; PollPcm is just to waste time
    PollPcm                             ; between writes without wasting up
    ld      (iy+0), d                   ; space, PCM is guaranteed to have
    ld      (iy+1), e                   ; been stopped by StopAllSound.
    PollPcm
    PollPcm
    inc     d
    djnz    ResetPanLoop
    
    jp      EndOfCommand                ; Pop command off the queue

;****************************************************************************
; PlaySfxCmd [CMD_PLAYSFX]
; Starts playing a sound effect on 68000's request.
;----------------------------------------------------------------------------
; input CmdAddr1 ... pointer to stream data (in 68000 format)
; input CmdAddr2 ... pointer to instrument list (in 68000 format)
; input CmdByte1 ... number of instruments
; input CmdByte2 ... SFX priority
; input CmdByte3 ... SFX ID
; input CmdByte4 ... SFX argument #1 (stored in local VM variable 0)
; input CmdByte5 ... SFX argument #2 (stored in local VM variable 1)
;----------------------------------------------------------------------------
; continues in EndOfCommand
;****************************************************************************

PlaySfxCmd:
    ld      a, (CmdByte3)               ; Get current SFX priority
    add     a, SfxPriority&$FF
    ld      h, SfxPriority>>8
    ld      l, a
    ld      e, (hl)
    
    ld      a, (CmdByte2)               ; Check if the new priority is
    cp      e                           ; higher than the current one,
    jp      c, EndOfCommand             ; otherwise discard the request
    
    ld      a, -1                       ; Pretend that we're inside a SFX
    ld      i, a                        ; stream so the call below works
    
    push    hl                          ; Free up the resources that were
    ld      a, (CmdByte3)               ; being used by this stream if needed
    call    FreeSfxStream               ; (in case we interrupted a SFX)
    pop     hl
    
    ld      a, (CmdByte2)               ; Store new SFX priority
    ld      (hl), a
    
    ld      a, (CmdByte3)               ; Determine stream number to use
    add     a, FIRST_SFXSTREAM          ; for this sound effect
    
    add     a, a                        ; Initialize stream
    add     a, a
    add     a, a
    add     a, StreamState&$FF
    ld      h, StreamState>>8
    ld      l, a
    call    InitStreamFromCmd
    
    ld      hl, (CmdAddr2+0)            ; Get pointer to instrument
    ld      a, (CmdAddr2+2)             ; list for this track
    ToAddrBank
    ld      c, a
    
    ld      a, (CmdByte1)               ; Get number of instruments
    ld      b, a                        ; used by this sound effect
    
    ld      a, (CmdByte3)               ; Determine initial instrument list
    rrca                                ; ID for this SFX stream internally
    rrca                                ; (defaults to $A0, $C0 and $E0)
    rrca
    add     $A0
    
    call    LoadInstrList               ; Go load the instrument list
    
    ld      a, (CmdByte3)               ; You can pass two arguments for use
    add     a, a                        ; by SFX streams as they wish, these
    add     a, a                        ; are stored in VM variables. Since
    add     a, a                        ; we need to give each SFX its own
    add     a, 8*FIRST_SFXSTREAM        ; set of VM variables, determine in
    ld      h, VmVarList>>8             ; which ones to store the arguments.
    ld      l, a
    
    ld      de, (CmdByte4)              ; Store passed arguments
    ld      (hl), e                     ; E = CmdByte4 (1st argument)
    inc     l                           ; D = CmdByte5 (2nd argument)
    ld      (hl), d
    
    jp      EndOfCommand                ; Pop command off the queue

;****************************************************************************
; SubStreamOpcode [SONAOP_SUBSTREAM]
; Opcode that starts up a new stream within a BGM.
;----------------------------------------------------------------------------
; input c ..... current stream bank
; input hl .... current stream address
;----------------------------------------------------------------------------
; output c .... new stream bank
; output hl ... new stream address
;----------------------------------------------------------------------------
; breaks ...... a,bc,de,hl,a'
;****************************************************************************

SubStreamOpcode:
    ReadArgByte                         ; Get stream number
    ld      a, b
    ex      af, af'
    
    call    ReadAddressArg              ; Get address of the substream
                                        ; relative to the beginning of
                                        ; the stream
    
    push    bc                          ; InitStream wants the bank in C
    ld      c, a                        ; which holds the current stream
                                        ; bank so let's save it first
    
    ex      af, af'                     ; Now that we have the actual address
    add     a, a                        ; go set up the stream
    add     a, a
    add     a, a
    ld      l, a
    ld      h, StreamState>>8
    call    InitStream
    
    pop     bc                          ; Restore bank
    ret                                 ; End of subroutine

;****************************************************************************
; InitStreamFromCmd
; Initializes a stream from command queue arguments.
;----------------------------------------------------------------------------
; input hl ......... pointer to stream state to initialize
; input CmdAddr1 ... pointer to stream data (in 68000 format)
;----------------------------------------------------------------------------
; modifies ......... all
;****************************************************************************

InitStreamFromCmd:
    ex      de, hl                      ; Get pointer to the stream data
    ld      hl, (CmdAddr1+0)            ; and convert it to our internal
    ld      a, (CmdAddr1+2)             ; bank+address format
    ToAddrBank
    ex      de, hl
    
    ; fallthrough to InitStream...

;****************************************************************************
; InitStream
; Initializes a stream with the arguments in Z80 registers.
;----------------------------------------------------------------------------
; input hl ..... pointer to stream state to initialize
; input c/de ... pointer to stream data (bank+address)
;----------------------------------------------------------------------------
; modifies ..... all
;****************************************************************************

InitStream:
    ld      (hl), $00                   ; Set up stream stack (which also
    inc     l                           ; marks the stream as playing)
    
    ld      (hl), $01                   ; Make stream start playing as soon
    inc     l                           ; as possible (set up 1 tick left as
                                        ; the remaining delay)
    
    ld      b, 2                        ; Store it into the stream as both
InitStreamLoop:                         ; the current address and loop point
    ld      (hl), e                     ; (to-do? look to see if there's a
    inc     l                           ; way to make this take up even less
    ld      (hl), d                     ; space)
    inc     l
    ld      (hl), a
    inc     l
    djnz    InitStreamLoop
    
    ret                                 ; End of subroutine

;****************************************************************************
; GetChanOwnedByBgm
; Returns a bitmask of channels owned by the BGM stream (not owned by SFX).
;----------------------------------------------------------------------------
; output hl ... bit 0 = set if BGM owns FM1
;               bit 1 = set if BGM owns FM2
;               bit 2 = set if BGM owns FM3
;               bit 4 = set if BGM owns FM4
;               bit 5 = set if BGM owns FM5
;               bit 6 = set if BGM owns FM6
;               bit 8 = set if BGM owns PSG1
;               bit 9 = set if BGM owns PSG2
;               bit 10 = set if BGM owns PSG3
;               bit 11 = set if BGM owns PSG4
;               bit 14 = set if BGM owns PCM1
;               bit 15 = set if BGM owns PCM2
;               other bits are *undefined*, mask them away when needed
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl
;----------------------------------------------------------------------------
; to-do: move this subroutine to a better location
;****************************************************************************

GetChanOwnedByBgm:
    ld      de, ChanOwner+15            ; Make a list of which channels are
    ld      b, 16                       ; currently under BGM ownership into
CheckBgmOwnLoop:                        ; HL (one bit per channel, 1 = BGM
    ld      a, (de)                     ; owns it, 0 = SFX owns it)
    add     a, a                        ;
    adc     hl, hl                      ; A channel is marked as owned by BGM
    dec     e                           ; (not owned by SFX) when its owner
    djnz    CheckBgmOwnLoop             ; is -1/$FF, which means the MSB is
                                        ; set. So we push the MSB into carry
                                        ; and then into HL to quickly pull
                                        ; each bit into the mask.
    
    ret                                 ; End of subroutine