Sona 0.50 Source

Sona 0.50/src-z80/fm_pan.z80

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