Loading...
Searching...
No Matches
Object Size -- All 6 SNES Sprite Size Modes
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/graphics/sprites/object_size

Then open object_size.sfc in your emulator (Mesen2 recommended).

How It Works

1. Set up text on BG1 (4bpp in Mode 1)

bgSetGfxPtr(0, 0x0000);
bgSetMapPtr(0, 0x3800, SC_32x32);
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 byte address — 32×32 tilemap at VRAM byte $7000.
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

REG_INIDISP = 0x80; /* Force blank -- unlimited VRAM write time */
REG_INIDISP = 0x0F; /* Restore display */
void dmaCopyVram(u8 *source, u16 vramAddr, u16 size)
Copy data to VRAM (PVSnesLib compatible)
u8 palette[]
Full 256-color palette for BG and sprite layers (512 bytes)
static u16 bx
Definition main.c:159
u8 sprite8[]
8x8 sprite tile data (defined in data.asm)
u8 sprite32[]
32x32 sprite tile data
#define REG_INIDISP
Display control (W)
Definition registers.h:49
void oamInitGfxSet(u8 *tileSource, u16 tileSize, u8 *tilePalette, u16 paletteSize, u8 paletteEntry, u16 vramAddr, u8 oamSize)
Initialize sprite graphics and palette (PVSnesLib compatible)
#define OBJ_SIZE8_L32
Definition sprite.h:50

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); /* Small sprite, left */
oamSet(1, 170, 120, 0x0050, 1, 3, 0); /* Large sprite, right */
void oamSetSize(u16 id, u16 large)
Set sprite size (large/small)
#define OBJ_LARGE
Definition sprite.h:104
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:103

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

Mode Small Large OBJSEL Constant
0 8x8 16x16 OBJ_SIZE8_L16
1 8x8 32x32 OBJ_SIZE8_L32
2 8x8 64x64 OBJ_SIZE8_L64
3 16x16 32x32 OBJ_SIZE16_L32
4 16x16 64x64 OBJ_SIZE16_L64
5 32x32 64x64 OBJ_SIZE32_L64

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