SNES Sprite (Object) Management. More...
#include <snes/types.h>Go to the source code of this file.
Classes | |
| struct | MetaspriteItem |
| Metasprite item structure (PVSnesLib compatible). More... | |
| struct | MetaspriteStyle |
| What stays the same from one frame of a metasprite to the next. More... | |
| struct | OamDynamicConfig |
| Configuration for the dynamic sprite engine. More... | |
| struct | OamWorldBatch |
| A batch of sprites in world coordinates, for oamPlaceWorld(). More... | |
| struct | t_sprites |
| Dynamic sprite state structure (16 bytes, PVSnesLib compatible). More... | |
Macros | |
| #define | MAX_SPRITES 128 |
| Maximum number of hardware sprites. | |
| #define | MAXSPRTRF (7 * 6) |
| Maximum sprite transfers per frame (7 sprites * 6 bytes each). | |
| #define | METASPR_ITEM(dx, dy, tile, attr) |
| Metasprite item macro (PVSnesLib compatible). | |
| #define | METASPR_TERM { -128, 0, 0, 0, 0 } |
| End marker for metasprite data (dx = -128). | |
| #define | metasprite_end (-128) |
| Legacy end marker value. | |
| #define | OAM_ATTR(_tile, _pal, _prio, _fl) |
| Pre-compute OAM attribute byte (vhoopppc). | |
| #define | OAM_BUFFER_SIZE 544 |
| Total OAM buffer size in bytes (512 main + 32 extension). | |
| #define | OAM_DEFAULT_SIZE OBJ_SIZE8_L16 |
| Default sprite size mode — small=8×8 / large=16×16. | |
| #define | OAM_DEFAULT_TILE_BASE 0 |
| Default sprite tile base — 0 = tiles at VRAM word $0000. | |
| #define | OAM_EXT_OFFSET 512 |
| OAM extension table offset in bytes (starts after 128*4 main entries). | |
| #define | OAM_EXT_SIZE 32 |
| OAM extension table size in bytes (2 bits per sprite, 128 sprites). | |
| #define | OAM_SET_GFX(id, gfx) |
| Set sprite graphics address — any bank. | |
| #define | OAM_XHI_MASK(_slot) |
| X-high-bit mask for a sprite slot (0-3 within a high-table byte). | |
| #define | OAM_Y_OFFSCREEN 0xE0 |
| Y coordinate that hides a sprite below the NTSC visible area. | |
| #define | oamSetFast(_id, _x, _y, _tile, _pal, _prio, _fl) |
| Set sprite properties with zero function-call overhead. | |
| #define | oamSetXYFast(_id, _x, _y) |
| Update sprite position only (most common per-frame operation). | |
| #define | OBJ_BASE(vram_addr) |
| Convert VRAM word address to OBJSEL name base bits (0-2). | |
| #define | OBJ_CGRAM_BASE 128 |
| CGRAM base offset for sprite palettes (sprites use colors 128-255). | |
| #define | OBJ_CGRAM_PAL(n) |
| CGRAM color index for sprite palette n (0-7). | |
| #define | OBJ_FLIPX 0x40 |
| Metasprite horizontal flip flag. | |
| #define | OBJ_FLIPY 0x80 |
| Metasprite vertical flip flag. | |
| #define | OBJ_HIDE_Y 240 |
| Y position to hide sprite. | |
| #define | OBJ_LARGE 1 |
| #define | OBJ_NAME_BASE(vram_word_addr) |
| Convert a VRAM word address into an OBJ name base (0-7). | |
| #define | OBJ_NAMETABLE_HIGH 0x01 |
| OAM attribute bit 0: second name table select (tile numbers 256+). | |
| #define | OBJ_PAL(pal) |
| Metasprite palette attribute macro. | |
| #define | OBJ_PRIO(prio) |
| Metasprite priority attribute macro. | |
| #define | OBJ_QUEUELIST_SIZE 128 |
| Maximum sprites in VRAM upload queue. | |
| #define | OBJ_SIZE16_L32 3 |
| #define | OBJ_SIZE16_L64 4 |
| #define | OBJ_SIZE32_L64 5 |
| #define | OBJ_SIZE8_L16 0 |
| Sprite size indices (for oamInit, oamInitGfxSet). | |
| #define | OBJ_SIZE8_L32 1 |
| #define | OBJ_SIZE8_L64 2 |
| #define | OBJ_SIZE_TO_REG(size) |
| Macro to convert size index to OBJSEL register value. | |
| #define | OBJ_SMALL 0 |
| Sprite size selection. | |
| #define | OBJ_SPRITE16 2 |
| #define | OBJ_SPRITE32 1 |
| Sprite type identifiers for dynamic engine. | |
| #define | OBJ_SPRITE8 4 |
| #define | OBJSEL(size, vram_addr) |
| Build OBJSEL register value from size constant + VRAM base address. | |
| #define | PALETTE_16_SIZE 32 |
| Size of one 16-color palette in bytes (16 colors x 2 bytes per BGR555). | |
Typedefs | |
| typedef MetaspriteItem | t_metasprite |
| PVSnesLib compatibility typedef. | |
Functions | |
| void | oamClear (void) |
| Clear all sprites. | |
| u16 | oamDrawMetasprite (u16 startId, s16 x, s16 y, const MetaspriteItem *frame, const MetaspriteStyle *style, u8 flip) |
| Draw one frame of a metasprite. | |
| void | oamDynamicDrainQueue (void) |
| Block until the dynamic-sprite VRAM tile queue is empty. | |
| void | oamDynamicDraw (u16 id) |
| Draw a dynamic sprite — engine picks the size routine. | |
| void | oamDynamicInit (const OamDynamicConfig *cfg) |
| Initialize the dynamic sprite engine from a config struct. | |
| void | oamDynamicSetSize (u16 id, u8 size) |
| Override the dispatched pixel size for a dynamic sprite slot. | |
| u16 | oamGetX (u16 id) |
| Read back a sprite's X position. | |
| u8 | oamGetY (u16 id) |
| Read back a sprite's Y position. | |
| void | oamHide (u16 id) |
| Park a sprite off screen. | |
| void | oamInit (u16 size, u16 name_base) |
| Initialize the sprite (OAM) system. | |
| 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). | |
| void | oamMetaDrawDyn (u16 id, s16 x, s16 y, const MetaspriteItem *meta, const u8 *gfxptr, u8 size_class) |
| Draw a dynamic metasprite — engine picks the size routine. | |
| void | oamPlaceWorld (const OamWorldBatch *batch, u16 cam_x, u16 cam_y) |
| Place a batch of world-space sprites: camera, culling, OAM, in one call. | |
| void | oamSet (u16 id, u16 x, u16 y, u16 tile, u16 palette, u16 priority, u16 flags) |
| Set sprite properties. | |
| void | oamSetSize (u16 id, u16 large) |
| Set sprite size (large/small). | |
| void | oamSetTile (u16 id, u16 tile) |
| Set sprite tile. | |
| void | oamSetX (u16 id, u16 x) |
| Set sprite X position. | |
| void | oamSetXY (u16 id, u16 x, u16 y) |
| Set sprite position. | |
| void | oamSetY (u16 id, u16 y) |
| Set sprite Y position. | |
| void | oamUpdate (void) |
| Copy OAM buffer to hardware. | |
Variables | |
| t_sprites | oambuffer [128] |
| Dynamic sprite buffer (128 entries, 2048 bytes). | |
SNES Sprite (Object) Management.
Functions for managing hardware sprites (OBJ).
Based on: PVSnesLib sprite system by Alekmaul
| #define MAX_SPRITES 128 |
Maximum number of hardware sprites.
| #define MAXSPRTRF (7 * 6) |
Maximum sprite transfers per frame (7 sprites * 6 bytes each).
| #define METASPR_ITEM | ( | dx, | |
| dy, | |||
| tile, | |||
| attr ) |
Metasprite item macro (PVSnesLib compatible).
| #define METASPR_TERM { -128, 0, 0, 0, 0 } |
End marker for metasprite data (dx = -128).
| #define metasprite_end (-128) |
Legacy end marker value.
| #define OAM_ATTR | ( | _tile, | |
| _pal, | |||
| _prio, | |||
| _fl ) |
Pre-compute OAM attribute byte (vhoopppc).
| _tile | Tile number (0-511, only bit 8 used) |
| _pal | Palette (0-7) |
| _prio | Priority (0-3) |
| _fl | Flip flags (bit 6 = H flip, bit 7 = V flip) |
| #define OAM_BUFFER_SIZE 544 |
Total OAM buffer size in bytes (512 main + 32 extension).
| #define OAM_DEFAULT_SIZE OBJ_SIZE8_L16 |
Default sprite size mode — small=8×8 / large=16×16.
Same value as OBJ_SIZE8_L16 (the most common configuration: 8×8 tiles for HUD elements and 16×16 for characters/projectiles).
| #define OAM_DEFAULT_TILE_BASE 0 |
Default sprite tile base — 0 = tiles at VRAM word $0000.
| #define OAM_EXT_OFFSET 512 |
OAM extension table offset in bytes (starts after 128*4 main entries).
| #define OAM_EXT_SIZE 32 |
OAM extension table size in bytes (2 bits per sprite, 128 sprites).
| #define OAM_SET_GFX | ( | id, | |
| gfx ) |
Set sprite graphics address — any bank.
Stores the address AND the bank byte of gfx: a C pointer is a far pointer, so the macro reads the bank from it. Until 2026-09-20 it wrote bank 0 (a pre-A6 leftover: "cc65816 passes 16-bit pointers"), which only worked while the linker happened to put the tiles in bank $00 — and read the wrong bank, silently, for tiles declared with ASSET_SECTION.
| id | Sprite index (0-127) |
| gfx | Pointer to graphics data (any bank) |
| #define OAM_XHI_MASK | ( | _slot | ) |
X-high-bit mask for a sprite slot (0-3 within a high-table byte).
Each high-table byte covers 4 sprites. Bit 0 of each 2-bit pair is the X high bit. This macro returns the mask for the X-high bit of the given slot.
| #define OAM_Y_OFFSCREEN 0xE0 |
Y coordinate that hides a sprite below the NTSC visible area.
| #define oamSetFast | ( | _id, | |
| _x, | |||
| _y, | |||
| _tile, | |||
| _pal, | |||
| _prio, | |||
| _fl ) |
Set sprite properties with zero function-call overhead.
Drop-in replacement for oamSet() that writes directly to oamMemory[]. Same parameters, same behavior, but compiles to direct memory writes instead of a function call with framesize=158.
| _id | Sprite ID (0-127) |
| _x | X position (0-511) |
| _y | Y position (0-255) |
| _tile | Tile number (0-511) |
| _pal | Palette (0-7) |
| _prio | Priority (0-3) |
| _fl | Flip flags (bit 6 = H flip, bit 7 = V flip) |
| #define oamSetXYFast | ( | _id, | |
| _x, | |||
| _y ) |
Update sprite position only (most common per-frame operation).
Fastest possible sprite update — only writes X, Y, and X high bit. Use when tile/palette/priority/flags don't change between frames.
| _id | Sprite ID (0-127) |
| _x | X position (0-511) |
| _y | Y position (0-255) |
| #define OBJ_BASE | ( | vram_addr | ) |
Convert VRAM word address to OBJSEL name base bits (0-2).
| #define OBJ_CGRAM_BASE 128 |
CGRAM base offset for sprite palettes (sprites use colors 128-255).
| #define OBJ_CGRAM_PAL | ( | n | ) |
CGRAM color index for sprite palette n (0-7).
| #define OBJ_FLIPX 0x40 |
Metasprite horizontal flip flag.
| #define OBJ_FLIPY 0x80 |
Metasprite vertical flip flag.
| #define OBJ_HIDE_Y 240 |
Y position to hide sprite.
| #define OBJ_LARGE 1 |
Use large sprite size
| #define OBJ_NAME_BASE | ( | vram_word_addr | ) |
Convert a VRAM word address into an OBJ name base (0-7).
OBJSEL stores the sprite tile area as a 3-bit page number, not an address: page N means word address N << 13. Passing a VRAM address to oamInit() directly is therefore wrong — it is masked to 3 bits, so 0x6000 becomes base 0 and the sprites render whatever tiles happen to live at word 0 (usually the background's). Nothing reports it.
Use this macro and the intent survives the call:
| vram_word_addr | VRAM word address, a multiple of $2000 |
| #define OBJ_NAMETABLE_HIGH 0x01 |
OAM attribute bit 0: second name table select (tile numbers 256+).
| #define OBJ_PAL | ( | pal | ) |
| #define OBJ_PRIO | ( | prio | ) |
Metasprite priority attribute macro.
| #define OBJ_QUEUELIST_SIZE 128 |
Maximum sprites in VRAM upload queue.
| #define OBJ_SIZE16_L32 3 |
Small=16x16, Large=32x32
| #define OBJ_SIZE16_L64 4 |
Small=16x16, Large=64x64
| #define OBJ_SIZE32_L64 5 |
Small=32x32, Large=64x64
| #define OBJ_SIZE8_L16 0 |
Sprite size indices (for oamInit, oamInitGfxSet).
Small=8x8, Large=16x16
| #define OBJ_SIZE8_L32 1 |
Small=8x8, Large=32x32
| #define OBJ_SIZE8_L64 2 |
Small=8x8, Large=64x64
| #define OBJ_SIZE_TO_REG | ( | size | ) |
Macro to convert size index to OBJSEL register value.
| #define OBJ_SMALL 0 |
Sprite size selection.
Use small sprite size
| #define OBJ_SPRITE16 2 |
16x16 sprite identifier
| #define OBJ_SPRITE32 1 |
Sprite type identifiers for dynamic engine.
32x32 sprite identifier
| #define OBJ_SPRITE8 4 |
8x8 sprite identifier
| #define OBJSEL | ( | size, | |
| vram_addr ) |
Build OBJSEL register value from size constant + VRAM base address.
| size | One of OBJ_SIZE8_L16 .. OBJ_SIZE32_L64 |
| vram_addr | VRAM word address for sprite tiles (must be 8KB-aligned) |
| #define PALETTE_16_SIZE 32 |
Size of one 16-color palette in bytes (16 colors x 2 bytes per BGR555).
| typedef MetaspriteItem t_metasprite |
PVSnesLib compatibility typedef.
| void oamClear | ( | void | ) |
Clear all sprites.
Hides all sprites by moving them off-screen.
| u16 oamDrawMetasprite | ( | u16 | startId, |
| s16 | x, | ||
| s16 | y, | ||
| const MetaspriteItem * | frame, | ||
| const MetaspriteStyle * | style, | ||
| u8 | flip ) |
Draw one frame of a metasprite.
Draws a multi-tile sprite composed of multiple hardware sprites. The frame is an array of MetaspriteItem terminated by METASPR_TERM — what animTickMeta() returns, or a table of your own.
| startId | First sprite ID to use (0-127) |
| x | X position of the metasprite origin |
| y | Y position of the metasprite origin |
| frame | The frame to draw (MetaspriteItem[]) |
| style | Base tile, palette, size and extent (see MetaspriteStyle) |
| flip | 0, or OBJ_FLIPX and / or OBJ_FLIPY to mirror the whole metasprite inside its width x height box. Each piece is moved to the mirrored place and its own flip bit toggled. |
| void oamDynamicDrainQueue | ( | void | ) |
Block until the dynamic-sprite VRAM tile queue is empty.
Called once during init, after queueing the starting frame via oamDynamicDraw / oamMetaDrawDyn, and before setScreenOn. The NMI auto-flush hook drains up to 7 queue entries per VBlank, so init sequences that enqueue more than that (typical for metasprites with many sub-sprites) need several VBlanks to complete. This helper loops WaitForVBlank() until the queue is empty and tells the NMI hook to skip the end-of-frame "hide stale sprites" step during the drain so the just-drawn sprites are not pushed off-screen between waits.
Safe to call only while the screen is in force blank — VRAM writes during active display are silently dropped by the PPU.
| void oamDynamicDraw | ( | u16 | id | ) |
Draw a dynamic sprite — engine picks the size routine.
Preferred entry point — replaces the manual choice between oamDynamic8Draw / oamDynamic16Draw / oamDynamic32Draw. The engine uses the per-sprite size set via oamDynamicSetSize if any; otherwise it falls back to the "large" pixel size of the size pair selected at init by oamInitDynamicSprite (or oamDynamicInit).
64x64 dynamic streaming is not currently supported; calls that would resolve to 64x64 are silently skipped.
| id | Index into oambuffer array (0-127) |
| void oamDynamicInit | ( | const OamDynamicConfig * | cfg | ) |
Initialize the dynamic sprite engine from a config struct.
Preferred over oamInitDynamicSprite — the struct-based form makes the intent of each parameter visible at the call site and survives future additions without a signature break.
| cfg | Configuration (caller-owned; the engine reads it once). |
Override the dispatched pixel size for a dynamic sprite slot.
Optional companion to oamDynamicDraw. By default each slot dispatches to the "large" half of the size pair set at init. Call this to make a specific slot use a different pixel size (8, 16, or 32) — typical when mixing small and large dynamic sprites under the same mode pair.
Pass 0 to clear the override and revert to the mode default.
| id | Sprite slot id (0-127) |
| size | Pixel size: 8, 16, or 32 (or 0 to clear) |
Read back a sprite's X position.
What oamSet() / oamSetX() / oamSetXY() last stored, from the OAM shadow: 0-511, the ninth bit included (a value of 256 or more is a sprite partly or wholly off the left edge, as written with a negative X).
| id | Sprite ID (0-127) |
Read back a sprite's Y position.
The y that was given to oamSet() / oamSetY() / oamSetXY(), which is the OAM byte.
| id | Sprite ID (0-127) |
| void oamHide | ( | u16 | id | ) |
Park a sprite off screen.
Use this for anything outside the camera. OAM X is 9 bits and Y is 8: a sprite drawn at a position beyond the screen does not disappear, its coordinates WRAP and it reappears somewhere plausible. In a scrolling world that reads as an entity standing where no entity is — a villager inside a wall — which you find by looking at the screen, not by reading the code.
Sets Y to OBJ_HIDE_Y and X's high bit, because Y=240 alone still wraps for sprites taller than 16 px. That is why this is a function and not a oamSetXY(id, 0, 240) you write yourself.
| id | Sprite ID (0-127) |
Initialize the sprite (OAM) system.
Sets the OBJSEL register (sprite size mode + tile base) and clears the OAM shadow buffer so all sprites start hidden. Must be called before any other oam* function. Replaces the v1 oamInit/oamInitEx pair — pass OAM_DEFAULT_* constants for the previous oamInit(void) defaults.
| size | Sprite size mode (OBJ_SIZE_*; use OAM_DEFAULT_SIZE for the standard 8×8/16×16 layout) |
| name_base | OBJ name base, a PAGE NUMBER 0-7 — not a VRAM address. Each page is $2000 WORD addresses (16 KB), so tiles DMA'd to word $4000 need base 2. Prefer OBJ_NAME_BASE(addr) over computing it by hand; use OAM_DEFAULT_TILE_BASE for tiles at VRAM $0000. |
| 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).
Loads sprite tiles to VRAM and palette to CGRAM, and configures the sprite tile base address and sizes.
| tileSource | Address of sprite tile graphics |
| tileSize | Size of tile data in bytes |
| tilePalette | Address of sprite palette data |
| paletteSize | Size of palette data in bytes |
| paletteEntry | Palette number (0-7, placed at color 128+entry*16) |
| vramAddr | VRAM address for tiles (must be 8KB aligned) |
| oamSize | Sprite size configuration (OBJ_SIZE_*) |
| void oamMetaDrawDyn | ( | u16 | id, |
| s16 | x, | ||
| s16 | y, | ||
| const MetaspriteItem * | meta, | ||
| const u8 * | gfxptr, | ||
| u8 | size_class ) |
Draw a dynamic metasprite — engine picks the size routine.
Iterates meta (a MetaspriteItem array terminated by METASPR_TERM), sets up oambuffer[id..] for each sub-sprite, and dispatches to the matching oamDynamic{8,16,32}Draw based on the engine's current size pair (set at init via oamDynamicInit) and the caller-supplied size_class:
Replaces the legacy trio oamMetaDrawDyn{8,16,32}. The pixel size is resolved from the engine state, so callers no longer pick a function by sprite size — they just say which half of the pair to use.
| id | Starting oambuffer index (each sub-sprite uses id, id+1, ...). |
| x,y | Metasprite origin in screen coordinates. |
| meta | MetaspriteItem array, terminated by METASPR_TERM. |
| gfxptr | ROM source for the dynamic tile data (bank $00 — cc65816 passes 16-bit pointers only). |
| size_class | OBJ_SMALL (0) or OBJ_LARGE (1). |
| void oamPlaceWorld | ( | const OamWorldBatch * | batch, |
| u16 | cam_x, | ||
| u16 | cam_y ) |
Place a batch of world-space sprites: camera, culling, OAM, in one call.
For each sprite of the batch: screen position = world position - camera. A sprite that is on screen, even partly, gets its x, y, tile, attribute and ninth x bit written; any other is hidden the way oamHide() hides. The loop a scrolling game runs every frame, in assembly: in compiled C it cost a real project about 6,000 master cycles a sprite (issue #165).
| batch | The sprites (see OamWorldBatch) |
| cam_x | World x of the screen's left edge |
| cam_y | World y of the screen's first line |
Set sprite properties.
| id | Sprite ID (0-127) |
| x | X position (0-511, negative wraps) |
| y | Y position (0-255, use OBJ_HIDE_Y to hide): the picture line of the sprite's top row, 0 being the first visible line — the same y as bgSetScroll()'s, so a sprite and the background it stands on take the same camera value. (Until 2026-10-09 the library stored y - 1 and every sprite was drawn one line too high.) |
| tile | Tile number (0-511) |
| palette | Palette (0-7) |
| priority | Priority (0-3, 3=highest) |
| flags | Flip flags (bit 6 = H flip, bit 7 = V flip) |
Set sprite size (large/small).
| id | Sprite ID (0-127) |
| large | TRUE for large size, FALSE for small |
Set sprite tile.
| id | Sprite ID (0-127) |
| tile | Tile number (0-511) |
Set sprite position.
| id | Sprite ID (0-127) |
| x | X position |
| y | Y position |
| void oamUpdate | ( | void | ) |
|
extern |
Dynamic sprite buffer (128 entries, 2048 bytes).
Game-level sprite state for the dynamic sprite engine. Each entry tracks a sprite's position, animation frame, and graphics pointer. Separate from oamMemory (hardware OAM buffer at $0300-$051F).