Find a file
2026-03-31 23:04:38 +01:00
arch More docs 2026-03-29 22:20:12 +01:00
common Solid doors, simplified segment parenting logic 2025-09-02 12:15:54 +01:00
data Switched to 20:12 fixed point for velocity 2025-09-02 12:15:54 +01:00
doc Updated README 2025-09-02 12:13:49 +01:00
math Added ledge grabbing 2025-09-02 12:15:52 +01:00
meta Solid doors, simplified segment parenting logic 2025-09-02 12:15:54 +01:00
src Finished documentation of main codebase 2026-03-31 23:04:38 +01:00
.gitattributes No longer use locking for PNG files 2025-09-02 12:15:13 +01:00
.gitignore Improved sliding, added coins to castle 2025-09-02 12:15:52 +01:00
build.rs Solid doors, simplified segment parenting logic 2025-09-02 12:15:54 +01:00
Cargo.lock Added CLI frontend 2026-01-28 12:40:55 +00:00
Cargo.toml Added CLI frontend 2026-01-28 12:40:55 +00:00
README.md Finished documentation of main codebase 2026-03-31 23:04:38 +01:00
rust-toolchain.toml Began updating rust 2025-09-02 12:15:51 +01:00

An icon showing Mario's head Super Mario 64, GBA Edition

A screenshot showing Peach's Castle, the starting stage

What is this?

Super Mario 64 GBA (SM64 GBA) is an unofficial demake (i.e: from-scratch remake for less capable hardware) of the 1996 game Super Mario 64, for the Nintendo Gameboy Advance. It is not a complete game and should, for the time being, be considered a tech demo only.

SM64 GBA is a clean-room reimplementation of the mechanics of the original SM64. That means that none of the following have been used in its creation:

  • Disassembly of the original SM64 ROMs
  • Information observed in SM64 decompilation projects
  • Propertiary technical information about the original SM64
  • Use of Nintendo patents or other intellectual property not broadly considered fair use

An exception to this are the game's assets, which are based on rips from a combination of SM64 and SM64 DS. However, effort has been made to keep the code and assets logically distinct, allowing the code to be freely reused by anybody that wants to make use of it, without infringing on Nintendo's intellectual property. We do not condone the public redistribution of proprietary Nintendo assets.

Note that a license has not yet been chosen for the source code. A reasonable assumption is that the code will be placed under a 'credit required, for non-profit use only' style license, allowing its use in personal, hobby, and fan projects but not commercial ventures.

Goals

  • Demonstrate that a port of SM64 to the GBA is, at least in theory, possible

Non-goals

  • Reimplementation of buggy behaviour (no BLJs or HOLPs, for example)
  • Perfect 1:1 recreation of game mechanics. SM64 GBA is its own game, and should be considered 'inspired by' the original

Features

The following list hasn't been properly maintained and might not be up to date.

Feature list - [-] Rendering - [x] Implement basic rasterisation - [x] Texture support - [x] Figures - [x] Animation - [x] Stage - [-] Water - [x] Water level - [x] Switch palette when underwater - [-] Interaction with figures - [x] Translucent shadows - [-] Particles - [x] Level geometry - [x] Build-time mesh processing - [x] Build-time flat colour derivation - [x] Acceleration structure - [x] Render sky texture - [-] Camera - [x] Vertex matrix transform - [x] Perspective transform - [x] Basic movement - [-] Camera reorientation - [-] First person - [-] Mario - [x] Basic rendering (blob) - [x] Basic camera-relative movement - [x] Gravity - [x] More complex movement - [x] Wall slide/jump - [x] Long jump - [x] Triple jump - [x] Ground pound - [x] Crouch - [x] Dive - [x] Belly slide - [x] Butt slide - [x] Punch - [x] Kick - [x] Side jump - [-] Ledge grab - [x] Swimming - [x] Pre-render sprites for each limb - [x] Animations - [-] Physics - [x] Collision detection - [x] Basic collision resolution - [-] Ordered/iterative collision resolution - [x] Moving terrain-like objects - [-] Entities - [x] Implement ECS - [-] Objects - [x] Doors - [x] Stars - [-] Coins - [-] Trees - [-] Enemies - [-] Goombas - [-] Characters - [-] Toads - [-] Koopa the Quick - [-] Scenes - [x] Stage changes - [-] Scene transitions - [x] Doors allow transitions - [x] Allow toggling stage elements when going through transitions - [x] Porthole - [x] Bowser face - [-] Separate 'stage' and 'scene' - [-] Intro sequence - [-] Progression - [x] Stars - [-] Saving game state - [-] Associate stars with save state - [-] Gameplay - [x] Scene changes - [x] Health - [-] Lives - [-] Menu/UI - [x] Pause menu - [-] Main menu - [-] Signs - [-] Audio - [x] Basic sample-based audio - [x] Simple additive mixer - [x] Support for playing sound effects through channels - [x] Background music - [-] Sequenced music - [-] Debugging - [x] Build-time metrics switch - [x] Metric: pixel tests - [x] Metric: pixel draws - [x] Metric: vertex transforms - [x] Metric: polygon draws

Build & run (mGBA)

Note that these instructions assume a Unix-like environment. Linux, Mac OS, or Windows Subsystem for Linux (WSL) should all work.

Requirements:

  1. Install rustup.

  2. Install mGBA (on Ubuntu, the package is mgba-qt).

Run the executable with mGBA (mGBA can run ELF executables directly):

# Build and run the game executable
cargo --config arch/gba.toml run -p sm64-gba-gba --release

Build & run (PC)

Note that these instructions assume a Unix-like environment. Linux, Mac OS, or Windows Subsystem for Linux (WSL) should all work.

Requirements:

  1. Install rustup.

Run the executable:

# Build and run the game executable
cargo --config arch/pc.toml run -p sm64-gba-pc --release

Build (hardware / other emulators)

Note that these instructions assume a Unix-like environment. Linux, Mac OS, or Windows Subsystem for Linux (WSL) should all work.

Requirements:

  1. Install rustup.

  2. Install the freestanding ARM version of GCC's binutils (on Ubuntu, the package is binutils-arm-none-eabi).

  3. Install gbafix (there are several implementations. Since you have Rust installed, cargo install gbafix will do).

# Build the game executable
cargo --config arch/gba.toml build -p sm64-gba-gba --release

# Copy the contents of the game executable's ELF into a ROM binary
arm-none-eabi-objcopy -O binary target/gba/release/sm64-gba-gba target/sm64-gba.gba

# Fix-up the ROM header such that it is compatible with the GBA's BIOS
gbafix target/sm64-gba.gba

Side-load the .gba using your desired method, such as a flash cart.

Debugging

Requirements:

  1. Installed cargo-asm, by running cargo install cargo-show-asm.

When developing, it's often useful to disassemble a specific function to ensure that the compiler is generating good assembly code.

# Disassemble the given function with inline Rust annotations, stripping data (LUTs, etc.)
cargo asm --config arch/gba.toml -p sm64-gba-gba --bin sm64-gba-gba --color --rust --simplify --this-workspace -- function_name | grep -v "asciz"

# Disassemble math code
cargo asm --target armv5te-unknown-linux-gnueabi --rust -p sm64-gba-math --lib

# Inspect symbols in address order
arm-none-eabi-nm -S -C rust target/gba/release/sm64-gba-gba | sort

Testing

# Run tests
cargo test -p sm64-gba-math

The build script

To minimise the amount of work that needs to happen as the game is running, SM64 GBA uses a compile-time build script (build.rs) to preprocess assets before inserting them into the game ROM. A lot of this code is untidy and quite heavily macro-ified. It's certainly possible to improve it, but be aware that a lot of its behaviour is surprisingly subtle. Changes to some assets can also require corresponding changes in the source code to match. For example, the number of vertices in the player model is a hard-coded constant that needs to be updated when the model changes. Most of these constants are specified in common/src/lib.rs, and the build script will at least make an attempt to indicate which value requires changing.

Portability

SM64 GBA has been ported to PC. This has been done by abstracting the environment provided by the platform out (see the Env trait), allowing the core game code to run on both PC and the GBA. The entry point (main) for each can be found in their respective architecture crates (arch/<platform>).

A note on unsafe

Rust is what is often referred to as a 'memory-safe' language. In reality, this means that it has a memory-safe subset that cannot invoke undefined behaviour. However, for this to be true first requires that any unsafe code respects the language's semantic safety rules.

In SM64 GBA, this is not always the case. Dynamic safety mechanisms like bounds checks are much costlier on old ARM devices like the GBA than on modern hardware, so SM64 GBA very liberally removes them. In fact, bounds checks in particular are removed by default where possible when running in release mode, and there are several edge cases that result in UB as a result: knobbled ECS memory, rasteriser overflows corrupting the palette memory, etc. Fixing them all, and in all cases, has proven to be extremely difficult without compromising performance. Many of them, particularly in the rasteriser, rely on subtle numerical properties of the calculations being performed.

Needless to say, this isn't how you should be writing Rust. If you're new to the language, don't take SM64 GBA as an example of best-practice. It is deeply utilitarian, domain-specific code. The GBA is a very low-stakes environment: it is difficult to write code that can permanently damage the hardware and the worst that can happen is a corrupted save file.

Material attributes

Stage material attributes are applied with material names like <name>:attr0,attr1,attr2.

Attributes include:

  • slide: causes the player to enter a sliding state when standing on the surface
  • slippery: reduces friction when moving on the surface
  • unsolid: Surface does not partake in collision detection
  • floor: Surface always behaves like a floor, no matter how steep
  • monkeybars: Surface can be traversed when a ceiling
  • animate_u=<f32> and animate_v=<f32>: Surface texture coordinates scroll by the given amount over one second
  • double_sided: causes surfaces to have a front and back side, useful for transparent objects like fences
  • billboard: causes surfaces to turn into a billboard. Ensure that you do not triangular the surface or you will end up with multiple billboards
  • node=<name>: label the surface as the origin of a particular world node. Can be used to create entities, specify the spawn point, and much more. Does not respect other attributes unless also_geometry is specified too.
  • also_geometry: Used only to specify that a node material also generates geometry (used for doors, for example)
  • face=<name>: label the surface as having particular non-trivial properties (TODO: rename to surface and squash all other attributes into this)
  • transparent=<kind>: make a surface semi-transparent (accepts darken, lighten, water)
  • invisible: make a surface entirely invisible (i.e: does not partake in rendering at all)
  • doorway: The geometry is solid except if the player is performing some forced action (like walking through a door). Implies also_geometry and invisible.