All non-modern code contributions must produce a ROM that is byte-identical to the original. This page describes what that means in practice, and the conventions you must follow to get there.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pret/pokeemerald/llms.txt
Use this file to discover all available pages before exploring further.
The matching requirement
agbcc vs. modern builds
The original game was compiled with agbcc, an old ARM C compiler derived from GCC 2.7. Non-modern code must compile cleanly under agbcc.The repository supports a
MODERN build flag that uses a current GCC toolchain. Modern-only code is guarded with #if MODERN. Do not introduce modern-only constructs into code paths that must match the original ROM.Integer types
Use the GBA integer types frominclude/gba/types.h. Do not use int, unsigned int, short, etc. directly in game structs or function signatures where a specific width is required.
| Type | Width | Signedness |
|---|---|---|
u8 | 8-bit | unsigned |
u16 | 16-bit | unsigned |
u32 | 32-bit | unsigned |
s8 | 8-bit | signed |
s16 | 16-bit | signed |
s32 | 32-bit | signed |
bool8 | 8-bit | boolean |
bool16 | 16-bit | boolean |
bool32 | 32-bit | boolean |
Naming conventions
Follow the patterns already established in the codebase:- Functions and variables:
snake_case - Constants and macros:
UPPER_CASE - Files:
snake_casematching the existingsrc/andinclude/layout
Key macros from global.h
Several macros defined ininclude/global.h have matching implications — they reflect patterns GameFreak used that the compiler must reproduce exactly.
BLOCK_CROSS_JUMP
Array size macros
ARRAY_COUNT for new code. NELEMS is retained because it matches AgbAssert call sites in the original binary.
SWAP
stdlib or C99 compound-literal tricks.
Fixed-point math
The GBA lacks an FPU. GameFreak used Q-format fixed-point arithmetic throughout. The relevant macros are:Q_8_8_TO_INT, Q_4_12_TO_INT, Q_24_8_TO_INT) are also defined. Use the appropriate format to match the original code’s precision.
gametypes.h and save-compatible types
include/gametypes.h defines families of typedefs for fields that appear in save data. The naming convention encodes the underlying width so it is visible at a glance:
The smallest typedef in a family (e.g.
mapsec_u8_t) is the canonical type — the width at which the value is actually stored in flash memory. Larger variants exist only to describe places where the original code handled the value inconsistently.gametypes.h following this pattern, and use the smallest type that fits the full range of valid values.
Save data constraints
The vanilla game uses 96% of its available EEPROM. The remaining free space is not contiguous: Struct members that land in save blocks must be ordered to minimize compiler-inserted padding. Because field widths are visible in thegametypes.h typedef names, you can reason about padding without needing a sizeof check each time.
asm stubs
Functions not yet decompiled exist as assembly stubs underasm/. The goal of the project is to replace each stub with matching C code.
- Write the C implementation in the appropriate
src/file. - Remove the corresponding
.sstub (or the relevant label from it). - Run
make compareto confirm the output is still byte-identical.
UBFIX and BUGFIX
The codebase uses two annotation macros to mark intentional fixes:UBFIX— guards fixes for undefined behavior present in the original ROM. Enabled separately from a standard build.BUGFIX— guards fixes for observable gameplay bugs.
make compare still passes with the fix disabled.