Port Sonic 3's Sound Driver to Sonic 1
From Sonic Retro
You probably saw a quick and dirty way to do this on SSRG, but lets do it the proper way, the way that sonic team would do it.
Contents
Overview
First off, Sonic 3's sound driver has the V_Int reloader built into it so it is not needed, some routines need replacement, sounds need fixing, and we need to replace the sounds and music. We will be using the hivebrain version, if you wish to do it via svn version, I am sure you can come up with a way that works by seeing what changes I made in the hivebrain version.
Preparing to use Sonic 3/K/3K sound system
The Vertical Interrupt and Horizontal Interrupt need to be fixed, since the sonic 1/2 system uses them but the Sonic 3/K/3K system doesn't.
first we will fix the vertical interrupt, by changing: <asm> loc_B5E: ; XREF: loc_B88 jsr (sub_71B4C).l </asm>
to: <asm> loc_B5E: ; XREF: loc_B88 nop </asm>
Now we locate: <asm> loc_119E: ; XREF: PalToCRAM clr.b ($FFFFF64F).w movem.l d0-a6,-(sp) bsr.w Demo_Time jsr (sub_71B4C).l movem.l (sp)+,d0-a6 rte
- End of function PalToCRAM
</asm> See another familiar line? Yes, you've got it, do the same thing to it: <asm> loc_119E: ; XREF: PalToCRAM clr.b ($FFFFF64F).w movem.l d0-a6,-(sp) bsr.w Demo_Time nop movem.l (sp)+,d0-a6 rte
- End of function PalToCRAM
</asm> That effectively disables the sonic1 style v_int and h_int driver reloading that we will not need and prevents a nasty error down the road.
Upgrading the Load Driver Routine
Okay, we disabled the sonic1 driver junk, wouldn't we want now to use the sonic3 driver instead? Here is where we start installing it. Locate: <asm>
- ---------------------------------------------------------------------------
- Subroutine to load the sound driver
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen
nop
move.w #$100,($A11100).l ; stop the Z80
move.w #$100,($A11200).l ; reset the Z80
lea (Kos_Z80).l,a0 ; load sound driver
lea ($A00000).l,a1
bsr.w KosDec ; decompress
move.w #0,($A11200).l
nop
nop
nop
nop
move.w #$100,($A11200).l ; reset the Z80
move.w #0,($A11100).l ; start the Z80
rts
- End of function SoundDriverLoad
</asm>
and replace it all with: <asm>
- ---------------------------------------------------------------------------
- Subroutine to load the sound driver
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
SoundDriverLoad: ; XREF: GameClrRAM; TitleScreen nop move.w #$100,($A11100).l ; Z80 bus request - Start move.w #$100,($A11200).l ; Z80 stop reset lea (DriverData).l,a0 lea ($A00000).l,a1 move.w #DriverDataEnd-DriverData,d0
DriverLoadLoop: move.b (a0)+,(a1)+ dbf d0,DriverLoadLoop lea (DriverPointers).l,a0 lea ($A01300).l,a1 move.w #DriverPointersEnd-DriverPointers,d0
DriverPointersLoadLoop: move.b (a0)+,(a1)+ dbf d0, DriverPointersLoadLoop lea (UniversalVoiceBank).l,a0 lea ($A017D8).l,a1 move.w #UniversalVoiceBankEnd-UniversalVoiceBank,d0
UniversalVoiceBankLoadLoop: move.b (a0)+,(a1)+ dbf d0,UniversalVoiceBankLoadLoop lea (DriverResetData).l,a0 lea ($A01C00).l,a1 move.w #DriverResetDataEnd-DriverResetData,d0
DriverResetDataLoadLoop: move.b (a0)+,(a1)+ dbf d0,DriverResetDataLoadLoop btst #6,($FFFFFFF8).w beq.s DriverAlreadyInitialized move.b #1,($A01C02).l
DriverAlreadyInitialized: move.w #0,($A11200).l nop nop nop nop move.w #$100,($A11200).l ; Z80 start reset move.w #0,($A11100).l ; Z80 bus request - Stop rts
- End of function SoundDriverLoad
DriverResetData: dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 DriverResetDataEnd: </asm>
Upgrading the Playback Routines
Now we have the code to load the new driver, time to add the new playback routines.
First the PlaySound routine, locate: <asm>
- ---------------------------------------------------------------------------
- Subroutine to play a sound or music track
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound:
move.b d0,($FFFFF00A).w
rts
- End of function PlaySound
</asm>
and replace it completely with: <asm>
- ---------------------------------------------------------------------------
- Subroutine to play a sound or music track
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound:
cmpi.w #$FB,d0
blt.s PlayNotSpecialFlag
bhi.s TestForNormalSpeed
move #8,d0
jmp SetTempo
TestForNormalSpeed: cmpi.w #$FC,d0 bne.s PlayNotSpecialFlag clr.w d0 jmp SetTempo
PlayNotSpecialFlag: move.w #$100,($A11100).l
PlaySoundZ80NotStopped: btst #0,($A11100).l bne.s PlaySoundZ80NotStopped ; loop until it says it's stopped move.b d0,($A01C0A).l move.w #0,($A11100).l rts
- End of function PlaySound
- ---------------------------------------------------------------------------
- Exclusive sound/music subroutine
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound_Ex:
tst.b 4(A0)
bpl.s SkipPlaySound_Special
</asm>
Now we will replace that unused routine with something more appropriate as well as upgrade the PlaySound_Special routine, find: <asm>
- ---------------------------------------------------------------------------
- Subroutine to play a special sound/music (E0-E4)
- E0 - Fade out
- E1 - Sega
- E2 - Speed up
- E3 - Normal speed
- E4 - Stop
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PlaySound_Special:
move.b d0,($FFFFF00B).w
rts
- End of function PlaySound_Special
- ===========================================================================
- ---------------------------------------------------------------------------
- Unused sound/music subroutine
- ---------------------------------------------------------------------------
PlaySound_Unk: move.b d0,($FFFFF00C).w rts </asm>
and replace it entirely with: <asm>
- ---------------------------------------------------------------------------
- Unused sound/music subroutine
- ---------------------------------------------------------------------------
PlaySound_Unk: nop
- ---------------------------------------------------------------------------
- Subroutine to play a special sound/music (FB-FF)
- ---------------------------------------------------------------------------
PlaySound_Special: move.w #$100,($A11100).l
PlaySound_SpecialZ80NotStopped: btst #0,($A11100).l bne.s PlaySound_SpecialZ80NotStopped cmp.b ($A01C0B).l,d0 beq.s PlaySound_Special1 tst.b ($A01C0B).l bne.s PlaySound_Special0 move.b d0,($A01C0B).l move.w #0,($A11100).l rts
PlaySound_Special0: move.b d0,($A01C0C).l
PlaySound_Special1: move.w #0,($A11100).l
SkipPlaySound_Special: rts
- End of function PlaySound_Special
- ---------------------------------------------------------------------------
- Subroutine to change the music tempo
- ---------------------------------------------------------------------------
SetTempo: move.w #$100,($A11100).l
SetTempoZ80NotStopped: btst #0,($A11100).l bne.s SetTempoZ80NotStopped move.b D0,($A01C08).l move.w #0,($A11100).l rts </asm>
Upgrading Pause / Resume routines
Now the playback routines are fixed and we have a routine to set the tempo the way the sneakers do, which we will elaberate later on, but we still have to update the pause / resume routines to the sonic 3 equivilents.
Find: <asm>
- ---------------------------------------------------------------------------
- Subroutine to pause the game
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PauseGame: ; XREF: Level_MainLoop; et al
nop
tst.b ($FFFFFE12).w ; do you have any lives left?
beq.s Unpause ; if not, branch
tst.w ($FFFFF63A).w ; is game already paused?
bne.s loc_13BE ; if yes, branch
btst #7,($FFFFF605).w ; is Start button pressed?
beq.s Pause_DoNothing ; if not, branch
loc_13BE: move.w #1,($FFFFF63A).w ; freeze time move.b #1,($FFFFF003).w ; pause music
loc_13CA: move.b #$10,($FFFFF62A).w bsr.w DelayProgram tst.b ($FFFFFFE1).w ; is slow-motion cheat on? beq.s Pause_ChkStart ; if not, branch btst #6,($FFFFF605).w ; is button A pressed? beq.s Pause_ChkBC ; if not, branch move.b #4,($FFFFF600).w ; set game mode to 4 (title screen) nop bra.s loc_1404
- ===========================================================================
Pause_ChkBC: ; XREF: PauseGame btst #4,($FFFFF604).w ; is button B pressed? bne.s Pause_SlowMo ; if yes, branch btst #5,($FFFFF605).w ; is button C pressed? bne.s Pause_SlowMo ; if yes, branch
Pause_ChkStart: ; XREF: PauseGame btst #7,($FFFFF605).w ; is Start button pressed? beq.s loc_13CA ; if not, branch
loc_1404: ; XREF: PauseGame move.b #$80,($FFFFF003).w
Unpause: ; XREF: PauseGame move.w #0,($FFFFF63A).w ; unpause the game
Pause_DoNothing: ; XREF: PauseGame rts
- ===========================================================================
Pause_SlowMo: ; XREF: PauseGame move.w #1,($FFFFF63A).w move.b #$80,($FFFFF003).w rts
- End of function PauseGame
</asm>
and replace it completely with: <asm>
- ---------------------------------------------------------------------------
- Subroutine to pause the game
- ---------------------------------------------------------------------------
- ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||
PauseGame: ; XREF: Level_MainLoop; et al
nop
tst.b ($FFFFFE12).w
beq Unpause
tst.w ($FFFFF63A).w
bne.s PauseGame_AlreadyPaused
move.b ($FFFFF605).w,d0
or.b ($FFFFF607).w,d0
andi.b #$80,d0
beq Pause_DoNothing
PauseGame_AlreadyPaused: move.w #1,($FFFFF63A).w move.w #$100,($A11100).l
PauseGameZ80NotStopped: btst #0,($A11100).l bne.s PauseGameZ80NotStopped move.b #1,($A01C10).l move.w #0,($A11100).l
PauseGameLoop: move.b #$10,($FFFFF62A).w jsr DelayProgram tst.b ($FFFFFFD1).w beq.s Pause_ChkStart btst #6,($FFFFF605).w beq.s Pause_ChkBC move.b #$28,($FFFFF600).w nop bra.s PauseGame1
Pause_ChkBC: btst #4,($FFFFF604).w bne.s Pause_SlowMo btst #5,($FFFFF605).w bne.s Pause_SlowMo
Pause_ChkStart: cmpi.b #$E,($FFFFFE10).w bcs.s PauseGame0 cmpi.b #$12,($FFFFFE10).w bhi.s PauseGame0 tst.b ($FFFFFF8B).w bpl.s PauseGame0 btst #4,($FFFFF605).w beq.s PauseGame0 move.b #$C0,($FFFFF600).w bra.s PauseGame1
PauseGame0: move.b ($FFFFF605).w,d0 or.b ($FFFFF607).w,d0 andi.b #$80,d0 beq.s PauseGameLoop
PauseGame1: move.w #$100,($A11100).l
Pause_ChkStartZ80NotStopped: btst #0,($A11100).l bne.s Pause_ChkStartZ80NotStopped move.b #$80,($A01C10).l move.w #0,($A11100).l
Unpause: move.w #0,($FFFFF63A).w
Pause_DoNothing: rts
Pause_SlowMo: move.w #1,($FFFFF63A).w move.w #$100,($A11100).l
Pause_SlowMoZ80NotStopped: btst #0,($A11100).l bne.s Pause_SlowMoZ80NotStopped move.b #$80,($A01C10).l move.w #0,($A11100).l rts
- End of function PauseGame
</asm>