docs: expand 7 Tier 3 articles to Deep quality

- gcc_amiga.md: 101→606 lines — pipeline, Docker, platform builds, flags, antipatterns, FAQ
- trackdisk.md: 178→428 lines — MFM encoding, 16-command reference, antipatterns, FPGA impact
- console.md: 244→470 lines — decision guide, TUI/progress cookbooks, antipatterns, pitfalls
- layers.md: 224→739 lines — ClipRect engine, LVO API, backfill hooks, 4 antipatterns, optimization
- text_fonts.md: 215→708 lines — ColorFont, Compugraphic outlines, 3 cookbooks, 4 antipatterns
- windows.md: 370→778 lines — 5 antipatterns, decision guide, 3 cookbooks, modern analogies
- menus.md: 378→695 lines — render chain diagram, 5 antipatterns, lifecycle cookbook, 6 FAQ
This commit is contained in:
Ilia Sharin 2026-05-12 22:04:16 -04:00
parent ab88118dc1
commit 9f5d9de1ed
8 changed files with 3129 additions and 209 deletions

View file

@ -2,14 +2,10 @@
# console.device — Text Terminal I/O
## Overview
Every CLI/Shell window on the Amiga is powered by `console.device`. It is a software terminal emulator that sits between raw keyboard input and Intuition window rendering: it translates keycodes into ASCII characters, parses ANSI escape sequences for cursor control and color, and renders text through the window's RastPort. If you need a text interface in an Intuition window — a debugger console, a text editor, a terminal emulator — console.device is the foundation.
`console.device` provides an ANSI-compatible text terminal within Intuition windows. It handles:
- **Input**: translating raw keycodes from `input.device` into ASCII/ANSI characters
- **Output**: rendering text, parsing escape sequences for cursor control, color, and formatting
- **Clipboard**: cut/copy/paste integration
Every CLI/Shell window is backed by a console.device unit. Applications can open their own console units in any Intuition window for text I/O without implementing their own keyboard translation or cursor rendering.
> [!NOTE]
> For simple file I/O, use [dos.library](../07_dos/file_io.md) with `CON:` windows instead of opening console.device directly. The `CON:` and `RAW:` handlers wrap console.device and provide buffered line editing, window management, and automatic cleanup.
```mermaid
flowchart LR
@ -238,7 +234,237 @@ BPTR raw = Open("RAW:0/0/640/200/Raw Input", MODE_OLDFILE);
## References
- NDK39: `devices/conunit.h`, `devices/console.h`
### NDK Headers
- `devices/conunit.h` — unit type constants (`CONU_STANDARD`, etc.)
- `devices/console.h` — console-specific structures
### Documentation
- ADCD 2.1: console.device autodocs
- See also: [keyboard.md](keyboard.md) — raw keycode to console.device pipeline
- See also: [input.md](input.md) — input handler chain
- *Amiga ROM Kernel Reference Manual: Devices* — console chapter
### Related Knowledge Base Articles
- [keyboard.md](keyboard.md) — raw keycode to console.device pipeline
- [input.md](input.md) — input handler chain
- [cli_shell.md](../07_dos/cli_shell.md) — Shell architecture built on console.device
- [windows.md](../09_intuition/windows.md) — Intuition window management
---
## Decision Guide: How to Render Text
```mermaid
flowchart TD
START{"Need text output?"} --> WHERE{"Where?"}
WHERE -->|"Shell window"| CON{"Use CON: handler<br>via dos.library"}
WHERE -->|"Own Intuition window"| COMPLEX{"Need cursor, colors,<br>line editing?"}
COMPLEX -->|Yes| CD{"Use console.device"}
COMPLEX -->|No| RP{"Use RastPort text<br>functions directly"}
WHERE -->|"Full-screen TUI"| CD
WHERE -->|"Custom text editor"| CD
```
| Approach | Use When | Pros | Cons |
|----------|----------|------|------|
| **CON: / RAW:** | Simple text I/O in a window | Easiest — just `Open()` / `Write()` | Limited control over rendering |
| **console.device** | Full-screen TUI, text editor | ANSI escape sequences, cursor, colors | Must manage I/O requests manually |
| **RastPort text** | Custom text rendering, game UI | Full pixel-level control | No built-in cursor, scrolling, or input handling |
| **Intuition gadgets** | Forms, menus, buttons | Standard UI elements | Not suitable for free-form text |
---
## Use-Case Cookbook
### Full-Screen Text UI (Status Display)
```c
/* con_tui.c — full-screen TUI using console.device */
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>
void ConPuts(struct IOStdReq *con, const char *str)
{
con->io_Command = CMD_WRITE;
con->io_Data = (APTR)str;
con->io_Length = -1;
DoIO((struct IORequest *)con);
}
void run_tui(struct Window *win, struct IOStdReq *con)
{
/* Clear screen and draw a frame */
ConPuts(con, "\033[2J"); /* clear screen */
ConPuts(con, "\033[1;1H"); /* home cursor */
ConPuts(con, "\033[7m Status Monitor \033[0m\n"); /* inverse title */
ConPuts(con, "\033[3;1H\033[33mCPU: \033[32m7.14 MHz\033[0m");
ConPuts(con, "\033[4;1H\033[33mChip: \033[32m512 KB\033[0m");
ConPuts(con, "\033[20;1H\033[7m Press ESC to exit \033[0m");
/* Main loop — wait for keypress */
char buf[8];
con->io_Command = CMD_READ;
con->io_Data = (APTR)buf;
con->io_Length = sizeof(buf);
DoIO((struct IORequest *)con);
/* Check if ESC was pressed (0x1B) */
}
```
### Progress Bar Using Escape Sequences
```c
void DrawProgressBar(struct IOStdReq *con, int row, int percent)
{
char buf[80];
int bar_width = 40;
int filled = (percent * bar_width) / 100;
/* Move to row, column 10 */
RawDoFmt("\033[%ld;10H", (RAWARG)&row, NULL, buf);
ConPuts(con, buf);
/* Draw filled portion in green, empty in black */
ConPuts(con, "\033[42m"); /* green background */
for (int i = 0; i < filled; i++) ConPuts(con, " ");
ConPuts(con, "\033[40m"); /* black background */
for (int i = filled; i < bar_width; i++) ConPuts(con, " ");
ConPuts(con, "\033[0m"); /* reset */
/* Percentage text */
RawDoFmt(" %ld%%", (RAWARG)&percent, NULL, buf);
ConPuts(con, buf);
}
```
---
## Best Practices
1. Use `CON:` via dos.library for simple text I/O — avoid opening console.device directly unless you need escape sequence control
2. Always save/restore cursor position around multi-step output operations
3. Reset all text attributes (`\033[0m`) at the end of every output operation — stale attributes cause rendering bugs
4. Use `CONU_SNIPMAP` (OS 3.0+) for clipboard support in text editors
5. Set scroll margins with `\033[t` / `\033[b` for status bars that stay fixed
6. Always abort pending reads before closing the device — see Proper Shutdown above
7. Handle both ASCII and escape-sequence input when reading — cursor keys arrive as `\033[A` etc.
---
## Named Antipatterns
### "The Leaking Read" — Pending I/O on Shutdown
```c
/* BAD: Pending async read never completed */
con->io_Command = CMD_READ;
con->io_Data = (APTR)buffer;
con->io_Length = 256;
SendIO((struct IORequest *)con);
/* ... user closes window ... */
CloseDevice((struct IORequest *)con); /* CRASH: pending I/O */
```
```c
/* CORRECT: Abort pending I/O before closing */
if (!CheckIO((struct IORequest *)con))
{
AbortIO((struct IORequest *)con);
WaitIO((struct IORequest *)con);
}
CloseDevice((struct IORequest *)con);
```
### "The Attribute Leak" — Stale Colors After Output
```c
/* BAD: Set color but never reset — next output inherits it */
ConPuts(con, "\033[31mError!");
/* Next write is still red! */
ConPuts(con, "Normal text"); /* Still red! */
```
```c
/* CORRECT: Always reset attributes */
ConPuts(con, "\033[31mError!\033[0m");
ConPuts(con, "Normal text"); /* Correctly default color */
```
### "The Blocking Shell" — DoIO Read Freezes UI
```c
/* BAD: DoIO on CMD_READ blocks forever if no input comes */
con->io_Command = CMD_READ;
con->io_Length = 1;
DoIO((struct IORequest *)con); /* Frozen — can't handle IDCMP events */
```
```c
/* CORRECT: Use SendIO + Wait with IDCMP signals */
con->io_Command = CMD_READ;
con->io_Length = 1;
SendIO((struct IORequest *)con);
ULONG conSig = 1 << conPort->mp_SigBit;
ULONG winSig = 1 << window->UserPort->mp_SigBit;
ULONG sigs = Wait(conSig | winSig);
if (sigs & winSig) {
/* Handle IDCMP event (close window, etc.) */
AbortIO((struct IORequest *)con);
WaitIO((struct IORequest *)con);
}
if (sigs & conSig) {
WaitIO((struct IORequest *)con);
/* Process input character */
}
```
---
## Pitfalls & Common Mistakes
### 1. Console Position is 1-Based, Not 0-Based
**Symptom:** Text appears one row/column off from expected position.
**Cause:** ANSI `H` command uses 1-based coordinates: `\033[1;1H` is the top-left corner, not `\033[0;0H`.
**Fix:** Always add 1 to your 0-based coordinates.
### 2. Writing to a Closed Window
**Symptom:** Guru meditation or silent crash.
**Cause:** Console.device renders through the window's RastPort. If the window is closed (user clicked close gadget), writing to the console device dereferences a stale window pointer.
**Fix:** Monitor `IDCMP_CLOSEWINDOW` and abort all console I/O before closing.
### 3. Buffer Overrun on Read
**Symptom:** Memory corruption.
**Cause:** `CMD_READ` returns up to `io_Length` bytes. If you allocated a smaller buffer, the device writes past the end.
**Fix:** Always ensure `io_Length <= sizeof(buffer)`.
---
## FAQ
**Q: What is the difference between CON: and RAW:?**
A: `CON:` is line-buffered — input is collected until the user presses Enter, with line editing (backspace, cursor keys). `RAW:` delivers each keypress immediately with no editing. Use `RAW:` for games and interactive TUIs; use `CON:` for command-line tools.
**Q: Can I use console.device without an Intuition window?**
A: Yes — open with `CONU_LIBRARY` unit type. This gives access to the keymap translation without rendering. Useful for translating raw keycodes to ASCII.
**Q: How do I create a console with a specific font size?**
A: Open the window with the desired font, then open console.device on that window. The console uses the window's RastPort font. Set the font with `SetFont(window->RPort, myFont)` before opening the console.
**Q: Why does my text look wrong after a screen drag?**
A: Console.device renders into the window's RastPort. When the screen is dragged, the console does not automatically redraw. You must handle `IDCMP_NEWSIZE` and redraw the console content.
---

View file

@ -1,10 +1,11 @@
[← Home](../README.md) · [Devices](README.md)
# trackdisk.device — Floppy Disk DMA Controller
# trackdisk.device — Floppy Disk Controller
## Overview
Every Amiga shipped with a floppy drive — and every Amiga program that loaded from disk went through `trackdisk.device`. It is the lowest-level software interface to the Amiga's custom-chip floppy DMA controller, providing sector-level read/write access to 3.5" double-density disks. The Amiga's floppy controller is unusual for its era: it reads and writes **raw MFM bitstreams** rather than decoded data, giving software complete control over on-disk format. This is why the Amiga could read PC, Mac, and ST disks — and why copy-protected Amiga disks are so hard to duplicate.
`trackdisk.device` interfaces with the Amiga's floppy disk controller — a custom DMA engine that reads and writes raw MFM-encoded data from double-density 3.5" disks. It provides block-level access (512 bytes/sector, 11 sectors/track, 80 tracks × 2 sides = 1,760 sectors = 880 KB per disk).
> [!NOTE]
> Most application developers should use [dos.library](../07_dos/file_io.md) (`Open()`, `Read()`, `Write()`) instead of trackdisk.device directly. The filesystem handles sector-to-file mapping, buffering, and error recovery. Use trackdisk.device only when you need raw disk access: disk duplication, custom bootblocks, copy-protection analysis, or low-level diagnostics.
---
@ -42,17 +43,22 @@ graph LR
### MFM Encoding
The disk stores data in **Modified Frequency Modulation** format. Each byte becomes 16 bits on disk (clock + data interleaved):
The disk stores data in **Modified Frequency Modulation** format. Each data byte becomes **16 bits** on disk — a clock bit and a data bit interleaved. The clock bit is determined by a simple rule: write a `1` clock only when both the previous data bit and current data bit are `0`.
```
Data bit: 1 0 1 1 0 0 0 1
MFM: 01 10 01 01 10 10 10 01
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
c/d pairs (clock bit inserted before each data bit)
Data byte: $A1 = 1010 0001
MFM encoding (data bits underlined, clock bits computed):
Data: 1 0 1 0 0 0 0 1
Clock: 0 1 0 1 1 1 1 0
MFM: 01 10 01 10 10 10 10 01
= $4489 (the famous Amiga sync word)
```
A raw track is ~12,668 bytes of MFM data (including gaps, sync words, and sector headers).
> **Key insight**: The Amiga floppy controller does **not** decode MFM in hardware — it DMA-transfers the raw MFM bitstream directly to Chip RAM. Decoding is done in software by `trackdisk.device`. This design choice is what gives the Amiga its unique ability to read and write non-standard disk formats.
### Track Format (AmigaDOS)
```
@ -67,6 +73,20 @@ Track = 11 sectors, each containing:
Gaps between sectors: variable-length padding
```
```mermaid
flowchart LR
subgraph TRACK["One Track = 11 Sectors + Gaps"]
direction LR
GAP1["Gap"] --> S0["Sector 0<br>Sync+Header+Data"] --> GAP2["Gap"] --> S1["Sector 1"] --> S2["..."] --> S10["Sector 10"] --> GAP3["Gap"]
end
subgraph SECTOR["Single Sector Layout"]
direction LR
SYNC["$4489 $4489<br>Sync"] --> HDR["4 longs<br>Header"] --> HCRC["Checksum<br>1 long"] --> DATA["512 bytes<br>Data"] --> DCRC["Checksum<br>1 long"]
end
```
> **Why sync words matter**: The controller synchronizes to the MFM bitstream by looking for the pattern `$4489 $4489`, which violates normal MFM encoding rules (a missing clock pulse). This makes sync words unambiguous — they can never appear in normal data. Copy protection schemes exploit this by placing non-standard sync patterns that normal disk copiers cannot read.
---
## Using trackdisk.device
@ -172,7 +192,237 @@ Games often bypass trackdisk.device for speed and copy protection:
## References
- NDK39: `devices/trackdisk.h`, `resources/disk.h`
### NDK Headers
- `devices/trackdisk.h``struct IOExtTD`, command constants
- `resources/disk.h` — disk resource management
### Documentation
- HRM: *Amiga Hardware Reference Manual* — Disk Controller chapter
- ADCD 2.1: trackdisk.device autodocs
- See also: [filesystem.md](../07_dos/filesystem.md) — FFS/OFS block format on top of trackdisk
- *Amiga ROM Kernel Reference Manual: Devices* — trackdisk chapter
### Related Knowledge Base Articles
- [filesystem.md](../07_dos/filesystem.md) — FFS/OFS block format on top of trackdisk
- [boot_block](../02_boot_sequence/dos_boot.md) — bootblock format and loading sequence
- [custom_loaders_and_drm.md](../05_reversing/custom_loaders_and_drm.md) — copy protection schemes that exploit raw disk access
- [io_requests.md](../06_exec_os/io_requests.md) — IORequest, DoIO, SendIO protocol
---
## trackdisk vs. dos.library — Decision Guide
```mermaid
flowchart TD
START{"Need disk access?"} --> WHAT{"What level?"}
WHAT -->|"File read/write"| DOS{"Use dos.library<br>Open/Read/Write"}
WHAT -->|"Raw sector access"| WHY{"Why?"}
WHY -->|"Bootblock, disk tool"| TD{"Use trackdisk.device"}
WHY -->|"Copy protection analysis"| TD
WHY -->|"Non-Amiga disk format"| RAW{"Use raw disk DMA"}
WHY -->|"Disk duplication"| TD
```
| Criterion | dos.library | trackdisk.device | Raw DMA (CIA+custom) |
|-----------|-------------|------------------|----------------------|
| **Level** | File I/O | Sector I/O | MFM bitstream |
| **Buffer** | Any RAM | Chip RAM only | Chip RAM only |
| **Error recovery** | Automatic (retries) | Built-in (retries) | Your responsibility |
| **Filesystem aware** | Yes | No | No |
| **Write protection** | Respects WP tab | Respects WP tab | Must check yourself |
| **Disk change detection** | Automatic | TD_CHANGENUM | Must poll CIA pins |
| **Motor control** | Automatic | Manual (TD_MOTOR) | Manual (CIA PRB) |
| **Typical use** | Applications | Disk tools, bootblocks | Games, demos, protection |
| **FPGA impact** | None — OS handles it | Must emulate whole-track cache | Must emulate exact DMA timing |
---
## Command Reference
| Command | LVO | Description |
|---------|-----|-------------|
| `CMD_READ` | 0 | Read sectors from disk |
| `CMD_WRITE` | 1 | Write sectors to disk |
| `CMD_UPDATE` | 2 | Flush write buffer to disk |
| `CMD_CLEAR` | 3 | Clear read-ahead buffer |
| `TD_CHANGENUM` | 6 | Get disk change counter |
| `TD_CHANGESTATE` | 7 | Check if disk is in drive (0=in, 1=empty) |
| `TD_PROTSTATUS` | 8 | Check write-protect status |
| `TD_ADDCHANGEINT` 9 | Add disk-change interrupt handler |
| `TD_REMCHANGEINT` 10 | Remove disk-change interrupt handler |
| `TD_GETNUMTRACKS` 12 | Get total number of tracks |
| `TD_ADDTRACK` | 13 | Add track buffer (AmigaOS 2.0+) |
| `TD_FORMAT` | 4 | Format a track (write raw MFM) |
| `TD_RAWREAD` | 10 | Read raw MFM track (AmigaOS 2.0+) |
| `TD_RAWWRITE` | 11 | Write raw MFM track (AmigaOS 2.0+) |
| `TD_GETDRIVETYPE` 15 | Get drive type (AmigaOS 3.0+) |
> [!WARNING]
> All I/O buffers passed to `CMD_READ` and `CMD_WRITE` **must be in Chip RAM**. The floppy DMA engine can only access Chip RAM. Using Fast RAM causes silent data corruption.
---
## Best Practices
1. Always use `MEMF_CHIP` for trackdisk I/O buffers — the floppy DMA engine cannot reach Fast RAM
2. Call `CMD_UPDATE` after writing — the device buffers writes internally
3. Check `TD_CHANGESTATE` before critical operations — the user may have ejected the disk
4. Turn off the motor with `TD_MOTOR(0)` when done — leaving it on wears out the disk and drive
5. Use `DoIO()` (synchronous) for simple operations; `SendIO()` only when overlapping disk I/O with computation
6. Handle `TDERR_DiskChange` errors — always verify the disk hasn't been swapped mid-operation
7. For disk-to-disk copy, read a full track then write a full track — not sector by sector
---
## Named Antipatterns
### "The Fast RAM Buffer" — DMA-Corrupted Reads
```c
/* BAD: AllocMem without MEMF_CHIP returns Fast RAM on expanded systems */
UBYTE *buf = AllocMem(512, 0); /* could be Fast RAM */
diskReq->iotd_Req.io_Data = buf;
diskReq->iotd_Req.io_Command = CMD_READ;
DoIO((struct IORequest *)diskReq);
/* DMA writes to Chip RAM, but buf points to Fast RAM — silent garbage! */
```
```c
/* CORRECT: Always use MEMF_CHIP */
UBYTE *buf = AllocMem(512, MEMF_CHIP | MEMF_CLEAR);
```
### "The Motor Hog" — Wearing Out the Drive
```c
/* BAD: Motor left running forever */
diskReq->iotd_Req.io_Command = TD_MOTOR;
diskReq->iotd_Req.io_Length = 1; /* motor on */
DoIO((struct IORequest *)diskReq);
/* ... read disk ... */
/* ... forget to turn motor off ... */
```
```c
/* CORRECT: Turn off motor after use */
/* ... after all disk operations ... */
diskReq->iotd_Req.io_Command = TD_MOTOR;
diskReq->iotd_Req.io_Length = 0; /* motor off */
DoIO((struct IORequest *)diskReq);
```
### "The Stale Disk" — Ignoring Disk Changes
```c
/* BAD: Assumes the same disk is still in the drive */
/* User swaps disk between reads — data is from wrong disk! */
ULONG oldCount = diskReq->iotd_Req.io_Actual;
/* ... much later ... */
ReadSector(buf, 0);
/* No check if disk was changed */
```
```c
/* CORRECT: Check change counter before each operation */
diskReq->iotd_Req.io_Command = TD_CHANGENUM;
DoIO((struct IORequest *)diskReq);
if (diskReq->iotd_Req.io_Actual != oldCount) {
/* Disk was changed — re-read disk info */
}
```
---
## Pitfalls & Common Mistakes
### 1. Forgetting CMD_UPDATE After Writes
**Symptom:** Written data disappears after reboot.
**Cause:** trackdisk.device buffers writes internally. `CMD_WRITE` stores data in the device's track buffer, but does not flush to physical disk until `CMD_UPDATE` is issued.
**Fix:**
```c
diskReq->iotd_Req.io_Command = CMD_WRITE;
DoIO((struct IORequest *)diskReq);
/* MUST call CMD_UPDATE to flush: */
diskReq->iotd_Req.io_Command = CMD_UPDATE;
DoIO((struct IORequest *)diskReq);
```
### 2. Wrong Byte Offset Calculation
**Symptom:** Reading wrong sectors, corrupt data.
**Cause:** The `io_Offset` field uses **byte offsets from disk start**, not track/sector numbers.
**Fix:** Calculate offset correctly:
```c
/* Linear byte offset for track T, side S, sector SEC: */
ULONG offset = ((T * 2 + S) * 11 + SEC) * 512;
/* Example: Track 5, side 1, sector 3 = ((5*2+1)*11 + 3) * 512 = 63,488 */
```
### 3. Unit Number Out of Range
**Symptom:** `OpenDevice()` succeeds but operations fail or access wrong drive.
**Cause:** Unit numbers are DF0:=0, DF1:=1, DF2:=2, DF3:=3. Higher numbers are invalid.
**Fix:** Always validate the unit number and handle `OpenDevice()` errors.
---
## Use Cases
| Use Case | Approach | Notes |
|----------|----------|-------|
| Loading files | Use [dos.library](../07_dos/file_io.md) | Never use trackdisk directly |
| Bootblock installation | `CMD_WRITE` sector 0 | Must be Chip RAM buffer |
| Disk duplication | Read full track, write full track | Track-at-once is faster than sector-at-once |
| Copy protection detection | `TD_RAWREAD` (OS 2.0+) or raw DMA | Analyze non-standard sync words |
| Disk format tool | `TD_FORMAT` | Writes MFM sector headers + data |
| Non-Amiga disk (PC/ST) | `TD_RAWREAD` + custom MFM decode | PC uses different sector format |
| Write-protect detection | `TD_PROTSTATUS` | Returns 0=writable, 1=protected |
---
## Impact on FPGA / Emulation
The floppy controller is one of the trickiest subsystems to emulate accurately:
| Aspect | FPGA/Emulation Challenge |
|--------|--------------------------|
| **Raw MFM DMA** | Must transfer the complete raw MFM bitstream to/from memory, not decoded data |
| **Whole-track caching** | trackdisk.device caches full tracks — emulation must preserve this for correct seek timing |
| **CIA-B floppy control** | Motor, step, side select via CIA-B PRA/PRB registers — must be cycle-accurate |
| **Sync word detection** | Hardware searches for `$4489` sync in the MFM stream — must handle missing-clock encoding correctly |
| **Copy protection** | Non-standard sync words, weak bits, extra-long tracks — many games depend on exact timing |
| **Write-speed mismatch** | MFM encoding runs at 250 kbit/s regardless of CPU speed — emulation must throttle DMA |
| **TrackMotor delay** | Motor spin-up takes ~500 ms — games check this delay for protection |
> **Practical impact**: The MiSTer Amiga (Minimig) core emulates the floppy controller at the MFM level, reading ADF images and reconstructing the raw MFM bitstream. This is why copy-protected disks require special formats (IPF/CTRaw) that preserve the exact MFM encoding.
---
## FAQ
**Q: Why is the Amiga floppy format different from PC?**
A: The Amiga uses a unique sector format with `$4489` sync words and XOR checksums, whereas PCs use IBM MFM format with `$A1` address marks and CRC-16. The Amiga controller is more flexible because it processes raw MFM — the PC's uPD765 controller decodes MFM in hardware.
**Q: Can I read PC disks on an Amiga?**
A: Yes — the Amiga's raw MFM controller can read any MFM format. The `CrossDOS` filesystem (built into AmigaOS 2.1+) decodes PC MFM sectors. Hardware-wise, the drive mechanism is identical to PC double-density drives.
**Q: Why does a full disk copy take so long?**
A: 80 tracks × 2 sides × 200 ms/revolution = 32 seconds minimum (one revolution per track). With seek time (~3 ms per step) and motor spin-up (~500 ms), a full 880 KB copy takes 4590 seconds in practice.
**Q: What is the difference between ADF and IPF?**
A: ADF stores decoded sector data (880 KB). IPF stores the raw MFM bitstream with timing information, preserving copy protection, weak bits, and non-standard formatting. See [custom_loaders_and_drm.md](../05_reversing/custom_loaders_and_drm.md).
**Q: What happens if I use Fast RAM for a DMA buffer?**
A: The DMA engine writes to the physical Chip RAM address. If your pointer is in Fast RAM, the DMA data goes to Chip RAM (wrong location) while your code reads from Fast RAM (stale or uninitialized data). The result is silent corruption — no error is reported.
---