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