Some time after starting my Sonic CD decompilation, I read Clownacy's Everything That I Know About Sonic the Hedgehog's Source Code post. It mentioned the presence of a symbol list in Sonic & Knuckles Collection, but didn't delve further. Digging further, I found an old Sonic Retro thread by LOst (that for some reason I can't find again) that dumped the entire list, and then I found MainMemory's Sonic & Knuckles Collection labels, where she figured out part of the debug info format. Apparently she also has a GitHub repo for a disassembly, but aside from IDA files and some modding tools there doesn't seem to be anything there. In late august last year, I decided to take a look myself. Instead of ripping my CD, I downloaded a copy of the trial version. Yes, the trial version also contains the debug info. I'm not sure if it contains the same amount, but it was enough to get started. First is a long symbol list. Like MainMemory's post explains, these are Pascal style strings, where each string is prepended by a byte that mentions its length. After this list is some kind of index, because between the bytes you can find addresses that point to code in the executable. Each address is also followed by an number that increases with each entry. However, after 0x45, the format seems to change, and that seems to be where the excavation stopped. However, I've figured it out. You see, after entry 0x45 it starts documenting the local labels. Each entry first points to the location of the label, and then has sub-entries that are pairs of addresses and numbers. The address points to the location of the local labels, while the number points to the symbol list, with identifier 0x50 pointing to the 80th item, for example. This way, it can reuse labels, as some, like @@100 and @@ret, are used more than once. I decided to come back to this later and try my hand at a decompilation. That time was last weekend. At first I was intimidated by all those unknown memory locations in the code. Then I remembered that Sonic Jam, which was also the result of automatic 68000 assembly conversion, assigned specific parts of memory to the 68000's registers. I cross-referenced with the Sonic 3 & Knuckles disassembly, and sure enough, it's also the case here. Thanks to info on Sonic 3 Unlocked I was also able to find the actwk array that holds the 110 sprite status objects. All right, so what do I have so far? A disassembly of the code and data related to Sandopolis Zone's ghost enemy. I don't know if the ASM is valid, as it was copied from Ghidra and cleaned up. A C port of the same code. Incomplete until all of the called functions are decompiled so I can figure out the return values. Array indexing might also be off. A first version of the sprite status structure. The chosen names are obviously inspired by my work on the Sonic CD decompilation. I don't know how far I'll get with this, as there's only so much time I can devote to multiple projects, x86 assembly isn't as nice to read as MIPS assembly, and the amount of debug info is far from being the treasure trove that Sonic CD's PS2 port is, but I figured I could at least try. So far I already noticed one inconsistency with the Mega Drive game's disassembly when it comes to data. 29/02/2024 EDIT: I've extracted all 15213+ ASM labels with their memory address! Attached them as labels.txt. The file can be imported into Ghidra using the ImportSymbolsScript.py script. 12/03/2024 EDIT: There's a Git repository now. I've also added three labels to labels.txt that had gone missing for some reason.
I've been poking around at these kinds of things for a long while, but I always held off on fully committing to a decompilation because ultimately the game's code is 99% just the 68000 ASM fed into a machine that spit out equivalent x86 ASM with no thought to optimization, unlike Sonic CD or Sonic 3D Blast which were properly converted to C. The labels are cool, but without a ton of manual cleanup what you're getting out of this is probably going to look worse than if someone just decompiled the MD version directly. I do still hold a moderate interest in making mods for this version, as unoptimized code aside, it still is the original game running natively on PC, and there's a lot of good that could come from properly utilizing that. Also, I believe the trial version uses the same EXE as the full version, and can be changed into such with an INI flag. I can't promise that I'll be jumping in to decompile it, but if you have any questions about the game, feel free to ask me. I'm fairly certain there's an IDB importer for Ghidra that would let you use all the things I found previously.
The debug info covers the memory range 0x006ebcb8 to 0x00820771. But functions in the 0x40xxxxxx to 0x42xxxxxx range are often called. Do you have any idea what those are?
Here's everything I have from the region not covered by the debug info, using names mainly taken from the S&K disasm on GitHub. Note that the game's code is largely in the same order as the MD version with some PC-specific additions and some extra code from S3A where appropriate. Code (Text): GetDSoundInfo 00404390 AllocateRAMAndVRAM 004043B0 DrawSprites 00404564 ConvertSpriteTable 004049EB ConvertSpriteTable_P2 00404B1A ReadSaveFile 00404E0C WriteSaveFile 00404ECE VInt 004050C0 VInt_12_Main 00405201 VInt_4_Main 00405237 VInt_14_Main 00405284 VInt_2_Main 004052B0 VInt_16_Main 004052DC VInt_1A_Main 00405312 VInt_1C_Main 0040535F VInt_1E_Main 0040539F VInt_8_A_C_E_Main 004053E9 ConvertPalette 00405536 ConvertPalette2 004056F7 ConvertSpriteTable_Call 00405A23 Poll_Controllers 00405A45 SpecialVInt_Function 00405A91 DrawGraphics 00405B76 CopyPalette 0040699E CopyPalette2 004069D0 InitDirectSound 004097BC CreateSoundBuffer 00409A27 PlaySoundFlags 00409EBF StopSound 00409F33 PlaySound 00409F9F SetSoundPosition 0040A0EF LoadSounds 0040A25F unknown_libname_25 0040A4DD GetSoundPlaying 0040A4F8 StopAllSounds 0040A563 PlaySoundRegister 0040A98E LoadAndPlaySong 0040AEA5 ReloadAndPlaySong 0040AEE7 InitializeMidiInterface 0040B5BB LoadSong 0040B5EB PlaySong 0040B62E StopSong 0040B65B PauseMusic 0040B69E ResumeMusic 0040B6CB SetMusicSpeed 0040B6F8 SpeedUpMusic 0040B72B Change_Music_Tempo 0040B75A ReadFileInt 0040B800 GetSoundBuffer 0040B8B0 IsDSoundInit 0040B8E0 TitleSonic_LoadFrames 0040B900 Poll_JoysticksAndKeyboard 0040BCDB Poll_Controller1 0040C3D2 Poll_Controller2 0040C4D0 ReadFileWrapper 0040EAC8 PixelConvTable 0040F5AC DebugStartup 0040FF14 SKAloneStartup 0040FF54 S3orS3KStartup 0040FF63 BlueSpheresStartup 0040FF72 Test_Checksum 0040FFB6 Test_Checksum_removed 0040FFB7 SonicAndKnucklesStartup 00410095 GameLoop 004100CF GameModes 00410130 JumpToSegaScreen 00410184 DetectPAL 004101ED Kosinski_Decomp_2 0041028C Kosinski_Decomp 0041034B Queue_Kos 00410365 Process_Kos_Queue 0041037A Plane_Map_To_VRAM 00410384 Copy_Data_To_VRAM 00410426 Enigma_Decomp 00410456 Eni_Decomp_00 00410683 Eni_Decomp_01 004106C3 Eni_Decomp_100 004106F6 Eni_Decomp_101 0041072E Eni_Decomp_110 00410776 Eni_Decomp_111 004107BE Eni_Decomp_Index 00410805 Nemesis_Decomp 00410CE8 Nemesis_Decomp2 00410D43 Sega_Screen 00411443 Load_PLC 0041144B Load_PLC_Raw 0041149C Process_PLC1 004114A9 Queue_Kos_Module 004114AA Process_Kos_Module_Queue 00411510 DrawSprite_NoFlipNoPriority 00411698 DrawSprite_XFlipNoPriority 0041177C DrawSprite_YFlipNoPriority 0041186D DrawSprite_XYFlipNoPriority 00411957 CheckSpriteMask 00411A50 DrawSpriteMasked_NoFlip 00411ADD DrawSpriteMasked_XFlip 00411BD6 DrawSpriteMasked_YFlip 00411CE0 DrawSpriteMasked_XYFlip 00411DE3 DrawSprite_NoFlip 00411EF4 DrawSprite_XFlip 00411FD5 DrawSprite_YFlip 004120C3 DrawSprite_XYFlip 004121AA Render_HUD 004122AC UpdateHUD 004125BD HUD_LoadZero 00412944 DrawThreeDigitNumber 00412DA7 DrawSixDigitNumber 00412DBD DrawSingleDigitNumber 00413363 DrawTwoDigitNumber 00413379 HUD_Lives 004139B8 LevelSelect_S2Options 00415104 Obj_Titlecard 00416412 SpecialStage_Results 00417946 Init_Controllers 0041A6FC Init_VDP 0041A6FE Clear_DisplayData 0041A75C SndDrvInit 0041A81A Play_Sound 0041A81B Play_Sound_2 0041A82D Paused_Debug_Controls 0041A834 Level 0041A838 LevelLoop 0041B142 Level_NotLRZ 0041B1B2 Level_NotEnding 0041B1E3 DemoMode 0041B1FA SpawnLevelMainSprites 0041B557 SpawnLevelMainSprites_SpawnPlayers 0041BA7E Handle_Onscreen_Water_Height 0041BD97 HCZ_WaterTunnels 0041C40D HCZ1_WaterTunLocs 0041C6E0 HCZ2_WaterTunLocs 0041C7B4 GetDemoPtr 0041D422 Demo_PlayRecord 0041D4DA LoadSolids 0041D697 LoadLevelLoadBlock 0041DA83 CheckLevelForWater 0041DB98 LoadWaterPalette 0041DD39 LevelMusic_Playlist 0041DF65 Offs_PLC 0041DF98 PLC_00 0041E090 PLC_01 0041E0AA PLC_02 0041E0C4 PLC_05 0041E0E8 PLC_07 0041E116 Init_SpriteTable 0041E61C Process_Sprites 0041E760 MoveSprite 0041E85B MoveSprite2 0041E8F6 MoveSprite_TestGravity 0041E979 MoveSprite_TestGravity2 0041EA30 Delete_Current_Sprite 0041EACF Draw_Sprite 0041EB34 Animate_Sprite 0041EBC7 Render_Sprites 0041F0B6 Render_Sprites_CompetitionMode 00420A46 Sprite_OnScreen_Test 00421B44 TouchResponse 00421F59 HurtCharacter 00422B67 Kill_Character 00422D84 Add_Object_To_Collision_Response_List 00422F11 HyperAttackTouchResponse 004232EC nullsub_110 0042363D nullsub_109 00423AC9 AnPal_Pachinko 00424946 Pal_FadeFromBlack 00426210 Animate_Palette 004264F7 Pal_FadeToBlack 0042653F Pal_ToWhite 00426A53 LoadPalette 00426C05 LoadPalette_Immediate 00426CD1 GetSine 00426FDE Load_Sprites 0042A860 Create_New_Sprite 0042B8B8 Create_New_Sprite3 0042B8CF Create_New_Sprite_Int 0042B8FF Obj_Air_CountDown 0042BD04 Player_ResetAirTimer 0042C8F3 Obj_Invincibility 0042CB7F Obj_188E8 0042CF01 Map_Invincibility 0042D3E8 Obj_DashDust 0042D43C Obj_SuperSonicKnux_Stars 0042E1DA Obj_HyperSonic_Stars 0042E614 Obj_Insta_Shield 0042EA10 Obj_Fire_Shield 0042EC14 Obj_Lightning_Shield 0042EEBB Obj_Bubble_Shield 0042F3DA Obj_SuperTails_Birds 0042FF46 Obj_HyperSonicKnux_Trail 004308C8 Get_LevelSizeStart 00430A6C LevelSizes 00430B8A DeformBgLayer 004310A7 j_LevelSetup 00431B36 LoadLevelLoadBlock2 00431B3B Load_Level 00431D18 LevelLoadBlock 00431DC8 Obj_Sonic2P 00433450 Obj_Sonic 00434009 Sonic_Index 004340CA Sonic_Init 004340D8 Sonic_Control 0043428D Sonic_Modes 00434488 Sonic_Display 00434490 Sonic_RecordPos 00434703 Reset_Player_Position_Array 004348E9 Sonic_Water 00434A0A Sonic_MdNormal 00434CA3 Call_Player_AnglePos 00434D72 Sonic_MdAir 00434E05 Sonic_MdRoll 00434E50 Sonic_MdJump 00434F22 Sonic_Move 00434F6D Sonic_RollSpeed 00435D45 Sonic_ChgJumpDir 00436186 Player_LevelBound 00436400 SonicKnux_Roll 0043656C Sonic_Jump 004366C1 Sonic_JumpHeight 004369B6 Sonic_HyperDash_Velocities 00436EA7 Tails_Super 00436ECF SonicKnux_SuperHyper 00436EDE SonicKnux_Spindash 00437033 Player_SlopeResist 00437531 Player_RollRepel 00437639 Player_SlopeRepel 00437719 Player_JumpAngle 00437836 SonicKnux_DoLevelCollision 0043795B Player_TouchFloor_Check_Spindash 0043809E Player_TouchFloor 004380BA BubbleShield_Bounce 004382B7 Animate_Sonic 00438BAF AniSonic 00439B76 Sonic_Load_PLC 00439D88 Sonic_Load_PLC2 00439D9D Tails_Carry_LoadPLC 00439F69 SonicKnuckles2P_Load_PLC 0043A8B4 Obj_Tails2P 0043AA88 Obj_Tails 0043B7A6 Tails_Display 0043BD3E TailsCPU_Control 0043BFB1 Tails_Catch_Up_Flying 0043C203 Tails_FlySwim_Unknown 0043C47C Tails_Water 0043E0FB Animate_Tails 00441510 Tails_Tail_Load_PLC 00441E98 Tails_Load_PLC 00441F61 Tails2P_Tail_Load_PLC 00442B0C Tails2P_Load_PLC 00442BD4 Obj_Tails_Tail 00442D87 AniTails_Tail 00442F9C Obj_Tails2P_Tail 00443000 Ani_Tails2P_Tail 004431DC Obj_MGZ2_BossTransition 004431F4 Obj_Knuckles 004435CC Draw_LRZ_Special_Rock_Sprites 004485D0 Animate_Tiles 00448AD0 Animate_Init 0044CE15 Load_Rings 0044D020 Render_Rings 0044DA9C Sprite_Listing3 0044F0E8 Sprite_ListingK 0044F4E8 Do_ResizeEvents 0044F7CC LevelResizeArray 0044F94F AIZ1_Resize 0044F9AF AIZ2_Resize 0044FCE8 AIZ2_SonicResize1 0044FD60 AIZ2_SonicResize2 0044FDBD AIZ2_SonicResize3 0044FE53 AIZ2_SonicResize4 0044FE80 AIZ2_SonicResize5 0044FF03 AIZ2_SonicResize6 0044FF3E AIZ2_SonicResize7 0044FF79 AIZ2_SonicResizeEnd 0044FF98 AIZ2_KnuxResize1 0044FF99 AIZ2_KnuxResize2 0044FFF6 AIZ2_KnuxResize3 00450096 AIZ2_KnuxResize4 004500B9 AIZ2_KnuxResize5 00450137 AIZ2_KnuxResizeEnd 0045016B CalcRoomOverHead 00450A88 CheckRightWallDist 00451522 CheckLeftWallDist 00452006 DebugMode 00452264 DebugOffs 00452A6B Debug_AIZ1 00452ACB Debug_AIZ2 00452C29 Debug_HCZ 00452DE7 Debug_MGZ 00453071 Debug_CNZ 00453223 Debug_FBZ1 004533D5 Debug_FBZ2 00453623 Debug_ICZ1 00453865 Debug_ICZ2 004539FF Debug_LBZ1 00453BA5 Debug_LBZ2 00453D57 Debug_MHZ 00453F39 Debug_SOZ1 004540D3 Debug_SOZ2 00454279 Debug_LRZ1 00454437 Debug_LRZ2 00454601 Debug_SSZ 004547CB Debug_DEZ1 004548D5 Debug_DEZ2 00454A4B Debug_DDZ 00454B9D Debug_DEZBoss 00454C2F Debug_Ending 00454C91 Debug_ALZ 00454CF3 Debug_BPZ 00454DCD Debug_DPZ 00454ECB Debug_CGZ 00454FC9 Debug_EMZ 004550BB Debug_Pachinko_Special 004551C5 Debug_HPZ 00455233 Debug_Gumball_Special 004552AD Player_AnglePos 00455310 Obj_PathSwap 0045712C ArtUnc_Tails 00459FDC ArtUnc_Tails_Tail 0047051C ArtUnc_Sonic2P 0047167C ArtUnc_Tails2P 0047473C ArtUnc_Tails2P_Tail 004774DC ArtUnc_Knuckles2P 00477A5C ArtUnc_Invincibility 0047ED80 MapEni_S3TitleSonic1 00485700 MapEni_S3TitleSonic2 004857BC MapEni_S3TitleSonic3 00485880 MapEni_S3TitleSonic4 00485944 MapEni_S3TitleSonic5 00485A08 MapEni_S3TitleSonic6 00485ACC MapEni_S3TitleSonic7 00485B94 MapEni_S3TitleSonic8 00485C94 MapEni_S3TitleSonic9 00485D80 MapEni_S3TitleSonicA 00485E50 MapEni_S3TitleSonicB 00485F14 MapEni_S3TitleSonicC 00485FB4 MapEni_S3TitleSonicD 00486084 MapEni_S3TitleBg 00486180 ArtNem_Title_S3Banner 004862DC ArtKos_S3TitleSonic1 00486D98 ArtKos_S3TitleSonic8 00487CF8 ArtKos_S3TitleSonic9 0048A358 ArtKos_S3TitleSonicA 0048C658 ArtKos_S3TitleSonicB 0048DB38 ArtKos_S3TitleSonicC 0048EE58 ArtKos_S3TitleSonicD 00490038 ArtUnc_AirCountDown 004966D8 Map_Sonic2P 00498BCC Map_Tails2P 00498E78 Map_Tails2P_Tail 00499036 Map_Knuckles2P 0049923C ArtNem_Title_SonicSprites 00499554 PalKos_Pachinko 004DB190 ArtKos_SaveScreen 004DE00E ArtKos_SaveScreenSKZone 004E05FC ArtKos_SaveScreenPortrait 004E264C ArtKosM_MHZEndBoss 004ED9D8 ArtNem_SonicLifeIcon 0050E10C ArtNem_TailsLifeIcon 0050E224 ArtNem_KnucklesLifeIcon 0050E310 ArtNem_Monitors 0050E508 ArtNem_Explosion 0050F8F4 ArtNem_SpikesSprings 00510234 ArtNem_Ring 00510524 ArtNem_EnemyPts 00510760 ArtNem_BlueFlicky 00510C0C ArtNem_SignpostStub 00532CDC ArtUnc_HCZWaterSplash2 00542F24 ArtUnc_HCZWaterSplash 005436A4 Pal_SaveMenuBG 0054DE0E MapEni_S3MenuBG 0054DE4E ArtKos_S3MenuBG 0054E050 MapEni_SaveScreen_Layout 00552BCC ArtKos_SaveScreenMisc 00552F56 FBZ_16x16_Kos 005542F8 ArtKosM_FBZ 00554F38 FBZ_128x128_Kos 0055739A ArtUnc_Sonic 005A3AAC Knuckles_Art 005C3B8C ArtUnc_Sonic_Extra 005E3B0C ArtUnc_Tails_Extra 005E77AC Map_Sonic 005EA0CC Map_SuperSonic 005EA2C2 PLC_Sonic 005EBC2E PLC_SuperSonic 005EBE24 Map_Tails 005EC964 PLC_Tails 005EDB36 Map_Knuckles 005EE382 PLC_Knuckles 005EF7B6 AIZ1_16x16_Primary_Kos 005F06DC AIZ1_16x16_Secondary_Kos 005F08DC AIZ1_8x8_Primary_KosM 005F25FC AIZ1_8x8_Secondary_KosM 005F340E AIZ1_128x128_Kos 005F8BD2 AIZ2_16x16_Primary_Kos 005FCFE4 AIZ2_16x16_Secondary_Kos 005FD884 AIZ2_8x8_Primary_KosM 005FE564 AIZ2_8x8_Secondary_KosM 00600716 AIZ2_128x128_Kos 0060217A HCZ_16x16_Primary_Kos 0060618A HCZ_8x8_Primary_KosM 006065FA HCZ_128x128_Primary_Kos 0060723C HCZ1_16x16_Secondary_Kos 0060757C HCZ1_8x8_Secondary_KosM 00607E0C HCZ1_128x128_Secondary_Kos 0060A05E HCZ2_16x16_Secondary_Kos 0060C10E HCZ2_8x8_Secondary_KosM 0060C9FE HCZ2_128x128_Secondary_Kos 0060E880 MGZ_16x16_Primary_Kos 00610500 MGZ_8x8_Primary_KosM 00610E50 MGZ_128x128_Primary_Kos 006133F2 MGZ1_16x16_Secondary_Kos 006156F2 MGZ1_8x8_Secondary_KosM 00615A02 MGZ1_128x128_Secondary_Kos 006163E4 MGZ2_16x16_Secondary_Kos 00616C64 MGZ2_8x8_Secondary_KosM 006170C4 MGZ2_128x128_Secondary_Kos 00618156 CNZ_16x16_Kos 00619BC6 CNZ_8x8_KosM 0061AC06 CNZ_128x128_Kos 0061DE28 AngleArray 0064CEB4 HeightMaps 0064CFB4 HeightMapsRot 0064DFB4 SolidIndexes 0064EFB4 Solid_AIZ1 0064F074 Solid_AIZ2 0064F674 Solid_HCZ1 0064FC74 Solid_HCZ2 00650274 Solid_MGZ1 00650874 Solid_MGZ2 00650E74 Solid_CNZ 00651474 Solid_FBZ 00651A74 Solid_ICZ1 00652074 Solid_ICZ2 00652674 Solid_LBZ1 00652C74 Solid_LBZ2 00653274 Solid_MHZ 00653874 Solid_SOZ 00654474 Solid_LRZ1 00654A74 Solid_LRZ2 00655074 Solid_SSZ1 00655674 Solid_SSZ2 00655C74 Solid_DEZ 00656274 Solid_DDZ 00656874 Solid_ALZ 00656E74 Solid_BPZ 00657474 Solid_DPZ 00657A74 Solid_CGZ 00658074 Solid_EMZ 00658674 Solid_Gumball_Special 00658C74 Solid_Pachinko_Special 00659274 Solid_Slots_Special 00659874 Solid_LRZ3 00659E74 Solid_HPZ 0065A474 LevelPtrs 0065AA74 Layout_AIZ1 0065AB34 Layout_AIZ2 0065B380 Layout_HCZ1 0065C0E6 Layout_HCZ2 0065CC0E Layout_MGZ1 0065DAB6 Layout_MGZ2 0065E746 Layout_CNZ1 0065F4F6 Layout_CNZ2 006600C0 Layout_FBZ1 0066104E Layout_FBZ2 00661DF6 Layout_ICZ1 00662D84 Layout_ICZ2 00663D4C Layout_LBZ1 00664B9C Layout_LBZ2 00665B48 Layout_MHZ1 00666A70 Layout_MHZ2 006677A4 Layout_SOZ1 00668414 Layout_SOZ2 00669280 Layout_LRZ1 0066A098 Layout_LRZ2 0066AEC0 Layout_SSZ1 0066BC48 Layout_SSZ2 0066C8B8 Layout_DEZ1 0066CA34 Layout_DEZ2 0066DA02 Layout_DDZ 0066E890 Layout_ALZ 0066E99C Layout_BPZ 0066EB68 Layout_DPZ 0066ECBC Layout_CGZ 0066EDB4 Layout_EMZ 0066EEAC Layout_Gumball_Special 0066EFA4 Layout_Pachinko_Special 0066F05C Layout_Slots_Special 0066F244 Layout_LRZBoss 0066F3E6 Layout_HPZ 0066F69E Layout_DEZBoss 0066FCC6 PalPoint 006704A6 Pal_SonicTails 006707B6 Pal_Knuckles 00670876 Pal_AIZIntro 00670896 Pal_AIZ 006708F6 Pal_AIZFire 00670956 Pal_AIZBoss 006709B6 Pal_AIZ_Water 00670A16 Pal_AIZ2_Water 00670A96 Pal_HCZ1 00670B16 Pal_HCZ2 00670B76 Pal_HCZ1_Water 00670BD6 Pal_HCZ2_Water 00670C56 Pal_MGZ 00670CD6 Pal_CNZ 00670D36 Pal_CNZ_Dark 00670D96 Pal_FBZ1 00670E16 Pal_FBZ2 00670E76 Pal_ICZ1 00670ED6 Pal_ICZ2 00670F36 Pal_ICZ2_Water 00670F96 Pal_LBZ1 00671016 Pal_LBZ2 00671076 Pal_LBZ_Water 006710D6 Pal_LBZ_DeathEgg 00671156 Pal_MHZ1 006711B6 Pal_MHZ2 00671216 Pal_SOZ1 00671276 Pal_SOZ2 006712D6 Pal_SOZ_Dark1 00671336 Pal_SOZ_Dark2 00671396 Pal_LRZ1 006713F6 Pal_LRZ2 00671456 Pal_SSZ1 006714B6 Pal_SSZ2 00671516 Pal_DEZ1 00671636 Pal_DEZ2 00671696 Pal_DDZ 006716F6 Pal_ALZ 00671756 Pal_BPZ 006717B6 Pal_DPZ 00671816 Pal_CGZ 00671876 Pal_EMZ 006718D6 Pal_Gumball_Special 00671936 Pal_Pachinko_Special 00671996 Pal_Slot_Special 006719F6 Pal_LRZBoss 00671A56 Pal_HPZ 00671AB6 Pal_HPZ_Line3 00671AD6 Pal_DEZBoss 00671B16 ArtNem_S22POptions 006916DE ArtKos_BigSEGA 006937AC ArtKos_TitleScreenBG 00693B2C ArtKos_Title_699610 00699610 ArtKos_Title_69A4F0 0069A4F0 ArtKos_69AB70 0069AB70 ArtNem_Title_ANDKnuckles 0069D5A4 SpecialStage 0069DDD4 SSLayoutOffs_RAM 0069E3DF LoadSpecialStageMap 0069E3FF Rotate_SSPal 0069EC3F Update_SSMap 0069ED2E SS_Pal_Map_Ptrs 0069EF32 Draw_SSNum 0069F2DA Draw_SSSprites 006A1439 Draw_SSSprite_Normal 006A1E6F Draw_SSSprite_FlyAway 006A1FB8 Animate_SSRings 006A2819 BlueSpheresTitle 006A4398 aNoWayNoWayNoWa 006A4787 aGetBlueSpheres 006A47B3 aGm000010090 006A487B aGm000040490 006A4888 BlueSpheresResults 006A716E aCongratulation 006A77E6 aPerfect 006A77F8 Obj_Sonic_RotatingSlotBonus 006A9460 LevelSetup 006AC52C ScreenEvents 006AC6DD Offs_ScreenEvents 006AC7E0 VInt_DrawLevel 006ACAE0 VInt_DrawLevel_2 006ACB0B VInt_DrawLevel_Done 006ACC1F VInt_VRAMWrite 006ACC20 VInt_VRAMWrite_Col 006ACC53 SpecialVInt_VRAMWrite 006ACC9B SpecialVInt_VRAMRead 006ACCCE SpecialVInt_LBZ2WindowCopy 006ACD01 SpecialVInt_LBZ2ScrollAClear 006ACE0B SpecialVInt_LBZ2ScrollAClear2 006ACE85 SpecialVInt_LBZ2WindowClear 006ACEFD Setup_TileRowDraw 006AD96B Refresh_PlaneFull 006ADFAB DrawTilesAsYouMove 006AE347 DrawBGAsYouMove 006AE3A9 PlainDeformation 006AEF73 ApplyDeformation 006AF1EC Reset_TileOffsetPositionActual 006AFF3E Reset_TileOffsetPositionEff 006AFFA1 ShakeScreen_BG 006B0355 AIZ_TreeReveal 006B08D0 AIZ_TreeRevealArray 006B0952 AIZ1_IntroDeform 006B09C2 AIZ1_IntroDeformArray 006B12C4 AIZ1_ScreenInit 006B1BD4 AIZ1_ScreenEvent 006B1BE9 AIZ1SE_ChangeChunk2 006B1DF0 AIZ1SE_ChangeChunk3 006B1E1E AIZ1SE_ChangeChunk4 006B1E4C Adjust_AIZ1Chunks 006B1F29 AIZ1_BackgroundInit 006B215E AIZ1_BackgroundEvent 006B2221 AIZ2_ScreenInit 006B2BF2 AIZ2_ScreenEvent 006B2C07 AIZ2_BackgroundInit 006B2F42 AIZ2_BackgroundEvent 006B2FA3 FBZ1_ScreenInit 006BA9D4 FBZ1_ScreenEvent 006BAB4E FBZ1_BackgroundInit 006BC75A FBZ1_BackgroundEvent 006BC899 FBZ2_ScreenInit 006BD544 FBZ2_ScreenEvent 006BD609 FBZ2_BackgroundInit 006BDCED FBZ2_BackgroundEvent 006BDDF5 j_DrawTilesAsYouMove 006C2FF5 ICZ1_ScreenInit 006C4416 ICZ1_ScreenEvent 006C44C3 ICZ1_BackgroundInit 006C4567 ICZ1_BackgroundEvent 006C466F ICZ2_ScreenInit 006C53D5 ICZ2_ScreenEvent 006C53E3 ICZ2_BackgroundInit 006C53EA ICZ2_BackgroundEvent 006C54BA CNZ1_ScreenInit 006CBFD8 CNZ1_ScreenEvent 006CBFE6 CNZ1_BackgroundInit 006CC46C CNZ1_BackgroundEvent 006CC4A3 CNZ2_ScreenInit 006CD3C5 CNZ2_ScreenEvent 006CD3D3 CNZ2_BackgroundInit 006CD5DD CNZ2_BackgroundEvent 006CD61E LBZ1_ScreenInit 006CE60D LBZ1_ScreenEvent 006CE7E5 LBZ1_BackgroundInit 006CF1A7 LBZ1_BackgroundEvent 006CF2C1 LBZ2_ScreenInit 006CF5E2 LBZ2_ScreenEvent 006CF625 LBZ2_BackgroundInit 006CF96B LBZ2_BackgroundEvent 006CF9F5 MGZ1_ScreenInit 006D30E4 MGZ1_ScreenEvent 006D30FA MGZ1_BackgroundInit 006D312A MGZ1_BackgroundEvent 006D315F MGZ2_ScreenInit 006D34C7 MGZ2_ScreenEvent 006D34F3 MGZ2_BackgroundInit 006D4596 MGZ2_BackgroundEvent 006D46F3 HCZ1_ScreenInit 006D5010 HCZ1_ScreenEvent 006D5026 HCZ1_BackgroundInit 006D502D HCZ1_BackgroundEvent 006D50AD HCZ1BGE_Normal 006D50D1 HCZ1BGE_DoTransition 006D51E6 HCZ2_ScreenInit 006D5CD7 HCZ2_ScreenEvent 006D5CE5 HCZ2_BackgroundInit 006D5D0E HCZ2_BackgroundEvent 006D5DA1 Pachinko_ScreenInit 006D63A8 Pachinko_ScreenEvent 006D63B2 Pachinko_BackgroundInit 006D63B7 Pachinko_BackgroundEvent 006D63CD Pachinko_BGScroll 006D63DE Competition_Menu 006D6499 Competition_LevelSelect 006D6C13 Competition_PlayerSelect 006D7CB9 Competition_Results 006D889D TimeAttack_Records 006D94A2 KosArt_To_VDP 006D9DF4 Set_Lives_and_Continues 006DA1B4 Title_Screen 006DBFE2 S3K_Title_Screen 006DBFF0 Pal_Title 006DCE75 Pal_TitleSonic1 006DCEE5 Pal_TitleSonicD 006DD085 Obj_TitleBanner 006DD105 Obj_TitleANDKnuckles 006DD408 Obj_TitleCopyright 006DD578 Obj_TitleSelection 006DD5F1 Obj_TitleSonicFinger 006DD736 Obj_TitleSonicWink 006DD804 Obj_TitleTailsPlane 006DD8C0 S3_Level_Select_Code_Dummy 006DDA20 S3_Level_Select_Code 006DDA21 LSelect3CodeDat 006DDACB ArtNem_TitleScreenText 006DDB68 SK_Alone_Title_Screen 006DDE88 ArtNem_TitleScreen1997 006E0340 SRAM_Load 006E04C8 SaveGame 006E0980 SaveScreen 006E0D1D Pal_Save_Chars 006E1B43 Pal_Save_Emeralds 006E1B65 Map_SaveScreen 006E1ED9 ObjDat_SaveScreen 006E2209 ArtKos_SaveScreenS3Zone 006E3D00 S3_Alone_Title_Screen 006E62E0 Iterate_Sonic_TitleFrame 006E6C59 TitleSonic_LoadFrame 006E6DAD ObjDat_SaveScreen_S3 006EA783
Looking at sprite_status.h: Any reason why sproffset is called pattern and patbase is called mapping? Shouldn't mstno be a short/char union as in Sonic CD? Same goes for direc (which is split into direc1 and direc2) as well as xposi and yposi (which are words that should be treated as int/short/char unions). Isn't $2A cddat and $2C-$2D userflag? In addition, I'm pretty sure that several symbols missing from the EXE's debug info can be found in Gems Collection Sonic CD as well as Sonic 2 Nick Arcade. That said, I very much appreciate this. Thank you for doing this!
To name the sprite_status struct's members, I cross-referenced Sonic the Hedgehog's SST, your post on how Sonic CD's SST compares to it, and Sonic 3's SST. It's very likely I made some mistakes. I'm guessing I chose "pattern" because the description on the wiki lead me to think that was what it represented. But looking more closely, you're right; it's the same thing as "sproffset" in Sonic CD. It looks like for "mapping" I didn't cross-reference at all, because the position and description is the same in both the original game, Sonic CD, and Sonic 3. It should indeed be "patbase". I'm trying to avoid unions as it leads to code that is dependent on the CPU's endianness. That's why I split them up. If I find that solution isn't workable, I'll join them again, but unions are a last resort. The reason I made actfree start at 0x2A is because it seems to essentially be the start of data specific to the type of object. I guess it would be more accurate to assign cddat and userflag. But what about 0x2B? It seems to also be a status bitfield. Do I name that cddat2? Having decompiled a large chunk of Sonic CD definitely helps. Yesterday I happened to find our good friend random() and its associated global variable ranum, seemingly unchanged. But I want to be careful with this, as who knows what changed on the way to Sonic 3 while passing through Sonic 2. I haven't looked at the symbols found in Sonic 2 Nick Arcade yet, because they seemed to map to an older build. But I'm open to suggestions on how they could be of use. Thanks for your corrections!
First of all, you're welcome. As for cddat2, I guess with your avoidance of unions in mind, uh... sure, why not. For Sonic 2 Nick Arcade, I would imagine that there would at least be parallels for, going off of these lists: bgmset, soundset, dmactrset, mapdevr, random, sinset, atan, ringgetsub, speedset, speedset2, actionsub, frameout, patchg, spatsetsub, actwkchk, actwkchk2, colichg, efectwrt, efecttbl, score, edit, mapinittbl, divdevtbl, scddirtbl1, scddirtbl2, scdtbl, asettbl, and their associated labels (there's also frameoutchk and frameoutchkd, which aren't listed in that post). random was not changed between Sonic 2 and 3, though Sonic & Knuckles changed the default seed value for whatever reason.
Someone correct me if I'm wrong, but it looks like what the disassembly calls Create_New_Sprite3 has an off-by-one error. It takes the address of the first reserved SST (0xCAE2), then subtracts the address of the current SST, and divides it by 0x40. That in itself is already odd, because the size of an SST is 0x4A. It then uses a lookup table to get a loop counter. If the loop counter is negative, which it would only be if the current SST is the first reserved SST, it returns. If not, it loops until it either finds a free SST or if the loop counter becomes negative. The lookup table's first values are -1, 0, and 1. Let's suppose that the current SST is the last dynamic SST, right before the first reserved SST. The calculation would be: Code (Text): (0xCAE2 - 0xCA98) / 0x40 The result being 1. So it would retrieve 0 from the lookup table. It's not negative, so it enters the loop. The first thing that happens in the loop is to advance to the next SST. But this would be the first reserved SST! If it's free at any point, and the range of dynamic SSTs is full, it could be claimed by mistake! I've ported this to C as follows: Code (Text): void Create_New_Sprite3(sprite_status* pActwk, sprite_status** ppNewActwk) { *ppNewActwk = pActwk; // Off by one error. The reserved range's first slot might be overwritten. for (int slots = (&actwk[93] - pActwk) - 1; slots >= 0; --slots) { ++*ppNewActwk; if ((*ppNewActwk)->r_ptr == 0) return true; } return false; } There also seems to be a problem when the current SST is the first dynamic SST, based on a comment in the disassembly and the lookup table. Thoughts?
I need some help figuring out some mysterious stack use. In the function chk_framein, at one point it copies the current stack value into EAX and pops the stack. Then it assigns EAX to an int starting at byte 52 (0x34) of the sprite status object. Code (Text): POP EAX MOV EDI,dword ptr [68k_a0] MOV dword ptr [EDI + 0x34],EAX At the bottom of the function this value is retrieved again and assigned to the sprite status object's routine pointer. Code (Text): MOV EDI,dword ptr [68k_a0] MOV EAX,dword ptr [EDI + 0x34] EDI,dword ptr [68k_a0] MOV dword ptr [EDI],EAX After some searching I found the equivalent code in the Mega Drive version's disassembly, Obj_WaitOffscreen. This is the equivalent code: Code (Text): move.l (sp)+,$34(a0) The equivalent of the bottom part: Code (Text): move.l $34(a0),(a0) My question is: where is the value on the stack coming from? I've ran multiple searches. The Kosinski decompression manipulates the stack, but it doesn't seem to be related. All other stack manipulations are reverted before the end of the function in which they are found.
It'll be the return address of the caller. It's a way of setting the object's next routine to run, without using an instruction that needs that address as an immediate value. If you used move to set the address, you'd have to store a long-word address. But because the next routine is literally next to the jmp/jp instruction, they can swapped out a jmp/jp for a jsr/call, and abuse the fact that these instruction will push that same routine address onto the stack, and because the subroutine is reusable, you save 6 bytes for every call. There are other minor benefits such as program flow, but space is definitely a key factor, memory cost money.
It should be noted that the Buggernaut, Blaster, and Technosqueek badniks, as well as the ICZ path-following platforms, actually have code that refers to their pointers after they are on-screen.
While I did port some code to C, these days I'm mostly doing research before beginning the porting in earnest. So I figured I'd talk some more about the labels, which have been extracted before, but never talked about. But before that, I want to talk about the internal zone IDs. Outside of a section on Sonic 3 cheat codes and a TCRF page about Sonic & Knuckles content in Sonic 3, these don't seem to be documented anywhere. 00 - Angel Island Zone 01 - Hydro City Zone 02 - Marble Garden Zone 03 - Carnival Night Zone 04 - Flying Battery Zone 05 - Ice Cap Zone 06 - Launch Base Zone 07 - Mushroom Hill Zone 08 - Sandopolis Zone 09 - Lava Reef Zone 0A - Sky Sanctuary Zone 0B - Death Egg Zone 0C - Doomsday Zone Note that it starts at 00, not 01. I already knew that Flying Battery was supposed to be the fifth zone, but its ID is 04, not 05. With that out of the way, let's take a look at some of the labels. gost08: The ghost enemy from Sandopolis Zone, called Hyudoro. The same section also contains the code related to their capsule prison, labelled obox08. iwa09: Iwamodoki, a rock enemy from Lava Reef Zone. rolldai: I've often seen things called "dai" in Sonic source code, but I have no idea what it means. Is this a rolling spike, or something? caos09: This seems to refer to the Hidden Palace that you access through a big ring. I know this because the same section also refers to a light_piller (light pillar), which is how you make your entrance, and schaos, which refers to the Super Chaos Emeralds. shel03: A shell in Carnival Night Zone? That must be Shell Star, known in English as Clamer. bs00b0: Angel Island Zone's end boss, the Flame Mobile. nozu05: Something in Ice Cap Zone. A nozzle? Related labels are nozu05_igas, nozu05_ice, and ice_break. This seems to be what's termed as a "freeze gun", which spews gas that freezes Sonic if he touches it. fish01: A fish in Hydro City Zone. A related label is dec_ring, which means that it decreases rings. That must be Kapu-Kapu, known in English as Mega Chopper. nuckle: Knuckles, of course. The regular labels always misspell his name, while local labels spell it correctly. At least there's some consistency. That's all for now. Let me know if this interests you, and I'll write more soon.
Oh gosh, that's primitive stuff! If it's not documented anywhere, then I'm honestly surprised. Here it is documented going all the way back to the old Sonic 3K Hacking Guide from the early 2000s: Although, it looks like I messed up the IDs somewhere, because Doomsday isn't 0D. By the way, look forward to seeing more of your decompilation work.
I see that you listed Hidden Palace in between Lava Reef and Sky Sanctuary, rather than waaaaay at the end of the list. rolldai is the barrels in Carnival Night Zone, including the Barrel of Doom. rolldai2 is the Hydrocity version. I've actually decided to take a crack at lining up the labels myself, and reached something very interesting... MainMemory has said multiple times before, including in this very thread, that Sonic & Knuckles Collection was machine-translated from 68000 assembly to x86 assembly, without attempting to optimize the results. Something I noticed, though, is that I think there might be some portions of the code that were optimized to some extent. For example, let's look at two functions, which, in the current version of skdisasm, are called CreateChild1_Normal and CreateChild7_Normal2: Spoiler: CreateChild1_Normal (Mega Drive) Code (Text): CreateChild1_Normal: moveq #0,d2 ; Includes positional offset data move.w (a2)+,d6 .loop: jsr (AllocateObjectAfterCurrent).l bne.s .end move.w a0,parent3(a1) ; Parent RAM address into $46 move.l mappings(a0),mappings(a1) move.w art_tile(a0),art_tile(a1) ; Mappings and VRAM offset copied from parent object move.l (a2)+,(a1) ; Object address move.b d2,subtype(a1) ; Index of child object (done sequentially for each object) move.w x_pos(a0),d0 move.b (a2)+,d1 ; X Positional offset move.b d1,child_dx(a1) ; $42 has the X offset ext.w d1 add.w d1,d0 move.w d0,x_pos(a1) ; Apply offset to new position move.w y_pos(a0),d0 move.b (a2)+,d1 ; Same as above for Y move.b d1,child_dy(a1) ; $43 has the Y offset ext.w d1 add.w d1,d0 move.w d0,y_pos(a1) ; Apply offset addq.w #2,d2 ; Add 2 to index dbf d6,.loop ; Loop moveq #0,d0 .end: rts Spoiler: CreateChild7_Normal2 (Mega Drive) Code (Text): CreateChild7_Normal2: moveq #0,d2 ; Same as child routine 1, but does not limit children to object slots after the parent move.w (a2)+,d6 .loop: jsr (AllocateObject).l bne.s .end move.w a0,parent3(a1) move.l mappings(a0),mappings(a1) move.w art_tile(a0),art_tile(a1) move.l (a2)+,(a1) move.b d2,subtype(a1) move.w x_pos(a0),d0 move.b (a2)+,d1 move.b d1,child_dx(a1) ext.w d1 add.w d1,d0 move.w d0,x_pos(a1) move.w y_pos(a0),d0 move.b (a2)+,d1 move.b d1,child_dy(a1) ext.w d1 add.w d1,d0 move.w d0,y_pos(a1) addq.w #2,d2 dbf d6,.loop moveq #0,d0 .end: rts You can see that the two functions, in the Mega Drive version of Sonic & Knuckles, are identical save for which function is called to check where in RAM to spawn the child object. As such, any input data will be identical between the two. You'd think that these would be the same in PC version, right? Spoiler: CreateChild1_Normal (PC) Note that I haven't seen any direct attestation of this label, I'm just making an educated guess. Also, this was copied and cleaned up from Ghidra, leaving in the address and binary data so you can see that some of the instructions were assembled differently. Code (Text): set_act00 7584ac 55 PUSH EBP 7584ad 33 c9 XOR ECX,ECX 7584af 8b 2d cc 47 85 00 MOV EBP,dword ptr [68k_a2] 7584b5 66 8b 55 00 MOV DX,word ptr [EBP] 7584b9 83 c5 02 ADD EBP,0x2 7584bc 66 42 INC DX @@loop0 7584be e8 0c 34 cd ff CALL actwkchk2 7584c3 75 59 JNZ @@ret 7584c5 8b 35 c0 47 85 00 MOV ESI,dword ptr [68k_a0] 7584cb 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 7584d1 66 89 77 46 MOV word ptr [EDI + oya_adr],SI 7584d5 8b 46 0c MOV EAX,dword ptr [ESI + patbase] 7584d8 89 47 0c MOV dword ptr [EDI + patbase],EAX 7584db 66 8b 46 0a MOV AX,word ptr [ESI + sproffset] 7584df 66 89 47 0a MOV word ptr [EDI + sproffset],AX 7584e3 8b 45 00 MOV EAX,dword ptr [EBP] 7584e6 83 c5 04 ADD EBP,0x4 7584e9 89 07 MOV dword ptr [EDI],EAX 7584eb 88 4f 2c MOV byte ptr [EDI + userflag],CL 7584ee 66 8b 46 10 MOV AX,word ptr [ESI + xposi] 7584f2 66 0f be 5d 00 MOVSX BX,byte ptr [EBP] 7584f7 45 INC EBP 7584f8 88 5f 42 MOV byte ptr [EDI + 0x42],BL 7584fb 66 03 c3 ADD AX,BX 7584fe 66 89 47 10 MOV word ptr [EDI + xposi],AX 758502 66 8b 46 14 MOV AX,word ptr [ESI + yposi] 758506 66 0f be 5d 00 MOVSX BX,byte ptr [EBP] 75850b 45 INC EBP 75850c 88 5f 43 MOV byte ptr [EDI + 0x43],BL 75850f 66 03 c3 ADD AX,BX 758512 66 89 47 14 MOV word ptr [EDI + yposi],AX 758516 66 83 c1 02 ADD CX,0x2 75851a 66 4a DEC DX 75851c 75 a0 JNZ @@loop0 @@ret 75851e 5d POP EBP 75851f c3 RET Spoiler: CreateChild7_Normal2 (PC) This, too, was copied and cleaned up from Ghidra, leaving in the address and binary data so you can see that some of the instructions were assembled differently. Code (Text): set_act06 6ecd70 33 c0 XOR EAX,EAX 6ecd72 a3 ac 49 85 00 MOV [68k_d2],EAX 6ecd77 8b 3d cc 47 85 00 MOV EDI,dword ptr [68k_a2] 6ecd7d 66 8b 07 MOV AX,word ptr [EDI] 6ecd80 83 c7 02 ADD EDI,0x2 6ecd83 89 3d cc 47 85 00 MOV dword ptr [68k_a2],EDI 6ecd89 66 a3 bc 49 85 00 MOV [68k_d6],AX @@loop0 6ecd8f e8 24 eb d3 ff CALL actwkchk 6ecd94 0f 85 5a 01 00 00 JNZ @@ret 6ecd9a 66 a1 c0 47 85 00 MOV AX,[68k_a0] 6ecda0 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ecda6 66 89 47 46 MOV word ptr [EDI + oya_adr],AX 6ecdaa 8b 3d c0 47 85 00 MOV EDI,dword ptr [68k_a0] 6ecdb0 8b 47 0c MOV EAX,dword ptr [EDI + patbase] 6ecdb3 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ecdb9 89 47 0c MOV dword ptr [EDI + patbase],EAX 6ecdbc 8b 3d c0 47 85 00 MOV EDI,dword ptr [68k_a0] 6ecdc2 66 8b 47 0a MOV AX,word ptr [EDI + sproffset] 6ecdc6 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ecdcc 66 89 47 0a MOV word ptr [EDI + sproffset],AX 6ecdd0 8b 3d cc 47 85 00 MOV EDI,dword ptr [68k_a2] 6ecdd6 8b 07 MOV EAX,dword ptr [EDI] 6ecdd8 83 c7 04 ADD EDI,0x4 6ecddb 89 3d cc 47 85 00 MOV dword ptr [68k_a2],EDI 6ecde1 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ecde7 89 07 MOV dword ptr [EDI],EAX 6ecde9 a0 ac 49 85 00 MOV AL,[68k_d2] 6ecdee 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ecdf4 88 47 2c MOV byte ptr [EDI + userflag],AL 6ecdf7 8b 3d c0 47 85 00 MOV EDI,dword ptr [68k_a0] 6ecdfd 66 8b 47 10 MOV AX,word ptr [EDI + xposi] 6ece01 66 a3 a4 49 85 00 MOV [68k_d0],AX 6ece07 8b 3d cc 47 85 00 MOV EDI,dword ptr [68k_a2] 6ece0d 8a 07 MOV AL,byte ptr [EDI] 6ece0f 47 INC EDI 6ece10 89 3d cc 47 85 00 MOV dword ptr [68k_a2],EDI 6ece16 a2 a8 49 85 00 MOV [68k_d1],AL 6ece1b a0 a8 49 85 00 MOV AL,[68k_d1] 6ece20 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ece26 88 47 42 MOV byte ptr [EDI + 0x42],AL 6ece29 a0 a8 49 85 00 MOV AL,[68k_d1] 6ece2e 66 98 CBW 6ece30 66 a3 a8 49 85 00 MOV [68k_d1],AX 6ece36 66 8b 1d a8 49 85 00 MOV BX,word ptr [68k_d1] 6ece3d 66 a1 a4 49 85 00 MOV AX,[68k_d0] 6ece43 66 03 c3 ADD AX,BX 6ece46 66 a3 a4 49 85 00 MOV [68k_d0],AX 6ece4c 66 a1 a4 49 85 00 MOV AX,[68k_d0] 6ece52 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ece58 66 89 47 10 MOV word ptr [EDI + xposi],AX 6ece5c 8b 3d c0 47 85 00 MOV EDI,dword ptr [68k_a0] 6ece62 66 8b 47 14 MOV AX,word ptr [EDI + yposi] 6ece66 66 a3 a4 49 85 00 MOV [68k_d0],AX 6ece6c 8b 3d cc 47 85 00 MOV EDI,dword ptr [68k_a2] 6ece72 8a 07 MOV AL,byte ptr [EDI] 6ece74 47 INC EDI 6ece75 89 3d cc 47 85 00 MOV dword ptr [68k_a2],EDI 6ece7b a2 a8 49 85 00 MOV [68k_d1],AL 6ece80 a0 a8 49 85 00 MOV AL,[68k_d1] 6ece85 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ece8b 88 47 43 MOV byte ptr [EDI + 0x43],AL 6ece8e a0 a8 49 85 00 MOV AL,[68k_d1] 6ece93 66 98 CBW 6ece95 66 a3 a8 49 85 00 MOV [68k_d1],AX 6ece9b 66 8b 1d a8 49 85 00 MOV BX,word ptr [68k_d1] 6ecea2 66 a1 a4 49 85 00 MOV AX,[68k_d0] 6ecea8 66 03 c3 ADD AX,BX 6eceab 66 a3 a4 49 85 00 MOV [68k_d0],AX 6eceb1 66 a1 a4 49 85 00 MOV AX,[68k_d0] 6eceb7 8b 3d c8 47 85 00 MOV EDI,dword ptr [68k_a1] 6ecebd 66 89 47 14 MOV word ptr [EDI + yposi],AX 6ecec1 66 a1 ac 49 85 00 MOV AX,[68k_d2] 6ecec7 66 05 02 00 ADD AX,0x2 6ececb 66 a3 ac 49 85 00 MOV [68k_d2],AX 6eced1 9c PUSHFD 6eced2 66 a1 bc 49 85 00 MOV AX,[68k_d6] 6eced8 66 48 DEC AX 6eceda 66 a3 bc 49 85 00 MOV [68k_d6],AX 6ecee0 66 3d ff ff CMP AX,0xffff 6ecee4 74 06 JZ @@DBCA0059 6ecee6 9d POPFD 6ecee7 e9 a3 fe ff ff JMP @@loop0 @@DBCA0059 6eceec 9d POPFD @@DBCC0059 6eceed 33 c0 XOR EAX,EAX 6eceef a3 a4 49 85 00 MOV [68k_d0],EAX @@ret 6ecef4 c3 RET So while the first subroutine seems to have been cleaned up somewhat, the seventh (or second, in this post) was not, resulting in things like 68K register simulation rather than using the x86 registers, as well as writing from writing from memory to a register... and then immediately writing from the same memory address to the same register. The optimized code does not have any debug symbol data... I wonder if the debug data was meant to indicate what code had yet to be cleaned up.
Maybe I didn't use the correct search terms, but I only found a list on Sonic Retro where it was explained how to make an Action Replay code for Sonic 3. Hidden Palace seems to be part of Lava Reef Zone. Ah, I see! So a "dai" is a barrel, then? What exactly do you mean by debug symbol data? Is it something else than the labels? Because CreateChild7_Normal2 does fall within the range that there are labels for. But I can't tell you which it is yet, because I haven't gotten that far yet (I'm at the 74xxxx range).
Kind of. LRZ3 and HPZ are acts 1 and 2 of zone $16. "dai" seems to be a platform. Compare certain labels found in Sonic 2 Nick Arcade. I meant the labels, yes. CreateChild1_Normal is not within the range of the labels.
I got the function name wrong. I meant CreateChild1_Normal, at address 0x007584ac. The debug info covers the range 0x006ebcb8 to 0x00820771. I'm attaching an export of my labelling work so far so no one else has to do it. It can be imported using the ImportSymbolsScript.py script included with Ghidra. EDIT: Export is obsolete. Find the last version in the first post.
It's been a lifetime since I was studying computer science, but isn't this the norm? I remember one of my textbooks back in the day went so far as having the first chapter "Chapter 0" to really drive the point home.
These "cleaned up" functions were likely rewritten in C, with the debug data only covering the ASM parts, while the C portion was covered by a standard PDB file (which wasn't shipped with the game).