;****************************************************************************
; LoadFmOp [SONAOP_LOAD_FM1,FM2,FM3,FM3SP,FM4,FM5,FM6]
; Processes the stream opcode for loading a FM instrument.
;----------------------------------------------------------------------------
; 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'
;****************************************************************************
LoadFmOp:
PollPcm
ld a, b ; Get channel ID
and $07
cp $03 ; Was it ch3 special? (you should use
jr nz, LoadFmOpChOk ; ch3 normal instead, so let's fix it
dec a ; before continuing)
LoadFmOpChOk:
ld b, a ; Check if we own the channel (we
ex af, af' ; need to do this after the above
CheckOwner ; or it'll be wrong if somebody used
; FM3 special instead of FM3 normal)
call ReadInstrumentId ; Get instrument ID
JumpIfSfx LoadFmOpNotBgm ; If it's a BGM keep track of the
ex af, af' ; instrument ID in case we need to
ld d, BgmFmInstr>>8 ; restore it after a SFX stomps on
add a, BgmFmInstr&$FF ; the channel
ld e, a ;
sub BgmFmInstr&$FF ; TO-DO: is there a cleaner way to
ex de, hl ; do this? (this block of code needs
ld (hl), b ; to leave a',c,hl intact)
ex de, hl
ex af, af'
LoadFmOpNotBgm:
RetIfNotOwner ; Don't load the new instrument
ex af, af' ; into the YM2612 if we don't own
; this channel
push bc ; Save stream position, since these
push hl ; registers will get overwritten
call LoadFmInstr ; Load the instrument
pop hl ; Restore stream position
pop bc
ret ; End of subroutine
;****************************************************************************
; LoadFmInstr
; Loads a FM instrument.
;----------------------------------------------------------------------------
; input a .... channel ID (0,1,2,4,5,6)
; input b .... instrument ID (0..255)
;----------------------------------------------------------------------------
; modifies ... a,bc,de,hl,a',iy
; writes into ScratchBuf
;****************************************************************************
LoadFmInstr:
ex af, af'
PollPcm
ld h, InstrList>>8 ; Retrieve instrument addr+bank
ld l, b ; into c/hl
ld e, (hl)
inc h
ld d, (hl)
inc h
ld c, (hl)
ex de, hl
PollPcm
ex af, af' ; Stop the channel now while we have
ld b, a ; a chance (since all registers this
ex af, af' ; clobbers are free here)
ld a, b
call StopFmChannel
PollPcm
ex af, af'
; a = channel ID
; c = instrument bank
; hl = instrument address
ld b, a ; Determine which YM2612 bank
and 1<<2 ; to write to later
rrca ; ch1~ch3 = YmAddr1/YmData1
ld (LoadFmInstr_Base), a ; ch4~ch6 = YmAddr2/YmData2
ld a, b ; Determine register offset
and $03 ; ch1/ch4 = +0
ld (LoadFmInstr_Offset), a ; ch2/ch5 = +1
; ch3/ch6 = +2
ld a, b ; Compute pointer to variable where
add a, RealFmVol&$FF ; the volume to load with is stored
ld (LoadFmInstr_RealVolPtr), a ; (so instrument is loaded with the
; correct volume instead of loudest)
PollPcm
ld a, b ; The algorithm and TL values are
add a, a ; needed to do volume math, so we
add a, a ; need to store their values in a
add a, b ; buffer.
add a, FmVolumeRegs&$FF ; Determine the pointer to the
ld iyh, FmVolumeRegs>>8 ; fields for this channel.
ld iyl, a
PollPcm
; c = instrument bank
; hl = instrument address
ld a, SIZEOF_FMINSTR ; Load entire FM instrument into the
ld de, ScratchBuf ; scratch buffer (probably not the
; fastest method, but makes things
LoadFmInstr_ReadLoop: ; easier and we need to poke at some
ex af, af' ; of the data too)
ReadRomByte ;
ld a, b ; a = loop counter
ld (de), a ; b = byte being read
inc e ; c = current bank
ex af, af' ; hl = current address
dec a ; de = pointer to buffer
jp nz, LoadFmInstr_ReadLoop
PollPcm
LoadFmInstr_RealVolPtr: equ $+1
ld a, (RealFmVol) ; Read current volume of the channel
ld l, a ; so the instrument is loaded with
; the correct TL values
ld a, (LoadFmInstr_Algo) ; Retrieve the instrument's
and $07 ; algorithm (and remember to mask
; away the feedback value! yikes)
ld bc, (LoadFmInstr_TL_S1)
ld de, (LoadFmInstr_TL_S2)
ld (iy+FMVOLREG_ALGO), a ; Store these values where the FM
ld (iy+FMVOLREG_TL_S1), c ; volume routines can find them for
ld (iy+FMVOLREG_TL_S2), e ; adjusting later
ld (iy+FMVOLREG_TL_S3), b
ld (iy+FMVOLREG_TL_S4), d
ex af, af'
PollPcm
ex af, af'
call AdjustFmTL ; Apply the channel's attenuation
ld (LoadFmInstr_TL_S1), bc ; to our copy of the instrument data
ld (LoadFmInstr_TL_S2), de ; so it takes effect immediately
;
; If this looks "wrong", remember
; that register order is S1, S3, S2,
; S4 so it's really loading S1/S3
; into BC and S2/S4 into DE.
PollPcm
LoadFmInstr_Base: equ $+2
ld iy, YmAddr1
LoadFmInstr_Offset: equ $+1
ld a, $00 ; First write the algorithm/feedback
add a, YMREG_ALGO ; register since it's out of sequence
ld (iy+0), a ; compared to the rest
ld a, (LoadFmInstr_Algo) ;
LoadFmInstr_LoadCount: equ $+1 ; We also set up the loop counter and
ld b, SIZEOF_FMINSTR-1 ; pointer for the loop below to spend
ld hl, ScratchBuf+1 ; some time between the writes
ld (iy+1), a ;
; The loop count depends on whether
; SSG-EG is enabled or not.
PollPcm
ld a, (LoadFmInstr_Offset) ; Remaining instruments go from +$30
add a, YMREG_MULDT ; onwards in steps of 4, so loop
; through all of them and write them
LoadFmInstr_WriteLoop: ; to the YM2612
ld c, (hl) ;
ld (iy+0), a ; a = register number
ld (iy+1), c ; b = loop counter
push hl ; c = scratch
pop hl ; hl = pointer to buffer
add a, 4
inc l
djnz LoadFmInstr_WriteLoop
PollPcm
ret ; End of subroutine