VIC-II Hardware Sprites: A Complete Guide
8 Hardware Sprites
The VIC-II chip provides 8 hardware sprites, each 24×21 pixels in hi-res mode or 12×21 in multicolour mode. While 8 may seem limiting, techniques like toggleplexing and sprite multiplexing can display 20+ sprites on screen simultaneously.
Sprite Registers
All sprite control lives in the VIC-II register space ($D000–$D02E):
$D000-$D00F X/Y positions (sprite 0-7)
$D010 X position MSB (bit 8 for each sprite)
$D015 Sprite enable register
$D017 Y-expand
$D01B Sprite-to-background priority
$D01C Multicolour mode select
$D01D X-expand
$D025-$D026 Shared multicolour colours
$D027-$D02E Individual sprite colours
Sprite data pointers sit at the end of the current VIC bank’s screen memory — by default at $07F8–$07FF (addresses 2040–2047).
Positioning Sprites
Each sprite has an 8-bit Y register (0–255) and a 9-bit X position (the 8-bit register plus bit from $D010). The visible screen area runs roughly X:24–343 and Y:50–249 on PAL. To move sprite 0 to position (160, 100):
LDA #160
STA $D000 ; sprite 0 X low byte
LDA $D010
AND #$FE ; clear bit 0 (MSB for sprite 0)
STA $D010
LDA #100
STA $D001 ; sprite 0 Y
For X positions beyond 255, set the corresponding bit in $D010. The geo-referenced sprite system handles this mapping automatically through pre-calculated lookup tables.
Hi-Res vs. Multicolour
Hi-res sprites use 1 bit per pixel (24×21 = 63 bytes per frame), displaying the sprite in one colour against transparent background. Multicolour mode uses 2 bits per pixel (12×21 effective resolution), providing 3 colours plus transparent — at the cost of halved horizontal resolution.
Most games use multicolour sprites for characters and hi-res for projectiles and small objects. The luma-driven colour selection approach helps maximise the visual impact of the limited palette in both modes.
Sprite Priority and Collision
Register $D01B controls whether each sprite appears in front of or behind background graphics. The VIC-II also provides hardware collision detection: $D01E for sprite-sprite and $D01F for sprite-background. These registers are cleared on read, so you must capture them in a single read per frame.
Hardware collision is pixel-perfect but coarse — it detects any overlap of non-transparent pixels. Most games implement additional bounding-box checks before trusting the hardware result.
Sprite Expansion
Setting bits in $D017 (Y-expand) or $D01D (X-expand) doubles the sprite in that axis. Expanded sprites use the same 63 bytes of data — each pixel is simply drawn twice. This is free in terms of data but halves the effective resolution. Some games toggle expansion mid-frame for mixed-size effects.
Beyond 8 Sprites
The real power comes from re-using sprites within a single frame. By changing a sprite’s Y position, data pointer, and colour during the vertical blank between its display lines, you can show the same hardware sprite multiple times. This is the core of multiplexing — for a detailed implementation, see the Seawolves engine analysis which handles up to 24 visible sprites through raster-timed re-positioning.