Chapter 3: alttp, reading Hyrule out of 128 KiB
Chapter 2 gave us the machine's work RAM: 131,072 bytes that change sixty
times a second. Somewhere in there is everything the game knows. Byte
$7E0010 says which part of the game is running (the title, the overworld,
a dungeon, a text box). $7E0022 and $7E0020 are Link's position in
pixels. $7EF36D is his health, in eighths of a heart, because that is how
the HUD draws half and quarter hearts.
This crate turns those bytes into names. Hand it the whole of work RAM and
you get a State back:
let state = alttp::State::read(console.wram());
state.mode // Mode::Overworld
state.step // what that mode is doing, by the game's own table
state.world // World::Light or World::Dark
state.place // Place::Outdoors(area) or Place::Indoors(room)
state.hearts() // 3.5
state.control // whether the pad moves Link this very frame
It is Serialize, so the same value is what the window's State panel
shows (chapter 7) and what the MCP state tool returns (chapter 8).
Aside: where do the names come from? Not from guessing. The game was taken apart long ago by the romhacking community, and snesrev/zelda3 is a C reimplementation that compiles to a bit-exact match of the real ROM. A mode's name here is the name of the routine the game's own dispatch table jumps to for that byte, and each table is cited on the type it produced. A byte with no table behind it stays
Unrecognised(byte)orNumbered, carrying its value, rather than getting a plausible name.
The most careful function in here is has_control. "Link is in a walking
mode" is not the same as "the player's buttons move him": four other flags
(a spell animation, an immobilised flag, a menu block, and on the overworld a
special-entrance trigger) can each hold him in place. The rule is copied out
of the two functions in the game that enforce it, and a test sets each flag
in turn to prove each one takes control away.
Mode::kind sorts every mode into what a caller has to do about it (play,
an overlay, a title, a load that ends by itself, ...), and it has no wildcard
arm: a new mode is a compile error until someone says which kind it is.
Try it.
nix develop -c buck2 test //packages/alttp:testruns the control-gate tests, the text-box test and the opening-flags test. No ROM needed: they build work RAM by hand.
For the people who maintain it
In this folder
| Path | What |
|---|---|
| src/lib.rs | State::read and its parts: Mode and Kind, Step and Interface, World, Place, Player (Link's state machine), Progress, Opening, Follower, Direction; the control gate; the tests. |
| BUCK | The rust_library (its only dependency is serde) and //packages/alttp:test. |
The addresses and what they mean come from
research/alttp-ram-map.md, which cites
the disassemblies it read them from. Who reads a State: packages/panels,
packages/mcp, packages/zbanks (its stall detector uses Place), and the
apps.
← Previous: Chapter 2, console/ · Up: packages · Next: Chapter 4, zbanks/ →