OpenSNES
Modern Open-Source SNES Development SDK
Loading...
Searching...
No Matches
sprite.h
Go to the documentation of this file.
1
35
36#ifndef OPENSNES_SPRITE_H
37#define OPENSNES_SPRITE_H
38
39#include <snes/types.h>
40
41/* Removed on 2026-10-05 (1.0 plan, lot C): oamDrawMeta, oamDrawMetaFlip.
42 * The replacements are in docs/UPGRADING.md; `make check-upgrade` names them. */
43
44/*============================================================================
45 * Constants
46 *============================================================================*/
47
49#define MAX_SPRITES 128
50
52#define OBJ_SIZE8_L16 0
53#define OBJ_SIZE8_L32 1
54#define OBJ_SIZE8_L64 2
55#define OBJ_SIZE16_L32 3
56#define OBJ_SIZE16_L64 4
57#define OBJ_SIZE32_L64 5
58
60#define OBJ_SIZE_TO_REG(size) ((size) << 5)
61
63#define OBJ_BASE(vram_addr) (((vram_addr) >> 13) & 0x07)
64
75#define OBJSEL(size, vram_addr) ((u8)(OBJ_SIZE_TO_REG(size) | OBJ_BASE(vram_addr)))
76
78#define OBJ_CGRAM_BASE 128
79
81#define OBJ_CGRAM_PAL(n) (OBJ_CGRAM_BASE + (n) * 16)
82
84#define PALETTE_16_SIZE 32
85
87#define OAM_Y_OFFSCREEN 0xE0
88
90#define OAM_EXT_OFFSET 512
91
93#define OAM_EXT_SIZE 32
94
96#define OAM_BUFFER_SIZE 544
97
99#define OBJ_HIDE_Y 240
100
102#define OBJ_SMALL 0
103#define OBJ_LARGE 1
104
105/*============================================================================
106 * Dynamic Sprite Engine Constants
107 *============================================================================*/
108
110#define OBJ_SPRITE32 1
111#define OBJ_SPRITE16 2
112#define OBJ_SPRITE8 4
113
115#define OBJ_QUEUELIST_SIZE 128
116
118#define MAXSPRTRF (7 * 6)
119
120/*============================================================================
121 * Dynamic Sprite Structure
122 *============================================================================*/
123
155
156/*============================================================================
157 * Compile-time struct layout assertions
158 * Must match the oambuffer layout in sprite_dynamic.asm exactly.
159 *============================================================================*/
160
161_Static_assert(sizeof(t_sprites) == 16, "t_sprites must be 16 bytes");
162_Static_assert(__builtin_offsetof(t_sprites, oamx) == 0, "oamx offset mismatch");
163_Static_assert(__builtin_offsetof(t_sprites, oamy) == 2, "oamy offset mismatch");
164_Static_assert(__builtin_offsetof(t_sprites, oamframeid) == 4, "oamframeid offset mismatch");
165_Static_assert(__builtin_offsetof(t_sprites, oamattribute) == 6, "oamattribute offset mismatch");
166_Static_assert(__builtin_offsetof(t_sprites, oamrefresh) == 7, "oamrefresh offset mismatch");
167_Static_assert(__builtin_offsetof(t_sprites, oamgfxaddr) == 8, "oamgfxaddr offset mismatch");
168_Static_assert(__builtin_offsetof(t_sprites, oamgfxbank) == 10, "oamgfxbank offset mismatch");
169
182#define OAM_SET_GFX(id, gfx) do { \
183 oambuffer[id].oamgfxaddr = (u16)(u32)(const void *)(gfx); \
184 oambuffer[id].oamgfxbank = (u8)((u32)(const void *)(gfx) >> 16); \
185} while(0)
186
187/* OAM_SET_GFX_BANK(id, gfx, bank) was removed on 2026-10-05: OAM_SET_GFX() reads
188 * the bank from its pointer (docs/UPGRADING.md). */
189
190/* --- Bank $00 SLOT 1 (C-accessible, < $2000) --- */
191
199extern t_sprites oambuffer[128];
200
201/*============================================================================
202 * Initialization
203 *============================================================================*/
204
211#define OAM_DEFAULT_SIZE OBJ_SIZE8_L16
213#define OAM_DEFAULT_TILE_BASE 0
214
239void oamInit(u16 size, u16 name_base);
240
257#define OBJ_NAME_BASE(vram_word_addr) (((vram_word_addr) >> 13) & 0x07)
258
280void oamInitGfxSet(const u8 *tileSource, u16 tileSize, const u8 *tilePalette,
281 u16 paletteSize, u8 paletteEntry, u16 vramAddr, u8 oamSize);
282
283/*============================================================================
284 * Sprite Properties
285 *============================================================================*/
286
287/* Sprite ids are u16 in every function below. The setters other than oamSet()
288 * took a u8 until 2026-09-21: an id of 256 was truncated to 0 BEFORE the
289 * `id >= 128` check could refuse it, and silently overwrote sprite 0. Same
290 * stack slot either way — no ABI change, and a u8 variable still converts. */
291
322void oamSet(u16 id, u16 x, u16 y, u16 tile, u16 palette, u16 priority, u16 flags);
323
330void oamSetX(u16 id, u16 x);
331
338void oamSetY(u16 id, u16 y);
339
347void oamSetXY(u16 id, u16 x, u16 y);
348
361
373
381typedef struct {
382 const s16 *x;
383 const s16 *y;
384 const u8 *tile;
385 const u8 *attr;
390 const u8 *order;
392
393/* Not under the host's syntax check (clang, 8-byte pointers): the layout
394 * that matters is cc65816's, 4 bytes a pointer. */
395#ifndef __clang__
396_Static_assert(__builtin_offsetof(OamWorldBatch, y) == 4, "OamWorldBatch.y offset (sprite_world.asm)");
397_Static_assert(__builtin_offsetof(OamWorldBatch, tile) == 8, "OamWorldBatch.tile offset (sprite_world.asm)");
398_Static_assert(__builtin_offsetof(OamWorldBatch, attr) == 12, "OamWorldBatch.attr offset (sprite_world.asm)");
399_Static_assert(__builtin_offsetof(OamWorldBatch, visible) == 16, "OamWorldBatch.visible offset (sprite_world.asm)");
400_Static_assert(__builtin_offsetof(OamWorldBatch, first_id) == 20, "OamWorldBatch.first_id offset (sprite_world.asm)");
401_Static_assert(__builtin_offsetof(OamWorldBatch, count) == 21, "OamWorldBatch.count offset (sprite_world.asm)");
402_Static_assert(__builtin_offsetof(OamWorldBatch, size) == 22, "OamWorldBatch.size offset (sprite_world.asm)");
403_Static_assert(__builtin_offsetof(OamWorldBatch, order) == 24, "OamWorldBatch.order offset (sprite_world.asm)");
404#endif
405
460
467void oamSetTile(u16 id, u16 tile);
468
469/* Note: there is deliberately no oamSetVisible(id, show) — SNES sprite
470 * visibility is Y-position-based, so a "show" call can't know which Y to
471 * restore and could only be a silent no-op (which the removed v0.x
472 * function was). Use oamHide() to hide and oamSetY()/oamSet() with a
473 * valid Y to show. */
474
504void oamHide(u16 id);
505
513void oamSetSize(u16 id, u16 large);
514
515/*============================================================================
516 * OAM Update
517 *============================================================================*/
518
529void oamUpdate(void);
530
536void oamClear(void);
537
538/*============================================================================
539 * Metasprites
540 *============================================================================*/
541
564
567
569#define METASPR_ITEM(dx, dy, tile, attr) { (dx), (dy), (tile), (attr), 0 }
570
572#define METASPR_TERM { -128, 0, 0, 0, 0 }
573
575#define metasprite_end (-128)
576
578#define OBJ_PAL(pal) ((pal) << 1)
579
581#define OBJ_PRIO(prio) ((prio) << 4)
582
584#define OBJ_FLIPX 0x40
585
587#define OBJ_FLIPY 0x80
588
590#define OBJ_NAMETABLE_HIGH 0x01
591
610
653 const MetaspriteStyle *style, u8 flip);
654
655/*============================================================================
656 * Dynamic Sprite Engine
657 *============================================================================*/
658
677
699
713void oamDynamicSetSize(u16 id, u8 size);
714
731
756
757/*============================================================================
758 * Dynamic Metasprite Engine
759 *============================================================================*/
760
806void oamMetaDrawDyn(u16 id, s16 x, s16 y,
807 const MetaspriteItem *meta, const u8 *gfxptr, u8 size_class);
808
809/*============================================================================
810 * Fast Macro Sprite API
811 *
812 * Zero-overhead alternatives to oamSet/oamSetXY for performance-critical code.
813 * These write directly to oamMemory[] without function call overhead.
814 *
815 * oamSet() is a call with seven arguments pushed and a stack frame; for a
816 * handful of sprites updated every frame the macros below write the four
817 * OAM bytes in place. (A byte count of an earlier oamSet's frame and a
818 * "visible jitter" claim stood here until 2026-10-04; the cost is in
819 * docs/PERF.md, measured, not here.)
820 *
821 * Note: cc65816 does not truly inline 'static inline' functions — they
822 * become separate SUPERFREE sections with global labels that conflict
823 * across translation units. Macros are the only zero-overhead option.
824 *
825 * oamMemory[] and oam_update_flag are declared in <snes/system.h>
826 * (included automatically via <snes.h>).
827 *
828 * Usage:
829 * // Pre-compute attribute byte once at init
830 * u8 attr = OAM_ATTR(tile, palette, priority, flags);
831 *
832 * // Per-frame: fast full update
833 * oamSetFast(id, x, y, tile, palette, priority, flags);
834 *
835 * // Per-frame: position-only update (most common)
836 * oamSetXYFast(id, x, y);
837 *============================================================================*/
838
848#define OAM_ATTR(_tile, _pal, _prio, _fl) \
849 ((u8)(((_fl) & 0xC0) | \
850 (((_prio) & 0x03) << 4) | \
851 (((_pal) & 0x07) << 1) | \
852 (((_tile) >> 8) & 0x01)))
853
860#define OAM_XHI_MASK(_slot) \
861 ((u8)((_slot) == 0 ? 0x01 : (_slot) == 1 ? 0x04 : \
862 (_slot) == 2 ? 0x10 : 0x40))
863
879#define oamSetFast(_id, _x, _y, _tile, _pal, _prio, _fl) do { \
880 u16 _off = (u16)(_id) << 2; \
881 oamMemory[_off + 0] = (u8)((_x) & 0xFF); \
882 oamMemory[_off + 1] = (u8)((_y) & 0xFF); \
883 oamMemory[_off + 2] = (u8)((_tile) & 0xFF); \
884 oamMemory[_off + 3] = OAM_ATTR(_tile, _pal, _prio, _fl); \
885 u16 _ext = 512 + ((u16)(_id) >> 2); \
886 u16 _sl = (u16)(_id) & 0x03; \
887 u8 _xhi = OAM_XHI_MASK(_sl); \
888 if ((_x) & 0x100) \
889 oamMemory[_ext] |= _xhi; \
890 else \
891 oamMemory[_ext] &= ~_xhi; \
892 oam_update_flag = 1; \
893} while(0)
894
905#define oamSetXYFast(_id, _x, _y) do { \
906 u16 _off = (u16)(_id) << 2; \
907 oamMemory[_off + 0] = (u8)((_x) & 0xFF); \
908 oamMemory[_off + 1] = (u8)((_y) & 0xFF); \
909 u16 _ext = 512 + ((u16)(_id) >> 2); \
910 u16 _sl = (u16)(_id) & 0x03; \
911 u8 _xhi = OAM_XHI_MASK(_sl); \
912 if ((_x) & 0x100) \
913 oamMemory[_ext] |= _xhi; \
914 else \
915 oamMemory[_ext] &= ~_xhi; \
916 oam_update_flag = 1; \
917} while(0)
918
919#endif /* OPENSNES_SPRITE_H */
u16 flip
Frame counter for scroll speed throttle (scrolls every 3 frames).
Definition main.c:65
u8 palette[]
Full 256-color palette for BG and sprite layers (512 bytes).
signed short s16
16-bit signed integer (-32768 to 32767)
Definition types.h:50
unsigned short u16
16-bit unsigned integer (0 to 65535)
Definition types.h:53
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:47
u16 frame
Current sprite tile number.
Definition main.c:77
void oamDynamicSetSize(u16 id, u8 size)
Override the dispatched pixel size for a dynamic sprite slot.
void oamInit(u16 size, u16 name_base)
Initialize the sprite (OAM) system.
void oamSetY(u16 id, u16 y)
Set sprite Y position.
t_sprites oambuffer[128]
Dynamic sprite buffer (128 entries, 2048 bytes).
void oamSetSize(u16 id, u16 large)
Set sprite size (large/small).
void oamDynamicInit(const OamDynamicConfig *cfg)
Initialize the dynamic sprite engine from a config struct.
void oamDynamicDraw(u16 id)
Draw a dynamic sprite — engine picks the size routine.
u16 oamGetX(u16 id)
Read back a sprite's X position.
void oamSet(u16 id, u16 x, u16 y, u16 tile, u16 palette, u16 priority, u16 flags)
Set sprite properties.
void oamSetXY(u16 id, u16 x, u16 y)
Set sprite position.
void oamClear(void)
Clear all sprites.
void oamUpdate(void)
Copy OAM buffer to hardware.
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 oamHide(u16 id)
Park a sprite off screen.
u8 oamGetY(u16 id)
Read back a sprite's Y position.
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 oamDynamicDrainQueue(void)
Block until the dynamic-sprite VRAM tile queue is empty.
u16 oamDrawMetasprite(u16 startId, s16 x, s16 y, const MetaspriteItem *frame, const MetaspriteStyle *style, u8 flip)
Draw one frame of a metasprite.
void oamSetTile(u16 id, u16 tile)
Set sprite tile.
MetaspriteItem t_metasprite
PVSnesLib compatibility typedef.
Definition sprite.h:566
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 oamSetX(u16 id, u16 x)
Set sprite X position.
u16 cam_y
Definition main.c:80
u16 cam_x
Probe oracles: the camera, the coins placed this frame, the walker in the front slot.
Definition main.c:79
Metasprite item structure (PVSnesLib compatible).
Definition sprite.h:557
u16 tile
Definition sprite.h:560
u8 reserved
Definition sprite.h:562
s16 dx
Definition sprite.h:558
s16 dy
Definition sprite.h:559
u8 attr
Definition sprite.h:561
What stays the same from one frame of a metasprite to the next.
Definition sprite.h:598
u8 height
Definition sprite.h:607
u16 baseTile
Definition sprite.h:599
u8 pieceSize
Definition sprite.h:602
u8 width
Definition sprite.h:605
u8 basePalette
Definition sprite.h:600
u8 size
Definition sprite.h:601
Configuration for the dynamic sprite engine.
Definition sprite.h:666
u16 vramLarge
Definition sprite.h:667
u16 vramSmall
Definition sprite.h:671
u16 slotSmallInit
Definition sprite.h:674
u8 sizeMode
Definition sprite.h:675
u16 slotLargeInit
Definition sprite.h:673
A batch of sprites in world coordinates, for oamPlaceWorld().
Definition sprite.h:381
const s16 * x
Definition sprite.h:382
u8 count
Definition sprite.h:388
u8 * visible
Definition sprite.h:386
const u8 * order
Definition sprite.h:390
const u8 * attr
Definition sprite.h:385
u8 size
Definition sprite.h:389
u8 first_id
Definition sprite.h:387
const u8 * tile
Definition sprite.h:384
const s16 * y
Definition sprite.h:383
Dynamic sprite state structure (16 bytes, PVSnesLib compatible).
Definition sprite.h:143
u16 oamframeid
Definition sprite.h:146
u8 _pad
Definition sprite.h:151
s16 oamx
Definition sprite.h:144
u16 _reserved2
Definition sprite.h:153
s16 oamy
Definition sprite.h:145
u16 oamgfxaddr
Definition sprite.h:149
u16 _reserved1
Definition sprite.h:152
u8 oamgfxbank
Definition sprite.h:150
u8 oamattribute
Definition sprite.h:147
u8 oamrefresh
Definition sprite.h:148
OpenSNES Standard Types.