Screenshot
What This Example Shows
How to display a single 32x32 pixel sprite on screen using the SNES Object Attribute Memory (OAM). This is the simplest possible sprite program – no animation, no input, just one sprite at the center of the screen.
Prerequisites
Read text/print_string first – it covers consoleInit(), VRAM, and the PPU basics.
Controls
No interactive controls. A static sprite is displayed at the center of the screen.
Build & Run
cd $OPENSNES_HOME
make -C examples/sprites/simple_sprite
Then open simple_sprite.sfc in your emulator (Mesen2 recommended).
How It Works
1. Load sprite graphics into VRAM
void dmaCopyVram(const u8 *source, u16 vramAddr, u16 size)
Copy data to VRAM (PVSnesLib compatible).
u8 sprite32_end[]
Definition main.c:32
u8 sprite32[]
4bpp 32x32 sprite tile data (data.asm, in ROM)
Sprite tiles go to a different VRAM region than background tiles. Here we load them at word address $2100. The SNES has a shared 64 KB VRAM – backgrounds and sprites coexist by using non-overlapping address ranges.
2. Load the sprite palette
void dmaCopyCGram(const u8 *source, u16 startColor, u16 size)
Copy palette data to CGRAM (PVSnesLib compatible).
u8 palsprite32[]
16-colour palette for the sprite
CGRAM (Color Generator RAM) holds 256 entries. The first 128 are for backgrounds, the last 128 are for sprites. Offset 128 = first sprite palette (palette 0 in sprite terms). Each palette is 16 colors (32 bytes in 15-bit SNES format).
3. Configure OBJ size
void oamInit(u16 size, u16 name_base)
Initialize the sprite (OAM) system.
#define OBJ_SIZE8_L32
Definition sprite.h:50
The SNES supports two sprite sizes simultaneously: "small" and "large". Here we use 8x8 small / 32x32 large. The second parameter (1) sets the name base – the VRAM region where sprite tiles start ($2000 in word addressing).
4. Place the sprite
oamSet(0, 112, 96, 0x0010, 0, 3, 0);
void oamSetSize(u16 id, u16 large)
Set sprite size (large/small).
#define OBJ_LARGE
Definition sprite.h:100
void oamSet(u16 id, u16 x, u16 y, u16 tile, u16 palette, u16 priority, u16 flags)
Set sprite properties.
oamSet() configures OAM entry 0:
- Position: (112, 96) – roughly centered on the 256x224 screen
- Tile number: 0x0010 – calculated as (0x2100 - 0x2000) / 16
- Palette: 0 (first sprite palette)
- Priority: 3 (in front of all backgrounds)
oamSetSize() selects the "large" size (32x32) for this sprite. Visibility is controlled by Y position; oamSet placed it on-screen.
5. Enable display
void setScreenOn(void)
Enable screen display.
Definition console.h:115
#define BG_MODE1
Definition video.h:35
#define LAYER_OBJ
Definition video.h:111
#define setMainScreen(layers)
Enable layers on the main screen.
Definition video.h:132
void setMode(u8 mode, u8 flags)
Set background mode.
Mode 1 is used but we only enable sprites (LAYER_OBJ) on the main screen – no backgrounds needed for this demo.
SNES Concepts
OAM (Object Attribute Memory)
The SNES has 128 sprite entries in OAM, each with:
- X/Y position (9-bit X, 8-bit Y)
- Tile number (9 bits – selects which VRAM tiles to display)
- Palette (3 bits – selects from 8 sprite palettes)
- Priority (2 bits – controls layering with backgrounds)
- Horizontal/vertical flip flags
Plus 32 bytes of "high table" storing the X position MSB and size-select bit for each sprite (2 bits per sprite, packed 4 per byte).
CGRAM Split
Colors 0-127 are for backgrounds, colors 128-255 are for sprites. Sprite palette 0 starts at CGRAM offset 128, palette 1 at 144, and so on. Each palette holds 16 colors (32 bytes).
Name Base
The VRAM word address where the PPU starts looking for sprite tiles. Set via oamInit() or register $2101 (OBJSEL). The tile number in each OAM entry is an offset from this base.
Project Structure
| File | Purpose |
| main.c | Sprite loading, OAM setup, display configuration |
| data.asm | Sprite tile data and palette via .INCBIN |
| res/sprite32.png | Source 32x32 sprite image |
| Makefile | LIB_MODULES := console dma sprite |
Going Further
- Move the sprite: Add padHeld() input reading and update the X/Y position in the main loop with oamSet().
- Change the palette: Write different colors to CGRAM offset 128 and see the sprite change appearance without reloading tiles.
- Explore related examples:
- sprites/animated_sprite – Add movement and animation frames
- sprites/dynamic_sprite – Stream sprite tiles to VRAM each frame