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.
- Overview
- Sprite data and pointers
- Position and enable
- Colour and multicolour
- Expansion, priority and collision
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.
| Property | Value |
|---|---|
| Sprites | 8 (numbered 0–7) |
| Size | 24 × 21 pixels |
| Data | 63 bytes each (3 bytes × 21 rows) |
| Colours | 1 (hi-res) or 3 shared plus per-sprite (multicolour) |
| Registers | VIC-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.
| Address | Selects |
|---|---|
| $07F8 | Data pointer for sprite 0 |
| $07F9 | Data pointer for sprite 1 |
| … | … |
| $07FF | Data 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.
| Register | Name | Meaning |
|---|---|---|
| $D000, $D001 | Sprite 0 X, Y | Low 8 bits of X, and Y position. |
| $D002, $D003 | Sprite 1 X, Y | … through sprite 7 at $D00E, $D00F. |
| $D010 | X MSBs | Bit n is the ninth X bit of sprite n. |
| $D015 | Enable | Bit 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 pair | Colour source |
|---|---|
| 00 | Transparent (background shows through) |
| 01 | Shared multicolour 0, register $D025 |
| 10 | The sprite's own colour, $D027–$D02E |
| 11 | Shared 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.
| Register | Meaning |
|---|---|
| $D017 | Y-expand: bit n doubles sprite n height. |
| $D01D | X-expand: bit n doubles sprite n width. |
| $D01C | Multicolour select per sprite. |
| $D01B | Priority: bit n set puts sprite n behind the foreground. |
| $D01E | Sprite–sprite collision, read and clear. |
| $D01F | Sprite–background collision, read and clear. |
| $D025, $D026 | Shared multicolour 0 and 1. |
| $D027–$D02E | Per-sprite colour for sprites 0–7. |