Back to Projects

Mario Tennis GBC: the level-up system, decoded

Reverse engineered from a ROM dumped from my own cartridge · May 2026

The four RPG characters (Alex, Harry, Nina, Kate) each have 11 substats shown on a 1-10 scale. On level-up you pick a category (Spin, Power, Control, Speed) to invest in. Stats are not stored directly; they are recomputed from your category investments and each character's hidden threshold table every time the game recalculates. The "stat decay" community players observe is a natural consequence of this formula, not a separate mechanic.

How it was done

Three days, seven working sessions, alternating between reading the game's code and watching it run.

Tools

  • Ghidra with the GhidraBoy extension, for disassembly and decompilation of the Game Boy Color's SM83 CPU.
  • GhidraMCP connected to Claude Code, so the AI could decompile functions, follow cross-references and rename symbols in Ghidra directly. GhidraMCP is LaurieWired's bridge; I set it up by hand from its 1.4 release, before it shipped as part of a ready-made reverse-engineering bundle.
  • BGB, a high-accuracy emulator, for memory searches and write watchpoints on the running game.

The loop

The AI walked the code statically and flagged the exact bytes or memory addresses that needed checking at runtime. I checked them in the emulator and reported back, and the analysis continued from confirmed ground truth.

Milestones

  1. Mapped the engine and decoded its cross-bank call trampoline, which every function in the level-up system goes through.
  2. Found the 11 substats in memory with emulator searches, plus a second block holding the up/down arrows on the level-up screen. Whatever wrote those arrows had to be the stat algorithm.
  3. Traced the level-up screen's call flow back from those addresses.
  4. Found the function that recomputes every stat from scratch, and with it the formula and the equipment system.
  5. Read all four characters' threshold tables (44 arrays) straight out of the ROM file.
  6. Resolved a call the decompiler couldn't follow by reading its two hidden inline bytes from the ROM. It led to the arrow preview, which simulates the real spend on a copy of the character and then rolls everything back.
  7. Matched each character to their internal type from their starting stats on fresh saves. The first guess had two of them swapped.

Finally, the formula was checked against the running game, which caught an off-by-one in the first version.

What came out of it

  • An exact model of every displayed stat, for every character, at every point distribution, including equipment.
  • "Stat decay" explained: there is no decay routine. Every level adds a penalty to the categories you didn't invest in.
  • Each character's hidden affinities, measured. For example, Harry needs 17 straight points in Speed to max it, the hardest grind in the game; no other character needs more than 13 anywhere.
Level 1
Category Substat Value
How it works: Each category's intermediate = (pts_in_category × 5) − total_pts. The displayed stat = 1 + (number of the character's 9 thresholds that the intermediate strictly exceeds). Equipment bonuses apply after, clamped to [1, 10]. Yellow = maxed at 10.

Objective

Configure options above and click Find Best Build.
How it works: Exhaustive search over all point distributions (Spin, Power, Control, Speed) summing to max level − 1. C(N+3, 3) combinations — at max level 50 that is ~23,000 combinations, instant in JS. The optimizer finds the allocation that maximises your chosen objective. Max total stats sums all 11 displayed values. Max weakest stat maximises the minimum displayed value (balanced build). Weighted uses per-substat weights you configure.

Best builds at max level (99) for every character, searched across all 21 equipment combinations (7 rackets × 3 shoes). Max Total Stats shows the build that maximises the sum of all 11 displayed values. Max Weakest Stat shows the build that maximises the lowest displayed stat — the most balanced option available. Both total and weakest values are shown for every build.

Results are fixed at level 99 (98 points). Equipment is searched exhaustively — the combination shown is guaranteed optimal for that objective and character.