Static Analysis of the Stage Transition

Reading the ROM instructions that run after a stage ends

TipConclusion

The ROM instructions show that processing after a stage ends proceeds through three broad phases: the ending animation, an update to the level number, and a transition to the next screen.

When observed in mGBA 0.10.3, however, execution leaves the intended instructions during the ending animation and never advances to the next stage. Changing the argument to the second banked call to DE=1400 restores the normal transition from Stage 1 to Stage 2.

About this report

This report reads the instructions stored in the Pokémon Jade (Special Pikachu Edition) ROM and organizes the processing performed after a stage ends. Examining a program and its data without running it is known as static analysis.

Technical details such as addresses and instructions are retained so that the findings can be checked independently. Each section first explains the purpose in plain language, followed by the values that support it. See the glossary for terminology.

For the procedure used to run the game and verify the cause dynamically, see Investigating and Correcting the Stage-Transition Crash.

Target ROM

Item Value
Size 524,288 bytes (512 KiB, or 32 banks of 16 KiB)
SHA-2561 0E90D7659339A1F52C733BDC2B502D033E1C4C4004F761B6C539B236FA7FDD45
Header declarations CGB compatible, MBC1, 512 KiB ROM, no external RAM
Source-ROM checksums Both header and global checksums match

The ROM may require behavior specific to Makon/NT-family cartridges. Consequently, the MBC1 declaration in the header alone is not enough to determine how the image will behave on hardware or in an emulator.

Overall flow

Processing after a stage ends follows this sequence.

flowchart TD
  A["C21C = 6<br>Process ending animation"] --> B{"C213 = 0?"}
  B -- "No" --> A
  B -- "Yes" --> C["C21C = 7"]
  C --> D["Increment C27D"]
  D --> E{"C27D < 5?"}
  E -- "Yes" --> F["C219 = 5"]
  E -- "No" --> G["C219 = 8<br>Post-final-stage processing"]
  F --> H["C219 = 6<br>Screen transition"]
  H --> I["Convert logical level number<br>to internal level ID"]
  I --> J["Return to normal game processing"]

1. Advance from the ending animation to the next state

Clearing a stage does not move immediately to the next one. The game first plays the ending animation to completion and then determines the number of the next stage.

Progress through this sequence is stored at memory address C21C.

(i) C21C = 6: Wait for the ending animation

When C21C is 6, the routine at $240A runs.

It checks C213, a value used by the ending animation. Once C213 reaches zero, the game considers the animation complete and changes C21C to 7.

(ii) C21C = 7: Advance to the next level

When C21C becomes 7, the routine at $242A runs. It first increments the current logical level number at C27D, then uses that value to choose the next screen.

flowchart TD
  A["Check C27D"] --> B{"C27D < 5?"}
  B -- Yes --> C["C219 = 5"]
  B -- No --> D["C219 = 8"]

In other words, ordinary stages advance to the next stage, while reaching the end of the last stage selects a different screen.

The routine at $240A can therefore be understood as waiting for the stage-ending animation to finish, and the one at $242A as updating the level number and selecting the next screen.

2. A two-phase transition to the next screen

Except after the final stage, C219 passes through values 5 and 6 while the transition to the next stage is prepared.

(i) C219 = 5

The routine beginning at $0C98 runs, and its final step at $0D9F changes C219 to 6.

(ii) C219 = 6

The routine beginning at $0DA9 then runs. Its final step at $0E64 converts C27D to an internal level ID and returns C219 to 0.

This completes the screen change and returns to normal game processing.

Thus, rather than starting the next stage immediately after incrementing the level number, the program passes through two distinct states, C219 = 5 and C219 = 6.

3. Convert the displayed order to an internal level ID

C27D is a logical level number representing the stage order visible to the player. Value 0 is the first stage, and values 1 through 4 correspond to Stages 2 through 5.

Internally, the game does not use that number directly. It replaces it with an internal level ID using this table:

Logical level number 0 1 2 3 4
Internal level ID 3 4 2 1 0

The table is stored at $0E7C in the ROM as the byte sequence 03 04 02 01 00. For example, logical level 1, which is Stage 2, uses internal level ID 4.

4. Narrow down the stopping point at runtime

The routines identified by static analysis were traced in the mGBA 0.10.3 debugger. Execution reached $240A, the entry to stage-ending processing, but did not reach the next state at $242A. Instead, a call from $241C to another ROM region caused the CPU to execute non-code data as instructions and stop at an illegal opcode.

The failure occurs during the second call

Near $241C, the program calls code in other ROM regions twice.

Call DE2 Result
First 0400 Runs valid code and returns to $FF9E
Second 05C1 Enters a non-code region and stops on an illegal opcode

Why DE becomes 05C1

The first call begins with DE=0400. The called routine does not restore DE before returning, however. In the observed run, its value on return was DE=9BC1.

The caller then prepares for the second call by changing only D to 05. Since it does not change E, the C1 left by the first routine remains in place.

The unintended value DE=05C1 is therefore supplied to the second call.

Selector 05 does not expose the correct code

In addition, this ROM uses a mapper different from standard MBC1. mGBA identifies it as the special GB_UNL_NT_NEW mapper.

The value used here therefore cannot be treated as a simple ROM bank number.

Direct observation in mGBA showed that $4000 with selector 05 contained data rather than executable code. Transferring control there makes the CPU interpret the data as instructions, eventually reaching an illegal opcode.

With selector 14, operation number 00, and therefore DE=1400, the correct routine appeared at $4000. It ran successfully and returned to $FF9E.

The second call therefore has these outcomes:

DE Result
Before correction 05C1 Enters an invalid region
After correction 1400 Executes the correct routine

This evidence shows that the second call must receive DE=1400. The header CRC323 that mGBA uses when identifying this ROM as GB_UNL_NT_NEW is 8628A287.

5. Place the corrective routine in unused ROM space

Only five bytes at $241A-$241E are available for replacing the original instructions. Setting DE=1400 and calling $FF90 requires six bytes, so the corrected instructions do not fit in place.

A small seven-byte routine is therefore placed at the unused range $37F9-$37FF in the fixed bank4.

37F9: 11 00 14    ld   de,$1400
37FC: CD 90 FF    call $FF90
37FF: C9          ret
NoteMeaning of the instructions
Instruction Meaning
ld Loads a value into a register or memory. ld de,$1400 places $1400 in the 16-bit DE register.
call Calls the routine at an address. Execution returns to the instruction after call when that routine finishes.
ret Returns to the caller using the return address saved by the preceding call.

Because 16-bit values are stored in little-endian order, $1400 appears in machine code as 00 14, and $FF90 as 90 FF.

The original five bytes are replaced by a call to this routine and two NOP instructions.5

241A: CD F9 37    call $37F9
241D: 00          nop
241E: 00          nop

This approach leaves the update to C25C and the RET at $241F-$2423 unchanged. When generating the patch, three conditions are checked to avoid modifying the wrong ROM:

  1. The source ROM’s SHA-256 must match.
  2. $241A-$241E must contain the original instructions.
  3. $37F9-$37FF must contain seven FF bytes marking unused space.

6. Preserve the way mGBA identifies the ROM

mGBA uses the CRC32 of the ROM header at $0100-$014F to select a special mapper for this image. The global checksum stored at $014E-$014F is itself part of that CRC32 range.

The corrected build therefore does not recalculate the global checksum and preserves the source ROM’s header exactly. Although the modified ROM contents no longer agree with the stored global checksum, its header CRC32 remains 8628A287, allowing mGBA to continue selecting GB_UNL_NT_NEW.

7. Verify the stage transition in the corrected build

In the mGBA-compatible corrected ROM with its header preserved, the Stage 1 goal processing completed and the game advanced normally to Stage 2.

This verifies three points:

  • The call site responsible for the crash was identified correctly.
  • Control returns normally from the added corrective routine.
  • The real stage transition completes after the correction.

Appendix: Glossary

Term Meaning in this report
byte A small unit of data, used here for the lengths of instructions and unused regions.
ROM A file containing the game’s program and data.
bank A 16 KiB division of a larger ROM, selected as needed.
mapper Cartridge hardware or emulated logic that changes which ROM bank the CPU can see.
CPU The central part of the console that executes instructions from the ROM.
ROM header Administrative data describing properties such as ROM type and size.
checksum, CRC32 Values calculated from data and used to detect corruption or identify a ROM.
mGBA A Game Boy emulator; version 0.10.3 was used in this investigation.
memory address A number identifying a program or data location. A leading $, as in $240A, indicates hexadecimal notation.
state number A value indicating which process is currently active. This ROM stores such values at C21C and C219.
logical level number A value representing the stage order seen by the player, stored at C27D.
DE A combined value formed from the CPU’s small D and E registers, used here to select a bank and operation.
illegal opcode A state in which the CPU attempts to execute data it cannot treat as an instruction, indicating that control has left the intended path.

Appendix: Uses of C27D

The logical level number at C27D is read at these locations:

Address Purpose
$0E67 Select an internal level ID from the logical level number.
$242A Read the current number when a stage is completed.
$6061 in bank 3 Look up level-specific data.

It is written at these locations:

Address Purpose
$03E6, $07AD, $0A94, $108E Initialize the value.
$242E Store the incremented value when a stage is completed.

The instruction sequences needed for independent verification appear here and in the Investigation and Complete Fix Record. Extensive disassembly is not published.

Footnotes

  1. SHA-256 is an identifying value used here to distinguish ROM images. Changing even one byte will normally produce a different value, making it possible to detect a different ROM with the same filename.↩︎

  2. DE is a 16-bit Game Boy CPU register formed by combining the 8-bit D and E registers. Here it is used as an argument when calling another ROM region.↩︎

  3. A checksum that calculates a short 32-bit value from data; formally, a 32-bit Cyclic Redundancy Check.↩︎

  4. A ROM area that remains visible to the CPU and is not affected by bank switching.↩︎

  5. No Operation: a one-byte instruction that performs no work and advances to the next instruction.↩︎