mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-12 16:16:28 +00:00
Sprites and DMA
This commit is contained in:
parent
f19e3f16b5
commit
a83d89ce22
15 changed files with 1306 additions and 141 deletions
|
|
@ -23,6 +23,7 @@ The **Advanced Graphics Architecture** (AGA) is the final custom chipset develop
|
|||
| [aga_palette.md](aga_palette.md) | 24-bit color system, 256 registers |
|
||||
| [aga_display_modes.md](aga_display_modes.md) | HAM8, 256-color, doublescan, VGA |
|
||||
| [aga_blitter.md](aga_blitter.md) | 64-bit blitter bus, FMODE |
|
||||
| [aga_sprites.md](aga_sprites.md) | AGA sprite enhancements: 32/64px width, FMODE, color bank selection (ESPRM/OSPRM) |
|
||||
| [aga_copper.md](aga_copper.md) | AGA Copper programming guide |
|
||||
| [cpu_030_040.md](cpu_030_040.md) | 68030/040/060: accelerator catalog, benchmarks, named antipatterns (cache/DMA coherency, missing libraries, timing loops, chip bus speed), FPGA impact, FAQ |
|
||||
| [akiko_cd32.md](akiko_cd32.md) | CD32 Akiko chip: C2P conversion, CD-ROM, NVRAM |
|
||||
|
|
|
|||
181
01_hardware/aga_a1200_a4000/aga_sprites.md
Normal file
181
01_hardware/aga_a1200_a4000/aga_sprites.md
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
[← Home](../../README.md) · [Hardware](../README.md) · [AGA](README.md)
|
||||
|
||||
# AGA Sprite Enhancements
|
||||
|
||||
## Overview
|
||||
|
||||
AGA Lisa extends the [ECS sprite hardware](../ecs_a600_a3000/ecs_sprites.md) with substantial changes to width, color depth, and DMA data format. While the fundamental 8-channel architecture and position/control register layout remain OCS-compatible, AGA sprites can be **4× wider** and draw from **any 16-color slice** of the 256-color palette.
|
||||
|
||||
## What Changed from ECS
|
||||
|
||||
| Feature | ECS (Denise 8373) | AGA (Lisa) | Register |
|
||||
|---|---|---|---|
|
||||
| Maximum width | 16 pixels | **16, 32, or 64 pixels** | `FMODE` bits 15-14 |
|
||||
| Color palette | COLOR16–COLOR31 fixed | **Any 16-color bank** of 256 | `BPLCON4` bits 7-0 |
|
||||
| Even/odd bank | Same bank for pair | **Independent** even/odd banks | `BPLCON4` ESPRM/OSPRM |
|
||||
| Scan doubling | Not available | **SSCAN2** for 31kHz displays | `BPLCON0` |
|
||||
| Border sprites | BRDSPRT (inherited) | Same, over 256-color backgrounds | `BPLCON3` bit 3 |
|
||||
|
||||
---
|
||||
|
||||
## FMODE — Sprite Fetch Width ($DFF1FC)
|
||||
|
||||
The `FMODE` register controls DMA fetch width for sprites independently of bitplanes and blitter. Bits 15-14 (`SPR_FMODE`) set the sprite width:
|
||||
|
||||
| SPR_FMODE (bits 15-14) | Sprite Width | Words per Line | DMA Slots per Channel | Alignment |
|
||||
|---|---|---|---|---|
|
||||
| `00` | 16 pixels | 2 (DATA + DATB) | 2 | Word (2-byte) |
|
||||
| `01` | 32 pixels | 4 (2 longwords) | 4 | Long (4-byte) |
|
||||
| `10` | 64 pixels | 8 (4 longwords) | 8 | Quad (8-byte) |
|
||||
| `11` | Reserved | — | — | — |
|
||||
|
||||
```asm
|
||||
; Enable 64-pixel wide sprites
|
||||
move.w #$C000, $DFF1FC ; FMODE: SPR_FMODE = %10 (64px)
|
||||
; (preserves BPL_FMODE and BLT_FMODE at 1×)
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> `SPR_FMODE` is a **global setting** — all 8 sprite channels use the same width. You cannot mix 16px and 64px sprites in the same display. The Copper can change FMODE mid-frame but this requires careful DMA timing.
|
||||
|
||||
### DMA Data Format Changes
|
||||
|
||||
At wider FMODE settings, the sprite data block in memory grows proportionally:
|
||||
|
||||
**16px (SPR_FMODE = 00) — OCS-compatible:**
|
||||
```
|
||||
POS word | CTL word
|
||||
DATA_A | DATB_A ← 1 pair per line (2 words)
|
||||
...
|
||||
$0000 | $0000 ← terminator
|
||||
```
|
||||
|
||||
**32px (SPR_FMODE = 01):**
|
||||
```
|
||||
POS word | CTL word
|
||||
DATA_A0 | DATA_A1 | DATB_A0 | DATB_A1 ← 2 pairs per line (4 words)
|
||||
...
|
||||
$0000 | $0000 ← terminator
|
||||
```
|
||||
|
||||
**64px (SPR_FMODE = 10):**
|
||||
```
|
||||
POS word | CTL word
|
||||
DATA_A0 | DATA_A1 | DATA_A2 | DATA_A3 |
|
||||
DATB_A0 | DATB_A1 | DATB_A2 | DATB_A3 ← 4 pairs per line (8 words)
|
||||
...
|
||||
$0000 | $0000 ← terminator
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> Sprite data pointers (`SPRxPTH/L`) must be aligned to the fetch width boundary. Misaligned pointers produce **corrupted sprite data** — the DMA engine reads incorrect word boundaries.
|
||||
>
|
||||
> | SPR_FMODE | Required Pointer Alignment |
|
||||
> |---|---|
|
||||
> | 1× (16px) | 2-byte (word) |
|
||||
> | 2× (32px) | 4-byte (longword) |
|
||||
> | 4× (64px) | 8-byte (quadword) |
|
||||
|
||||
### DMA Budget Impact
|
||||
|
||||
Wider sprites consume proportionally more DMA bandwidth:
|
||||
|
||||
| SPR_FMODE | DMA Slots (all 8 channels) | Bus Budget Impact |
|
||||
|---|---|---|
|
||||
| 1× (16px) | 16 slots/line | ~7% of scanline |
|
||||
| 2× (32px) | 32 slots/line | ~14% of scanline |
|
||||
| 4× (64px) | 64 slots/line | ~28% of scanline |
|
||||
|
||||
At 4× mode, sprite DMA alone consumes nearly a third of available bus bandwidth, leaving significantly less for bitplanes, blitter, and CPU.
|
||||
|
||||
---
|
||||
|
||||
## BPLCON4 — Sprite Color Bank Selection ($DFF10C)
|
||||
|
||||
AGA separates sprite color bank selection for even and odd channels, allowing each half of a sprite pair to draw from a different 16-color slice of the 256-color palette:
|
||||
|
||||
```
|
||||
bits 7-4: ESPRM7:4 Even sprite color bank (upper nibble of color index)
|
||||
bits 3-0: OSPRM7:4 Odd sprite color bank (upper nibble of color index)
|
||||
```
|
||||
|
||||
### Color Index Calculation
|
||||
|
||||
The final 8-bit color register index for a sprite pixel is:
|
||||
|
||||
```
|
||||
Even sprite: color_index = {ESPRM[7:4], sprite_pixel[3:0]}
|
||||
Odd sprite: color_index = {OSPRM[7:4], sprite_pixel[3:0]}
|
||||
```
|
||||
|
||||
| ESPRM/OSPRM Value | Color Registers Used | Palette Slice |
|
||||
|---|---|---|
|
||||
| `$0` | COLOR0–COLOR15 | Shares with bitplane colors! |
|
||||
| `$1` | COLOR16–COLOR31 | OCS-compatible default |
|
||||
| `$2` | COLOR32–COLOR47 | — |
|
||||
| ... | ... | ... |
|
||||
| `$F` | COLOR240–COLOR255 | — |
|
||||
|
||||
```asm
|
||||
; Even sprites use colors 64-79, odd sprites use colors 128-143
|
||||
move.w #$0048, $DFF10C ; BPLCON4: ESPRM = $4, OSPRM = $8
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Setting `ESPRM` or `OSPRM` to `$0` causes sprite colors to overlap with **bitplane palette entries** (COLOR0–COLOR15). This is rarely intentional and can produce confusing visual artifacts.
|
||||
|
||||
### Attached Sprite Color Banks
|
||||
|
||||
In attached mode (15-color pairs), the 4-bit pixel index uses the **odd** channel's `OSPRM` value as the color bank, since the combined pixel index spans 0-15 within that bank.
|
||||
|
||||
---
|
||||
|
||||
## SSCAN2 — Sprite Scan Doubling
|
||||
|
||||
On AGA machines using 31kHz (VGA-compatible) display modes, the `SSCAN2` mechanism doubles each sprite scanline vertically to maintain correct aspect ratio. This is handled automatically by the display system when using `graphics.library` screen modes — direct hardware programming requires manual configuration.
|
||||
|
||||
---
|
||||
|
||||
## Programming Example
|
||||
|
||||
Complete example: 64px wide sprite using color bank 3 (COLOR48–COLOR63):
|
||||
|
||||
```asm
|
||||
; Prerequisites: AGA machine, system taken over, custom registers accessible
|
||||
|
||||
; 1. Set sprite fetch mode to 64px
|
||||
move.w #$C000, $DFF1FC ; FMODE: SPR_FMODE = 10 (64px)
|
||||
|
||||
; 2. Set color bank for sprite 0 (even channel)
|
||||
move.w #$0031, $DFF10C ; BPLCON4: ESPRM = $3, OSPRM = $1
|
||||
|
||||
; 3. Point sprite 0 to quad-word aligned data in Chip RAM
|
||||
move.l #SpriteData, d0
|
||||
move.w d0, SPR0PTL+$DFF000
|
||||
swap d0
|
||||
move.w d0, SPR0PTH+$DFF000
|
||||
|
||||
; 4. Enable sprite DMA
|
||||
move.w #$8020, $DFF096 ; DMACON: SET + SPREN
|
||||
|
||||
; ... SpriteData must be 8-byte aligned and use 64px data format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [OCS Sprite Hardware](../ocs_a500/sprites.md) — base register reference (SPRxPOS/CTL/DATA, color mapping, position formula)
|
||||
- [ECS Sprite Enhancements](../ecs_a600_a3000/ecs_sprites.md) — border sprites, independent resolution (SPRES)
|
||||
- [Sprites — Graphics Programming Guide](../../08_graphics/sprites.md) — OS API, techniques, decision guides, antipatterns
|
||||
- [AGA Chipset Internals](chipset_aga.md) — Alice/Lisa overview, FMODE architecture
|
||||
- [AGA Register Deltas](aga_registers_delta.md) — complete BPLCON4 definition
|
||||
- [DMA Architecture](../common/dma_architecture.md) — FMODE bandwidth impact, sprite DMA slot budget, bus arbitration
|
||||
- [AGA Palette](aga_palette.md) — 256-register 24-bit color system
|
||||
|
||||
## References
|
||||
|
||||
- *Amiga Hardware Reference Manual* 3rd ed. — AGA sprite extensions
|
||||
- ADCD 2.1 Hardware Manual — AGA sprite chapter
|
||||
- NDK39: `hardware/custom.h` — FMODE, BPLCON4 definitions
|
||||
- Commodore A1200 Technical Reference Manual — Lisa sprite section
|
||||
|
|
@ -124,3 +124,4 @@ move.w #$9411, BPLCON0+custom ; HIRES=1 (if needed), BPU=8 (BPU3=1, BPU2-0=000
|
|||
- [Akiko — CD32 Custom Chip](akiko_cd32.md) — CD32-exclusive ASIC (C2P, CD-ROM, NVRAM) that sits alongside Alice/Lisa/Paula
|
||||
- [AGA Blitter](aga_blitter.md) — 64-bit FMODE blitter details
|
||||
- [AGA Palette](aga_palette.md) — 256-register 24-bit color system
|
||||
- [DMA Architecture](../common/dma_architecture.md) — scanline slot allocation, FMODE bandwidth impact, bus arbitration
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@
|
|||
- [cia_chips.md](cia_chips.md) — Complex Interface Adapters (8520): Timers, Parallel, Serial
|
||||
- [gayle_ide_pcmcia.md](gayle_ide_pcmcia.md) — Gayle system controller: IDE and PCMCIA logic
|
||||
- [floppy_hardware.md](floppy_hardware.md) — MFM encoding, disk controller, and drive mechanics
|
||||
- [dma_architecture.md](dma_architecture.md) — DMA architecture: scanline slot allocation, bus arbitration, even/odd interleaving, per-model bus widths, Buster/Ramsey/SDMAC, FMODE bandwidth, FPGA timing
|
||||
|
|
|
|||
581
01_hardware/common/dma_architecture.md
Normal file
581
01_hardware/common/dma_architecture.md
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
[← Home](../../README.md) · [Hardware](../README.md) · [Common](README.md)
|
||||
|
||||
# DMA Architecture — Scanline Slot Allocation, Bus Arbitration, and Bandwidth
|
||||
|
||||
## Why It Matters
|
||||
|
||||
Every pixel displayed, every audio sample played, every Copper instruction executed, and every Blitter copy performed is a **bus transaction** competing for the same 280 ns memory-access windows on the Chip RAM bus. The Amiga's custom chipset doesn't use interrupts or request queues to share memory — it uses a **fixed time-division schedule** where Agnus (the DMA controller) assigns each color-clock cycle of every scanline to a specific hardware channel. The CPU gets whatever is left over.
|
||||
|
||||
This architecture means that **your choice of display mode directly determines how fast your code runs**. A 320×256 lores display with 4 bitplanes leaves the 68000 roughly 53% of the bus; switching to hires 640×256 with 4 bitplanes drops it to ~17%. Understanding DMA slot allocation isn't optional knowledge — it's the single most important performance variable on every Amiga from the A1000 to the CD32.
|
||||
|
||||
> [!NOTE]
|
||||
> **Modern analogy:** Think of DMA slot allocation as **GPU command buffer scheduling** crossed with **PCIe bus arbitration**. The Chip RAM bus is like a PCIe lane shared between the GPU (custom chips) and CPU, with Agnus acting as the bus arbiter that guarantees real-time display and audio never drop frames — at the expense of CPU throughput.
|
||||
|
||||
---
|
||||
|
||||
## The Scanline Budget — 227.5 Color Clocks
|
||||
|
||||
### Color Clock Fundamentals
|
||||
|
||||
The entire Amiga chipset is synchronized to a master clock derived from the video crystal:
|
||||
|
||||
| Parameter | PAL | NTSC |
|
||||
|---|---|---|
|
||||
| Master clock | 28.37516 MHz | 28.63636 MHz |
|
||||
| Color clock (÷8) | 3,546,895 Hz | 3,579,545 Hz |
|
||||
| Color clock period | ~282 ns | ~279 ns |
|
||||
| Color clocks per line | 227.5 (alternating 227/228) | 227.5 (alternating 227/228) |
|
||||
| Lines per frame | 312.5 (625 interlaced) | 262.5 (525 interlaced) |
|
||||
| Frame rate | 50 Hz | ~59.94 Hz |
|
||||
|
||||
> **A DMA slot** is a single color-clock cycle (~280 ns). During each slot, one 16-bit word can be transferred between Chip RAM and a custom chip register. This is the fundamental quantum of all Amiga DMA — when this article says "sprites use 16 slots," it means 16 of these 280 ns windows are reserved for sprite data fetches each scanline.
|
||||
|
||||
### Scanline Anatomy
|
||||
|
||||
```
|
||||
Color Clock Position (approximate):
|
||||
0 28 53 113 196 212 227
|
||||
├──────────┼─────┼──────────┼──────────────────────────────┼────┼──────┤
|
||||
│ HSYNC │RefDk│ Sprite │ Active Display │Spr │HBlnk │
|
||||
│ blanking│Audio│ fetch │ (Bitplane DMA here) │tail│ │
|
||||
│ (~28 cc)│ │ │ DDFSTRT → DDFSTOP │ │ │
|
||||
└──────────┴─────┴──────────┴──────────────────────────────┴────┴──────┘
|
||||
```
|
||||
|
||||
Of the 227.5 total color clocks:
|
||||
- **~1.5 clocks** are consumed by sync/blanking overhead
|
||||
- **~226 usable clocks** remain for DMA allocation
|
||||
- These 226 slots are divided between **fixed** allocations (always reserved) and **demand-driven** allocations (used only when the channel is active)
|
||||
|
||||
### Even/Odd Cycle Interleaving
|
||||
|
||||
The 68000 CPU requires **4 master clocks** (= 2 color clocks) for a single memory access. Agnus exploits this by interleaving access:
|
||||
|
||||
- **Odd color clocks** → reserved for custom chip DMA (if requested)
|
||||
- **Even color clocks** → available to the CPU
|
||||
|
||||
This interleaving means the CPU appears to run at full speed on a stock A500 with light DMA — the 68000 naturally "fits" into the even slots. Contention only occurs when **high-priority DMA channels steal even slots** (which happens with 5+ bitplanes or hires modes).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The even/odd interleaving applies only to **Chip RAM access**. When the CPU executes code from **Fast RAM**, it runs on a completely separate bus with zero contention — the custom chips and CPU operate in true parallel. This is why adding Fast RAM is the single biggest performance upgrade for any Amiga.
|
||||
|
||||
---
|
||||
|
||||
## DMA Channel Allocation
|
||||
|
||||
### Fixed-Position Channels
|
||||
|
||||
These channels have **guaranteed, non-negotiable** slot assignments. They always consume their slots, even if the channel is disabled in DMACON — the slots simply go unused rather than being released to the CPU.
|
||||
|
||||
| Channel | Slots/Line | Position | DMACON Bit | Notes |
|
||||
|---|---|---|---|---|
|
||||
| **Memory Refresh** | 4 | Fixed (early blanking) | — (always active) | DRAM refresh; non-negotiable hardware requirement |
|
||||
| **Disk** | 3 | Fixed (after refresh) | Bit 4 (`DSKEN`) | Reserved even when disk motor is off |
|
||||
| **Audio** (4 ch) | 4 | Fixed (after disk) | Bits 0–3 (`AUD0EN`–`AUD3EN`) | One slot per channel; Paula reads sample words |
|
||||
| **Sprites** (8 ch) | 16 | Fixed (before/after display) | Bit 5 (`SPREN`) | 2 words × 8 channels (DATA + DATB) |
|
||||
|
||||
**Total fixed overhead: 27 slots** — always consumed regardless of display configuration.
|
||||
|
||||
### Display-Dependent Channels
|
||||
|
||||
| Channel | Slots/Line | Position | Control | Notes |
|
||||
|---|---|---|---|---|
|
||||
| **Bitplane DMA** | 0–160+ | Within DDFSTRT–DDFSTOP window | Bit 8 (`BPLEN`) + `BPLCONx` | Varies by resolution and depth |
|
||||
|
||||
### Demand-Driven Channels
|
||||
|
||||
| Channel | Slots/Line | Position | Control | Notes |
|
||||
|---|---|---|---|---|
|
||||
| **Copper** | Variable | Any free slot | Bit 7 (`COPEN`) | Takes one free slot per instruction word |
|
||||
| **Blitter** | Variable | Any free slot | Bit 6 (`BLTEN`) | Bulk consumer; 1 slot per channel per word |
|
||||
| **CPU** | Remainder | Whatever Agnus doesn't claim | — | Lowest priority on the chip bus |
|
||||
|
||||
### Priority Hierarchy
|
||||
|
||||
When multiple channels want the same slot, Agnus resolves by fixed priority:
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ 1. Memory Refresh │ ← Highest: silicon-level, non-maskable
|
||||
│ 2. Disk DMA │
|
||||
│ 3. Audio DMA │
|
||||
│ 4. Bitplane DMA │
|
||||
│ 5. Sprite DMA │
|
||||
│ 6. Copper │
|
||||
│ 7. Blitter │ ← Can be promoted with BLTPRI ("Nasty")
|
||||
│ 8. CPU │ ← Lowest: gets leftover slots
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> This priority order means the **display never glitches** — bitplane and sprite DMA always get their slots. But it also means heavy display modes can **starve the CPU entirely**, leaving zero slots for code execution from Chip RAM. See the bandwidth table below.
|
||||
|
||||
---
|
||||
|
||||
## Bitplane DMA — The Bandwidth Equation
|
||||
|
||||
### How Bitplane DMA Works
|
||||
|
||||
Agnus fetches bitplane data during the **display data fetch window**, defined by two registers:
|
||||
|
||||
| Register | Address | Function |
|
||||
|---|---|---|
|
||||
| `DDFSTRT` | `$DFF092` | Display Data Fetch Start — first color clock where Agnus begins fetching bitplane data |
|
||||
| `DDFSTOP` | `$DFF094` | Display Data Fetch Stop — last color clock for bitplane fetch |
|
||||
| `DIWSTRT` | `$DFF08E` | Display Window Start — where Denise/Lisa **begins showing pixels** |
|
||||
| `DIWSTOP` | `$DFF090` | Display Window Stop — where Denise/Lisa **stops showing pixels** |
|
||||
| `BPL1MOD` | `$DFF108` | Odd-plane modulo — bytes to skip at end of each row |
|
||||
| `BPL2MOD` | `$DFF10A` | Even-plane modulo — bytes to skip at end of each row |
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Critical distinction:** `DDFSTRT`/`DDFSTOP` control **when Agnus fetches data** (DMA). `DIWSTRT`/`DIWSTOP` control **what Denise displays** (video output). They are independent — you can fetch more data than you display (for smooth scrolling) or display less than you fetch (for windowed views). Confusing these two register pairs is a common source of display bugs.
|
||||
|
||||
### Standard DDFSTRT/DDFSTOP Values
|
||||
|
||||
| Mode | DDFSTRT | DDFSTOP | Fetch Width | Fetches/Line |
|
||||
|---|---|---|---|---|
|
||||
| Lores (320px) | `$0038` | `$00D0` | 160 clocks | 20 words/plane |
|
||||
| Hires (640px) | `$003C` | `$00D4` | 160 clocks | 40 words/plane |
|
||||
| SuperHires (1280px) | `$003C` | `$00D4` | 160 clocks | 80 words/plane |
|
||||
|
||||
### Bitplane DMA Slots Per Mode
|
||||
|
||||
Each bitplane requires one word fetch per 16 pixels. The total DMA cost depends on resolution × depth:
|
||||
|
||||
| Display Mode | Planes | DMA Slots/Line | Fixed DMA (27) | **CPU Slots Left** | **CPU Bandwidth** |
|
||||
|---|---|---|---|---|---|
|
||||
| Lores 320px, 1 plane | 1 | 20 | 27 | **179** | **79%** |
|
||||
| Lores 320px, 2 planes | 2 | 40 | 27 | **159** | **70%** |
|
||||
| Lores 320px, 3 planes | 3 | 60 | 27 | **139** | **62%** |
|
||||
| Lores 320px, 4 planes | 4 | 80 | 27 | **119** | **53%** |
|
||||
| Lores 320px, 5 planes | 5 | 100 | 27 | **99** | **44%** |
|
||||
| Lores 320px, 6 planes (EHB/HAM) | 6 | 120 | 27 | **79** | **35%** |
|
||||
| Hires 640px, 2 planes | 2 | 80 | 27 | **119** | **53%** |
|
||||
| Hires 640px, 4 planes | 4 | 160 | 27 | **39** | **17%** |
|
||||
| SuperHires 1280px, 2 planes | 2 | 160 | 27 | **39** | **17%** |
|
||||
| **AGA** Lores 8 planes (FMODE=0) | 8 | 160 | 27 | **39** | **17%** |
|
||||
| **AGA** Lores 8 planes (FMODE=2×) | 8 | 80 | 27 | **119** | **53%** |
|
||||
| **AGA** Lores 8 planes (FMODE=4×) | 8 | 40 | 27 | **159** | **70%** |
|
||||
|
||||
> [!CAUTION]
|
||||
> **Hires 4-plane is a CPU killer.** At 160 DMA slots for bitplanes + 27 fixed = 187 slots consumed. Only 39 slots remain for CPU + Copper + Blitter. If the Blitter is running, the CPU may get **zero** Chip RAM access for entire scanlines. This is why OCS/ECS hires is practically limited to 4 colors (2 planes) for games.
|
||||
|
||||
### Why AGA FMODE Changes Everything
|
||||
|
||||
AGA's `FMODE` register (see [chipset_aga.md](../aga_a1200_a4000/chipset_aga.md)) allows Alice to fetch **2 or 4 words per DMA slot** by widening the internal data bus:
|
||||
|
||||
| FMODE | Fetch Width | Effect on Bitplane DMA | Effect on CPU |
|
||||
|---|---|---|---|
|
||||
| 0 (1×) | 16 bits | Same as OCS/ECS | Same as OCS/ECS |
|
||||
| 1 (2×) | 32 bits | Half the slots for same data | ~2× more CPU time |
|
||||
| 2 (4×) | 64 bits | Quarter the slots for same data | ~4× more CPU time |
|
||||
|
||||
This is why AGA machines can display 8 bitplanes (256 colors) without killing the CPU — 4× fetch mode reduces the DMA slot consumption to OCS 2-plane levels.
|
||||
|
||||
---
|
||||
|
||||
## Bus Arbitration Mechanics
|
||||
|
||||
### Agnus as Bus Master
|
||||
|
||||
Unlike modern systems where the CPU is the bus master and peripherals request access, the Amiga inverts the hierarchy: **Agnus owns the Chip RAM bus**. The 68000 CPU is a *client* that receives leftover bus cycles.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Agnus as Agnus (Bus Master)
|
||||
participant ChipRAM as Chip RAM
|
||||
participant CPU as 68000 CPU
|
||||
participant FastRAM as Fast RAM
|
||||
|
||||
Note over Agnus,ChipRAM: Odd color clock (DMA slot)
|
||||
Agnus->>ChipRAM: Bitplane fetch (word read)
|
||||
|
||||
Note over CPU,ChipRAM: Even color clock (CPU slot)
|
||||
CPU->>ChipRAM: Instruction fetch (if code in Chip RAM)
|
||||
|
||||
Note over Agnus,ChipRAM: Odd color clock (DMA slot)
|
||||
Agnus->>ChipRAM: Sprite fetch
|
||||
|
||||
Note over CPU,FastRAM: Even color clock
|
||||
CPU->>FastRAM: Instruction fetch (zero contention)
|
||||
```
|
||||
|
||||
### Contention and Wait States
|
||||
|
||||
When the CPU needs Chip RAM on a cycle already claimed by DMA:
|
||||
|
||||
1. CPU asserts address on the bus
|
||||
2. Agnus detects conflict and **holds the CPU's DTACK line** (data acknowledge)
|
||||
3. CPU enters **wait state** — it freezes, burning clock cycles doing nothing
|
||||
4. When the DMA slot completes, Agnus releases DTACK
|
||||
5. CPU completes its access on the next available slot
|
||||
|
||||
On a stock A500 with no Fast RAM, **every** CPU instruction accesses Chip RAM (for both instruction fetch and data). Heavy DMA can stall the CPU for multiple consecutive cycles, creating visible performance drops that scale linearly with display complexity.
|
||||
|
||||
### The Fast RAM Escape
|
||||
|
||||
When the CPU has Fast RAM available:
|
||||
|
||||
| CPU Activity | Memory Bus | Contention? |
|
||||
|---|---|---|
|
||||
| Execute code from Fast RAM | Fast RAM bus | **None** — full speed |
|
||||
| Read/write data in Fast RAM | Fast RAM bus | **None** — full speed |
|
||||
| Read/write Chip RAM (DMA buffers) | Chip RAM bus | **Yes** — competes with DMA |
|
||||
| Access custom chip registers ($DFFxxx) | Chip RAM bus | **Yes** — register space is on chip bus |
|
||||
|
||||
This is why a 68030 accelerator with 8 MB Fast RAM feels dramatically faster even though the display hardware is identical: the CPU runs from its own private bus while Agnus simultaneously feeds the display from Chip RAM. The only contention occurs when the CPU explicitly touches Chip RAM (e.g., writing to a screen buffer) or custom chip registers.
|
||||
|
||||
> **Optimization insight:** Game loops that keep code + data in Fast RAM and only touch Chip RAM for DMA buffer setup (sprite pointers, Copper list writes) can achieve near-100% CPU utilization regardless of display mode. The Blitter handles Chip RAM operations in parallel.
|
||||
|
||||
---
|
||||
|
||||
## Blitter-Nasty and CPU Starvation
|
||||
|
||||
### The BLTPRI Bit (DMACON Bit 10)
|
||||
|
||||
The Blitter normally operates at **low priority** — below bitplane, sprite, Copper, and audio DMA. When the Blitter needs a bus cycle and the CPU also wants one, the default behavior is:
|
||||
|
||||
- Blitter gets **3 consecutive slots**, then yields **1 slot** to the CPU
|
||||
- This 3:1 ratio ensures the CPU isn't completely locked out during large blits
|
||||
|
||||
Setting `BLTPRI` (the "**Nasty**" bit) in DMACON **promotes the Blitter above the CPU**:
|
||||
|
||||
```asm
|
||||
; Enable Blitter-Nasty
|
||||
move.w #$8400, $DFF096 ; DMACON: set BLTPRI (bit 10)
|
||||
|
||||
; Disable Blitter-Nasty (restore normal priority)
|
||||
move.w #$0400, $DFF096 ; DMACON: clear BLTPRI
|
||||
```
|
||||
|
||||
With Nasty set:
|
||||
- Blitter holds the bus **continuously** until its operation completes
|
||||
- CPU is **completely locked out** of Chip RAM — it cannot fetch instructions, read data, or write to DMA buffers
|
||||
- CPU continues to execute from Fast RAM (if available) but any Chip RAM access stalls indefinitely until the blit finishes
|
||||
|
||||
### OwnBlitter / DisownBlitter
|
||||
|
||||
AmigaOS provides mutual-exclusion wrappers for safe Blitter access:
|
||||
|
||||
```c
|
||||
OwnBlitter(); /* Acquire exclusive Blitter access */
|
||||
/* Set up and start your blit */
|
||||
WaitBlit(); /* Wait for completion */
|
||||
DisownBlitter(); /* Release for other tasks */
|
||||
```
|
||||
|
||||
These calls are **mandatory** for OS-friendly programs. Without them, the OS graphics library and your program can issue simultaneous conflicting blits, corrupting both outputs. Direct hardware-banging games that take over the machine still benefit from `OwnBlitter()` to prevent interrupt-driven system code from touching the Blitter during gameplay.
|
||||
|
||||
### Antipattern: "The Nasty Lockout"
|
||||
|
||||
```c
|
||||
/* ANTIPATTERN — Blitter-Nasty during audio playback */
|
||||
custom->dmacon = 0x8400; /* Set BLTPRI */
|
||||
/* Start a large blit: 320×256 full-screen copy, 4 planes */
|
||||
/* Duration: ~15ms at 16-bit bus width */
|
||||
|
||||
/* PROBLEM: During those 15ms, the CPU cannot service audio interrupts.
|
||||
Paula still DMA-feeds audio samples, but if the audio interrupt
|
||||
handler (running from Chip RAM) needs to update Paula registers
|
||||
for the next sample buffer, it can't execute. Result: audio clicks,
|
||||
pops, or silence gaps. */
|
||||
|
||||
/* CORRECT — Use Blitter-Nasty only for small, fast blits */
|
||||
custom->dmacon = 0x8400; /* Nasty ON */
|
||||
/* Small blit: 16×16 sprite stamp — completes in ~50µs */
|
||||
WaitBlit();
|
||||
custom->dmacon = 0x0400; /* Nasty OFF immediately */
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> **Never leave BLTPRI set across frame boundaries.** A Blitter-Nasty blit that spans the vertical blank period blocks *all* VBlank interrupt processing — CIA timer updates, keyboard scanning, serial I/O, and disk motor control all freeze.
|
||||
|
||||
---
|
||||
|
||||
## Per-Model Bus Architecture
|
||||
|
||||
The memory bus architecture evolved significantly across Amiga models, but the **chipset DMA mechanism remained fundamentally the same** — Agnus/Alice always controls the Chip RAM bus using the same slot-allocation scheme.
|
||||
|
||||
### Model Comparison
|
||||
|
||||
| Model | Year | DMA Controller | Chip Bus Width | Chip RAM Max | Expansion Bus | Glue/Bridge Chips |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **A1000** | 1985 | Agnus (8367) | 16-bit | 512 KB | Side expansion | — |
|
||||
| **A500** | 1987 | Agnus / Fat Agnus (8370/8371) | 16-bit | 512 KB / 1 MB | Zorro II (via sidecar) | — |
|
||||
| **A2000** | 1987 | Fat Agnus → Super Agnus | 16-bit | 1 MB → 2 MB | Zorro II (5 slots) | **Buster** |
|
||||
| **A3000** | 1990 | Super Agnus (8372B) | 16-bit chip + 32-bit fast | 2 MB | Zorro III (4 slots) | **Super Buster** + **Ramsey** + **SDMAC** |
|
||||
| **CDTV** | 1991 | Fat Agnus (8372A) | 16-bit | 1 MB | Internal | **DMAC** (CD-ROM) |
|
||||
| **A600** | 1992 | Super Agnus (8375) | 16-bit | 1 MB (2 MB upgradeable) | PCMCIA | **Gayle** |
|
||||
| **A1200** | 1992 | Alice (8374) | 32-bit (AGA internal) | 2 MB | Trapdoor (clock port) | **Gayle** |
|
||||
| **A4000** | 1992 | Alice (8374) | 32-bit (AGA internal) | 2 MB | Zorro III (5 slots) | **Super Buster** + **Ramsey** + **SDMAC** |
|
||||
| **CD32** | 1993 | Alice (8374) | 32-bit (AGA internal) | 2 MB | FMV slot | **Akiko** |
|
||||
|
||||
### Key Architectural Transitions
|
||||
|
||||
**A500/A2000 — Pure 16-bit:**
|
||||
The simplest bus architecture. Agnus directly drives the Chip RAM address/data bus. The CPU connects to the same bus through Agnus's bus arbitration. No separate Fast RAM bus exists on the A500 motherboard (expansion-only).
|
||||
|
||||
**A3000 — Dual-Bus Architecture:**
|
||||
First Amiga with a **separate 32-bit Fast RAM bus** managed by Ramsey, alongside the 16-bit Chip RAM bus managed by Super Agnus. The CPU can access either bus, but never both simultaneously. This architecture introduced:
|
||||
|
||||
- **Ramsey** — DRAM controller for the 32-bit Fast RAM bus; generates refresh, provides timing for Zorro III DMA
|
||||
- **Super Buster** — Zorro III bus controller; handles bus arbitration for expansion cards performing DMA. Revision history:
|
||||
- Rev 7 — original; known DMA timing bugs
|
||||
- Rev 9 — fixed some bugs, introduced new ones (DMA instability with some SCSI cards)
|
||||
- Rev 11 — production-stable; required for reliable Zorro III DMA
|
||||
- **SDMAC** — SCSI DMA controller; offloads WD33C93 SCSI chip transfers from the CPU, moving data directly between SCSI bus and Fast RAM via Ramsey
|
||||
|
||||
**A1200 — AGA Without Expansion:**
|
||||
Alice's 32-bit internal data bus connects to 2 MB Chip RAM through two 16-bit DRAM chips. The wider internal bus enables FMODE 2×/4× fetches. However, **the A1200 has no Zorro bus** — expansion is via the trapdoor slot (directly on the CPU bus) or PCMCIA via Gayle.
|
||||
|
||||
**A4000 — AGA + Zorro III:**
|
||||
Combines Alice's 32-bit AGA bus with the A3000's Zorro III expansion architecture. Same Ramsey + Super Buster + SDMAC glue as the A3000, but with Alice instead of Super Agnus for the chipset DMA.
|
||||
|
||||
### Glue Chip Summary
|
||||
|
||||
| Chip | Found In | Role in DMA |
|
||||
|---|---|---|
|
||||
| **Buster** | A2000 | Zorro II bus arbiter; manages bus grant/request for expansion DMA |
|
||||
| **Super Buster** | A3000, A4000 | Zorro III bus arbiter; 32-bit DMA bus mastering support |
|
||||
| **Ramsey** | A3000, A4000 | Fast RAM DRAM controller; generates addresses for SDMAC and Zorro III DMA |
|
||||
| **SDMAC** | A3000, A4000 | SCSI DMA; transfers between WD33C93 SCSI and Fast RAM without CPU involvement |
|
||||
| **Gayle** | A600, A1200 | IDE controller + PCMCIA bridge; no Zorro bus functionality |
|
||||
| **Akiko** | CD32 | Chunky-to-planar converter, CD-ROM controller, NVRAM; see [akiko_cd32.md](../aga_a1200_a4000/akiko_cd32.md) |
|
||||
| **Gary / Fat Gary** | A500/A2000 / A3000 | Address decode, ROM overlay, bus timeout; see [gary_system_controller.md](../ocs_a500/gary_system_controller.md) |
|
||||
|
||||
> [!NOTE]
|
||||
> For deep coverage of Gary/Fat Gary's role in address decoding and bus timeout, see the dedicated [Gary System Controller](../ocs_a500/gary_system_controller.md) article. The Buster, Ramsey, and SDMAC chips are candidates for future dedicated articles as the documentation suite expands.
|
||||
|
||||
---
|
||||
|
||||
## AGA FMODE — Wider Fetches, New Trade-offs
|
||||
|
||||
### The FMODE Register ($DFF1FC)
|
||||
|
||||
AGA's most transformative register changes the data bus width for DMA fetches. See [chipset_aga.md](../aga_a1200_a4000/chipset_aga.md) for register-level detail. Here we focus on the **DMA bandwidth implications**:
|
||||
|
||||
```
|
||||
FMODE ($DFF1FC) — write-only on AGA
|
||||
bits 15-14: SSCAN2-1 — sprite scan-doubling (AGA)
|
||||
bits 13-12: reserved
|
||||
bits 3- 2: SPR_FMODE — sprite fetch width (00=16-bit, 11=64-bit)
|
||||
bits 1- 0: BPL_FMODE — bitplane fetch width (00=16-bit, 11=64-bit)
|
||||
```
|
||||
|
||||
### Bandwidth Impact Table
|
||||
|
||||
| FMODE (BPL) | Fetch Width | Words/Slot | 6-Plane Lores Slots | 8-Plane Lores Slots | Blitter Throughput |
|
||||
|---|---|---|---|---|---|
|
||||
| 0 (1×) | 16-bit | 1 | 120 | 160 | 1× (OCS parity) |
|
||||
| 1 (2×) | 32-bit | 2 | 60 | 80 | 2× |
|
||||
| 3 (4×) | 64-bit | 4 | 30 | 40 | 4× |
|
||||
|
||||
At FMODE=3 (4×), an 8-bitplane 256-color lores display consumes only **40 DMA slots** — the same bus cost as a 2-bitplane OCS display. This is AGA's core performance advantage: **same visual quality, dramatically lower bus cost**.
|
||||
|
||||
### Alignment Requirements
|
||||
|
||||
Wider fetches impose stricter memory alignment:
|
||||
|
||||
| FMODE | Alignment Requirement | Consequence of Misalignment |
|
||||
|---|---|---|
|
||||
| 0 (1×) | Word-aligned (2 bytes) | Standard OCS/ECS requirement |
|
||||
| 1 (2×) | Longword-aligned (4 bytes) | Display shifts by up to 16 pixels |
|
||||
| 3 (4×) | 8-byte aligned | Display shifts by up to 32 pixels; hardware may fetch garbage |
|
||||
|
||||
```c
|
||||
/* Correct AGA bitplane pointer setup */
|
||||
APTR plane = AllocMem(planeSize, MEMF_CHIP | MEMF_CLEAR);
|
||||
/* Verify alignment for FMODE=3: */
|
||||
if ((ULONG)plane & 7) {
|
||||
/* PROBLEM: pointer is not 8-byte aligned */
|
||||
/* AllocMem should return aligned memory, but verify */
|
||||
}
|
||||
```
|
||||
|
||||
### FMODE and Sprites
|
||||
|
||||
FMODE also widens sprite fetches, changing sprite width and DMA cost:
|
||||
|
||||
| SPR_FMODE | Sprite Width | DMA Slots/Sprite | Total Sprite DMA (8 ch) |
|
||||
|---|---|---|---|
|
||||
| 0 (1×) | 16 pixels | 2 | 16 |
|
||||
| 1 (2×) | 32 pixels | 4 | 32 |
|
||||
| 3 (4×) | 64 pixels | 8 | 64 |
|
||||
|
||||
> [!CAUTION]
|
||||
> At FMODE=3, sprite DMA consumes **64 slots** instead of 16 — quadrupling the sprite overhead. Combined with 8-plane bitplane DMA (40 slots at 4×), total DMA becomes 64 + 40 + 27(fixed) = **131 slots**, leaving 95 for CPU/Copper/Blitter. This is still better than OCS 6-plane (120 + 16 + 27 = 163), but the sprite cost increase is significant. See [AGA Sprites](../aga_a1200_a4000/aga_sprites.md) for per-mode details.
|
||||
|
||||
---
|
||||
|
||||
## FPGA and Emulation Implementation Notes
|
||||
|
||||
Replicating Agnus/Alice DMA behavior is one of the most critical and error-prone aspects of Amiga FPGA cores and cycle-accurate emulators. The DMA slot scheduler is effectively a **state machine** synchronized to the beam position counter.
|
||||
|
||||
### Core Requirements
|
||||
|
||||
| Requirement | Why It's Critical | Common Mistakes |
|
||||
|---|---|---|
|
||||
| **Cycle-accurate slot scheduler** | Copper WAITs and demo effects count exact DMA cycles; off-by-one breaks them | Implementing DMA as "request/grant" instead of fixed-position slots |
|
||||
| **Even/odd interleaving** | CPU must be stalled on odd slots when DMA is active, not just "whenever" | Stalling CPU on every DMA cycle instead of only when accessing Chip RAM |
|
||||
| **DDFSTRT/DDFSTOP precision** | Bitplane fetch start/stop must match the exact color clock | Starting fetch 1 clock early/late shifts the display by 16 pixels |
|
||||
| **Sprite fetch positioning** | Sprite DMA occurs at fixed positions relative to HSYNC, not relative to DDFSTRT | Placing sprite DMA inside the bitplane fetch window |
|
||||
| **FMODE fetch widening** | AGA wider fetches must consume 1 slot but transfer 2/4 words internally | Consuming 2/4 slots per wide fetch instead of 1 |
|
||||
|
||||
### Minimig Reference Architecture
|
||||
|
||||
The MiSTer Minimig-AGA core implements the DMA scheduler in `agnus.v` (or equivalent) as a finite state machine:
|
||||
|
||||
```
|
||||
State Machine: DMA Slot Sequencer
|
||||
─────────────────────────────────
|
||||
Inputs: hpos (horizontal position counter, 0–227)
|
||||
DMACON (enabled channels bitmask)
|
||||
DDFSTRT, DDFSTOP (fetch window)
|
||||
BPLCONx (display mode / depth)
|
||||
FMODE (AGA fetch width)
|
||||
|
||||
Output: bus_owner (enum: REFRESH, DISK, AUDIO, SPRITE, BPL, COPPER, BLITTER, CPU)
|
||||
chip_ram_addr (21/23-bit address)
|
||||
chip_ram_data (16/32/64-bit)
|
||||
```
|
||||
|
||||
Key implementation patterns:
|
||||
1. **Horizontal position counter** (`hpos`) increments each color clock, wrapping at 227/228
|
||||
2. **Fixed-channel lookup table** maps specific `hpos` ranges to channel owners (refresh at 0–3, disk at 4–6, audio at 7–10, etc.)
|
||||
3. **Bitplane fetch logic** activates when `hpos >= DDFSTRT && hpos <= DDFSTOP && BPLEN`
|
||||
4. **Demand channels** (Copper, Blitter) check remaining slots using priority arbitration
|
||||
5. **CPU grant** is the default when no DMA channel claims the current slot
|
||||
|
||||
### SDRAM vs Async DRAM
|
||||
|
||||
FPGA implementations face a fundamental timing mismatch: original Amiga Chip RAM used asynchronous DRAM with ~150 ns access time, naturally fitting the ~280 ns color clock. FPGA boards use synchronous SDRAM (or DDR) with different timing:
|
||||
|
||||
| Challenge | Solution |
|
||||
|---|---|
|
||||
| SDRAM requires burst access | Pre-fetch multiple words per SDRAM burst; buffer internally |
|
||||
| SDRAM has fixed latency | Pipeline DMA requests 2–3 clocks ahead of need |
|
||||
| Multiple channels need SDRAM simultaneously | Time-multiplex SDRAM access within each color clock (SDRAM is much faster than the original DRAM) |
|
||||
|
||||
> [!TIP]
|
||||
> Modern SDRAM running at 100+ MHz can service multiple Amiga DMA requests within a single 280 ns color clock. The MiSTer framework's SDRAM controller typically provides 4–8 access slots per color clock, enabling the core to satisfy all DMA channels plus CPU without contention — a luxury the original hardware never had.
|
||||
|
||||
### Common FPGA Bugs
|
||||
|
||||
1. **Bitplane over-fetch**: Fetching one word too many per scanline, causing the last word to overwrite the first word of the next plane
|
||||
2. **Sprite/bitplane slot collision**: Sprite DMA and bitplane DMA overlapping at DDFSTRT boundary
|
||||
3. **Blitter priority inversion**: Blitter-Nasty not correctly promoting Blitter above CPU
|
||||
4. **Copper slot starvation**: Copper unable to execute between bitplane fetches in high-bandwidth modes
|
||||
5. **FMODE alignment errors**: 4× fetch not enforcing 8-byte alignment, causing display glitches
|
||||
|
||||
---
|
||||
|
||||
## Bandwidth Calculation Cookbook
|
||||
|
||||
### The Formula
|
||||
|
||||
```
|
||||
Available CPU slots per line = 226 - Fixed(27) - BPL_slots - SPR_slots_extra
|
||||
CPU bandwidth % = Available / 226 × 100
|
||||
```
|
||||
|
||||
Where:
|
||||
- **Fixed** = 27 (4 refresh + 3 disk + 4 audio + 16 sprites at FMODE=0)
|
||||
- **BPL_slots** = `(DDFSTOP - DDFSTRT + 1) × depth ÷ FMODE_multiplier`
|
||||
- **SPR_slots_extra** = additional sprite slots when FMODE > 0
|
||||
|
||||
### Worked Examples
|
||||
|
||||
**Example 1: Typical OCS Game (A500, 320×256, 5 planes)**
|
||||
```
|
||||
Fixed DMA: 27 slots (refresh + disk + audio + sprites)
|
||||
Bitplane DMA: 100 slots (20 words/plane × 5 planes)
|
||||
Total DMA: 127 slots
|
||||
CPU available: 226 - 127 = 99 slots (43.8%)
|
||||
```
|
||||
At 7.09 MHz / 2 = ~3.55 MHz effective, 43.8% gives ~1.55 MHz effective CPU throughput from Chip RAM.
|
||||
|
||||
**Example 2: OCS Hires Workbench (A2000, 640×256, 4 planes)**
|
||||
```
|
||||
Fixed DMA: 27 slots
|
||||
Bitplane DMA: 160 slots (40 words/plane × 4 planes)
|
||||
Total DMA: 187 slots
|
||||
CPU available: 226 - 187 = 39 slots (17.3%)
|
||||
```
|
||||
The Workbench feels sluggish on a stock 68000 because CPU bandwidth is only 17%.
|
||||
|
||||
**Example 3: AGA Game (A1200, 320×256, 8 planes, FMODE=3)**
|
||||
```
|
||||
Fixed DMA: 27 slots (sprites at FMODE=0; assume SPR_FMODE=0)
|
||||
Bitplane DMA: 40 slots (160 words ÷ 4× fetch)
|
||||
Total DMA: 67 slots
|
||||
CPU available: 226 - 67 = 159 slots (70.4%)
|
||||
```
|
||||
Full 256-color display with 70% CPU — AGA's core advantage.
|
||||
|
||||
**Example 4: AGA Worst Case (A1200, 640×512 LACE, 8 planes, FMODE=3, SPR_FMODE=3)**
|
||||
```
|
||||
Fixed: 11 slots (refresh + disk + audio)
|
||||
Sprites: 64 slots (8 × 8 at 4× fetch)
|
||||
Bitplane DMA: 80 slots (320 words ÷ 4× fetch)
|
||||
Total DMA: 155 slots
|
||||
CPU available: 226 - 155 = 71 slots (31.4%)
|
||||
```
|
||||
Still feasible — but add Copper + Blitter and the CPU drops to single digits.
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Choose display depth based on CPU budget** — Use the bandwidth table above to determine how many bitplanes your game can afford before the CPU is starved.
|
||||
2. **Put code and data in Fast RAM** — The single most impactful optimization. CPU accesses to Fast RAM never contend with DMA.
|
||||
3. **Use `OwnBlitter()` / `DisownBlitter()`** — Always bracket Blitter operations. Never leave the Blitter unguarded.
|
||||
4. **Avoid Blitter-Nasty for large operations** — BLTPRI is useful for tiny blits (< 100 words) where completion speed matters more than CPU starvation.
|
||||
5. **Profile with `VPOSR` / beam position** — Measure how many scanlines your game loop takes per frame to detect DMA starvation early.
|
||||
6. **Test on stock hardware** — A stock A500 with 512 KB Chip RAM and no Fast RAM is the worst-case DMA scenario. If it works there, it works everywhere.
|
||||
7. **Understand DDFSTRT vs DIWSTRT** — Fetch window (DMA cost) and display window (visible area) are independent. Over-fetching for scroll buffers costs DMA even if the extra pixels aren't visible.
|
||||
8. **On AGA, set FMODE appropriately** — Don't leave FMODE=0 if you need bandwidth. Don't set FMODE=3 if you don't need it (alignment constraints, wider sprites).
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Do disabled DMA channels free their slots for the CPU?**
|
||||
> Partially. Fixed-position channels (refresh, disk, audio, sprites) have **reserved** time positions — disabling them means those slots go idle, not that the CPU can use them. The CPU can only use a slot if no higher-priority channel claims it. Demand-driven channels (Copper, Blitter) release their slots when disabled.
|
||||
|
||||
**Q: Does the 68020/030/040 change the DMA equation?**
|
||||
> No — the DMA slot allocation is controlled entirely by Agnus/Alice, which is independent of the CPU type. A 68040 at 25 MHz still gets the same number of Chip RAM slots per scanline as a 68000 at 7 MHz. The faster CPU benefits only when accessing Fast RAM (its own bus) or when it can do more work per Chip RAM access (larger caches, instruction pipeline).
|
||||
|
||||
**Q: Can the Copper "see" DMA slots?**
|
||||
> The Copper doesn't "see" slots — it gets granted free slots by Agnus's priority arbiter. In heavy display modes, the Copper may wait several color clocks between instructions because all slots are consumed by bitplane and sprite DMA. This is why complex Copper effects are impractical in hires 4-plane mode.
|
||||
|
||||
**Q: Why does Fast RAM not help audio or disk DMA?**
|
||||
> Audio and disk DMA channels are **hard-wired to Chip RAM** — Paula (audio/disk controller) can only read from the Chip RAM bus. There is no hardware path for Paula to access Fast RAM. Audio samples and disk buffers **must** be in Chip RAM regardless of how much Fast RAM is installed.
|
||||
|
||||
**Q: How does this relate to Zorro DMA bus mastering?**
|
||||
> Zorro bus mastering (Zorro III on A3000/A4000) is handled by Super Buster, which arbitrates between Zorro cards and the CPU for the Fast RAM bus. This is completely separate from the Chip RAM DMA scheduler — Zorro DMA and chipset DMA operate on different buses and don't contend. See [Zorro Bus](../common/zorro_bus.md) for expansion bus DMA details.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- *Amiga Hardware Reference Manual* 3rd ed. — Chapter 6: DMA and the Display, Figure 6-9 (DMA time slot diagram)
|
||||
- ADCD 2.1: Custom chip register documentation (`DMACON`, `DDFSTRT`, `DDFSTOP`, `FMODE`)
|
||||
- NDK 3.9: `hardware/custom.h`, `hardware/dmabits.h`
|
||||
- Commodore A3000 Technical Reference — Ramsey, Super Buster, SDMAC chapters
|
||||
- Toni Wilen, "WinUAE Chip RAM DMA Cycle Map" — reverse-engineered per-slot allocation tables
|
||||
- Minimig FPGA source: `agnus.v` — reference DMA scheduler implementation
|
||||
|
||||
## See Also
|
||||
|
||||
- [OCS Chipset Internals](../ocs_a500/chipset_ocs.md) — DMACON register, Agnus DMA priority
|
||||
- [AGA Chipset Internals](../aga_a1200_a4000/chipset_aga.md) — FMODE register, Alice DMA controller
|
||||
- [Memory Types](memory_types.md) — Chip RAM vs Fast RAM vs Slow RAM accessibility
|
||||
- [Blitter](../ocs_a500/blitter.md) — Blitter DMA channels, WaitBlit, minterm operations
|
||||
- [AGA Blitter](../aga_a1200_a4000/aga_blitter.md) — 64-bit FMODE blitter details
|
||||
- [Sprites](../../08_graphics/sprites.md) — Sprite DMA budget, hardware vs software sprites
|
||||
- [AGA Sprites](../aga_a1200_a4000/aga_sprites.md) — FMODE impact on sprite DMA and width
|
||||
- [BitMap](../../08_graphics/bitmap.md) — Planar bitplane layout, AllocBitMap, interleaving
|
||||
- [Display Modes](../../08_graphics/display_modes.md) — Mode selection, DMA slot budget overview
|
||||
- [Copper](../ocs_a500/copper.md) — Copper instruction timing and DMA interaction
|
||||
- [Gary System Controller](../ocs_a500/gary_system_controller.md) — Bus timeout, address decode
|
||||
- [Zorro Bus](zorro_bus.md) — Expansion bus DMA, Buster/Super Buster
|
||||
|
|
@ -421,3 +421,4 @@ A: WHDLoad patches old games that assume all memory is Chip RAM. It redirects al
|
|||
- See also: [zorro_bus.md](zorro_bus.md) — Zorro II/III expansion bus (Fast RAM cards)
|
||||
- See also: [blitter_programming.md](../../08_graphics/blitter_programming.md) — Blitter DMA (Chip RAM only)
|
||||
- See also: [exec_memory.md](../../06_exec_os/exec_memory.md) — AmigaOS memory management API
|
||||
- See also: [dma_architecture.md](dma_architecture.md) — DMA slot allocation, bus arbitration, why Chip RAM bandwidth matters
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ The **Enhanced Chip Set** (ECS) is a significant revision of OCS, shipping from
|
|||
| [productivity_modes.md](productivity_modes.md) | Multiscan/productivity display modes |
|
||||
| [gary_system_controller.md](gary_system_controller.md) | Gary & Fat Gary — system controller gate array: address decode, bus arbitration, AutoConfig, SCSI/FPU glue, chip variants, runtime detection, antipatterns, FPGA timing |
|
||||
| [Gayle IDE & PCMCIA](../common/gayle_ide_pcmcia.md) | A600 Gayle: IDE and PCMCIA (shared with A1200) |
|
||||
| [ecs_sprites.md](ecs_sprites.md) | ECS sprite enhancements: border sprites, independent resolution (SPRES), color bank |
|
||||
| [chip_ram_expansion.md](chip_ram_expansion.md) | 2 MB Chip RAM with Super Agnus |
|
||||
|
||||
## ECS vs OCS — Key Differences
|
||||
|
|
|
|||
98
01_hardware/ecs_a600_a3000/ecs_sprites.md
Normal file
98
01_hardware/ecs_a600_a3000/ecs_sprites.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
[← Home](../../README.md) · [Hardware](../README.md) · [ECS](README.md)
|
||||
|
||||
# ECS Sprite Enhancements
|
||||
|
||||
## Overview
|
||||
|
||||
ECS Denise (MOS 8373) extends the [OCS sprite hardware](../ocs_a500/sprites.md) with three register-level enhancements while maintaining full backward compatibility. All eight sprite DMA channels, data formats, and color mapping rules remain identical to OCS — ECS adds control over **where** sprites appear and **at what resolution** they render.
|
||||
|
||||
## What Changed from OCS
|
||||
|
||||
| Feature | OCS (Denise 8362) | ECS (Denise 8373) | Register |
|
||||
|---|---|---|---|
|
||||
| Border visibility | Sprites clipped to display window | Sprites visible in border area | `BPLCON3` bit 3 |
|
||||
| Pixel resolution | Tied to playfield mode | Independent: lores/hires/shres | `BPLCON3` bits 7-6 |
|
||||
| Color bank select | Fixed: COLOR16–COLOR31 | Upper bits selectable (AGA prep) | `BPLCON3` bits 15-13 |
|
||||
|
||||
---
|
||||
|
||||
## BPLCON3 Sprite Bits ($DFF106)
|
||||
|
||||
All ECS sprite enhancements are controlled through the `BPLCON3` register. On OCS hardware, this register does not exist — writes are silently ignored.
|
||||
|
||||
```
|
||||
bit 15-13: BANK2:0 Sprite color bank select (upper bits of color index)
|
||||
bit 7-6: SPRES1:0 Sprite resolution
|
||||
bit 3: BRDSPRT Enable sprites in border area
|
||||
```
|
||||
|
||||
### SPRES — Independent Sprite Resolution
|
||||
|
||||
ECS decouples sprite pixel width from the playfield display mode. This allows sprites to have a different pixel clock than the background:
|
||||
|
||||
| SPRES1:0 | Pixel Width | Pixel Clock | Use Case |
|
||||
|---|---|---|---|
|
||||
| `00` | Follow playfield | — | OCS-compatible default |
|
||||
| `01` | Lores (140ns) | ~7.09 MHz | Large, chunky sprites on hires backgrounds |
|
||||
| `10` | Hires (70ns) | ~14.18 MHz | Fine-detail sprites on lores backgrounds |
|
||||
| `11` | Super-hires (35ns) | ~28.37 MHz | Maximum resolution sprites (productivity modes) |
|
||||
|
||||
```asm
|
||||
; Set sprites to hires resolution independent of playfield
|
||||
move.w #$0080, $DFF106 ; BPLCON3: SPRES1:0 = %10 (hires)
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> SPRES affects the **horizontal pixel resolution** of all 8 sprite channels globally. Individual per-sprite resolution is not possible.
|
||||
|
||||
### BRDSPRT — Border Sprite Enable
|
||||
|
||||
On OCS, sprites outside the display window boundaries (`DIWSTRT`/`DIWSTOP`) are clipped. ECS allows sprites to appear in the overscan border area:
|
||||
|
||||
```asm
|
||||
; Enable sprites in border region
|
||||
move.w #$0008, $DFF106 ; BPLCON3: BRDSPRT = 1
|
||||
```
|
||||
|
||||
This is useful for status bars, score displays, or HUD elements positioned outside the scrolling playfield.
|
||||
|
||||
### BANK — Sprite Color Bank (AGA Preparation)
|
||||
|
||||
BPLCON3 bits 15-13 provide upper bits for the sprite color register index. On ECS hardware these bits have limited practical use (only 32 color registers exist), but they establish the register interface that AGA's [256-color sprite palette](../aga_a1200_a4000/aga_sprites.md) builds upon.
|
||||
|
||||
---
|
||||
|
||||
## "Half-ECS" Trap
|
||||
|
||||
> [!WARNING]
|
||||
> Many Amigas shipped with **ECS Agnus** (Super Agnus 8372A) but **OCS Denise** (8362). This configuration — common in late A500 boards and some A2000 revisions — provides 2 MB Chip RAM addressing but **no sprite enhancements**. Writing to `BPLCON3` on these machines has no effect.
|
||||
>
|
||||
> Always check for ECS Denise specifically:
|
||||
> ```c
|
||||
> if (GfxBase->ChipRevBits0 & (1 << GFXB_HR_DENISE)) {
|
||||
> /* Safe to use BPLCON3 sprite features */
|
||||
> }
|
||||
> ```
|
||||
|
||||
---
|
||||
|
||||
## Programming Notes
|
||||
|
||||
- All ECS sprite changes are backward-compatible: leaving `BPLCON3` at `$0000` gives identical behavior to OCS
|
||||
- `SPRES` only affects horizontal resolution — vertical resolution is always 1 pixel per scanline
|
||||
- `BRDSPRT` works with both standard and attached sprites
|
||||
- Sprite DMA format, pointer registers, and collision detection (`CLXCON`/`CLXDAT`) are unchanged from OCS
|
||||
|
||||
## See Also
|
||||
|
||||
- [OCS Sprite Hardware](../ocs_a500/sprites.md) — base register reference (SPRxPOS/CTL/DATA, color mapping)
|
||||
- [AGA Sprite Enhancements](../aga_a1200_a4000/aga_sprites.md) — wider sprites, FMODE, 256-color palette banks
|
||||
- [Sprites — Graphics Programming Guide](../../08_graphics/sprites.md) — OS API, techniques, antipatterns
|
||||
- [ECS Chipset Internals](chipset_ecs.md) — Super Agnus and ECS Denise overview
|
||||
- [ECS Register Deltas](ecs_registers_delta.md) — complete BPLCON3 bit definition
|
||||
|
||||
## References
|
||||
|
||||
- *Amiga Hardware Reference Manual* 3rd ed. — Appendix F (ECS extensions)
|
||||
- ADCD 2.1 Hardware Manual — ECS Denise sprite section
|
||||
- NDK39: `hardware/custom.h` — BPLCON3 definition
|
||||
|
|
@ -152,3 +152,9 @@ WaitBlit(); /* graphics.library — waits and resets to safe state */
|
|||
- NDK39: `hardware/blit.h`, `graphics/blitattr.h`
|
||||
- graphics.library Autodocs: `BltBitMap`, `BltTemplate`, `BltClear`
|
||||
- http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node006D.html
|
||||
|
||||
## See Also
|
||||
|
||||
- [DMA Architecture](../common/dma_architecture.md) — Blitter-Nasty (BLTPRI), bus arbitration, CPU starvation mechanics
|
||||
- [Blitter Programming](../../08_graphics/blitter_programming.md) — Advanced minterms, cookie-cut, area fill
|
||||
- [AGA Blitter](../aga_a1200_a4000/aga_blitter.md) — 64-bit FMODE blitter
|
||||
|
|
|
|||
|
|
@ -105,3 +105,9 @@ BPL2PTH/BPL2PTL $DFF0E4/$DFF0E6
|
|||
- ADCD 2.1 Hardware Manual — Agnus/DMA chapters
|
||||
- NDK39: `hardware/dmabits.h`, `hardware/intbits.h`, `hardware/custom.h`
|
||||
- *Amiga Hardware Reference Manual* 3rd ed.
|
||||
|
||||
## See Also
|
||||
|
||||
- [DMA Architecture](../common/dma_architecture.md) — scanline slot allocation, bus arbitration, bandwidth calculations
|
||||
- [ECS Chipset](../ecs_a600_a3000/chipset_ecs.md) — Super Agnus and ECS Denise enhancements
|
||||
- [AGA Chipset](../aga_a1200_a4000/chipset_aga.md) — Alice, Lisa, FMODE
|
||||
|
|
|
|||
|
|
@ -129,6 +129,12 @@ SetPointer(window, pointer_data, height, width, x_offset, y_offset);
|
|||
ClearPointer(window); /* restore system default */
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [ECS Sprite Enhancements](../ecs_a600_a3000/ecs_sprites.md) — border sprites, independent resolution (SPRES)
|
||||
- [AGA Sprite Enhancements](../aga_a1200_a4000/aga_sprites.md) — 32/64px width, FMODE, color bank selection
|
||||
- [Sprites — Graphics Programming Guide](../../08_graphics/sprites.md) — OS API, techniques, antipatterns, decision guides
|
||||
|
||||
## References
|
||||
|
||||
- ADCD 2.1 Hardware Manual — Sprites chapter
|
||||
|
|
|
|||
|
|
@ -596,3 +596,4 @@ For MiSTer and emulator developers, planar BitMap emulation has specific require
|
|||
- See also: [blitter_programming.md](blitter_programming.md) — Advanced Blitter minterms and cookie-cut
|
||||
- See also: [views.md](views.md) — Attaching BitMaps to ViewPorts for display
|
||||
- See also: [rastport.md](rastport.md) — RastPort drawing context and primitives
|
||||
- See also: [dma_architecture.md](../01_hardware/common/dma_architecture.md) — bitplane DMA slot budget, DDFSTRT/DDFSTOP registers, bandwidth calculations
|
||||
|
|
|
|||
|
|
@ -643,3 +643,4 @@ LACE alternates two 256-line fields at 25/30 Hz each to produce 512 visible line
|
|||
- 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
|
||||
|
|
|
|||
|
|
@ -1,41 +1,84 @@
|
|||
[← Home](../README.md) · [Graphics](README.md)
|
||||
|
||||
# Hardware Sprites — DMA Engine, Multiplexing, and Tricks
|
||||
# Hardware Sprites — Architecture, API, and Techniques
|
||||
|
||||
## Overview
|
||||
## Why Hardware Sprites Matter
|
||||
|
||||
The Amiga has **8 hardware sprites**, each 16 pixels wide with 3 colors + transparent. Sprites are entirely DMA-driven — the custom chips fetch sprite data from Chip RAM and composite them over the playfield with zero CPU overhead. The Copper reloads sprite pointers every frame.
|
||||
In 1985, the Amiga's sprite engine solved a problem that consumed most of its competitors' CPU budgets: **moving objects without redrawing the screen**. While the Atari ST had no sprite hardware at all and the C64's VIC-II required CPU-driven interrupt juggling, the Amiga's Denise chip composites up to 8 DMA-driven sprite channels over the playfield with **zero CPU cost**. The CPU never touches sprite pixels — Agnus fetches sprite data from Chip RAM, Denise renders it, and the Copper reloads pointers each frame. This architecture freed the 68000 to run game logic, audio mixing, and OS multitasking simultaneously.
|
||||
|
||||
Sprite 0 is reserved by Intuition for the **mouse pointer**. Sprites 1–7 are available for application use.
|
||||
> [!NOTE]
|
||||
> **Modern analogy:** Think of hardware sprites as GPU-side instanced quads with automatic Z-compositing — the CPU submits position + texture pointer, and dedicated hardware handles all rendering. The `SimpleSprite` API maps to a GPU sprite batch submission; direct register programming maps to writing raw GPU command buffers.
|
||||
|
||||
### Competitive Landscape (1985–1992)
|
||||
|
||||
| Platform | Sprite Hardware | Width | Per-Line Limit | Colors | Multiplexing |
|
||||
|---|---|---|---|---|---|
|
||||
| **Amiga OCS** | 8 DMA channels (Denise) | 16px | 8 | 3 (+15 attached) | Copper-driven, unlimited vertical |
|
||||
| C64 (VIC-II) | 8 hardware sprites | 24px | 8 (IRQ mux) | 3 (multicolor) | Software IRQ, limited |
|
||||
| NES (PPU) | 64 OAM entries | 8px | 8 (flicker beyond) | 3 per tile | Hardware, with flicker |
|
||||
| Atari ST | None | — | — | — | Software only |
|
||||
| Arcade (System 16) | 128+ sprites | 8-32px | 32+/line | 15+ | Hardware ASIC |
|
||||
| **Amiga AGA** | 8 DMA channels (Lisa) | 16/32/64px | 8 | 3 (+15 attached) | Same + wider fetch |
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Chip RAM"
|
||||
SD0["Sprite 0 data<br/>(mouse pointer)"]
|
||||
SD1["Sprite 1 data"]
|
||||
SD2["Sprite 2-7 data"]
|
||||
SD1["Sprite 1-7 data"]
|
||||
end
|
||||
|
||||
subgraph "Custom Chips (Denise/Lisa)"
|
||||
DMA["Sprite DMA<br/>(8 channels)"] --> MUX["Priority MUX"]
|
||||
PF["Playfield<br/>(bitplane data)"] --> MUX
|
||||
MUX --> DAC["Color DAC<br/>→ Video Out"]
|
||||
subgraph "Agnus — DMA Controller"
|
||||
SDMA["Sprite DMA<br/>16 slots/scanline<br/>(2 words × 8 channels)"]
|
||||
end
|
||||
|
||||
SD0 --> DMA
|
||||
SD1 --> DMA
|
||||
SD2 --> DMA
|
||||
subgraph "Denise/Lisa — Display"
|
||||
SREG["Shift Registers<br/>(8 channels)"] --> PMUX["Priority MUX<br/>(BPLCON2)"]
|
||||
PF["Playfield<br/>(bitplanes)"] --> PMUX
|
||||
PMUX --> DAC["Color DAC<br/>→ Video Out"]
|
||||
end
|
||||
|
||||
subgraph "Copper"
|
||||
COP["Copper List"] -->|"Set SPRxPTH/L<br/>every frame"| DMA
|
||||
COP["Copper List"] -->|"Set SPRxPTH/L<br/>every frame"| SDMA
|
||||
end
|
||||
|
||||
style DMA fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
SD0 --> SDMA
|
||||
SD1 --> SDMA
|
||||
SDMA --> SREG
|
||||
|
||||
style SDMA fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
style COP fill:#fff9c4,stroke:#f9a825,color:#333
|
||||
style PMUX fill:#f3e5f5,stroke:#7b1fa2,color:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DMA Timing and Bus Budget
|
||||
|
||||
Sprite DMA occupies **dedicated time slots** in the Agnus scanline bus schedule. A *slot* (or *color clock*) is a single ~280 ns memory-access window on the Chip RAM bus — the fundamental quantum of all Amiga DMA. Each sprite channel fetches 2 words (position/control on the first line, data on subsequent lines), consuming 16 of the ~226 available DMA slots per scanline. See [DMA Architecture](../01_hardware/common/dma_architecture.md) for the complete scanline budget, bus arbitration, and bandwidth calculations.
|
||||
|
||||
### DMA Priority Hierarchy
|
||||
|
||||
```
|
||||
Refresh > Disk > Audio > Sprites > Copper > Bitplane > Blitter > CPU
|
||||
^^^^^^^
|
||||
16 slots guaranteed
|
||||
```
|
||||
|
||||
Sprite DMA consumes approximately **7% of total Chip RAM bus bandwidth** at standard OCS/ECS 16px width. On AGA with 64px sprites (`SPR_FMODE=10`), this rises to **~28%** — see [AGA Sprite Enhancements](../01_hardware/aga_a1200_a4000/aga_sprites.md).
|
||||
|
||||
### Bitplane Stealing
|
||||
|
||||
> [!WARNING]
|
||||
> In **6-bitplane display modes**, the expanded bitplane DMA fetch window can consume time slots normally reserved for sprites 6 and 7. This makes the highest-numbered sprite channels unreliable or invisible. In extreme overscan + 6-plane modes, sprites 4-5 can also be affected.
|
||||
>
|
||||
> **Rule of thumb:** If your game uses 5+ bitplanes, test sprite channels 6-7 carefully. Prefer lower-numbered channels for critical objects.
|
||||
|
||||
---
|
||||
|
||||
## Sprite DMA Data Format
|
||||
|
||||
Each sprite is stored as a contiguous block in Chip RAM:
|
||||
|
|
@ -43,7 +86,7 @@ Each sprite is stored as a contiguous block in Chip RAM:
|
|||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Header Word 0: VSTART/HSTART │ Position control
|
||||
│ Header Word 1: VSTOP/Control │
|
||||
│ Header Word 1: VSTOP/Control/ATTACH │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Line 0: DATA word (bit 0 of each pixel) │ ← 16 pixels per line
|
||||
│ Line 0: DATB word (bit 1 of each pixel) │
|
||||
|
|
@ -78,10 +121,6 @@ Word 0 (SPRxPOS):
|
|||
|
||||
Word 1 (SPRxCTL):
|
||||
Bits 15–8: VSTOP[7:0] (vertical stop line, low 8 bits)
|
||||
Bit 7: unused
|
||||
Bit 6: unused
|
||||
Bit 5: unused
|
||||
Bit 4: unused
|
||||
Bit 3: VSTART[8] (bit 8 of start — for lines > 255)
|
||||
Bit 2: VSTOP[8] (bit 8 of stop)
|
||||
Bit 1: HSTART[0] (low bit of horizontal position)
|
||||
|
|
@ -104,12 +143,7 @@ Each pair of sprites shares 3 color registers (color 0 = transparent for all):
|
|||
| 4–5 | `COLOR25`–`COLOR27` | `$DFF1B2`–`$DFF1B6` | |
|
||||
| 6–7 | `COLOR29`–`COLOR31` | `$DFF1BA`–`$DFF1BE` | |
|
||||
|
||||
```c
|
||||
/* Set sprite 0-1 colors directly: */
|
||||
custom->color[17] = 0xF00; /* red */
|
||||
custom->color[18] = 0x0F0; /* green */
|
||||
custom->color[19] = 0xFFF; /* white */
|
||||
```
|
||||
On AGA, sprites can draw from **any 16-color slice** of the 256-color palette via `BPLCON4` — see [AGA Sprite Enhancements](../01_hardware/aga_a1200_a4000/aga_sprites.md#bplcon4--sprite-color-bank-selection-dff10c).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -130,81 +164,65 @@ flowchart LR
|
|||
style SA fill:#c8e6c9,stroke:#2e7d32,color:#333
|
||||
```
|
||||
|
||||
When attached, the even sprite provides bits 0–1 and the odd sprite provides bits 2–3 of the color index. The 4-bit value indexes into color registers 16–31.
|
||||
When attached, the even sprite provides bits 0–1 and the odd sprite provides bits 2–3 of the color index. The 4-bit value indexes into color registers 16–31 (for pair 0/1).
|
||||
|
||||
```c
|
||||
/* Enable attachment: set bit 0 of odd sprite's CTL word */
|
||||
oddSpriteData[1] |= 0x0001; /* ATTACH bit */
|
||||
```
|
||||
|
||||
Valid attachment pairs: 0+1, 2+3, 4+5, 6+7. Attaching reduces available sprite channels from 8 to 4.
|
||||
|
||||
---
|
||||
|
||||
## Sprite Multiplexing — More Than 8 Sprites
|
||||
## Hardware Collision Detection (CLXCON / CLXDAT)
|
||||
|
||||
The hardware only supports 8 simultaneous sprites, but demos and games use **multiplexing** to display many more by reusing sprite channels on different scanlines:
|
||||
The Amiga provides **pixel-perfect hardware collision detection** between sprites and playfields via two registers:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph "Screen (top to bottom)"
|
||||
Z1["Lines 0-50:<br/>Sprite 2 = Enemy A"]
|
||||
Z2["Lines 60-110:<br/>Sprite 2 = Enemy B"]
|
||||
Z3["Lines 120-170:<br/>Sprite 2 = Enemy C"]
|
||||
Z4["Lines 180-230:<br/>Sprite 2 = Enemy D"]
|
||||
end
|
||||
### CLXCON — Collision Control ($DFF098, Write)
|
||||
|
||||
COP["Copper List"] -->|"WAIT line 0<br/>Set SPR2PT → Enemy A"| Z1
|
||||
COP -->|"WAIT line 55<br/>Set SPR2PT → Enemy B"| Z2
|
||||
COP -->|"WAIT line 115<br/>Set SPR2PT → Enemy C"| Z3
|
||||
COP -->|"WAIT line 175<br/>Set SPR2PT → Enemy D"| Z4
|
||||
Configures which objects are tested for collision:
|
||||
|
||||
style COP fill:#fff9c4,stroke:#f9a825,color:#333
|
||||
```
|
||||
Bits 15-12: ENSP7-ENSP1 Enable sprite pair collision (odd sprites)
|
||||
Bits 11-6: ENBP6-ENBP1 Enable bitplane collision checks
|
||||
Bits 5-0: MVBP6-MVBP1 Match value for enabled bitplanes
|
||||
```
|
||||
|
||||
The Copper waits for a line after one sprite ends, then reprograms the sprite pointer register for the next object. This gives effectively **unlimited sprites** as long as they don't overlap vertically on the same channel.
|
||||
### CLXDAT — Collision Data ($DFF00E, Read)
|
||||
|
||||
### Multiplexing Rules
|
||||
Returns collision results. **Auto-clears on read** — read once per frame:
|
||||
|
||||
| Rule | Explanation |
|
||||
|---|---|
|
||||
| No vertical overlap on same channel | Two sprites using the same DMA channel cannot appear on the same scanline |
|
||||
| 1-line gap required | After a sprite ends (VSTOP), the DMA channel needs at least 1 blank line before starting the next |
|
||||
| Copper must be fast enough | Copper WAIT + MOVE takes 2 DMA cycles; pointer reload = 2 MOVEs (PTH+PTL) |
|
||||
| Max per scanline: 8 | Absolute hardware limit — 8 DMA channels, one fetch per channel per line |
|
||||
```
|
||||
Bit 14: SPR4-5 vs SPR6-7 Bit 9: SPR0-1 vs BPL (odd)
|
||||
Bit 13: SPR2-3 vs SPR6-7 Bit 8: SPR0-1 vs BPL (even)
|
||||
Bit 12: SPR2-3 vs SPR4-5 Bit 7: BPL (even) vs BPL (odd)
|
||||
Bit 11: SPR0-1 vs SPR6-7 Bits 6-1: other sprite-vs-BPL pairs
|
||||
Bit 10: SPR0-1 vs SPR4-5 Bit 0: SPR0-1 vs SPR2-3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AGA Sprite Enhancements
|
||||
|
||||
| Feature | OCS/ECS | AGA |
|
||||
|---|---|---|
|
||||
| Width | 16 pixels | 16, 32, or 64 pixels (via FMODE) |
|
||||
| Colors (single) | 3 + transparent | 3 + transparent |
|
||||
| Colors (attached) | 15 + transparent | 15 + transparent |
|
||||
| Horizontal resolution | Low-res ÷ 2 | Same (unchanged) |
|
||||
### Practical Example
|
||||
|
||||
```c
|
||||
/* AGA: wider sprites via FMODE register */
|
||||
custom->fmode = 3; /* 4× fetch — sprites become 64 pixels wide */
|
||||
/* WARNING: this also affects bitplane fetch! */
|
||||
/* Configure collision: sprite 0/1 vs bitplane 1 */
|
||||
custom->clxcon = 0x0002; /* Enable BP1, match value = 0 */
|
||||
|
||||
/* In game loop — read once per frame: */
|
||||
UWORD coll = custom->clxdat; /* Read & auto-clear */
|
||||
if (coll & 0x0040) {
|
||||
/* Sprite 0/1 collided with bitplane 1 pixel */
|
||||
HandleCollision();
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Hardware collision is **pixel-perfect** but provides no spatial information — you know *that* a collision occurred, not *where*. Most games supplement with software bounding-box checks for gameplay logic, using hardware collision only for effects (e.g., bullet-vs-terrain).
|
||||
|
||||
---
|
||||
|
||||
## Sprite-Playfield Priority
|
||||
|
||||
Sprites interact with playfields via the **priority control register** (`BPLCON2`):
|
||||
|
||||
```c
|
||||
/* BPLCON2 ($DFF104) priority bits: */
|
||||
/* PF2P2-PF2P0: playfield 2 priority vs sprites */
|
||||
/* PF1P2-PF1P0: playfield 1 priority vs sprites */
|
||||
|
||||
/* Priority order (front to back): */
|
||||
/* SP01 > SP23 > SP45 > SP67 > PF1 > PF2 (default) */
|
||||
|
||||
/* Make sprites appear behind playfield 1: */
|
||||
custom->bplcon2 = 0x0024; /* PF1 in front of all sprites */
|
||||
```
|
||||
Sprites interact with playfields via `BPLCON2` ($DFF104):
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
|
|
@ -218,7 +236,313 @@ flowchart LR
|
|||
style PF2 fill:#bbdefb,stroke:#1565c0,color:#333
|
||||
```
|
||||
|
||||
The priority can be configured so that sprites appear **between** playfield 1 and playfield 2 — creating the illusion of depth (e.g., a character walking behind foreground objects but in front of the background).
|
||||
The priority can be configured so sprites appear **between** playfields — creating depth illusions (e.g., a character walking behind foreground trees but in front of the sky).
|
||||
|
||||
```c
|
||||
/* Make sprites appear between PF1 (front) and PF2 (back): */
|
||||
custom->bplcon2 = 0x0024; /* PF1 in front of all sprites */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Choosing Your Sprite Strategy
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
START["Need moving objects?"] --> Q1{"≤16px wide,<br/>≤3 colors,<br/>≤8 objects?"}
|
||||
Q1 -->|Yes| HW["Hardware Sprite<br/>(SimpleSprite / ExtSprite)"]
|
||||
Q1 -->|No| Q2{"Need 15 colors?"}
|
||||
Q2 -->|Yes| ATT["Attached Sprite Pair<br/>(reduces to 4 channels)"]
|
||||
Q2 -->|No| Q3{"Need >8 simultaneous<br/>on same scanline?"}
|
||||
Q3 -->|Yes| BOB["BOB (Blitter Object)<br/>→ See animation.md"]
|
||||
Q3 -->|No| Q4{"Can sort by Y<br/>with no overlap?"}
|
||||
Q4 -->|Yes| MUX["Multiplexed Sprites<br/>(Copper-driven reuse)"]
|
||||
Q4 -->|No| BOB
|
||||
START --> Q5{"System-friendly app?"}
|
||||
Q5 -->|Yes| VSPRITE["VSprite (GEL system)<br/>→ See animation.md"]
|
||||
Q5 -->|No| HW
|
||||
|
||||
style HW fill:#c8e6c9,stroke:#2e7d32,color:#333
|
||||
style BOB fill:#bbdefb,stroke:#1565c0,color:#333
|
||||
style MUX fill:#fff9c4,stroke:#f9a825,color:#333
|
||||
style VSPRITE fill:#f3e5f5,stroke:#7b1fa2,color:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OS-Level Sprite API
|
||||
|
||||
### SimpleSprite (V33+ — All Kickstart Versions)
|
||||
|
||||
```c
|
||||
#include <graphics/sprite.h>
|
||||
|
||||
struct SimpleSprite ss;
|
||||
WORD sprnum;
|
||||
|
||||
/* Obtain a free sprite (Intuition reserves sprite 0): */
|
||||
sprnum = GetSprite(&ss, -1); /* -1 = any available */
|
||||
if (sprnum >= 0)
|
||||
{
|
||||
ss.x = 100;
|
||||
ss.y = 50;
|
||||
ss.height = 16;
|
||||
|
||||
/* Set sprite image data (MUST be in Chip RAM): */
|
||||
ChangeSprite(NULL, &ss, spriteData);
|
||||
|
||||
/* Move sprite to screen position: */
|
||||
MoveSprite(NULL, &ss, 100, 50);
|
||||
|
||||
/* Release when done: */
|
||||
FreeSprite(sprnum);
|
||||
}
|
||||
```
|
||||
|
||||
### ExtSprite (V39+ — Kickstart 3.0+)
|
||||
|
||||
The V39 API provides tag-based sprite management with better AGA integration:
|
||||
|
||||
```c
|
||||
#include <graphics/sprite.h>
|
||||
|
||||
struct ExtSprite *ext;
|
||||
struct BitMap *spriteBM; /* pre-prepared sprite bitmap */
|
||||
|
||||
/* Allocate ExtSprite from a BitMap: */
|
||||
ext = AllocSpriteData(spriteBM,
|
||||
SPRITEA_Width, 16,
|
||||
SPRITEA_OutputHeight, 0, /* natural height */
|
||||
TAG_DONE);
|
||||
|
||||
if (ext) {
|
||||
WORD num = GetExtSprite(ext, GSTAG_SPRITE_NUM, -1, TAG_DONE);
|
||||
if (num >= 0) {
|
||||
MoveSprite(NULL, (struct SimpleSprite *)ext, 120, 80);
|
||||
|
||||
/* ... use sprite ... */
|
||||
|
||||
FreeSprite(num);
|
||||
}
|
||||
FreeSpriteData(ext);
|
||||
}
|
||||
```
|
||||
|
||||
| Feature | `SimpleSprite` (V33) | `ExtSprite` (V39) |
|
||||
|---|---|---|
|
||||
| Allocation | `GetSprite()` with manual struct | `AllocSpriteData()` + `GetExtSprite()` |
|
||||
| AGA width support | No | Yes (via tags) |
|
||||
| Color bank control | Manual register writes | Tag-based |
|
||||
| Data source | Raw word array in Chip RAM | `BitMap` structure |
|
||||
| Recommended for | OCS/ECS games, simple use | AGA apps, OS-friendly code |
|
||||
|
||||
### Custom Mouse Pointer
|
||||
|
||||
```c
|
||||
/* Change Intuition mouse pointer via Window: */
|
||||
UWORD pointerData[] = {
|
||||
0x0000, 0x0000, /* reserved (position) */
|
||||
0x8000, 0xC000, /* line 0 */
|
||||
0xC000, 0xE000, /* line 1 */
|
||||
0xE000, 0xF000, /* line 2 */
|
||||
/* ... etc ... */
|
||||
0x0000, 0x0000 /* terminator */
|
||||
};
|
||||
SetPointer(window, pointerData, height, width, xOffset, yOffset);
|
||||
ClearPointer(window); /* restore system default */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sprite Multiplexing — Unlimited Vertical Sprites
|
||||
|
||||
The hardware supports 8 simultaneous sprites per scanline, but the Copper can **rearm** channels mid-frame to display many more objects:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph "Screen (top to bottom)"
|
||||
Z1["Lines 0-50:<br/>Sprite 2 = Enemy A"]
|
||||
Z2["Lines 60-110:<br/>Sprite 2 = Enemy B"]
|
||||
Z3["Lines 120-170:<br/>Sprite 2 = Enemy C"]
|
||||
Z4["Lines 180-230:<br/>Sprite 2 = Enemy D"]
|
||||
end
|
||||
|
||||
COP["Copper List"] -->|"WAIT line 55<br/>Set SPR2PT → Enemy B"| Z2
|
||||
COP -->|"WAIT line 115<br/>Set SPR2PT → Enemy C"| Z3
|
||||
COP -->|"WAIT line 175<br/>Set SPR2PT → Enemy D"| Z4
|
||||
|
||||
style COP fill:#fff9c4,stroke:#f9a825,color:#333
|
||||
```
|
||||
|
||||
### Multiplexing Rules
|
||||
|
||||
| Rule | Explanation |
|
||||
|---|---|
|
||||
| No vertical overlap on same channel | Two objects using the same DMA channel cannot share a scanline |
|
||||
| 1-line gap required | After VSTOP, the DMA channel needs ≥1 blank line before restart |
|
||||
| Copper timing | Pointer reload = WAIT + 2 MOVEs (PTH+PTL) = 3 Copper instructions |
|
||||
| Max per scanline: 8 | Absolute hardware limit — 8 DMA channels, one fetch per channel per line |
|
||||
| Sort objects by Y | Efficient multiplexing requires Y-sorted sprite lists |
|
||||
|
||||
---
|
||||
|
||||
## Advanced Techniques (Demoscene & Gamedev)
|
||||
|
||||
### Horizontal Sprite Reuse — "Chasing the Raster"
|
||||
|
||||
By updating `SPRxPOS` and the data pointer **mid-scanline** via tightly-timed Copper instructions, the same sprite channel can appear at multiple horizontal positions on a single line. The Copper "chases" the raster beam across the screen:
|
||||
|
||||
```asm
|
||||
; Copper list: display sprite 0 at three horizontal positions on line 100
|
||||
dc.w $6401,$FFFE ; WAIT for line $64 (100), hpos $01
|
||||
dc.w SPR0POS,$6440 ; position at H=$40
|
||||
dc.w $6481,$FFFE ; WAIT same line, hpos $81
|
||||
dc.w SPR0POS,$6480 ; reposition at H=$80
|
||||
dc.w $64C1,$FFFE ; WAIT same line, hpos $C1
|
||||
dc.w SPR0POS,$64C0 ; reposition at H=$C0
|
||||
```
|
||||
|
||||
This technique was used in *Jim Power* to create pseudo-playfield background layers from sprite channels, freeing all bitplanes for the scrolling foreground.
|
||||
|
||||
### Color Multiplexing
|
||||
|
||||
The Copper can update sprite **color registers** per-scanline, giving different colors to each vertical zone of a multiplexed sprite:
|
||||
|
||||
```asm
|
||||
; Change sprite 0-1 colors at line 100
|
||||
dc.w $6401,$FFFE ; WAIT line 100
|
||||
dc.w COLOR17,$0F00 ; red
|
||||
dc.w $C801,$FFFE ; WAIT line 200
|
||||
dc.w COLOR17,$00F0 ; green
|
||||
```
|
||||
|
||||
### Sprite-as-Playfield
|
||||
|
||||
Using all 8 sprite channels horizontally multiplexed creates a full-width pseudo-playfield layer with independent scrolling — zero bitplane cost. Limited to sprite color depth (3 or 15 colors) and requires extensive Copper list bandwidth.
|
||||
|
||||
---
|
||||
|
||||
## Real-World Use Cases
|
||||
|
||||
| Game / Demo | Technique | What It Achieved |
|
||||
|---|---|---|
|
||||
| *Shadow of the Beast* | Multiplexing + priority layering | Moon/foreground objects behind 12-layer parallax |
|
||||
| *Jim Power* | Sprite-as-playfield (horizontal reuse) | Background layers from sprite channels, freeing bitplanes |
|
||||
| *Lionheart* | Full chipset integration | Sprites for player/enemies, blitter for large terrain objects |
|
||||
| *State of the Art* (demo) | Horizontal + color multiplexing | Unprecedented visual complexity from 8 channels |
|
||||
| *Workbench* | Sprite 0 via Intuition | System mouse pointer — `SetPointer()` / `ClearPointer()` |
|
||||
| *Pinball Dreams* | Minimal sprite use | Ball and flippers as sprites; table as blitter-scrolled bitmap |
|
||||
|
||||
---
|
||||
|
||||
## ECS and AGA Enhancements
|
||||
|
||||
The sprite subsystem was significantly enhanced across chipset generations:
|
||||
|
||||
### ECS (Denise 8373)
|
||||
|
||||
| Feature | Register | Effect |
|
||||
|---|---|---|
|
||||
| Border sprites | `BPLCON3` bit 3 (`BRDSPRT`) | Sprites visible in overscan border area |
|
||||
| Independent resolution | `BPLCON3` bits 7-6 (`SPRES`) | Lores/hires/shres pixel width independent of playfield |
|
||||
| Color bank prep | `BPLCON3` bits 15-13 | Upper bits of color index (used by AGA) |
|
||||
|
||||
→ Full register details: **[ECS Sprite Enhancements](../01_hardware/ecs_a600_a3000/ecs_sprites.md)**
|
||||
|
||||
### AGA (Lisa)
|
||||
|
||||
| Feature | Register | Effect |
|
||||
|---|---|---|
|
||||
| 32/64px width | `FMODE` bits 15-14 (`SPR_FMODE`) | Global sprite width: 16, 32, or 64 pixels |
|
||||
| Color bank (even) | `BPLCON4` bits 7-4 (`ESPRM`) | Even channels use any 16-color slice of 256 |
|
||||
| Color bank (odd) | `BPLCON4` bits 3-0 (`OSPRM`) | Odd channels use a different slice |
|
||||
| Scan doubling | `SSCAN2` | Vertical doubling for 31kHz display modes |
|
||||
|
||||
→ Full register details, DMA format changes, alignment: **[AGA Sprite Enhancements](../01_hardware/aga_a1200_a4000/aga_sprites.md)**
|
||||
|
||||
---
|
||||
|
||||
## Named Antipatterns
|
||||
|
||||
### "The Stolen Pointer"
|
||||
|
||||
Writing directly to `SPR0PTH/L` while Intuition owns sprite 0 for the mouse pointer. The OS will immediately overwrite your pointer on the next VBlank, and you'll corrupt the mouse display.
|
||||
|
||||
```c
|
||||
/* ✗ BAD — conflicts with Intuition's mouse pointer */
|
||||
custom->sprpt[0] = (ULONG)mySpriteData;
|
||||
|
||||
/* ✓ GOOD — use the OS API, or use sprite 1-7 */
|
||||
WORD num = GetSprite(&ss, 2); /* request specific channel 2 */
|
||||
```
|
||||
|
||||
### "The Fast RAM Trap"
|
||||
|
||||
Allocating sprite data in Fast RAM. The DMA engine can **only** access Chip RAM — sprite data in Fast RAM is invisible to the hardware.
|
||||
|
||||
```c
|
||||
/* ✗ BAD — Fast RAM is default on accelerated machines */
|
||||
UWORD *data = AllocMem(size, MEMF_ANY);
|
||||
|
||||
/* ✓ GOOD — always specify MEMF_CHIP for DMA-accessed data */
|
||||
UWORD *data = AllocMem(size, MEMF_CHIP | MEMF_CLEAR);
|
||||
```
|
||||
|
||||
### "The Phantom Sprite"
|
||||
|
||||
Not reloading sprite pointers every frame. After the sprite DMA finishes displaying one frame's data, the pointer is left pointing past the terminator. If not reloaded by the Copper before the next frame, DMA reads **whatever follows in memory** — producing random garbage sprites.
|
||||
|
||||
```asm
|
||||
; ✗ BAD — pointer set once, never refreshed
|
||||
move.l #SprData, SPR2PTH+$DFF000
|
||||
|
||||
; ✓ GOOD — Copper list reloads pointer every frame
|
||||
CopperList:
|
||||
dc.w SPR2PTH, (SprData>>16)&$FFFF
|
||||
dc.w SPR2PTL, SprData&$FFFF
|
||||
```
|
||||
|
||||
### "The Missing Terminator"
|
||||
|
||||
Forgetting the `$0000/$0000` end marker in sprite data. Without it, the DMA engine reads past the sprite data into whatever follows in memory, potentially producing a tall strip of garbage down the screen.
|
||||
|
||||
### "The Color Bleed"
|
||||
|
||||
Not realizing that sprite pairs **share** 3 color registers. Changing colors for sprite 2 also changes sprite 3's colors — they use the same `COLOR21-23` registers.
|
||||
|
||||
```c
|
||||
/* Setting COLOR21 affects BOTH sprite 2 AND sprite 3: */
|
||||
custom->color[21] = 0x0F00; /* both sprites now use red as color 1 */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## FPGA / Emulation Implementation Notes
|
||||
|
||||
For FPGA core developers (MiSTer Minimig, etc.) and emulator authors:
|
||||
|
||||
### Sprite DMA State Machine
|
||||
|
||||
```
|
||||
IDLE → FETCH_POS_CTL → COMPARE_VPOS → FETCH_DATA → SHIFT_OUT → END/REARM
|
||||
```
|
||||
|
||||
- **Fetch timing:** Sprite data must be fetched **before** the horizontal position counter reaches `HSTART` — account for the 2-cycle pipeline delay between fetch and pixel output in Denise/Lisa
|
||||
- **VSTOP comparison:** Must be checked **after** data fetch, not before — off-by-one errors here cause sprites to be 1 line too tall or short
|
||||
- **Terminator detection:** The `$0000/$0000` terminator is detected by the DMA controller, not Denise — it prevents further fetches until the pointer is reloaded
|
||||
|
||||
### Common FPGA Bugs
|
||||
|
||||
| Bug | Symptom | Root Cause |
|
||||
|---|---|---|
|
||||
| Sprite priority inversion | Sprite 4 appears in front of sprite 0 | Priority encoder bit ordering reversed |
|
||||
| Attached mode color index off-by-one | Colors shifted by 1 in attached mode | Even/odd bit concatenation order wrong |
|
||||
| Missing 1-line gap | Multiplexed sprites overlap | VSTOP comparison doesn't account for DMA prefetch cycle |
|
||||
| AGA width mode corruption | Garbage pixels at 32/64px | FMODE not applied to DMA fetch state machine |
|
||||
|
||||
### Reference Implementations
|
||||
|
||||
- **Minimig AGA**: `denise.v` (sprite shift registers, collision), `agnus.v` (DMA slot allocation) — [GitHub](https://github.com/MiSTer-devel/Minimig-AGA_MiSTer)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -235,72 +559,25 @@ The priority can be configured so that sprites appear **between** playfield 1 an
|
|||
| `SPR6PTH/L` | `$DFF138–$DFF13B` | Sprite 6 |
|
||||
| `SPR7PTH/L` | `$DFF13C–$DFF13F` | Sprite 7 |
|
||||
|
||||
These must be set **every frame** — typically by the Copper list during vertical blank. If not set, the sprite DMA will fetch from wherever the pointer was left, producing garbage.
|
||||
|
||||
---
|
||||
|
||||
## OS-Level Sprite API
|
||||
|
||||
```c
|
||||
/* graphics.library — system-friendly sprite access */
|
||||
struct SimpleSprite ss;
|
||||
WORD sprnum;
|
||||
|
||||
/* Obtain a free sprite (Intuition reserves sprite 0 for the pointer): */
|
||||
sprnum = GetSprite(&ss, -1); /* -1 = any available */
|
||||
if (sprnum >= 0)
|
||||
{
|
||||
ss.x = 100;
|
||||
ss.y = 50;
|
||||
ss.height = 16;
|
||||
|
||||
/* Set sprite image data (must be in Chip RAM!): */
|
||||
ChangeSprite(NULL, &ss, spriteData);
|
||||
|
||||
/* Move sprite to screen position: */
|
||||
MoveSprite(NULL, &ss, 100, 50);
|
||||
|
||||
/* Release when done: */
|
||||
FreeSprite(sprnum);
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Mouse Pointer
|
||||
|
||||
```c
|
||||
/* Change the Intuition mouse pointer: */
|
||||
UWORD pointerData[] = {
|
||||
0x0000, 0x0000, /* reserved (position) */
|
||||
/* 16 lines of sprite data: */
|
||||
0x8000, 0xC000, /* line 0 */
|
||||
0xC000, 0xE000, /* line 1 */
|
||||
0xE000, 0xF000, /* line 2 */
|
||||
/* ... etc ... */
|
||||
0x0000, 0x0000 /* terminator */
|
||||
};
|
||||
SetPointer(window, pointerData, height, width, xOffset, yOffset);
|
||||
/* Restore default: */
|
||||
ClearPointer(window);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
| Pitfall | Consequence | Fix |
|
||||
|---|---|---|
|
||||
| Sprite data not in Chip RAM | DMA can't access it — sprite invisible or garbage | Use `AllocMem(MEMF_CHIP)` for sprite data |
|
||||
| Not setting pointer every frame | Sprite DMA reads stale pointer → random data displayed | Use Copper list to reload SPRxPT |
|
||||
| Forgetting `FreeSprite` | Sprite channel stays reserved → other apps can't use it | Always free in cleanup |
|
||||
| Using sprite 0 directly | Conflicts with Intuition's mouse pointer | Use `GetSprite(-1)` to get a free one |
|
||||
These must be set **every frame** — typically by the Copper list during vertical blank. See antipattern ["The Phantom Sprite"](#the-phantom-sprite).
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- HRM: *Amiga Hardware Reference Manual* — Sprites chapter
|
||||
- *Amiga Hardware Reference Manual* — Sprites chapter, DMA time slot allocation (Figure 6-9)
|
||||
- NDK39: `graphics/sprite.h`, `hardware/custom.h`
|
||||
- ADCD 2.1: `GetSprite`, `MoveSprite`, `ChangeSprite`, `FreeSprite`
|
||||
- See also: [copper_programming.md](copper_programming.md) — Copper-driven sprite multiplexing
|
||||
- See also: [rastport.md](rastport.md) — BOBs (software sprites via Blitter)
|
||||
- See also: [memory_types.md](../01_hardware/common/memory_types.md) — sprite data must reside in Chip RAM (sprite DMA)
|
||||
- ADCD 2.1: `GetSprite`, `MoveSprite`, `ChangeSprite`, `FreeSprite`, `AllocSpriteData`, `GetExtSprite`
|
||||
- Phaze101 — *Amiga 68000 Assembly Course* Chapter 7 (sprite programming tutorial)
|
||||
- ChibiAkumas — *68000 Assembly Lessons* (sprite and blitter tutorials)
|
||||
- Codetapper — Game reverse engineering analyses (Jim Power, Lionheart)
|
||||
|
||||
## See Also
|
||||
|
||||
- [OCS Sprite Hardware](../01_hardware/ocs_a500/sprites.md) — register-level reference (SPRxPOS/CTL/DATA)
|
||||
- [ECS Sprite Enhancements](../01_hardware/ecs_a600_a3000/ecs_sprites.md) — border sprites, SPRES, color bank
|
||||
- [AGA Sprite Enhancements](../01_hardware/aga_a1200_a4000/aga_sprites.md) — FMODE width, BPLCON4 palette banks
|
||||
- [Copper Programming](copper_programming.md) — Copper-driven sprite multiplexing
|
||||
- [Animation & GEL System](animation.md) — VSprites, BOBs, AnimObs (higher-level sprite abstractions)
|
||||
- [Memory Types](../01_hardware/common/memory_types.md) — why sprite data must reside in Chip RAM
|
||||
- [DMA Architecture](../01_hardware/common/dma_architecture.md) — scanline slot allocation, bus arbitration, bandwidth calculations
|
||||
|
|
|
|||
5
TODO.md
5
TODO.md
|
|
@ -105,6 +105,7 @@ Articles were scored against [AGENTS.md](../amiga/AGENTS.md) "Deep" criteria:
|
|||
| `aga_blitter.md` | aga | 167 | ✅ Adequate | AGA 32-bit blitter: 4× throughput, FMODE register |
|
||||
| `aga_palette.md` | aga | 134 | ✅ Adequate | AGA 24-bit palette: LOCT/SHCT banks |
|
||||
| `chipset_aga.md` | aga | 126 | ✅ Adequate | AGA chipset architecture overview |
|
||||
| `aga_sprites.md` | aga | 180 | ✅ Adequate | AGA sprite enhancements: 32/64px width, FMODE SPR_FMODE, BPLCON4 ESPRM/OSPRM color bank, DMA format changes, alignment |
|
||||
| `aga_display_modes.md` | aga | 122 | ✅ Adequate | AGA display modes: DBLPAL, Multiscan, HAM8 |
|
||||
| `aga_registers_delta.md` | aga | 97 | ✅ Adequate | OCS→AGA register differences table |
|
||||
| `memory_types.md` | common | 423 | ✅ Deep | Chip/Fast/Slow RAM, DMA constraints, $C00000 alias |
|
||||
|
|
@ -114,6 +115,7 @@ Articles were scored against [AGENTS.md](../amiga/AGENTS.md) "Deep" criteria:
|
|||
| `m68k_cpu.md` | common | 141 | ✅ Adequate | 68000 architecture baseline: registers, exceptions |
|
||||
| `zorro_bus.md` | common | 139 | ✅ Adequate | Zorro II/III expansion bus: AutoConfig, bus arbitration |
|
||||
| `autoconfig.md` | common | 662 | ✅ Deep | AutoConfig protocol: CFGIN/CFGOUT chain, ExpansionRom encoding, nibble-pair map, size codes, shut-up mechanism, Z2/Z3 differences, antipatterns, FPGA notes, boot sequence positioning |
|
||||
| `dma_architecture.md` | common | 581 | ✅ Deep | Scanline slot allocation, even/odd interleaving, DMA channel priority, bitplane DMA budget (DDFSTRT/DDFSTOP/BPLxMOD), bus arbitration (Agnus as bus master, DTACK, wait states), Blitter-Nasty/BLTPRI, per-model bus table (A1000→CD32), Buster/Ramsey/SDMAC/Gayle glue chips, AGA FMODE bandwidth equation, alignment requirements, FPGA implementation notes (Minimig state machine, SDRAM timing, common bugs), bandwidth calculation cookbook with 4 worked examples, best practices, FAQ |
|
||||
| `cdtv_hardware.md` | ocs | 283 | ✅ Adequate | CDTV-specific: CD-ROM controller, front panel, boot ROM |
|
||||
| `custom_registers.md` | ocs | 183 | ✅ Adequate | OCS custom chip register map |
|
||||
| `blitter.md` | ocs | 154 | ✅ Adequate | Blitter engine basics: channels, minterms, line draw |
|
||||
|
|
@ -124,6 +126,7 @@ Articles were scored against [AGENTS.md](../amiga/AGENTS.md) "Deep" criteria:
|
|||
| `chipset_ocs.md` | ocs | 107 | ✅ Adequate | OCS chipset architecture: Agnus/Denise/Paula |
|
||||
| `chipset_ecs.md` | ecs | 119 | ✅ Adequate | ECS chipset changes: Super Agnus, 2 MB Chip RAM |
|
||||
| `ecs_registers_delta.md` | ecs | 114 | ✅ Adequate | OCS→ECS register differences table |
|
||||
| `ecs_sprites.md` | ecs | 98 | ✅ Adequate | ECS sprite enhancements: border sprites (BRDSPRT), independent resolution (SPRES), color bank, half-ECS trap |
|
||||
| `productivity_modes.md` | ecs | 97 | ✅ Adequate | ECS productivity/VGA: 640×480×4, 31 kHz scan |
|
||||
| `chip_ram_expansion.md` | ecs | 77 | ✅ Adequate | A600 trapdoor Chip RAM expansion |
|
||||
| `gary_system_controller.md` | ecs | 576 | ✅ Deep | Gary & Fat Gary: address decoding, bus arbitration, interrupt controller, SCSI/SDMAC integration, AutoConfig controller, chip variants (5719/5391/5393), runtime detection (4 methods), Gary vs Gayle vs Ramsey/Budgie decision guide, 3 named antipatterns, 3 pitfalls, FPGA timing requirements, historical context (TTL→ASIC), modern Northbridge analogy |
|
||||
|
|
@ -249,7 +252,7 @@ Articles were scored against [AGENTS.md](../amiga/AGENTS.md) "Deep" criteria:
|
|||
| `ham_ehb_modes.md` | 442 | ✅ Adequate | HAM6/HAM8 encoding, EHB half-brite, FPGA decoder logic |
|
||||
| `rastport.md` | 403 | ✅ Adequate | RastPort drawing context, layer clipping, text pipeline |
|
||||
| `copper_programming.md` | 319 | ✅ Deep | Copper deep dive: copper list construction, gradient, raster effects |
|
||||
| `sprites.md` | 306 | ❌ Pending | Tier 3 #15: needs antipatterns, pitfalls (HW vs SimpleSprite), FPGA sprite timing |
|
||||
| `sprites.md` | 582 | ✅ Deep | Tier 3 #15 upgrade: DMA timing, CLXCON/CLXDAT collision, V39 ExtSprite API, decision flowchart, 5 named antipatterns, multiplexing techniques, real-world use cases, FPGA notes, competitive landscape |
|
||||
| `gfx_base.md` | 237 | ✅ Adequate | GfxBase, chipset detection, display pipeline overview |
|
||||
| `text_fonts.md` | 215 | ❌ Pending | Tier 3 #16: needs cookbook (bitmap vs outline), font spacing pitfalls, ColorFont layout |
|
||||
| `copper.md` | 124 | ✅ Adequate | Copper coprocessor basics, instruction format, UCopList |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue