Port Sonic 3's Sound Driver to Sonic 2
From Sonic Retro
(Original guide by Kram1024)
Updated and fixed by User:WBilgini
You probably saw my method of placing the Sonic 3 sound driver in Sonic 1. Well, we are going to put it in Sonic 2 Rev01 this time. The changes are very similar to that of the Sonic 1 port. Over the years, I found that the S3 driver actually works without a Sonic game with minimal hacking (it just needs a wait routine to time it). Thus it is actually possible to put it into Sonic 2 with relative ease as well and into a homebrew too, though in homebrew it might not be legal anyways due to IP laws but Sega no longer enforces them on Genesis games anyways, thus why nobody gets sued for Sonic hacks, but let's get back to the hack.
Contents
- 1 Overview
- 2 Preparing to Use Sonic 3/K/3K Sound System
- 3 Upgrading the SoundDriverLoad Routine
- 4 Upgrading the Playback Routines
- 5 Upgrading Pause / Resume Routines
- 6 Driver Data Files
- 7 Fixing the Music and Sound Effects
- 8 Fixing the special sounds
- 9 Fixing the Sneakers
- 10 Optional: Apply New Sounds to Existing Objects
- 11 Optional: Fix the 2P VS Screen
- 12 Optional: Fix the Sound Test
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 latest Github version of Sonic 2, if you wish to do it via a different version, I am sure you can come up with a way that works by seeing what changes I made in the Github 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.
Removal of all Callbacks to the S2 Driver in the Vertical Interrupt
Unlike the S1 driver, Sonic 2 actually calls the driver over and over again after stopping the Z80 then restarting the Z80. The PlaySound routine of our new driver will do that automatically, so we do not really need any of that kind of code. We will search for all instances of:
stopZ80 ; stop the Z80 bsr.w sndDriverInput ; give input to the sound driver startZ80 ; start the Z80
and comment it out or just plain remove it. I prefer to replace it with a nop instruction, but even comments or removal work as well. In case you are a little lazy, here is a list of routines with this code:
- VintSub0
- Loc_54A
- Vint0_noWater (only touch the " bsr.w sndDriverInput ; give input to the sound driver" line)
- loc_748 (only touch the " bsr.w sndDriverInput ; give input to the sound driver" line)
- Vint_Pause_specialStage (only touch the " jsr (sndDriverInput).l)
- off_97A (only touch the " jsr (sndDriverInput).l)
- loc_BD6 (only touch the " jsr (sndDriverInput).l)
- VintSub18 (only touch the " bsr.w sndDriverInput)
- VintSub16 (only touch the " bsr.w sndDriverInput)
- Loc_EFE (only touch the " bsr.w sndDriverInput)
Removal of sndDriverInput
Okay, we disabled the Sonic 2 Z80 sound driver junk, but we still have a vestige of the old driver taking up valuable ROM space. Locate:
sndDriverInput: lea (Music_to_play&$00FFFFFF).l,a0 lea (Z80_RAM+zAbsVar).l,a1 ; $A01B80 cmpi.b #$80,zVar.QueueToPlay(a1) ; If this (zReadyFlag) isn't $80, the driver is processing a previous sound request. bne.s loc_10C4 ; So we'll wait until at least the next frame before putting anything in there. _move.b 0(a0),d0 beq.s loc_10A4 _clr.b 0(a0) bra.s loc_10AE ; --------------------------------------------------------------------------- loc_10A4: move.b 4(a0),d0 ; If there was something in Music_to_play_2, check what that was. Else, just go to the loop. beq.s loc_10C4 clr.b 4(a0) loc_10AE: ; Check that the sound is not FE or FF move.b d0,d1 ; If it is, we need to put it in $A01B83 as $7F or $80 respectively subi.b #MusID_Pause,d1 bcs.s loc_10C0 addi.b #$7F,d1 move.b d1,zVar.StopMusic(a1) bra.s loc_10C4 ; --------------------------------------------------------------------------- loc_10C0: move.b d0,zVar.QueueToPlay(a1) loc_10C4: moveq #4-1,d1 ; FFE4 (Music_to_play_2) goes to 1B8C (zMusicToPlay), - move.b 1(a0,d1.w),d0 ; FFE3 (unk_FFE3) goes to 1B8B, (unknown) beq.s + ; FFE2 (SFX_to_play_2) goes to 1B8A (zSFXToPlay2), tst.b zVar.SFXToPlay(a1,d1.w) ; FFE1 (SFX_to_play) goes to 1B89 (zSFXToPlay). bne.s + clr.b 1(a0,d1.w) move.b d0,zVar.SFXToPlay(a1,d1.w) + dbf d1,- rts ; End of function sndDriverInput
and remove it. We don't need it anymore because the Sonic 3 driver does this stuff on its own, in its own way.
Upgrading the SoundDriverLoad Routine
Okay, now we are finally ready to install the Sonic 3 sound driver itself. SoundDriverLoad is located at the end of the ROM in Sonic 2 instead of the beginning, but we have some artwork entangled with it as well. We will have to relocate it while we insert the new code.
Delete everything from SoundDriverLoad until the line "; end of 'ROM'"
In its place, paste the code inside this file:
![]() |
Download The Sonic 3 Sound Driver Load code
File: SoundDriverLoadCode.asm (54 kB) (info)
|
Note: Linux systems and some mac installs have a case sensitive filesystem and that can cause build errors,
to fix, simply rename all non-sound effects to lowercase names.
Upgrading the Playback Routines
Now we have the code to load the new driver, time to add the new playback routines.
Upgrade Music Routine
First the PlayMusic routine, locate:
; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; If Music_to_play is clear, move d0 into Music_to_play, ; else move d0 into Music_to_play_2. ; sub_135E: PlayMusic: tst.b (Music_to_play).w bne.s + move.b d0,(Music_to_play).w rts + move.b d0,(Music_to_play_2).w rts ; End of function PlayMusic
and we will replace it with this code:
; --------------------------------------------------------------------------- ; Subroutine to play a music track ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| PlayMusic: cmpi.w #$FB,d0 blt.s ++ bhi.s + move #8,d0 jmp SetTempo + cmpi.w #$FC,d0 bne.s + clr.w d0 jmp SetTempo + stopZ80 move.b d0,($A01C0A).l startZ80 rts ; End of function PlaySound
Upgrade Sound Routines
now find:
; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; play a sound if the source is onscreen ; sub_137C: PlaySoundLocal: tst.b render_flags(a0) bpl.s + ; rts move.b d0,(SFX_to_play).w + rts ; End of function PlaySoundLocal
and replace it with:
; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; play a sound if the source is onscreen ; sub_137C: PlaySoundLocal: tst.b render_flags(A0) bpl.s SkipPlaySound bra.s PlaySound ; End of function PlaySoundLocal
now we will find:
; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; sub_1370 PlaySound: move.b d0,(SFX_to_play).w rts ; End of function PlaySound ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; play a sound in alternating speakers (as in the ring collection sound) ; sub_1376: PlaySoundStereo: move.b d0,(SFX_to_play_2).w rts ; End of function PlaySoundStereo
and replace it with:
; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; sub_1370 PlaySound: stopZ80 cmp.b ($A01C0B).l,d0 beq.s ++ tst.b ($A01C0B).l bne.s + move.b d0,($A01C0B).l startZ80 rts + move.b d0,($A01C0C).l + move.w #0,($A11100).l SkipPlaySound: rts ; End of function PlaySound ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; play a sound in alternating speakers (as in the ring collection sound) ; sub_1376: PlaySoundStereo: bra.s PlaySound ; End of function PlaySoundStereo ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; change the music tempo SetTempo: stopZ80 move.b D0,($A01C08).l startZ80 rts
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:
; --------------------------------------------------------------------------- ; Subroutine to pause the game ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; sub_1388: PauseGame: nop tst.b (Life_count).w ; do you have any lives left? beq.w Unpause ; if not, branch tst.w (Game_paused).w ; is game already paused? bne.s + ; if yes, branch move.b (Ctrl_1_Press).w,d0 ; is Start button pressed? or.b (Ctrl_2_Press).w,d0 ; (either player) andi.b #button_start_mask,d0 beq.s Pause_DoNothing ; if not, branch + move.w #1,(Game_paused).w ; freeze time move.b #MusID_Pause,(Music_to_play).w ; pause music ; loc_13B2: Pause_Loop: move.b #$10,(Vint_routine).w bsr.w WaitForVint tst.b (Slow_motion_flag).w ; is slow-motion cheat on? beq.s Pause_ChkStart ; if not, branch btst #button_A,(Ctrl_1_Press).w ; is button A pressed? beq.s Pause_ChkBC ; if not, branch move.b #GameModeID_TitleScreen,(Game_Mode).w ; => TitleScreen nop bra.s Pause_Resume ; =========================================================================== ; loc_13D4: Pause_ChkBC: btst #button_B,(Ctrl_1_Held).w ; is button B pressed? bne.s Pause_SlowMo ; if yes, branch btst #button_C,(Ctrl_1_Press).w ; is button C pressed? bne.s Pause_SlowMo ; if yes, branch ; loc_13E4: Pause_ChkStart: move.b (Ctrl_1_Press).w,d0 ; is Start button pressed? or.b (Ctrl_2_Press).w,d0 ; (either player) andi.b #button_start_mask,d0 beq.s Pause_Loop ; if not, branch ; loc_13F2: Pause_Resume: move.b #MusID_Unpause,(Music_to_play).w ; loc_13F8: Unpause: move.w #0,(Game_paused).w ; return_13FE: Pause_DoNothing: rts ; =========================================================================== ; loc_1400: Pause_SlowMo: move.w #1,(Game_paused).w move.b #MusID_Unpause,(Music_to_play).w rts ; End of function PauseGame
and replace it with:
; --------------------------------------------------------------------------- ; Subroutine to pause the game ; --------------------------------------------------------------------------- ; ||||||||||||||| S U B R O U T I N E ||||||||||||||||||||||||||||||||||||||| ; sub_1388: PauseGame: nop tst.b (Life_count).w beq Unpause tst.w (Game_paused).w bne.s + move.b (Ctrl_1_Press).w,d0 or.b (Ctrl_2_Press).w,d0 andi.b #$80,d0 beq Pause_DoNothing + move.w #1,(Game_paused).w stopZ80 move.b #1,($A01C10).l startZ80 Pause_Loop: move.b #$10,(Vint_routine).w jsr WaitForVint tst.b (Slow_motion_flag).w beq.s Pause_ChkStart btst #button_A,(Ctrl_1_Press).w beq.s Pause_ChkBC move.b #GameModeID_TitleScreen,(Game_Mode).w ; Go To Title Screen nop bra.s Pause_Resume Pause_ChkBC: btst #button_B,(Ctrl_1_Held).w bne.s Pause_SlowMo btst #button_C,(Ctrl_1_Press).w bne.s Pause_SlowMo Pause_ChkStart: move.b (Ctrl_1_Press).w,d0 ; is Start button pressed? or.b (Ctrl_2_Press).w,d0 ; (either player) andi.b #button_start_mask,d0 beq.s Pause_Loop ; if not, branch Pause_Resume: stopZ80 move.b #$80,($A01C10).l startZ80 Unpause: move.w #0,(Game_paused).w Pause_DoNothing: rts Pause_SlowMo: move.w #1,(Game_paused).w stopZ80 move.b #$80,($A01C10).l startZ80 rts ; End of function PauseGame
now find:
; loc_541A: SpecialStage_Unpause: move.b #MusID_Unpause,(Music_to_play).w move.b #8,(Vint_routine).w bra.w WaitForVint
replace the line right after the label with:
stopZ80 move.b #$80,($A01C10).l startZ80
Driver Data Files
Unpack this into the 'sound' folder and, optionally, remove the original files since we no longer need them anymore:
![]() |
Download The Sonic 3 Driver data files
File: s3driverdata.7z (131 kB) (info)
|
Fixing the Music and Sound Effects
Now we will open s2.constants.asm.
Music
Go to MusID__First and you will notice this:
; Music IDs offset := zMasterPlaylist ptrsize := 1 idstart := $81 ; $80 is reserved for silence, so if you make idstart $80 or less, ; you may need to insert a dummy zMusIDPtr in the $80 slot MusID__First = idstart MusID_2PResult = id(zMusIDPtr_2PResult) ; 81 MusID_EHZ = id(zMusIDPtr_EHZ) ; 82 MusID_MCZ_2P = id(zMusIDPtr_MCZ_2P) ; 83 MusID_OOZ = id(zMusIDPtr_OOZ) ; 84 MusID_MTZ = id(zMusIDPtr_MTZ) ; 85 MusID_HTZ = id(zMusIDPtr_HTZ) ; 86 MusID_ARZ = id(zMusIDPtr_ARZ) ; 87 MusID_CNZ_2P = id(zMusIDPtr_CNZ_2P) ; 88 MusID_CNZ = id(zMusIDPtr_CNZ) ; 89 MusID_DEZ = id(zMusIDPtr_DEZ) ; 8A MusID_MCZ = id(zMusIDPtr_MCZ) ; 8B MusID_EHZ_2P = id(zMusIDPtr_EHZ_2P) ; 8C MusID_SCZ = id(zMusIDPtr_SCZ) ; 8D MusID_CPZ = id(zMusIDPtr_CPZ) ; 8E MusID_WFZ = id(zMusIDPtr_WFZ) ; 8F MusID_HPZ = id(zMusIDPtr_HPZ) ; 90 MusID_Options = id(zMusIDPtr_Options) ; 91 MusID_SpecStage = id(zMusIDPtr_SpecStage) ; 92 MusID_Boss = id(zMusIDPtr_Boss) ; 93 MusID_EndBoss = id(zMusIDPtr_EndBoss) ; 94 MusID_Ending = id(zMusIDPtr_Ending) ; 95 MusID_SuperSonic = id(zMusIDPtr_SuperSonic); 96 MusID_Invincible = id(zMusIDPtr_Invincible); 97 MusID_ExtraLife = id(zMusIDPtr_ExtraLife) ; 98 MusID_Title = id(zMusIDPtr_Title) ; 99 MusID_EndLevel = id(zMusIDPtr_EndLevel) ; 9A MusID_GameOver = id(zMusIDPtr_GameOver) ; 9B MusID_Continue = id(zMusIDPtr_Continue) ; 9C MusID_Emerald = id(zMusIDPtr_Emerald) ; 9D MusID_Credits = id(zMusIDPtr_Credits) ; 9E MusID_Countdown = id(zMusIDPtr_Countdown) ; 9F MusID__End = id(zMusIDPtr__End) ; A0
That may be good if you were using the Sonic 2 driver, but obviously you are not, so we need to change it.
MusID__First = 1 MusID_AIZ1 = 1 MusID_AIZ2 = 2 MusID_HCZ1 = 3 MusID_HCZ2 = 4 MusID_MGZ1 = 5 MusID_MGZ2 = 6 MusID_CNZ1 = 7 MusID_CNZ2 = 8 MusID_FBZ1 = 9 MusID_FBZ2 = $A MusID_ICZ1 = $B MusID_ICZ2 = $C MusID_LBZ1 = $D MusID_LBZ2 = $E MusID_MHZ1 = $F MusID_MHZ2 = $10 MusID_SZ1 = $11 MusID_SZ2 = $12 MusID_LRZ1 = $13 MusID_LRZ2 = $14 MusID_SSZ = $15 MusID_DEZ1 = $16 MusID_DEZ2 = $17 MusID_MBSK = $18 MusID_Boss = $19 MusID_TDZ = $1A MusID_GSBonus = $1B MusID_SpecStage = $1C MusID_SMBonus = $1D MusID_GBMBonus = $1E MusID_KTE = $1F MusID_ALZ = $20 MusID_BPZ = $21 MusID_DPZ = $22 MusID_CGZ = $23 MusID_EMZ = $24 MusID_Title = $25 MusID_Credits = $26 MusID_GameOver = $27 MusID_Continue = $28 MusID_EndLevel = $29 MusID_ExtraLife = $2A MusID_Emerald = $2B MusID_Invincible = $2C MusID_2PVS = $2D MusID_MB = $2E MusID_Options = $2F MusID_EndBoss = $30 MusID_Countdown = $31 MusID_Ending = $32 ; for compatibility with Sonic 2 MusID_2PResult = MusID_Continue MusID_EHZ = MusID_AIZ1 MusID_MCZ_2P = MusID_MHZ2 MusID_OOZ = MusID_DPZ MusID_MTZ = MusID_DEZ1 MusID_HTZ = MusID_LRZ1 MusID_ARZ = MusID_HCZ1 MusID_CNZ_2P = MusID_CNZ2 MusID_CNZ = MusID_CNZ1 MusID_DEZ = MusID_DEZ2 MusID_MCZ = MusID_EMZ MusID_EHZ_2P = MusID_AIZ2 MusID_SCZ = MusID_FBZ1 MusID_CPZ = MusID_HCZ2 MusID_WFZ = MusID_FBZ2 MusID_HPZ = MusID_LRZ1 MusID_SuperSonic = MusID_Invincible MusID__End = $33
The original music IDs in the compatibility section can be set to whichever ones you want in the above (I wouldn't recommend editing the 2PResult one though)
Sound Effects
Okay, now we actually have the music working (well, sorta working, fades and music stops aren't working yet though and we will fix that very soon), so it is time to get those sound effects working as they should. We want to start with SndID__First. you may notice that this area looks like this:
; Sound IDs offset := SoundIndex ptrsize := 2 idstart := $A0 ; $80 is reserved for silence, so if you make idstart $80 or less, ; you may need to insert a dummy SndPtr in the $80 slot SndID__First = idstart SndID_Jump = id(SndPtr_Jump) ; A0 SndID_Checkpoint = id(SndPtr_Checkpoint) ; A1 SndID_SpikeSwitch = id(SndPtr_SpikeSwitch) ; A2 SndID_Hurt = id(SndPtr_Hurt) ; A3 SndID_Skidding = id(SndPtr_Skidding) ; A4 SndID_BlockPush = id(SndPtr_BlockPush) ; A5 SndID_HurtBySpikes = id(SndPtr_HurtBySpikes) ; A6 SndID_Sparkle = id(SndPtr_Sparkle) ; A7 SndID_Beep = id(SndPtr_Beep) ; A8 SndID_Bwoop = id(SndPtr_Bwoop) ; A9 SndID_Splash = id(SndPtr_Splash) ; AA SndID_Swish = id(SndPtr_Swish) ; AB SndID_BossHit = id(SndPtr_BossHit) ; AC SndID_InhalingBubble = id(SndPtr_InhalingBubble) ; AD SndID_ArrowFiring = id(SndPtr_ArrowFiring) ; AE SndID_LavaBall = id(SndPtr_LavaBall) ; AE SndID_Shield = id(SndPtr_Shield) ; AF SndID_LaserBeam = id(SndPtr_LaserBeam) ; B0 SndID_Zap = id(SndPtr_Zap) ; B1 SndID_Drown = id(SndPtr_Drown) ; B2 SndID_FireBurn = id(SndPtr_FireBurn) ; B3 SndID_Bumper = id(SndPtr_Bumper) ; B4 SndID_Ring = id(SndPtr_Ring) ; B5 SndID_RingRight = id(SndPtr_RingRight) ; B5 SndID_SpikesMove = id(SndPtr_SpikesMove) ; B6 SndID_Rumbling = id(SndPtr_Rumbling) ; B7 SndID_Smash = id(SndPtr_Smash) ; B9 SndID_DoorSlam = id(SndPtr_DoorSlam) ; BB SndID_SpindashRelease = id(SndPtr_SpindashRelease) ; BC SndID_Hammer = id(SndPtr_Hammer) ; BD SndID_Roll = id(SndPtr_Roll) ; BE SndID_ContinueJingle = id(SndPtr_ContinueJingle) ; BF SndID_CasinoBonus = id(SndPtr_CasinoBonus) ; C0 SndID_Explosion = id(SndPtr_Explosion) ; C1 SndID_WaterWarning = id(SndPtr_WaterWarning) ; C2 SndID_EnterGiantRing = id(SndPtr_EnterGiantRing) ; C3 SndID_BossExplosion = id(SndPtr_BossExplosion) ; C4 SndID_TallyEnd = id(SndPtr_TallyEnd) ; C5 SndID_RingSpill = id(SndPtr_RingSpill) ; C6 SndID_Flamethrower = id(SndPtr_Flamethrower) ; C8 SndID_Bonus = id(SndPtr_Bonus) ; C9 SndID_SpecStageEntry = id(SndPtr_SpecStageEntry) ; CA SndID_SlowSmash = id(SndPtr_SlowSmash) ; CB SndID_Spring = id(SndPtr_Spring) ; CC SndID_Blip = id(SndPtr_Blip) ; CD SndID_RingLeft = id(SndPtr_RingLeft) ; CE SndID_Signpost = id(SndPtr_Signpost) ; CF SndID_CNZBossZap = id(SndPtr_CNZBossZap) ; D0 SndID_Signpost2P = id(SndPtr_Signpost2P) ; D3 SndID_OOZLidPop = id(SndPtr_OOZLidPop) ; D4 SndID_SlidingSpike = id(SndPtr_SlidingSpike) ; D5 SndID_CNZElevator = id(SndPtr_CNZElevator) ; D6 SndID_PlatformKnock = id(SndPtr_PlatformKnock) ; D7 SndID_BonusBumper = id(SndPtr_BonusBumper) ; D8 SndID_LargeBumper = id(SndPtr_LargeBumper) ; D9 SndID_Gloop = id(SndPtr_Gloop) ; DA SndID_PreArrowFiring = id(SndPtr_PreArrowFiring) ; DB SndID_Fire = id(SndPtr_Fire) ; DC SndID_ArrowStick = id(SndPtr_ArrowStick) ; DD SndID_Helicopter = id(SndPtr_Helicopter) ; DE SndID_SuperTransform = id(SndPtr_SuperTransform) ; DF SndID_SpindashRev = id(SndPtr_SpindashRev) ; E0 SndID_Rumbling2 = id(SndPtr_Rumbling2) ; E1 SndID_CNZLaunch = id(SndPtr_CNZLaunch) ; E2 SndID_Flipper = id(SndPtr_Flipper) ; E3 SndID_HTZLiftClick = id(SndPtr_HTZLiftClick) ; E4 SndID_Leaves = id(SndPtr_Leaves) ; E5 SndID_MegaMackDrop = id(SndPtr_MegaMackDrop) ; E6 SndID_DrawbridgeMove = id(SndPtr_DrawbridgeMove) ; E7 SndID_QuickDoorSlam = id(SndPtr_QuickDoorSlam) ; E8 SndID_DrawbridgeDown = id(SndPtr_DrawbridgeDown) ; E9 SndID_LaserBurst = id(SndPtr_LaserBurst) ; EA SndID_Scatter = id(SndPtr_Scatter) ; EB SndID_LaserFloor = id(SndPtr_LaserFloor) ; EB SndID_Teleport = id(SndPtr_Teleport) ; EC SndID_Error = id(SndPtr_Error) ; ED SndID_MechaSonicBuzz = id(SndPtr_MechaSonicBuzz) ; EE SndID_LargeLaser = id(SndPtr_LargeLaser) ; EF SndID_OilSlide = id(SndPtr_OilSlide) ; F0 SndID__End = id(SndPtr__End) ; F1 if MOMPASS == 2 if SndID__End > MusID_StopSFX fatal "You have too many SndPtrs. SndID__End ($\{SndID__End}) can't exceed MusID_StopSFX ($\{MusID_StopSFX})." endif endif
Yikes! that last section about IDs really has to go! But on another note, this code seems best for the Sonic 2 driver, it needs fixing. the fixed version is below:
SndID__First = $33 SndID_RingRight = $33 SndID_RingLeft = $34 SndID_Hurt = $35 SndID_Skidding = $36 SndID_HurtBySpikes = $37 SndID_InhalingBubble = $38 SndID_Splash = $39 SndID_Shield = $3A SndID_Drown = $3B SndID_Roll = $3C SndID_Explosion = $3D SndID_ShieldFire = $3E SndID_ShieldWater = $3F SndID_UnkSpark = $40 SndID_ShieldMagnet = $41 SndID_ShieldInstant = $42 SndID_ShieldFAction = $43 SndID_ShieldWAction = $44 SndID_ShieldMAction = $45 SndID_SSMonitor = $46 SndID_Unk1 = $47 SndID_RhinoCharge = $48 SndID_PunchVehic = $49 SndID_TailsCatch = $4A SndID_RockAppear = $4B SndID_KTEDrop = $4C SndID_BotShoot = $4D SndID_LargeLaser = $4E SndID_FireBurn = $4F SndID_MechLBZDoorSlam = $50 SndID_BotThrow = $51 SndID_SpikeSwitch = $52 SndID_TeleportStart = $53 SndID_LargeLaser2 = $54 SndID_HTZLiftClick = $55 SndID_UnkDrop = $56 SndID_BigSplash = $57 SndID_DoorSlam = $58 SndID_Smash = $59 SndID_S3DLauncher = $5A SndID_SwitchButton = $5B SndID_MetMalfunction = $5C SndID_KTEThud = $5D SndID_LaserBeam = $5E SndID_Hammer = $5F SndID_MGZBossWhirr = $60 SndID_MechCrash = $61 SndID_Jump = $62 SndID_Checkpoint = $63 SndID_SpikesMove = $64 SndID_SSSphere = $65 SndID_SSComplete = $66 SndID_PreArrowFiring = $67 SndID_Unlock = $68 SndID_BlockPush = $69 SndID_SpecStageExit = $6A SndID_S1SSSpecial = $6B SndID_Splash2 = $6C SndID_MObjMove = $6D SndID_BossHit = $6E SndID_Rumbling = $6F SndID_LavaBall = $70 SndID_ShieldAgain = $71 SndID_AntiGravTube = $72 SndID_TeleportEnd = $73 SndID_Repel = $74 SndID_PlatformRise = $75 SndID_Trapdoor = $76 SndID_BalloonPop = $77 SndID_S3DZapper = $78 SndID_Zap = $79 SndID_Unk2 = $7A SndID_Bounce = $7B SndID_ArrowFiring = $7C SndID_Unk3 = $7D SndID_Unk4 = $7E SndID_Flamethrower2 = $7E SndID_IZIceSpikeball = $80 SndID_LBZCannon = $81 SndID_Unk5 = $82 SndID_KTEBlowBridge = $83 SndID_Unk6 = $84 SndID_Unk7 = $85 SndID_LBZAlarm = $86 SndID_ShroomBounce = $87 SndID_MHZHandlePull = $88 SndID_Beep = $89 SndID_GhostFlee = $8A SndID_Chop = $8B SndID_BotDash = $8C SndID_Unk8 = $8D SndID_Unk9 = $8E SndID_SZStoneDoor = $8F SndID_SZDoorTimer = $90 SndID_SZDoorClose = $91 SndID_GhostCome = $92 SndID_SZBossIllusion = $93 SndID_LRZPlatformBelt = $94 SndID_LRZMBArm = $95 SndID_CrushingBlk = $96 SndID_Unk10 = $97 SndID_Unk20 = $98 SndID_Unk11 = $99 SndID_SpringPltfrm = $9A SndID_GolemBossWalk = $9B SndID_Sparkle = $9C SndID_SMLaser = $9D SndID_Unk12 = $9E SndID_Teleport2 = $9F SndID_SSZEggRoboFly = $A0 SndID_Unk13 = $A1 SndID_Unk14 = $A2 SndID_CNZElevator = $A3 SndID_Unk15 = $A4 SndID_Unk16 = $A5 SndID_Unk17 = $A6 SndID_Ready = $A7 SndID_MechaSonicBuzz = $A8 SndID_WaterWarning = $A9 SndID_Bumper = $AA SndID_SpindashRev = $AB SndID_ContinueJingle = $AC SndID_Go = $AD SndID_Catapult = $AE SndID_SpecStageEntry = $AF SndID_TallyEnd = $B0 SndID_Spring = $B1 SndID_Error = $B2 SndID_EnterGiantRing = $B3 SndID_BossExpOld = $B4 SndID_SpecStageGlass = $B5 SndID_SpindashRelease = $B6 SndID_CasinoBonus = $B7 SndID_Sparkle2 = $B8 SndID_RingSpill = $B9 SndID_TailsFly = $BA SndID_TailsExhaust = $BB SndID_Unk18 = $BC SndID_FBFlyBy = $BD SndID_EggmobileMHZ = $BE SndID_MBHCZSwirl = $BF SndID_Propeller2 = $C0 SndID_Propeller = $C1 SndID_Flamethrower = $C2 SndID_OrbitOrb = $C3 SndID_MBDEZAngry = $C4 SndID_Unk19 = $C5 SndID_Levitate = $C6 SndID_CNZCannon = $C7 SndID_OilSlide = $C8 SndID_MGZMace = $C9 SndID_RaceTrackDEZ = $CA SndID_Rumbling2 = $CB SndID_Crumbling = $CC SndID_DeathEggFly = $CD SndID_Unk21 = $CE SndID_Unk22 = $CF SndID_MPltfrmRise = $D0 SndID_Unk23 = $D1 SndID_Chain = $D2 SndID_Unk24 = $D3 SndID_KTEBlower = $D4 SndID_Lavafall = $D5 SndID_Unk25 = $D6 SndID_Chain2 = $D7 SndID_Unk26 = $D8 SndID_DEZTube = $D9 SndID_Unk27 = $DA SndID_Unk28 = $DB Snd_Open1 = $DC Snd_Open2 = $DD Snd_Open3 = $DE Snd_Open4 = $DF ;compatibility sfx SndID_Ring = SndID_RingRight SndID_Teleport = SndID_TeleportEnd SndID_BossExplosion = SndID_Explosion ;for original sound, use SndID_BossExpOld SndID_Bwoop = SndID_Lavafall SndID_Swish = SndID_Unk28 SndID_Blip = SndID_SwitchButton SndID_SlowSmash = SndID_Smash SndID_Signpost = SndID_Sparkle2 SndID_Bonus = SndID_BotDash SndID_CNZBossZap = SndID_S3DZapper SndID_Signpost2P = SndID_Sparkle2 SndID_OOZLidPop = SndID_ArrowFiring SndID_SlidingSpike = SndID_SSZEggRoboFly SndID_PlatformKnock = SndID_Chop SndID_BonusBumper = SndID_Bounce SndID_LargeBumper = SndID_Catapult SndID_Gloop = SndID_Lavafall SndID_Fire = SndID_FireBurn SndID_ArrowStick = SndID_KTEDrop SndID_Helicopter = SndID_TailsFly SndID_SuperTransform = SndID_Teleport2 SndID_CNZLaunch = SndID_KTEBlowBridge SndID_Flipper = SndID_Catapult SndID_Leaves = SndID_Unk28 SndID_MegaMackDrop = SndID_Unk28 SndID_DrawbridgeMove = SndID_Trapdoor SndID_QuickDoorSlam = SndID_DoorSlam SndID_DrawbridgeDown = SndID_Trapdoor SndID_LaserBurst = SndID_LargeLaser SndID_LaserFloor = SndID_LargeLaser2 SndID_Scatter = SndID_Unk14 SndID__End = $E0
Fixing the special sounds
Any time you update the sound driver, you need to change these sounds:
; Special sound IDs MusID_StopSFX = $78+$80 ; F8 MusID_FadeOut = $79+$80 ; F9 SndID_SegaSound = $7A+$80 ; FA MusID_SpeedUp = $7B+$80 ; FB MusID_SlowDown = $7C+$80 ; FC MusID_Stop = $7D+$80 ; FD MusID_Pause = $7E+$80 ; FE MusID_Unpause = $7F+$80 ; FF
First of all, speed up and slow down are no longer sounds, they are called by a completely different routine, but new equates for those are still useful. Second, pause and unpause are no longer sounds but are directly handled by the PauseGame routine that we upgraded above. No equates are necessary for those two. StopSFX is not used in the Sonic 3 driver as far as I know, so we will treat it as a compatibility ID. The resulting lists will look like THIS:
; Special sound IDs MusID_FadeOut = $E1 MusID_Stop = $E0 SndID_SegaSound = $FF ;these are here for compatibility MusID_StopSFX = MusID_Stop ;Tempo IDs Tempo_SpeedUp = 8 Tempo_SlowDown = 0
Fixing the Sneakers
I bet you expected that the sneakers would be fixed with the previous section, but no, Sonic 3 uses a different routine for the sneaker speed up / slow down. First, we will go to super_shoes_Tails and, beyond the +, we'll find this:
move.w #MusID_SpeedUp,d0 jmp (PlayMusic).l ; Speed up tempo
That will not work the way we want it to. It will actually cause an error during assembly of the ROM, so we need to fix it:
move.w #Tempo_SpeedUp,d0 jmp (SetTempo).l ; Speed up tempo
The music now speeds up, but we need it to slow down when the sneakers wear off, so find:
; loc_1A14A: Obj01_RmvSpeed: bclr #2,status_secondary(a0) move.w #MusID_SlowDown,d0 ; Slow down tempo jmp (PlayMusic).l
There are 2 lines that need editing here, just as before.
; loc_1A14A: Obj01_RmvSpeed: bclr #2,status_secondary(a0) move.w #Tempo_SlowDown,d0 ; Slow down tempo jmp (SetTempo).l
This will fix it for Sonic, but for Tails, do the exact same thing to Obj02_ChkShoes. Now the sneakers should be working as intended.
Optional: Apply New Sounds to Existing Objects
(coming soon)
Optional: Fix the 2P VS Screen
You will probably notice that the 2 player mode level select is still playing the same music as the options screen, but sonic 3 has its own 2 player mode music. Here we will fix that. locate this code:
move.b #MusID_Options,d0 bsr.w JmpTo_PlayMusic move.w #$707,(Demo_Time_left).w clr.w (Two_player_mode).w clr.l (Camera_X_pos).w clr.l (Camera_Y_pos).w move.b #$16,(Vint_routine).w bsr.w WaitForVint move.w (VDP_Reg1_val).w,d0 ori.b #$40,d0 move.w d0,(VDP_control_port).l bsr.w Pal_FadeFromBlack ;loc_8DA8: LevelSelect2P_Main:
and change the music id to MusID_2PVS.
the result should be:
move.b #MusID_2PVS,d0 bsr.w JmpTo_PlayMusic move.w #$707,(Demo_Time_left).w clr.w (Two_player_mode).w clr.l (Camera_X_pos).w clr.l (Camera_Y_pos).w move.b #$16,(Vint_routine).w bsr.w WaitForVint move.w (VDP_Reg1_val).w,d0 ori.b #$40,d0 move.w d0,(VDP_control_port).l bsr.w Pal_FadeFromBlack ;loc_8DA8: LevelSelect2P_Main:
Optional: Fix the Sound Test
Okay, I assume that you would want the sound test working too, so I added a section on that. First, we want to go to OptionScreen_Choices. Here is what we have:
OptionScreen_Choices: dc.l (3-1)<<24|(Player_option&$FFFFFF) dc.l (2-1)<<24|(Two_player_items&$FFFFFF) dc.l ($80-1)<<24|(Sound_test_sound&$FFFFFF)
change that last line to:
OptionScreen_Choices: dc.l ($FF)<<24|(Sound_test_sound&$FFFFFF)
now we have a full range, but it is not fixed yet, so we will locate:
move.w d2,(a1) cmpi.b #2,(Options_menu_box).w bne.s + ; rts andi.w #button_B_mask|button_C_mask,d0 beq.s + ; rts move.w (Sound_test_sound).w,d0 addi.w #$80,d0 bsr.w JmpTo_PlayMusic lea (level_select_cheat).l,a0 lea (continues_cheat).l,a2 lea (Level_select_flag).w,a1 moveq #0,d2 ; flag to tell the routine to enable the continues cheat bsr.w CheckCheats +
see that addi.w #$80,d0 line? It needs to go. the result will be:
move.w d2,(a1) cmpi.b #2,(Options_menu_box).w bne.s + ; rts andi.w #button_B_mask|button_C_mask,d0 beq.s + ; rts move.w (Sound_test_sound).w,d0 bsr.w JmpTo_PlayMusic lea (level_select_cheat).l,a0 lea (continues_cheat).l,a2 lea (Level_select_flag).w,a1 moveq #0,d2 ; flag to tell the routine to enable the continues cheat bsr.w CheckCheats +
okay, that fixes the options screen, but not the level select. We will do that next. go to LevSelControls_CheckLR. Below I have shown the code:
; loc_9522: LevSelControls_CheckLR: cmpi.w #$15,(Level_select_zone).w ; are we in the sound test? bne.s LevSelControls_SwitchSide ; no move.w (Sound_test_sound).w,d0 move.b (Ctrl_1_Press).w,d1 btst #button_left,d1 beq.s + subq.b #1,d0 bcc.s + moveq #$7F,d0 + btst #button_right,d1 beq.s + addq.b #1,d0 cmpi.w #$80,d0 blo.s + moveq #0,d0 + btst #button_A,d1 beq.s + addi.b #$10,d0 andi.b #$7F,d0 + move.w d0,(Sound_test_sound).w andi.w #button_B_mask|button_C_mask,d1 beq.s + ; rts move.w (Sound_test_sound).w,d0 addi.w #$80,d0 bsr.w JmpTo_PlayMusic lea (debug_cheat).l,a0 lea (super_sonic_cheat).l,a2 lea (Debug_options_flag).w,a1 moveq #1,d2 ; flag to tell the routine to enable the Super Sonic cheat bsr.w CheckCheats + rts
First of all, it checks when you press left to see if the sound test value is -1, since the sound driver uses the whole byte for the sound ID, we don't want it to rest to 127 like it usually does. It is very likely clear what you need to change there. Next it checks when you press right if the value is 128, at that point we do not want it resetting to 0 for the same reason above, then there is that andi.b on the sound test value of 127 that caps the value at $7F, that needs to go. Also just before playing the sound, 128 is added to the sound test value, which we don't want, just in case you haven't figured it out, I outlined the changes below:
; loc_9522: LevSelControls_CheckLR: cmpi.w #$15,(Level_select_zone).w ; are we in the sound test? bne.s LevSelControls_SwitchSide ; no move.w (Sound_test_sound).w,d0 move.b (Ctrl_1_Press).w,d1 btst #button_left,d1 beq.s + subq.b #1,d0 ;bcc.s + ;<-- remove this line ;moveq #$7F,d0 ;<-- remove this line + btst #button_right,d1 beq.s + addq.b #1,d0 ;cmpi.w #$80,d0 ;<-- remove this line ;blo.s + ;<-- remove this line ;moveq #0,d0 ;<-- remove this line + btst #button_A,d1 beq.s + addi.b #$10,d0 ;andi.b #$7F,d0 ;<-- remove this line + move.w d0,(Sound_test_sound).w andi.w #button_B_mask|button_C_mask,d1 beq.s + ; rts move.w (Sound_test_sound).w,d0 ;addi.w #$80,d0 ;<-- remove this line bsr.w JmpTo_PlayMusic lea (debug_cheat).l,a0 lea (super_sonic_cheat).l,a2 lea (Debug_options_flag).w,a1 ; Also S1_hidden_credits_flag moveq #1,d2 ; flag to tell the routine to enable the Super Sonic cheat bsr.w CheckCheats + rts
Note from WBilgini
Alright, there is a chance when you compile, you'll a error about this line:
; share these symbols externally (WARNING: don't rename, move or remove these labels!) shared word_728C_user,Obj5F_MapUnc_7240,off_3A294,MapRUnc_Sonic,movewZ80CompSize
Remove the movewZ80CompSize and you'll be good.
Also, I noticed a bug. In the title screen, after you choose a option, for a second the sound will go earrape. I don't know how to fix this though. If you do, please edit this page to fix it!
|Port Sonic 3's Sound Driver to Sonic 2]]