# AGA Copper — What It Is, How It Works, and Programming Guide
## What Is the Copper?
The **Copper** (Co-Processor) is one of the most distinctive pieces of hardware in any computer ever built. It is a tiny, ultra-simple programmable DMA engine that executes a program — called a **copper list** — in perfect synchronisation with the video beam as it sweeps across the screen.
The Copper watches the beam position and can **write any value to any custom chip register at any specific screen position**. This single capability enables an astonishing range of visual effects.
### Why Does It Matter?
On a conventional computer, changing display parameters (colours, scroll positions, resolutions) requires the CPU to execute code at precisely the right moment. This is fragile, wastes CPU time, and is limited by interrupt latency.
The Copper does this **automatically, for free, with perfect timing** — every single frame, without any CPU involvement at all.
The Copper fetches its program (the copper list) from Chip RAM via DMA. It reads one instruction (2 words = 4 bytes) every 4 colour clocks. The copper list **must** reside in Chip RAM — it cannot be in Fast RAM because only Chip RAM is DMA-accessible.
**Copper ↔ Custom Registers:**
When the Copper executes a MOVE instruction, it writes directly to a custom chip register (`$DFF000`–`$DFF1FE`). This is the exact same register space the CPU writes to. The Copper can set colours, bitplane pointers, sprite pointers, display window, scroll offsets, DMA control, and audio parameters.
**Copper ↔ Beam Counter:**
The Copper continuously compares the current beam position (V count, H count) against WAIT instructions. When the beam reaches or passes the specified position, execution continues. This is a hardware comparator — no polling loop, no interrupt latency.
**Copper ↔ CPU:**
- They share the DMA bus. Agnus arbitrates: DMA engines get priority, CPU gets remaining cycles
- The CPU can modify copper list words in Chip RAM; changes take effect on the next frame (or immediately if the Copper hasn't read that instruction yet)
- The CPU can set `COP1LC`/`COP2LC` to change which copper list runs
- The CPU can strobe `COPJMP1`/`COPJMP2` to restart the Copper mid-frame
- The CPU is **not interrupted** by Copper activity — they are fully independent
**Copper ↔ Video Output:**
The Copper doesn't produce video directly. It modifies the registers that Denise/Lisa uses to produce video. By changing registers at specific beam positions, the Copper indirectly controls what appears on every scanline.
**Copper ↔ Blitter:**
A WAIT instruction with bit 15 of the mask word cleared becomes a "blitter-finished WAIT" — the Copper pauses until both the beam position is reached AND the blitter is idle. This coordinates copper list execution with blitter operations.
| No computation | Cannot add, subtract, compare, branch, or loop |
| No memory read | Can only WRITE to registers, never read |
| Write-only to custom regs | Cannot write to CPU memory, CIA, or Fast RAM |
| Limited register set | Protected registers ($000–$03E) need `COPCON` unlock |
| No sub-pixel timing | Horizontal resolution is 4 colour clocks (~8 low-res pixels) |
| Vertical wrapping | V counter wraps at 255; PAL lines 256+ need two WAITs |
---
## Instruction Set
The Copper has exactly **3 instructions**. Each is 32 bits (two 16-bit words):
### MOVE — Write Value to Register
```
Word 1: 0RRRRRRR RR000000 R = register offset ($040–$1FE, even)
Word 2: DDDDDDDD DDDDDDDD D = 16-bit data to write
Example: Set COLOR00 ($180) to bright red ($0F00):
dc.w $0180, $0F00
```
The register address in word 1 is the offset from `$DFF000`. Bit 0 of word 1 is always 0 (this distinguishes MOVE from WAIT/SKIP).
### WAIT — Wait for Beam Position
```
Word 1: VVVVVVVV HHHHHHHH V = vertical beam (8 bits), H = horizontal
Bit 0 = 1 (marks this as WAIT, not MOVE)
Word 2: vvvvvvvv hhhhhhhm v,h = mask bits, m = 0 for WAIT
(bit 0 of word 2 = 0 distinguishes from SKIP)
Example: Wait for line 100 ($64), any horizontal position:
dc.w $6401, $FFFE
```
The mask controls which beam position bits are compared. `$FFFE` means "match all bits" (standard). You can mask horizontal bits to wait for a specific column.
### SKIP — Conditional Skip
```
Same format as WAIT, but bit 0 of word 2 = 1.
If beam ≥ specified position, skip the next instruction.
Example: Skip next if beam is past line 200:
dc.w $C801, $FFFF ; SKIP if V ≥ $C8
dc.w $0180, $0F00 ; this MOVE is skipped if condition met
```
### End of List
```
dc.w $FFFF, $FFFE ; WAIT for impossible position (V=$FF, H=$FF)
; Copper halts until next frame
```
---
## Your First Copper List
Here's the simplest possible copper list — it changes the background colour at line 128:
```asm
SECTION copperlist,DATA_C ; *** MUST be in Chip RAM! ***
MyCopperList:
; Top half: blue background
dc.w $0180, $005F ; MOVE COLOR00 = blue
; Wait for middle of screen
dc.w $8001, $FFFE ; WAIT line 128
; Bottom half: red background
dc.w $0180, $0F00 ; MOVE COLOR00 = red
; End of list
dc.w $FFFF, $FFFE ; WAIT forever
```
To activate it:
```asm
lea $DFF000,a5
move.l #MyCopperList,$080(a5) ; COP1LCH = pointer to our list
dc.w $2C01,$FFFE ; WAIT line 44 (first visible PAL line)
dc.w $0180,$0F00 ; red
dc.w $2D01,$FFFE ; line 45
dc.w $0180,$0E10
dc.w $2E01,$FFFE ; line 46
dc.w $0180,$0D20
dc.w $2F01,$FFFE ; line 47
dc.w $0180,$0C30
dc.w $3001,$FFFE ; line 48
dc.w $0180,$0B40
dc.w $3101,$FFFE ; line 49
dc.w $0180,$0A50
dc.w $3201,$FFFE ; line 50
dc.w $0180,$0960
; ... continue for each line ...
dc.w $FFFF,$FFFE
```
This produces a smooth colour gradient down the screen — **zero CPU cost**.
---
## Parallax Scrolling (Per-Layer Scroll Speed)
```asm
ParallaxCopper:
; Sky layer (no scroll)
dc.w $0102,$0000 ; BPLCON1 = 0 (no shift)
; Wait for horizon
dc.w $6001,$FFFE ; WAIT line 96
; Hills layer (scroll slow)
dc.w $0102,$0022 ; BPLCON1 = shift 2 pixels
; Wait for ground
dc.w $A001,$FFFE ; WAIT line 160
; Ground layer (scroll fast)
dc.w $0102,$0066 ; BPLCON1 = shift 6 pixels
dc.w $FFFF,$FFFE
```
Each frame, a VBlank interrupt updates the scroll values in the copper list. The CPU just modifies 3 words — the Copper handles all the per-line register changes.
---
## Sprite Multiplexing
The Amiga has 8 hardware sprites, but the Copper can repoint them mid-frame for more:
```asm
; Sprite 0 shows character A at Y=50
dc.w $3001,$FFFE ; WAIT before sprite starts
dc.w $0120,SprDataA>>16 ; SPR0PTH
dc.w $0122,SprDataA ; SPR0PTL
; After sprite A finishes (Y=66), reuse for character B at Y=120
dc.w $7801,$FFFE ; WAIT line 120
dc.w $0120,SprDataB>>16 ; SPR0PTH = new sprite data
dc.w $0122,SprDataB ; SPR0PTL
; After character B finishes, reuse for character C...
dc.w $A001,$FFFE
dc.w $0120,SprDataC>>16
dc.w $0122,SprDataC
```
This gives you **24+ sprites** on screen (8 physical × 3+ reuses per frame).
---
## System-Friendly Copper (OS API)
If your program coexists with Workbench, use `graphics.library`: