amiga-bootcamp/01_hardware/aga_a1200_a4000/aga_copper.md
2026-04-26 14:46:18 -04:00

18 KiB
Raw Blame History

← Home · Hardware · AGA

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 synchronization 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 (colors, 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.


Copper in the System Architecture

Block Diagram

┌─────────────────────────────────────────────────────────────────────┐
│                        CHIP RAM (up to 2MB)                         │
│  ┌──────────────┐   ┌──────────────┐  ┌───────────────────────────┐ │
│  │ Copper List  │   │ Bitplane Data│  │ Sprite Data               │ │
│  │ (MOVE/WAIT/  │   │ (screen      │  │ (hardware sprite          │ │
│  │  SKIP words) │   │  pixels)     │  │  images)                  │ │
│  └──────┬───────┘   └──────┬───────┘  └──────────┬────────────────┘ │
└─────────┼──────────────────┼─────────────────────┼──────────────────┘
          │ DMA read         │ DMA read            │ DMA read
          ▼                  ▼                      ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    AGNUS / ALICE  (DMA Controller)                  │
│                                                                     │
│  ┌────────────┐    ┌──────────────┐    ┌───────────────────────┐    │
│  │   COPPER   │    │  Bitplane    │    │   Sprite DMA          │    │
│  │   Engine   │    │  DMA Engine  │    │   Engine              │    │
│  │            │    │              │    │                       │    │
│  │ • Fetches  │    │ Fetches 1-8  │    │ Fetches sprite        │    │
│  │   copper   │    │ planes per   │    │ data for 8 sprites    │    │
│  │   list via │    │ line from    │    │ per line              │    │
│  │   DMA      │    │ BPLxPT       │    │                       │    │
│  │ • Compares │    │ pointers     │    │                       │    │
│  │   beam pos │    │              │    │                       │    │
│  │ • Writes   │    │              │    │                       │    │
│  │   to regs  │    │              │    │                       │    │
│  └─────┬──────┘    └──────┬───────┘    └───────────┬───────────┘    │
│        │                  │                        │                │
│  ┌─────┴──────────────────┴────────────────────────┴─────────┐      │
│  │              BEAM COUNTER (V count, H count)              │      │
│  │    Increments every color clock, resets each frame        │      │
│  │    PAL: 312 lines × 227 clocks   NTSC: 262 × 227          │      │
│  └───────────────────────────────────────────────────────────┘      │
└──────────┬──────────────────────────────────────────────────────────┘
           │ register writes ($DFF000$DFF1FE)
           ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    DENISE / LISA  (Video Encoder)                   │
│                                                                     │
│  Receives bitplane data + sprite data + color register values       │
│  Composites them into a final pixel stream:                         │
│                                                                     │
│  ┌────────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │
│  │ Bitplane   │  │ Sprite   │  │ Color    │  │ Playfield        │   │
│  │ Decode     │→ │ Priority │→ │ Palette  │→ │ Priority &       │   │
│  │ (planar→   │  │ Merge    │  │ Lookup   │  │ Genlock Control  │   │
│  │  index)    │  │          │  │ (32/256) │  │                  │   │
│  └────────────┘  └──────────┘  └──────────┘  └────────┬─────────┘   │
└───────────────────────────────────────────────────────┬─────────────┘
                                                        │
                                                        ▼
                                                  ┌────────────┐
                                                  │  RGB / DAC │
                                                  │  → Monitor │
                                                  └────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                          M68K CPU                                   │
│                                                                     │
│  • Can read/write the SAME custom registers as the Copper           │
│  • Can modify the copper list in Chip RAM at any time               │
│  • Shares the DMA bus with Copper (Agnus arbitrates)                │
│  • CPU is STALLED when DMA bus is busy (Chip RAM access only)       │
│  • Fast RAM access is NOT affected by DMA contention                │
└─────────────────────────────────────────────────────────────────────┘

Component Interactions

Copper ↔ Chip RAM: The Copper fetches its program (the copper list) from Chip RAM via DMA. It reads one instruction (2 words = 4 bytes) every 4 color 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 colors, 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.


What the Copper Can Do

Effect How Used In
Per-line color changes WAIT for line, MOVE color register Gradient skies, rainbow bars
Split screens Change bitplane pointers mid-frame Status bar + scrolling playfield
Parallax scrolling Change BPLCON1 (scroll offset) at different lines Multi-layer side-scrollers
Resolution changes Change BPLCON0 mid-frame HiRes menu + LoRes game area
Sprite multiplexing Repoint sprite DMA pointers after sprite finishes More than 8 sprites per frame
Palette animation Modify color registers each frame Cycling colors, water shimmer
Display window tricks Change DIWSTRT/DIWSTOP Overscan, letterbox
Interlace tricks Toggle LOF bit Custom interlace effects

What the Copper Cannot Do

Limitation Detail
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 color 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 color at line 128:

    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:

    lea     $DFF000,a5
    move.l  #MyCopperList,$080(a5)   ; COP1LCH = pointer to our list
    move.w  d0,$088(a5)              ; COPJMP1 strobe = restart copper
    move.w  #$8280,$096(a5)          ; DMACON: enable Copper + Master DMA

Rainbow Gradient (Color Per Scanline)

RainbowCopper:
    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 color gradient down the screen — zero CPU cost.


Parallax Scrolling (Per-Layer Scroll Speed)

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:

    ; 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:

struct UCopList *ucl = AllocMem(sizeof(struct UCopList), MEMF_CLEAR);

CINIT(ucl, 50);                          /* init, max 50 instructions */
CWAIT(ucl, 0, 0);                         /* wait top of screen */
CMOVE(ucl, custom.color[0], 0x005F);     /* COLOR00 = blue */
CWAIT(ucl, 128, 0);                       /* wait line 128 */
CMOVE(ucl, custom.color[0], 0x0F00);     /* COLOR00 = red */
CEND(ucl);                                /* end */

viewport->UCopIns = ucl;
RethinkDisplay();                         /* merge into system copper list */

The OS inserts your instructions into its own copper list, interleaved with its own display management.


AGA Copper Enhancements

AGA (Alice chip) keeps the same 3-instruction Copper but gains access to the extended AGA registers:

AGA Feature Copper Can Set
256-color palette COLOR00COLOR255 via BPLCON3 bank select
Extended sprites 64-color sprites via palette banks
FMODE DMA fetch width (but careful — affects in-progress DMA)
BPLCON3/BPLCON4 AGA-specific bitplane/sprite control

AGA Palette via Copper

AGA has 256 colors but still only 32 color registers visible at a time. To load all 256 colors, the Copper uses BPLCON3 to select palette banks:

    ; Load colors 031 (bank 0)
    dc.w    $0106,$0000         ; BPLCON3: bank 0
    dc.w    $0180,$0000         ; COLOR00
    dc.w    $0182,$0111         ; COLOR01
    ; ... all 32 colors ...
    
    ; Switch to bank 1 (colors 3263)
    dc.w    $0106,$2000         ; BPLCON3: bank 1
    dc.w    $0180,$0222         ; COLOR32
    dc.w    $0182,$0333         ; COLOR33
    ; ... etc for all 8 banks ...

Copper Timing

Parameter Value
Instruction time 4 color clocks (= 8 lo-res pixels = ~1.12 µs)
Max instructions per line ~112 (NTSC) / ~114 (PAL)
Horizontal resolution 4 color clocks (~8 lo-res pixels)
Vertical range 0255 (wraps; use double-WAIT for PAL lines 256+)
PAL visible lines 44300 (256 visible)
NTSC visible lines 44244 (200 visible)

References