;****************************************************************************
; StopAllFm
; Stops all FM channels (see StopFmChannel for details).
;----------------------------------------------------------------------------
; breaks .... a,bc,de,hl,iy,a'
;****************************************************************************
StopAllFm:
; ld c, 6 ; Stop every FM channel
;StopAllFmLoop: ;
; ld a, c ; Note that we scan backwards to make
; cp 3 ; the loop simpler, and that we skip
; call nz, StopFmChannel ; channel $03 since it isn't real and
; dec c ; can cause trouble
; jp p, StopAllFmLoop ;
; ; Using JP P instead of JP NC because
; ; turns out that DEC doesn't change
; ; the carry flag (what?)
;
; ret ; End of subroutine
ld e, $77 ; Set up channel mask to include
; fallthrough to StopFmByMask... ; every FM channel and reuse the
; routine below to do the job
;****************************************************************************
; StopFmByMask
;
; Stops FM channels as specified by the passed bit mask (see StopFmChannel
; for details). It's one bit per channel, which bit = 1 to indicate that we
; want to stop that channel. UNUSED BITS MUST BE 0 FOR CORRECT FUNCTION.
;----------------------------------------------------------------------------
; input e ... bit 0 = set to mute FM1
; bit 1 = set to mute FM2
; bit 2 = set to mute FM3
; bit 3 = must be 0
; bit 4 = set to mute FM4
; bit 5 = set to mute FM5
; bit 6 = set to mute FM6
; bit 7 = must be 0
;----------------------------------------------------------------------------
; breaks .... a,bc,de,iy
;****************************************************************************
StopFmByMask:
ld d, 7 ; Loop through every channel
StopFmByMaskLoop: ;
ld a, d ; D = FM channel ID
sla e ; E = FM channel mask
call c, StopFmChannel ;
dec d ; This loop pushes the next channel's
jp p, StopFmByMaskLoop ; bit into carry and then we call
; StopFmChannel whenever that bit was
; set (indicating we should stop it)
;
; Using JP P instead of JP NC because
; turns out that DEC doesn't change
; the carry flag (what?)
ret ; End of subroutine
;****************************************************************************
; StopFmChannel
;
; Stops a FM channel's output immediately. Used to "deinitialize" a FM
; channel when we need to shut down all sound or are about to load a new
; instrument or something like that.
;----------------------------------------------------------------------------
; input a .... channel ID (0,1,2,4,5,6)
; input ix ... pointer to YmAddr1
;----------------------------------------------------------------------------
; breaks ..... a,b,iyl
;----------------------------------------------------------------------------
; note: this "mutes" the FM channel by setting release rate to maximum and
; forcing key-off, but it still takes 128 samples to actually decay
; completely. Make sure to account for the need to wait this long (or at
; least long enough that it's practically unhearable).
;****************************************************************************
StopFmChannel:
ld b, a ; Keep a copy of the channel ID
; since we're gonna manipulate it
; a bunch of times
ld (StopFmKeyCmd), a ; Store the channel ID into the
; instruction that does the key-off
; write later to shut down output
and $04 ; Determine which bank
rrca ; this channel uses
ld iy, YmAddr1 ;
ld iyl, a ; ch1~3 = YmAddr1/YmData1
; ch4~6 = YmAddr2/YmData2
ld a, b ; Get release rate register number
and $03
add a, YMREG_RR
ld b, 4 ; Go through all operators and set
StopFmLoopRR: ; their release rate to maximum
ld (iy+0), a ; (which should make their release
ld (iy+1), $0F ; instantaneous on key-off)
push hl
pop hl
push hl
pop hl
add 4
djnz StopFmLoopRR
ld (ix+0), YMREG_KEY ; Key-off the channel to force it
ld (ix+1), $00 ; into the release phase, which is
StopFmKeyCmd: equ $-1 ; what should mute it for good
ret ; End of subroutine