- New: 11_libraries/ahi_programming.md — AHI retargetable audio API - New: 13_toolchain/cross_compilation_guide.md — cross-compiling for Amiga - New: 08_graphics/rtg_programming.md — RTG Picasso96/CyberGraphX programming - New: 17_demoscene/ — full demoscene techniques section: - copper_effects.md (6 techniques, 10 Pouet screenshots, antipatterns) - sprite_techniques.md (5 techniques, antipatterns) - pixel_tricks.md (5 techniques, antipatterns) - 3d_rendering.md (fixed-point math, 4 techniques, antipatterns) - timing_optimization.md (7 techniques, instruction timing tables) - README.md (section index with Mermaid diagrams) - images/ (10 authentic Amiga screenshots from Pouet.net) - New: 05_reversing/games/ (4 copper-analysis screenshots) - Updated: README index, TODO status (30/30 complete) - Added external references: Pouet/Demozoo links, Scoopex YouTube tutorial series, Amiga Graphics Archive, coppershade.org
26 KiB
Pixel Tricks — Copper Chunky, HAM Art, Scroll Register Abuse, and Modulo Wrapping
Overview
The Amiga's display hardware was designed for planar bitplane graphics — a memory-efficient scheme that matched the DMA streaming pattern perfectly. But the demoscene never accepted "designed for" as a limitation. From 1989 onward, demoscene coders systematically abused every display register to create effects that Commodore's engineers never imagined: copper chunky (no bitplanes at all, just color register writes), HAM art (photorealistic images in 4096 colors), scroll-register distortion (sine-wave text), and modulo wrapping (infinite scrolling surfaces).
These techniques share a common thread: they treat the display registers themselves as the primary rendering surface, not the bitplane memory. The Copper, scroll registers, and modulo values become the "pixels" — a fundamental inversion of the intended programming model.
graph TB
subgraph "Bitplane Alternatives"
CC["Copper Chunky<br/>Color register = pixel"]
HAM["HAM-6/HAM-8 Art<br/>Delta-encoded photorealism"]
end
subgraph "Register Abuse"
SCROLL["Scroll Distortion<br/>BPLCON1 sine waves"]
MOD["Modulo Wrapping<br/>BPLMOD virtual surfaces"]
DDF["DDFSTRT/DDFSTOP<br/>Per-line resolution changes"]
end
subgraph "Hybrid"
PLASMA["Plasma Effects<br/>Copper + precalc palette"]
SHADER["Copper Shader<br/>Per-line pseudo-rendering"]
end
CC --> PLASMA
HAM --> SHADER
SCROLL --> MOD
MOD --> DDF
PLASMA --> SHADER
Technique 1: Copper Chunky
The most extreme display hack on the Amiga. Copper chunky creates a pixel display without any bitplanes at all. The Copper writes to a single color register (COLOR01) at every pixel position across each scanline. The result is a "chunky pixel" display where each pixel's color is set directly by the Copper — no bitplane memory, no Blitter, no CPU rendering.
Why It Works
sequenceDiagram
participant Copper
participant COLOR01 as COLOR01 $DFF182
participant Denise as Denise DAC
Note over Copper: Scanline Y=100, pixel positions:
Copper->>COLOR01: MOVE #$0F00 (blue) at x=0
COLOR01->>Denise: Output blue pixel
Copper->>COLOR01: MOVE #$00F0 (green) at x=2
COLOR01->>Denise: Output green pixel
Copper->>COLOR01: MOVE #$0FFF (white) at x=4
COLOR01->>Denise: Output white pixel
Copper->>COLOR01: MOVE #$0F00 (blue) at x=6
COLOR01->>Denise: Output blue pixel
Note over Copper: ...continues across entire line
Resolution and Bandwidth
Copper chunky resolution is limited by how many MOVE instructions fit per scanline:
| Mode | Pixels/Line | Colors | DMA Budget | Notes |
|---|---|---|---|---|
| LoRes (1×) | ~56 | Any of 4096 | WAIT + MOVE = 4 slots/pixel | Sanity "Arte" style |
| LoRes (2×) | ~112 | Any of 4096 | MOVE only = 2 slots/pixel | No WAIT, consecutive moves |
| HiRes (1×) | ~28 | Any of 4096 | More DMA used by bitplanes | Rarely used |
Copper Chunky Template
; copper_chunky.asm — Minimal copper chunky (56 pixels wide, 1 line)
; No bitplanes enabled — display comes entirely from COLOR01 writes
COPPER_CHUNKY:
; Disable all bitplanes, enable color
dc.w $0100,$0200 ; BPLCON0: 0 bitplanes, color on
; Wait for display area start
dc.w $802C,$FFFE ; WAIT line 44
; Write COLOR01 at each pixel position
; Each MOVE takes 2 DMA slots; ~56 pixels per LoRes line
dc.w $0182,$0F00 ; COLOR01 = blue (pixel 0)
dc.w $0182,$0FF0 ; COLOR01 = cyan (pixel 1)
dc.w $0182,$0FFF ; COLOR01 = white (pixel 2)
dc.w $0182,$0F0F ; COLOR01 = magenta (pixel 3)
dc.w $0182,$0FF0 ; COLOR01 = cyan (pixel 4)
dc.w $0182,$0F00 ; COLOR01 = blue (pixel 5)
; ... repeat for ~56 pixels total ...
; End of line — black for rest of frame
dc.w $8030,$FFFE ; WAIT next line
dc.w $0182,$0000 ; COLOR01 = black
dc.w $FFFF,$FFFE ; End
Full Copper Chunky Frame
For a full-screen effect, the CPU pre-calculates the copper list each frame, writing color values for every visible pixel:
/* copper_chunky.c — Generate full copper chunky frame */
/* Resolution: ~56 pixels × 200 lines = 11,200 "pixels" */
#define CHUNKY_WIDTH 56
#define CHUNKY_HEIGHT 200
#define FIRST_LINE 44
/* The copper list is an array of UWORDs */
/* Each pixel = 1 MOVE instruction = 2 UWORDs (register, value) */
/* Each line = 1 WAIT (2 UWORDs) + 56 MOVEs (112 UWORDs) = 114 UWORDs */
#define LINE_WORDS (2 + CHUNKY_WIDTH * 2)
#define TOTAL_WORDS (LINE_WORDS * CHUNKY_HEIGHT + 4) /* +4 for end marker */
static UWORD copper_list[TOTAL_WORDS];
void generate_chunky_frame(const UBYTE *pixel_data, ULONG frame) {
int y, x;
UWORD *cop = copper_list;
for (y = 0; y < CHUNKY_HEIGHT; y++) {
/* WAIT for this scanline */
*cop++ = 0x8001 | (((FIRST_LINE + y) & 0xFF) << 8);
*cop++ = 0xFFFE;
/* Write each pixel's color */
for (x = 0; x < CHUNKY_WIDTH; x++) {
*cop++ = 0x0182; /* Register: COLOR01 */
*cop++ = rgb_palette[pixel_data[y * CHUNKY_WIDTH + x]];
}
}
/* End marker */
*cop++ = 0xFFFF;
*cop++ = 0xFFFE;
}
Warning
Copper chunky requires disabling all bitplane DMA — you cannot display bitplane graphics alongside copper chunky pixels. The technique is mutually exclusive with normal display rendering.
Technique 2: HAM Art
Hold-And-Modify (HAM) mode gives the Amiga 4,096 on-screen colors from only 6 bitplanes. Instead of direct color lookup, HAM encodes most pixels as deltas — modifications to the previous pixel's color. This makes HAM ideal for photorealistic images but nearly useless for animation.
HAM-6 Encoding (OCS/ECS)
| Bit 5 | Bit 4 | Bits 3-0 | Meaning |
|---|---|---|---|
| 0 | 0 | color index | Use palette[color index] (64 palette entries) |
| 0 | 1 | blue delta | Modify blue component of previous pixel |
| 1 | 0 | red delta | Modify red component of previous pixel |
| 1 | 1 | green delta | Modify green component of previous pixel |
HAM-8 Encoding (AGA)
| Bits 7-6 | Bits 5-0 | Meaning |
|---|---|---|
| 00 | palette index | Use palette[index] (64 entries) |
| 01 | blue value | Set blue to 6-bit value, keep R,G |
| 10 | red value | Set red to 6-bit value, keep G,B |
| 11 | green value | Set green to 6-bit value, keep R,B |
HAM Art Technique
HAM art for demos works by pre-rendering images in HAM mode, then displaying them as static or slowly-animated screens. The trick is managing the delta encoding to minimize color fringing:
/* ham_render.c — Render a pre-calculated image to HAM-6 bitmap */
/* OCS HAM: 6 bitplanes, 4 bits per component, 64 palette + 3 modify modes */
/* Total on-screen colors: 4,096 (12-bit RGB space) */
#define HAM_SET_COLOR 0 /* Use palette entry (bits 3-0 = index 0-15) */
#define HAM_MOD_BLUE 1 /* Modify blue (bits 3-0 = new blue 0-15) */
#define HAM_MOD_RED 2 /* Modify red (bits 3-0 = new red 0-15) */
#define HAM_MOD_GREEN 3 /* Modify green (bits 3-0 = new green 0-15) */
/* Convert a 12-bit RGB pixel to the best HAM encoding
given the previous pixel's color */
UWORD rgb_to_ham(UWORD target_rgb, UWORD prev_rgb) {
int tr = (target_rgb >> 8) & 0xF;
int tg = (target_rgb >> 4) & 0xF;
int tb = target_rgb & 0xF;
int pr = (prev_rgb >> 8) & 0xF;
int pg = (prev_rgb >> 4) & 0xF;
int pb = prev_rgb & 0xF;
int err_r = abs(tr - pr);
int err_g = abs(tg - pg);
int err_b = abs(tb - pb);
/* If exact match in palette → use SET mode */
/* (simplified: check against 16 base colors) */
if (err_r == 0 && err_g == 0 && err_b == 0) {
/* Exact match — encode as palette reference */
return (HAM_SET_COLOR << 4) | 0; /* Palette entry 0 */
}
/* Choose the component with largest error to modify */
if (err_b >= err_r && err_b >= err_g) {
return (HAM_MOD_BLUE << 4) | tb;
} else if (err_r >= err_g) {
return (HAM_MOD_RED << 4) | tr;
} else {
return (HAM_MOD_GREEN << 4) | tg;
}
}
HAM Limitations
| Limitation | Impact | Workaround |
|---|---|---|
| Fringing | Color deltas only change 1 component per pixel | Pre-calculate optimal delta sequences |
| Slow rendering | Each pixel depends on previous pixel state | Use Blitter for fast HAM blits |
| No random access | Can't set arbitrary pixel without context | Pre-render entire scanlines |
| Limited animation | Moving objects create fringing artifacts | Reserve palette entries for sprites/objects |
| Display artifacts | Vertical color bleeding from delta chains | Reset color at start of each scanline |
Technique 3: Scroll Register Distortion
BPLCON1 ($DFF102) controls horizontal scroll offset for the playfield. By changing it per scanline via the Copper, you create wave distortion effects — the classic "sine scrolling text" that defined the Amiga demo aesthetic.
How Scroll Distortion Works
graph LR
subgraph "Normal Display"
N["Line 0: offset=0<br/>Line 1: offset=0<br/>Line 2: offset=0<br/>Line 3: offset=0"]
end
subgraph "Sine Distorted"
S["Line 0: offset=0<br/>Line 1: offset=3<br/>Line 2: offset=7<br/>Line 3: offset=4<br/>Line 4: offset=0"]
end
N -->|"Copper writes<br/>BPLCON1 per line"| S
Sine Scroll Implementation
/* sine_scroll.c — Generate copper list for sine wave scroll */
#define DISPLAY_LINES 200
#define FIRST_LINE 44
#define SCROLL_WIDTH 16 /* Max scroll offset (0-15 for LoRes) */
void generate_sine_scroll(UWORD *copper, const UBYTE *sine_table,
ULONG phase) {
int y;
for (y = 0; y < DISPLAY_LINES; y++) {
int offset;
/* Get sine value for this line */
int sine_idx = (phase + y * 4) & 0xFF;
offset = sine_table[sine_idx] >> 4; /* 0-15 */
/* WAIT for this scanline */
*copper++ = 0x8001 | (((FIRST_LINE + y) & 0xFF) << 8);
*copper++ = 0xFFFE;
/* MOVE scroll offset to BPLCON1 */
*copper++ = 0x0102; /* BPLCON1 register */
*copper++ = (offset << 4) | offset; /* Even/odd playfield same */
}
/* End marker */
*copper++ = 0xFFFF;
*copper++ = 0xFFFE;
}
Technique 4: Modulo Wrapping
BPL1MOD and BPL2MOD ($DFF108/$DFF10A) define the byte offset added to each bitplane's data pointer at the end of a scanline. Normally this compensates for interleaving. By setting the modulo to unusual values, you create wrapping effects — the bitmap appears to fold, repeat, or scroll infinitely.
How Modulo Wrapping Works
Normal display (320px wide, 40 bytes/line):
Line 0: address 0
Line 1: address 40 (modulo = 40)
Line 2: address 80
With modulo = -40 (wraps back 40 bytes per line):
Line 0: address 0 → displays data[0..39]
Line 1: address -40 → wraps to end of bitmap!
Line 2: address -80 → wraps further back!
With modulo = 20 (half-width):
Line 0: address 0 → displays data[0..39]
Line 1: address 20 → offset by 20 bytes = half-line shift
Line 2: address 40
Tunnel Effect via Modulo
/* modulo_tunnel.c — Create a tunnel effect using modulo wrapping */
void setup_modulo_tunnel(ULONG frame) {
int y;
UWORD *cop = copper_list;
for (y = 0; y < 200; y++) {
/* Distance from center creates tunnel perspective */
int dist = 100 - abs(y - 100); /* 0 at top/bottom, 100 at center */
int modulo = dist * 2; /* Tighter wrapping at center */
/* WAIT for line */
*cop++ = 0x8001 | (((FIRST_LINE + y) & 0xFF) << 8);
*cop++ = 0xFFFE;
/* Set modulo per line */
*cop++ = 0x0108; /* BPL1MOD */
*cop++ = (UWORD)(short)modulo; /* Signed modulo value */
*cop++ = 0x010A; /* BPL2MOD */
*cop++ = (UWORD)(short)modulo;
}
}
Scroll Register + Modulo Combined
The most impressive effects combine scroll offset and modulo changes per scanline:
; combined_distort.asm — Per-line scroll + modulo for wave effect
; Line 100: normal
dc.w $8064,$FFFE ; WAIT line 100
dc.w $0102,$0000 ; BPLCON1 scroll = 0
dc.w $0108,$0028 ; BPL1MOD = 40 (normal)
; Line 101: slight wave
dc.w $8065,$FFFE ; WAIT line 101
dc.w $0102,$0030 ; BPLCON1 scroll = 3
dc.w $0108,$0026 ; BPL1MOD = 38 (slightly tighter)
; Line 102: more wave
dc.w $8066,$FFFE ; WAIT line 102
dc.w $0102,$0070 ; BPLCON1 scroll = 7
dc.w $0108,$0020 ; BPL1MOD = 32 (tighter still)
; Line 103: peak wave
dc.w $8067,$FFFE ; WAIT line 103
dc.w $0102,$00F0 ; BPLCON1 scroll = 15 (max)
dc.w $0108,$0018 ; BPL1MOD = 24
; Line 104: coming back
dc.w $8068,$FFFE ; WAIT line 104
dc.w $0102,$00A0 ; BPLCON1 scroll = 10
dc.w $0108,$0020 ; BPL1MOD = 32
Technique 5: Plasma Effects
Plasma is a classic demoscene effect created by overlapping sine waves mapped to a color palette. On the Amiga, plasma is implemented by pre-calculating a color lookup table and using the Copper to write it per scanline (or per block).
Plasma Generation
/* plasma.c — Generate plasma color values */
#define PLASMA_WIDTH 40 /* One color per 8 pixels (320/8) */
#define PLASMA_HEIGHT 25 /* One color per 8 scanlines (200/8) */
#define NUM_COLORS 64 /* Plasma palette size */
static UWORD plasma_palette[NUM_COLORS];
/* Pre-calculate plasma palette (rainbow gradient) */
void init_plasma_palette(void) {
int i;
for (i = 0; i < NUM_COLORS; i++) {
int phase = i * 360 / NUM_COLORS;
int r = (int)(8.0 + 7.0 * sin(phase * 3.14159 / 180.0));
int g = (int)(8.0 + 7.0 * sin((phase + 120) * 3.14159 / 180.0));
int b = (int)(8.0 + 7.0 * sin((phase + 240) * 3.14159 / 180.0));
plasma_palette[i] = (r << 8) | (g << 4) | b;
}
}
/* Calculate plasma value at (x,y) with time offset */
UBYTE plasma_value(int x, int y, ULONG t) {
int v = 0;
v += sine_table[(x * 4 + t) & 0xFF];
v += sine_table[(y * 6 + t * 2) & 0xFF];
v += sine_table[((x + y) * 3 + t) & 0xFF];
v += sine_table[((x * 2 - y + t * 3) & 0xFF)];
return (v >> 2) & 0x3F; /* 0-63 → palette index */
}
Antipatterns
1. The Copper Chunky Bandwidth Wall
Attempting copper chunky at too high a resolution. With 5+ bitplanes enabled, there aren't enough DMA slots for per-pixel color writes. The display will flicker, show garbage, or skip pixels.
Broken:
; Trying 112-pixel copper chunky with 4 bitplanes active
; → DMA starvation, half the pixels never get written
dc.w $0100,$$4200 ; BPLCON0: 4 bitplanes enabled
; ... then try 112 MOVE instructions per line → FAIL
Fixed:
; Disable ALL bitplanes for copper chunky
dc.w $0100,$0200 ; BPLCON0: 0 bitplanes, color on
; Now full DMA bandwidth available for Copper writes
2. The HAM Fringe
Rendering sharp edges in HAM mode without resetting the color state. Adjacent pixels of very different colors create visible "fringing" as the delta encoding struggles to transition.
Broken:
/* Drawing a red square on a blue background in HAM */
/* Each pixel transition goes: blue→red→blue→red → massive fringing */
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
/* Each transition needs 2-3 pixels to change color */
set_ham_pixel(x, y, is_square ? RED : BLUE); /* Fringes! */
}
}
Fixed:
/* Use palette entries for the square colors, and place the
square on scanline boundaries where HAM state resets */
for (y = 0; y < height; y++) {
/* At start of each line, set base color from palette */
set_ham_pixel(0, y, is_square ? RED_PAL_ENTRY : BLUE_PAL_ENTRY);
/* Now deltas work from a known state */
for (x = 1; x < width; x++) {
/* Only use SET mode for sharp transitions */
if (is_border_pixel(x, y)) {
set_ham_pixel(x, y, target_palette_entry);
}
}
}
3. The Modulo Overflow
Setting a modulo value that causes bitplane addresses to wrap into non-bitplane memory or cross allocation boundaries. The result is visible garbage, DMA conflicts, or even system crashes.
Broken:
/* Modulo too large → bitplane addresses go past allocated memory */
custom.bpl1mod = 0x7FFF; /* Almost 32KB jump per line! */
/* → Reads from random memory, shows garbage, may crash */
Fixed:
/* Calculate modulo based on actual bitmap dimensions */
int bytes_per_line = (width / 8) * num_bitplanes; /* Interleaved */
int modulo = bytes_per_line - (display_bytes_per_line);
custom.bpl1mod = (UWORD)(short)modulo; /* Sign-extend correctly */
4. The Scroll Reset Gap
Forgetting that BPLCON1 scroll applies from the Copper write position to the end of the frame. If you set scroll per-line but forget to reset it, the last value persists for all remaining lines.
Broken:
; Sine scroll lines 50-150, but no reset after line 150
dc.w $8060,$FFFE ; WAIT line 96
dc.w $0102,$0070 ; Scroll = 7
; ... more lines ...
dc.w $8096,$FFFE ; WAIT line 150 (last sine line)
dc.w $0102,$0003 ; Scroll = 3
; BUG: Lines 151+ still have scroll=3!
Fixed:
; After the sine section, reset scroll to 0
dc.w $8097,$FFFE ; WAIT line 151
dc.w $0102,$0000 ; Reset scroll to 0
5. The Palette Stride
Assuming HAM palette entries are contiguous. HAM-6 uses only 16 palette entries (0–15) for SET mode — the upper 48 entries of the 64-entry palette are not directly accessible via HAM SET codes.
Broken:
/* Setting HAM palette entry 32 — NOT accessible via HAM SET! */
ham_palette[32] = 0x0FFF;
/* HAM SET mode only uses bits 3-0 → entries 0-15 */
Fixed:
/* Only entries 0-15 are usable with HAM SET (00xxxx) */
/* Place your most important anchor colors in entries 0-15 */
ham_palette[0] = 0x0000; /* Black (background) */
ham_palette[1] = 0x0FFF; /* White (highlights) */
/* ... entries 2-15 for key scene colors */
Decision Guide
flowchart TD
START[Need pixel-level effect] --> Q1{Bitplanes available?}
Q1 -->|No bitplanes needed| CC[Copper Chunky<br/>~56px wide, any colors]
Q1 -->|Yes, need bitplanes| Q2{Photorealistic or generated?}
Q2 -->|Photorealistic| HAM[HAM-6/HAM-8 Art<br/>4096/262,144 colors]
Q2 -->|Generated/procedural| Q3{Wave distortion or wrapping?}
Q3 -->|Wave distortion| Q4{Horizontal or vertical?}
Q3 -->|Surface wrapping| MOD[Modulo Wrapping]
Q4 -->|Horizontal| SCROLL[BPLCON1 Sine Scroll]
Q4 -->|Vertical| Q5{Color cycling or structure?}
Q5 -->|Color only| PLASMA[Plasma / Color Cycling]
Q5 -->|Structural| SPLIT[Raster Split (see copper_effects.md)]
CC --> BUDGET{DMA budget<br/>for copper writes?}
HAM --> BUDGET2{Accept fringing<br/>at sharp edges?}
SCROLL --> BUDGET
MOD --> BUDGET
BUDGET -->|Sufficient| OK[Proceed]
BUDGET -->|Insufficient| REDUCE[Reduce width or<br/>disable bitplanes]
BUDGET2 -->|Yes| OK2[Proceed with HAM]
BUDGET2 -->|No| ALT[Use 6-bitplane normal mode<br/>or copper chunky instead]
Historical Timeline
timeline
title Pixel Tricks Evolution
1985 : Amiga launch — HAM-6 mode (4096 colors)
: Planar display standard
1987 : First HAM art images appear in graphics competitions
1988 : Red Sector — sine scroll distortion in demos
: Scroll register abuse becomes standard demo technique
1989 : First plasma effects using copper color cycling
1990 : Sanity "Arte" — copper chunky full-screen effect
: HAM art becomes competition category at parties
1991 : Combined scroll + modulo distortion effects
: Copper chunky reaches 56-pixel practical limit
1992 : AGA HAM-8 mode — 262,144 colors
: Photorealistic HAM-8 images in demos
1993 : Advanced plasma with pre-calculated tables
: Modulo tunnel effects
1994 : Full copper chunky animations at acceptable framerates
2000+ : Pixel tricks preserved in demo archives
: FPGA ensures exact register timing
Modern Analogies
| Amiga Pixel Trick | Modern Equivalent | Why It Maps |
|---|---|---|
| Copper chunky | Fragment shader per-pixel output | Both generate color per pixel programmatically |
| HAM delta encoding | DXT/Etc2 texture compression | Both store deltas from neighbors, not absolute values |
| Scroll register distortion | Vertex displacement shader | Both deform the output geometry |
| Modulo wrapping | UV coordinate wrapping/repeating | Both tile the source data |
| Plasma effect | GPU procedural texture | Both generate visuals from math functions |
| Per-line register changes | Per-scanline render state | Both change parameters at Y boundaries |
| Self-modifying copper list | Dynamic command buffer | Both generate GPU commands per frame |
| Color register palette | CLUT (Color Lookup Table) | Both map indices to RGB values |
Use Cases
| Use Case | Technique | Notable Examples |
|---|---|---|
| Full-screen chunky graphics | Copper chunky | Sanity "Arte", ASD demos |
| Photorealistic static images | HAM-6/HAM-8 | Amiga art competitions, IPF cover art |
| Sine-wave text scroll | BPLCON1 distortion | Every Amiga demo with scrolling text |
| Wave distortion effect | Scroll + modulo combined | Red Sector, Kefrens demos |
| Background plasma | Copper color cycling | Numerous demos, cracktros |
| Tunnel perspective | Modulo wrapping | "Tunnel" demo effect |
| Virtual scrolling surface | Modulo repeat | Racing games, side-scrollers |
| Color gradient animation | Per-line color writes | Copper bar variants |
FPGA / Emulation Impact
| Concern | Impact | Notes |
|---|---|---|
| Copper chunky timing | Exact per-cycle color register writes | Denise must latch COLORxx at precise pixel positions |
| HAM delta decoding | Per-pixel color state machine | Must track running R/G/B across entire scanline |
| Scroll register latency | BPLCON1 changes take effect next line | Must match hardware pipeline delay |
| Modulo arithmetic | Signed 16-bit addition to address | Address wrapping must handle 24-bit space correctly |
| BPLCON0 bitplane disable | 0-bitplane mode must suppress all BPL DMA | Copper chunky depends on zero bitplane DMA |
| HAM-8 AGA extension | 8-bitplane HAM mode with 6-bit deltas | AGA Lisa chip HAM decoder must be implemented |
FAQ
Q: Can copper chunky animate at full frame rate? A: Rarely. A 56×200 copper chunky frame requires generating ~11,200 copper instructions per frame. On a 7 MHz 68000, that takes most of the frame time. Most copper chunky demos animate at reduced resolution or partial updates.
Q: Why not always use HAM for more colors? A: HAM's delta encoding makes it nearly impossible to render sharp-edited graphics or animations. Any pixel transition that requires changing more than one color component creates visible fringing. HAM is excellent for pre-rendered images but terrible for real-time rendering.
Q: What's the difference between plasma and copper bars? A: Copper bars are horizontal bands of uniform color (one color per scanline or per group of scanlines). Plasma varies color both horizontally and vertically using sine-based lookup tables, creating a 2D color field. Plasma typically renders to bitplanes (pre-calculated pixel data) while copper bars use register writes only.
Q: Can I combine copper chunky with normal bitplane graphics?
A: Not simultaneously — copper chunky requires disabling all bitplane DMA. However, you can use copper chunky on some scanlines and bitplane graphics on others, switching BPLCON0 mid-frame via the Copper. This is rarely done because the copper list size doubles.
Q: How does modulo wrapping differ from scroll wrapping?
A: Scroll (BPLCON1) shifts pixel data horizontally within a scanline by 0–15 pixels. Modulo (BPL1MOD) changes the byte offset between consecutive scanlines in memory — it affects which memory addresses are read for each line, not how the bits are shifted. They produce different visual effects: scroll creates smooth horizontal displacement, modulo creates discontinuous "jumps" in the displayed data.
Q: What is EHB (Extra Half-Brite) and how does it relate? A: EHB is a 6-bitplane mode where the 32nd palette entry is automatically generated as a half-brightness copy of entries 0–31, giving 64 colors. It's not a "pixel trick" per se — it's a hardware feature of ECS+. It can be combined with copper effects for interesting results but is documented separately in HAM & EHB Modes.
References
Related Knowledge Base Articles
- Pixel Conversion — C2P algorithms, copper chunky theory
- HAM & EHB Modes — HAM-6/HAM-8 encoding, EHB mode
- Copper Effects — Copper bars, gradients, raster splits
- Display Modes — ModeID, BPLCON0 settings
- Video Timing — Scanline structure, beam position
- Blitter Programming — Blitter fill for HAM rendering
External Resources
- Amiga Hardware Reference Manual — Chapter 3: BPLCON0/1, HAM mode, scroll registers
- Amiga Graphics Archive — https://amiga.lychesis.net/specials/Copper.html — Copper chunky and gradient analysis in commercial games
- Scoopex Amiga Hardware Programming (Photon) — YouTube playlist — Bitplane and copper-based effects walkthroughs; companion site: coppershade.org
- Pouet.net — https://www.pouet.net — Copper chunky and plasma demos
- Demozoo — https://demozoo.org — Demoscene production encyclopedia
- HAM Encoder — Various tools for converting images to HAM format