;**************************************************************************** ; NoteOnFM ; Does a "note on" for a FM channel ;**************************************************************************** NoteOnFMSFX: call NoteOnFM ; We're just a wrapper jp ProcessSFXRun ; End of subroutine NoteOnFMBGM: ld b, a PollPCM push hl ld a, b and $07 ; Check if channel is free ld hl, RAM_Locked add l ld l, a ld a, (hl) pop hl or a jp nz, ProcessBGMSkip1 ; Don't play if locked ld a, b call NoteOnFM ; We're just a wrapper jp ProcessBGMRun ; End of subroutine NoteOnFM: and $07 ; Get channel ID ld (ix+0), $28 ; Note off ld (ix+1), a ld b, a ex af, af' PollPCM ld a, b ; Determine which port to write and $04 rrca ld iyl, a PollPCM call GetParam ; Get note PollPCM ex af, af' push de push hl ld d, a and $03 ; Index of first frequency register add $A4 ld e, a PollPCM ld h, FMFreqTable>>8 ; Get address of note ld a, b and $1F add FMFreqTable&$FF ld l, a PollPCM ld a, b ; Set new frequency and $E0 rrca rrca ld b, a ld a, (hl) or b ld (iy+0), e ld (iy+1), a PollPCM ld a, e sub 4 ld e, a dec l ld a, (hl) ld (iy+0), e ld (iy+1), a PollPCM ld a, d ; Note on! or $F0 ld (ix+0), $28 ld (ix+1), a pop hl pop de ret ; End of subroutine ;**************************************************************************** ; NoteOffFM ; Does a "note off" for a FM channel ;**************************************************************************** NoteOffFMSFX: call NoteOffFM ; We're just a wrapper jp ProcessSFXRun ; End of subroutine NoteOffFMBGM: ld b, a PollPCM ld a, b push hl and $07 ; Check if channel is free ld hl, RAM_Locked add l ld l, a ld a, (hl) pop hl or a jp nz, ProcessBGMSkip ; Don't stop if locked ld a, b call NoteOffFM ; We're just a wrapper jp ProcessBGMRun ; End of subroutine NoteOffFM: and $07 ; Get channel ID ld (ix+0), $28 ; Note off ld (ix+1), a ret ; End of subroutine ;**************************************************************************** ; SetFMNote* ; Sets the note of a FM channel without "note on" ;**************************************************************************** SetNoteFMSFX: call SetNoteFM ; We're just a wrapper jp ProcessSFXRun ; End of subroutine SetNoteFMBGM: ld b, a PollPCM ld a, b push hl and $07 ; Check if channel is free ld hl, RAM_Locked add l ld l, a ld a, (hl) pop hl or a jp nz, ProcessBGMSkip2 ; Don't change if locked ld a, b call SetNoteFM ; We're just a wrapper jp ProcessBGMRun ; End of subroutine SetNoteFM: push af ld b, a PollPCM ld a, b ; Determine which port to write and $04 rrca ld iyl, a PollPCM call GetParam ; Get high byte PollPCM pop af push de and $07 ld d, a and $03 ; Index of first frequency register add $A4 ld e, a PollPCM ld (iy+0), e ; Load high byte ld (iy+1), b PollPCM ; Load low byte call GetParam PollPCM ld a, e sub 4 ld e, a ld (iy+0), e ld (iy+1), b PollPCM pop de ret ; End of subroutine ;**************************************************************************** ; LoadFM* ; Loads a FM instrument ;**************************************************************************** LoadFMSFX: call LoadFMEvent ; We're just a wrapper jp ProcessSFXRun ; End of subroutine LoadFMBGM: and $07 ; Get channel ID ld b, a PollPCM push de push bc ld a, b ld de, RAM_BGMFMInstr ; Store instrument ID add e ld e, a PollPCM call GetParam PollPCM ld a, b ld (de), a ld a, e ; Reset volume add 8 ld e, a xor a ld (de), a ld e, c pop bc ld c, e pop de PollPCM push hl ; Check if channel is free ld a, b ld h, RAM_Locked>>8 add RAM_Locked&$FF ld l, a ld a, (hl) pop hl or a jp nz, ProcessBGMSkip ; Don't load if locked PollPCM ld a, b ; Get back instrument ID push hl ld hl, RAM_BGMFMInstr add l ld l, a ld b, (hl) pop hl ex af, af' PollPCM ex af, af' call LoadFMDirect ; We're just a wrapper jp ProcessBGMRun ; End of subroutine LoadFMEvent: and $07 ; Get channel ID ex af, af' PollPCM call GetParam ; Get instrument ID PollPCM ex af, af' LoadFMDirect: push af and $04 ; Determine which port to write rrca ld iyl, a PollPCM pop af push bc push de push hl ld h, RAM_PointerList>>8 ; Get instrument address ld l, b ld d, (hl) inc h ld e, (hl) inc h ld c, (hl) ex de, hl push af PollPCM ld b, 28/4 ; Load FM instrument into our scratch buffer, ld de, RAM_Scratch ; so we don't conflict with PCM bank .getinstr: ; switch (which would slow down A LOT) ld a, b ex af, af' call GetParam ex de, hl ld (hl), b ex de, hl inc e call GetParam ex de, hl ld (hl), b ex de, hl inc e call GetParam ex de, hl ld (hl), b ex de, hl inc e call GetParam ex de, hl ld (hl), b ex de, hl inc e ex af, af' ld b, a djnz .getinstr call GetParam ex de, hl ld (hl), b ex de, hl PollPCM pop af ; Kill ADSR ld b, a call KillFM ld a, b ld de, RAM_FMData ; Get address of FM data and $07 add e ld e, a push af and $03 ; Register index for operator add $B0 ld hl, RAM_Scratch ; Get address of buffer with the instrument ; data ex af, af' PollPCM ; Write operator ex af, af' ld (iy+0), a ld b, (hl) inc l ld (iy+1), b ex af, af' ; Store $B0 register in FM data buffer ld a, b ld (de), a ld a, e add 8 ld e, a PollPCM ex af, af' sub $B0-$30 ld b, 7*4 ; Load operator registers into the YM2612 .loadfmloop: ld (iy+0), a ld c, (hl) ld (iy+1), c add 4 inc l djnz .loadfmloop PollPCM ld a, l ; Store operator TLs (needed to set the sub 24 ; volume properly when it changes) ld l, a ld b, 4 .loadfmsavetl: ld a, (hl) ld (de), a ld a, e add 8 ld e, a inc l djnz .loadfmsavetl PollPCM pop af pop hl pop de pop bc ret ;**************************************************************************** ; SetFMVol* ; Sets the volume of a FM channel ;**************************************************************************** SetFMVolSFX: call SetFMVolSFXEvent ; We're just a wrapper jp ProcessSFXRun ; End of subroutine SetFMVolBGM: and $07 ld b, a PollPCM push de push bc ld a, b ld d, RAM_BGMFMVol>>8 ; Store BGM volume add RAM_BGMFMVol&$FF ld e, a PollPCM call GetParam PollPCM ex de, hl ld (hl), b ex de, hl ld e, c pop bc ld c, e pop de PollPCM push hl ld a, b ; Check if channel is free ld hl, RAM_Locked add l ld l, a ld a, (hl) pop hl or a jp nz, ProcessBGMSkip ; Don't change if locked PollPCM ld a, b push hl ld hl, RAM_BGMFMVol add l ld l, a ld b, (hl) pop hl push af PollPCM pop af call SetFMVolLoad ; We're just a wrapper jp ProcessBGMRun ; End of subroutine SetFMVolSFXEvent: push af PollPCM call GetParam ; Get new volume PollPCM pop af SetFMVolLoad: push bc push de push hl and $07 ; Get channel ID push af ld h, RAM_FMData>>8 ; Get address of FM data add RAM_FMData&$FF ld l, a ex af, af' PollPCM ex af, af' and $04 ; Determine which port to write rrca ld iyl, a PollPCM pop af and $03 ; Index of first volume register add $40 ld c, a ld a, (hl) ; Get algorithm and $07 ld e, a PollPCM ld a, l add 8 ld l, a ld a, e ; Process operator #1 cp $07 jr c, .noop1 ld (iy+0), c ld a, (hl) add b cp $7F jr c, .notooloud1 ld a, $7F .notooloud1: ld (iy+1), a .noop1: ld a, c add 4 ld c, a PollPCM ld a, l add 8 ld l, a ld a, e ; Process operator #2 cp $05 jr c, .noop2 ld (iy+0), c ld a, (hl) add b cp $7F jr c, .notooloud2 ld a, $7F .notooloud2: ld (iy+1), a .noop2: ld a, c add 4 ld c, a PollPCM ld a, l add 8 ld l, a ld a, e ; Process operator #3 cp $04 jr c, .noop3 ld (iy+0), c ld a, (hl) add b cp $7F jr c, .notooloud3 ld a, $7F .notooloud3: ld (iy+1), a .noop3: ld a, c add 4 ld c, a PollPCM ld a, l add 8 ld l, a ld (iy+0), c ld a, (hl) ; Process operator #4 add b cp $7F jr c, .notooloud4 ld a, $7F .notooloud4: ld (iy+1), a PollPCM pop hl pop de pop bc ret ; End of subroutine ;**************************************************************************** ; SetFMParam* ; Sets the different parameters of a FM channel ;**************************************************************************** SetFMParamSFX: ld b, a PollPCM ld a, b ; Determine which port to write and $04 rrca ld iyl, a ld a, b ex af, af' PollPCM call GetParam ; Get parameters PollPCM ex af, af' and $03 ; Get channel ID add $B4 ld (iy+0), a ; Set new parameters ld (iy+1), b jp ProcessSFXRun ; End of subroutine SetFMParamBGM: ld b, a PollPCM ld a, b ; Determine which port to write and $04 rrca ld iyl, a ld a, b ex af, af' PollPCM call GetParam ; Get parameters PollPCM ex af, af' push hl and $07 ; Store parameters ld h, RAM_BGMFMPan>>8 add RAM_BGMFMPan&$FF ld l, a ld (hl), b ex af, af' PollPCM ex af, af' and $07 ; Check if channel is free add RAM_Locked&$FF ld l, a ex af, af' ld a, (hl) or a pop hl jp nz, ProcessBGMRun ; Don't modify if locked PollPCM ex af, af' and $03 ; Get channel ID add $B4 ld (iy+0), a ; Set new parameters ld (iy+1), b jp ProcessBGMRun ; End of subroutine ;**************************************************************************** ; LockChannelFM [events $E0-$E7] ; Locks a FM channel ;**************************************************************************** LockChannelFM: and $07 ld b, a PollPCM push hl ld h, RAM_Locked>>8 ; Get address of channel to lock ld a, b add RAM_Locked&$FF ld l, a ld (hl), $01 ; Lock channel pop hl PollPCM ld a, b ; Determine which port to write and $04 rrca ld iyl, a ld a, b ; Reset FM parameters and $03 add a add a add $B4 ld (iy+0), a ld (iy+1), $C0 jp ProcessSFXRun ; End of subroutine ;**************************************************************************** ; KillFM ; Kills a FM channel ;**************************************************************************** KillFM: and $07 ; Get channel ID push af push de push hl ld c, a ; Determine to which port to write and $04 rrca ld iyl, a ld a, c and $03 ; Load dummy FM instrument add $40 ld c, 6 ld hl, DummyFMInstr .loaddummy: ex af, af' PollPCM ex af, af' ld e, (hl) ld (iy+0), a add 4 ld (iy+1), e nop ld (iy+0), a add 4 ld (iy+1), e nop ld (iy+0), a add 4 ld (iy+1), e nop ld (iy+0), a add 4 ld (iy+1), e inc l dec c jp nz, .loaddummy pop hl pop de PollPCM pop af ld c, a ; Cause the ADSR to reset or $F0 ld (ix+0), $28 ld (ix+1), c ld (ix+0), $28 ld (ix+1), a ld (ix+0), $28 ld (ix+1), c PollPCM ret ; End of subroutine ;**************************************************************************** ; RestoreFM ; Restores a FM channel (from BGM information) ;---------------------------------------------------------------------------- ; input b ... FM channel (0..7) ;---------------------------------------------------------------------------- ; breaks: af, hl ;**************************************************************************** RestoreFM: ld a, b ; Restore BGM FM instrument ld h, RAM_BGMFMInstr>>8 add RAM_BGMFMInstr&$FF ld l, a push bc ld a, b ld b, (hl) call LoadFMDirect pop bc PollPCM push bc ld a, l ; Restore BGM FM volume add 8 ld l, a ld a, b ld b, (hl) call SetFMVolLoad pop bc ld a, l ; Restore BGM FM panning add 8 ld l, a ld a, b and $03 add $B4 ld (iy+0), a ld a, (hl) ld (iy+1), a PollPCM ret ; End of subroutine