mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
Expand documentation suite: 30+ articles enriched with diagrams, code examples, and hardware details
Graphics: text_fonts (bitmap layout, styles), sprites (DMA, multiplexing), gfx_base (chipset detection), rastport (draw modes, clipping), ham_ehb (mermaid fixes), display_modes (HAM palettes) Devices: scsi (per-model interfaces, Gayle limits, CD-ROM, native vs vendor drivers), console (ANSI sequences, CON:/RAW:), parallel (CIA registers, pinout), timer (resource exhaustion), gameport (quadrature, XOR state) Libraries: workbench (WBStartup, AppWindow/Icon/MenuItem), rexxsyslib (ARexx port hosting, command parsing), diskfont (font directory, colour fonts), keymap (rawkey codes, dead keys), locale (catalogue system, date formatting), layers (ClipRect, refresh types), utility (TagItem chains), icon (DiskObject, ToolTypes), iffparse (IFF structure, ByteRun1), expansion (Zorro AutoConfig) Networking: tcp_ip_stacks (major rewrite - Amiga vs Unix architecture, SANA-II pipeline, PPP/SLIP dial-up, Ethernet cards, MiSTer), bsdsocket (pure API ref), sana2 (buffer hooks, driver requirements), protocols (all code examples). Deduplicated overlap between the three files. Toolchain: debugging (Enforcer patterns, SnoopDOS, GDB remote, kprintf checklist), sasc (pragma encoding, __saveds idioms), stormc (NEW - StormC IDE, C++, PowerPC) References: error_codes (DOS, Exec, trackdisk, Intuition error tables) Driver development: rtg_driver (Native driver analysis, P96 tuning) All 22 README indexes updated. Root README synced with stormc.md entry.
This commit is contained in:
parent
0ded078134
commit
f61c26b542
38 changed files with 6402 additions and 1065 deletions
|
|
@ -1,102 +1,305 @@
|
|||
[← Home](../README.md) · [Graphics](README.md)
|
||||
|
||||
# Hardware Sprites — SimpleSprite, MoveSprite
|
||||
# Hardware Sprites — DMA Engine, Multiplexing, and Tricks
|
||||
|
||||
## Overview
|
||||
|
||||
The Amiga has **8 hardware sprites**, each 16 pixels wide with 3 colours + transparent. Sprites are DMA-driven — the Copper sets their pointers each frame and the display hardware renders them with zero CPU overhead.
|
||||
The Amiga has **8 hardware sprites**, each 16 pixels wide with 3 colours + 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.
|
||||
|
||||
Sprite 0 is reserved by Intuition for the **mouse pointer**. Sprites 1–7 are available for application use.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Chip RAM"
|
||||
SD0["Sprite 0 data<br/>(mouse pointer)"]
|
||||
SD1["Sprite 1 data"]
|
||||
SD2["Sprite 2-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["Colour DAC<br/>→ Video Out"]
|
||||
end
|
||||
|
||||
SD0 --> DMA
|
||||
SD1 --> DMA
|
||||
SD2 --> DMA
|
||||
|
||||
subgraph "Copper"
|
||||
COP["Copper List"] -->|"Set SPRxPTH/L<br/>every frame"| DMA
|
||||
end
|
||||
|
||||
style DMA fill:#e8f4fd,stroke:#2196f3,color:#333
|
||||
style COP fill:#fff9c4,stroke:#f9a825,color:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sprite DMA Data Format
|
||||
|
||||
Each sprite scanline is two words (4 bytes):
|
||||
Each sprite is stored as a contiguous block in Chip RAM:
|
||||
|
||||
```
|
||||
Word 0 (DATA): bits 15–0 = pixel colour bit 0 for this line
|
||||
Word 1 (DATB): bits 15–0 = pixel colour bit 1 for this line
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Header Word 0: VSTART/HSTART │ Position control
|
||||
│ Header Word 1: VSTOP/Control │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Line 0: DATA word (bit 0 of each pixel) │ ← 16 pixels per line
|
||||
│ Line 0: DATB word (bit 1 of each pixel) │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Line 1: DATA word │
|
||||
│ Line 1: DATB word │
|
||||
├──────────────────────────────────────────┤
|
||||
│ ...repeat for each line... │
|
||||
├──────────────────────────────────────────┤
|
||||
│ Terminator: 0x0000 │ End marker
|
||||
│ Terminator: 0x0000 │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Pixel Colour Encoding
|
||||
|
||||
```
|
||||
Pixel colour = (DATB_bit << 1) | DATA_bit
|
||||
00 = transparent
|
||||
01 = colour 1 (from sprite palette)
|
||||
10 = colour 2
|
||||
11 = colour 3
|
||||
|
||||
00 = transparent (playfield shows through)
|
||||
01 = sprite colour 1
|
||||
10 = sprite colour 2
|
||||
11 = sprite colour 3
|
||||
```
|
||||
|
||||
### Sprite Header
|
||||
### Header Bit Layout
|
||||
|
||||
```
|
||||
WORD 0: VSTART (vertical start position) + HSTART high bits
|
||||
WORD 1: VSTOP (vertical stop position) + control bits
|
||||
Word 0 (SPRxPOS):
|
||||
Bits 15–8: VSTART[7:0] (vertical start line, low 8 bits)
|
||||
Bits 7–0: HSTART[8:1] (horizontal start, in low-res pixels ÷ 2)
|
||||
|
||||
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)
|
||||
Bit 0: ATTACH (1 = attached to previous sprite)
|
||||
```
|
||||
|
||||
### End marker
|
||||
```
|
||||
WORD 0: 0x0000
|
||||
WORD 1: 0x0000
|
||||
```
|
||||
> [!IMPORTANT]
|
||||
> **Horizontal position is in low-res pixel units ÷ 2** — a sprite can only be positioned on even low-res pixel boundaries. In hires/superhires modes, this means sprites have coarser horizontal positioning than playfield pixels.
|
||||
|
||||
---
|
||||
|
||||
## Sprite Colour Palette
|
||||
|
||||
| Sprite pair | Colour registers | Custom addresses |
|
||||
|---|---|---|
|
||||
| 0–1 | `COLOR17`–`COLOR19` | `$DFF1A2`–`$DFF1A6` |
|
||||
| 2–3 | `COLOR21`–`COLOR23` | `$DFF1AA`–`$DFF1AE` |
|
||||
| 4–5 | `COLOR25`–`COLOR27` | `$DFF1B2`–`$DFF1B6` |
|
||||
| 6–7 | `COLOR29`–`COLOR31` | `$DFF1BA`–`$DFF1BE` |
|
||||
Each pair of sprites shares 3 colour registers (colour 0 = transparent for all):
|
||||
|
||||
Sprite pairs can be **attached** to form a single 15-colour sprite (using both sets of 2 bits = 4 bits per pixel).
|
||||
| Sprite Pair | Colour Registers | Custom Addresses | Notes |
|
||||
|---|---|---|---|
|
||||
| 0–1 | `COLOR17`–`COLOR19` | `$DFF1A2`–`$DFF1A6` | Pair with mouse pointer |
|
||||
| 2–3 | `COLOR21`–`COLOR23` | `$DFF1AA`–`$DFF1AE` | |
|
||||
| 4–5 | `COLOR25`–`COLOR27` | `$DFF1B2`–`$DFF1B6` | |
|
||||
| 6–7 | `COLOR29`–`COLOR31` | `$DFF1BA`–`$DFF1BE` | |
|
||||
|
||||
```c
|
||||
/* Set sprite 0-1 colours directly: */
|
||||
custom->color[17] = 0xF00; /* red */
|
||||
custom->color[18] = 0x0F0; /* green */
|
||||
custom->color[19] = 0xFFF; /* white */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OS-Level Sprite API
|
||||
## Attached Sprites — 15 Colours
|
||||
|
||||
Two sprites from the same pair can be **attached** to form a single 15-colour (+ transparent) sprite:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Normal (2 independent sprites)"
|
||||
S0["Sprite 0<br/>3 colours"] --- S1["Sprite 1<br/>3 colours"]
|
||||
end
|
||||
|
||||
subgraph "Attached (1 wide-colour sprite)"
|
||||
SA["Sprites 0+1 attached<br/>4 bits per pixel<br/>15 colours + transparent"]
|
||||
end
|
||||
|
||||
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 colour index. The 4-bit value indexes into colour registers 16–31.
|
||||
|
||||
```c
|
||||
/* graphics.library */
|
||||
struct SimpleSprite ss;
|
||||
WORD sprnum;
|
||||
|
||||
/* Obtain a free sprite: */
|
||||
sprnum = GetSprite(&ss, -1); /* -1 = any available */
|
||||
if (sprnum >= 0) {
|
||||
ss.x = 100;
|
||||
ss.y = 50;
|
||||
ss.height = 16;
|
||||
|
||||
/* Move sprite to position: */
|
||||
MoveSprite(NULL, &ss, 100, 50);
|
||||
|
||||
/* Set sprite image data: */
|
||||
ChangeSprite(NULL, &ss, spriteData);
|
||||
|
||||
/* Release: */
|
||||
FreeSprite(sprnum);
|
||||
}
|
||||
/* Enable attachment: set bit 0 of odd sprite's CTL word */
|
||||
oddSpriteData[1] |= 0x0001; /* ATTACH bit */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sprite Multiplexing — More Than 8 Sprites
|
||||
|
||||
The hardware only supports 8 simultaneous sprites, but demos and games use **multiplexing** to display many more by reusing sprite channels on different scanlines:
|
||||
|
||||
```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 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
|
||||
|
||||
style COP fill:#fff9c4,stroke:#f9a825,color:#333
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Multiplexing Rules
|
||||
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
## AGA Sprite Enhancements
|
||||
|
||||
| Feature | OCS/ECS | AGA |
|
||||
|---|---|---|
|
||||
| Width | 16 pixels | 16, 32, or 64 pixels (via FMODE) |
|
||||
| Colours (single) | 3 + transparent | 3 + transparent |
|
||||
| Colours (attached) | 15 + transparent | 15 + transparent |
|
||||
| Horizontal resolution | Low-res ÷ 2 | Same (unchanged) |
|
||||
|
||||
```c
|
||||
/* AGA: wider sprites via FMODE register */
|
||||
custom->fmode = 3; /* 4× fetch — sprites become 64 pixels wide */
|
||||
/* WARNING: this also affects bitplane fetch! */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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 */
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph "Default Priority (front → back)"
|
||||
direction LR
|
||||
S01["Sprites 0-1"] --- S23["Sprites 2-3"] --- S45["Sprites 4-5"] --- S67["Sprites 6-7"] --- PF1["Playfield 1"] --- PF2["Playfield 2"]
|
||||
end
|
||||
|
||||
style S01 fill:#ffcdd2,stroke:#c62828,color:#333
|
||||
style PF1 fill:#c8e6c9,stroke:#2e7d32,color:#333
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
## Sprite Pointer Registers
|
||||
|
||||
| Register | Address | Sprite |
|
||||
|---|---|---|
|
||||
| `SPR0PTH/L` | `$DFF120–$DFF122` | Sprite 0 |
|
||||
| `SPR1PTH/L` | `$DFF124–$DFF126` | Sprite 1 |
|
||||
| `SPR2PTH/L` | `$DFF128–$DFF12A` | Sprite 2 |
|
||||
| `SPR3PTH/L` | `$DFF12C–$DFF12E` | Sprite 3 |
|
||||
| `SPR4PTH/L` | `$DFF130–$DFF132` | Sprite 4 |
|
||||
| `SPR5PTH/L` | `$DFF134–$DFF136` | Sprite 5 |
|
||||
| `SPR6PTH/L` | `$DFF138–$DFF13A` | Sprite 6 |
|
||||
| `SPR7PTH/L` | `$DFF13C–$DFF13E` | Sprite 7 |
|
||||
| `SPR0PTH/L` | `$DFF120–$DFF123` | Sprite 0 (mouse pointer) |
|
||||
| `SPR1PTH/L` | `$DFF124–$DFF127` | Sprite 1 |
|
||||
| `SPR2PTH/L` | `$DFF128–$DFF12B` | Sprite 2 |
|
||||
| `SPR3PTH/L` | `$DFF12C–$DFF12F` | Sprite 3 |
|
||||
| `SPR4PTH/L` | `$DFF130–$DFF133` | Sprite 4 |
|
||||
| `SPR5PTH/L` | `$DFF134–$DFF137` | Sprite 5 |
|
||||
| `SPR6PTH/L` | `$DFF138–$DFF13B` | Sprite 6 |
|
||||
| `SPR7PTH/L` | `$DFF13C–$DFF13F` | Sprite 7 |
|
||||
|
||||
These must be set every frame — typically via the Copper list.
|
||||
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 |
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- HRM: *Amiga Hardware Reference Manual* — Sprites chapter
|
||||
- NDK39: `graphics/sprite.h`
|
||||
- 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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue