Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pmret/papermario/llms.txt

Use this file to discover all available pages before exploring further.

Matching a function means writing C code that — when compiled with the project’s IDO compiler — produces MIPS assembly that is byte-for-byte identical to the original ROM. This is the primary contribution activity in the project. Each matched function brings the decompilation one step closer to a fully reconstructed source tree.

Before you begin

You need a successful build (papermario.z64: OK) and the extra contributor dependencies installed:
./install_deps.sh --extra

Matching workflow

1

Snapshot the expected build

Once you have a passing build, copy it to the expected/ directory so diff.py has a reference to compare against:
./make_expected.sh
This copies ver/us/build/ to ver/us/expected/ver/us/ (and equivalently for JP, PAL, and iQue). You only need to do this once per session, or after pulling upstream changes that affect the build.
2

Pick a function to match

Unmatched functions live as .s assembly files under ver/us/asm/nonmatchings/. Browse the subdirectories and choose one. Also locate the .c file that references your chosen function — it will contain an INCLUDE_ASM macro with the function name.
3

Generate a context file with m2ctx.py

mips_to_c needs to know the project’s type definitions and function prototypes. Generate a context file from the relevant .c source file:
./tools/m2ctx.py path/to/source_file.c
This produces a file called ctx.c in the repository root, containing preprocessed declarations from all included headers.
4

Convert assembly to C with mips_to_c

Pass the function’s .s file through mips_to_c to get an initial C scaffold.Local version (recommended once you’re used to it):
../mips_to_c/mips_to_c.py ver/us/asm/nonmatchings/FOO/func_DEADBEEF.s --context ctx.c
Clone mips_to_c into a sibling directory of papermario first and run with -h to see all options.Web version (easier to start with): paste the .s file contents into the top box at simonsoftware.se/other/mips_to_c.py and the ctx.c contents into the lower box.
5

Replace INCLUDE_ASM with the C code

Open the .c file and replace the INCLUDE_ASM line for your function with the output from mips_to_c. For example, for func_DEADBEEF in src/FOO.c:
// src/FOO.c
- INCLUDE_ASM("FOO", func_DEADBEEF);
+ s32 func_DEADBEEF() {
+     // ...
+ }
mips_to_c frequently emits void pointers and unusual syntax. You will need to correct the function signature and fix type errors before the file compiles.
6

Compile with ninja

Rebuild the ROM:
ninja
The build will either FAIL (ROM doesn’t match the baserom) or fail to compile. Address compilation errors first — consult include/common_structs.h for struct definitions.
7

Compare assembly with diff.py

Once the file compiles, compare your output against the original:
./diff.py -mwo func_DEADBEEF
  • The left column shows the original game’s assembly.
  • The right column shows what your C code produces.
  • -mw tells diff.py to watch the file and recompile on every save.
  • -o colourises the diff output.
Use -3 to add a third column showing the previous saved version, or -b to compare against the assembly from when diff.py was started.
If you use Visual Studio Code, Run Test Task runs diff.py and shows compiler errors inline. Binding it to a key makes the iteration loop much faster.
8

Iterate until the function matches

Edit your C code, save, and watch diff.py update automatically (with -mw). Keep adjusting until both columns are identical — at that point the function is matched.If you get stuck, try:
  • Asking on Discord
  • Running decomp-permuter to search for equivalent code that compiles to a match
  • Wrapping your best attempt in #ifdef NON_MATCHING and moving on to a smaller function
9

Clean up matched ASM files

After matching, run:
./coverage.py --delete-matched
This removes the .s files from nonmatchings/ that correspond to functions now implemented in C.
10

Open a pull request

Push your branch and open a pull request on GitHub. By submitting, you agree to give the maintainers permission to use, alter, and distribute your contribution.

When you can’t fully match a function

Sometimes matching isn’t possible — the compiler produces different register allocation, or a code pattern has no clean C equivalent. Use the labels below to mark these cases. Always leave a comment above the function explaining the specific issue.

NON_MATCHING

Use NON_MATCHING when your C code is logically equivalent to the original but doesn’t compile to identical assembly (for example, a register swap you can’t reproduce):
// s3/s4 swap — can't reproduce without matching register allocation
#ifdef NON_MATCHING
void func(void) {
    // your best-effort C implementation
}
#else
INCLUDE_ASM(void, "file", func, void);
#endif
Once the project is shiftable, NON_MATCHING functions will be used in place of the included ASM.

NON_EQUIVALENT

Use NON_EQUIVALENT when you’re unable to figure out a section of the code, or when you’re confident the original logic can’t be replicated cleanly. Make a genuine best-effort attempt — a half-finished function is less useful to the next contributor:
// can't figure out the MULT_HI() stuff
#ifdef NON_EQUIVALENT
void func(void) {
    // partial implementation
}
#else
INCLUDE_ASM(void, "file", func, void);
#endif
Don’t submit a NON_EQUIVALENT function without a comment explaining what you tried and where you got stuck.

Build docs developers (and LLMs) love