Overview
The VIC-II gives you eight sprites — but only eight at once on a given scanline. The instant the raster beam passes the bottom of a sprite, that hardware sprite is idle and can be sent somewhere lower down the screen to appear as a completely different one. Trigger that with a raster interrupt, do it repeatedly, and eight sprites become sixteen, twenty-four, thirty — a multiplexer. It’s how nearly every crowded C64 game put more than eight things on screen.
It rests on three pieces: a list of “virtual” sprites, kept sorted by Y; a raster-interrupt chain that fires as each one finishes; and the reprogramming of a freed hardware sprite to the next virtual one in the list.
Code
; =============================================================================
; SPRITE MULTIPLEXING - C64
; Each raster interrupt hands a freed hardware sprite to the next virtual one
; Needs: a Y-sorted virtual sprite list, and ideally a stable raster
; =============================================================================
VIC_IRQ = $D019
VIC_RASTER = $D012
SCREEN = $0400
SPR_PTR = SCREEN + $3F8
; Virtual sprites, sorted top-to-bottom each frame (more than 8 of them):
; vy,x vx,x vptr,x vcol,x count in NUM
; msp = index of the next virtual sprite waiting for a hardware slot
; slot = which hardware sprite (0..7) to reuse next
mux_irq:
lda #$01
sta VIC_IRQ ; acknowledge the raster interrupt
ldx msp
cpx #NUM
bcs frame_end ; all virtual sprites placed — wait for next frame
ldy slot ; the hardware sprite that just came free (0..7)
; --- hand virtual sprite X to hardware sprite Y ---
; (register index is Y*2 for X/Y position; Y for colour, pointer, MSB bit)
lda vy,x
sta hw_y,y ; -> $D001 + 2*slot (see note on indexing)
lda vx,x
sta hw_x,y ; -> $D000 + 2*slot
lda vptr,x
sta SPR_PTR,y ; -> pointer for sprite `slot`
lda vcol,x
sta hw_col,y ; -> $D027 + slot
; ... set/clear this sprite's X-MSB bit in $D010 too ...
inc msp ; advance the virtual list
iny ; rotate the hardware slot 0..7
tya
and #$07
sta slot
; --- aim the next interrupt just below the NEXT virtual sprite ---
ldx msp
lda vy,x
sec
sbc #1 ; a line or two of margin so we're not too late
sta VIC_RASTER
jmp $EA81
frame_end:
; reset for the next frame: msp=0, slot=0, IRQ back to the topmost sprite
; (the per-frame Y-sort of the virtual list happens here or in the main loop)
lda #0
sta msp
sta slot
lda vy ; topmost sprite's line
sta VIC_RASTER
jmp $EA81
(The hw_y,y / hw_x,y writes stand in for the ×2 register indexing — most multiplexers pre-build a small table of the eight $D000+2n addresses, or unroll the eight cases, rather than compute it each time.)
Trade-offs
| Aspect | Cost |
|---|---|
| CPU | One interrupt per reused sprite — several per frame |
| Memory | Virtual sprite tables, plus the sort |
| Limitation | Still only 8 on any one scanline; total depends on vertical spread |
When to use: Any game that needs more than eight sprites — shmups, crowds of enemies, big bosses built from several sprites.
When to avoid: If eight is genuinely enough. The multiplexer’s timing is unforgiving; don’t pay for it unless you need it.
The sort is the heart of it
Hardware sprites are reused in Y order, so the virtual list has to be sorted top-to-bottom every frame. A full sort each frame would be dear — but sprites move only a little between frames, so the list is almost sorted already. An insertion sort exploits that: it’s near-linear on nearly-sorted data, so the per-frame cost stays small. Getting the sort wrong is the classic multiplexer bug — a sprite reused before the beam has passed its old position flickers or tears.
Timing and limits
- Never reprogram a sprite the beam is still drawing. Reposition it only after its line has passed, with a line or two of margin — hence aiming the next interrupt just below the current sprite.
- Eight per scanline is the hard ceiling. Multiplexing buys you more down the screen, never more across one line. Bunch too many at the same height and the ninth vanishes.
- Jitter shows. Because the reprogramming races the beam, an unstable raster makes sprites shimmer. Serious multiplexers run on a stable raster.
Related
Patterns: Hardware Sprites, Raster Splits
Vault: VIC-II | Commodore 64