Cart slot
Insert a cart
Every cart below is a plain WebAssembly module running on the reference renderer, right in this page.
Hardware
GBA-era specs, bit for bit
Palettes, tiles, maps and sprite attributes use the Game Boy Advance's own formats, so decades of documentation and tools still apply.
How it works
The hardware is memory
There is no drawing API. A cart writes tiles, maps, palettes and sprites into fixed addresses of
its own linear memory, laid out like the GBA's. Sixty times a second the console calls
update() and turns that memory into a frame.
Any language
Rust, Zig, C, AssemblyScript, Go: if it compiles to wasm and can write to an address, it can make a cart. No bindings needed.
Save states for free
All state lives in linear memory. Snapshot it between frames and you have save states, rewind and replays.
Deterministic
No clock, no random syscalls: one boot seed plus the inputs fully determine a run. Netplay and golden-image tests just work.
One file
Code, art and metadata ship in a single .wasm, at most 32 MiB, like a cartridge. One import: trace.
Get started
A cart in twenty lines
The Rust SDK wraps the registers in typed helpers and emulates the hardware natively, so cart logic is testable with plain cargo test.
use wasm_ad::*;
wasm_ad::cart! { title: "My Cart", author: "me" }
#[unsafe(no_mangle)]
pub extern "C" fn start() {
bg_palette()[0] = rgb(40, 60, 160); // backdrop
obj_palette()[1] = rgb(255, 216, 64);
obj_tiles()[0] = [0x11; 32]; // a solid 8×8 tile
reg::DISPCNT.write(dispcnt::MODE0 | dispcnt::OBJ | dispcnt::OBJ_1D);
}
#[unsafe(no_mangle)]
pub extern "C" fn update() {
let x = (frame() % 240) as i32;
oam::set(0, Sprite::new(x, 76, SpriteSize::S8x8, 0));
}
Add the SDK
wasm-ad = { git = "https://github.com/raskell-io/wasm-ad" }Keep out of the hardware region
Two linker flags put your data and stack above 0x28000. The README has the .cargo/config.toml.
Build and play
cargo build --release --target wasm32-unknown-unknownDrop the .wasm into the cart slot above.