diff --git a/00_overview/README.md b/00_overview/README.md
new file mode 100644
index 0000000..dbf69bb
--- /dev/null
+++ b/00_overview/README.md
@@ -0,0 +1,9 @@
+[← Home](../README.md)
+
+# Amiga Platform Overview
+
+## Contents
+
+- [history.md](history.md) — A brief history of the Amiga (1985–1994 and beyond)
+- [hardware_models.md](hardware_models.md) — Comprehensive guide to Amiga models (A500 to A4000T)
+- [os_versions.md](os_versions.md) — AmigaOS evolution (Workbench 1.0 to 3.2+)
diff --git a/01_hardware/common/README.md b/01_hardware/common/README.md
new file mode 100644
index 0000000..67c1dd0
--- /dev/null
+++ b/01_hardware/common/README.md
@@ -0,0 +1,13 @@
+[← Home](../../README.md) · [Hardware](../README.md)
+
+# Common Amiga Hardware Components
+
+## Contents
+
+- [m68k_cpu.md](m68k_cpu.md) — The Motorola 68000 family (68000 to 68060)
+- [address_space.md](address_space.md) — The 24-bit and 32-bit Amiga memory maps
+- [memory_types.md](memory_types.md) — Chip RAM, Fast RAM, Slow RAM, and Ranger RAM
+- [zorro_bus.md](zorro_bus.md) — Zorro II/III expansion architecture and PCI bridges
+- [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
diff --git a/01_hardware/common/floppy_hardware.md b/01_hardware/common/floppy_hardware.md
new file mode 100644
index 0000000..0a7ff3b
--- /dev/null
+++ b/01_hardware/common/floppy_hardware.md
@@ -0,0 +1,211 @@
+[← Home](../../README.md) · [Hardware](../README.md)
+
+# Bare-Metal Floppy Drive Hardware
+
+## Overview
+
+Unlike early PCs or the ZX Spectrum, the Amiga does not use an intelligent, high-level floppy disk controller (like the NEC 765 or WD1793). Instead, floppy disk access on the Amiga is orchestrated by a combination of the **[CIA](cia_chips.md)** chips (for mechanical control) and the **[Paula](../ocs_a500/chipset_ocs.md)** custom chip (for Direct Memory Access data streaming).
+
+This architecture provides zero-overhead background reads, but completely places the burden of sector decoding, MFM parsing, and checksum validation onto the CPU (or a reverse-engineering Imager Slave).
+
+---
+
+## 1. The Dual-Chip Architecture
+
+The physical connection between the Amiga motherboard and the 3.5" floppy drive is split into mechanical logic and data lines:
+
+```mermaid
+graph TD
+ subgraph CPU [Motorola 68000]
+ A[CPU]
+ end
+
+ subgraph CIA [CIA Chips]
+ B[CIA-A $BFE001
Status Inputs]
+ C[CIA-B $BFD000
Command Outputs]
+ end
+
+ subgraph Paula [Paula $DFF000]
+ D[Disk DMA Engine
DSKSYNC, DSKLEN]
+ end
+
+ A -->|1. Turn Motor On, Step Head| C
+ C -->|Motor, Sel, Step| E[3.5 Floppy Drive]
+
+ A -->|2. Check if ready, wait for TK0| B
+ E -->|Status: Ready, TK0, WPro| B
+
+ A -->|3. Set DSKPT, ADKCON, DSKLEN| D
+ E -->|4. Raw MFM Bitstream| D
+
+ D -->|5. DMA Push| F[(Chip RAM)]
+ F -.->|6. CPU Decodes MFM| A
+```
+
+---
+
+## 2. Mechanical Control via CIA
+
+The CIA (Complex Interface Adapter) chips handle the physical motors of the floppy drive.
+
+### CIA-B (`$BFD000`) - The Command Register
+Port A of CIA-B (`ciapra`) is used to send commands to the drive. Bits are **active low** (0 = ON, 1 = OFF).
+
+| Bit | Name | Function |
+|---|---|---|
+| 7 | `/MTR` | Motor On/Off (0 = spinning). Must be set *while* selecting the drive. |
+| 6-3 | `/SEL3`-`/SEL0` | Drive Select. Setting bit 3 to `0` selects DF0. |
+| 2 | `/SIDE` | Head Select. `0` = Upper head (Side 0), `1` = Lower head (Side 1). |
+| 1 | `/DIR` | Step Direction. `0` = Step in (towards center), `1` = Step out (towards Track 0). |
+| 0 | `/STEP` | Step Pulse. A high-to-low transition physically moves the head one track. |
+
+### CIA-A (`$BFE001`) - The Status Register
+Port A of CIA-A (`ciapra`) is used to read the physical state of the drive. Bits are **active low**.
+
+| Bit | Name | Function |
+|---|---|---|
+| 5 | `/RDY` | Drive Ready. `0` = Disk is inserted and spinning at full speed. |
+| 4 | `/TK0` | Track 0 Sensor. `0` = The head has physically reached the outermost track. |
+| 3 | `/WPRO` | Write Protect. `0` = Disk is locked (write tab open). |
+| 2 | `/CHNG` | Disk Change. `0` = Disk has been removed or inserted. |
+
+### The Stepping Flow (Assembly)
+To physically seek to a track, developers bypass the OS and write directly to CIA-B.
+
+> [!WARNING]
+> Mechanical components are slow. You cannot pulse the `/STEP` bit rapidly. You must wait approximately **3 milliseconds** between step pulses, usually by polling a CIA timer or using a `DBF` delay loop, otherwise the head will jam.
+
+```asm
+; Example: Step head outward towards Track 0
+ LEA $BFD000, A0 ; CIA-B
+
+ ; 1. Set Direction to OUT (Bit 1 = 1)
+ BSET #1, (A0)
+
+.seekLoop:
+ ; 2. Check if we are at Track 0
+ BTST #4, $BFE001 ; CIA-A: Check /TK0
+ BEQ .atTrack0 ; If 0, we are at Track 0
+
+ ; 3. Pulse /STEP (High -> Low -> High)
+ BCLR #0, (A0) ; Step Low
+ NOP ; Short hardware delay
+ BSET #0, (A0) ; Step High
+
+ ; 4. Wait 3ms for mechanical head movement
+ BSR Wait3ms
+
+ BRA .seekLoop ; Keep stepping
+
+.atTrack0:
+```
+
+---
+
+## 3. Paula's Disk DMA Engine
+
+Once the head is at the correct track, the developer instructs Paula to start reading the magnetic flux.
+
+### `ADKCON` (Audio/Disk Control)
+Before reading, Paula must be configured via the `$DFF09E` (`ADKCON`) register.
+* **WORDSYNC** (Bit 10): If set, Paula will not start dumping data into RAM until it sees the exact 16-bit sequence specified in `DSKSYNC`. If cleared, Paula blindly dumps data immediately.
+* **MFMPREC** (Bit 9): MFM Precompensation. (Usually handled automatically by Amiga drives, set to 0 for reads).
+* **FAST** (Bit 8): Set to 0 for standard Amiga DD drives (2 µs per bit cell). Set to 1 for High Density (HD) drives.
+
+### `DSKSYNC` (Disk Sync Register)
+Located at `$DFF07E`. When `WORDSYNC` is enabled, Paula constantly monitors the incoming flux stream for this 16-bit value.
+* The standard AmigaDOS sync word is `$4489`.
+* **Copy protection often uses custom sync words** (e.g., `$8944`). By changing `DSKSYNC`, an Imager Slave can force Paula to lock onto proprietary, non-DOS tracks.
+
+### `DSKPT` (Disk DMA Pointer)
+Located at `$DFF020`. This is a 32-bit pointer to the Chip RAM buffer where Paula will write the data.
+
+### `DSKLEN` (Disk Length & Trigger)
+Located at `$DFF024`. This register dictates how many words Paula should transfer, and starts the DMA.
+* Bit 15 (`DMAEN`): Must be set to `1` to enable the transfer.
+* Bit 14 (`WRITE`): `0` = Read from disk, `1` = Write to disk.
+* Bits 13-0: Number of 16-bit words to transfer (usually `$1900` / 6400 words for a full DD track).
+
+> [!IMPORTANT]
+> **The Double-Write Quirk**
+> To actually trigger the DMA hardware, you must write the exact same value to `DSKLEN` **twice**. This is a safety mechanism built into Paula's silicon to prevent accidental disk overwrites from random runaway code pointers.
+
+```asm
+ ; Example: Start DMA Read of a raw track
+ LEA $DFF000, A5
+
+ ; 1. Set Sync Word
+ MOVE.W #$4489, $07E(A5) ; DSKSYNC
+
+ ; 2. Configure ADKCON
+ MOVE.W #$8400, $09E(A5) ; Set WORDSYNC (Bit 15=Set, Bit 10=WORDSYNC)
+
+ ; 3. Set Chip RAM Pointer
+ MOVE.L #TrackBuffer, $020(A5) ; DSKPT
+
+ ; 4. Start DMA Read (Double-Write Quirk)
+ MOVE.W #$8000|6400, $024(A5) ; DSKLEN: Enable, Read, 6400 words
+ MOVE.W #$8000|6400, $024(A5) ; Write AGAIN to trigger Paula
+
+ ; 5. Wait for DSKBLK interrupt flag
+.waitDMA:
+ BTST #1, $01F(A5) ; INTREQR (Bit 1 = DSKBLK)
+ BEQ .waitDMA
+```
+
+---
+
+## 4. MFM Decoding: Historical & Technical Context
+
+Once the DMA transfer finishes, the buffer in Chip RAM is full of raw MFM (Modified Frequency Modulation) data.
+
+### 4.1 FM vs MFM Encoding
+Magnetic media cannot store a long sequence of identical bits (like `00000000`) without losing synchronization, because there would be no magnetic flux transitions to keep the drive's read circuitry locked on track.
+
+* **FM (Frequency Modulation)**: Early floppy disks (like the 1973 IBM 3740 standard, used by Shugart Associates on their 8-inch SA800 drives) solved this by inserting a "clock bit" before *every single* data bit. This ensured constant magnetic transitions but was highly inefficient, known as "Single Density."
+* **MFM (Modified Frequency Modulation)**: Introduced by IBM in 1970 for hard drives (IBM 3330) and later adopted as the universal standard for "Double Density" floppy disks, MFM mathematically reduces the number of clock bits.
+
+### 4.2 The MFM Encoding Rules
+MFM doubles the storage capacity of FM by only inserting clock bits when strictly necessary to prevent magnetic desynchronization.
+
+The Amiga encoding algorithm translates every 1 bit of logical data into 2 bits of physical disk data (1 clock bit + 1 data bit) based on the following rules:
+1. If the data bit is `1`, the clock bit is `0`. (Physical = `01`)
+2. If the data bit is `0`, and the *previous* data bit was `1`, the clock bit is `0`. (Physical = `00`)
+3. If the data bit is `0`, and the *previous* data bit was `0`, the clock bit is `1`. (Physical = `10`)
+
+```text
+Logical Data: 1 0 1 1 0 0 0 1
+Physical MFM: 01 00 01 01 00 10 10 01
+ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
+ Clock bits dynamically inserted based on history
+```
+
+### 4.3 Amiga CPU Decoding
+Because Paula only handles the DMA transfer, the CPU must manually decode this MFM data. The CPU takes the interleaved 32-bit longs, masks out the clock bits, shifts the data bits together, and verifies the XOR checksums.
+
+If a game uses a custom trackloader, it intentionally breaks the AmigaDOS MFM checksum algorithm. By reading the raw bitstream via Paula, reversing the game's custom decoding loop, and writing a WHDLoad **Imager Slave**, developers can successfully extract data that the OS considers "corrupt."
+
+---
+
+## 5. Supported Diskette Capacities
+
+Because Paula handles data streaming via raw DMA instead of a rigid sector controller, the Amiga OS writes entire tracks to the disk continuously. This eliminates the massive inter-sector sync gaps required by standard PC floppy formats. Consequently, the Amiga fits 11 sectors per track instead of the PC's 9, yielding higher formatted capacities on the exact same physical media.
+
+| Disk Type | Magnetic Density | Unformatted Capacity | Standard PC Format | Amiga Format |
+| :--- | :--- | :--- | :--- | :--- |
+| **3.5" DD** | Double Density (MFM) | ~1.0 MB | 720 KB (9 sectors/track) | **880 KB** (11 sectors/track) |
+| **3.5" HD*** | High Density (MFM) | ~2.0 MB | 1.44 MB (18 sectors/track) | **1.76 MB** (22 sectors/track) |
+
+> [!NOTE]
+> ***HD Drive Requirement**: High Density disks use a different magnetic coercivity. Standard Amiga DD drives cannot reliably format HD disks. To support 1.76 MB HD disks, later machines (like the Amiga 3000 and 4000) shipped with specialized High Density drives that literally spun at half-speed (150 RPM instead of 300 RPM) when an HD disk was inserted. This halved the data rate, allowing the standard Paula chip (designed for 250 kbit/s DD data) to read the 500 kbit/s HD flux stream without requiring an upgraded DMA controller!
+
+---
+
+## 6. References
+
+* **Encoding & Magnetic Storage:**
+ * [Modified Frequency Modulation (Wikipedia)](https://en.wikipedia.org/wiki/Modified_Frequency_Modulation)
+ * [IBM 3740 Data Entry System & FM Encoding (Wikipedia)](https://en.wikipedia.org/wiki/IBM_3740)
+* **Amiga Floppy Controller Programming:**
+ * [Amiga Hardware Reference Manual: Floppy Disk Controller](http://amigadev.elowar.com/read/ADCD_2.1/Hardware_Manual_guide/node0127.html)
+ * [WHDLoad Developer Documentation: RawDIC & Imager Slaves](http://whdload.de/docs/en/rawdic.html)
diff --git a/01_hardware/common/zorro_bus.md b/01_hardware/common/zorro_bus.md
index 8486f3b..ca9e1be 100644
--- a/01_hardware/common/zorro_bus.md
+++ b/01_hardware/common/zorro_bus.md
@@ -131,9 +131,59 @@ struct DiagArea {
The boot vector is called by `ConfigChain()` during the early boot sequence — this is how SCSI controllers install their filesystem handlers.
+## Real-World Performance & Bottlenecks
+
+While the theoretical speeds are high, real-world Zorro performance is often gated by the Amiga's system bus controller and motherboard design.
+
+### The "Buster" Bottleneck
+In the A3000 and A4000, the **Buster** chip manages Zorro III traffic.
+- **Revision 9**: Contained bugs that made Zorro III DMA unstable or slow.
+- **Revision 11**: The "Golden" revision. It fixed several timing bugs and allowed for reliable **Burst Mode** transfers, which are essential for reaching speeds above 10 MB/s.
+
+### Bandwidth Comparison (Real-World)
+
+| Interface | Effective CPU-to-VRAM | Notes |
+|---|---|---|
+| **Zorro II** | ~2.5 – 3.5 MB/s | Gated by 7 MHz 68000 bus timing. |
+| **Zorro III** | ~10 – 15 MB/s | Requires Buster 11 and a 68040/060. |
+| **PCI Bridge** | ~20 – 30 MB/s | Limited by the Zorro-to-PCI bridge interface. |
+| **Local Bus** | **~60 – 80 MB/s** | Bypasses Zorro; uses CPU-slot (CyberStorm PPC). |
+
+### 2. PCI Bridgeboards (Mediator, G-REX, Prometheus)
+PCI bridges allowed the Amiga to break out of the aging Zorro ecosystem and tap into the vast, cheap pool of PC hardware.
+
+#### Hardware Architecture
+* **Mediator (Elbox)**: The most popular solution. It consists of a "Logic Board" (containing GALs/CPLDs) and a "Busboard." It uses a **Memory Windowing** technique (typically 8MB) to map the vast 4GB PCI address space into the Amiga's 24-bit or 32-bit space.
+* **G-REX (DCE)**: Designed for high-end PowerPC systems. It connects directly to the **CPU local slot** of a BlizzardPPC or CyberStormPPC. Unlike the Mediator, it uses **Linear Mapping**, making the entire PCI memory space directly addressable without window switching, resulting in superior performance.
+* **Prometheus (Mayap/Individual Computers)**: A simpler Zorro III-to-PCI bridge using a single PLX bridge chip.
+
+#### Software & Libraries
+* **pci.library**: The standard API for PCI hardware on the Amiga. It handles resource allocation (BARs), interrupt routing, and device discovery.
+* **OpenPCI**: An open-source alternative library designed to provide a unified driver interface across different bridge brands.
+* **Mediator Multimedia CD**: The proprietary driver suite from Elbox, required for many of their advanced features (like using a graphics card's VRAM as system Fast RAM).
+
+#### Supported PCI Cards
+| Category | Supported Models | Notable Drivers |
+|---|---|---|
+| **Graphics** | 3dfx Voodoo 3/4/5, ATI Radeon 9200/9250, S3 ViRGE | Picasso96, CyberGraphX v4 |
+| **Sound** | Sound Blaster 128, ESS Solo-1, ForteMedia FM801 | AHI (Amiga Hardware Interface) |
+| **Networking** | Realtek 8139 (10/100 Mbps) | SANA-II (Roadshow, Genesis) |
+| **USB** | NEC-based cards (e.g., Elbox Spider) | Poseidon USB Stack |
+| **TV/Video** | Brooktree Bt848/878 based cards | TVPaint, VGR |
+
+> [!TIP]
+> **Mediator VRAM trick**: One of the most powerful features of the Mediator is the ability to use the 128MB+ of RAM on a Radeon card as system Fast RAM. While slower than local motherboard RAM, it is significantly faster than swapping to a hard drive.
+
+### 3. FPGA-Based Modern Cards
+Modern Zorro cards like the **MNT ZZ9000** use an FPGA to provide RTG, Ethernet, and USB. They often include an ARM processor or specialized hardware logic to perform operations (like JPEG decoding) locally on the card, reducing the amount of raw data that needs to be sent over the Zorro bus.
+
+* **Product Page**: [MNT ZZ9000](https://mntre.com/zz9000)
+* **Official Sources (GitLab)**: [Firmware & Drivers](https://source.mnt.re/amiga)
+
## References
- NDK39: `libraries/expansion.h`, `libraries/configregs.h`, `libraries/configvars.h`
- ADCD 2.1 Autodocs: `expansion` — http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_3._guide/node025B.html
- *Amiga Hardware Reference Manual* 3rd ed. — AutoConfig chapter
- Dave Haynie's Zorro III specification documents
+- Mediator PCI Technical Reference — http://www.elbox.com/mediator_tech.html
diff --git a/02_boot_sequence/README.md b/02_boot_sequence/README.md
index be96389..271f764 100644
--- a/02_boot_sequence/README.md
+++ b/02_boot_sequence/README.md
@@ -12,4 +12,5 @@ From power-on to Workbench desktop: hardware init, ROM validation, kernel creati
| [kickstart_rom.md](kickstart_rom.md) | **Kickstart ROM internals: binary structure, module inventory, extraction tools, custom ROM building, EPROM burning** |
| [kickstart_init.md](kickstart_init.md) | ExecBase creation, capture vectors, resident module scan algorithm, 4-phase initialization, bootstrap handoff |
| [dos_boot.md](dos_boot.md) | strap module, boot block format and execution, MountList/DOSDrivers, system assigns, Startup-Sequence walkthrough |
+| [floppy_vs_hdd_physical_boot.md](floppy_vs_hdd_physical_boot.md) | Physical hardware mechanisms: floppy MFM streaming, HDD RDB scanning, DMA transfers, and physical failure modes |
| [early_startup.md](early_startup.md) | Early Startup Control menu: boot device selection, display mode override, recovery scenarios |
diff --git a/02_boot_sequence/floppy_vs_hdd_physical_boot.md b/02_boot_sequence/floppy_vs_hdd_physical_boot.md
new file mode 100644
index 0000000..12cd553
--- /dev/null
+++ b/02_boot_sequence/floppy_vs_hdd_physical_boot.md
@@ -0,0 +1,123 @@
+[← Home](../../README.md) · [Boot Sequence](../README.md)
+
+# Physical Boot Process: Floppy vs Hard Drive
+
+While [DOS Boot](dos_boot.md) covers the logical software handoff (from Kickstart to `dos.library` to the Startup-Sequence), it's equally important to understand the *physical* hardware mechanisms involved when an Amiga boots from a storage medium.
+
+The physical boot sequence dictates how raw data is pulled from magnetic media into Chip RAM before the CPU ever sees a `DOS\0` signature or an executable bootblock. This is especially relevant when analyzing [Custom Trackloaders](../05_reversing/custom_loaders_and_drm.md) which bypass the OS and interact with the hardware directly.
+
+---
+
+## 1. Booting from Floppy (DF0:)
+
+The Amiga's floppy drive interface is entirely "dumb." Unlike a modern SATA drive with its own onboard microcontroller, the Amiga CPU and custom chips must manually control the floppy drive's motors and decode its raw magnetic data.
+
+### 1.1 The Hardware Handoff
+When the Kickstart ROM's `strap` module decides to boot from `DF0:`, it invokes `trackdisk.device`. This device driver issues commands directly to the CIA (Complex Interface Adapter) and Paula chips.
+
+### 1.2 Step-by-Step Physical Process
+
+```mermaid
+sequenceDiagram
+ participant CPU (trackdisk.device)
+ participant CIA (8520)
+ participant Floppy Drive
+ participant Paula (8364)
+
+ CPU->>CIA: Set Motor On (CIA-B Port B)
+ CIA->>Floppy Drive: Spin up spindle motor
+ CPU->>CIA: Assert /STEP until /TRK0 is low
+ CIA->>Floppy Drive: Move read head to Cylinder 0
+ CPU->>Paula: Write $4489 to DSKSYNC
+ CPU->>Paula: Write buffer address to DSKPTH
+ CPU->>Paula: Write $8000+size to DSKLEN
+ Paula->>Floppy Drive: Start reading flux transitions
+ Floppy Drive-->>Paula: Raw MFM bitstream
+ Paula-->>Paula: Wait for $4489 Sync Word
+ Paula->>Chip RAM: DMA stream data
+ CPU->>CPU: Decode MFM to binary (Sectors 0 & 1)
+```
+
+1. **Spin Up**: The CPU toggles a bit on CIA-B Port B (`$BFD100`) to turn on the floppy drive motor. It waits a few milliseconds for the spindle to reach a stable 300 RPM.
+2. **Seek Track 0**: The CPU pulses the `/STEP` line while monitoring the `/TRK0` sensor. The drive clicks as the read/write head physically moves to the outermost cylinder (Track 0).
+3. **MFM DMA Read**: The CPU configures Paula for a disk read:
+ - Sets the sync word in `DSKSYNC` (`$4489`).
+ - Points `DSKPTH` (Disk Pointer) to an empty buffer in Chip RAM.
+ - Writes to `DSKLEN` to start the DMA transfer.
+4. **Data Streaming**: Paula watches the raw magnetic flux stream coming from the drive. The moment it sees the magnetic pattern for `$4489`, it starts streaming the subsequent MFM data straight into Chip RAM via DMA.
+5. **Software Decode**: `trackdisk.device` reads the MFM data from RAM and runs an EOR/Bit-shift loop to convert the MFM bits back into standard binary bytes. It extracts the first two sectors (the 1024-byte bootblock).
+6. **Execution**: If the bootblock is executable, the CPU jumps to it. At this exact moment, **Custom Trackloaders** (used by games) will typically execute `Forbid()` and `Disable()`, permanently locking out `trackdisk.device` and manually repeating steps 2-5 for the rest of the game's data.
+
+---
+
+## 2. Booting from Hard Drive (IDE / SCSI)
+
+Unlike floppies, Hard Disk Drives (HDDs) are "smart." They have onboard controllers that handle physical head stepping, sector addressing (LBA or CHS), and error correction automatically. The Amiga simply asks the drive for a specific block of data.
+
+However, the Amiga's architecture means it doesn't intrinsically know how to read an HDD upon power-up.
+
+### 2.1 The AutoConfig Phase
+Before an HDD can boot, the Amiga must discover its controller.
+
+1. **Zorro / AutoConfig**: During early startup, Kickstart probes the expansion bus.
+2. **ROM Mapping**: The HDD controller board (e.g., A2091 SCSI or A1200 internal IDE) responds. Kickstart maps the controller's onboard ROM into the Amiga's memory space.
+3. **Driver Initialization**: The controller's ROM contains the device driver (e.g., `scsi.device`). Kickstart runs the initialization code in this ROM, which binds the driver to the OS.
+
+### 2.2 Reading the RDB
+The Amiga does not use PC-style MBR (Master Boot Record) or GPT. It uses the **Rigid Disk Block (RDB)**.
+
+1. **RDB Scan**: `scsi.device` asks the drive to read Block 0. If it doesn't find the ASCII signature `RDSK`, it checks blocks 1 through 15 until it finds it.
+2. **Partition Discovery**: The RDB contains a linked list of partitions (e.g., `DH0:`, `DH1:`).
+3. **Filesystem Loading**: Crucially, the Amiga supports loadable filesystems. If `DH0:` uses a custom filesystem (like PFS3 or SFS) that isn't in Kickstart, the RDB actually contains the *binary code for the filesystem itself*. `scsi.device` loads the filesystem code from the RDB into RAM and initializes it.
+
+### 2.3 Step-by-Step Physical Process
+
+```mermaid
+sequenceDiagram
+ participant CPU (scsi.device)
+ participant HDD Controller
+ participant HDD
+ participant Fast RAM
+
+ CPU->>HDD Controller: Send READ command (LBA 0)
+ HDD Controller->>HDD: Fetch RDB Sector
+ HDD-->>HDD Controller: Sector Data
+ HDD Controller->>Fast RAM: DMA Transfer
+ CPU->>CPU: Parse RDB & Mount DH0:
+ CPU->>CPU: Load Filesystem from RDB (if needed)
+ CPU->>HDD Controller: Request Bootblock (LBA of DH0: root)
+ HDD Controller->>HDD: Fetch Bootblock
+ HDD-->>HDD Controller: Data
+ HDD Controller->>Fast RAM: DMA Transfer
+ CPU->>CPU: Execute Bootblock / Mount DOS
+```
+
+Unlike floppies, HDD DMA transfers can typically go directly into **Fast RAM**, bypassing the chipset entirely (depending on the controller). Once the partition's bootblock is loaded and executed, control passes to `dos.library` and the `Startup-Sequence` exactly as it does for a floppy.
+
+---
+
+## 3. What Can Go Wrong (Failure Modes)
+
+Because physical boot sequences rely on moving parts and magnetic media, they are prone to specific physical failure states.
+
+### 3.1 Floppy Failures
+* **Track 0 Sensor Failure**: If the drive's `/TRK0` microswitch is dirty or broken, the drive will repeatedly slam the read head against the physical stop, producing a loud, rapid "grinding" or "machine gun" noise. It cannot find sector 0.
+* **Alignment Drift**: Over time, the stepper motor can drift. The drive's head will physically rest between two tracks, reading a garbled mix of flux transitions. Paula will never see the `$4489` sync word. The system hangs or presents a "Not a DOS disk" error.
+* **Dirty Read Heads**: Dust on the magnetic head weakens the flux signal. The hardware reads the `$4489` sync word, but random bits in the payload flip. The software MFM decode loop succeeds, but the Bootblock Checksum fails, kicking the user back to the Kickstart screen.
+
+### 3.2 Hard Drive Failures
+* **Spin-Up Timeout**: Older SCSI drives take up to 15 seconds to reach operational RPM. If the Kickstart ROM's wait loop times out before the drive asserts the `READY` signal, the Amiga will boot to the Kickstart screen, ignoring the HDD entirely. (Fixed by pressing `Ctrl-Amiga-Amiga` for a warm reboot after the drive is spinning).
+* **RDB Corruption**: If the RDB sectors are overwritten, `scsi.device` will scan blocks 0-15, find nothing, and report the drive as unformatted/empty, even if all partition data is perfectly intact.
+* **Filesystem Validation**: If the Amiga was powered off during a write, the filesystem's bitmap (which tracks free space) is left in an inconsistent state. On the next boot, `dos.library` locks the partition as Read-Only and launches a background "Validator" process. The hard drive LED will blink rapidly for minutes while it rebuilds the bitmap. If validation fails, the drive remains Read-Only.
+
+---
+
+## 4. Summary
+
+| Feature | Floppy (DF0:) | Hard Drive (IDE/SCSI) |
+|---|---|---|
+| **Hardware Control** | CPU directly controls motors & head stepping | Onboard controller handles mechanics |
+| **Data Encoding** | Raw MFM (decoded by CPU in software) | Hardware handles sector decoding |
+| **Boot Discovery** | Hardcoded to Track 0, Sector 0 | Scans blocks 0-15 for RDB (Rigid Disk Block) |
+| **DMA Target** | Must go to Chip RAM | Usually goes to Fast RAM |
+| **OS Bypass** | Trivial (Custom Trackloaders) | Rare (Requires full IDE/SCSI driver implementation) |
diff --git a/05_reversing/README.md b/05_reversing/README.md
index 1a033b1..5539276 100644
--- a/05_reversing/README.md
+++ b/05_reversing/README.md
@@ -24,9 +24,18 @@ This section provides a systematic methodology for reverse engineering AmigaOS e
| [static/code_vs_data_disambiguation.md](static/code_vs_data_disambiguation.md) | Distinguishing code bytes from data — IDA/Ghidra workflows |
| [patching_techniques.md](patching_techniques.md) | Surgical binary patching methods |
| [unpacking_and_decrunching.md](unpacking_and_decrunching.md) | Executable unpacking, decruncher architecture, and manual extraction |
+| [custom_loaders_and_drm.md](custom_loaders_and_drm.md) | Bypassing DOS, Trackloaders, and physical DRM tricks |
+| [anti_debugging.md](anti_debugging.md) | The Cracker vs. Developer arms race: Trace vector abuse, NMI defeat, CIA timers |
+| [whdload_architecture.md](whdload_architecture.md) | WHDLoad internals, slaves, resload_DiskLoad, and runtime memory patching |
| [case_studies/](case_studies/) | Real-world RE walkthroughs |
| [case_studies/ramdrive_device.md](case_studies/ramdrive_device.md) | ramdrive.device RE walkthrough |
+### Game Reverse Engineering
+
+| File | Topic |
+|---|---|
+| [games/game_reversing.md](games/game_reversing.md) | Game RE: disassembly, modification, asset extraction, save game analysis |
+
### Per-Compiler Reverse Engineering — Binary Field Manuals
| File | Topic |
diff --git a/05_reversing/anti_debugging.md b/05_reversing/anti_debugging.md
new file mode 100644
index 0000000..4f4910a
--- /dev/null
+++ b/05_reversing/anti_debugging.md
@@ -0,0 +1,145 @@
+[← Home](../README.md) · [Reverse Engineering](README.md)
+
+# Amiga Anti-Debugging & The Arms Race
+
+The late 1980s and early 1990s saw an intense "arms race" between Amiga software developers (implementing DRM) and crackers (removing DRM). Because the Amiga allowed user software to take complete, bare-metal control of the hardware, developers created highly sophisticated anti-debugging techniques to crash or hang the system if a cracker tried to analyze the game in a debugger.
+
+This article codifies the most prominent anti-debugging tricks used on the Motorola 68000 and Amiga custom chips.
+
+---
+
+## 1. Trace Vector Abuse (Trace Vector Decoding - TVD)
+
+The most famous anti-debugging trick is the **Rob Northen Copylock**. It abuses the 68000's Trace Exception to prevent single-stepping.
+
+### The Mechanism
+The 68000 CPU has a **Trace bit** (bit 15) in the Status Register (SR). When set, the CPU executes exactly one instruction and then automatically triggers a Trace Exception (Vector `$24` at memory address `$00000024`). Debuggers use this to implement "single-step" functionality.
+
+Developers used **Trace Vector Decoding (TVD)** to maliciously overwrite the debugger's trace vector with their own decryption routine.
+
+```assembly
+; Copylock TVD Pattern
+move.l $24.w, old_trace ; Save the debugger's vector (or just trash it)
+move.l #my_decryption, $24.w; Install our own trace handler
+ori.w #$8000, sr ; Set the CPU Trace bit!
+nop ; The interrupt fires immediately after this instruction
+
+; ... CPU jumps to my_decryption ...
+
+my_decryption:
+ ; Decrypt the next instruction of the game
+ ; Execute it
+ ; Re-encrypt it (optional)
+ rte ; Return from exception (which immediately fires AGAIN)
+```
+
+### The Cracker's Solution
+If a cracker is single-stepping through this code, the moment `ori.w #$8000, sr` executes, the game steals the trace vector. The debugger loses control, and the game decrypts itself synchronously in the background. Crackers defeated this by using **Trace Emulators** or custom scripts that ran the decryption loop virtually, extracting the final decrypted payload from memory without relying on the hardware Trace bit.
+
+---
+
+## 2. Action Replay NMI Defeat
+
+The **Action Replay** was a hardware cartridge that plugged into the Amiga's expansion port. Pressing its physical button generated a **Level 7 Non-Maskable Interrupt (NMI)**. This instantly froze the Amiga, dumping the user into a powerful machine-code monitor regardless of what the OS or game was doing.
+
+Because it was non-maskable, developers could not simply disable it via the `INTENA` register.
+
+### The Mechanism
+While Level 7 interrupts cannot be masked, the CPU still uses an exception vector (`$0000007C`) to handle them. Developers simply pointed the NMI vector to a dummy `RTE` (Return from Exception) instruction.
+
+```assembly
+; Defeating the Action Replay
+move.l #ignore_nmi, $7c.w ; Overwrite Level 7 Auto-Vector
+
+; ... game code ...
+
+ignore_nmi:
+ rte ; Silently return if the freeze button is pressed
+```
+
+### The Cracker's Solution
+Action Replay Mk II and III introduced features to intercept writes to `$7C.w` or used the MMU (on 68030 processors) to write-protect the vector table, ensuring the freezer always retained control.
+
+---
+
+## 3. CIA Timer Checks
+
+If a developer suspects their code is being single-stepped, they can measure the exact time it takes to execute a block of instructions.
+
+### The Mechanism
+The Amiga's 8520 CIA (Complex Interface Adapter) chips contain highly accurate hardware timers (Timer A and Timer B).
+
+```assembly
+; CIA Timer Anti-Debugging
+move.b #$08, $bfd400 ; Start CIA-B Timer A
+; ... decryption loop ...
+move.b $bfd400, d0 ; Read timer value
+cmp.b #$A0, d0 ; Did this take longer than X microseconds?
+bgt.s .debugger_detected ; Yes? We are being single-stepped!
+
+.debugger_detected:
+ ; Intentionally corrupt the decryption key
+ eori.l #$FFFFFFFF, d7
+```
+
+If a human is pressing "Step" in a debugger, the time elapsed will be millions of times slower than native execution. The game detects this and silently corrupts the decryption key. The game will crash later, confusing the cracker.
+
+---
+
+## 4. Software Breakpoint Checksumming
+
+A common way to set a breakpoint in a 68k debugger is to overwrite an instruction with `$4AFC` (`ILLEGAL`). When the CPU hits it, it triggers an Illegal Instruction exception (Vector `$10`), handing control back to the debugger.
+
+### The Mechanism
+To detect `$4AFC` breakpoints, games constantly checksum their own code in memory.
+
+```assembly
+; Checksumming a block of code
+ lea code_start(pc), a0
+ move.w #100, d7
+ moveq #0, d0
+.csum:
+ add.w (a0)+, d0
+ dbf d7, .csum
+
+ cmp.w #$1234, d0 ; Does the checksum match?
+ bne.s .debugger_found ; If not, someone modified the code!
+```
+
+### Self-Modifying Code (SMC)
+To make this even harder, developers used SMC. The code would dynamically alter its own instructions just before executing them. If a debugger placed a `$4AFC` breakpoint in the path of the SMC, the SMC would corrupt the breakpoint, or the breakpoint would corrupt the SMC calculation, leading to a crash.
+
+---
+
+## 5. VBR (Vector Base Register) Relocation
+
+On the 68000, exception vectors are hardcoded at memory address `$00000000`. Standard software debuggers place their hooks here.
+
+### The Mechanism
+Starting with the 68010 (and present on the 68020/030/040/060), Motorola introduced the **Vector Base Register (VBR)**. This register allows the OS to move the entire exception vector table to any location in memory.
+
+```assembly
+; Moving the vector table (Requires Supervisor State)
+ lea new_vector_table, a0
+ movec a0, vbr
+```
+
+Games running on AGA Amigas (A1200/A4000) would allocate a new block of memory, copy the vector table there, alter the `vbr`, and then install their trace/NMI traps in the *new* table. Older debuggers hardcoded to read `$00000000` would be completely blind to these changes.
+
+---
+
+## 6. Hardware Register Traps
+
+Debuggers often alter hardware state slightly (e.g., stopping DMA, changing screen colors, reading registers to update the UI).
+
+### The Mechanism
+* **Write-Only Registers**: Many custom chip registers (like `BLTCON0` or `COLOR00`) are write-only. If a debugger tries to read them to save the system state, it actually reads open bus (garbage). If the debugger blindly writes that garbage back when resuming execution, the game crashes.
+* **Undocumented CIA Bits**: Some games would write specific patterns to undocumented bits in the CIA registers. Hardware freezers that didn't perfectly emulate or save/restore these undocumented bits would cause the game to fail its own integrity checks upon resuming.
+
+---
+
+## Summary
+
+The Amiga anti-debugging scene was characterized by its proximity to the bare metal. Because there was no memory protection on the standard 68000, developers had free rein to abuse CPU exceptions, hijack hardware interrupts, and weaponize the system's own debugging features against reverse engineers.
+
+To defeat these protections today, modern reverse engineers rely on instruction-level emulators (like WinUAE's built-in debugger), which allow for invisible tracing, hardware watchpoints, and memory dumps that the running game cannot detect.
diff --git a/05_reversing/case_studies/README.md b/05_reversing/case_studies/README.md
new file mode 100644
index 0000000..c21bf21
--- /dev/null
+++ b/05_reversing/case_studies/README.md
@@ -0,0 +1,7 @@
+[← Home](../../README.md) · [Reverse Engineering](../README.md)
+
+# Reverse Engineering Case Studies
+
+## Contents
+
+- [ramdrive_device.md](ramdrive_device.md) — ramdrive.device walkthrough
diff --git a/05_reversing/custom_loaders_and_drm.md b/05_reversing/custom_loaders_and_drm.md
new file mode 100644
index 0000000..9803b47
--- /dev/null
+++ b/05_reversing/custom_loaders_and_drm.md
@@ -0,0 +1,152 @@
+[← Home](../README.md) · [Reverse Engineering](README.md)
+
+# Bypassing Custom Loaders and DRM Analysis
+
+In the classic Amiga era, the vast majority of commercial games **did not use AmigaOS or `dos.library`**. Instead, they booted directly from the floppy disk's bootblock, took full control of the hardware, and used custom MFM (Modified Frequency Modulation) routines to load data directly from the floppy drive (`trackdisk.device` or direct hardware banging).
+
+This technique, known as a **Trackloader**, allowed for faster loading, non-standard disk formats (holding >880KB), and robust copy protection (DRM). For a reverse engineer, analyzing an Amiga game almost always starts with defeating the trackloader and its associated DRM.
+
+---
+
+## 1. The Boot Sequence & Taking Control
+
+When an Amiga boots from a floppy, the Kickstart ROM reads the first two sectors (1024 bytes) into memory. If the first four bytes are `DOS\0`, it treats it as a standard AmigaDOS disk. If the bootblock is executable, the ROM jumps to it.
+
+```mermaid
+graph TD
+ A[Kickstart Boot] -->|Reads Block 0 & 1| B{Signature Check}
+ B -->|'DOS\\0'| C[Mount FastFileSystem]
+ B -->|Other Executable| D[Execute Bootblock Code]
+ D --> E[Disable OS Multitasking]
+ E --> F[Take Over Interrupts & DMA]
+ F --> G[Execute Custom Trackloader]
+
+ style D fill:#ffcccc,stroke:#ff0000
+ style G fill:#ffcccc,stroke:#ff0000
+```
+
+### 1.1 The "Hardware Takeover" Pattern
+
+Commercial games typically execute a standard sequence to disable AmigaOS and ensure uninterrupted hardware access:
+
+```assembly
+; Standard Hardware Takeover (Often found in Bootblocks)
+move.w #$7FFF, $dff09a ; INTENA - Disable all interrupts
+move.w #$7FFF, $dff096 ; DMACON - Disable all DMA (sprites, copper, bitplane)
+move.l $4.w, a6 ; Get ExecBase
+jsr -132(a6) ; Forbid() - Disable multitasking
+jsr -120(a6) ; Disable() - Disable OS interrupts
+
+; Setup custom VBlank and hardware
+move.l #my_copperlist, $dff080
+move.w #$8020, $dff096 ; Enable Sprite DMA
+move.w #$c020, $dff09a ; Enable VBlank Interrupt
+```
+
+> [!CAUTION]
+> Once the bootblock calls `Forbid()` and writes to `INTENA`, standard Amiga debuggers (like MonAM or HRTmon running under the OS) will lose control unless they are Action Replay hardware cartridges or running in emulation (WinUAE/FS-UAE).
+
+---
+
+## 2. Trackloader Architecture
+
+A trackloader replaces `dos.library` with custom code that reads raw MFM data from the floppy controller (Paula).
+
+### 2.1 MFM and Sync Words
+
+Floppy disks store data as magnetic flux transitions. To prevent long sequences of zeros (which cause the read head to lose synchronization), data is encoded using MFM.
+
+To find the start of a sector, the hardware looks for a specific 16-bit **Sync Word**. The standard Amiga sync word is `$4489`.
+
+### 2.2 Finding the Trackloader in IDA/Ghidra
+
+When reverse engineering a game, you must locate the trackloader to extract the game's actual files. Look for these specific hardware register accesses:
+
+| Address | Register | What it means in a Trackloader |
+|---|---|---|
+| `$DFF07E` | `DSKSYNC` | Setting the Sync Word (usually `$4489`) |
+| `$DFF020` | `DSKPTH` | Disk DMA Pointer (where to write the read MFM data) |
+| `$DFF024` | `DSKLEN` | Disk DMA Length (how many words to read, and start reading) |
+| `$BFD100` | `CIAB-PRB` | CIA-B Port B: Used for floppy motor control, head stepping, and side selection. |
+
+### 2.3 Advanced Trackloader Disk Formats
+
+To prevent copying by standard AmigaDOS tools like X-Copy, developers altered the physical geometry and structure of the tracks on the floppy disk:
+
+1. **Long Tracks**: A standard AmigaDOS track holds 11 sectors. Trackloaders would format tracks with 12 sectors by slightly reducing the physical gaps between sectors, fitting more data (and breaking standard sector-by-sector copying algorithms).
+2. **Sync-less Formats**: Bypassing the `$4489` sync word entirely. The trackloader uses raw timing loops (via CIA timers) or completely custom bit-patterns to find the start of the data stream, preventing hardware from locking onto the track.
+3. **Fuzzy/Weak Bits**: Mastering original disks with weak magnetic flux. The hardware reads the bit randomly as a 0 or a 1. When a standard drive copies the disk, it writes a "strong" 0 or 1. The DRM reads the sector multiple times; if the bit doesn't flip, it knows it's a pirated copy.
+
+### 2.4 The Decodification Loop
+
+After reading raw MFM data into memory, the trackloader must decode it back into binary data. This almost always involves interleaved loops using the `eor` (exclusive OR) instruction or bit-shifting to separate clock bits from data bits.
+
+```assembly
+; Typical MFM decoding loop footprint
+.decode_loop:
+ move.l (a0)+, d0 ; Read MFM odd bits
+ move.l (a1)+, d1 ; Read MFM even bits
+ and.l d2, d0 ; Mask clock bits ($55555555)
+ and.l d2, d1
+ lsl.l #1, d0 ; Shift odd bits
+ or.l d1, d0 ; Combine into decoded byte
+ move.l d0, (a2)+ ; Write decoded data
+ dbf d7, .decode_loop
+```
+
+> [!TIP]
+> If you find a loop utilizing the constant `$55555555` extensively near disk hardware access, you have found the MFM decoder.
+
+---
+
+## 3. DRM: The Rob Northen Copylock
+
+The most famous Amiga copy protection is the **Rob Northen Copylock** (RN Copylock). It was designed to prevent cracking by encrypting the game executable and tying the decryption key to a physical flaw deliberately mastered onto track 0 of the original floppy disk.
+
+### 3.1 Copylock Architecture
+
+```mermaid
+graph LR
+ A[Game Executable] --> B[Encrypted Payload]
+ A --> C[Copylock Wrapper]
+ C -->|Read Track 0| D[Custom MFM Read]
+ D -->|Extract Timing Data| E[Generate Decryption Key]
+ E -->|Decrypt & Jump| B
+```
+
+### 3.2 Trace Vector Abuse
+
+Rob Northen's genius was preventing crackers from stepping through the decryption code by abusing the Motorola 68000 **Trace Exception** (Vector $24). By pointing the Trace vector to its own decryption routine and setting the CPU Trace bit, the code decrypts itself via hardware interrupts.
+
+> **Deep Dive**: For a complete analysis of Copylock's trace exception abuse, CIA timer checks, and other anti-cracking techniques, see the dedicated [Anti-Debugging & Arms Race](anti_debugging.md) article.
+
+---
+
+## 4. Modern Analogies
+
+| Amiga Paradigm | Modern Equivalent | Explanation |
+|---|---|---|
+| **Trackloader** | Custom Bootloader / Hypervisor | Bypassing the standard OS kernel to control hardware I/O directly for performance or DRM purposes. |
+| **Rob Northen Trace Abuse** | Denuvo Anti-Tamper / VMProtect | Using CPU exceptions, hardware-specific timing, and virtualization to break standard debuggers (x64 SEH/Vectored Exception Handling abuse). |
+| **MFM `$55555555` Decoding** | Base64 / AES Decryption loops | Transforming an obfuscated or wire-encoded stream back into executable binary code in memory before jumping to it. |
+
+---
+
+## 5. Reverse Engineering Best Practices
+
+1. **Memory Dumps over Static Analysis**: Because most commercial games are packed (Imploder, PowerPacker) and encrypted (Copylock), static analysis of the binary on disk is often useless. Use WinUAE/FS-UAE's built-in debugger to let the game boot, decrypt itself into RAM, and then take a memory dump to analyze in IDA Pro.
+2. **Identify `$DFF024` (DSKLEN)**: Set write breakpoints on `$DFF024` in your emulator. This is the hardware trigger to start a disk DMA read. When it hits, look at the stack to find the trackloader code.
+3. **Beware of `$4.w` (ExecBase)**: If a game reads `$4.w` and immediately calls `Forbid()` (offset `-132`), it is preparing to kill the OS. Put your breakpoints *before* this happens if you are relying on an OS-level debugger.
+
+---
+
+## 6. FAQ
+
+**Q: Why did games use Trackloaders instead of standard AmigaDOS files?**
+A: AmigaDOS (OFS) has significant overhead. It requires memory for file buffers, wastes bytes on directory structures, and the floppy motor turns off between file reads. A custom trackloader keeps the motor spinning and reads entire raw cylinders into RAM sequentially, reducing loading times from minutes to seconds.
+
+**Q: How do WHDLoad patches work with Trackloaders?**
+A: WHDLoad is an OS-replacement system that patches games to run from hard drives. A WHDLoad "Slave" (the patch file) replaces the game's custom trackloader (which expects floppy hardware) with calls to WHDLoad's `resload_DiskLoad` API, emulating the floppy load via standard hard drive I/O.
+
+**Q: If Copylock relies on a physical disk flaw, how do cracked ADFs work?**
+A: Cracked disk images (ADFs) contain the already-decrypted game executable, with the Copylock routine completely bypassed or stubbed out (`NOP` instructions). The physical flaw cannot be represented in a standard ADF, which is why original, uncracked games must be preserved in IPF (Interchangeable Preservation Format) instead of ADF.
diff --git a/05_reversing/dynamic/README.md b/05_reversing/dynamic/README.md
new file mode 100644
index 0000000..83f0d10
--- /dev/null
+++ b/05_reversing/dynamic/README.md
@@ -0,0 +1,10 @@
+[← Home](../../README.md) · [Reverse Engineering](../README.md)
+
+# Dynamic Analysis & Debugging
+
+## Contents
+
+- [serial_debug.md](serial_debug.md) — Monitoring system output via the serial port
+- [live_memory_probing.md](live_memory_probing.md) — Using wack, MonAm, and other on-device tools
+- [enforcer_mungwall.md](enforcer_mungwall.md) — Detecting illegal memory access (MMU protection)
+- [setfunction_patching.md](setfunction_patching.md) — Tracing and intercepting library vectors
diff --git a/05_reversing/games/README.md b/05_reversing/games/README.md
new file mode 100644
index 0000000..ee20340
--- /dev/null
+++ b/05_reversing/games/README.md
@@ -0,0 +1,8 @@
+[← Home](../../README.md) · [Reverse Engineering](../README.md)
+
+# Game Reverse Engineering
+
+## Contents
+
+- [game_reversing.md](game_reversing.md) — Asset extraction, save games, and engine disassembly
+- [whdload_architecture.md](whdload_architecture.md) — WHDLoad internals, slaves, and memory patching
diff --git a/05_reversing/games/game_reversing.md b/05_reversing/games/game_reversing.md
new file mode 100644
index 0000000..c8c52c3
--- /dev/null
+++ b/05_reversing/games/game_reversing.md
@@ -0,0 +1,712 @@
+[← Home](../../README.md) · [Reverse Engineering](../README.md)
+
+# Game Reverse Engineering — Disassembly, Modification, and Asset Extraction
+
+## Overview
+
+Commercial Amiga games were fortresses built from hand-written 68000 assembly, custom trackloaders, and executable packers. The OS was irrelevant — most titles booted straight from disk, seized the hardware, and never called `OpenLibrary` in their lives. Reversing them is not like reversing an AmigaOS `.library` where you anchor on `JSR -N(A6)` and follow the LVO table. You are facing raw metal: arbitrary register usage, self-modifying code, data embedded mid-instruction, and protection schemes designed by people who understood the 68000 trace exception better than Motorola's own engineers.
+
+This article covers the complete workflow for reverse engineering Amiga games — from the first triage of an NDOS disk image to identifying the memory location that holds your lives counter, extracting sprite data, and building a patch that compiles. The techniques here apply equally to demos, bootblock intros, and any binary that bypasses AmigaOS.
+
+> [!NOTE]
+> This article assumes you are already comfortable with 68000 assembly and Amiga hardware registers. If not, start with [Hand-Written Assembly RE](../static/asm68k_binaries.md) and the [OCS Custom Registers](../../01_hardware/ocs_a500/custom_registers.md).
+
+---
+
+## The Game Binary Landscape
+
+Before touching a disassembler, determine what kind of binary you are facing. The approach differs radically.
+
+```mermaid
+graph TD
+ START["Load disk image"] --> SIGNATURE{"Bootblock signature?"}
+ SIGNATURE -->|"DOS\\0"| DOS["AmigaDOS disk
files accessible"]
+ SIGNATURE -->|"Other / NDOS"| NDOS["Non-DOS bootblock
custom trackloader"]
+ DOS --> FILETYPE{"Examine main executable"}
+ NDOS --> PROTECTED{"Trackloader type?"}
+ PROTECTED -->|"Standard MFM"| MFM["Raw track read
sync $4489"]
+ PROTECTED -->|"Custom format"| CUSTOM["Non-standard sync
long tracks, weak bits"]
+ FILETYPE -->|"HUNK executable"| HUNK["Standard RE workflow"]
+ FILETYPE -->|"Packed / Crunched"| PACKED["Decrunch first
→ exe_crunchers.md"]
+ FILETYPE -->|"Raw binary blob"| RAW["Absolute load address
no relocations"]
+```
+
+### NDOS vs DOS-Based Games
+
+| Type | Boot Sequence | Disk Format | RE Strategy |
+|---|---|---|---|
+| **NDOS** | Bootblock disables OS, installs custom trackloader | Non-standard or raw MFM | Dump from emulator after boot; analyze raw memory image |
+| **DOS-based** | Standard AmigaDOS boot; executable launched from disk | Standard OFS/FFS | Analyze HUNK executable directly; may still use hardware banging |
+| **Hybrid** | AmigaDOS boot, but executable takes over hardware | Standard filesystem | Analyze HUNK, but expect `Forbid()` + direct register access |
+
+Most pre-1992 games are NDOS. Most post-1992 titles (especially AGA games and CD32 ports) use AmigaDOS but still bypass the OS for graphics and audio.
+
+### Packed vs Unpacked
+
+Games were packed to fit on floppies. Common packers:
+
+| Packer | Era | Signature | Decrunch Speed |
+|---|---|---|---|
+| **PowerPacker** | 1989–1994 | `$42` + `LEA`/`MOVE.L` pattern | Fast |
+| **Imploder** | 1988–1992 | `$49` (often); ATN!Imploder header | Medium |
+| **ByteKiller** | 1988–1991 | Short `BRA.S` over header, `MOVEM.L` | Very fast |
+| **Shrinkler** | 1999+ | Context-mixing setup; no fixed magic | Slow (minutes on 7 MHz) |
+
+> [!IMPORTANT]
+> Always attempt automated decrunching with `xfdmaster.library` before manual analysis. See [Executable Unpacking](../unpacking_and_decrunching.md) for the full decruncher archaeology workflow.
+
+### Copy Protection Landscape
+
+| Scheme | Mechanism | How to Defeat |
+|---|---|---|
+| **Rob Northen Copylock** | Trace exception decryption tied to disk timing | Let it run in emulator; dump decrypted payload from RAM |
+| **Custom trackloader** | Non-standard MFM, long tracks | Use RawDIC + Imager Slave; see [Custom Loaders](../custom_loaders_and_drm.md) |
+| **Weak / fuzzy bits** | Mastered flux that reads randomly | Preserve as IPF; ADF loses the weakness |
+| **Checksum loops** | Self-checksums with delayed failure | NOP out checksum routine; trace to find patch point |
+
+---
+
+## Tooling
+
+### IRA — Interactive Reassembler
+
+IRA is the native Amiga disassembler of choice for generating **re-assemblable source code**. Unlike IDA or Ghidra, which produce annotated databases, IRA outputs 68000 assembly source (`.asm`) plus a configuration file (`.cnf`) that you can refine iteratively.
+
+**Basic workflow:**
+
+```bash
+# First pass: auto-detect code vs data
+ira -A -KEEPZH -NEWSTYLE -COMPAT=bi -PREPROC MatchPatch
+
+# Refine the .cnf file manually, then re-run with config:
+ira -A -KEEPZH -NEWSTYLE -COMPAT=bi -CONFIG MatchPatch
+```
+
+| Flag | Purpose |
+|---|---|
+| `-A` | Show hex bytes alongside disassembly (invaluable for hex editing later) |
+| `-KEEPZH` | Preserve zero hunks (empty hunks that may hold metadata) |
+| `-NEWSTYLE` | Modern label naming convention |
+| `-COMPAT=bi` | Big-endian compatibility mode |
+| `-PREPROC` | Attempt automatic code/data separation |
+| `-CONFIG` | Re-run using manual `.cnf` corrections |
+
+**Why IRA over IDA/Ghidra for games?**
+- Outputs compilable assembly source, not just annotations
+- The `.cnf` file lets you add `SYMBOL` (rename labels), `LABEL` (add new labels), `COMMENT`, and `BANNER` directives, then regenerate
+- Native awareness of Amiga executable quirks
+- Cross-platform: compiles for Windows, macOS, and Linux (and runs much faster than on real hardware)
+
+**Signs IRA misidentified code as data:**
+- `DC.L` lines containing values like `$4E75` (`RTS`) or `$4E71` (`NOP`)
+- Data areas with labels that look like subroutine names
+
+**Signs IRA misidentified data as code:**
+- Strings of `EXT_` declarations at the start of a program
+- Code sections full of `ORI #0` (`$0000`)
+- Sequences of hex values from `$41` to `$7A` — these are likely unidentified ASCII text
+
+### Ghidra + ghidra-amiga
+
+Ghidra with the `ghidra-amiga` extension provides a full HUNK loader, M68k decompiler, custom chip register mapping, and automatic LVO resolution. See [Ghidra Setup](../ghidra_setup.md) for installation and configuration.
+
+**When Ghidra shines for games:** C-coded late-era titles, cross-reference graphs, global renaming.
+
+**When Ghidra struggles:** Hand-written assembly with no prologues, self-modifying code, `JMP (PC, D0.W)` jump tables, and mixed code/data sections.
+
+### IDA Pro
+
+IDA Pro with the Amiga HUNK plugin is the traditional static analysis choice for Amiga binaries. It excels at interactive annotation, FLIRT signatures, and scripted automation. See [IDA Setup](../ida_setup.md) for configuration details.
+
+**When IDA shines for games:** Interactive tracing, custom IDA Python scripts for jump table resolution, and hardware register enum creation.
+
+**When IDA struggles:** No native M68k decompiler (unlike Ghidra). Heavily optimized hand-written assembly requires manual function boundary definition.
+
+### Where to Get the Tools
+
+| Tool | Where to Obtain | Notes |
+|---|---|---|
+| **IRA** | Aminet: `dev/misc/ira.lha` | Also compiles from source for Windows/macOS/Linux |
+| **Ghidra** | https://ghidra-sre.org/ | Free, from NSA; v10.x+ recommended |
+| **ghidra-amiga** | https://github.com/BartmanAbyss/ghidra-amiga | Load as Ghidra extension; do not unzip |
+| **IDA Pro** | https://hex-rays.com/ida-pro/ | Commercial; requires separate Amiga HUNK plugin |
+| **WinUAE** | https://www.winuae.net/ | Windows Amiga emulator with built-in debugger |
+| **FS-UAE** | https://fs-uae.net/ | Cross-platform (macOS/Linux/Windows); debugger via `Shift+F12` |
+| **xfdmaster.library** | Aminet: `util/pack/xfdmaster.lha` | Native Amiga decruncher; use via `xfdDecrunch` CLI |
+| **hunkinfo** | Aminet: `dev/misc/hunkinfo.lha` | Quick hunk structure dump |
+| **SPS / IPF tools** | https://softpres.org/ | For preserving copy-protected disks as IPF |
+| **RawDIC** | Bundled with WHDLoad distribution | Used with custom Imager Slaves for protected disks |
+
+> [!NOTE]
+> Many of these tools are also available pre-installed in curated Amiga emulation distributions like **Amiga Forever** or pre-configured WinUAE environments from EAB.
+
+### Emulator Debugging
+
+Static analysis alone is often insufficient for games. You need dynamic verification.
+
+**WinUAE / FS-UAE debugger:**
+
+| Key | Action |
+|---|---|
+| `Shift+F12` | Enter debugger |
+| `g
` | Go to address |
+| `z` | Step one instruction |
+| `t` | Trace (step into) |
+| `W ` | Write watchpoint |
+| `R ` | Read watchpoint |
+| `m ` | Dump memory |
+| `d ` | Disassemble from address |
+| `s "filename" ` | Save memory range to file |
+
+**Critical technique — memory dump after decrunch:**
+1. Boot the game in emulator
+2. Enter debugger (`Shift+F12`)
+3. Find the decruncher's final `JMP` to the original entry point
+4. Set a breakpoint on that `JMP`
+5. Let the game run — it decrunches in memory
+6. When breakpoint hits, dump the entire decrunched region with `s`
+
+---
+
+## Pre-Flight: Research Before Disassembly
+
+The most common mistake in game RE is failing to check if the work is already done.
+
+1. **Search for existing analysis** — EAB (English Amiga Board) threads, GitHub repos, speedrun communities, and TCRF (The Cutting Room Floor) often document exactly what you are looking for.
+2. **Check for source code releases** — Original authors sometimes release source decades later (e.g., *Frontier: Elite II* sources, various demo sources).
+3. **Contact the author** — Many Amiga developers are reachable and willing to share insights or even original source.
+4. **Preserve the original media** — If dealing with copy-protected disks, create IPF images (not ADF) using SPS/IPF tools. ADF loses weak-bit protection and custom track formats.
+
+---
+
+## Phase 1: Triage and Loading
+
+### Step 1: Identify the Binary Type
+
+```bash
+# Check first bytes of disk image or executable
+xxd game.adf | head -1
+```
+
+| First Bytes | Meaning |
+|---|---|
+| `44 4F 53 00` (`DOS\0`) | Standard AmigaDOS disk |
+| Other executable | NDOS bootblock or custom format |
+| `00 00 03 F3` | HUNK executable (file, not disk) |
+
+### Step 2: For NDOS Disks — Extract the Bootblock
+
+The bootblock is the first 1024 bytes (2 sectors) of the disk. It is loaded to `$7C00` by Kickstart and executed directly.
+
+```bash
+# Extract bootblock from ADF
+dd if=game.adf of=bootblock.bin bs=1024 count=1
+```
+
+Analyze `bootblock.bin` at base address `$7C00`. Look for:
+- `BRA` or `JMP` to the main loader
+- `DSKSYNC` writes (`$DFF07E`) — indicates trackloader
+- `INTENA` / `DMACON` writes — indicates OS takeover
+
+### Step 3: For HUNK Executables — Dump Structure
+
+```bash
+hunkinfo game.exe
+```
+
+Note hunk types, sizes, and whether symbols are present. Some late-era games shipped with debug symbols accidentally left in — these are gold.
+
+### Step 4: Detect Packing
+
+Scan the first CODE hunk for packer signatures. See [Executable Unpacking](../unpacking_and_decrunching.md) for the full signature table.
+
+---
+
+## Phase 2: Finding Anchors
+
+A 500 KB game binary is overwhelming. You need **anchors** — known values or patterns that let you orient yourself.
+
+### Anchor 1: Text Strings
+
+Games contain strings for menus, cheat codes, status messages, and file names.
+
+```bash
+# Extract strings from binary
+strings game.exe > strings.txt
+
+# Or with IRA:
+ira -TEXT=1 -A -PREPROC game.exe
+```
+
+**String types and what they reveal:**
+
+| String Pattern | Likely Meaning |
+|---|---|
+| `"graphics.library"` | Game uses OS graphics (unusual) |
+| `"dos.library"` | Game uses OS file I/O |
+| `"FORM"`, `"ILBM"`, `"8SVX"` | Embedded IFF assets |
+| `"MATCHPATCH"`, `"ZOOL"` | Game title or internal project name |
+| File paths like `"worlds/nif2txt.dat"` | Level data loading routines nearby |
+| `"CHEAT ENABLED"` | Cheat code handler |
+
+> [!WARNING]
+> `strings` often returns garbage from misidentified data sections. Cross-reference with the disassembly to confirm the string is actually referenced by code.
+
+### Anchor 2: Known Numeric Values
+
+Games contain specific numbers: starting lives, maximum health, item prices, level counts.
+
+| Decimal | Hex (16-bit) | Hex (32-bit) | Likely Meaning |
+|---|---|---|---|
+| 3 | `$0003` | `$00000003` | Starting lives |
+| 10 | `$000A` | `$0000000A` | Common statistic |
+| 100 | `$0064` | `$00000064` | Percentage scale, health |
+| 1000 | `$03E8` | `$000003E8` | Score multiplier, currency |
+| 320 | `$0140` | `$00000140` | Screen width (lowres) |
+| 200 | `$00C8` | `$000000C8` | Screen height (PAL lowres) |
+
+Search for these values in hex. If you find a `MOVE.W #$0003, D0` near initialization code, you have likely found the lives setup.
+
+### Anchor 3: Hardware Register Accesses
+
+Even games that take over the OS usually hit hardware registers. These are unambiguous anchors.
+
+| Register | Address | What It Reveals |
+|---|---|---|
+| `JOY0DAT` | `$DFF00A` | Joystick/mouse port 0 reads — player input handling |
+| `JOY1DAT` | `$DFF00C` | Joystick/mouse port 1 reads |
+| `AUD0LCH`–`AUD3LCH` | `$DFF0A0`–`$DFF0D0` | Audio channel setup — sound effects, music |
+| `VHPOSR` | `$DFF006` | Vertical/horizontal position — RNG seeding, VBlank waits |
+| `VPOSR` | `$DFF004` | Vertical position (high bits) — frame timing |
+| `COP1LC` | `$DFF080` | Copper list pointer — display setup |
+| `BLTCON0` | `$DFF040` | Blitter control — graphics rendering |
+
+**Input handling identification:**
+
+```asm
+; Classic joystick read pattern:
+MOVE.W $DFF00A, D0 ; Read JOY0DAT
+AND.W #$0101, D0 ; Mask direction bits
+; ... decode into game state ...
+```
+
+**Random number generator identification:**
+
+```asm
+; Common RNG seed pattern — uses beam position for entropy:
+MOVE.W $DFF006, D0 ; VHPOSR = current raster position
+; ... shuffle with ROXR ...
+```
+
+The `ROXR` (rotate right with extend) instruction is a dead giveaway for RNG routines. Once you find the RNG, every caller is potentially a game mechanic.
+
+### Anchor 4: AmigaOS Library Calls
+
+Some games, especially later titles and CD32 ports, use AmigaOS for initialization or file I/O.
+
+```asm
+; OpenLibrary call pattern:
+MOVEA.L 4.W, A6 ; SysBase
+LEA graphics_name(PC), A1
+MOVEQ #33, D0 ; minimum version
+JSR -552(A6) ; OpenLibrary
+```
+
+| LVO | Library | Function | Game RE Relevance |
+|---|---|---|---|
+| `-552` | exec | `OpenLibrary` | Loading libraries |
+| `-30` | dos | `Open` | File loading — level data, save games |
+| `-42` | dos | `Read` | Reading data into memory — reveals file→memory mapping |
+| `-48` | dos | `Write` | **Save games and high scores** — critical for state analysis |
+| `-36` | dos | `Close` | File cleanup |
+
+**Save game exploitation:** `Write` calls in games are almost always save games or high scores. The data written holds persistent game state (inventory, stats, level progress). Finding the `Write` call reveals the in-memory structure of the save game, enabling editor construction.
+
+### Anchor 5: File Read Patterns
+
+```asm
+; File read pattern — reveals memory destinations:
+JSR -30(A6) ; Open(file_name, MODE_OLDFILE)
+MOVEA.L D0, D1 ; FileHandle
+MOVEA.L #buffer, D2 ; Destination address
+MOVE.L #size, D3 ; Bytes to read
+JSR -42(A6) ; Read(FileHandle, buffer, size)
+```
+
+The `buffer` address is the in-memory location of that file's data. Cross-reference this with string anchors (e.g., `"worlds/nif2txt.dat"`) to map file contents to memory layout.
+
+---
+
+## Phase 3: Mapping Game Mechanics
+
+Once anchored, trace outward to reconstruct the game's logic.
+
+### Finding the Main Game Loop
+
+Games need to synchronize to the display refresh (50 Hz PAL / 60 Hz NTSC). Look for:
+
+```asm
+; VBlank wait pattern — the heartbeat of the game:
+wait_vblank:
+ MOVE.W $DFF006, D0 ; VHPOSR
+ AND.W #$FF00, D0 ; Mask vertical position
+ CMP.W #$0000, D0 ; Wait for line 0 (start of frame)
+ BNE.S wait_vblank
+```
+
+Or the more common VPOSR check:
+
+```asm
+wait_frame:
+ MOVE.W $DFF004, D0 ; VPOSR
+ AND.W #$01FF, D0 ; Mask vertical position bits
+ CMP.W #303, D0 ; Wait for bottom of PAL frame
+ BNE.S wait_frame
+```
+
+The code immediately following the VBlank wait is the **main game loop**.
+
+### Identifying Score and Lives
+
+1. **Hex search**: Search for the starting value (e.g., 3 lives = `$0003`).
+2. **Cross-reference**: Find all instructions that write this value. One is initialization; others are decrement (lose life) or increment (gain life).
+3. **Verify with emulator**: Patch the value at the memory location and run the game. If you start with 99 lives, you found it.
+
+### Identifying the RNG
+
+```asm
+; Typical Amiga game RNG (seeded from beam position):
+rng_seed:
+ MOVE.W $DFF006, D0 ; VHPOSR
+ EOR.W D0, rng_state ; Mix with current state
+ ROXR.W #1, rng_state ; Shuffle
+ MOVE.W rng_state, D0 ; Return random value
+ RTS
+```
+
+**Key signatures:**
+- Read from `$DFF006` or `$DFF004`
+- `ROXR` or `ROR` instruction
+- Called from combat, item drops, enemy spawn, or any probabilistic mechanic
+
+### Audio as a Navigation Aid
+
+Audio register writes reveal what the code is doing:
+
+```asm
+; Sound effect trigger:
+MOVE.L #bullet_sample, $DFF0A0 ; AUD0LCH/LCL = sample pointer
+MOVE.W #period, $DFF0A6 ; AUD0PER = playback period
+MOVE.W #volume, $DFF0A8 ; AUD0VOL = volume
+```
+
+If you extract the sample referenced by `bullet_sample` and hear a gunshot, you have found the shooting code. From there, trace back to find collision detection, enemy damage, and scoring.
+
+---
+
+## Phase 4: Modification and Patching
+
+### Hex Editing Known Values
+
+Once you have identified a value's location in the binary:
+
+1. Note the file offset and original bytes from the IRA `-A` output
+2. Open the binary in a hex editor
+3. Search for the unique byte sequence surrounding the value
+4. Patch and test
+
+| File Offset | Original | Patched | Effect |
+|---|---|---|---|
+| `$0001A4` | `66 0A` | `4E 71 4E 71` | Replace `BNE` with two `NOP`s (defeat branch) |
+| `$003210` | `03` | `63` | Change starting lives from 3 to 99 |
+
+> [!WARNING]
+> Patched bytes must preserve instruction alignment. A 16-bit `MOVE.W` patch that changes length will shift all subsequent code and break absolute addresses.
+
+### Building a Re-Assemblable Patch
+
+For complex modifications, IRA's output is ideal:
+
+1. Disassemble with IRA to get `game.asm` and `game.cnf`
+2. Edit `game.asm` directly (e.g., change `MOVEQ #3, D0` to `MOVEQ #99, D0`)
+3. Assemble with vasm or AsmOne
+4. Test on emulator
+
+### Trainer / Cheat Menu Construction
+
+A trainer is a small patch that installs a hotkey handler to modify game state at runtime:
+
+```asm
+; Minimal trainer hook — intercepts keyboard and grants lives
+ MOVE.W $BFEC01, D0 ; Read keyboard data port (CIAA)
+ NOT.B D0
+ ROR.B #1, D0 ; Decode raw keycode
+ CMP.B #$45, D0 ; F1 key?
+ BNE.S .no_cheat
+ MOVE.B #99, lives_counter ; Grant 99 lives
+.no_cheat:
+```
+
+Install this in the VBlank interrupt or keyboard handler. See [SetFunction Patching](../dynamic/setfunction_patching.md) for runtime hook techniques.
+
+---
+
+## Phase 5: Asset Extraction
+
+### Text and Strings
+
+Use the IRA `-TEXT=1` option or `strings` to find all text. For games with custom text encoding (e.g., compressed or shifted character sets), identify the font rendering routine and reverse the encoding table.
+
+### Graphics — IFF Extraction
+
+Amiga games often store graphics in IFF format (`FORM ILBM`) or raw planar bitmaps.
+
+**IFF detection:** Search for `FORM` (`$464F524D`) and `ILBM` (`$494C424D`) signatures in the binary or memory dump. The IFF header gives you width, height, depth, and palette.
+
+**Raw planar extraction:**
+1. Find `BPL1PT`–`BPL5PT` writes in the copper list or code
+2. The pointers reveal bitmap base addresses in memory
+3. Dump the memory range; decode as planar (interleaved or non-interleaved based on `BPLMOD`)
+
+### Audio — Sample and Module Extraction
+
+| Format | Signature | Extraction |
+|---|---|---|
+| **8SVX** | `FORM` + `8SVX` | IFF audio chunk; playable directly |
+| **Protracker MOD** | `M.K.`, `FLT4`, `4CHN` | Standard 31-sample + pattern data format |
+| **Raw PCM** | None — identified via `AUDxLCH` writes | Mono 8-bit signed; import to Audacity as raw 8-bit signed, ~8000–28000 Hz |
+
+---
+
+## Decision Guide: Choosing Your Toolchain
+
+| Scenario | Recommended Tool | Why |
+|---|---|---|
+| Need re-assemblable source | **IRA** | Outputs `.asm` + `.cnf`; iterative refinement |
+| Need C pseudocode / cross-references | **Ghidra + ghidra-amiga** | Decompiler, global renaming, xref graph |
+| Heavy OS library usage (late games) | **Ghidra** | Automatic LVO resolution |
+| Pure assembly, no OS calls | **IRA + emulator** | Ghidra decompiler gives up; IRA + dynamic trace works better |
+| Packed / protected game | **Emulator debugger first** | Let protection run, dump decrypted memory, then load into static tool |
+| Quick value patch (lives, score) | **Hex editor** | Fastest for one-byte changes |
+| Bootblock analysis (1024 bytes) | **IRA or raw disassembly** | Small enough to read linearly |
+
+---
+
+## Historical Context
+
+### Why Games Bypassed the OS
+
+| Factor | Impact |
+|---|---|
+| **7 MHz CPU** | Every CPU cycle mattered. `graphics.library` added overhead (layer locking, clipping checks). |
+| **512 KB Chip RAM** | OS structures consumed precious DMA-accessible memory. Games needed every byte for sprites and sound. |
+| **Disk speed** | 880 KB floppy at ~50 KB/s effective. Custom trackloaders achieved 2–3× speed by reading raw tracks sequentially. |
+| **Copy protection** | AmigaDOS disks were trivially copyable with X-Copy. NDOS + custom formats + weak bits made mass duplication harder. |
+| **Demoscene culture** | Assembly was the standard. Using a C compiler for a game engine was seen as lazy until the mid-1990s. |
+
+### The NDOS-to-DOS Transition
+
+- **1985–1990**: Almost all commercial games are NDOS, hand-written assembly, custom trackloaders.
+- **1990–1993**: Hybrid era. Games boot from AmigaDOS but take over hardware after loading. Some use `dos.library` for file I/O.
+- **1993–1996**: AGA and CD32 era. Larger budgets, more C code, AmigaDOS-based loading. WHDLoad emerges to patch games for hard drive installation.
+
+---
+
+## Modern Analogies
+
+| Amiga Game RE Concept | Modern Equivalent | Where It Holds / Breaks |
+|---|---|---|
+| NDOS bootblock takeover | UEFI bootkit / custom bootloader | Holds: bypasses OS entirely. Breaks: bootblock is 1024 bytes, UEFI is MBs. |
+| Custom trackloader | Direct NAND flash controller access | Holds: raw media access for speed. Breaks: no MFM encoding on flash. |
+| Executable packer | UPX, VMProtect packing | Holds: runtime decompression + jump to OEP. Breaks: modern packers use virtualization. |
+| Rob Northen Copylock | Denuvo anti-tamper | Holds: trace/exception abuse, timing checks. Breaks: Copylock is 68000-specific; Denuvo uses x64 VM. |
+| Hardware register banging | Embedded MCU programming (STM32, Arduino) | Holds: direct MMIO register access. Breaks: Amiga chips are video/audio-specific. |
+| Memory patch (lives counter) | Cheat Engine / GameGuardian | Holds: scan for known value, patch at runtime. Breaks: modern games use encrypted/process-isolated memory. |
+
+---
+
+## Best Practices
+
+1. **Always preserve original media as IPF before modifying** — ADF loses copy protection and custom formats.
+2. **Try automated decrunching first** — `xfdmaster.library` can save hours.
+3. **Document every patch with file offset, original bytes, and rationale** — you will forget why you changed something.
+4. **Use the `-A` flag in IRA** — seeing raw hex bytes alongside disassembly is essential for building patch tables.
+5. **Verify anchors dynamically** — a suspected lives counter may actually be a loop iterator. Patch and test in emulator.
+6. **Build a register map as you trace** — hand-written assembly has no ABI. Document what each register means in each routine.
+7. **Save memory dumps at key moments** — after decrunch, after level load, after title screen. Compare dumps to find dynamic data structures.
+8. **Trace audio register writes to locate game events** — sound effects are the most reliable event markers in assembly.
+9. **Cross-reference file reads with string anchors** — `"level1.dat"` + `dos.library Read` = level data structure.
+10. **Work iteratively** — name one function, trace its callers, name them too. Do not attempt to understand the entire binary in one pass.
+
+---
+
+## Antipatterns
+
+### 1. The Linear Reading Trap
+
+**Wrong**: Opening the disassembly at offset 0 and reading top-to-bottom expecting to understand the game.
+
+**Why it fails**: Hand-written assembly is non-linear. The entry point sets up interrupts and copper lists, then the real game logic lives in ISR chains and event handlers scattered across the binary.
+
+**Right**: Start from anchors (strings, hardware registers, known values) and trace outward using cross-references.
+
+### 2. The Compiler Assumption
+
+**Wrong**: Expecting `A6` to be a library base, `D0`/`D1` to be scratch, and `LINK`/`UNLK` function boundaries.
+
+**Why it fails**: Games are hand-written assembly. `A6` might hold the hardware base pointer. `D6` might be the frame counter. Functions may have no prologue.
+
+**Right**: Treat every register as unknown until proven otherwise. Document the actual convention per routine.
+
+### 3. The OS Dependency Delusion
+
+**Wrong**: Searching extensively for `JSR -N(A6)` library calls to anchor analysis.
+
+**Why it fails**: Most games make zero OS calls after initialization. The action is at `$DFF000` and `$BFE001`, not in `exec.library`.
+
+**Right**: Scan for hardware register constants (`$DFF`, `$BFE`) first. If none appear, then check for OS calls.
+
+### 4. The Phantom String
+
+**Wrong**: Assuming every readable ASCII sequence in the binary is a meaningful string.
+
+**Why it fails**: Random data bytes can decode as printable ASCII. A string with no code cross-reference is likely not a string.
+
+**Right**: Always verify that code actually references the string address (via `LEA string(PC), A0` or similar).
+
+### 5. The In-Place Patch Disaster
+
+**Wrong**: Changing a `MOVE.W #3, D0` to `MOVE.W #99, D0` without checking instruction length.
+
+**Why it fails**: `MOVEQ #3, D0` is 2 bytes. `MOVE.W #99, D0` is 4 bytes. The patch overflows into the next instruction, corrupting the code stream.
+
+**Right**: Use equivalently-sized instructions. `MOVEQ #99, D0` is invalid (`MOVEQ` range is -128 to +127, so `#99` is fine and still 2 bytes). For larger values, `MOVE.W` is required but check alignment.
+
+---
+
+## Pitfalls & Common Mistakes
+
+### 1. Misidentifying Data as Code
+
+Mixed code/data is the norm in game binaries. Copper lists, sprite data, and audio samples often reside in CODE hunks.
+
+```asm
+; This looks like instructions:
+OR.B #$80, D0
+OR.B #0, D0
+OR.B #$82, D0
+OR.B #$FF, D0
+
+; But it is actually a copper list:
+; DC.W $0180, $0000 = COLOR00 = $0000
+; DC.W $0182, $0FFF = COLOR01 = $0FFF
+```
+
+**Fix**: Search for `COP1LC` writes to find copper list addresses. Force-define those ranges as data arrays, not code.
+
+### 2. Ignoring Relocation State
+
+When you dump decrunched memory from an emulator, absolute addresses have already been patched by the decrunch stub. If you try to run that dump at a different load address, it crashes.
+
+**Fix**: Note the load address used by the emulator. If re-assembling, either use the same base address or reconstruct PC-relative addressing.
+
+### 3. Debugging After OS Death
+
+Games call `Forbid()` and disable interrupts early. If your debugger relies on AmigaOS (like MonAm or HRTmon), it stops working the moment the game takes over.
+
+**Fix**: Use emulator built-in debuggers (WinUAE/FS-UAE `Shift+F12`) or hardware cartridges (Action Replay) that trap via NMI, not OS services.
+
+### 4. Overlooking Self-Modifying Code
+
+Copy protection and optimization both use SMC. The disassembly shows one instruction; the runtime executes another.
+
+```asm
+; Static disassembly shows:
+MOVE.W D0, D0 ; This gets patched at runtime
+
+; Init routine overwrites it:
+MOVE.W #$4E71, (patched+2, PC) ; Patch in a NOP
+```
+
+**Fix**: Set write breakpoints on CODE hunk addresses in the emulator. If anything writes there during init, you have SMC.
+
+### 5. Confusing VBlank Wait with Game Logic
+
+The VBlank wait loop is easy to find but tells you nothing about *what* the game does each frame.
+
+**Fix**: Trace forward from the VBlank wait exit. The next block is usually the frame update routine. Set a breakpoint there and step through one full frame.
+
+---
+
+## Use Cases
+
+### Speedrun Research
+
+Reverse engineering reveals frame-perfect mechanics: RNG seeds, hitbox dimensions, level transition triggers. Documenting the `RNG_seed` routine and its callers lets speedrunners manipulate luck.
+
+### Translation Projects
+
+Finding the text rendering routine and font data enables text replacement. Games with embedded ASCII strings are trivial; games with custom encoding require reversing the blit-based font routine.
+
+### Save Game Editors
+
+The `dos.library Write` call that saves game state reveals the exact memory structure of the persistent state. Mapping this structure enables external save game editors.
+
+### Modding and Enhancement
+
+Patching the weapon damage table, adding a cheat menu, or replacing audio samples all require understanding the binary's data layout. IRA's re-assemblable output makes this sustainable.
+
+### Preservation and Documentation
+
+Documenting the internal structure of unreleased or poorly documented games contributes to the historical record. TCRF and similar archives rely on this work.
+
+---
+
+## FAQ
+
+### Q1: IRA vs Ghidra — which should I use?
+
+Use **IRA** when you need re-assemblable source code or are working with pure hand-written assembly. Use **Ghidra** when you need cross-references, decompilation, or the game was written in C. Many RE projects use both: Ghidra for exploration, IRA for final patch generation.
+
+### Q2: How do I handle a game with no readable strings?
+
+High entropy and no strings suggest encryption or compression. Let the game boot in an emulator, dump memory after decrunch/decryption, then analyze the dump. The decrypted payload will have strings.
+
+### Q3: Can I reverse a game back to C?
+
+Not really. Generic decompilation of hand-written 68000 assembly produces unreadable pseudocode. The only successful "decompilations" are hand-crafted rewrites based on deep understanding of the assembly (e.g., *GLFrontier*).
+
+### Q4: How do I find the level data format?
+
+Anchor on file read calls (`dos.library Read`) or look for large data tables referenced by the rendering code. Level data often follows audio/graphic assets in memory. Compare memory dumps between levels to find what changes.
+
+### Q5: What if the game uses a custom trackloader I can't read?
+
+Use WinUAE's disk DMA breakpoint (`W $DFF024 2`) to catch every disk read. Trace backward from the breakpoint to find the trackloader code. Document the sync word, sector count, and MFM decode routine. See [Custom Loaders](../custom_loaders_and_drm.md).
+
+### Q6: How do I patch a game that checksums itself?
+
+Find the checksum routine (usually a tight loop with `ADD.L` or `EOR.L` over a memory range). NOP it out, or recalculate the checksum to match your patch. The checksum routine is often called from multiple places — patch all callers.
+
+### Q7: Why does my patched game crash on real hardware but work in emulator?
+
+Emulators are more forgiving of timing violations. Your patch may have altered cycle-exact code (e.g., a copper wait or blitter poll). Verify that you haven't changed instruction timing or introduced bus errors.
+
+---
+
+## References
+
+- [Hand-Written Assembly RE](../static/asm68k_binaries.md) — Pure m68k binary methodology
+- [Executable Unpacking](../unpacking_and_decrunching.md) — Decruncher archaeology and memory extraction
+- [Custom Loaders & DRM](../custom_loaders_and_drm.md) — Trackloaders, copy protection, RawDIC
+- [Ghidra Setup](../ghidra_setup.md) — Ghidra + ghidra-amiga extension configuration
+- [Anti-Debugging](../anti_debugging.md) — Trace vector abuse, NMI defeat, checksum loops
+- [WHDLoad Architecture](../whdload_architecture.md) — Slave authoring and snooping
+- [Copper Programming](../../08_graphics/copper_programming.md) — Copper list format
+- [Blitter Programming](../../08_graphics/blitter_programming.md) — Blitter register sequences
+- [Paula Audio](../../01_hardware/ocs_a500/paula_audio.md) — Audio DMA registers
+- *Amiga Hardware Reference Manual* — Custom chip register reference
+- *M68000 Programmer's Reference Manual* — Instruction set and cycle timing
+- EAB: Small IRA Tutorial — https://eab.abime.net (search "IRA tutorial")
+- ghidra-amiga: https://github.com/BartmanAbyss/ghidra-amiga
+- Tetracorp Amiga RE Guide — https://tetracorp.github.io/guide/reverse-engineering-amiga.html
diff --git a/05_reversing/games/whdload_architecture.md b/05_reversing/games/whdload_architecture.md
new file mode 100644
index 0000000..b140a3b
--- /dev/null
+++ b/05_reversing/games/whdload_architecture.md
@@ -0,0 +1,222 @@
+[← Home](../../README.md) · [Reverse Engineering](../README.md)
+
+# WHDLoad Architecture & Reverse Engineering
+
+If [Trackloaders](custom_loaders_and_drm.md) were the developers' way of taking complete control of the Amiga to bypass the OS, **WHDLoad** is the modern reverse engineer's way of taking that control *back*.
+
+WHDLoad is essentially an AmigaOS-compliant "hypervisor" that wraps hardware-banging games, fools them into thinking they have absolute control of an Amiga 500, and intercepts their physical hardware requests to run them from modern hard drives.
+
+Creating a WHDLoad port of a protected game is a multi-stage process involving disk imaging, execution profiling, reverse engineering, and assembly programming.
+
+---
+
+## 1. End-to-End Developer Workflow
+
+Before diving into the low-level details, the entire lifecycle of creating a WHDLoad patch flows from the physical floppy disk through reverse engineering, ending in a deployable hard-drive package.
+
+```mermaid
+graph TD
+ subgraph Phase1 [Phase 1: Imaging]
+ A[Original Protected Floppy] -->|RawDIC + Imager Slave| B[(Disk.1 Image)]
+ end
+
+ subgraph Phase2 [Phase 2: Reverse Engineering]
+ B --> C{WHDLoad Snoop Mode}
+ C -->|Logs hardware traps| D[Analyze Memory & Trackloader]
+ D -->|Write 68k Assembly| E[Compile Game Slave]
+ end
+
+ subgraph Phase3 [Phase 3: Execution]
+ F[AmigaOS / Workbench] -->|Launch| G[WHDLoad Host]
+ G -->|Reads| B
+ G -->|Reads| E
+ G -->|Kills OS, Allocates Walled Garden| H[Memory]
+ E -->|Hooks disk access & memory| I[Game Executable]
+ I -.->|resload API| G
+ end
+```
+
+---
+
+## 2. The Imaging Phase: From Floppy to File
+
+Before a game can run from a hard drive, its physical floppy disks must be perfectly preserved as data files.
+
+### 2.1 DIC vs. RawDIC
+WHDLoad provides two primary tools for imaging:
+* **DIC (Disk Image Creator)**: Used for standard AmigaDOS formatted disks. It rips the disk into standard `.iso` or `.adf` style files (usually named `Disk.1`, `Disk.2`).
+* **RawDIC**: Used for games with custom trackloaders or physical DRM (weak bits, long tracks). RawDIC does not know how to read these formats inherently.
+
+### 2.2 Imager Slaves & The Amiga Floppy Controller
+
+To use RawDIC on a protected game, the developer must first reverse-engineer the game's bootblock and write an **Imager Slave**.
+
+An Imager Slave is a **68000 Assembly language program** (never C code). Developers write the `.asm` file using the official [WHDLoad Developer Package](http://whdload.de/), which provides necessary macros (like `rawdic.i`). The source code is compiled using a standard Amiga assembler (like PhxAss or VASM) into an executable binary.
+
+When you run RawDIC from the command line, you pass the Imager Slave to it as an argument:
+`RawDIC slave=MyGame.ISlave`
+
+RawDIC loads the Imager Slave, which then dictates exactly how to decode the disk's specific Magnetic Flux Reversals (MFM).
+To understand how RawDIC works, you must understand the Amiga's unique floppy architecture, which is radically different from a PC (NEC 765) or ZX Spectrum (WD1793). The Amiga does not use an intelligent, high-level floppy controller. It uses two custom chips:
+1. **[CIA-A / CIA-B (8520)](../01_hardware/common/cia_chips.md)**: Handles mechanical logic. The CPU writes to CIA registers to turn on the motor, select a drive, choose head direction, and send step pulses.
+2. **[Paula (8364)](../01_hardware/common/floppy_hardware.md)**: Handles data transfer via **DMA (Direct Memory Access)**. Paula does not decode sectors. It simply looks for a 16-bit "Sync Word" (standard is `$4489`) in the magnetic flux. When it sees that word, Paula blindly streams the raw, decoded MFM bitstream directly into Chip RAM, completely bypassing the CPU.
+
+RawDIC is a **software tool** that leverages this architecture directly. It bypasses the standard AmigaOS `trackdisk.device`. Instead, it uses the CIA to mechanically step to a track, and then programs Paula's DMA registers (`DSKLEN`, `DSKPTH`, `DSKSYNC`).
+
+If a game uses a proprietary format (e.g., a custom sync word like `$8944` instead of `$4489`), standard AmigaDOS fails. An Imager Slave tells RawDIC exactly what custom parameters to feed Paula. RawDIC then triggers the DMA transfer, pulling the custom MFM stream into memory, where the Imager Slave executes custom 68000 routines to decode the MFM bits into a flat `Disk.1` payload file suitable for WHDLoad.
+
+> **Note: RawDIC vs. Hardware Flux Readers**
+> This process is completely separate from modern PC-based hardware devices like **KryoFlux**, **SuperCard Pro**, or **GreaseWeazle**. Those are external USB devices that capture magnetic flux at the hardware level to create archival `.scp` or `.raw` images. RawDIC, conversely, is an Amiga-native software solution that relies on Paula's DMA and the developer's Imager Slave to decode the MFM stream on-the-fly.
+
+### 2.3 Post-Processing
+If the game uses a custom filesystem, the developer might not want a massive `Disk.1` image. They might write a script to extract individual files from the raw tracks so the WHDLoad patch can load them natively via the Host OS. They also manually correct any bad checksums caused by original mastering errors.
+
+---
+
+## 3. The Snooping Phase (Execution Profiling)
+
+Once the data is ripped, the developer must figure out exactly what the game is doing to the hardware. WHDLoad has built-in profiling called **Snooping** (activated via `Snoop=1` or `Snoop=2` in the `.info` tooltypes).
+
+When Snooping is enabled, WHDLoad uses the CPU's Memory Management Unit (MMU) to trap all memory accesses. It generates a massive log of:
+* **Custom Register Violations**: Intercepts illegal byte-writes to 16-bit custom registers (which legally require word writes, with exceptions like `bltcon0l`). It also traps writes to read-only registers or reads from write-only registers.
+* **CIA Hazards**: Detects illegal read-modify-write instructions (like `BCHG`) on CIA Time of Day registers when the Alarm bit is active.
+* **Memory Bounds**: Every read/write outside the game's allocated memory.
+* **Advanced DMA Validation**: WHDLoad provides granular Snoop flags (`ChkBltSize`, `ChkBltWait`, `ChkCopCon`, `ChkAudPt`) that use instruction tracing and bounds checking to catch Copper and Blitter jobs attempting to read/write outside `BaseMem`, or the Copper illegally attempting to configure the Blitter (`custom.copcon` bit 1).
+
+> **Warning: 68040/060 Snoop Limitations**
+> On 68040 and 68060 processors, `MOVEM` (Move Multiple) instructions can sometimes bypass Snoop's Access Fault handler. This occurs because the MMU only verifies the *first* address accessed during the burst transfer, potentially allowing illegal chip accesses further down the block to slip through undetected. **Because of this hardware flaw, a 68030 with a full MMU is considered the "gold standard" hardware for accurately profiling games.**
+
+This log becomes the developer's "To-Do" list. Every illegal or hardware-banging operation in the log must be intercepted and patched.
+
+---
+
+## 4. Writing the Game Slave
+
+The **Game Slave** (`game.slave`) is a small piece of 68000 assembly code written specifically for one game. It is the core reverse-engineering patch.
+
+### 4.1 The Walled Garden & MMU Virtualization
+When WHDLoad launches, it allocates a contiguous block of RAM for the game (`BaseMem` and optionally `ExpMem`). Using the MMU, WHDLoad builds a precise translation tree that explicitly marks the following physical regions as **Valid Pages**:
+* `$00000000` through the end of allocated `BaseMem`/`ExpMem`.
+* `$dff000 - $dff200` (Custom Hardware Registers).
+* `$bfd000 - $bff000` (CIA Registers).
+
+Every other memory page is marked as **Invalid**. Any read or write outside these explicitly defined Walled Garden boundaries immediately triggers an Access Fault Exception handled by WHDLoad. This forces the game to believe it is running at absolute address `$00000000`, with full ownership of the 512KB Chip RAM, while absolutely protecting the host OS.
+
+### 4.2 KickEmu (OS Emulation)
+Some games bypass the OS for disk access but still rely on `exec.library` or `graphics.library` for initialization. WHDLoad provides **KickEmu**, a set of pre-built modules that load actual Kickstart ROM images (1.3 or 3.1) into Fast RAM and emulate a pristine boot environment just for the game.
+
+### 4.3 Patching the Footprint
+The Slave searches the game's loaded memory for the exact byte signature of its custom trackloader. It then overwrites the original `JSR` (Jump to Subroutine) entry points with jumps to the Slave's own code.
+
+```mermaid
+graph TD
+ A[AmigaOS] -->|Runs| B[WHDLoad Host]
+ B -->|Allocates RAM| C[(Memory Walled Garden)]
+ B -->|Loads| D[Game.Slave]
+ B -->|Kills OS & Interrupts| D
+ D -->|Searches & Patches| E[Game Executable]
+ D -->|Executes| E
+ E -.->|Intercepted Hardware Access| D
+ D -.->|Proxies via Resload API| B
+```
+
+### 4.4 Redirection to `resload_DiskLoad`
+Inside the Slave's hook, the game's request (e.g., "Cylinder 5, Head 0") is translated into a byte offset within the `Disk.1` image file.
+
+The Slave then calls `resload_DiskLoad`, a callback function provided by the WHDLoad Host.
+
+```mermaid
+sequenceDiagram
+ participant Game
+ participant Slave
+ participant Host
+ participant HardDrive
+
+ Game->>Slave: Read Track 5, Head 0 (Patched JSR)
+ Slave->>Slave: Translate physical Track to file offset
+ Slave->>Host: resload_DiskLoad(offset, length, dest_ram)
+ Host->>HardDrive: Read bytes from disk.1
+ HardDrive-->>Host: Data
+ Host-->>Slave: Write to dest_ram
+ Slave-->>Game: RTS (Data loaded!)
+```
+
+### 4.5 The Soft Reset (`QuitKey`)
+WHDLoad requires that every game can be exited gracefully, returning the user to the AmigaOS Workbench without rebooting.
+
+The Slave implements this by intercepting the keyboard hardware interrupt (Level 2) or the Action Replay NMI (Level 7). When the user presses the designated `QuitKey` (often F10 or PrtScn), the Slave intercepts it and triggers a `resload_Abort` call. WHDLoad then restores the OS interrupt vectors, flushes the caches, and turns multitasking back on.
+
+---
+
+## 5. Hardware Virtualization & Fixes
+
+Beyond disk access, Slaves must fix hardware incompatibilities so an A500 game runs on an A1200 or 68060 accelerator:
+
+1. **MMU Virtualization & Traps**: WHDLoad takes over the MMU to proxy OS functions and validate memory bounds. Because of this, running debugging tools like **Enforcer** or **CyberGuard** simultaneously with WHDLoad causes machine lockups, as WHDLoad intentionally generates hundreds of MMU hits during normal operation.
+2. **SMC Defeat & The 68060 Paradox**: The 68000 had no instruction cache; the 68020+ does. Games using Self-Modifying Code (SMC) crash on newer processors because the CPU executes stale instructions. Slaves patch these areas with `resload_FlushCache`.
+ * *The 68060 Paradox*: The Motorola 68060 introduced a "Branch Cache" that **completely ignores the MMU setup**. Even if WHDLoad marks a memory page as Non-Cacheable to protect SMC, the 68060 will *still* cache branch instructions. Game Slaves must explicitly use `resload_SetCPU` (or the historical `resload_SetCACR`) to disable or flush this cache, or the game will crash on an 060.
+3. **Access Faults**: Games might try to read memory outside their allocated Chip RAM. Slaves `NOP` out these checks.
+3. **Interrupt Timing**: If a game relies on the specific speed of a 68000 executing a loop for timing, it runs 10x too fast on a 68030. Slaves replace these loops with `resload_Delay` calls to normalize the speed.
+
+---
+
+## 6. Advanced Debugging & Profiling
+
+### 6.1 Advanced Snooping & Memory Dumps
+While basic `Snoop` logs hardware violations, developers can combine `Snoop=1`, `Expert=1`, and `DebugKey` to trigger a total system dump. Pressing the configured `DebugKey` forces WHDLoad to write the entire Walled Garden memory state, CPU registers, and custom chip states to disk.
+
+Developers use the included **SP (Save Picture)** tool to extract raw framebuffer images directly from these dump files by parsing the captured copperlists, which is invaluable for identifying exactly when and where a game crashes or hangs during display routines.
+
+### 6.2 System Monitors & Freezer Integration
+WHDLoad directly supports specific software freezers (like HRTmon and ThrillKill). When WHDLoad detects a supported freezer in memory during startup, it modifies its MMU setup to declare the monitor's memory as valid and WriteThrough cacheable.
+It forwards all NMI (Non-Maskable Interrupts) to the monitor's vector table. If the VBR (Vector Base Register) is moved, WHDLoad compares the `FreezeKey` at each Level 7 interrupt, transforming the stackframe into an NMI stackframe to safely drop the user into the debugger without disrupting the host OS.
+
+### 6.3 Memory Protection API & Checksum Defeat
+Some games implement anti-tampering checksums that scan memory for modifications to the trackloader. Reverse engineers can easily defeat these checks using WHDLoad's `resload_ProtectRead` and `resload_ProtectWrite` APIs.
+
+By declaring the 4KB memory page containing the modified code as protected, WHDLoad modifies the page descriptors in the MMU translation tree. Any subsequent access to that page by the game's protection routine will instantly trigger an Access Fault exception. WHDLoad's exception handler evaluates the access; if it matches the specific patched bytes, it halts execution and drops the developer exactly at the checksum routine's Program Counter (PC), completely exposing the DRM mechanism.
+
+---
+
+## 7. Publishing the Container
+
+Once the Imager Slave and Game Slave are complete, the developer packages the release. A standard WHDLoad container looks like this:
+
+* `Game.slave`: The compiled Game Slave binary.
+* `Disk.1`, `Disk.2`: The disk images ripped by RawDIC.
+* `Game.info`: The AmigaOS icon file containing WHDLoad Tooltypes (e.g., `Preload=1`, `QuitKey=$59`).
+* `ReadMe`: Documentation detailing what protections were removed, what hardware is required, and who wrote the Slave.
+
+**Integrity Checking:** The Slave contains a hardcoded CRC16 or MD5 hash of the original unmodified disk images. When WHDLoad launches, it hashes the `Disk.1` file. If the user tries to use a corrupted dump or an improperly cracked ADF file downloaded from the internet, WHDLoad will throw an integrity error, ensuring that the Slave is only patching the exact bytes it was programmed for.
+
+---
+
+## 8. Development Resources & SDK
+
+If you want to create your own WHDLoad installs, the official tools are freely available:
+
+### 7.1 Acquiring the DevKit
+You must download the **DEV Package** (not the USR package) from the official [WHDLoad Homepage](http://whdload.de/). The USR package only contains the runtime tools for end-users.
+* Look for `WHDLoad_dev.lha` or the versioned archive (e.g., `WHDLoad_20.0_dev.lzx`).
+
+### 7.2 What's in the SDK?
+The DEV package is the definitive toolkit for reverse engineers:
+* **`Include/`**: Contains the critical assembly macros (`rawdic.i` for Imager Slaves, `resload.i` for the host API, and `kickemu.i` for OS faking).
+* **`Src/`**: Dozens of open-source Game Slaves and Imager Slaves that act as reference examples.
+* **`Autodoc/`**: The detailed API reference for every `resload_` function (e.g., `resload_DiskLoad`, `resload_FlushCache`).
+* **CLI Utilities**: Additional command-line tools for low-level patching and analysis:
+ * **Patcher**: A generic binary patcher to apply standard crack patches.
+ * **Reloc**: A tool to handle and relocate standard AmigaDOS executables within the Walled Garden.
+ * **Fa / Ibb / Itd**: Utilities for file analysis and track-disk debugging.
+
+### 7.3 Official Documentation
+The complete HTML documentation for WHDLoad development is available online at [http://whdload.de/docs/WHDLoad.html](http://whdload.de/docs/WHDLoad.html). It includes:
+* Memory map specifications for the Walled Garden.
+* The exact calling conventions for the `resload` API.
+* Detailed guides on configuring `Snoop` mode and using KickEmu.
+
+### 7.4 How to Use It
+1. Extract the `WHDLoad_dev.lha` archive to your Amiga hard drive (or cross-compilation environment).
+2. Copy the `Include/` files to your assembler's standard include directory.
+3. Use a 68k macro assembler (like **VASM**, **PhxAss**, or **Barfly**) to compile your `.asm` code.
+4. To build an Imager Slave, include `rawdic.i` and compile. To build a Game Slave, include `resload.i` and compile.
diff --git a/05_reversing/ghidra_setup.md b/05_reversing/ghidra_setup.md
index bbf128a..c5ef5c0 100644
--- a/05_reversing/ghidra_setup.md
+++ b/05_reversing/ghidra_setup.md
@@ -72,6 +72,25 @@ For dynamic debugging, the workflow is identical to IDA:
---
+## Step 6: GCC Binary Specific Workflows
+
+When dealing with GCC-compiled Amiga binaries (especially those with debug info), there are a few Ghidra-specific workflows to note:
+
+**1. Install `ghidra-gcc2-stabs`** (`RidgeX/ghidra-gcc2-stabs`) if the binary has debug info. After loading:
+- Run the script: `Analysis → Run Script → ImportGCC2Stabs.java`
+- The script reads `HUNK_DEBUG`, extracts `N_FUN`/`N_SLINE`/`N_LSYM` stabs, and creates function labels, source line annotations, and local variable names automatically.
+- Even partial stabs (e.g., `N_SO` + `N_FUN` only) restore function boundaries and names.
+
+**2. PC-relative string handling.** Ghidra's m68k analyzer natively handles `LEA xxx(PC), An` correctly and creates data cross-references. Check the `References` view for `LEA` targets — strings listed there can be viewed and renamed.
+
+**3. Function boundary heuristic.** Ghidra's default analysis finds GCC functions reasonably well. For missed functions:
+- Use `Search → For Instruction Patterns` → `MOVEM.L *, -(SP)` (opcode `48E7`) to find all prologues.
+- Right-click → `Create Function` at each found address.
+
+**4. Recognizing tail calls.** Ghidra may misidentify `BRA _otherFunc` as a local branch. If Ghidra marks code after a `BRA` as unreachable or creates a new function at the `BRA` target, verify manually: if the `BRA` target is a named function elsewhere in `.text`, it's a tail call — the `BRA` terminates the current function and the target function returns directly to the original caller.
+
+---
+
## References
- [ghidra-amiga by BartmanAbyss](https://github.com/BartmanAbyss/ghidra-amiga) — The definitive Amiga loader and extension suite for Ghidra.
diff --git a/05_reversing/ida_setup.md b/05_reversing/ida_setup.md
index 56fa369..f8f2ebb 100644
--- a/05_reversing/ida_setup.md
+++ b/05_reversing/ida_setup.md
@@ -217,6 +217,28 @@ If C pseudocode generation is a strict requirement for your workflow, you must u
---
+## Step 12: GCC Binary Specific Workflows
+
+When analyzing a binary compiled with GCC (often identified by a `.text` hunk or a `LINK A6` in the first function), the standard analysis workflow changes slightly:
+
+**1. Handle `.text` as mixed code+data.** GCC embeds strings and jump tables directly in the code hunk. After auto-analysis:
+- Search for `LEA xxx(PC), An` instructions (Edit → Find → by instruction mnemonic or IDAPython)
+- For each, check if the target address contains ASCII bytes — if yes, press `A` to define as string
+- Mark the string as `DATA` type so IDA doesn't try to disassemble it as code
+
+**2. Function boundary detection without LINK.** IDA's auto-analysis finds most functions via call-graph tracing from the entry point. For stragglers:
+- Every `BSR addr` / `JSR addr` target is a function entry — use `Create function` (P key) at those addresses
+- Look for `MOVEM.L Dn/An, -(SP)` at addresses following a `RTS` — strong function-start indicator
+- Use IDAPython to scan: `for ea in idautils.Heads(): if idc.print_insn_mnem(ea) == 'MOVEM.L': ...`
+
+**3. Identify `main()` in stripped builds.** The libnix startup sequence is fixed:
+```
+Entry → MOVEA.L 4.W, A6 → JSR __startup_SysBase → (open dos.library) → JSR _main
+```
+The `JSR` immediately after the `dos.library` open is `_main`. Mark it as a function and rename.
+
+---
+
## References
- IDA Pro 7.x documentation — processor modules, FLIRT
diff --git a/05_reversing/static/README.md b/05_reversing/static/README.md
new file mode 100644
index 0000000..f62ce7e
--- /dev/null
+++ b/05_reversing/static/README.md
@@ -0,0 +1,22 @@
+[← Home](../../README.md) · [Reverse Engineering](../README.md)
+
+# Static Analysis & Binary Archaeology
+
+## Contents
+
+### Fundamentals
+- [hunk_reconstruction.md](hunk_reconstruction.md) — Understanding the Amiga HUNK structure in disassemblers
+- [code_vs_data_disambiguation.md](code_vs_data_disambiguation.md) — Distinguishing instructions from data blocks
+- [m68k_codegen_patterns.md](m68k_codegen_patterns.md) — Common 68k assembly sequences and optimizations
+
+### API & Data Analysis
+- [library_jmp_table.md](library_jmp_table.md) — Reconstructing library jump tables and LVOS
+- [api_call_identification.md](api_call_identification.md) — Identifying system calls in naked disassembly
+- [string_xref_analysis.md](string_xref_analysis.md) — Using strings to anchor functional analysis
+- [struct_recovery.md](struct_recovery.md) — Identifying AmigaOS structures in memory
+
+### Language-Specific RE
+- [asm68k_binaries.md](asm68k_binaries.md) — Hand-written 68k assembly (Demos, Bootblocks)
+- [ansi_c_reversing.md](ansi_c_reversing.md) — Recovering C code logic
+- [cpp_vtables_reversing.md](cpp_vtables_reversing.md) — C++ Objects and VTables
+- [other_languages.md](other_languages.md) — AMOS, Blitz Basic, Amiga E, and more
diff --git a/05_reversing/static/compilers/gcc.md b/05_reversing/static/compilers/gcc.md
index 6b3c6af..3d95bc4 100644
--- a/05_reversing/static/compilers/gcc.md
+++ b/05_reversing/static/compilers/gcc.md
@@ -116,6 +116,61 @@ _large_func:
---
+## Debug Information — HUNK_DEBUG and Stabs
+
+GCC 2.95.x for AmigaOS embeds debug info when compiled with `-g`. The format is **stabs** (BSD DBX format) — not DWARF2, which is disabled on this target. Debug data lives in a `HUNK_DEBUG` block (hunk type `0x3F1`) separate from `HUNK_SYMBOL`.
+
+### Hunk Types for Symbols
+
+| Hunk type | Hex | Contents |
+|---|---|---|
+| `HUNK_SYMBOL` | `0x3F0` | Linker-visible public symbol names + offsets. Present in non-stripped builds. |
+| `HUNK_DEBUG` | `0x3F1` | Stabs debug info: source file names, function names, line numbers, type info. Only with `-g`. |
+
+`HUNK_DEBUG` structure:
+```
+ULONG magic; // BSD a.out magic (checked against ZMAGIC)
+ULONG symsz; // size of symbol table (N × sizeof(struct nlist))
+ULONG strsz; // size of string table
+struct nlist[N]; // stabs entries
+char strings[]; // null-terminated string pool
+```
+
+### Key Stabs Entry Types
+
+| Stab type | Decimal | Meaning |
+|---|---|---|
+| `N_OPT` | 60 | Compiler option — value `"gcc2_compiled."` marks GCC 2.x output |
+| `N_SO` | 100 | Source file. Two consecutive `N_SO` entries = directory + filename |
+| `N_SOL` | 132 | Included sub-source file (`#include`) |
+| `N_FUN` | 36 | Function entry: `"name:Fdesc"` (global) or `"name:fdesc"` (static); `n_value` = start address. Empty name `""` marks function *end*. |
+| `N_SLINE` | 68 | Source line: `n_desc` = line number, `n_value` = code offset from function start |
+| `N_LSYM` | 128 | Local variable (stack): `"name:type"`, `n_value` = frame offset |
+| `N_GSYM` | 32 | Global variable |
+| `N_LBRAC` / `N_RBRAC` | 192 / 224 | Open/close scope block |
+
+### Finding Function Names in a Debug Build
+
+1. Locate `HUNK_DEBUG` (type `0x3F1`) in the binary
+2. Read the BSD header; verify magic
+3. Iterate `struct nlist` entries, looking for `n_type == N_FUN`
+4. The string before the `:` in the stabs string is the function name
+5. `n_value` is the function's start offset within the hunk
+6. The next `N_FUN` with empty name marks the function's end
+
+**Tooling:**
+- `GccFindHit` (from `cnvogelg/m68k-amigaos-toolchain`) — reads HUNK_DEBUG and maps a crash address to source file + line + function
+- `ghidra-gcc2-stabs` (GitHub: `RidgeX/ghidra-gcc2-stabs`) — Ghidra plugin that imports stabs from `HUNK_DEBUG` and creates function labels, line numbers, and local variable annotations automatically
+
+### `gcc2_compiled.` Marker
+
+An `N_OPT` stab with string `"gcc2_compiled."` appears before the first `N_SO` entry in every GCC 2.x debug build. It:
+- Confirms GCC 2.x lineage (not SAS/C, VBCC, or GCC 3+)
+- Is only present when `-g` was passed — absent in release/stripped builds
+- In stripped binaries, use hunk naming (`.text`) and code patterns instead
+
+---
+
## Calling Conventions
GCC uses a simpler calling convention model than SAS/C — one primary convention with variations controlled by function attributes. However, what GCC lacks in convention count it makes up for in **register allocation flexibility**: every function gets a customized stack frame and register save set based on exactly which variables the compiler decides to keep in registers.
@@ -347,6 +402,31 @@ GCC's call-site code reveals whether the caller passes parameters in registers o
> [!NOTE]
> **Varargs functions** (like `Printf`, `sprintf`, custom `Format()`) force ALL arguments onto the stack in GCC 2.95.x — even the first two. This is a reliable disambiguator: if you see a call with 3+ stack pushes and NO register args, the target is likely a varargs function.
+#### Varargs Callee — `va_arg()` Expansion
+
+Inside a varargs function, `va_list` is a plain `char *` pointer into the stack frame. `va_start` initializes it; `va_arg(ap, T)` reads the next argument and advances by `sizeof(T)` rounded to 4 bytes.
+
+```asm
+; va_arg() for a 32-bit value — canonical pattern:
+ MOVEA.L -$04(A6), A0 ; load va_list (ap) from stack slot
+ MOVE.L (A0)+, D0 ; read next arg, advance ap by 4
+ MOVE.L A0, -$04(A6) ; write back updated ap
+
+; va_arg() inside a loop (ap kept in address register):
+.va_loop:
+ MOVE.L (A2)+, D0 ; A2 = ap; read 32-bit arg, A2 += 4
+ TST.L D0
+ BEQ.S .va_done
+ ; process D0 ...
+ BRA.S .va_loop
+```
+
+**Key recognition patterns:**
+- `(An)+` post-increment reads in a loop — the defining mark of `va_arg` iteration
+- `ap` is either kept in an address register across the loop or reloaded/stored each iteration
+- 16-bit types (`short`) are promoted to 32 bits on the stack — `va_arg` still advances by 4, not 2
+- Format strings for `printf`-style calls always appear as `LEA .LCx(PC), Dn` followed by `MOVE.L Dn, -(SP)` (the format string is the first stack push, last to arrive at the function)
+
### `__attribute__((interrupt))` — Interrupt Handler
```asm
@@ -369,6 +449,76 @@ _exit_func:
; May be followed by ILLEGAL or DC.B 0 padding
```
+### AmigaOS-Specific GCC Attributes
+
+GCC 2.95.x for AmigaOS defines attribute macros that map to Amiga calling conventions. These produce fundamentally different code and must be recognized to correctly reconstruct function prototypes.
+
+#### `__regargs` — Register-Based Argument Passing
+
+`__attribute__((regparm(N)))` (available as the `__regargs` macro) passes the first N arguments in registers using a different layout than standard cdecl:
+
+| Arg # | Type | Standard cdecl | `__regargs` |
+|---|---|---|---|
+| **arg1** | integer | D0 | D0 |
+| **arg1** | pointer | D0 | **A0** |
+| **arg2** | integer | D1 | D1 |
+| **arg2** | pointer | D1 | **A1** |
+| **arg3** | any | stack | D2 or A2 |
+| **remaining** | any | stack | stack |
+
+The critical difference: **pointer arguments arrive in address registers (A0, A1), not D0/D1**. If you assume cdecl and a function's first parameter is a pointer, you will look for it in `D0` — but with `__regargs` it arrives in `A0`.
+
+```asm
+; Standard cdecl: Write(fh, buf, len) — fh/buf/len in D0/D1/stack
+ MOVE.L #1024, -(SP) ; len on stack
+ MOVE.L buffer, D1 ; buf in D1 (integer-sized)
+ MOVE.L fh, D0 ; fh in D0
+ BSR _Write
+
+; __regargs: WriteEx(fh, buf, len) — fh(int) in D0, buf(ptr) in A0, len in D1
+ MOVE.L #1024, D1 ; len in D1
+ MOVEA.L buffer, A0 ; buf (pointer) → A0, not D1!
+ MOVE.L fh, D0 ; fh in D0
+ BSR _WriteEx
+```
+
+Callee with `__regargs` — how the prologue differs:
+
+```asm
+_WriteEx: ; __regargs: D0=fh, A0=buf(ptr), D1=len
+ MOVEM.L D2-D3/A2, -(SP)
+ MOVE.L D0, D2 ; save fh (from D0 — same as cdecl)
+ MOVEA.L A0, A2 ; save buf from A0 — NOT from D1!
+ MOVE.L D1, D3 ; save len from D1
+```
+
+**RE trap**: If you assume cdecl and see `MOVEA.L A0, A2` early in the prologue with no preceding `MOVEA.L D0, A2`, the function is `__regargs` and the first pointer arg arrived directly in A0.
+
+#### `__saveds` — Small-Data Register Reload
+
+`__attribute__((saveds))` forces the function to reload the small-data base register (A4) at entry from `__DATA_BAS`. Used for library functions callable from a different task context. Recognizable by `LEA __DATA_BAS(PC), A4` as the very first instruction before any other work:
+
+```asm
+_saveds_func:
+ LEA __DATA_BAS(PC), A4 ; reload small-data base — __saveds signature
+ MOVEM.L D2/A2, -(SP)
+ ; ... normal function body follows ...
+```
+
+On most AmigaOS GCC builds without `-msep-data`, `__saveds` is a no-op in the generated code — present in source for SAS/C compatibility, invisible in the binary.
+
+#### `__chip` — Chip RAM Variable Placement
+
+Variables declared `__attribute__((chip))` land in a `.datachip` section. The linker emits this as a separate `HUNK_DATA` block with chip-RAM flag bits set in the hunk size longword (bits 30–31 encode `MEMF_CHIP`):
+
+```
+HUNK_DATA 0x3EA size=0x80000040 ; bit 30 set = MEMF_CHIP requested
+ ; ...chip-RAM variable data...
+HUNK_END
+```
+
+Accesses to chip-RAM variables in disassembly look identical to normal `.data` accesses — the `MEMF_CHIP` flag is only visible in the hunk header, not in the instructions.
+
---
## Library Call Patterns
@@ -409,6 +559,109 @@ When `-fPIC` is enabled, globals are accessed through a GOT (Global Offset Table
---
+## BOOPSI / MUI Dispatcher Pattern
+
+BOOPSI and MUI custom class dispatchers compiled with GCC require special recognition because the OS invokes them with a **non-GCC calling convention**: arguments arrive in specific m68k registers hardcoded by the OS ABI, not in D0/D1/stack.
+
+### OS Entry Convention vs GCC Normal
+
+| Register | OS-mandated meaning | GCC normal cdecl meaning |
+|---|---|---|
+| **A0** | `IClass *cl` — the class pointer | arg1 (if pointer, with `__regargs`) |
+| **A1** | `Msg msg` — message (first field = MethodID) | arg2 (if pointer, with `__regargs`) |
+| **A2** | `Object *obj` — the object being operated on | callee-saved (must be preserved) |
+
+GCC-compiled dispatchers always begin by saving A2 and remapping all three inputs to callee-saved registers before any dispatch logic:
+
+```asm
+_MyClass_Dispatcher:
+ ; Entered with: A0=class, A1=msg, A2=obj (OS convention)
+ MOVEM.L D2-D3/A2-A4, -(SP) ; save callee-saved regs (A2 saved here!)
+ MOVEA.L A0, A3 ; A3 = cl (callee-saved)
+ MOVEA.L A2, A4 ; A4 = obj (A2 clobbered next, save first)
+ MOVEA.L A1, A2 ; A2 = msg (now A2 holds msg, not obj)
+
+ MOVE.L (A2), D2 ; D2 = msg->MethodID (first field)
+```
+
+### MethodID Dispatch — CMP Chain vs Jump Table
+
+For fewer than ~8 methods, GCC emits a linear comparison chain:
+
+```asm
+ CMPI.L #$0101, D2 ; OM_NEW?
+ BEQ .om_new
+ CMPI.L #$0102, D2 ; OM_DISPOSE?
+ BEQ .om_dispose
+ CMPI.L #$0103, D2 ; OM_SET?
+ BEQ .om_set
+ CMPI.L #$0104, D2 ; OM_GET?
+ BEQ.S .om_get
+ ; ... more methods ...
+ ; Default: forward to superclass
+ MOVEA.L A3, A0 ; restore cl
+ MOVEA.L A4, A2 ; restore obj
+ ; A1 still = msg (or reload from A2 if clobbered)
+ JMP (_IDoSuperMethodA).L ; tail-call superclass dispatcher
+```
+
+For dense MethodID ranges with 8+ methods, GCC may emit a jump table. The MethodID base is subtracted, range-checked, then used as a scaled index:
+
+```asm
+ MOVE.L D2, D0
+ SUB.L #$0101, D0 ; normalize MethodID to 0-based index
+ CMPI.L #, D0
+ BHI.S .default_handler ; out of range → superclass
+ ADD.L D0, D0 ; scale by 2 (word offsets)
+ MOVE.W .method_table(PC,D0.L), D1
+ JMP .method_table(PC,D1.W) ; indirect branch through table
+.method_table:
+ DC.W .om_new-.method_table
+ DC.W .om_dispose-.method_table
+ ; ...
+```
+
+### Common MUI Method IDs
+
+These appear in `CMPI.L #$XXXXXXXX, D2` comparisons in MUI class dispatchers:
+
+| MethodID | BOOPSI/MUI Method | Typical handler action |
+|---|---|---|
+| `0x0101` | `OM_NEW` | Allocate instance data, call superclass OM_NEW |
+| `0x0102` | `OM_DISPOSE` | Free resources, call superclass OM_DISPOSE |
+| `0x0103` | `OM_SET` | Apply attribute list from msg |
+| `0x0104` | `OM_GET` | Return attribute value |
+| `0x80420006` | `MUIM_Draw` | Render the gadget |
+| `0x8042000D` | `MUIM_Cleanup` | Release render resources |
+| `0x80420012` | `MUIM_Setup` | Prepare for rendering |
+
+Custom class methods use MUI-registered IDs starting at `0x80420000 + offset`. If you see a large hex constant as a CMPI operand starting with `0x8042`, it's a custom MUI method.
+
+### `MakeClass()` / `MUI_CreateCustomClass()` Call Pattern
+
+Class initialization code (often in a global constructor or `LibInit`):
+
+```asm
+; MUI_CreateCustomClass(NULL, superclass_name, NULL, inst_size, dispatcher):
+ PEA _MyClass_Dispatcher ; dispatcher function pointer
+ MOVE.L #, -(SP)
+ MOVE.L #0, -(SP) ; taglist (NULL)
+ PEA .superclass_str(PC) ; "Group.mui" etc.
+ MOVE.L #0, -(SP) ; base (NULL for public classes)
+ JSR _MUI_CreateCustomClass
+ LEA $14(SP), SP ; clean 5 args × 4 bytes
+ MOVE.L D0, (_MyClass).L ; store IClass * globally
+```
+
+**RE checklist for dispatcher identification:**
+1. Function entered with no MOVEM of D0/D1 first — instead A0, A1, A2 are immediately remapped
+2. First read is `MOVE.L (A2), Dn` or `MOVE.L (A1), Dn` — loading MethodID
+3. A chain of `CMPI.L #$0101`…`#$0104` or larger hex values
+4. At least one path ends with `JMP (_IDoSuperMethodA).L` or `BSR _DoSuperMethod`
+5. Nearby global holds the result of `MUI_CreateCustomClass`/`MakeClass`
+
+---
+
## C++ Support — What It Means for RE
### Global Constructors and Destructors
@@ -459,6 +712,59 @@ See [cpp_vtables_reversing.md](../cpp_vtables_reversing.md) for the complete GCC
- `offset_to_top` at `vtable[-2]`
- C++ name mangling follows GCC 2.95 conventions (different from StormC++)
+### C++ Exception Handling — SJLJ Mechanism
+
+GCC 2.95.x on AmigaOS uses **SJLJ (setjmp/longjmp) exception handling**. Zero-cost DWARF2 unwinding is explicitly disabled (`DWARF2_UNWIND_INFO 0` in `amigaos.h`) because AmigaOS has no OS-level stack unwinder.
+
+Every function containing a `try` block gets an exception frame registered on a thread-local EH stack at entry and deregistered at exit:
+
+```asm
+; try-block function prologue — SJLJ EH:
+_func_with_try:
+ LINK A6, #- ; allocate ExceptionFrame on stack
+ ; ExceptionFrame layout: {jmp_buf[6], *prev_frame, *exception_type, *handler}
+ MOVEM.L D2/A2, -(SP)
+
+ LEA -(A6), A0 ; A0 = &ExceptionFrame
+ JSR ___sjljeh_init_handler ; push frame onto __sjlj_eh_stack
+
+ JSR _setjmp ; setjmp into frame's jmp_buf
+ TST.L D0
+ BEQ.S .normal_path ; D0=0: initial entry, execute try body
+ ; D0≠0: returning from longjmp — exception in flight
+ BRA .catch_handler
+
+.normal_path:
+ ; ... try block body ...
+
+.function_exit:
+ JSR ___sjljeh_remove_handler ; pop frame from __sjlj_eh_stack
+ MOVEM.L (SP)+, D2/A2
+ UNLK A6
+ RTS
+
+.catch_handler:
+ ; ... catch block body ...
+ BRA.S .function_exit
+```
+
+**RE identification of try/catch blocks:**
+- `JSR ___sjljeh_init_handler` — always marks the start of a try region
+- `JSR _setjmp` followed immediately by `TST.L D0` / `BEQ` — the try/catch branch
+- `JSR ___sjljeh_remove_handler` — always paired with init, marks the end of the guarded region
+- Functions without exceptions have neither; the overhead is obvious (30+ extra instructions)
+
+**Throw site pattern:**
+```asm
+; throw SomeException():
+ ; allocate exception object (or use static)
+ MOVE.L D0, _current_exception ; store exception pointer globally
+ JSR ___sjljeh_throw ; unwind: calls longjmp on innermost frame
+ ; unreachable (noreturn)
+```
+
+If you see `___sjljeh_throw` in a function, that function throws an exception. If you see `___sjljeh_init_handler` + `_setjmp`, it catches one.
+
---
## Optimization Level Fingerprints
@@ -717,6 +1023,20 @@ The A6 frame pointer choice (rather than A5) comes from the System V m68k ABI, w
---
+## Practical RE Workflow — Stripped Binary Analysis
+
+Quick steps when you have a stripped GCC Amiga binary with zero symbols:
+
+1. **Confirm compiler**: `.text` hunk name? → GCC. `LINK A5` in first function? → SAS/C. `LINK A6`? → GCC with frame pointer.
+2. **Find entry**: `MOVEA.L 4.W, A6` near start of `.text` → libnix entry. Follow to `JSR _main`.
+3. **Apply FLIRT**: identify C runtime, `dos.library` stubs, math functions — this names 20–40% of functions immediately.
+4. **Trace call graph from `main`**: rename each function as you understand it; GCC's per-function register save sets help scope the function boundary.
+5. **Look for `__CTOR_LIST__`**: if present, trace it before `main` — global constructors may initialize important state.
+6. **Check for `__regargs`**: if a function's first pointer arg seemingly doesn't use D0/D1, check if it arrives in A0 (regparm convention).
+7. **BOOPSI/MUI class?**: look for `MakeClass`/`MUI_CreateCustomClass` and trace to the dispatcher function.
+
+---
+
## FAQ
**Q: How do I tell GCC 2.95.x from GCC 6.x (bebbo) in a binary?**
@@ -738,4 +1058,14 @@ A: Search for libnix startup signature: `MOVE.L 4.W, A6` / `JSR ___startup_SysBa
- [startup_code.md](../../../04_linking_and_libraries/startup_code.md) — libnix/clib2 startup internals
- *bebbo's amiga-gcc*: https://codeberg.org/bebbo/amiga-gcc
- *GeekGadgets*: GCC 2.95 for AmigaOS (archived documentation)
+- *adtools/amigaos-gcc-2.95.3 — amigaos.h*: https://github.com/adtools/amigaos-gcc-2.95.3/blob/master/gcc/config/m68k/amigaos.h — calling convention, attributes, EH config
+- *RidgeX/ghidra-gcc2-stabs*: https://github.com/RidgeX/ghidra-gcc2-stabs — Ghidra plugin for stabs import
+- *BartmanAbyss/ghidra-amiga*: https://github.com/BartmanAbyss/ghidra-amiga — Ghidra hunk loader + AmigaOS types
+- *cnvogelg/GccFindHit* (stabs parser): https://github.com/cnvogelg/m68k-amigaos-toolchain/blob/master/tools/GccFindHit.c
+- *cahirwpz — AmigaOS GCC regparm*: http://cahirwpz.users.sourceforge.net/gcc-amigaos/regparm.html
+- *Tetracorp — Reverse Engineering Amiga*: https://tetracorp.github.io/guide/reverse-engineering-amiga.html
+- *GCC 2.95 caveats*: https://gcc.gnu.org/gcc-2.95/caveats.html
+- *STABS format reference*: https://sourceware.org/gdb/onlinedocs/stabs.html
+- *SDI_hook.h (BOOPSI/MUI dispatcher macros)*: https://github.com/amiga-mui/betterstring/blob/master/include/SDI_hook.h
+- *BOOPSI documentation*: https://wiki.amigaos.net/wiki/BOOPSI_-_Object_Oriented_Intuition
- See also: [sasc.md](sasc.md), [vbcc.md](vbcc.md) — compare with other compilers
diff --git a/08_graphics/README.md b/08_graphics/README.md
index 596e914..5261763 100644
--- a/08_graphics/README.md
+++ b/08_graphics/README.md
@@ -22,3 +22,4 @@ The Amiga graphics system is built on custom DMA-driven hardware (Agnus/Alice +
| [text_fonts.md](text_fonts.md) | TextFont bitmap layout, baseline rendering, algorithmic styles, AvailFonts enumeration |
| [pixel_conversion.md](pixel_conversion.md) | Chunky ↔ Planar conversion deep dive: naive, merge/butterfly (Kalms), Copper Chunky, Akiko hardware, Blitter-assisted, RTG bypass, SoA/AoS theory, GPU swizzle modern parallels |
| [animation.md](animation.md) | GEL system deep dive: BOBs, VSprites, AnimObs, hardware foundation (Blitter/Copper/Sprite interaction), collision detection, double buffering, performance tuning |
+| [rtg_programming.md](rtg_programming.md) | Retargetable Graphics (CyberGraphX/Picasso96): Planar vs Chunky, LockBitMapTags, Pixel Formats, Direct VRAM rendering |
diff --git a/08_graphics/rtg_programming.md b/08_graphics/rtg_programming.md
new file mode 100644
index 0000000..fec2df3
--- /dev/null
+++ b/08_graphics/rtg_programming.md
@@ -0,0 +1,439 @@
+[← Home](../README.md) · [Graphics](README.md)
+
+# RTG (Retargetable Graphics) Programming — Chunky Framebuffers and Hardware Abstraction
+
+Retargetable Graphics (RTG) is an abstraction layer that decouples Amiga applications from the platform's native planar custom chipset (OCS/ECS/AGA), enabling transparent support for modern chunky-pixel graphics hardware connected via Zorro or PCI expansion buses. It lives as a set of library extensions (Picasso96 or CyberGraphX) to the standard AmigaOS `graphics.library`, solving the performance bottleneck of planar memory layouts for 3D rendering and high-resolution video. Understanding the interaction between CPU-side chunky framebuffers and the hardware expansion bus bandwidth is the critical constraint for any high-performance RTG software.
+
+---
+
+## 1. Architecture: The RTG Lifecycle
+
+The core design of RTG moves the Amiga from **Planar** (bitplanes scattered in Chip RAM) to **Chunky** (linear pixels in VRAM). Because VRAM resides on a peripheral bus, the OS must arbitrate access between the CPU and the graphics card's internal blitter.
+
+### 1.1 Framebuffer Lifecycle
+```mermaid
+sequenceDiagram
+ participant App as Application
+ participant OS as graphics.library
+ participant HW as RTG Hardware (VRAM)
+
+ Note over App, HW: Application decides to update the screen
+ App->>OS: LockBitMapTags(bitmap, LBMI_BASEADDRESS, ...)
+ OS->>OS: Suspend screen dragging / VRAM relocation
+ OS-->>App: Returns vram_ptr, pitch, format
+
+ loop Per Scanline
+ App->>HW: Direct CPU writes to chunky pixels
+ end
+
+ App->>OS: UnLockBitMap(lock)
+ OS->>OS: Resume OS background operations
+ Note over App, HW: Frame is now visible and stable
+```
+
+### 1.2 Rendering Decision Matrix
+When deciding between using OS-native draw functions or direct VRAM access, use this logic:
+
+```mermaid
+graph TD
+ Start[Need to update graphics?] --> Q1{Is it a simple rect fill/copy?}
+ Q1 -- Yes --> Q2{Is BMF_HARDWARE set?}
+ Q2 -- Yes --> UseHW[Use BltBitMap / RectFill
'Stays in VRAM']
+ Q2 -- No --> UseCPU
+ Q1 -- No --> UseCPU[LockBitMapTags
'CPU pushes data over bus']
+
+ style UseHW fill:#e8f4fd,stroke:#2196f3
+ style UseCPU fill:#fff9c4,stroke:#f9a825
+```
+
+---
+
+## 2. The Planar vs. Chunky Paradigm
+
+* **Planar (Custom Chipset)**: To draw a single 8-bit (256 color) pixel, the CPU or Blitter must write 1 bit to 8 separate areas of memory. This is computationally expensive for 3D texture mapping or unscaled sprites.
+* **Chunky (RTG)**: To draw a single 8-bit pixel, the CPU writes 1 byte to 1 memory address. To draw a 32-bit pixel, the CPU writes 1 longword (4 bytes).
+
+> **Deep Dive**: For a comprehensive architectural breakdown of the Chunky-to-Planar conversion problem (including the Kalms algorithm, Akiko hardware, and Blitter-assisted conversion), refer to our dedicated [Chunky ↔ Planar Pixel Conversion](pixel_conversion.md) article.
+
+---
+
+
+## 3. Requesting an RTG ScreenMode
+
+The standard way to open an RTG screen is to ask the user what resolution they want using `asl.library` (`screenmode.req`).
+
+```c
+#include
+#include
+
+struct ScreenModeRequester *req;
+ULONG displayID = INVALID_ID;
+
+req = AllocAslRequest(ASL_ScreenModeRequest, NULL);
+if (req) {
+ // Filter for Chunky, TrueColor modes (e.g., >= 16-bit depth)
+ if (AslRequestTags(req,
+ ASLSM_MinDepth, 16,
+ ASLSM_PropertyMask, DIPF_IS_RTG,
+ TAG_DONE)) {
+ displayID = req->sm_DisplayID;
+ }
+ FreeAslRequest(req);
+}
+```
+
+Once you have the `displayID`, you can open an OS-friendly screen:
+
+```c
+struct Screen *rtgScreen = OpenScreenTags(NULL,
+ SA_DisplayID, displayID,
+ SA_Title, "My RTG Application",
+ SA_Depth, 32,
+ TAG_DONE);
+```
+
+---
+
+## 4. Direct VRAM Access: `LockBitMapTags`
+
+While AmigaOS provides functions like `WritePixel()` or `BltBitMap()`, these are far too slow for real-time video playback or 3D games. Game developers need a direct pointer to the graphics card's Video RAM (VRAM) to blast pixels via CPU loops.
+
+Starting with `graphics.library` V39, AmigaOS provides `LockBitMapTags()`, which asks the graphics driver to lock the VRAM buffer and return its absolute memory address.
+
+> [!WARNING]
+> **Never hold the lock!** You must call `UnLockBitMap()` as quickly as possible. While a bitmap is locked, the OS cannot move it in VRAM, and multitasking can be heavily degraded on some drivers.
+
+### 3.1 The Framebuffer Lock Workflow
+
+```c
+#include // Or graphics.library V39+ equivalents
+
+APTR vram_base;
+ULONG pitch, format;
+
+// Lock the bitmap belonging to our window's screen
+APTR lock = LockBitMapTags(rtgScreen->RastPort.BitMap,
+ LBMI_BASEADDRESS, (ULONG)&vram_base,
+ LBMI_BYTESPERROW, (ULONG)&pitch,
+ LBMI_PIXFMT, (ULONG)&format,
+ TAG_DONE);
+
+if (lock) {
+ // We now have direct access to VRAM!
+ // Example: Fill screen with red (Assuming 32-bit ARGB format)
+ ULONG *pixel_ptr = (ULONG *)vram_base;
+ for (int y = 0; y < rtgScreen->Height; y++) {
+ for (int x = 0; x < rtgScreen->Width; x++) {
+ pixel_ptr[x] = 0x00FF0000; // Red
+ }
+ // Advance pointer by 'pitch' (bytes), not pixels!
+ pixel_ptr = (ULONG *)((UBYTE *)pixel_ptr + pitch);
+ }
+
+ // Release the lock immediately when done drawing the frame
+ UnLockBitMap(lock);
+}
+```
+
+### 3.2 Understanding Pitch (Modulo)
+Never assume `pitch = Width * BytesPerPixel`. Graphics cards often align rows of VRAM to 16, 32, or 64-byte boundaries for burst-transfer optimization. You must always advance your `y` loop pointer by adding the `pitch` in bytes.
+
+---
+
+## 5. Pixel Formats & Endianness
+
+Because Amiga RTG cards are historically PC VGA cards plugged into Zorro bus adapters, pixel formats can be complex.
+
+When you query `LBMI_PIXFMT` (or use `GetCyberMapAttr()`), you will get one of several formats. Your rendering engine must check this format and output pixels accordingly.
+
+| Format Constant | Depth | Structure | Notes |
+|---|---|---|---|
+| `PIXFMT_LUT8` | 8-bit | Index | 256 colors requiring a palette table. |
+| `PIXFMT_RGB15` | 16-bit | `xRRRRRGG GGGBBBBB` | 5:5:5 format. |
+| `PIXFMT_RGB16` | 16-bit | `RRRRRGGG GGGBBBBB` | 5:6:5 format (Common for fast 3D). |
+| `PIXFMT_RGB24` | 24-bit | `R, G, B` | 3 bytes per pixel (unaligned, slow). |
+| `PIXFMT_ARGB32` | 32-bit | `A, R, G, B` | Alpha is ignored by most cards. |
+| `PIXFMT_BGRA32` | 32-bit | `B, G, R, A` | PC-native Little-Endian format. |
+
+### 5.1 The Endianness Hazard
+The Motorola 68000 is **Big-Endian**. Modern graphics cards are built for x86 (**Little-Endian**). Both Picasso96 and CyberGraphX drivers attempt to hide this by configuring the bridgeboard (like the Prometheus PCI bridge) to byte-swap the data automatically on the hardware bus.
+
+However, older drivers or direct hardware manipulation might expose the Little-Endian VRAM directly. Always rely on the `PIXFMT` returned by the OS rather than hardcoding RGB bitshifts.
+
+---
+
+## 6. Double Buffering in RTG
+
+For tear-free rendering, use standard AmigaOS double-buffering via `AllocScreenBuffer()` and `ChangeScreenBuffer()`.
+
+1. Allocate two `ScreenBuffer` structures.
+2. `LockBitMapTags()` on the *back buffer*.
+3. Draw the frame.
+4. `UnLockBitMap()`.
+5. Call `ChangeScreenBuffer(screen, back_buffer)` to flip the display pointers during the next VBlank.
+6. Swap your logic so the old front buffer becomes the new back buffer.
+
+---
+
+## 7. Vendor & Platform Comparison
+
+Amiga RTG programming shares many concepts with modern desktop APIs, though it lacks the high-level scene graphs or shaders found in modern pipelines.
+
+| Concept | Amiga RTG (P96/CGX) | Modern Equivalent (SDL / DirectX) |
+|---|---|---|
+| **Framebuffer Lock** | `LockBitMapTags()` | `SDL_LockSurface()` / `Map()` |
+| **Pixel Format** | `LBMI_PIXFMT` | `SDL_PixelFormat` / `DXGI_FORMAT` |
+| **Buffer Flip** | `ChangeScreenBuffer()` | `SDL_RenderPresent()` / `Present()` |
+| **VRAM Buffer** | `BitMap` (RTG) | `Texture2D` / `Surface` |
+
+---
+
+## 8. Use-Case Cookbook: Minimal RTG Application
+
+This complete example demonstrates the "Proper Lifecycle" for opening an RTG screen, locking it for direct access, and rendering a simple procedural pattern.
+
+```c
+/* RTG Minimal Renderer - AmigaOS 3.1+ */
+#include
+#include
+#include
+#include
+
+int main() {
+ struct Screen *scr;
+ struct BitMap *bm;
+ APTR lock;
+ UBYTE *vram;
+ ULONG pitch, format;
+
+ // 1. Open an 800x600 32-bit screen (assuming driver exists)
+ scr = OpenScreenTags(NULL,
+ SA_Width, 800,
+ SA_Height, 600,
+ SA_Depth, 32,
+ SA_Title, (ULONG)"RTG Cookbook Example",
+ SA_Type, PUBLICSCREEN,
+ TAG_DONE);
+
+ if (!scr) return 20;
+
+ bm = scr->RastPort.BitMap;
+
+ // 2. Lock the VRAM for direct CPU access
+ lock = LockBitMapTags(bm,
+ LBMI_BASEADDRESS, (ULONG)&vram,
+ LBMI_BYTESPERROW, (ULONG)&pitch,
+ LBMI_PIXFMT, (ULONG)&format,
+ TAG_DONE);
+
+ if (lock) {
+ // 3. Render a blue-to-black gradient
+ for (int y = 0; y < 600; y++) {
+ ULONG *row_ptr = (ULONG *)(vram + (y * pitch));
+ for (int x = 0; x < 800; x++) {
+ row_ptr[x] = (x & 0xFF); // Blue component
+ }
+ }
+
+ // 4. Unlock immediately!
+ UnLockBitMap(lock);
+ }
+
+ // 5. Cleanup
+ Delay(150); // Show for 3 seconds
+ CloseScreen(scr);
+ return 0;
+}
+```
+
+---
+
+## 9. Best Practices & Antipatterns
+
+### Best Practices
+1. **Always use the Pitch**: Never calculate row offsets as `x * BytesPerPixel`. Use the pitch returned by the OS.
+2. **Minimize Lock Time**: Hold the `BitMap` lock only during the actual memory copy/render loop.
+3. **Check PIXFMT**: Verify the return pixel format. A 32-bit mode could be ARGB, BGRA, or RGBA depending on the PCI bridgeboard.
+4. **Prefer Hardware Blits**: If doing a simple rectangular clear, use `RectFill()` instead of a CPU loop to keep data on the card.
+
+### Antipatterns
+
+#### 1. The Infinite Lock
+Holding the VRAM lock across a blocking system call.
+* **The Bug**: Calling `WaitPort()` or `Delay()` while holding a lock returned by `LockBitMapTags()`.
+* **Why it fails**: It prevents the RTG driver from performing background tasks or handling screen depth-arrangements, potentially deadlocking the UI.
+* **The Fix**: Unlock the bitmap before waiting for user input or the next vertical blank.
+
+#### 2. The Implicit Pitch Assumption
+Assuming VRAM is a contiguous, unpadded array.
+* **The Bug**: `pixel_ptr = vram + (y * Width * 4);`
+* **Why it fails**: Graphics cards often align scanlines to 64-byte or 128-byte boundaries for hardware performance.
+* **The Fix**: `pixel_ptr = vram + (y * pitch);`
+
+---
+
+## 10. Pitfalls & Common Mistakes
+
+### 8.1 The "Zorro II Crawl"
+* **Bad Code**: Attempting to render 1024x768 32-bit video over a Zorro II bus using direct CPU writes.
+* **Reality**: Zorro II tops out at ~3.5 MB/s. A single 32-bit 1024x768 frame is 3 MB. You will get **1 FPS**.
+* **Correct Version**: Use 8-bit indexed modes for high resolutions on Zorro II, or restrict 32-bit color to small windows/dirty rectangles.
+
+---
+
+
+## 11. Hardware Constraints & Limitations
+
+While RTG liberates the Amiga from the planar custom chipset, developers must be acutely aware of the hardware bottlenecks introduced by the expansion bus.
+
+### 6.1 Bus Bandwidth (The Ultimate Bottleneck)
+The Amiga's expansion buses were never designed for modern chunky pixel-pushing:
+* **Zorro II (A2000/A500)**: Maximum bandwidth is theoretically ~3.5 MB/s. An 800x600 32-bit frame is ~1.9 MB. If the CPU pushes a full frame over Zorro II, you will achieve fewer than **2 Frames Per Second (FPS)**.
+* **Zorro III (A3000/A4000)**: Maximum bandwidth is ~12–15 MB/s. Still a severe bottleneck for full-screen 32-bit updates.
+* **PCI Bridgeboards (Mediator/Prometheus)**: Significantly faster, but still bottlenecked by the Amiga motherboard's interface to the bridge.
+
+**The Solution:** Do not redraw the whole screen. Use dirty rectangles (only update what changed), or utilize the 2D acceleration features (Blits, Fills) of the graphics card via `graphics.library` so the data stays in VRAM and doesn't cross the Zorro bus.
+
+### 6.2 Hardware VRAM Limits
+Could you create a 4K (3840x2160) framebuffer if you had enough RAM?
+
+In short: **No, not on classic hardware.**
+* A 4K framebuffer at 32-bit color requires **~33.1 MB** of contiguous Video RAM.
+* Classic Amiga RTG cards (Picasso II, CyberVision 64) typically had 2 MB to 4 MB of VRAM.
+* Even the most advanced classic RTG setups (3dfx Voodoo 3 3000 via Mediator PCI) only possessed 16 MB of VRAM.
+
+If `AllocBitMap()` fails to find contiguous VRAM on the graphics card, AmigaOS may fall back to allocating the bitmap in standard Fast RAM. If it does this, rendering happens in standard system RAM, but displaying it requires the OS to blindly copy it to the graphics card every frame, instantly suffocating the Zorro bus.
+
+### 6.3 RTG Card Catalog
+
+The following table catalogs the major RTG cards available for classic Amiga systems. Maximum resolution depends on both VRAM capacity and pixel clock; interlaced modes can push horizontal resolution higher at the cost of flicker.
+
+| Card | Chipset | Bus | VRAM | Max 8-bit | Max 16-bit | Max 24/32-bit | Pixel Clock | Key Features |
+|---|---|---|---|---|---|---|---|---|
+| **Retina** | NCR 77C22E+ | Zorro II | 1–4 MB | 2400×1200 (i) | 1280×1024 | 1024×768 (ni) | 90 MHz | Early high-res; P96 / EGS |
+| **Picasso II** | Cirrus GD5426/28 | Zorro II | 1–2 MB | 1600×1280 (i) | 1152×864 (i) | 800×600 (ni) | 85 MHz | PAL video out; maps into Z2 space |
+| **Picasso II+** | Cirrus GD5428 | Zorro II | 2 MB | 1600×1280 (i) | 1152×864 (i) | 800×600 (ni) | 85 MHz | DPMS support; improved blitter |
+| **Piccolo** | Cirrus GD5426 | Z2/Z3 | 1–2 MB | 1600×1280 (i) | 1152×864 (i) | 800×600 (ni) | 85 MHz | Z2/Z3 autosense; video encoder option |
+| **1600GX** | Weitek 91460 | Zorro III | 2 MB | 1600×1280 (ni) | — | — | 180 MHz | Workstation-grade; X-Windows draw modes |
+| **CyberVision 64** | S3 Trio64 | Zorro III | 2–4 MB | 1600×1200 (ni) | 1280×1024 (ni) | 1024×768 (ni) | 135 MHz | Monitor switcher; Roxxler C2P chip |
+| **CyberVision 64/3D** | S3 ViRGE | Z2/Z3 | 4 MB | 1600×1200 (ni) | 1280×1024 (ni) | 1024×768 (ni) | 135 MHz | Built-in scan doubler; 3D functions |
+| **Picasso IV** | Cirrus GD5446 | Z2/Z3 | 4 MB | 1600×1200 (ni) | 1600×1200 (i) | 1280×1024 (ni) | 135 MHz | Built-in flicker fixer; video in/out; TV tuner option |
+| **Retina BLT Z3** | NCR 77C32BLT | Zorro III | 1–4 MB | 2400×1200 (i) | 1280×1024 (ni) | 1152×864 (ni) | 110 MHz | BLT engine; 15–80 kHz H-sync |
+| **BlizzardVision PPC** | Permedia 2 | A1200 CPU | 8 MB | 1600×1200 (ni) | 1600×1200 (ni) | 1280×1024 (ni) | 230 MHz RAMDAC | 3D acceleration; no draggable screens |
+| **CyberVision PPC** | Permedia 2 | CPU slot | 8 MB | 1600×1200 (ni) | 1600×1200 (ni) | 1280×1024×32 (ni) | 230 MHz RAMDAC | 3D acceleration; no draggable screens |
+| **Voodoo 3 2000/3000** | 3dfx | PCI (Mediator) | 16 MB | 2048×1536 | 2048×1536 | 2048×1536 | 300+ MHz | Full 3D; CGX4 / P96 |
+| **Voodoo 4/5** | 3dfx | PCI (Mediator) | 16–32 MB | 2048×1536 | 2048×1536 | 2048×1536 | 300+ MHz | Voodoo3-compatible mode; larger VRAM |
+| **Radeon 7000/9200** | ATI | PCI (Mediator) | 32–128 MB | 1920×1200+ | 1920×1200+ | 1920×1200+ | 200+ MHz | DVI output; modern P96 drivers |
+| **S3 Virge DX** | S3 | PCI (Mediator) | 4 MB | 1600×1200 | 1600×1200 | 1024×768 | 135 MHz | Budget PCI option |
+
+> **Legend:** `(ni)` = non-interlaced; `(i)` = interlaced. Pixel clock values are maximum dot-clock frequencies; actual achievable refresh rates depend on the specific timing modeline.
+
+### 11.4 OS & Library Constraints
+* **16-bit Coordinate Limits**: Deep inside `graphics.library` and `intuition.library`, dimensions and coordinates are frequently passed as signed 16-bit integers (`WORD`). This imposes a hard mathematical limit of 32,767 x 32,767 for logical bitmap sizes, though physical VRAM limits hit far earlier.
+* **Contiguity**: RTG drivers require *contiguous* blocks of VRAM to allocate a screen. Over time, opening and closing windows can fragment VRAM. If you request an 800x600 double-buffered screen (requiring two large blocks), it might fail due to fragmentation, even if total free VRAM is technically sufficient.
+
+---
+
+## 12. SDK & API Constraints
+
+Programming against RTG is not transparent. Both Picasso96 and CyberGraphX expose extensions to `graphics.library`, but the APIs differ and not all features are available on all hardware.
+
+---
+
+## 13. Bypassing Bus Bottlenecks
+
+For high-performance applications (3D games, video players), the Zorro bus is the primary bottleneck. Developers have historically used several strategies to overcome this.
+
+### 13.1 Local Bus RTG (CPU-Slot)
+High-end accelerators like the **CyberStorm PPC** or **Blizzard PPC** provide a proprietary local bus connector that bypasses Zorro entirely.
+* **The CyberVision PPC / BlizzardVision PPC**: These cards sit directly on the CPU's local bus, achieving bandwidth up to 60-80 MB/s—far exceeding the 10-15 MB/s limit of a typical Zorro III setup.
+* **Programming Impact**: No change to the RTG API; however, the speed of `LockBitMapTags()` based rendering increases dramatically.
+
+### 13.2 PCI Bridgeboards & Peer-to-Peer
+Bridgeboards like the **Mediator PCI** allow using PC-standard PCI cards (Voodoo, Radeon).
+* **Peer-to-Peer DMA**: PCI cards can transfer data to each other without crossing the Amiga's motherboard bus. A PCI TV-Tuner can write directly to a PCI Graphics Card's VRAM.
+* **Hardware Acceleration**: Utilizing the 3D features of the card (via Warp3D or StormMESA) moves the computation to the card, meaning the CPU only sends compact command lists rather than raw pixel data over the Zorro bus.
+
+### 13.3 Software Mitigation: Dirty Rectangles
+If stuck on a Zorro II bus (3.5 MB/s), you must minimize bus traffic.
+* **Technique**: Track exactly which regions of the screen have changed and only `LockBitMapTags()` and update those specific "Dirty Rectangles."
+* **Bad**: Redrawing the whole 640x480 screen every frame.
+* **Good**: Only updating the 32x32 sprite area and the UI text fields that changed.
+
+---
+
+## 14. References
+
+### 14.1 Picasso96 vs CyberGraphX API Surface
+
+| Feature | Picasso96 | CyberGraphX (CGX) |
+|---|---|---|
+| **Primary header** | `picasso96api.h` | `cybergraphics.h` |
+| **Lock function** | `LockBitMapTags()` | `LockBitMapTags()` (compatible) |
+| **Pixel format query** | `GetCyberMapAttr()` | `GetCyberMapAttr()` |
+| **Mode creation** | `p96AllocModeListTags()` | `CGXAllocModeListTags()` |
+| **Hardware blit** | `BltBitMapRastPort()` + flags | `BltBitMapRastPort()` + flags |
+| **VRAM query** | `p96GetBitMapAttr()` | `CGXGetBitMapAttr()` |
+
+> [!IMPORTANT]
+> While `LockBitMapTags()` is API-compatible between P96 and CGX, the tags and return values for pixel format queries differ subtly. Always check `picasso96api.h` or `cybergraphics.h` for the exact constant names in your build environment.
+
+### 14.2 LockBitMapTags Lifetime Constraints
+
+`LockBitMapTags()` acquires an exclusive lock on the bitmap's VRAM. While locked:
+
+- The driver **cannot move** the bitmap in VRAM.
+- The OS **cannot page** the bitmap out.
+- On some drivers (especially early CGX), multitasking is **degraded** for the duration.
+
+**Rule**: Hold the lock for the shortest time possible — typically one frame render. Never hold a lock across a `Wait()` or `Delay()` call.
+
+### 14.3 Hardware Acceleration Availability
+
+Not all RTG cards accelerate 2D operations in VRAM. The following table maps common operations to typical hardware support:
+
+| Operation | Retina / Picasso II | CyberVision 64 | Picasso IV | Permedia 2 | Voodoo 3 / Radeon |
+|---|---|---|---|---|---|
+| **Rect fill** | Software | S3 blitter | Cirrus blitter | GLINT | Yes |
+| **Rect copy (Blit)** | Software | S3 blitter | Cirrus blitter | GLINT | Yes |
+| **Line draw** | Software | S3 | Cirrus | GLINT | Yes |
+| **Pattern fill** | Software | Limited | Limited | GLINT | Yes |
+| **Scaled blit** | Software | Software | Software | GLINT | Yes |
+| **3D triangle** | No | No (ViRGE: limited) | No | Yes | Yes |
+
+To query acceleration at runtime, check the `BMA_FLAGS` attribute after allocating a bitmap. If `BMF_HARDWARE` is not set, the operation will fall back to CPU rendering — which on Zorro II means crossing the bus for every pixel.
+
+### 14.4 ModeID and Display Database Constraints
+
+RTG modes are identified by 32-bit `DisplayID` values, but they are **not** native Amiga chipset ModeIDs. Key constraints:
+
+- **No guarantee of availability**: A ModeID that works on a CyberVision 64 may not exist on a Picasso II, even at the same resolution. Always query the display database with `NextDisplayInfo()` or use `asl.library` to let the user choose.
+- **Refresh rate encoding**: RTG ModeIDs often encode refresh rate in the upper bits. A ModeID for 800×600@60 Hz is a different value than 800×600@75 Hz.
+- **Interlace flag**: Setting the `DIPF_IS_LACE` bit in an RTG ModeID is legal only if the card and driver support interlaced output. Many PCI cards (Voodoo 3, Radeon) reject interlaced ModeIDs entirely.
+
+### 14.5 VRAM Allocation Failures
+
+When `AllocBitMap()` or `OpenScreenTags()` fails, the error code is often `NULL` with no explicit reason. Common causes:
+
+1. **Insufficient contiguous VRAM** — fragmentation or oversized request.
+2. **Unsupported pixel format** — requesting `PIXFMT_ARGB32` on a card that only supports `PIXFMT_RGB24`.
+3. **ModeID not supported** — the driver rejected the requested resolution/depth combination.
+4. **Bus timeout** — on Zorro II, very large allocations can trigger bus timeout during AutoConfig.
+
+**Debugging strategy**: Reduce the request depth (32 → 16 → 8) and resolution until allocation succeeds. Query `AvailableMem(MEMF_FAST)` to check Fast RAM fallback; if it drops, VRAM is exhausted.
+
+### 14.6 Draggable Screens and Passthrough
+
+Some RTG cards cannot coexist with native chipset output:
+
+| Card | Draggable Screens | Native Passthrough | Notes |
+|---|---|---|---|
+| **Picasso II/IV** | Yes | Automatic | Hardware monitor switcher |
+| **CyberVision 64** | Yes | Hardware switcher | Digital video expansion bus |
+| **CyberVision 64/3D** | Yes | Scan doubler | Doubles 15 kHz to 31 kHz |
+| **CyberVision PPC** | **No** | External switch required | Permedia 2 design limitation |
+| **BlizzardVision PPC** | **No** | External switch required | A1200 CPU-slot card |
+| **Voodoo 3 (PCI)** | Yes | No native output | Pure RTG; no Amiga video |
+| **Radeon (PCI)** | Yes | No native output | Pure RTG; no Amiga video |
+
+If your application relies on dragging screens between native and RTG displays, test on hardware that supports it — or restrict usage to Picasso96/CyberGraphX cards with known switcher support.
diff --git a/README.md b/README.md
index 40a66bb..c8a5ff4 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ The Amiga's documentation was scattered across out-of-print manuals, Usenet post
| **📚 Libraries** | utility, expansion, IFFParse, locale, ARexx, math, layers, diskfont, DataTypes, AmigaGuide, translator (speech) |
| **🌐 Networking** | bsdsocket.library API, SANA-II, TCP/IP stacks comparison |
| **🛠️ Toolchain** | GCC (bebbo/Codeberg), vasm/vlink, SAS/C, NDK, Makefiles, debugging |
-| **🔍 Reverse Engineering** | IDA/Ghidra setup, compiler fingerprints, binary patching, 3 case studies |
+| **🔍 Reverse Engineering** | IDA/Ghidra setup, compiler fingerprints, binary patching, game RE, 3 case studies |
| **🧮 FPU, MMU & Cache** | 68881/68882 FPU architecture, 68040/060 Line-F FPU emulation, PMMU page tables, cache coherency |
| **🏗️ Driver Development** | exec.device framework, SANA-II network drivers, Picasso96/RTG, AHI audio |
@@ -39,7 +39,7 @@ The Amiga's documentation was scattered across out-of-print manuals, Usenet post
| **New to Amiga** | [History & chipsets](00_overview/history.md) → [Boot sequence](02_boot_sequence/cold_boot.md) → [Exec kernel](06_exec_os/exec_base.md) |
| **Writing code** | [Toolchain setup](13_toolchain/gcc_amiga.md) → [Calling conventions](04_linking_and_libraries/register_conventions.md) → [.fd files](04_linking_and_libraries/fd_files.md) |
| **Doing hardware** | [Address space](01_hardware/common/address_space.md) → [Memory types](01_hardware/common/memory_types.md) → [Custom registers](01_hardware/ocs_a500/custom_registers.md) → [Copper programming](08_graphics/copper_programming.md) |
-| **Reverse engineering** | [RE methodology](05_reversing/methodology.md) → [IDA/Ghidra setup](05_reversing/ida_setup.md) → [API call identification](05_reversing/static/api_call_identification.md) |
+| **Reverse engineering** | [RE methodology](05_reversing/methodology.md) → [Game RE](05_reversing/games/game_reversing.md) → [IDA/Ghidra setup](05_reversing/ida_setup.md) |
| **Building an FPGA core** | [Hardware models](00_overview/hardware_models.md) → [AGA chipset](01_hardware/aga_a1200_a4000/chipset_aga.md) → [68040/060 libs](15_fpu_mmu_cache/68040_68060_libraries.md) |
---
@@ -86,6 +86,7 @@ The Amiga's documentation was scattered across out-of-print manuals, Usenet post
| [**kickstart_rom.md**](02_boot_sequence/kickstart_rom.md) | **Kickstart ROM internals: binary structure, module inventory, extraction tools, custom ROM building** |
| [kickstart_init.md](02_boot_sequence/kickstart_init.md) | ExecBase creation, capture vectors, ROM scan algorithm, 4-phase resident init |
| [dos_boot.md](02_boot_sequence/dos_boot.md) | strap module, boot block format and execution, MountList, Startup-Sequence walkthrough |
+| [floppy_vs_hdd_physical_boot.md](02_boot_sequence/floppy_vs_hdd_physical_boot.md) | Physical hardware mechanisms: floppy MFM streaming, HDD RDB scanning, DMA transfers, failure modes |
| [early_startup.md](02_boot_sequence/early_startup.md) | Early Startup Control menu: device selection, display mode, recovery scenarios |
### 03 — Executable Loader & HUNK Format
@@ -123,6 +124,9 @@ The Amiga's documentation was scattered across out-of-print manuals, Usenet post
| [compiler_fingerprints.md](05_reversing/compiler_fingerprints.md) | SAS/C vs GCC vs VBCC codegen patterns |
| [patching_techniques.md](05_reversing/patching_techniques.md) | Binary patching strategies |
| [unpacking_and_decrunching.md](05_reversing/unpacking_and_decrunching.md) | Executable unpacking, decruncher architecture, and manual extraction |
+| [custom_loaders_and_drm.md](05_reversing/custom_loaders_and_drm.md) | Bypassing DOS, Trackloaders, and physical DRM tricks |
+| [anti_debugging.md](05_reversing/anti_debugging.md) | The Cracker vs. Developer arms race: Trace vector abuse, NMI defeat |
+| [whdload_architecture.md](05_reversing/whdload_architecture.md) | WHDLoad internals, slaves, resload_DiskLoad, memory patching |
| [case_studies/ramdrive_device.md](05_reversing/case_studies/ramdrive_device.md) | Case Study: ramdrive.device RE walkthrough |
| Per-Compiler RE Field Manuals | Topic |
@@ -163,6 +167,10 @@ The Amiga's documentation was scattered across out-of-print manuals, Usenet post
|---|---|
| [ramdrive_device.md](05_reversing/case_studies/ramdrive_device.md) | RAM disk device driver RE |
+| Game Reverse Engineering | Topic |
+|---|---|
+| [games/game_reversing.md](05_reversing/games/game_reversing.md) | Game RE deep dive: disassembly, modification, asset extraction, save game analysis |
+
### 06 — Exec Kernel (OS 3.1/3.2)
| File | Topic |
|---|---|
@@ -207,10 +215,10 @@ The Amiga's documentation was scattered across out-of-print manuals, Usenet post
| [sprites.md](08_graphics/sprites.md) | Hardware sprites, SimpleSprite |
| [rastport.md](08_graphics/rastport.md) | RastPort, drawing primitives, layers |
| [views.md](08_graphics/views.md) | View/ViewPort, MakeVPort, display pipeline |
-| [text_fonts.md](08_graphics/text_fonts.md) | TextFont, OpenFont, text rendering |
-| [display_modes.md](08_graphics/display_modes.md) | ModeID, display database |
-| [ham_ehb_modes.md](08_graphics/ham_ehb_modes.md) | HAM6, HAM8, EHB special display modes |
-| [animation.md](08_graphics/animation.md) | GEL system: BOBs, VSprites, AnimObs |
+| [text_fonts.md](08_graphics/text_fonts.md) | TextFont bitmap layout, baseline rendering, algorithmic styles, AvailFonts enumeration |
+| [pixel_conversion.md](08_graphics/pixel_conversion.md) | Chunky ↔ Planar conversion deep dive: naive, merge/butterfly (Kalms), Copper Chunky, Akiko hardware, Blitter-assisted, RTG bypass, SoA/AoS theory, GPU swizzle modern parallels |
+| [animation.md](08_graphics/animation.md) | GEL system deep dive: BOBs, VSprites, AnimObs, hardware foundation (Blitter/Copper/Sprite interaction), collision detection, double buffering, performance tuning |
+| [rtg_programming.md](08_graphics/rtg_programming.md) | Retargetable Graphics (CyberGraphX/Picasso96): Planar vs Chunky, LockBitMapTags, Pixel Formats, Direct VRAM rendering |
### 09 — Intuition
| File | Topic |
diff --git a/TODO.md b/TODO.md
index dbe7792..291a916 100644
--- a/TODO.md
+++ b/TODO.md
@@ -66,6 +66,18 @@ Articles were scored against [AGENTS.md](../amiga/AGENTS.md) "Deep" criteria:
---
+## Tier 4 — Modernization & Advanced Techniques (New Articles)
+
+| # | New Article | Why Needed | Status |
+|---|---|---|---|
+| 26 | **Custom Trackloaders & DRM** | 80% of classic games bypassed DOS. Reversing them requires understanding raw MFM sync words, bootblocks, and copy protection (e.g. Rob Northen Copylock). | ❌ **Pending** |
+| 27 | **RTG (Retargetable Graphics)** | Modern Amigas use RTG (Picasso96/CyberGraphX) for 16/24-bit chunky graphics. Application-level rendering is undocumented in our `08_graphics` folder. | ❌ **Pending** |
+| 28 | **AHI Audio Interface** | Hardware-agnostic 16-bit multi-channel audio mixing is standard for modern Amiga apps, decoupling audio from the 8-bit 4-channel Paula chip limits. | ❌ **Pending** |
+| 29 | **Demoscene Techniques** | Exploits like Sprite Multiplexing and Copper Chunks defined the platform's capabilities. Crucial for understanding high-performance hardware banging. | ❌ **Pending** |
+| 30 | **Modern Cross-Compilation** | Setting up `m68k-amigaos-gcc`, `vbcc`, and `vasm` via CMake on modern macOS/Linux to build native `.hunk` binaries. | ❌ **Pending** |
+
+---
+
## Per-Article Status
| Status | Meaning |
@@ -428,4 +440,14 @@ Articles were scored against [AGENTS.md](../amiga/AGENTS.md) "Deep" criteria:
| 24 | `10_devices/console.md` | 244 lines |
| 25 | `10_devices/trackdisk.md` | 178 lines |
-> **Progress**: 15 of 25 items complete (60%). Tier 1 fully cleared. 0 Tier 2 remaining. 10 Tier 3 items remain.
+**Tier 4 — 5 pending advanced topics:**
+
+| # | File | Current |
+|---|---|---|
+| 26 | `05_reversing/custom_loaders_and_drm.md` | 168 lines |
+| 27 | `08_graphics/rtg_programming.md` | 0 lines |
+| 28 | `11_libraries/ahi_programming.md` | 0 lines |
+| 29 | `17_demoscene/README.md` | 0 lines |
+| 30 | `13_toolchain/cross_compilation_guide.md` | 0 lines |
+
+> **Progress**: 15 of 30 items complete (50%). Tier 1 and 2 fully cleared. 10 Tier 3 items and 5 Tier 4 items remain.