Skip to content

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.