With my honours project complete, I decided to put my newfound free time into a project that I’ve been meaning to get around to for almost five years: disassembling Knuckles in Sonic 2.
In case you don’t know, Knuckles in Sonic 2 (which I’m just going to call ‘KiS2’ from now on) is a version of Sonic 2 that lets you play as Knuckles instead of Sonic and Tails. Sonic hackers like to port Knuckles from this version back into regular Sonic 2, but, in the process, they effectively undo the huge number of changes that KiS2 made to Sonic 2’s codebase. This ranges from simple alterations for Knuckles, to bugfixes that have gone undiscovered to this very day.
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:
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…
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.
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. You see, KiS2 isn’t a standalone game: it’s actually a bonus mode in Sonic & Knuckles. Sonic & Knuckles’ cartridge has a cartridge slot on top of it, allowing you to plug other cartridges into it, with KiS2 being the result of plugging in Sonic 2’s 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.
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 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?
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 256×256 format to Sonic 2’s 128×128 format, and after that is a function for eliminating duplicate 128×128 chunks. These were likely used to convert Green Hill Zone’s chunks to 128×128 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.
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.
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.
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.
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.