Sona 0.50 Source

Sona 0.50/src-z80/fm_key.z80

;****************************************************************************
; Fm3NormalKeyOnOp [SONAOP_KEYON_FM3]
; Handles key-on events in a stream for FM ch3 in normal mode.
;----------------------------------------------------------------------------
; input b ..... channel ID (bits 2-0 must be = 010)
; 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'
;****************************************************************************

Fm3NormalKeyOnOp:
    CheckOwner
    CallIfOwner SetNormalFm3            ; Set ch3 to normal mode
    ; fallthrough to FmKeyOnOp...       ; Now continue like other channels

;****************************************************************************
; FmKeyOnOp [SONAOP_KEYON_FM1,FM2,FM4,FM5,FM6]
; Handles key-on events in a stream for most FM channels.
;----------------------------------------------------------------------------
; 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'
;****************************************************************************

FmKeyOnOp:
    PollPcm
    CheckOwner
    
    ld      a, b                        ; Isolate channel ID
    and     $07
    ld      b, a
    
    or      $F0                         ; Store the value to write to do
    ld      (FmKeyOnOp_KeyOn), a        ; a key-on for all the operators
                                        ; for the channel later
    
    CheckOwner                          ; Don't do key-on if we don't own
    JumpIfNotOwner FmSetPitchOp_NoMask  ; the channel, only fetch the pitch
                                        ; (and save it if needed)
    
    ld      (ix+0), YMREG_KEY           ; Key-off all the operators
    ld      (ix+1), b                   ; for that channel
    
    call    FmSetPitchOp_NoMask         ; Fetch the pitch and set it
    
    PollPcm
    
    ld      (ix+0), YMREG_KEY           ; Key-on all the operators
FmKeyOnOp_KeyOn: equ $+3                ; for that channel now
    ld      (ix+1), 0
    
    ret                                 ; End of subroutine

;****************************************************************************
; Fm3NormalSetPitchOp [SONA_PITCH_FM3]
; Handles the stream event for changing the pitch of FM3 ch3 in normal mode.
;----------------------------------------------------------------------------
; input b ..... channel ID (bits 2-0 must be = 010)
; 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'
;****************************************************************************

Fm3NormalSetPitchOp:
    CheckOwner
    CallIfOwner SetNormalFm3            ; Set ch3 to normal mode
    ; fallthrough to FmSetPitchOp...    ; Continue as any other channel

;****************************************************************************
; FmSetPitchOp [SONAOP_PITCH_FM1,FM2,FM4,FM5,FM6]
; Handles the stream event for changing the pitch for most FM channels.
;----------------------------------------------------------------------------
; 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'
;****************************************************************************

FmSetPitchOp:
    CheckOwner
    
    ld      a, b                        ; Mask away the channel ID
    and     $07                         ; from the opcode
    ld      b, a
    
FmSetPitchOp_NoMask:
    PollPcm
    
    ld      a, i                        ; Compute pointer to where the
    and     SfxFmPitch-BgmFmPitch       ; pitch for this channel is stored
    add     a, BgmFmPitch&$FF
    add     a, b
    add     a, b
    ld      d, BgmFmPitch>>8
    ld      e, a
    
    PollPcm
    
    ld      a, b                        ; Determine YM2612 bank for this
    and     $04                         ; channel
    rrca
    ld      iyl, a
    ld      iyh, YmAddr1>>8
    
    ld      a, b                        ; Determine YM2612 register number
    and     $03                         ; for the high frequency byte for
    add     YMREG_FREQ_HI               ; this channel
    ld      (FmSetPitchOp_RegNum), a
    
    call    ReadPitchArg                ; Parse the pitch
    
    RetIfNotOwner                       ; Don't touch the YM2612's frequency
                                        ; if we don't have control over this
                                        ; channel
    
FmSetPitchOp_RegNum: equ $+1
    ld      b, 0                        ; Go set the frequency
    jp      SetYm2612Freq

;****************************************************************************
; Fm3SpecialKeyOnOp [SONAOP_KEYON_FM3SP]
; Handles key-on events in a stream for FM ch3 in special mode.
;----------------------------------------------------------------------------
; 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'
;****************************************************************************

Fm3SpecialKeyOnOp:
    PollPcm
    
    dec     b                           ; Adjust channel ID so CheckOwner
                                        ; looks for the correct channel
    
    CheckOwner                                  ; Don't do key-on if we don't
    JumpIfNotOwner Fm3SpecialSetPitchOp_NoCheck ; own the channel, only fetch
                                                ; the pitches (and save them
                                                ; if needed)
    
    ld      (ix+0), YMREG_KEY           ; Key-off all ch3 operators
    ld      (ix+1), $02
    
    call    Fm3SpecialSetPitchOp        ; Fetch all the pitches and set them
    
    ld      (ix+0), YMREG_KEY           ; Key-on all ch3 operators
    ld      (ix+1), $F2
    
    ret                                 ; End of subroutine

;****************************************************************************
; Fm3SpecialSetPitchOp [SONAOP_PITCH_FM3SP]
; Handles the event to set the pitch for all operators in FM3 special mode.
;----------------------------------------------------------------------------
; 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'
;****************************************************************************

Fm3SpecialSetPitchOp:
    dec     b                           ; Adjust channel ID so CheckOwner
                                        ; looks for the correct channel
    
    CheckOwner
    
Fm3SpecialSetPitchOp_NoCheck:
    CallIfOwner SetSpecialFm3           ; Switch ch3 to special mode
    ld      iy, YmAddr1                 ; ch3 is in bank 0
    
    ld      d, BgmFmPitch>>8            ; Upper byte of the pitch pointers
                                        ; is always the same so set it once
    
    ld      a, i                        ; Compute offset to FM3's S1 value
    and     SfxFmPitch-BgmFmPitch       ; (whether we use the BGM or the
    add     BgmFmPitch_Fm3_S1&$FF       ; SFX values)
    
    ld      e, a                        ; Read pitch for S1 and set it
    call    ReadPitchArg
    ld      b, YMREG_SPFREQ_S1HI
    CallIfOwner SetYm2612Freq
    
    dec     e                           ; Read pitch for S2 and set it
    dec     e
    call    ReadPitchArg
    ld      b, YMREG_SPFREQ_S2HI
    CallIfOwner SetYm2612Freq
    
    ld      a, e                        ; Read pitch for S3 and set it
    sub     BgmFmPitch_Fm3_S2-BgmFmPitch_Fm3_S3
    ld      e, a
    call    ReadPitchArg
    ld      b, YMREG_SPFREQ_S3HI
    CallIfOwner SetYm2612Freq
    
    dec     e                           ; Read pitch for S4 and set it
    dec     e
    call    ReadPitchArg
    ld      b, YMREG_SPFREQ_S4HI
    JumpIfOwner SetYm2612Freq
    
    ret                                 ; End of subroutine in case we
                                        ; don't control the channel

;****************************************************************************
; SetYm2612Freq
;
; Sets the YM2612 frequency of a FM channel based on the wanted pitch value.
; Low level routine called by the event handlers above.
;----------------------------------------------------------------------------
; input b .... YM2612 register number of high byte of frequency
; input iy ... pointer to YmAddr1/YmAddr2 as needed
; input de ... pointer to pitch value
;----------------------------------------------------------------------------
; modifies ... a,b
;----------------------------------------------------------------------------
; notes: b = YMREG_FREQ_HI+0    for FM1 or FM4
;            YMREG_FREQ_HI+1    for FM2 or FM5
;            YMREG_FREQ_HI+2    for FM3 or FM6
;            YMREG_SPFREQ_S1HI  for FM3:S1
;            YMREG_SPFREQ_S2HI  for FM3:S2
;            YMREG_SPFREQ_S3HI  for FM3:S3
;            YMREG_SPFREQ_S4HI  for FM3:S4
;****************************************************************************

SetYm2612Freq:
    push    bc                          ; Save registers
    push    de
    PollPcm
    
    ld      a, (de)                     ; Get octave, move it where it will
    add     a, a                        ; go in the YM2612 register write and
    add     a, a                        ; save it for later
    add     a, a
    ld      c, a
    
    inc     e                           ; Get pointer to the YM2612 frequency
    ld      a, (de)                     ; to use for the current pitch. Note
    and     $3F<<2                      ; that we advance it by one since the
    rrca                                ; YM2612 wants the high byte first
    add     a, (FmFreqTable&$FF)+1      ; and the table is little endian.
    ld      d, FmFreqTable>>8
    ld      e, a
    
    PollPcm
    
    ld      a, (de)                     ; Write the high byte of the
    or      c                           ; channel frequency
    ld      (iy+0), b
    ld      (iy+1), a
    
    ld      a, b                        ; Write low byte of the channel
    sub     $04                         ; frequency
    ld      b, a                        ;
    dec     e                           ; Pop registers here while we can
    ld      a, (de)                     ; (to waste a few more cycles so
    pop     de                          ; the YM2612 doesn't complain)
    ld      (iy+0), b
    pop     bc
    ld      (iy+1), a
    
    PollPcm
    ret                                 ; End of subroutine

;****************************************************************************
; FmKeyOffOp [SONAOP_KEYOFF_FM1,FM2,FM3,FM3SP,FM4,FM5,FM6]
; Handles key-off for FM channels.
;----------------------------------------------------------------------------
; 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'
;****************************************************************************

FmKeyOffOp:
    PollPcm
    
    ld      a, b                        ; Isolate channel ID
    and     $07
    
    cp      $03                         ; Make sure it isn't the ID for ch3
    jr      nz, FmKeyOffOpChOk          ; special, and if it is then make it
    dec     a                           ; behave like ch3 normal
FmKeyOffOpChOk:
    ld      b, a
    
    CheckOwner                          ; Do nothing if we don't have control
    RetIfNotOwner                       ; over this channel (we need to do
                                        ; this check after we've corrected
                                        ; for the ch3 special case)
    
    ld      (ix+0), YMREG_KEY           ; Send a key-off to all operators
    ld      (ix+1), b                   ; for this FM channel
    
    ret                                 ; End of subroutine