don't click here

Utility Knuckles in Sonic 2 Disassembled

Discussion in 'Engineering & Reverse Engineering' started by Clownacy, May 6, 2022.

  1. Clownacy

    Clownacy

    Tech Member
    1,061
    607
    93
    (Another cross-post from my blog).

    I had some free time lately, so I decided to start a project that I've been meaning to get around to for almost five years: disassembling Knuckles in Sonic 2.

    You might be asking yourself why I want to disassemble this game, since a disassembly for it already exists. Well, the reason is that the existing disassembly is completely separate from the Sonic 2 disassembly that also already exists. Not only does this mean that it is horrifically outdated in comparison to the Sonic 2 disassembly, but this also makes it extremely difficult to compare the two games and find differences between them.

    Rather than disassembling the game from scratch like the maker(s) of the other disassembly did, my approach is to take the Sonic 2 disassembly, and edit it to match KiS2. This is exactly what I did to create the disassemblies of Sonic 2's revisions (REV00 and REV02), the game's Mega Play arcade version, and the version of Sonic 2 found in Sonic Classics/Sonic Compilation.

    As of writing, this task is finally done, and I have a modified Sonic 2 disassembly that produces a perfect copy of KiS2. With this disassembly more or less complete, I figured I should explain everything I've learnt about KiS2 here:

    Changes
    Knuckles
    Obviously, Knuckles has replaced Sonic. This is actually surprisingly tacked-on: Knuckles is just a lightly-modified Sonic with all of the gliding and wall-climbing behaviour wrapped in a single function call. I suppose this isn't surprising, but I was under the impression that the whole Knuckles object was copied from an in-development version of Sonic & Knuckles. I think I got that idea from the Sonic 3 Unlocked blog, but I could just be misremembering.

    Notably, Knuckles' graphics are loaded from the Sonic & Knuckles cartridge: the tiles are recoloured at runtime to suit Sonic 2's palette. The sprite mappings and dynamic tile loading data are also loaded from the Sonic & Knuckles cartridge. Sonic hackers may find this surprising, since Sonic & Knuckles uses a different sprite mapping format to Sonic 2. This leads me into my next point...

    Mappings
    All of the game's mappings were converted to Sonic & Knuckles' format. This strikes me as very odd, as this means that the mappings now have to be included in the KiS2 ROM, instead of being loaded from the Sonic 2 cartridge, wasting space. Maybe it was considered too much effort to go through the whole game and split the mappings? This conversion was universal: even unused mappings were converted. Heck, even unreferenced parts of mappings were converted. This suggests that the mappings were created using assembly macros, and the macro itself was modified to convert the mappings to Sonic & Knuckles' format.

    The difference between Sonic 2's and Sonic & Knuckles' sprite mapping format is that Sonic 2's has extra data for the game's two player mode, which uses a fancy rendering mode of the Mega Drive's VDP. This leads me onto yet another point...

    Two Player Mode
    Two player mode was removed, but not entirely. It appears that the developer(s?) were struggling to fit the game to the size they wanted, so they began removing code related to two player mode, and once they reached their desired size, they stopped. In the end, they scraped by with only 680 bytes to spare.

    There are plenty of leftovers from two player mode in the game: the variable used to detect two player mode (dubbed 'Two_player_mode' in the disassembly) still exists, and is referenced frequently in the game's code. For example, the level title card object still makes heavy use of the flag.

    Being a Sonic hacker, I've removed two player mode from Sonic 2 before, and I've done it much more thoroughly than in KiS2. With that in mind, I know how complex removing two player mode is, so it doesn't surprise me that the developers didn't go all the way with it.

    Lock-On Technology
    This won't be a surprise to most people reading this, but KiS2, despite being a version of Sonic 2, doesn't have many of Sonic 2's assets in it. Instead, it copies them from the attached Sonic 2 cartridge.

    The way Sonic 2's assets were removed from KiS2 is pretty basic: at the end of Sonic 2 is a massive block of assets (including the game's music, sounds, drum samples, enemy graphics, player graphics, player sprite mappings, level graphics, level layout, level object placements, and more), and it is simply removed in KiS2. Notably, assets that aren't part of this giant block were not removed, such as the title screen's '1 PLAYER' and '2 PLAYER VS' text.

    As mentioned earlier, some assets are loaded from the Sonic & Knuckles cartridge, such as Knuckles' assets. However, those aren't the only things that are loaded from that cartridge: KiS2 features modified level object placements, which reward the player for exploring with Knuckles' wall-climbing. Strangely, the data for this is in the Sonic & Knuckles portion of the cartridge instead of KiS2. It's possible that this was done to free-up space in KiS2, with Sonic & Knuckles having room to spare.

    Bugfixes
    KiS2 contains a surprising number of bugfixes:

    Perhaps most notably, KiS2 removed the air speed cap, which appears to be a leftover from Sonic 1. This is significant because it has always been unclear whether the air speed cap was deliberately retained in Sonic 2 as a feature, or leftover as a bug. The air speed cap is responsible for at least two areas in Sonic 2 not working as intended: the red spring that leads to the 'monkey island' in Emerald Hill Zone Act 2, and the launcher that flings you over a large gap in the floor in Wing Fortress Zone. In both cases, the speed cap causes the player to undershoot their target if they press left or right on the D-pad while moving through the air. The removal of this speed cap in KiS2 suggests that it was indeed an unintentional leftover all along.

    One of the most well-known bugfixes in KiS2 is the correction of a bug that causes the bottom two lines of the screen to appear incorrectly in Emerald Hill Zone. I wonder how this bug was discovered, since televisions were especially prone to overscan hiding the edges of the screen back then.

    One type of bugfix that KiS2 contains is taking the player's character out of their 'roll-jumping' state, where their controls are basically locked. Being left in this state at a bad time can result in the game soft-locking, as the player is unable to move their character. Times when KiS2 makes the character exit their roll-jumping state is when they enter a wind-tunnel and when hovering over a propeller in Wing Fortress Zone.

    Sonic 2 suffers from a particularly glaring bug, where entering the cheat to gain 15 Continues causes the game to play Oil Ocean Zone's music forever. The cause is a nonsensical sound ID being submitted to the sound driver. This is corrected in KiS2. This bug was also fixed in the version of Sonic 2 included in Sonic Mega Collection.

    The title card appears to have had a bugfix applied to it which prevents odd behaviour if the graphic of the name of the zone goes too far to the left of the screen, causing its X coordinate to drop below 0. This bugfix works by replacing some unsigned conditional branches with signed conditional branches, and only drawing the sprite if it is within 48 pixels of the screen's left side.

    The bumpers in Casino Night Zone have their own layout data. This data needs to be terminated with special byte patterns that prevent the bumper manager from reading beyond them and parsing surrounding code as data. One of these termination patterns is missing from the very start of Act 1's layout data. In a stroke of good luck, the code before the data happens to resemble the terminating byte pattern, preventing the bumper manager from processing invalid data. In KiS2, however, this is no longer the case. A proper data terminator was added at the start of the data, fixing this problem. Fun fact: this bug appears to have not been fixed in the earliest prototype of KiS2, causing the game to crash if you go to the top left corner of the level.

    There are also some modifications to the game's collision code, which may be an attempt to fix bugs in it. Unfortunately, I haven't figured out the point of these modifications yet, so I can't say for sure what bugs, if any, they're trying to fix. One bug that it appears to be trying to fix is the bug in Sonic 2 where collision with an object from below doesn't properly push the player out, sometimes resulting in them phasing straight through the object. This fix does not work correctly, however, and cancels-out the player's inertia when it shouldn't. You can read more about it here.

    One rather funny bug is that if you're moving at a high speed towards a wall, and then start moving in the other direction at last second, Sonic will impact the wall and then start moving away from it while playing his pushing animation. KiS2 appears to fix this bug as well, preventing Knuckles from entering his pushing animation if he is not facing towards the object that he pushed against.

    In Sonic's movement code, a register that holds his speed is unintentionally partially overwritten before being used used later on to decide whether Sonic is moving fast enough to skid or not. This creates an asymmetry in what speed Sonic needs to be in order to skid when attempting to move in the opposite direction. This too is fixed in KiS2. You can read more about this bug here.

    Another bug fixed by KiS2 is that, when the player turns Super, a ring is instantly drained. This is due to a counter never being initialised. Now, the game waits a second before draining the first ring, which is consistent with how it drains every ring afterwards.

    In Mystic Cave Zone, it's possible for the player to become 'detached' from a hanging vine switch, appearing suspended in the air away from the vine itself. KiS2 addresses this by forcefully updating the player's coordinates to match the vine every frame.

    Speaking of Mystic Cave Zone, the boss of that zone has a nasty bug where, apparently due to a copy-paste error, the wrong address register is used at one point, causing a random byte of memory to be overwritten. Somehow, KiS2's developers noticed this and fixed it.

    And... that's it. That should be the last of the bugfixes that I've found in KiS2. So, what other changes were made in KiS2?

    JmpTos
    Yep, JmpTos again. They always find an excuse to crop up when I do this kind of thing. For those not in the loop, 'JmpTo' is the nickname given to branch extensions that are present through Sonic 2's codebase. If a branch is too short to reach its destination, it instead branches to a long-range jump instruction in order to reach it. In the first two revisions of Sonic 2 (REV00 and REV01), they appear to have been generated by the assembler. In the third revision - REV02 - they changed significantly, presumably because the developers switched to using a different assembler. They've once again changed quite a bit in KiS2.

    What's interesting about the JmpTos in KiS2 is that they appear to be hand-made, as opposed to the obviously-automated JmpTos in Sonic 2 REV00 and REV01. You see, it appears that the developers went through much of the game's code, 'tidying' the JmpTos: rather than being messily mixed into code, as they were in REV02, they were grouped and moved to the end of their respective blocks of code. Additionally, redundant branches to JmpTos were eliminated: in Sonic 2 REV02, it wasn't uncommon to see unconditional branches that branched to JmpTos, when they could have just been jump instructions that jumped straight to the intended destination - KiS2 removed many, if not all, of these.

    Further adding to the idea that REV02 and KiS2's JmpTos were hand-made is the fact that one of the JmpTos in REV02 ('JmpTo13_MarkObjGone') is completely unused. It was removed in KiS2.

    Restored Debug Features
    Invisible objects, such as plane-switchers and invisible walls, become visible in Debug Mode in KiS2. One object in particular is made visible with code that was previously only in REV00. This suggests that the code may have existed in REV01's and REV02's source code in a dummied-out form that was simply un-dummied-out in KiS2. Perhaps these debug features were hidden behind a build-time flag?

    Removed Development Code
    In Sonic 2, after the 'loadLevelLayout' function is some leftover code. The first chunk of code is the level layout loading function from Sonic 1, modified to repeat the background layout. This was used in some of Sonic 2's prototypes.

    After that is a function that converts a level's chunks from Sonic 1's 256x256 format to Sonic 2's 128x128 format, and after that is a function for eliminating duplicate 128x128 chunks. These were likely used to convert Green Hill Zone's chunks to 128x128 for Sonic 2's "Nick Arcade" prototype.

    After surviving through numerous prototypes, all three revisions of the final Sonic 2, Sonic Classics, and the Mega Play arcade version, this code was finally removed in KiS2. RIP.

    Demos
    Also known as 'attract mode', the game will play some demos if you leave it on the title screen. The developers of KiS2 attempted to preserve compatibility with Sonic 2's demos, reenabling things like the air speed cap and giving Knuckles Sonic's jump height when a demo is playing. Unfortunately, the result is not perfect, and the demos still manage to desynchronise at points. The developers went so far in their attempts to keep the demos working that they manually edited the inputs for the Emerald Hill Zone demo.

    Other
    I could talk about the modified title screen, Wing Fortress Zone cutscene, ending, and logo after the credits, but honestly I can't think of anything noteworthy about them. Maybe I'll go over them in a follow-up post, if I can think of anything interesting to say.

    Standalone
    As an experiment in what is possible with this disassembly, I've added an option to build a 'standalone' version of KiS2 that doesn't rely on Sonic 2 or Sonic & Knuckles in order to run. This is similar to the 'Sonic 3 Complete' mode of the Sonic & Knuckles disassembly, which produces a version of Sonic 3 & Knuckles that doesn't rely on Sonic 3. You can find a built ROM of this standalone KiS2 here. The intention of this, in addition to just being a tech demo, is also to make it feasible to produce ROM hacks of KiS2, which is practically impossible whilst it is dependant on two other ROMs.

    Conclusion
    Personally, I've learnt a lot about KiS2 from this disassembly, and I hope others will learn a lot from it too. KiS2 has always been a mysterious black box to me: its many changes and fixes always being out of reach and beyond our understanding, with no easy way to find the new in a sea of old. Every change and every fix was a needle in a haystack... but not anymore. Maybe now we can see a *complete* port of Knuckles to Sonic 2, title screen, ending, compatibility adjustments, and all!

    The disassembly can be found here.

    Fun fact: I started this disassembly on the 28th of April, and it was completed on the 5th of May. It took me almost five years to get around to doing something that only took a week. Geez.
     
    Last edited: May 22, 2022
    • Like Like x 12
    • Useful Useful x 3
    • Informative Informative x 2
    • List
  2. saxman

    saxman

    Oldbie Tech Member
    Interesting read. I ran across some of these details myself when I did the Sonic Boom Engine, which integrates the KiS2 portions into standard Sonic 2. Though you did find some things in here I did not. It's clear the development team put a little bit of TLC into the product.

    Good job on the build! I know this took a while to do.
     
    Last edited: May 6, 2022
  3. MainMemory

    MainMemory

    Kate the Wolf Tech Member
    4,742
    338
    63
    SonLVL
    Well I guess someone (probably me) needs to rewrite the Knuckles porting guide now...

    On the plus side, this should make that "Sonic 2 & Knuckles" hack I've been trying to make for the past couple years a lot easier.
     
    Last edited: May 6, 2022
    • Agree Agree x 2
    • Like Like x 1
    • List
  4. MarkeyJester

    MarkeyJester

    Original, No substitute Resident Jester
    2,201
    431
    63
    Japan
    This is freakin' amazing what you've done here!

    I don't intend to use this, but I certainly enjoyed the read. I am surprised about the JmpTos more than anything...
     
  5. Brainulator

    Brainulator

    Regular garden-variety member Member
    What's interesting is that in the Sonic 2 Nick Arcade prototype, the special stage mappings from Sonic 1 were not converted, but everything else was. At first, I took this to mean that macros weren't used, but now, I find it more likely that because those sprites were handled differently, different macros were used, if any.

    Fantastic work, by the way.
     
    Last edited: May 10, 2022
    • Informative Informative x 1
    • List
  6. Halved

    Halved

    What Member
    28
    3
    3
    Can I ask, how do you decompile a game? I've never done that and I wanted to know because it sounds fun i guess?
     
  7. Clownacy

    Clownacy

    Tech Member
    1,061
    607
    93
    IDA Pro was used to create many of the disassemblies that we have today. Though I think Ghidra can produce disassemblies as well. The process isn't automatic: you have to tell the disassembler what parts of the game are code and what parts are data. After that, you still have to write your own comments and give the labels proper names.
     
  8. Halved

    Halved

    What Member
    28
    3
    3
    Okay, thank you!
     
  9. The downside is that IDA Pro is ridiculously expensive.
     
  10. Halved

    Halved

    What Member
    28
    3
    3
    Yeah, plus the freeware version doesn't even support 68K...
     
  11. Brainulator

    Brainulator

    Regular garden-variety member Member
    You could also use Exodus's Active Disassembly option (described here) to generate something as you play the game.