[← Home](../README.md) · [Graphics](README.md)
# Display Modes — Chipset Generations, ModeID, and Timing
## Overview
The Amiga's display system evolved through three generations of custom chips: **OCS** (Original Chip Set, A1000/A500/A2000), **ECS** (Enhanced, A3000/A600), and **AGA** (Advanced Graphics Architecture, A1200/A4000). Each generation expanded resolution, color depth, and display flexibility while maintaining backward compatibility.
OS 3.0+ provides a **display database** that abstracts these capabilities. Applications query available modes by `ModeID` rather than hardcoding chipset-specific flags.
---
## Chipset Comparison
| Feature | OCS (Agnus/Denise) | ECS (Fat Agnus/Super Denise) | AGA (Alice/Lisa) |
|---|---|---|---|
| **Max Chip RAM** | 512 KB (8372) / 1 MB (8372A) | 2 MB (8375) | 2 MB (8374) |
| **Bitplanes** | 6 (32 colors, lowres) | 6 | 8 (256 colors) |
| **Palette entries** | 32 (4096 total, 12-bit RGB) | 32 (4096) | 256 (16.7M, 24-bit RGB) |
| **Max lowres** | 320×256 (PAL) | 320×256 | 320×256 |
| **Max hires** | 640×256 | 640×256 | 640×256 |
| **Super hires** | — | 1280×256 | 1280×256 |
| **Scan-doubled** | — | — | 640×512 non-interlaced |
| **HAM** | HAM6 (4096 colors) | HAM6 | HAM8 (262,144 colors) |
| **EHB** | EHB (64 colors) | EHB | EHB (superseded by 8 planes) |
| **Sprites** | 8 × 16px × 3 colors | 8 × 16px × 3 colors | 8 × 16/32/64px × 3/15 colors |
| **Fetch modes** | 1× | 1× | 1×, 2×, 4× (wider data bus) |
| **Bandwidth** | 3.58 MHz pixel clock | 3.58/7.16/14.32 MHz | Up to 28.64 MHz (4× fetch) |
---
## Display Timing Fundamentals
All Amiga display modes are based on PAL or NTSC television timing:
### PAL (Europe, Australia)
```
Line frequency: 15,625 Hz
Frame frequency: 50 Hz (25 Hz interlaced)
Lines per frame: 312.5 (625 interlaced)
Active lines: ~256 (non-interlaced) / ~512 (interlaced)
Color clock: 3,546,895 Hz
Pixel clock (lores): 7,093,790 Hz (1 pixel = 2 color clocks)
Pixel clock (hires): 14,187,580 Hz
```
### NTSC (Americas, Japan)
```
Line frequency: 15,734 Hz
Frame frequency: 60 Hz (30 Hz interlaced)
Lines per frame: 262.5 (525 interlaced)
Active lines: ~200 (non-interlaced) / ~400 (interlaced)
Color clock: 3,579,545 Hz
Pixel clock (lores): 7,159,090 Hz
Pixel clock (hires): 14,318,180 Hz
```
### Display Cycle Anatomy
```
←── Horizontal line (~64 µs PAL) ──→
┌───────────────────────────────────────────────────────────┐
│ HSYNC │ Left │ Active Display Area │ Right │
│ │ Border │ (bitplane DMA + sprite DMA) │ Border │
│ ~4.7µs│ │ │ │
└───────────────────────────────────────────────────────────┘
Vertical:
┌── VSYNC (2.5 lines) ──┐
│ Top Border │
│ Active Display │ ← 256 lines (PAL) / 200 (NTSC)
│ Bottom Border │
└────────────────────────┘
```
> **FPGA implication**: the MiSTer core must replicate these exact timings for correct DMA slot allocation. Many programs (especially demos) count DMA cycles and will break if timing is even slightly off.
---
## ModeID Format
A ModeID is a 32-bit value encoding the monitor driver and mode:
```
┌──────────────────────────────────────────────┐
│ 31 16 │ 15 0 │
│ Monitor ID │ Mode within monitor │
└──────────────────────────────────────────────┘
```
### Standard Mode IDs
| ModeID | Name | Resolution | Depth | Chipset |
|---|---|---|---|---|
| `$00000000` | PAL:LORES | 320×256 | 5 | OCS+ |
| `$00008000` | PAL:HIRES | 640×256 | 4 | OCS+ |
| `$00000004` | PAL:LORES-LACE | 320×512 | 5 | OCS+ |
| `$00008004` | PAL:HIRES-LACE | 640×512 | 4 | OCS+ |
| `$00080000` | PAL:SUPERHIRES | 1280×256 | 2 | ECS+ |
| `$00080004` | PAL:SUPERHIRES-LACE | 1280×512 | 2 | ECS+ |
| `$00039000` | DBLPAL:LORES | 320×256 | 8 | AGA |
| `$00039004` | DBLPAL:HIRES | 640×256 | 8 | AGA |
| `$00039024` | DBLPAL:HIRES-LACE | 640×512 | 8 | AGA |
| `$00011000` | DBLNTSC:LORES | 320×200 | 8 | AGA |
| `$00011004` | DBLNTSC:HIRES | 640×200 | 8 | AGA |
| `$00000800` | HAM | 320×256 HAM6 | 6 | OCS+ |
| `$00000080` | EHB | 320×256 EHB | 6 | OCS+ |
### Mode Flags (bits within ModeID)
| Bit | Mask | Meaning |
|---|---|---|
| 2 | `$0004` | LACE — interlaced (double vertical resolution) |
| 11 | `$0800` | HAM — Hold-And-Modify mode |
| 7 | `$0080` | EHB — Extra Half-Brite mode |
| 15 | `$8000` | HIRES — double horizontal resolution |
| 19 | `$80000` | SUPERHIRES — quadruple horizontal resolution (ECS+) |
---
## AGA Fetch Modes
AGA introduced wider data fetch widths, reducing DMA overhead:
| Fetch Mode | Bits per Fetch | FMODE Value | Effect |
|---|---|---|---|
| 1× | 16 bits | 0 | OCS compatible — 1 word per slot |
| 2× | 32 bits | 1 | 2 words per slot — more bandwidth for deeper modes |
| 4× | 64 bits | 3 | 4 words per slot — required for 8-plane hires |
```c
/* Set AGA fetch mode (custom register): */
custom->fmode = 3; /* 4× fetch — maximum bandwidth */
```
> [!WARNING]
> 4× fetch mode causes 64-pixel horizontal alignment constraints. Sprites also widen to 32 or 64 pixels in 2×/4× mode. Many OCS-era programs break if FMODE ≠ 0.
---
## Querying the Display Database
```c
/* graphics.library 39+ — enumerate all available modes: */
ULONG modeID = INVALID_ID;
struct DisplayInfo di;
struct DimensionInfo dims;
struct MonitorInfo mon;
while ((modeID = NextDisplayInfo(modeID)) != INVALID_ID)
{
if (GetDisplayInfoData(NULL, (UBYTE *)&di, sizeof(di),
DTAG_DISP, modeID))
{
if (di.NotAvailable) continue; /* skip unavailable modes */
GetDisplayInfoData(NULL, (UBYTE *)&dims, sizeof(dims),
DTAG_DIMS, modeID);
GetDisplayInfoData(NULL, (UBYTE *)&mon, sizeof(mon),
DTAG_MNTR, modeID);
Printf("$%08lx: %ldx%ld, %ld colors, %s\n",
modeID,
dims.Nominal.MaxX - dims.Nominal.MinX + 1,
dims.Nominal.MaxY - dims.Nominal.MinY + 1,
1L << dims.MaxDepth,
mon.Mspc->ms_Node.xln_Name);
}
}
```
### Best Mode Selection
```c
/* Find the best mode matching desired specs: */
ULONG bestMode = BestModeID(
BIDTAG_NominalWidth, 640,
BIDTAG_NominalHeight, 480,
BIDTAG_Depth, 8,
BIDTAG_MonitorID, PAL_MONITOR_ID,
TAG_DONE);
if (bestMode != INVALID_ID)
Printf("Best mode: $%08lx\n", bestMode);
```
---
## DMA Slot Budget
The display system shares DMA bandwidth with other custom chips. Each scanline has a fixed number of DMA slots:
| DMA Consumer | Slots Used |
|---|---|
| Disk DMA | 3 words |
| Audio (4 channels) | 4 words |
| Sprites (8) | 16 words |
| Bitplane (lowres, 5 planes) | 40 words |
| Bitplane (hires, 4 planes) | 80 words |
| Copper | 1 per instruction pair |
| Blitter | Variable (steals from CPU) |
| CPU | Whatever is left |
> In high-resolution 4-plane mode, bitplane DMA alone consumes 80 words per line — nearly the entire available bandwidth. This is why OCS/ECS hires is limited to 4 planes (16 colors) and AGA needed wider fetch modes.
---
## ModeID Selection Flowchart
Choosing the right display mode requires answering four questions in order: monitor type → resolution needs → color depth → interlacing preference.
```mermaid
flowchart TD
Q1["What monitor?"] --> |"15 kHz RGB (1084, TV)"| Q2["Resolution?"]
Q1 --> |"31 kHz VGA (DBLPAL/DBLNTSC)"| Q3["Depth?"]
Q1 --> |"RTG card (CyberGraphX/P96)"| RTG["Use RTG ModeIDs
not native graphics"]
Q2 --> |"Lowres 320px"| LORES["PAL:LORES / NTSC:LORES
$00000000 / $00000000"]
Q2 --> |"Hires 640px"| HIRES["PAL:HIRES / NTSC:HIRES
$00008000"]
Q2 --> |"SuperHires 1280px
(ECS+ only)"| SUPER["PAL:SUPERHIRES
$00080000"]
Q3 --> |"8-bit (256 colors)"| DBL8["DBLPAL/NTSC HIRES
$00039004 / $00011004"]
Q3 --> |"4–5 bit"| DBL4["DBLPAL/NTSC LORES
$00039000 / $00011000"]
LORES --> |"Need 6 planes?"| LACE["> Check if LACE needed"]
HIRES --> |"Limited to 4 planes
due to DMA budget"| LACE
SUPER --> |"Limited to 2 planes"| LACE
```
###encoding intoc decision logic
```c
/* Complete mode selection routine handling chipset fallback: */
ULONG SelectBestMode(ULONG wantWidth, ULONG wantHeight, ULONG wantDepth)
{
ULONG modeID = INVALID_ID;
/* Step 1: Try exact match via BestModeID */
modeID = BestModeID(
BIDTAG_NominalWidth, wantWidth,
BIDTAG_NominalHeight, wantHeight,
BIDTAG_Depth, wantDepth,
TAG_DONE);
if (modeID != INVALID_ID)
return modeID;
/* Step 2: Fall back — halve depth, try again */
if (wantDepth > 4)
{
modeID = BestModeID(
BIDTAG_NominalWidth, wantWidth,
BIDTAG_NominalHeight, wantHeight,
BIDTAG_Depth, wantDepth / 2,
TAG_DONE);
}
else
{
/* Step 3: Dichotomy fallback —attempt lores non-laced */
modeID = BestModeID(
BIDTAG_Depth, 4,
TAG_DONE);
}
return modeID; /* may still be INVALID_ID — caller must check */
}
```
---
## CRT vs Flat-Panel Considerations
The Amiga's display timing was designed for **15 kHz CRT televisions** and monitors. Modern flat-panel displays introduce several compatibility issues:
| Concern | CRT (Original) | Modern Flat-Panel | Workaround |
|---|---|---|---|
| **Sync frequency** | 15.625 kHz (PAL) / 15.734 kHz (NTSC) | Most LCDs require ≥ 31 kHz | Use DBLPAL/DBLNTSC modes or scandoubler (Indivision, OSSC) |
| **Refresh rate** | 50 Hz (PAL) / 60 Hz (NTSC) | Many monitors reject < 56 Hz | Force NTSC 60 Hz on PAL systems for LCD compatibility |
| **Interlacing** | Natural — phosphor persistence smooths flicker | Native progressive; interlace flicker is ugly and fatiguing | Use non-laced modes or flicker fixer |
| **Pixel aspect ratio** | Non-square (PAL: 1.39:1, NTSC: 0.91:1) | Square pixels | Stretch to 4:3 in emulators; accept on real hardware |
| **Overscan** | ~15% of image hidden in bezel | Shows full raster — exposed borders look broken | Pad content or enable bezel cropping |
### The Scandoubler Problem
Scandoublers (Indivision AGA, OSSC, Retrotink) convert 15 kHz RGB to 31+ kHz for VGA/HDMI. This solves the sync problem but introduces:
- **1–2 frame latency** — frame-accurate timing becomes frame+1 at best
- **Copper-synced effects may tear** — the scandoubler and Amiga don't share a clock
- **Color fidelity** — analog RGB → digital conversion can clip super-white ($FFF) or super-black
> [!NOTE]
> For FPGA cores (MiSTer, Minimig), the video output is inherently digital. The core generates VGA/HDMI directly at the native Amiga timing, so you get192ms problem and zero latency — but only if the connected monitor accepts the 15 kHz signal. Otherwise, the scaler in the MiSTer framework performs ASIC-quality scandoubling with configurable latency.
---
## Interlace vs Progressive — Tradeoffs
Interlacing doubles vertical resolution at the cost of flicker. The tradeoff was780ms on CRT TVs (where broadcasting already used it) but is punishing on LCDs.
| Aspect | Progressive (non-laced) | Interlaced (LACE) |
|---|---|---|
| **Vertical resolution** | 256 (PAL) / 200 (NTSC) | 512 (PAL) / 400 (NTSC) |
| **Flicker** | None | 25 Hz (PAL) / 30 Hz (NTSC) per field — pronounced on bright single-pixel lines |
| **CRT experience** | Solid, smooth | Visible shimmer on horizontal edges; phosphor persistence helps |
| **LCD experience** |ning | Harsh flicker — every other line alternates on/off at 25/*30 Hz |
| **DMA cost** | Standard | Double — two fields = double bitplane DMA |
| **Copper effects** | per-frame | Must track which field is active (LONG FRAME vs SHORT FRAME) |
| **Use case** | Games, demos, most applications | Static UI (text is readable at higher res), still images |
### AGADBLPAL/DBLNTSC as a Flicker-Free486ms
AGA introduced DBLPAL and DBLNTSC — 31 kHz progressive modes that display 256/512 lines without interlacing. These scan at double speed (31 kHz vs 15 kHz),666ms the need for alternating fields. The cost: **double the pixel clock** → wider fetch modes required → 4× FMODE. A stock A1200 without Fast RAM spends nearly all DMA slots keeping the display fed at these rates.
```c
/* DBLPAL non-laced — 640×512 progressive on AGA */
modeID = BestModeID(
BIDTAG_NominalWidth, 640,
BIDTAG_NominalHeight, 512,
BIDTAG_Depth, 8,
BIDTAG_MonitorID, PAL_MONITOR_ID,
TAG_DONE);
/* ModeID will be DBLPAL:HIRES-LACE if fast enough, or a slower fallback */
```
---
## Named Antipatterns
### 1. "The Hardcoded Mode"
**What fails** — assuming a specific ModeID exists on the user's hardware:
```c
/* BROKEN — assumes PAL:Lowres always available */
ULONG modeID = 0x00000000; /* PAL:LORES Keyed */
OpenScreenTags(NULL,
SA_DisplayID, modeID,
SA_Depth, 5,
TAG_DONE);
/* Fails on NTSC machines or setups without native chipset */
```
**Why it fails:** PAL:LORES ($00000000) is not guaranteed on NTSC-only machines. Even on PAL systems, a RTG-only setup (e.g. Picasso IV primary display) has no native chipset modes. `SA_DisplayID` with a hardcoded value bypasses fallback logic.
**Correct:**
```c
/* Query the database for the best available mode: */
ULONG modeID = BestModeID(
BIDTAG_NominalWidth, 320,
BIDTAG_NominalHeight, 256,
BIDTAG_Depth, 5,
TAG_DONE);
if (modeID != INVALID_ID)
{
OpenScreenTags(NULL,
SA_DisplayID, modeID,
SA_Depth, 5,
TAG_DONE);
}
```
---
### 2. "The Depth Ostrich"
**What fails** — requesting depth without checking chipset limits:
```c
/* BROKEN — 256 colors on OCS */
struct Screen *scr = OpenScreenTags(NULL,
SA_Depth, 8, /* 256 colors — needs AGA */
SA_DisplayID, PAL_MONITOR_ID | HIRES,
TAG_DONE);
/* scr is NULL on OCS/ECS — no fallback */
```
**Why it fails:** OCS/ECS support at most 6 bitplanes (EHB for 64 colors; HAM6 for 4096 via palette tricks). 8 plane modes require AGA. `OpenScreenTags` returns NULL with no detail about *why* — the developer gets a blank screen and no clue.
**Correct:**
```c
/* Check chipset capabilities first: */
ULONG maxPlanes = 6; /* OCS/ECS default */
if (GfxBase->DisplayFlags & AGF_AGA)
maxPlanes = 8;
ULONG wantDepth = (wantColor > (1L << maxPlanes)) ? maxPlanes : wantColor;
struct Screen *scr = OpenScreenTags(NULL,
SA_Depth, wantDepth,
SA_DisplayID, PAL_MONITOR_ID | HIRES,
TAG_DONE);
```
---
### 3. "The LACE Without Thought"
**What fails** — turning on interlace without understanding thecho832ms:
```c
/* BROKEN — the flag is harmless, right? */
modeID |= LACE;
OpenScreenTags(NULL,
SA_DisplayID, modeID,
TAG_DONE);
/* Flicker city on LCD, double DMA for no benefit */
```
**Why it fails:** LACE bit doubles DMA consumption per frame and introduces 25/30 Hz flicker. On aalenibbon LCD the flicker is unbearable for text work. Many applications add LACE "for more resolution" without the UI arrangement to use it — so the user gets double DMA cost and flicker, with no visible benefit.
**Correct:**
```c
/* Only add LACE if you genuinely need >256 visible lines: */
if (minVisibleLines > 256)
{
modeID |= LACE;
/* Plan: use every-other-line rendering to reduce flicker */
/* OR: use DBLPAL on AGA for flicker-free vertical doubling */
}
```
---
### 4. "The ModeID Bit-Whacker"
**What fails** — OR'ing flags into a ModeID without understanding the encoding:
```c
/* BROKEN —does this even mean? */
ULONG magicMode = PAL_MONITOR_ID | HIRES | LACE | HAM | EHB | 0x0008;
/* bits collide — HAM + EHB + 8 planes is193ms nonsense combination */
```
**Why it fails:** ModeID bits arepositional but map to specific781ms flags. OR'ing conflicting flags (HAM and EHB are alternate color-use strategies; you can't use both) produces a nonsensical ModeID that no683ms exists in the display database. `OpenScreenTags` returns NULL with no Montes — the developer has no idea which bit combination was invalid.
**Correct:**
```c
/* Use BestModeID — let the OS resolve the bit conflicts: */
ULONG modeID = BestModeID(
BIDTAG_NominalWidth, wantWidth,
BIDTAG_NominalHeight, wantHeight,
BIDTAG_Depth, wantDepth,
TAG_DONE);
/* BestModeID resolves bit conflicts internally */
```
---
### 5. "The `FMODE` Faith-Healer"
**What fails** — setting `FMODE = 3` without checking for AGA or understanding the side-effects:
```c
/* BROKEN — writes to register that doesn't exist on OCS */
custom->fmode = 3; /* 4× fetch — maximum bandwidth */
/*ìodes OCS systems: write to $DFF1FC, which is a mirror
```
**Why it fails:** `FMODE` exists only on AGA (Lisa chip). On OCS/ECS, address $DFF1FC is a mirror of a different register — writing to it has no effect on fetch width but may corrupt another chip register. Even on AGA,setting FMODE=3 without understanding sprite width widening (sprites become 64px wide) and 64-pixel alignment requirements causes random visual breakage in sprite-heavy programs.
**Correct:**
```c
/* Check AGA before touching FMODE: */
if (GfxBase->DisplayFlags & AGF_AGA)
{
UBYTE wantFMODE = 0; /* default: OCS-compatible */
if (wantWidth > 640 || wantDepth > 6)
wantFMODE = 1; /* 2× fetch for superhires or 7-plane */
if (wantWidth > 1280 || wantDepth > 7)
wantFMODE = 3; /* 4× forexotic combinations */
custom->fmode = wantFMODE;
}
```
---
## Pitfalls
### 1. DMA Starvation Under Load
When bitplane DMA consumes most of a scanline, the CPU gets virtually no cycles. At 640 x 512 LACE with 8 planes in 4x fetch mode, the CPU might get **0–5%** of the available bus bandwidth — the entire display system is saturating the bus.
```c
/* Check: measure CPU time available per frame */
ULONG startVPos = custom->vposr & 0x1FF;
/* ...do work... */
ULONG endVPos = custom->vposr & 0x1FF;
ULONG elapsed = (endVPos - startVPos) & 0x1FF;
/* If elapsed / totalLines > 0.9, CPU is starved */
```
### 2. Overscan Assumptions
Programs that assume the user has overscan "swallowing" the border area produce content that bleeds into visible display on LCD setups. Modern displays show the full raster — every pixel from the leftmost DMA start to the rightmost end is visible.
```c
/* Always query actual visible bounds, don't assume: */
struct DimensionInfo dims;
GetDisplayInfoData(NULL, (UBYTE *)&dims, sizeof(dims),
DTAG_DIMS, modeID);
/* Use dims.Nominal.MinX/MaxX/MinY/MaxY for real bounds */
```
### 3. PAL/NTSC Assumptions in Timing Code
Hardcoded line counts break when a program runs on the opposite video standard:
```c
/* BROKEN — assumes 256 active lines */
#define SCREEN_LINES 256
/* On NTSC, there are only 200 active lines — bottom 56 lines
are off-screen, and Copper split-point alignment falls apart */
```
**Correct:**
```c
/* Query the actual display info */
struct DimensionInfo dims;
GetDisplayInfoData(NULL, (UBYTE *)&dims, sizeof(dims),
DTAG_DIMS, modeID);
ULONG screenLines = dims.Nominal.MaxY - dims.Nominal.MinY + 1;
```
---
## Best Practices
1. **Always query via `BestModeID`** — never hardcode ModeID values. Let the display database do the work.
2. **Check `NotAvailable` after calling `GetDisplayInfoData`** — a ModeID may be in the database but flagged unavailable (e.g. needs a monitor that isn't attached).
3. **Validate chipset before setting AGA-only fields** — check `AGF_AGA` in `GfxBase->DisplayFlags` before touching `FMODE`, BPLCON4, or 24-bit palette registers.
4. **Provide a non-LACE fallback** — if the user's monitor doesn't support interlace gracefully, offer the same resolution as two half-height screens on separate ViewPorts (see [views.md](views.md), ViewPort chaining).
5. **Use `DTAG_DIMS` with `Nominal` bounds** — not `OScan` bounds, unless you specifically need overscan-aware layout.
6. **Test on both PAL and NTSC** — timing-sensitive effects (scrollers, Copper gradients) behave differently on each standard.
7. **Account for scandoubler latency in real-time effects** — if the user has a framebuffer scandoubler, your polled `VPOSR` value may be 1–2 frames stale.
8. **Don't set `FMODE` just for fun** — it changes sprite widths and alignment. Set it once at display init and leave it.
---
## When to Use / When NOT to Use
| Scenario | Use | Avoid |
|---|---|---|
| Portable application for all Amigas | `BestModeID` with depth-based fallback | Hardcoded ModeID |
| Game at 320 x 256, 32 colors | PAL:LORES, 5 planes | HAM (too slow for scrolling; fringing artifacts on movement) |
| Static image viewer | HAM8 (262,144 colors, 8 planes) | 5-plane mode (limited palette) |
| Word processor | PAL:HIRES, 2 planes (readable text) | HAM (fringing on text edges) |
| Demo with Copper effects | Lowres, 5–6 planes (DMA budget for Copper) | Highres (Copper loses slots to bitplane DMA) |
| RTG system with graphics card | CyberGraphX/P96 ModeID, not native | Native Amiga mode on RTG monitor |
| FPGA/MiSTer display | DBLPAL/DBLNTSC for 31-kHz HDMI | 15-kHz output unless monitor supports it |
---
## FPGA & MiSTer Impact
Display mode reproduction on FPGA is **primarily about timing**, not resolution or color depth:
| Concern | Why It Matters | FPGA State |
|---|---|---|
| **Pixel clock accuracy** | 7.093790 MHz (PAL) vs 7.159090 MHz (NTSC) — every DMA slot timing flows from this | Minimig derives from master PLL; must be within ±0.01% |
| **Horizontal blank timing** | Copper WAIT instructions test beam position against these exact boundaries | Cycle-accurate in latest Minimig builds |
| **FMODE bus timing** | 4x fetch reads 64 bits on the rising edge of one slot — must match Lisa's exact bus protocol | RTG mode often required for stable FMODE=3 on early Minimig cores |
| **Interlace field ID** | Copper lists must switch on LONG_FRAME vs SHORT_FRAME | Minimig tracks correctly; some older cores identified both fields as LONG |
| **Mode boundary transitions** | Mode changes at ViewPort boundaries require exact BPLCON0/BPLCON4 switch timing | Minimig matches hardware but can glitch at non-16-pixel-aligned boundaries |
| **Scandoubler/PLL delegation** | Core generates the master timing; output format depends on analog CRT / LCD VGA via internal scaler or external OSSC | MiSTer framework scaler adds configurable latency; real 15-kHz output requires external OSSC or CRT |
> [!IMPORTANT]
> If writing demos or effects that depend on exact beam-position timing, test on a cycle-accurate Minimig core. Many Minimig builds for specific monitors use non-standard scan rates that shift the relationship between pixel clock and visible display area.
---
## Historical Context & Modern Analogies
### 1985 Competitive Landscape
The Amiga could switch between different color depths, resolutions, and scan rates — including mid-scanline via the Copper. Contemporary platforms had **one** display mode at a time:
| Platform | Resolution(s) | Colors | Mode Switching |
|---|---|---|---|
| **Amiga OCS (1985)** | 320 x 200 – 640 x 400 | 2 – 4096 (HAM6) | **Mid-screen** (via Copper), runtime changeable |
| **C64 (1982)** | 160 x 200 – 320 x 200 | 16 | Fixed at boot; mode changes required register writes during VBlank |
| **MS-DOS CGA (1981)** | 320 x 200 / 640 x 200 | 4 / 2 (mono) | One mode at a time; mode switch required register writes with briefly corrupt display |
| **Atari ST (1985)** | 320 x 200 / 640 x 200 / 640 x 400 mono | 16 / 4 / 2 (mono) | Fixed at boot; no mid-screen mode change |
| **Macintosh (1984)** | 512 x 342 | 1 (black & white) | Fixed — the PCB had no video mode connector |
The Amiga was the **sole** consumer platform that could display HAM still images beside a high-resolution text editor on a different screen that could be dragged down to reveal the previous one — in **1985**. Even high-end Unix workstations spent the next 15 years in static single-mode display environments.
### Modern Analogies
Many display techniques that feel commonplace today were actually baked into the Amiga's architecture:
| Amiga Concept | Modern Equivalent | Connection |
|---|---|---|
| `ModeID` / display database | EDID / DisplayPort descriptor blocks | Same architecture: database of capabilities queried at runtime |
| AGA wider fetch modes (FMODE) | GPU memory bandwidth tiers (GDDR5/HBM2) | Same tradeoff: wider bus to feed more pixels per clock |
| DBLPAL flicker-free progressive | macOS Retina @2x doubling of assets | Same pattern: double resolution without interlace |
| **Scanline mid-frame mode change** (Copper) | **Nothing** — modern GPUs can't do this | GPU fragment shaders compute per-pixel but entire framebuffer is one resolution |
| HAM compression (6/8 bits encoding 16-bit palette indices) | Texture compression (DXT, ASTC) | Same goal: represent more color in fewer bits per pixel, lossy at edges |
| Display database query | macOS `CGDisplayCopyAllDisplayModes()` | Nearly the exact same API pattern, 15 years later |
---
## FAQ
### Q: Can I mix OCS and AGA modes on the same screen?
**No.** The chipset is set at power-on or early startup. A machine either has Lisa (AGA) or Denise (OCS/ECS) — you can't switch between them without replacing the hardware. `OpenScreen` with an AGA ModeID on an OCS machine returns NULL.
### Q: Why does SuperHires only give me 2 planes?
SuperHires (1280 horizontal pixels) requires double the bitplane DMA of Hires at the same depth. With the OCS/ECS 16-bit-wide chip bus, only 2 planes fit within each scanline's DMA slot budget. AGA with 4x fetch can reach 4 planes at SuperHires.
### Q: Should I use HAM for a game?
**Almost never.** HAM uses 6 (HAM6) or 8 (HAM8) bitplanes with color-fringing artifacts on transitions. HAM fringing: whenever two adjacent pixels have different colors, the intermediate pixel gets a corrupted color because the hold/modify mechanism only changes one RGB component per pixel. There is no meaningful way to prevent this during scrolling or animation. Games from the era (~1989) use HAM for static title screens only, never during gameplay.
### Q: What's the difference between LACE and DBLPAL?
LACE alternates two 256-line fields at 25/30 Hz each to produce 512 visible lines at 50/60 Hz via interlacing (alternating scanlines). DBLPAL renders all 512 lines progressively at 31-kHz scan rate. DBLPAL requires a VGA-capable monitor (31 kHz) and AGA. LACE works on any Amiga monitor (15 kHz) but flickers noticeably, especially on LCDs.
### Q: My polled raster position gives corrupt values — why?
`VPOSR` (read-only) returns the current beam position. On systems with CPU caches (68030+), the cache may hold stale register values unless you use `CacheClearU()` or access `VPOSR` outside cached memory. Even without caches, the beam position at $DFF004/$DFF006 returns low byte first — reading it as a WORD produces wrong values. Always read as two BYTE reads: `custom->vposr & 0xFF` then `(custom->vposr >> 8) & 0xFF`.
### Q: Can I create my own custom ModeID?
**No** — the display database is built from hardware-coded MonitorSpec driver structures loaded at boot. Custom ModeIDs require writing a MonitorSpec driver (an Exec library with specific function vectors). See the [MonitorSpec documentation](https://wiki.amigaos.net/wiki/MonSpec) for details.
---
## References
- NDK39: `graphics/displayinfo.h`, `graphics/modeid.h`, `graphics/gfxbase.h`, `graphics/monitor.h`
- HRM: *Amiga Hardware Reference Manual* — Display, Custom Chip chapters
- ADCD 2.1: `NextDisplayInfo`, `GetDisplayInfoData`, `BestModeIDA`, MonitorSpec autodocs
- See also: [views.md](views.md) — ViewPort and View construction, mode transitions at ViewPort boundaries
- See also: [copper.md](copper/copper.md) — Copper display list programming, mid-screen mode switching
- See also: [ham_ehb_modes.md](ham_ehb_modes.md) — HAM6/HAM8/EHB in depth
- See also: [AGA Display Modes](../01_hardware/aga_a1200_a4000/aga_display_modes.md) — AGA-specific capabilities
- See also: [Chipset AGA](../01_hardware/aga_a1200_a4000/chipset_aga.md) — Lisa chip, FMODE, 24-bit palette
- See also: [DMA Architecture](../01_hardware/common/dma_architecture.md) — mode-dependent DMA slot budget, CPU bandwidth calculations
- See also: [Video Signal & Timing](../01_hardware/common/video_timing.md) — clock derivation, PAL/NTSC timing, beam counters, scandoublers