Here we shall discuss how to dissect these pesky screen transitions, hopefully once and for all. It is advised that you first read the page on Level Data Banks before continuing on.
Now, as discussed on the previously linked page, every screen has a screen transition index assigned to it. The index refers to a pointer table that starts at 0x142E5 in the ROM. These pointers point to scripts that define the transition's behavior. The scripting “language” used in these transitions will be described below.
Once the game identifies the script to use, it copies it to $D700 in RAM (which is the easiest place to inspect how they work). From there, it interprets the script byte by byte in a big loop.
First it checks if it has reached the terminating operator ($FF), and exits if that's the case. After that, it masks the right nybble of the current byte (e.g. $B2 becomes $B0), and then goes through the low-level equivalent of a switch statement (or rather, a bunch of if-else statements) to find the routine that matches the operator. After processing the operator's operands, the interpreter loops back to the beginning, where it processes the next operand.
Scripts are limited to 64 (0x40) bytes in length, but you'll literally never hit that limit.
(Note that all 16-bit values (pointers, etc.) are assumed to be little-endian, as usual.)
Copies a block of data starting at a specified bank and address to somewhere in RAM.
This doesn't seem to be used that often, probably because it's so verbose. The B* operator is typically used instead for loading sprite and background graphics.
Copies the desired metatile table to $DA00-$DBFF in WRAM.
The metatile table pointer list begins at 0x23F1A in ROM.
For some reason, the pointers are a little out of order.
80 4C (final ruins) should be 3rd 80 4A (inside ruins) should be second 80 48 (bubbles/ruins 3 catacombs) should be first 80 4E (queen room) 80 50 (first/last caves) 80 52 (outside/ship) 94 55 (acid caves down) should be eighth A8 56 (acid caves up) should be ninth 80 54 (acid caves mid) should be seventh BC 57 (outside ruins)
Copies the desired collision table to $DC00-$DCFF in WRAM.
The collision table pointer list begins at 0x23EEA in ROM.
Copies the desired solidity indexes to WRAM.
The solidity indexes are stored in a table starting at 0x23EFA. Each entry row is four bytes wide, though only three are used.
The bytes are copied to these locations:
Basically, for Samus, the way it works is that if an 8×8 tile is less than the specified index, then it is treated as a solid. It works the same for enemies and projectiles as well.
Warps Samus to the specified map and screen.
Note: Place the x/y coordinate one screen “behind” the desired destination.
This opcode is used only once, when Samus retreats from the last boss fight by falling through the hole in the floor. It performs several tasks:
Form: 6* xx yy
I'm not sure if this is ever used, but it's cool at least.
For reference, the default starting values are $02 and $08.
This opcode is used only once, when leaving the last boss room after defeating her. It performs the following:
Handles a lot of special stuff regarding the queen fight, but not everything.
Form: 8a bbbb cccc dddd eeee
Form: 9* nn xxxx
“If the amount of Metroids remaining is less than or equal 'nn', then jump to and do transition 'xxxx' instead.”
This is how the game handles lowering (and raising) acid levels. Keep in mind that earthquakes are handled elsewhere.
Note that this uses the real Metroid counter (memory address $D089), which also accounts for the Metroids in the endgame. Also keep in mind that this value is in binary coded decimal.
When changing tileset, this is typically the first operator in the script.
0 - No change in music. - Silences the roar from "song" A below 1 - "The Last Metroid" 2 - Queen Fight 3 - Inside Ruins 4 - Main Tunnels 5 - Ambience 1 6 - Ambience 2 7 - Ambience 3 (bugs) 8 - Omega Metroid Area 9 - Final Ruins A - No music: SFX - Metroid Queen roar (persists) B - Final Alarm C - Metroid Fight D - Ambience 4 E - SFX: Earthquake F - Metroid Defeated
This loads the graphics for the orb, the desired item, the item font (well, up to the number 2), and the corresponding text string to VRAM.
0 - Save ... (just for the message) 1 - Plasma Beam 2 - Ice Beam 3 - Wave Beam 4 - Spazer 5 - Bombs 6 - Screw Attack 7 - Varia Suit 8 - High Jump Boots 9 - Space Jump A - Spider Ball B - Spring Ball C - Energy Tank (unused) D - Missile Tank (unused) E - Energy (i.e. refill; again, unused) F - Missiles (i.e. refill; unused)
Note that, for the last 4 items, their sprites are always in VRAM, and they don't cause text strings to appear, so this is unnecessary for them.
The game does not account for operators in the form of E* or F*. Using these will cause the game to freeze, because the interpreter will get stuck in an infinite loop. However, an intrepid hacker could use these to extend this language to do other radical things.
This the transition from the save room in the first ruins to the exterior area:
A0 B1 08 BC 69 26 36 19 B2 06 20 5D C5 49 E7 FF
Let's make this easier to read:
A0 B1 08 BC 69 26 36 19 B2 06 20 5D C5 49 E7 FF
Hypothetically, we could convert this into some sort of mnemonic or macro-based form (like assembly):
FADE GFX_BG 08:69BC COLLISION 6 SOLID 6 METATILE 9 GFX_SPR 06:5D20 SONG 5 WARP 9,E,7 END
But that would be just silly.
(Someone please do this.)
Here's the transition to the first cave with acid.
A0 B1 07 00 68 25 35 B2 06 20 61 4B 0C 90 46 E1 01 18 FF
For reference, this is the transition script referred to in the conditional operator it uses.
Deciphering this will be left as an exercise for the reader.