Screenshot
What This Example Shows
An interactive menu to switch between all 6 SNES sprite size combinations. The SNES OBJSEL register defines two sizes (small and large) that apply to all 128 sprites. Each sprite chooses small or large via its OAM high-table bit.
Controls
| Button | Action |
| D-PAD UP/DOWN | Select sprite size mode |
Build & Run
cd $OPENSNES_HOME
make -C examples/sprites/sprite_sizes
Then open sprite_sizes.sfc in luna (or any SNES emulator).
How It Works
1. Set up text on BG1 (4bpp in Mode 1)
void bgSetMapPtr(u8 bg, u16 vramAddr, u8 mapSize)
Set background tilemap address and size.
void bgSetGfxPtr(u8 bg, u16 vramAddr)
Set background tile graphics address.
#define SC_32x32
Definition background.h:36
void textLoadFont4bpp(u16 vram_addr)
Load font as 4bpp tiles for Mode 1 BGs.
#define TEXT_DEFAULT_FONT_TILE
Default first font tile (zero — font occupies tiles 0-95).
Definition text.h:44
#define TEXT_DEFAULT_PALETTE
Default palette slot (palette 0).
Definition text.h:46
void textInit(u16 tilemap_addr, u16 font_tile, u8 palette)
Initialize the text rendering system.
#define TEXT_DEFAULT_TILEMAP_ADDR
Default tilemap WORD address — 32×32 tilemap at VRAM word $3800.
Definition text.h:42
The built-in font is loaded as 4bpp tiles at VRAM $0000. The text system writes to a RAM buffer, then DMAs to the tilemap at $3800 during VBlank.
2. Load sprites with force blank
void dmaCopyVram(const u8 *source, u16 vramAddr, u16 size)
Copy data to VRAM (PVSnesLib compatible).
#define REG_INIDISP
Display control (W).
Definition registers.h:49
u8 sprite32[]
4bpp 32x32 sprite tile data (data.asm, in ROM)
#define OBJ_SIZE8_L32
Definition sprite.h:50
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 sprite8[]
8x8 sprite tile data (defined in data.asm)
Force blank is required because larger sprites (32x32, 64x64) exceed the ~4 KB VBlank DMA budget. Without it, VRAM writes are silently dropped by the PPU.
3. Two sprites per mode
oamSet(0, 70, 120, 0x0010, 0, 3, 0);
oamSet(1, 170, 120, 0x0050, 1, 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.
#define OBJ_SMALL
Sprite size selection.
Definition sprite.h:99
Tile numbers are offsets from the OBJSEL name base ($4000):
- Small at VRAM $4100: tile = ($4100 - $4000) / 16 = $10
- Large at VRAM $4500: tile = ($4500 - $4000) / 16 = $50
The 6 Size Modes
Most SNES games use mode 3 (OBJ_SIZE16_L32) – 16x16 for characters and 32x32 for bosses/effects. Mode 0 (OBJ_SIZE8_L16) is common for simple games with small sprites.
SNES Concepts
OBJSEL Register ($2101)
This register sets the global size pair and name base address. Changing it affects ALL 128 sprites simultaneously, so games typically pick one mode at startup and stick with it for the entire game. The size pair defines what "small" and "large" mean – individual sprites select between the two via oamSetSize().
OAM High Table
Each of the 128 sprites has 2 extra bits stored in 32 bytes of high table:
- Bit 0: X position bit 8 (extends the 8-bit X to 9 bits, allowing off-screen positions)
- Bit 1: Size select (0 = small, 1 = large)
These bits are packed 4 sprites per byte. The oamSetSize() function writes the size bit; the X high bit is set automatically by oamSet() / oamSetX() when needed.
Force Blank for Large DMA
The SNES PPU silently ignores VRAM writes during active display (scanlines 0-223). A 64x64 sprite is 8 KB of tile data – far too much for VBlank DMA. Use REG_INIDISP = 0x80 to disable rendering, transfer the data, then restore with REG_INIDISP = 0x0F.
VRAM Layout
| Address | Content | Size |
| $0000 | Font tiles (4bpp) | ~3 KB |
| $3800 | BG1 tilemap (text) | 2048 bytes |
| $4000 | Sprite name base | – |
| $4100 | Small sprite tiles | varies |
| $4500 | Large sprite tiles | varies |
Project Structure
| File | Purpose |
| main.c | Menu UI, size mode switching, sprite loading |
| data.asm | Four sprite sizes (8/16/32/64) with palettes via .INCBIN |
| res/ | Source PNGs for each sprite size |
| Makefile | LIB_MODULES := console sprite dma text text4bpp input background |
Going Further
- Size comparison: Modify the code to display all 6 small/large pairs on screen simultaneously (using sprites 0-11). This gives a visual reference chart.
- Runtime size switching: In a real game, you might switch OBJSEL between scenes – 8/16 for overworld, 16/32 for battles.
- Explore related examples:
- sprites/simple_sprite – Basic static sprite setup
- sprites/animated_sprite – Frame-based sprite animation
- sprites/dynamic_sprite – VRAM streaming for many animation frames