Common issues and their solutions.
Build Issues
"OPENSNES not defined" or "No rule to make target 'common.mk'"
Cause: The Makefile can't find the OpenSNES installation.
Fix: Set OPENSNES in your Makefile or set the environment variable:
# Option 1: Set in Makefile (recommended for project portability)
OPENSNES := /path/to/opensnes
# Option 2: Set environment variable
export OPENSNES_HOME=/path/to/opensnes
Empty compiler/ directories or "cc65816 not found"
Cause: Submodules weren't cloned.
Fix:
cd /path/to/opensnes
git submodule update --init --recursive
make compiler
"wla-65816: command not found"
Cause: Compiler toolchain not built.
Fix:
cd /path/to/opensnes
make compiler
"clang: command not found" or "cc: command not found"
Cause: Missing host C compiler.
Fix (by platform):
# macOS
xcode-select --install
# Ubuntu/Debian
sudo apt install clang
# Fedora
sudo dnf install clang
# Windows MSYS2 (UCRT64)
pacman -S mingw-w64-ucrt-x86_64-clang
Build fails with "unhandled op: XXX"
Cause: The QBE backend doesn't support a specific operation (usually 32-bit or complex expressions).
Fix: Simplify your code:
result = (
a *
b +
c) / d;
result = temp / d;
static u16 b
Definition main.c:157
static u16 a
Definition main.c:157
static u16 c
Definition main.c:157
unsigned int u32
32-bit unsigned integer (0 to 4294967295)
Definition types.h:90
unsigned short u16
16-bit unsigned integer (0 to 65535)
Definition types.h:53
Static variables with initializers
Initialized static variables now work correctly:
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:47
The CopyInitData routine in crt0.asm copies initial values from ROM to RAM before main() is called.
Runtime Issues (Black Screen / Crash)
ROM loads but screen is black
This is the most common issue. Causes and fixes:
1. Missing setScreenOn()
void setScreenOn(void)
Enable screen display.
Definition console.h:115
2. No VBlank loop
while (1) {
}
void WaitForVBlank(void)
Wait for next VBlank period.
3. Console not initialized
void consoleInit(void)
Initialize SNES hardware.
4. Memory overlap (WRAM mirror bug)
Bank $00 addresses $0000-$1FFF mirror Bank $7E addresses $7E:0000-$1FFF. If you have variables in both ranges, they'll overwrite each other.
Diagnose:
python3 devtools/symmap/symmap.py --check-overlap game.sym
Fix: The library uses ORGA $0300 to avoid the overlap zone.
ROM runs but crashes after a few seconds
Causes:
- Stack overflow - Too many function calls or large local arrays
- DMA during active display - Only do DMA in VBlank
- Uninitialized pointers - C doesn't zero local variables
Debug:
- Use luna to see where it crashes (run_until_pc / state over MCP — see the debugging tutorial)
- Check the call stack depth
- Look for NULL pointer dereferences
Sprites don't appear
Checklist:
static const u8 sprite_pal[]
Sprite palette — single palette shared by player and target.
Definition main.c:124
void oamInit(u16 size, u16 name_base)
Initialize the sprite (OAM) system.
#define OAM_DEFAULT_SIZE
Default sprite size mode — small=8×8 / large=16×16.
Definition sprite.h:261
#define OAM_DEFAULT_TILE_BASE
Default sprite tile base — 0 = tiles at VRAM word $0000.
Definition sprite.h:263
void oamSet(u16 id, u16 x, u16 y, u16 tile, u16 palette, u16 priority, u16 flags)
Set sprite properties.
#define OBJ_SIZE8_L16
Sprite size indices (for oamInit, oamInitGfxSet).
Definition sprite.h:49
void oamInitGfxSet(const u8 *tileSource, u16 tileSize, const u8 *tilePalette, u16 paletteSize, u8 paletteEntry, u16 vramAddr, u8 oamSize)
Initialize sprite graphics and palette (PVSnesLib compatible).
u8 sprite_pal_end[]
Definition main.c:47
Controller input not working
Checklist:
Audio not playing
For basic audio:
void audioInit(void)
Initialize the audio system.
u8 audioPlaySample(u8 sampleId)
Play a sample with default settings.
u8 audioLoadSample(u8 id, const u8 *brrData, u16 size, u16 loopPoint)
Load a BRR sample into SPC700 RAM.
For SNESMOD:
while (1) {
}
void snesmodSetSoundbank(u8 bank)
Set the soundbank bank number.
void snesmodInit(void)
Initialize SNESMOD audio engine.
void snesmodPlay(u8 startPosition)
Start module playback.
void snesmodLoadModule(u16 moduleId)
Load a module from the soundbank.
void snesmodProcess(void)
Process audio commands.
Graphics Issues
Wrong colors / garbled palette
Cause: BGR555 format mismatch.
SNES uses BGR555 (5 bits blue, 5 green, 5 red), not RGB. Use gfx4snes to convert PNGs correctly:
gfx4snes -i sprite.png -o sprite
Tiles appear corrupted
Causes:
- Wrong tile format (2bpp vs 4bpp vs 8bpp)
- VRAM address overlap
- DMA transfer size mismatch
Check your graphics mode:
void bgInitTileSet(u8 bgNumber, const u8 *tileSource, const u8 *tilePalette, u8 paletteEntry, u16 tileSize, u16 paletteSize, u16 colorMode, u16 vramAddr)
Initialize tileset with tiles and palette.
#define BG_MODE1
Definition video.h:35
void setMode(u8 mode, u8 flags)
Set background mode.
Scrolling is jerky
Cause: Updating scroll registers outside VBlank.
Fix:
while (1) {
}
void bgSetScroll(u8 bg, u16 x, u16 y)
Set background scroll position.
Memory Issues
"ROM too large" or linker errors about size
Cause: Too much data in your ROM.
Check ROM size:
Solutions:
- Compress graphics
- Use smaller audio samples
- Split into multiple banks (advanced)
Variables have wrong values
Check symbol placement:
grep "my_variable" game.sym
- Good (RAM): 00:0234 my_variable
- Bad (ROM): 00:8234 my_variable
If it's in ROM ($8000+), you can't write to it.
Getting More Help
Diagnostic Commands
# Check for memory overlaps
python3 devtools/symmap/symmap.py --check-overlap game.sym
# View symbol map
cat game.sym | grep "variable_name"
# Inspect ROM header
xxd -s 0x7FC0 -l 32 game.sfc
# Check ROM size
ls -la game.sfc
Interactive debugging with luna
See the debugging tutorial for the full workflows (who-writes-this-variable, break-at-function, OAM shadow-vs-hardware, poke and observe). A third-party GUI debugger also works:
Using Mesen Debugger (alternative)
- Load ROM in Mesen
- Tools → Debugger
- Set breakpoint at your code
- Step through and watch registers/memory
Reporting Bugs
Include in your issue:
- OpenSNES version (git rev-parse HEAD)
- OS and compiler version
- Minimal code that reproduces the issue
- Error messages (full output)
- What you expected vs what happened