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