Copper: The Amiga's Display Coprocessor
Two instructions that changed everything
The Copper synchronised effects to the video beam with just WAIT and MOVE—enabling rainbow gradients, split screens, and per-scanline palette changes.
The Copper (short for coprocessor) lives inside Agnus and executes simple programs synchronised to the video beam position. With just three instructions—WAIT, MOVE, and SKIP—programmers created effects that seemed impossible: smooth colour gradients, split-screen displays, and sprite multiplexing.
Fast facts
- Location: integrated into Agnus chip.
- Instructions: 3 (WAIT, MOVE, SKIP).
- Execution: two bus cycles per instruction — except
WAIT, which costs nothing while it waits. - Lists: two copper lists can be active (COP1/COP2).
Waiting is free
The Copper’s importance is usually put as “it changes registers mid-screen”. The sentence that explains why that is cheap is in the register description:
WAIT Wait until beam counter is equal to, or greater than. (keeps Copper off of bus until beam position has been reached)
A WAIT is not a loop. The Copper “examines the contents of the video beam
position counter directly”, and while it waits it is off the bus entirely — using
no memory cycles, competing with nothing. Only when the beam arrives does it
resume fetching, and then “all instructions (except for WAIT) require two bus
cycles”.
That is the whole economy of the chip. A Copper list is mostly waiting, and waiting is free. In a machine where the blitter, bitplanes, sprites, audio and the 680x0 are all contending for the same memory, Commodore added a coprocessor that spends most of its life not asking for any.
Copper danger mode
The manual’s other named bit is CDANG, in the Copper control register:
This is a 1-bit register that when set true, allows the Copper to access the blitter hardware. This bit is cleared by power-on reset, so that the Copper cannot access the blitter hardware.
CDANG stands for “Copper danger mode”. Out of reset the Copper is fenced
off from the blitter registers, and a program that wants the two custom chips
driving each other has to say so deliberately.
It is worth setting beside the blitter’s own BLTPRI, which Commodore documents
as “blitter-nasty”. The Amiga hardware manual contains a bit called nasty and a
bit called danger, both describing what one custom chip can do to the rest of
the machine if allowed.
The connection runs the other way too. Bit 15 of a WAIT’s second word is BFD,
“blitter-finished disable” — so a Copper wait can be made to depend not only on
where the beam is but on whether the blitter has finished.
Instruction format
All copper instructions are 32 bits (two words):
MOVE instruction
First word: Register offset (bit 0 = 0)
Second word: Data to write
Example: $0180, $0F00 — write $0F00 to COLOR00 (red).
WAIT instruction
First word: VP7-VP0 HP8-HP1 (position) | $0001
Second word: VPM7-VPM0 HPM8-HPM1 | $FFFE
- VP = vertical position (line number).
- HP = horizontal position (divided by 2).
- Mask bits allow partial matching.
Example: $4007, $FFFE — wait for line $40, any horizontal position.
SKIP instruction
First word: VP7-VP0 HP8-HP1 (position) | $0001
Second word: VPM7-VPM0 HPM8-HPM1 | $FFFF (note: bit 0 = 1)
Skip next instruction if beam is past specified position.
Common techniques
Copper rainbow
Change background colour each scanline:
dc.w $2c07,$fffe ; Wait line $2c
dc.w $0180,$0100 ; Dark red
dc.w $2d07,$fffe ; Wait line $2d
dc.w $0180,$0200 ; Slightly brighter
dc.w $2e07,$fffe ; Wait line $2e
dc.w $0180,$0300 ; Brighter still
; ... continue for smooth gradient
Split screen
Different display modes in upper/lower screen:
; Upper screen: hires
dc.w $0100,$8200 ; BPLCON0 = hires
dc.w $8007,$fffe ; Wait for line $80
; Lower screen: lowres
dc.w $0100,$1200 ; BPLCON0 = lowres
Sprite multiplexing
Reposition sprite after it passes:
dc.w $5007,$fffe ; Wait for sprite's last line
dc.w $0140,$8050 ; SPR0POS = new position
dc.w $0142,$8060 ; SPR0CTL = new control
dc.w $0144,sprite2 ; SPR0DATA = new graphics
Copper limitations
| Limitation | Details |
|---|---|
| Writes only | The Copper writes to registers and tests beam position; it can’t read register values back. Conditional logic is limited to SKIP (skip the next instruction if past a beam position). |
| No RAM data access | The Copper fetches its instruction stream from chip RAM but cannot read or write data RAM directly — every action is a register write. |
| Timing bound | Executes during the display, not just vblank — but each instruction consumes Copper DMA cycles |
| Instruction timing | MOVE consumes 4 cycles; WAIT consumes 4 cycles when the wait fires (more if it has to wait), SKIP consumes 4 cycles |
| Protected registers | Some registers (DMACON, INTENA, INTREQ, ADKCON) require the COPCON “copper danger” bit before the Copper can write them |
Copper danger
Certain registers are protected by default. To allow copper writes to these:
move.w #$8002,$dff096 ; Set COPCON bit in DMACON
Protected registers include DMACON and INTENA.
Copper list management
| Register | Address | Purpose |
|---|---|---|
| COP1LCH | $DFF080 | Copper list 1 high word |
| COP1LCL | $DFF082 | Copper list 1 low word |
| COP2LCH | $DFF084 | Copper list 2 high word |
| COP2LCL | $DFF086 | Copper list 2 low word |
| COPJMP1 | $DFF088 | Restart copper 1 (strobe) |
| COPJMP2 | $DFF08A | Restart copper 2 (strobe) |
Example copper list structure
copperlist:
dc.w $008e,$2c81 ; DIWSTRT - display window start
dc.w $0090,$f4c1 ; DIWSTOP - display window stop
dc.w $0092,$0038 ; DDFSTRT - data fetch start
dc.w $0094,$00d0 ; DDFSTOP - data fetch stop
dc.w $0100,$1200 ; BPLCON0 - 1 bitplane
dc.w $0180,$0000 ; COLOR00 - black background
dc.w $0182,$0fff ; COLOR01 - white foreground
dc.w $ffff,$fffe ; End - wait forever
Why the Copper mattered
The Copper democratised advanced display effects. What required expensive hardware on other systems—or wasn’t possible at all—became a matter of writing a simple list. Demo coders and game programmers alike exploited this to create visuals that defined the Amiga’s reputation.