Overview
The VIC-II can raise an interrupt the instant the raster beam reaches a scanline you choose. In the handler you change a register — and because the change lands part-way down the screen, the top and bottom show different settings. That one idea, “split the screen at a line,” is behind colour bars, separate score panels that don’t scroll, showing more than the usual sprites or colours, and almost every advanced C64 display trick.
This pattern sets up a raster interrupt that splits the background into two colours.
Code
; =============================================================================
; RASTER SPLITS - C64
; A raster interrupt changes the background colour part-way down the screen
; CPU: A handful of cycles per split | Memory: ~70 bytes
; =============================================================================
VIC_RASTER = $D012 ; Raster compare line (low 8 bits)
VIC_CTRL1 = $D011 ; Bit 7 = raster compare line bit 8
VIC_IRQ = $D019 ; Interrupt status — write a 1 to a bit to acknowledge
VIC_IRQEN = $D01A ; Interrupt enable — bit 0 = raster
BGCOLOR = $D021 ; Background colour
CIA1_ICR = $DC0D ; CIA #1 interrupt control
IRQVEC = $0314 ; KERNAL IRQ vector (low byte / high byte)
SPLIT_LINE = 130 ; Split the screen here
setup:
sei ; no interrupts while we wire this up
lda #<raster_irq ; point the IRQ vector at our handler
sta IRQVEC
lda #>raster_irq
sta IRQVEC+1
lda #$7f ; switch off CIA #1 timer interrupts...
sta CIA1_ICR
lda CIA1_ICR ; ...and acknowledge any that were pending
lda #$01 ; enable the VIC-II raster interrupt
sta VIC_IRQEN
lda #0 ; first interrupt at the top of the screen
sta VIC_RASTER
lda VIC_CTRL1 ; clear compare-line bit 8 (our lines are < 256)
and #$7f
sta VIC_CTRL1
lda #$01 ; acknowledge any pending raster interrupt
sta VIC_IRQ
cli ; interrupts back on
rts
; The handler fires twice a frame: once at the top, once at SPLIT_LINE.
; Read the raster line to tell which, paint that zone, and aim at the other.
raster_irq:
lda #$01
sta VIC_IRQ ; acknowledge the raster interrupt
lda VIC_RASTER
cmp #SPLIT_LINE
bcs at_split
at_top:
lda #$00 ; black above the split
sta BGCOLOR
lda #SPLIT_LINE ; next interrupt: the split line
sta VIC_RASTER
jmp $EA81 ; KERNAL exit — restore registers and RTI
at_split:
lda #$06 ; blue below the split
sta BGCOLOR
lda #0 ; next interrupt: top of the next frame
sta VIC_RASTER
jmp $EA81
Trade-offs
| Aspect | Cost |
|---|---|
| CPU | A few cycles in the handler per split |
| Memory | ~70 bytes for setup + handler |
| Limitation | Jitter — without a stable raster, the change line wobbles a pixel or two |
When to use: Colour bars, a fixed status panel above a scrolling playfield, raster-timed effects, and as the engine for sprite multiplexing.
When to avoid: If a single static screen is all you need — the basic game loop polling $D012 is simpler than wiring an interrupt.
How the interrupt is wired
Four things have to be set up, all behind sei/cli so nothing fires mid-change:
- The vector.
$0314/$0315is the KERNAL’s IRQ vector in RAM. Point it at your handler; the KERNAL saves the registers before jumping through it. - Silence the CIA. The KERNAL’s own 60 Hz interrupt comes from CIA #1. Writing
$7Fto$DC0Ddisables all of its interrupt sources so only the raster fires. - Enable the raster source with bit 0 of
$D01A, and set the compare line in$D012(with bit 8 in$D011). - Acknowledge. At the start of the handler, write a 1 to bit 0 of
$D019— until you do, the VIC-II keeps the request asserted and the interrupt re-fires forever.
Exit through $EA81, the KERNAL routine that pulls the saved registers and runs RTI.
Colour bars
Reprogram the line and the colour each time, cycling through a table, and you get bars:
bar_irq:
lda #$01
sta VIC_IRQ
ldx bar_index
lda bar_colors,x ; next colour
sta BGCOLOR
lda bar_lines,x ; next line
sta VIC_RASTER
inx
cpx #BAR_COUNT
bne +
ldx #0 ; wrap at the end of the frame
+ stx bar_index
jmp $EA81
Each entry is “at line L, switch to colour C” — a tiny copper-list-by-hand. The same loop drives sprite multiplexing, swapping sprite positions instead of colours.
Stable rasters
The split line wobbles because the CPU might be mid-instruction (or stalled on a badline) when the interrupt arrives, so the handler reaches the colour write a few cycles late and at a different point each frame. For a rock-steady edge you need a stable raster — a double-interrupt or cycle-counted NOP sled that lands the write on the exact same cycle every frame. See Stable Raster.
Related
Patterns: Game Loop (Basic), Hardware Sprites
Vault: VIC-II | Commodore 64