Loading...
Searching...
No Matches
Troubleshooting OpenSNES

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 <tt>compiler/</tt> 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:

// BAD: 32-bit operations are slow and may not work
u32 bigValue = x * 1000;
// GOOD: Use 16-bit when possible
u16 value = x * 10;
// BAD: Complex expressions in one line
result = (a * b + c) / d;
// GOOD: Break it down
u16 temp = a * b;
temp += c;
static u16 b
Definition main.c:157
static u16 bx
Definition main.c:159
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:66
unsigned short u16
16-bit unsigned integer (0 to 65535)
Definition types.h:52

Static variables with initializers

Initialized static variables now work correctly:

// Both work correctly:
static u8 counter = 0; // Initial value copied from ROM to RAM at startup
static u8 counter; // Zero-initialized by C standard
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:46

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()

// The screen is OFF by default. You must turn it on:
void setScreenOn(void)
Enable screen display.

2. No VBlank loop

// Without this, the CPU runs too fast and corrupts VRAM
while (1) {
WaitForVBlank(); // Required!
// Game logic here
}
void WaitForVBlank(void)
Wait for next VBlank period.

3. Console not initialized

// Always call consoleInit() first if using text/console functions
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:

  1. Stack overflow - Too many function calls or large local arrays
  2. DMA during active display - Only do DMA in VBlank
  3. Uninitialized pointers - C doesn't zero local variables

Debug:

  1. Use Mesen's debugger to see where it crashes
  2. Check the call stack depth
  3. Look for NULL pointer dereferences

Sprites don't appear

Checklist:

// 1. Initialize OAM
// 2. Load sprite graphics to VRAM
0, 0x0000, OBJ_SIZE8_L16);
// 3. Set sprite properties
oamSet(0, x, y, 3, 0, 0, 0, 0);
// 4. Make sure sprite is visible (not hidden)
// oamSetVisible(0, OBJ_SHOW); // If needed
// 5. Update OAM (usually done in VBlank by the library)
// The NMI handler calls oamUpdate() automatically
u8 sprite_pal_end[]
Definition main.c:46
u8 sprite_pal[]
Palette for the character sprite (16 colors, 4bpp)
void oamInitGfxSet(u8 *tileSource, u16 tileSize, u8 *tilePalette, u16 paletteSize, u8 paletteEntry, u16 vramAddr, u8 oamSize)
Initialize sprite graphics and palette (PVSnesLib compatible)
void oamSet(u16 id, u16 x, u16 y, u16 tile, u16 palette, u16 priority, u16 flags)
Set sprite properties.
void oamInit(u16 size, u16 tile_base)
Initialize the sprite (OAM) system.
#define OBJ_SIZE8_L16
Sprite size indices (for oamInit, oamInitGfxSet)
Definition sprite.h:49

Controller input not working

Checklist:

// Input is read automatically by the NMI handler every VBlank.
// Just call the query functions after WaitForVBlank:
u16 pressed = padPressed(0); // Just pressed this frame
u16 held = padHeld(0); // Currently held
u16 released = padReleased(0); // Just released this frame
// Check correct button bits
if (pressed & KEY_A) { } // A button (bit 7)
if (pressed & KEY_B) { } // B button (bit 15)
#define KEY_A
Definition input.h:81
#define KEY_B
Definition input.h:71
u16 padReleased(u8 pad)
Get buttons released this frame.
u16 padHeld(u8 pad)
Get buttons currently held.
u16 padPressed(u8 pad)
Get buttons pressed this frame.

Audio not playing

For basic audio:

void audioInit(void)
Initialize the audio system.
u8 audioPlaySample(u8 sampleId)
Play a sample with default settings.

For SNESMOD:

// MUST call every frame:
while (1) {
snesmodProcess(); // Required!
}
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:

  1. Wrong tile format (2bpp vs 4bpp vs 8bpp)
  2. VRAM address overlap
  3. DMA transfer size mismatch

Check your graphics mode:

// Mode 1: BG1/BG2 are 4bpp, BG3 is 2bpp
// Make sure you're loading the right format
bgInitTileSet(0, tiles_4bpp, ...); // 4bpp for BG0 in Mode 1
void bgInitTileSet(u8 bgNumber, u8 *tileSource, u8 *tilePalette, u8 paletteEntry, u16 tileSize, u16 paletteSize, u16 colorMode, u16 vramAddr)
Initialize tileset with tiles and palette.
#define BG_MODE1
Definition video.h:28
void setMode(u8 mode, u8 flags)
Set background mode.

Scrolling is jerky

Cause: Updating scroll registers outside VBlank.

Fix:

while (1) {
// Update scroll AFTER VBlank starts
// Game logic
}
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:

ls -la game.sfc

Solutions:

  1. Compress graphics
  2. Use smaller audio samples
  3. 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

Using Mesen Debugger

  1. Load ROM in Mesen
  2. Tools → Debugger
  3. Set breakpoint at your code
  4. Step through and watch registers/memory

Reporting Bugs

Include in your issue:

  1. OpenSNES version (git rev-parse HEAD)
  2. OS and compiler version
  3. Minimal code that reproduces the issue
  4. Error messages (full output)
  5. What you expected vs what happened