C64 Sprites

The Commodore 64 has eight hardware sprites, called MOBs (movable object blocks) in the VIC-II documentation. The chip fetches, positions and draws them over the background on its own, and reports where they overlap. Each is 24×21 pixels, held in 63 bytes, and controlled through registers from $D000 upward.

C64 Sprites

Overview

The VIC-II supports eight sprites at once, numbered 0 to 7. Each is a 24×21 pixel block that the chip draws over the character or bitmap display without the CPU touching the screen. Sprites can be moved to any pixel position, doubled in width or height, tinted, and made to sit in front of or behind the background. The hardware also latches every collision each frame, so a game can read which sprites touched each other or the background.

PropertyValue
Sprites8 (numbered 0–7)
Size24 × 21 pixels
Data63 bytes each (3 bytes × 21 rows)
Colours1 (hi-res) or 3 shared plus per-sprite (multicolour)
RegistersVIC-II, from $D000
Expansion×2 in width, height, or both

Sprite data and pointers

A sprite's shape is 63 bytes: 21 rows of 3 bytes, each byte holding 8 horizontal pixels with the most significant bit on the left. The 64th byte of the aligned block is unused. Because a sprite block is 64 bytes, its data must start on a 64-byte boundary within the VIC bank.

The chip finds each sprite's data through a one-byte pointer. The eight sprite pointers sit in the last 8 bytes of the 1K screen RAM, at $07F8–$07FF when the screen is at its default $0400. Pointer value p means the data is at bank base + p × 64, so a pointer can select any of 256 blocks in the current 16K VIC bank.

AddressSelects
$07F8Data pointer for sprite 0
$07F9Data pointer for sprite 1
$07FFData pointer for sprite 7

Each row of the shape uses three bytes: byte 0 is the leftmost 8 pixels, byte 1 the middle 8, byte 2 the rightmost 8. Row 0 is the top of the sprite, and the 21 rows follow in order.

Position and enable

Every sprite has an X and a Y position register, a pair per sprite from $D000. X is nine bits wide: the low eight bits are the per-sprite register, and the ninth bit lives in the shared MSB register $D010, one bit per sprite. Y is a single byte. A sprite is drawn only when its bit is set in the enable register $D015.

RegisterNameMeaning
$D000, $D001Sprite 0 X, YLow 8 bits of X, and Y position.
$D002, $D003Sprite 1 X, Y… through sprite 7 at $D00E, $D00F.
$D010X MSBsBit n is the ninth X bit of sprite n.
$D015EnableBit n switches sprite n on.

Positions are given in the VIC-II's own coordinate space, which is wider and taller than the visible display, so the on-screen area starts at a positive offset. Values outside the visible window let a sprite slide smoothly off any edge.

Colour and multicolour

In the default hi-res mode a sprite is a single colour. Each set pixel shows the sprite's own colour, taken from its per-sprite colour register $D027 to $D02E; each clear pixel is transparent and shows the background. The 24×21 shape gives fine detail in one colour.

Setting a sprite's bit in the multicolour select register $D01C switches it to multicolour. Pixels are then read in pairs, so the sprite is 12 double-width pixels across but still 24 bits of data, and each pair picks one of four meanings:

Bit pairColour source
00Transparent (background shows through)
01Shared multicolour 0, register $D025
10The sprite's own colour, $D027–$D02E
11Shared multicolour 1, register $D026

So a multicolour sprite shows up to three solid colours plus transparency: two are shared by every multicolour sprite, and one is unique to that sprite. The trade is horizontal resolution, since each coloured pixel is two screen pixels wide.

Expansion, priority and collision

Two expansion registers double a sprite's size. A set bit in $D01D doubles that sprite's width, and a set bit in $D017 doubles its height; both together give a 48×42 block from the same 63 bytes, with each source pixel drawn as a 2×2 square.

The priority register $D01B decides front-to-back order against the background. When a sprite's bit is clear it draws in front of the display; when set it draws behind the foreground graphics, so the background can hide it. Among sprites, lower numbers always draw in front of higher numbers.

The VIC-II latches two kinds of collision every frame. The sprite–sprite register $D01E sets a bit for each sprite that overlapped another sprite, and the sprite–background register $D01F sets a bit for each sprite that overlapped non-transparent background pixels. Reading a collision register returns the accumulated bits and clears them.

RegisterMeaning
$D017Y-expand: bit n doubles sprite n height.
$D01DX-expand: bit n doubles sprite n width.
$D01CMulticolour select per sprite.
$D01BPriority: bit n set puts sprite n behind the foreground.
$D01ESprite–sprite collision, read and clear.
$D01FSprite–background collision, read and clear.
$D025, $D026Shared multicolour 0 and 1.
$D027–$D02EPer-sprite colour for sprites 0–7.