Skip to content
junewunderPublic

About

GBA Advanced: a static recompiler from Game Boy Advance ROMs to native Rust. Not ready for general use yet.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GBA Advanced (gbaa)

gbaa is a static recompiler for Game Boy Advance games. It reads a ROM's ARM and Thumb machine code, translates it ahead of time into a Rust crate, and compiles that crate together with a small runtime (the bus, the PPU, sound, timers, DMA and the BIOS calls) into a native program. The game then runs as ordinary compiled code instead of through an emulator's interpreter loop.

Not ready for general use. This is public so people can watch it being built. It plays Pokémon Emerald and a couple of romhacks well for me; everything else is in progress, and nothing is stable. Give it time.

Who it's for

Eventually, two kinds of people:

  • People who want to play their own games without an emulator. I'm building a front end for this because I'd rather not deal with an emulator layer, and for me it already works nicely. The UI is ugly right now. Making it nice is next, and when it is, there'll be a GIF here.
  • Modders, and people starting a decompilation or a native port of a GBA game. gbaa gives you a Rust crate of the whole game that builds and runs exactly like the original from day one, so you can change a piece and play the result right away. It won't name functions or variables for you. Tools that recognize library code and make it easy for LLMs to fill in readable code are being built, but aren't ready. If you want to make a native port of Emerald or any other GBA game, I'm trying to make this a good place to start.

Where it is

  • Pokémon Emerald and two romhacks of it (Emerald Rogue, Emerald Imperium) play recompiled, including saving, save states, fast-forward, rewind and controller input. Emerald's intro runs frame for frame the same as mGBA with 99.7% of guest time in compiled code. Imperium has been played for half an hour on a Steam Deck in Game Mode.
  • The hardware test ROMs (jsmolka's, armwrestler) and a set of small Rust (agb) programs written here, one per hardware feature, also run recompiled. Games past Emerald are next; Kirby: Nightmare in Dream Land is the first.
  • The output has two forms: a flat one that is fast to build, and a structured one (loops and ifs rather than a block dispatcher) that is just as exact, builds about 15% faster and is 4% smaller. A third, readable form close to the pokeemerald decompilation's C is in progress, aimed at modding.
  • Side projects on the same runtime: an mGBA-compatible Lua scripting host, linked multiplayer over the internet, and builds for Linux and the Steam Deck (macOS and Windows builds exist but haven't been tried on real machines yet).

How it works

  1. Decode (crates/armv4t): every ARMv4T instruction, with its semantics written once against a Sem trait. The same definition drives the interpreter, an IR builder, and the code generator, so there is one description of what each instruction does.
  2. Discover (crates/analysis): find the code. Telling code from data in a ROM is undecidable in general, so discovery combines recursive descent, jump-table recovery by value-set analysis, and profiles of real runs. Anything it misses still runs, through an interpreter fallback that hands control back to compiled code as soon as it can.
  3. Emit (crates/codegen): one Rust function per guest function, built with quote! and prettyplease, with the runtime's memory accessors inlined. Code the game copies into RAM is handled too.
  4. Run (crates/runtime, #![no_std]; crates/desktop for the SDL frontend).

The reasoning behind each of these choices, with the alternatives and what would change our minds, is in docs/decisions.md.

How it's checked

Correctness is defined by comparison, not by eye:

  • The interpreter is checked against SingleStepTests' ARM7TDMI suite, jsmolka's gba-tests and armwrestler.
  • The recompiled build must match mGBA and the interpreter frame for frame at fixed checkpoints in each game ("routes": scripted input to a point, then compare the screen and state). just unit <route> runs one.
  • Code discovery and jump-table recovery are scored against ground truth from the pokeemerald decompilation's symbols.

How it was built

Most of this code was written by Claude agents over about a week, working from a tracker of small units of work. Each unit had a check that decided whether it was done, usually the mGBA comparison above, and lanes of agents claimed, implemented, checked and merged units unattended. I made the design decisions, wrote the specs, and played the games at gates that only a person could pass. The setup is WFactory; how this project uses it is in docs/factory.md. The interesting part, to me, is that it works because the oracle is exact: a recompiled frame either matches mGBA or it doesn't.

Building

Needs Rust (pinned in rust-toolchain.toml) and just. just check runs formatting, clippy, the tests and the no_std build. just recompile ROM writes a crate for a ROM to out/<name>/ and just run-recompiled out/<name> builds and runs it. The built binary carries the ROM inside it and plays it when started with no arguments; give it a ROM path to run that instead. Every command is described in docs/tools/commands.md.

No ROMs are included, and none ever were. Bring your own dumps.

gbaa isn't affiliated with Nintendo, Game Freak, The Pokémon Company or HAL Laboratory. Their games and names belong to them.

Developed in the open

The tracker (tracker/), the agents' instructions (AGENTS.md) and their notes (memory/) are all here, as the agents use them. Earlier history lives in a private repo, because it held code translated from the games. Game-derived code (mod copies, hand ports, snapshots of the output on a real game) stays out of git.

Issues are welcome. I'm not taking pull requests yet.

License

MIT.

About me

I'm june wunder, and I'm looking for work. gbaa is built with WFactory, my setup for running agents through a project unattended. Another project of mine is Willow to React.

References and thanks

gbaa is built on other people's work, and most of it was done for free. This is what we used, roughly in order of how much we leaned on it. The full reading list, about two hundred papers and projects, is in docs/references.md, and what we took from each is in the notes under docs/research/.

What we check against

  • mGBA, by Vicki Pfau (endrift) and its contributors. It is the oracle: the recompiled build has to match it frame for frame. Its source was our reference for the PPU, sound, DMA, timers and its HLE BIOS, its Lua API is the one our scripting host copies, and its demo scripts are among our tests.
  • GBATEK, by Martin Korth: the hardware spec everyone actually uses.
  • Arm's ARM7TDMI Technical Reference Manual and the ARM Architecture Reference Manual (ARMv4T), and the AAPCS (ARM-software/abi-aa).
  • SingleStepTests' ARM7TDMI suite: before and after state for every instruction. It was generated with NanoBoyAdvance by fleroviux, whose rules we follow wherever ARMv4 leaves behaviour unpredictable.
  • jsmolka's gba-tests, including the bios.gba that checks our BIOS calls.
  • armwrestler, originally by Mic, in destoer's fixed GBA build.
  • zaydlang's multiplication-algorithm: our multiplier's carry flag is ported from its model of the hardware.
  • The Cult-of-GBA BIOS: our reference, with mGBA's, for what the BIOS calls do. MidiKey2Freq's exp2f is ported from glibc's so it rounds the same way.

Decompilations

  • pret et al., above all pokeemerald. Its symbols are the ground truth that code discovery and jump-table recovery are scored against, its m4a sources taught us the sound engine, and its names are the ones the readable output uses. Also pokefirered, agbcc (the compiler the games were built with, which we build them with again), and camthesaxman's gbadisasm, the prior art for finding code in a GBA ROM.
  • jiangzhengwenjz's katam (Kirby & The Amazing Mirror) and agbcc fork, zeldaret's tmc (The Minish Cap) and metroidret's mzm (Zero Mission), which we build for symbols and library matching.
  • huderlem's poryscript, needed to build Emerald Rogue.

Games we ran

Every one of these was a target, and some found bugs nothing else would have. None of them are included here.

  • Pokémon Emerald (Game Freak, Nintendo), Kirby: Nightmare in Dream Land and Kirby & The Amazing Mirror (HAL Laboratory, Nintendo), WarioWare, Inc.: Mega Microgame$! (Nintendo).
  • Pokémon Emerald Rogue by Pokabbie. Our online multiplayer for it follows Pokabbie's Rogue Assistant closely enough to be compatible with it on the wire.
  • Pokémon Emerald Imperium by iriv24, the one played on the Steam Deck.
  • Super Mariomon by Kobazco and team.
  • moonbeam by Adam Ledoux, built with his moonbeamkit (devkitARM GCC, scripted in JavaScript through Duktape).
  • ROMs randomized with the Universal Pokemon Randomizer ZX (Ajarmar and the randomizer's earlier authors), with the Kaizo IronMON settings and Corvimae's kaizo-ironmon-plus.
  • Our own test programs are written with agb, the Rust GBA library.

Lua scripts we made run

The scripting host was checked by running the community's scripts in it. The survey, with commits, is in docs/research/lua-scripts-survey.md: Real96's PokeLua, PokeRNG-LuaScripts, besteon's Ironmon-Tracker, MKDasher's PokemonBizhawkLua, nikouu's mGBA-http, dmang-dev's mcp-mgba, Gianni1707's FireRedCoop, Jengolus's RAManimator, Archipelago's connectors, and the rest listed there.

Libraries

Prior art we learned from

Tools

The code was written by Claude (Anthropic) through Claude Code.

If we used your work and you aren't here, that's our mistake. Please open an issue and we'll add you.

About

GBA Advanced: a static recompiler from Game Boy Advance ROMs to native Rust. Not ready for general use yet.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages