|
|
As we saw last time, when Sonic & Knuckles detects the Sonic 3 ROM header in the lock-on slot, it starts up a mode in which it can reference content from the Sonic 3 ROM. However, 99% of the code executed still comes from the Sonic & Knuckles ROM, so you're effectively still playing Sonic & Knuckles, just a version which is allowed to access stuff in the Sonic 3 cartridge.
It bears repeating: regardless of whether you're playing Sonic 3 & Knuckles or standalone Sonic & Knuckles, the same exact code is executing, which means the game plays exactly the same. The only difference is whether the code is allowed to reach into the Sonic 3 ROM or not.
So, how exactly does the Sonic & Knuckles code manage to use content from the Sonic 3 ROM?
The developers had a powerful ally on their side: hindsight. By the time Sonic & Knuckles entered full production, Sonic 3 was already a done deal. The ROM was finalized, printed into game cartridges and being sold around the world. S&K can take full advantage of the fact that everything in the Sonic 3 ROM is in a known location.
All the developers had to do was take a pointer to a particular asset in the Sonic 3 source code, add $200000 to it, and that's where Sonic & Knuckles would find it once the cartridges were locked on.
EndOfROM: if Sonic3_Complete=0 org $200000 include "Lockon S3/LockOn Pointers.asm" else include "Lockon S3/LockOn Data.asm" endifThe current disassembly has two build modes, depending on the value of the Sonic3_Complete flag. When the flag is on, the LockOn Data.asm file includes all the assets from the Sonic 3 ROM which are referenced by Sonic & Knuckles, similarly to the setup the developers used until the 0517 prototype:
AIZ1_16x16_Primary_Kos: binclude "Levels/AIZ/Blocks/Act 1 Primary.bin" even AIZ1_16x16_Secondary_Kos: binclude "Levels/AIZ/Blocks/Act 1 Secondary.bin" even AIZ1_16x16_MainLevel_Kos: binclude "Levels/AIZ/Blocks/Act 1 Main Level.bin" even AIZ1_8x8_Primary_KosM: binclude "Levels/AIZ/Tiles/Act 1 Primary.bin" even AIZ1_8x8_Secondary_KosM: binclude "Levels/AIZ/Tiles/Act 1 Secondary.bin" even AIZ1_8x8_MainLevel_KosM: binclude "Levels/AIZ/Tiles/Act 1 Main Level.bin" even AIZ1_128x128_Kos: binclude "Levels/AIZ/Chunks/Act 1.bin" evenWhen the flag is off, the LockOn Pointers.asm file replaces all the Sonic 3 assets with their location in the final Sonic 3 ROM. This is analogous to the setup used from the 0525 prototype all the way through the final:
AIZ1_16x16_Primary_Kos: ds.b $200 AIZ1_16x16_Secondary_Kos: ds.b $C40 AIZ1_16x16_MainLevel_Kos: ds.b $10E0 AIZ1_8x8_Primary_KosM: ds.b $E12 AIZ1_8x8_Secondary_KosM: ds.b $2FD2 AIZ1_8x8_MainLevel_KosM: ds.b $27F2 AIZ1_128x128_Kos: ds.b $3990 AIZ1_8x8_Flames_KosM: ds.b $A82
Alright, next. What can we reuse from the Sonic 3 ROM?
Obviously, as we just saw, data can be referenced, so long as it doesn't contain pointers to other ROM locations. Data containing ROM pointers suffers the same fate as the 68000 vector table before it: all the pointers now refer to random location in the Sonic & Knuckles ROM rather than their intended location in the Sonic 3 ROM.
This is exactly the case with the level load block: all the underlying data can be reused, but the level load block itself is unusable and is thus completely reconstructed in the Sonic & Knuckles ROM.
LevelLoadBlock: dc.l AIZ1_8x8_Primary_KosM+$B000000 dc.l AIZ1_8x8_Secondary_KosM+$B000000 dc.l AIZ1_16x16_Primary_Kos+$A000000 dc.l AIZ1_16x16_Secondary_Kos+$A000000 dc.l AIZ1_128x128_Kos dc.l AIZ1_128x128_Kos ; --------------------------------------------------------------------------- dc.l AIZ2_8x8_Primary_KosM+$C000000 dc.l AIZ2_8x8_Secondary_KosM+$C000000 dc.l AIZ2_16x16_Primary_Kos+$B000000 dc.l AIZ2_16x16_Secondary_Kos+$B000000 dc.l AIZ2_128x128_Kos dc.l AIZ2_128x128_Kos ; --------------------------------------------------------------------------- ...
What about code?
Object code is mostly unusable, due to the frequent use of hard ROM pointers in the initialization of their SSTs, such as the code pointer and the mappings pointer.
move.l #loc_24090,(a0) ; unusable move.l #Map_Spikes,$C(a0) ; unusableThe next concern are jumps. Short jumps using the Bcc, BRA or BSR instructions are fine, because they're assembled into a branch opcode with a relative displacement. The displacement is always applied to the program counter's current value, so the branch target is always the same regardless of the address the ROM was rebased to.
bne.s Obj_MonitorFallUpsideDown ; OK bsr.w MoveSprite ; OKFinally, long jumps using the JMP or JSR instructions are fine, so long as they use the PC-relative syntax. Jumps using the effective address syntax are assembled as hard pointers to their destination addresses, and are therefore unusable due to the reasons outlined above.
jsr Refresh_PlaneTileDeform(pc) ; OK jmp (AIZ1_ApplyDeformWater).l ; unusableIn practice, the only code Sonic & Knuckles reuses from Sonic 3 is screen and background event code pertaining to the Sonic 3 stages. This code is present in the Screen Events.asm file, which is only included when the Sonic3_Complete build flag is on.
That file actually contains several routines related to the rendering of background planes, which then appear duplicated in the Sonic & Knuckles source code; they are identified by their "S3_" prefix. These routines might seem like pointless redundancy, but that line of reasoning is a sunk cost fallacy: in practice, the routines are on the Sonic 3 cartridge, which the customer already paid for!
I assume graphics and music can be reused?
ReplyDeleteOh, so that's why the disassembly is missing Sonic 3 alone data.
ReplyDeleteWait, so how was Sega able to use Sonic 3 objects in Sonic 3 & knuckles?
Thanks for a great rread
ReplyDelete