Could you write about the rotating red sphere thing on AIZ1 (Sonic 3 alone debug)? Did that have any purpose other than destroy the game´s palette and framerates?
Directly after the surfboarding Sonic object, Angel Island Zone 1's debug reel once again treats us to an unusual sprite: a set of red spheres revolving around each other in three dimensions:
What on earth could be the purpose of this object? Here's what I conjectured when I was just a kid: it's only available in AIZ1's debug reel, it comes right after the surfboard, it's red and it messes with the HUD once it's placed. It must be the early equivalent of the cutscene Knuckles object! I was a dumb kid.
The truth makes considerably more sense: it is a test of the 3D sprite effect seen in the game's special stages. The fact that the object is part of AIZ1's debug reel seems to be entirely circumstantial; it was likely added to the first level of the game simply for ease of access. However, its origins in the special stage become evident when we look up the location of its graphics within the ROM:
Name
Address
ArtUnc_SStageTailstails
0909E8
Map_SStageTailstails
0910E8
PLC_SStageTailstails
091106
ArtNem_SphereTest
0911CE
ArtNem_SStageShadow
091B2E
ArtNem_GetBlueSpheres
091B70
ArtNem_GBSArrow
091CF2
The object's overall behavior can actually be controlled by player 2. The controls, although a tad obtuse, are as follows:
Down/Left: moves the object up and down
Up/Right/B: toggles the rotation of each axis
A/C: shrinks/enlarges the object
Start: toggles between automatic and manual rotation
No doubt owing to its roots, the sphere test object stores its movement and rotation state in a region of RAM starting at address $E400, which is used by the special stage for the same purpose. In the main game however, the same area is used to maintain a buffer of the player's last few positions. As a result, exiting debug mode and moving around will very quickly make the object become all kinds of messed up.
In S3A only, if we go through the debug reel for Angel Island Zone 1, we eventually run across an unusual sprite, which when placed spawns the object seen below: a surfboarding Sonic that hovers around the player's position.
One thing which is immediately apparent about this object, is that much like the Flying Battery Zone monkey bar sprites before them, the surfboarding sprites are an almost perfect match for the standing sprite from Sonic 2:
When this object is spawned through debug mode, its subtype is always set to a non-zero value. And since it shares its VRAM address with the Sonic object, it corrupts Sonic's appearance as soon as it spawns.
However, when a subtype of 0 is supplied, the object actually turns Sonic invisible by setting his mapping frame to zero and disables his movement and animation routines by setting bits 0 and 1 of his object_control attribute:
So what's going on? It turns out that underneath its surface, this object contains code for an alternate intro sequence to Angel Island Zone 1. The presence of Sonic 2 sprites suggests that this sequence was the stage's original intro, before the plane intro seen in the final game was implemented.
We can restore the surfboard intro by simply changing which intro object is spawned at the start of the level:
Since we're going to make a lot of modifications to the game this time around, I'll put any relevant PAR codes alongside their respective snippets. Here are the codes for the above change:
004DF0:0002
004DF2:0B0E
That alone restores most of the scrapped intro, but there are still a bunch of rough spots. For instance, although the big waves have somehow fixed themselves, Sonic still has a splash of garbage trailing behind him, and as he jumps off the surfboard, it too turns into a garbled mess. This is because the graphics for these objects have not been loaded.
As part of the setup for his intro sequence, when Sonic first starts Angel Island Zone 1, the game loads PLC $A instead of the regular AIZ1 PLC. Just by looking at it, though, we can easily spot the problem: when the intro was reworked, the graphics for the surfboard and its splash were removed from the PLC:
Unfortunately, there's no longer any room to add those graphics back to the PLC without stepping over the regular AIZ1 PLC. Fortunately however, PLC 0 is completely unused, so we can repurpose it for our needs:
Should look mighty slick now, but we still have an additional issue: since the title card never comes down, the level gets stuck in this weird state where there's no HUD, rings are invisible, and most other objects appear as a garbled mess:
To fix this, we can simply replace the surfboard intro object's final call to the Sprite_OnScreen_Test function with a call to the last routine of the cutscene Knuckles object, which fixes everything for free.
That should about cover it. Let's finally take a look at our fully-restored surfboard intro sequence:
I know, it's not perfect. Since the game doesn't wait for the Nemesis queue to be empty before starting the level, the big waves still appear garbled for a while. However, fixing this would require much deeper changes to the game's code, the likes of which are beyond the scope of simple PAR codes.
It's not just Flying Battery Zone 2 that has cool stuff lyingaround in its object layout. Act 1's layout has an object with ID $8F placed at coordinates $2EE0, $5E0:
Okay, that probably wasn't much of a surprise if you happened to watch the video I linked to in my previous post. That's right, it's the boss of Flying Battery Zone 1, Gapsule:
In my video, I mentioned how the only difference between the S3A version of Gapsule and the one in Sonic & Knuckles lies in its color: the S3A version uses a slightly different shade of black from every other capsule in the stage.
Unfortunately, I made a slight faux pas while talking about Gapsule's button: I mentioned how its colors are also slightly different, using a dark tan around its edges rather than the light tan that every other capsule in the game uses.
Check this out, though. Here is the palette used by every other capsule in the game: the player palette:
Now, here's the palette that Gapsule uses, which is loaded over the level's regular enemy palette:
Do you see it? If you force the button to use Gapsule's palette instead of the player palette, then its edges become dark tan instead of light tan. The button simply uses a different palette line between S3A and Sonic & Knuckles.
Also, why is there a copy of the FBZ Gapsule boss in the disassembly labelled as unused? Is there anything special about it?
Let me elaborate. Way down at the bottom the S&K disassembly, there's a duplicate of Gapsule's art directly before the remaining Flying Battery Zone bosses; all three of them Nemesis-compressed. As the label notes, this copy of the art is unused: there are no other references to it anywhere in the disassembly.
ArtNem_FBZMinibossUnused: binclude "Levels/FBZ/Nemesis Art/Miniboss Unused.bin"
even
ArtNem_FBZ2Subboss: binclude "Levels/FBZ/Nemesis Art/Act 2 Subboss.bin"
even
ArtNem_FBZEndBoss: binclude "Levels/FBZ/Nemesis Art/End Boss.bin"
even
So what's the deal? Let's look at an excerpt of Gapsule's init code from S3A:
In S3A, the Nemesis art is neither a duplicate nor is it unused. As part of its setup, the boss performs a PLC request for PLC $5E, which happens to fall right inbetween the PLCs for Carnival Night Zone and Icecap Zone's act 1 bosses, and contains a reference to the Nemesis-compressed Gapsule art.
In Sonic & Knuckles, this was changed so Gapsule's art is instead stored in KosM format, most likely because Nemesis decompression isn't fast enough to display the boss immediately as it scrolls into view.
...except apparently, the developers forgot to remove the art itself from the build. Whoops.
Bonus chatter: Here are some PAR codes that allow you to place the unused Flying Battery Zone objects using debug mode. First, these two force every level to use FBZ's debug list:
05B58C:7010
05B5C2:7010
Then, these replace some of the objects in FBZ's debug list with the three unused bosses:
In my previous post, I pointed out how S3A's object layout for Flying Battery Zone 2 has a second object placed slightly to the left of the end capsule, at coordinates $3130, $6D8:
There isn't really any easy way to make the game load the level properly, and even if it did, there aren't any level blocks to stand on. Instead, let's just look up object ID $B4, or 180 in the object list:
That's right! Like I documented in this video a couple of years back, Flying Battery Zone's bosses are fully implemented in Sonic 3, apart from an overall lack of sound effects, anyway. This includes act 2's laser miniboss even though it's not actually placed in the object layout.
Incidentally, I based that video's presentation on GoldS' legendary Sonic 3 and Knuckles Glitches and Oversights video series, which I sure hope all of you have already watched by now.
That's not all, though. If we go a bit further up the list, what do you suppose we'll find sandwiched between the enemies for Marble Garden Zone and Icecap Zone? (Carnival Night Zone's enemies are even higher up for some reason.)
Both of Flying Battery Zone's regular enemies are also implemented! However, since their graphics normally get loaded through a PLC at the start of the level, they went out the door with the rest of the level load block.
Much like the bosses, both enemies are fully functional, except that Blaster tends to get stuck in place after firing some of his bullets. Oh, and you can jump on his bullets and they blow up like enemies. Freaky.
One of S3A's little idiosyncrasies is that, unlike the ones in which it descends from the top of the screen, any levels that feature stationary act 2 end capsules have them placed directly into the object layout, instead of having the boss object spawn them at the end of the fight.
In S&K, this was changed so that stationary capsules are also spawned by the boss, which unfortunately led to this bug where the capsule in Hydrocity Zone 2 is not properly aligned with the screen lock:
Why the developers changed this is a mystery; perhaps they were trying to remove any possibility that the player might somehow miss the screen lock and proceed to the next stage without fighting the boss, as can be seen here.
Surprisingly, such a minor detail can actually give us insight on an unrelated aspect of the game's development. It turns out, the object layout for Flying Battery Zone 2 contains one such stationary capsule, at coordinates $3210, $660.
The reason this is interesting is because the S3A placement of the capsule is a stone's throw away from its final resting place in Sonic & Knuckles. This suggests that by the time it was removed from S3A, the level layout for Flying Battery 2 was already quite similar to its final layout in Sonic & Knuckles.
Astute readers will notice the second object present in the layout placed slightly to the left of the capsule at coordinates $3130, $6D8. Foreshadowing: the sign of a quality blog.
is the emerald shrine that you're warped to for the super emerald special stages the same stage as hidden palace? how's that work?
Well, as I previously mentioned, they're completely separate levels. They just happen to have nearly identical level load blocks and level layouts, only really differing in their object layout and level size, as well as more esoteric aspects such as their background event routines.
Surprisingly enough, entries for these stages exist within S3A's level load block, hinting that their addition wasn't as late as one might think. Which brings me to this question by an anonymous commenter:
I'd be more interested in knowing what happens when a Sonic & Knuckles stage gets loaded in Sonic 3. For example, what tells the game to use Azure Lake graphics in Mushroom Valley Zone?
Well, since the data for the Sonic & Knuckles stages isn't included in the Sonic 3 cartridge, their entries in the level load block had to be replaced with placeholder values. For example, Flying Battery Zone, which occupies the slots between Carnival Night Zone and Icecap Zone, has all of its data pointers replaced with pointers to Icecap Zone's primary 16x16 block definitions:
Mushroom Valley Zone and Lava Reef Zone are both pointing at Azure Lake's primary 16x16 block definitions, which is what tells the game to use Azure Lake graphics in Mushroom Valley Zone. Meanwhile, Sandopolis Zone and the rest of the Sonic & Knuckles levels instead contain an unaltered copy of Angel Island Zone's level load block:
So what exactly is going on? Let's take Flying Battery Zone as an example again. What would happen if the developers simply removed all the FBZ level data from the build, without actually touching the level load block? Since the FBZ data now takes up zero bytes, all labels to it should now point at whatever followed FBZ's data when it was still in the ROM.
Assuming that FBZ's data originally came between the data for CNZ and ICZ, what do you suppose follows CNZ's data in the final ROM layout?
Name
Address
CNZ_16x16_Kos
1CCC34
CNZ_8x8_KosM
1CDC74
CNZ_128x128_Kos
1D0E96
ICZ_16x16_Primary_Kos
1D3FB6
ICZ_8x8_Primary_KosM
1D4296
ICZ_128x128_Primary_Kos
1D56A8
Mushroom Valley Zone, Lava Reef Zone, and the Sonic & Knuckles bonus stages follow the same pattern:
Name
Address
LBZ2_16x16_Secondary_Kos
1E5F70
LBZ2_8x8_Secondary_KosM
1E77D0
LBZ2_128x128_Kos
1EAF04
ALZ_16x16_Kos
1EE0C4
ALZ_8x8_KosM
1EEB84
ALZ_128x128_Kos
1F1936
...
...
Gumball_16x16_Kos
1FEA0E
Gumball_8x8_KosM
1FEE2E
Gumball_128x128_Kos
1FFB80
EndOfROM
1FFF20
Okay, but what about Sandopolis Zone and the remaining Sonic & Knuckles level slots? Why are they filled with copies of Angel Island Zone's level load block?
Here's my guess: the developers originally padded the entire table with copies of the AIZ data, before filling in each slot with the actual data as the respective level entered production. The presence of AIZ data in the slots for Sandopolis Zone and the remaining S&K stages is indication that those stages hadn't yet entered production at the time of Sonic 3's release.
Indeed, by digging around in the ROM, we can find unused object layouts for Mushroom Valley Zone as well as unused palette data for Lava Reef Zone and the S&K bonus stages, but seemingly nothing for the remaining levels.
Happy new year! I have survived the holiday season, and if you are reading this, then so have you. Congratulations.
In anticipation of Sonic 3's upcoming anniversary, I've decided to once again spend the days leading up to it discussing stuff that can only be encountered while playing standalone Sonic 3, henceforth referred to as Sonic 3 alone or S3A for brevity. I'll start with a comment by reader Josh Wallen, who asks:
I'd love to learn the details of why entering Sonic 2's level select code and hotswapping the cartridge works, AND why it's so much easier than entering Sonic 3's _actual_ cheat. I seem to recall it having something to do with the FMV meaning the Genesis doesn't check the input on every frame, so maybe you have covered that one...
I haven't, but other people have done so in the past, which is probably what you're remembering. The intended window for entering the S3A level select code is during the short animation that occurs between the SEGA logo and the game's title screen, but as you note, the controllers aren't checked every frame, which causes some inputs to be dropped.
So what's going on? First off, in order to conserve ROM space, each frame in the animation is compressed with Kosinki compression, and must be sequentially decompressed to RAM before being displayed on screen.
The game's developers actually optimized this procedure by spliting a region of RAM into two buffers, so that while one frame is being decompressed into the first buffer, a completed frame in the second buffer can be sent to VRAM through DMA. When the first frame finishes decompressing, the two buffers are swapped and the contents of the first buffer are sent to VRAM, and at the same time a new frame starts decompressing into the second buffer.
Now, the game usually polls the controllers for player inputs during the vertical interrupt period between outputting each frame to the TV screen, thereby ensuring that the inputs are checked 60 times a second together with the game's video output. And therein lies the rub: some of the animation frames are so large, take more than a frame to decompress.
As a result, the game misses the vertical interrupt window and doesn't output a new frame to the TV screen, creating a "lag frame". Which isn't that bad, since we're just showing an already choppy animation at this point. What sucks is that the controllers aren't polled for input either, so any new button presses are silently dropped.
It just so happens that after decompressing each animation frame, the game introduces an artificial 4 frame pause, just to pace the animation. During this pause, the interrupt handler executes normally, so the controllers get polled properly.
As for the cartridge swap trick, that works because Sonic 2 and Sonic 3 use the same RAM address to store their "level select unlocked" flag, and RAM isn't cleared when the console is reset. I would advise against this method, though: you might short-circuit two pins before the ground connects, which could damage your stuff. I recommend just punching the S3A code in as quickly as you can, before the animation advances to the more problematic, larger frames.
Modifying the player's hitbox when they curl into a ball can sometimes lead to unfortunate consequences. For example, they might be able to roll underneath badly-placed collision change objects and break the level collision. Also, because jumping makes the hitbox return to its regular size, players can get crushed in situations in which they would usually be fine, such as while breaking through Icecap Zone's stacked ice blocks.
However, there are times in which the game shines through. If Sonic breaks into a roll while upside down, the game will actually shift him up by five pixels rather than down, in order to keep him attached to the ceiling.
As we saw before though, when Sonic is rolling, the camera is programmed to aim five pixels above his actual position. Together with the change to his Y coordinate, this causes the camera to awkwardly jerk up a total of 10 pixels.
Objects that take control away from the player generally fail to handle the case in which Sonic is spinning. For instance, with the tube elevators I talked about last time, when you spindash directly into one, Sonic's feet will dig into the bottom of the capsule because his hitbox size and Y position aren't reset to their usual values.
This sort of oversight is normally harmless because Sonic only sinks into the object by about five pixels, a short enough distance that he ends up getting pushed out in the same direction he came from.
Play around with fire however, and eventually you'll get burned. When the player rides on one of Marble Garden Zone's spinning tops, it executes this interesting snippet of code:
Offsets $44 and $45 of the player's SST hold the default values of their y_radius and x_radius attributes. They are set once when the player object is first initialized and henceforth only read from, to restore the player's hitbox after rolling.
The spinning top object isn't messing with these values, which is good. But it is taking the default Y radius, adding 24 to it, and writing the new value to the player's y_radius attribute, which effectively changes the size of Sonic's hitbox from the usual 20×40 pixels to 20×88 pixels. This way lies madness.
Why does the object do this? To trick the solid object code into thinking the bottom of Sonic's hitbox is all the way at the bottom of the spinning top. You see, the game is written to process two kinds of solid collisions: object-to-level collision, and player-to-object collision. It is not equipped to process solidity between any two arbitrary objects.
However, by hijacking the player's own hitbox, the spinning top object can simulate floor collision between itself and the tops of other objects, which is all it really needs, without having to mess around with any of the underlying code.
Except the underlying code just so happens to contain this fragment (relevant bits in bold):
When bit 2 of the player's status bitfield (aka their rolling flag) is set, the above code must shift their Y position up a few pixels in order to account for the change in their hitbox size. It calculates the number of pixels by subtracting the default Y radius from the current collision height, which is usually 30 pixels while rolling.
The current collision height is 88 pixels.
Here's the rundown: if Sonic starts riding the spinning top while rolling, then landing on a solid object will make the floor collision code attempt to shift him up by five pixels. However, since his current Y radius is an insane value, this ends up shifting him down by 24 pixels, which is enough to clear the sinking mud object and send him right through the floor.
It doesn't stop there. Because Sonic never dismounted the spinning top properly, his hitbox is still abnormally tall, which causes him to touch the floor much sooner than he normally would. Fortunately, the floor collision code restores Sonic's hitbox size whenever he lands, so there's no long-term damage.
The size of a player's hitbox is determined by the values at offsets $1E and $1F of their SST, respectively known as the y_radius and x_radius attributes in the current disassembly:
As their labels imply, these attributes denote the player's radius in pixels across the vertical and horizontal axes, and as such, their values must first be doubled in order to obtain the actual collision size. Furthermore, since a radius of 0 isn't particularly helpful, the values stored in these attributes are actually off by a single pixel, so therefore we must also add 1 to each value before doubling it.
For instance, the Sonic object is initialized with a y_radius of $13 and an x_radius of 9. These values are off by one, so the true radii are 10 for the horizontal radius and $14, or 20 for the vertical one. Doubling these values gives us Sonic's final collision size of 20×40 pixels.
When Sonic curls into a ball, his hitbox gets reduced to the adequately smaller size of 16×30 pixels. However, since the precise location of the hitbox's edges is determined from Sonic's X and Y coordinates, which in turn define the center of the object, the game must also make sure to move Sonic down by five pixels in order to keep him grounded:
As one might expect, this easily lends itself to lots of tiny oversights. For example, when a player jumps directly out of a roll, the larger hitbox is incorrectly applied, causing them to hit the ceiling sooner than intended.
In Sonic 2, this also caused the player to suddenly jolt up after landing, thanks to the game subtracting five pixels from their Y position in order to prevent their hitbox from digging into the floor when it expands back to its normal size (which has no effect due to the previous bug).
These changes to the player's position also take their toll on the camera. Because Sonic suddenly moves down by five pixels when he curls into a ball, the camera counteracts this by aiming itself five pixels above his actual position so as to prevent the entire screen from jerking down awkwardly. However, this code does not account for Tails' shorter hitbox, and so whenever Tails curls into a ball, the screen awkwardly jerks up instead:
Over the next couple of posts, I'll talk about some of the more interesting height-related bugs. Not all of them; there are far too many to count.