;****************************************************************************
; SetPan [SONAOP_PAN_FM1..FM6]
; Handles the stream event for changing the panning for a FM channel.
;----------------------------------------------------------------------------
; input b ..... channel ID (bits 2-0 only)
; input c ..... current stream bank
; input hl .... current stream address
;----------------------------------------------------------------------------
; output c .... new stream bank
; output hl ... new stream address
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;****************************************************************************
SetPan:
ld a, PAN_MASK^$FF ; Panning opcode doesn't overwrite
; the PMS/AMS bits so set up the mask
; to exclude those
; fallthrough to SetPanCommon... ; Panning and PMS/AMS are so similar
; that they share most of the code,
; so the code below applies to both
; of them
;----------------------------------------------------------------------------
SetPanCommon:
ld (SetPanMask), a ; Store mask used to filter out the
; register bits that won't change
PollPcm
ld a, b ; Isolate channel ID
and $07
cp $03 ; Check if it's the opcode for FM3
jr nz, SetPanChOk ; special, in which case we change it
dec a ; since it should work the same way
SetPanChOk: ; as for FM3 normal
ld b, a
CheckOwner ; Check if we own the channel (we
; need to do this after the above
; or it'll be wrong if somebody used
; FM3 special instead of FM3 normal)
ld a, i ; Compute pointer to where panning
and SfxFmParam-BgmFmParam ; and PMS/AMS data is stored
add a, b
add a, BgmFmParam&$FF
ld d, BgmFmParam>>8
ld e, a
PollPcm
ld a, b ; Determine base port address
and $04 ; to use for this channel
rrca ;
ld iyl, a ; FM1~FM3 = YmAddr1/YmData1
ld iyh, YmAddr1>>8 ; FM4~FM6 = YmAddr2/YmData2
ld a, b ; Determine register number
and $03 ;
add a, YMREG_PAN ; FM1/FM4 = YMREG_PAN+0
ld (SetPanRegNum), a ; FM2/FM5 = YMREG_PAN+1
; FM3/FM6 = YMREG_PAN+2
ReadArgByte ; Get new value
ld a, (de) ; Retrieve old value and replace
SetPanMask: equ $+1 ; the bits with the new ones (this
and $00 ; byte stores all panning, PMS and
or b ; AMS so we need to keep the ones
ld (de), a ; we don't touch intact)
;
; The mask is overwritten depending
; on the opcode, it's $3F when we
; change panning, $C8 when we change
; PMS/AMS setting
ex af, af' ; Don't touch the YM2612 setting if
RetIfNotOwner ; we don't control this channel
SetPanRegNum: equ $+3
ld (iy+0), YMREG_PAN ; Write new panning/PMS/AMS setting
ex af, af' ; to the YM2612
SetPanOrMask: equ $+1 ;
or $00 ; The OR mask is used to toggle
ld (iy+1), a ; between stereo and mono (it becomes
; PAN_BOTH when forcing mono)
ret ; End of subroutine
;****************************************************************************
; SetStereoModeCmd [CMD_SETSTEREO]
; Toggles whether Sona is allowed to play in stereo or not.
;----------------------------------------------------------------------------
; input CmdArg1 ... 0 to force mono, otherwise to allow stereo
;----------------------------------------------------------------------------
; continues in EndOfCommand
;****************************************************************************
SetStereoModeCmd:
ld a, (CmdArg1) ; Determine whether stereo should
or a ; be enabled or not
jr z, SetMonoModeCmd ;
ld a, PAN_BOTH ; arg == 0 Stereo OK
SetMonoModeCmd: ; arg != 0 Force mono
xor PAN_BOTH
ld (SetPanOrMask), a ; Rewrite the instructions that
ld (RestorePanOrMask), a ; touch the panning registers to
; meddle with whether the speaker
; bits are forcefully set or not
; TO-DO: the OR mask approach fails to work when setting panning to
; YMPAN_NONE, which should essentially mute the channels (ignoring
; certain DAC quirk). We probably need to adjust this later.
; TO-DO: forcing mono means that channels that should be panned to the
; left or right speaker will sound louder than intended. We need to
; adjust the volume code as well.
jp EndOfCommand ; End of subroutine