OpenSNES
Modern Open-Source SNES Development SDK
Loading...
Searching...
No Matches
main.c File Reference

Sprites showcase — a bouncing swarm, and the OAM throughput ceiling. More...

#include <snes.h>

Macros

#define NBIRDS   32
 Sprite count — the smooth-60fps ceiling for per-sprite C motion.
#define SPR_VRAM   0x4000
 Sprite tile VRAM word address.
#define XMAX   248
#define XMIN   8
#define YMAX   208
#define YMIN   16

Functions

static void build_dot (void)
 Build the 8x8 dot (a filled ball), pixel index 1, rest transparent.
static void encode_4bpp (void)
 Pack px[] (8x8 palette indices) into a 4bpp planar tile.
int main (void)

Variables

static s8 bvx [32]
static s8 bvy [32]
static u8 bx [32]
static u8 by [32]
static u8 px [64]
 Scratch 8x8 tile as pixel indices, packed to 4bpp.
u16 swarm_frame
 Probe oracle: frames elapsed (advances while the flock animates).
static u8 tilebuf [32]

Detailed Description

Sprites showcase — a bouncing swarm, and the OAM throughput ceiling.

The finale of the sprites family, and an honest one. The SNES has 128 hardware sprites and one OAM DMA per frame to move them — but computing and writing 128 positions in C every frame does not fit a 60 fps budget on the base CPU. The OAM buffer lives in bank $7E (every write is a slow long address) and cc65816 spills registers in a busy loop, so the smooth ceiling for per-sprite C motion is around three dozen sprites.

So this swarm runs a comfortable 32 dots at a rock-steady 60 fps, each bouncing independently — and that number is the lesson:

moving sprites is cheap in principle (one OAM DMA), but touching each one from C is not free. For all 128 you either move the work off the main CPU — chips/sa1_starfield runs 128 birds of Lissajous trig on the SA-1 at 10.74 MHz — or hand-write the update loop in assembly.

Motion is deliberately cheap (integer add + edge bounce, no multiply or trig) so the cost you see is the OAM update itself, not the maths. Positions go straight into oamMemory[]; one oam_update_flag lets the NMI's DMA push them all. Four palettes tint the cloud. Zero assets.

ROM mode: LoROM (project default).

SNES Concepts
  • 128 hardware sprites; OAM is a RAM buffer (oamMemory[]) DMA'd to the PPU once per frame — set oam_update_flag, the NMI does the transfer
  • Direct oamMemory[] writes: the fastest per-sprite update, two bytes (X, Y) when tile/palette/attributes were set once at init
  • The per-sprite update budget: ~32 sprites of C motion fit 60 fps; the ceiling is OAM writes (bank $7E long addressing) + loop overhead, not the sprite hardware. sa1_starfield shows the coprocessor way past it
What to Observe
32 colourful dots bounce around at a steady 60 fps. swarm_frame (probe oracle) advances every frame.
Modules Used
console, dma, background, sprite
See also
lib/include/snes/sprite.hoamSetFast, oamMemory, oam_update_flag
examples/chips/sa1_starfield — 128 sprites of trig, offloaded to the SA-1

Macro Definition Documentation

◆ NBIRDS

#define NBIRDS   32

Sprite count — the smooth-60fps ceiling for per-sprite C motion.

◆ SPR_VRAM

#define SPR_VRAM   0x4000

Sprite tile VRAM word address.

◆ XMAX

#define XMAX   248

◆ XMIN

#define XMIN   8

◆ YMAX

#define YMAX   208

◆ YMIN

#define YMIN   16

Function Documentation

◆ build_dot()

void build_dot ( void )
static

Build the 8x8 dot (a filled ball), pixel index 1, rest transparent.

◆ encode_4bpp()

void encode_4bpp ( void )
static

Pack px[] (8x8 palette indices) into a 4bpp planar tile.

◆ main()

int main ( void )

Variable Documentation

◆ bvx

s8 bvx[32]
static

X velocity

◆ bvy

s8 bvy[32]
static

Y velocity

◆ bx

u8 bx[32]
static

X position (u8, always within bounds)

◆ by

u8 by[32]
static

Y position

◆ px

u8 px[64]
static

Scratch 8x8 tile as pixel indices, packed to 4bpp.

◆ swarm_frame

u16 swarm_frame

Probe oracle: frames elapsed (advances while the flock animates).

◆ tilebuf

u8 tilebuf[32]
static