Skip to content

IntuitionAmiga/IntuitionEngine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

11b8a53 · Apr 28, 2026

History

600 Commits
Apr 20, 2026
Apr 26, 2026
Feb 16, 2026
Apr 11, 2026
Apr 20, 2026
Apr 21, 2026
Apr 23, 2026
Apr 28, 2026
Mar 8, 2026
Apr 26, 2026
Apr 17, 2026
Apr 11, 2026
Apr 20, 2026
Apr 28, 2026
Mar 28, 2026
Apr 26, 2026
Feb 5, 2025
Apr 26, 2026
Apr 28, 2026
Feb 15, 2026
Feb 12, 2026
Jan 28, 2026
Feb 12, 2026
Feb 12, 2026
Feb 12, 2026
Feb 12, 2026
Feb 15, 2026
Feb 8, 2026
Feb 16, 2026
Feb 12, 2026
Feb 12, 2026
Feb 12, 2026
Jan 28, 2026
Feb 12, 2026
Apr 28, 2026
Feb 28, 2026
Apr 28, 2026
Apr 23, 2026
Apr 20, 2026
Apr 20, 2026
Mar 6, 2026
Mar 8, 2026
Apr 21, 2026
Apr 23, 2026
Apr 23, 2026
Apr 28, 2026
Apr 28, 2026
Feb 28, 2026
Apr 23, 2026
Apr 23, 2026
Apr 23, 2026
Jan 14, 2026
Feb 15, 2026
Feb 15, 2026
Jan 14, 2026
Feb 15, 2026
Apr 16, 2026
Feb 16, 2026
Feb 24, 2026
Feb 16, 2026
Feb 18, 2026
Jan 14, 2026
Jan 14, 2026
Jan 14, 2026
Jan 14, 2026
Feb 15, 2026
Feb 12, 2026
Feb 12, 2026
Apr 16, 2026
Apr 16, 2026
Jan 14, 2026
Feb 12, 2026
Feb 12, 2026
Feb 12, 2026
Feb 16, 2026
Feb 16, 2026
Feb 16, 2026
Feb 16, 2026
Feb 12, 2026
Jan 15, 2026
Feb 16, 2026
Feb 16, 2026
Apr 28, 2026
Feb 23, 2026
Feb 15, 2026
Feb 7, 2026
Apr 28, 2026
Apr 28, 2026
Apr 27, 2026
Apr 15, 2026
Apr 19, 2026
Apr 18, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 27, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 20, 2026
Mar 9, 2026
Feb 28, 2026
Jan 14, 2026
Apr 16, 2026
Jan 16, 2026
Feb 15, 2026
Feb 15, 2026
Mar 9, 2026
Feb 16, 2026
Feb 15, 2026
Feb 15, 2026
Feb 23, 2026
Mar 9, 2026
Mar 9, 2026
Mar 9, 2026
Mar 9, 2026
Apr 13, 2026
Apr 13, 2026
Apr 13, 2026
Apr 13, 2026
Apr 13, 2026
Apr 13, 2026
Apr 13, 2026
Apr 21, 2026
Apr 13, 2026
Feb 7, 2026
Feb 12, 2026
Feb 12, 2026
Apr 28, 2026
Feb 8, 2026
Feb 8, 2026
Feb 12, 2026
Feb 8, 2026
Apr 28, 2026
Feb 8, 2026
Apr 28, 2026
Feb 8, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 27, 2026
Apr 28, 2026
Apr 23, 2026
Apr 6, 2026
Feb 22, 2026
Apr 16, 2026
Apr 23, 2026
Apr 23, 2026
Apr 23, 2026
Apr 23, 2026
Apr 23, 2026
Mar 29, 2026
Feb 22, 2026
Apr 28, 2026
Apr 28, 2026
Apr 11, 2026
Apr 11, 2026
Apr 28, 2026
Jan 30, 2026
Feb 12, 2026
Jan 31, 2026
Apr 16, 2026
Apr 28, 2026
Feb 12, 2026
Apr 2, 2026
Feb 12, 2026
Jan 15, 2026
Feb 10, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Apr 28, 2026
Jan 15, 2026
Jan 15, 2026
Feb 8, 2026
Apr 28, 2026
Feb 27, 2026
Feb 16, 2026
Feb 15, 2026
Feb 15, 2026
Apr 28, 2026
Feb 15, 2026
Feb 15, 2026
Feb 15, 2026
Feb 15, 2026
Feb 15, 2026
Apr 17, 2026
Feb 16, 2026
Feb 15, 2026
Feb 15, 2026
Feb 15, 2026
Mar 11, 2026
Feb 16, 2026
Feb 27, 2026
Apr 20, 2026
Feb 14, 2026
Feb 17, 2026
Feb 15, 2026
Apr 28, 2026
Apr 20, 2026
Apr 15, 2026
Feb 14, 2026
Feb 23, 2026
Apr 28, 2026
Apr 28, 2026
Feb 22, 2026
Feb 22, 2026
Feb 23, 2026
Feb 18, 2026
Feb 15, 2026
Feb 28, 2026
Feb 10, 2026
Apr 15, 2026
Apr 15, 2026
Apr 15, 2026
Mar 8, 2026
Mar 8, 2026
Feb 13, 2026
Feb 13, 2026
Feb 13, 2026
Feb 13, 2026
Feb 13, 2026
Feb 13, 2026
Feb 16, 2026
Feb 13, 2026
Feb 13, 2026
Feb 13, 2026
Feb 16, 2026
Feb 22, 2026
Feb 22, 2026
Feb 22, 2026
Apr 20, 2026
Apr 20, 2026
Feb 3, 2026
Feb 3, 2026
Mar 26, 2026
Apr 27, 2026
Feb 8, 2026
Apr 28, 2026
Apr 11, 2026
Apr 11, 2026
Mar 30, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Mar 31, 2026
Apr 21, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 28, 2026
Apr 19, 2026
Apr 20, 2026
Apr 20, 2026
Apr 28, 2026
Apr 17, 2026
Apr 28, 2026
Apr 20, 2026
Apr 27, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 28, 2026
Apr 28, 2026
Apr 17, 2026
Apr 20, 2026
Apr 20, 2026
Apr 17, 2026
Apr 17, 2026
Apr 20, 2026
Apr 20, 2026
Apr 23, 2026
Apr 23, 2026
Apr 20, 2026
Apr 20, 2026
Apr 23, 2026
Apr 23, 2026
Apr 23, 2026
Apr 23, 2026
Apr 17, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 17, 2026
Apr 20, 2026
Apr 20, 2026
Apr 27, 2026
Apr 20, 2026
Apr 20, 2026
Apr 28, 2026
Apr 20, 2026
Apr 20, 2026
Apr 1, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 28, 2026
Apr 16, 2026
Apr 28, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 2, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 20, 2026
Apr 2, 2026
Apr 20, 2026
Apr 2, 2026
Feb 16, 2026
Feb 23, 2026
Feb 23, 2026
Feb 23, 2026
Feb 23, 2026
Feb 23, 2026
Feb 23, 2026
Apr 20, 2026
Feb 27, 2026
Feb 18, 2026
Apr 19, 2026
Apr 19, 2026
Apr 27, 2026
Apr 26, 2026
Apr 27, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 27, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 27, 2026
Jan 31, 2026
Apr 23, 2026
Apr 23, 2026
Apr 21, 2026
Apr 23, 2026
Feb 22, 2026
Feb 8, 2026
Jan 19, 2026
Jan 19, 2026
Apr 15, 2026
Mar 30, 2026
Mar 8, 2026
Feb 22, 2026
Feb 16, 2026
Jan 19, 2026
Jan 19, 2026
Jan 19, 2026
Apr 28, 2026
Feb 16, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 27, 2026
Feb 12, 2026
Feb 12, 2026
Apr 28, 2026
Apr 23, 2026
Apr 28, 2026
Feb 11, 2026
Feb 22, 2026
Feb 13, 2026
Feb 27, 2026
Feb 24, 2026
Feb 24, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 26, 2026
Apr 26, 2026
Apr 26, 2026
Apr 27, 2026
Apr 27, 2026
Apr 27, 2026
Apr 28, 2026
Apr 27, 2026
Apr 27, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 14, 2026
Jan 28, 2026
Feb 8, 2026
Feb 12, 2026
Feb 16, 2026
Jan 26, 2026
Feb 17, 2026
Feb 12, 2026
Feb 8, 2026
Apr 28, 2026
Apr 28, 2026
Apr 28, 2026
Apr 3, 2026
Mar 6, 2026
Jan 19, 2026
Feb 16, 2026
Feb 16, 2026
Feb 27, 2026
Feb 12, 2026
Feb 12, 2026
Jan 15, 2026
Jan 15, 2026
Jan 15, 2026
Feb 15, 2026
Feb 16, 2026
Feb 17, 2026
Feb 8, 2026
Feb 17, 2026
Feb 12, 2026
Feb 15, 2026
Apr 26, 2026
Apr 16, 2026
Apr 16, 2026
Feb 16, 2026
Apr 13, 2026
Apr 28, 2026
Feb 18, 2026
Apr 20, 2026
Mar 9, 2026
Feb 12, 2026
Jan 21, 2026
Feb 7, 2026
Feb 7, 2026
Feb 12, 2026
Feb 15, 2026
Feb 12, 2026
Feb 12, 2026
Jan 12, 2026
Apr 20, 2026
Apr 16, 2026
Mar 6, 2026
Feb 15, 2026
Feb 16, 2026
Feb 15, 2026
Feb 12, 2026
Feb 17, 2026
Feb 12, 2026
Jan 28, 2026
Feb 12, 2026
Feb 12, 2026
Feb 15, 2026
Feb 15, 2026
Feb 15, 2026
Feb 8, 2026
Feb 15, 2026
Feb 12, 2026
Feb 15, 2026
Jan 19, 2026
Feb 12, 2026
Feb 7, 2026
Feb 7, 2026
Feb 12, 2026
Feb 8, 2026
Feb 15, 2026
Feb 5, 2025
Feb 28, 2026
Apr 26, 2026
Apr 26, 2026
Feb 28, 2026
Feb 12, 2026
Feb 12, 2026
Feb 12, 2026
Feb 15, 2026
Feb 17, 2026
Feb 12, 2026
Feb 15, 2026
Feb 15, 2026
Feb 12, 2026
Feb 8, 2026
Feb 8, 2026
Jan 23, 2026
Jan 30, 2026
Feb 12, 2026
Feb 16, 2026
Feb 16, 2026
Mar 6, 2026
Feb 22, 2026
Jan 26, 2026
Feb 17, 2026
Feb 17, 2026
Feb 17, 2026
Feb 16, 2026
Feb 17, 2026
Feb 17, 2026
Feb 17, 2026
Feb 16, 2026
Feb 16, 2026
Jan 27, 2026
Feb 16, 2026
Jan 26, 2026
Feb 8, 2026
Feb 12, 2026
Feb 16, 2026
Feb 16, 2026
Feb 23, 2026
Feb 23, 2026
Apr 20, 2026
Feb 11, 2026
Feb 15, 2026
Feb 11, 2026
Mar 8, 2026
Apr 7, 2026
Feb 12, 2026
Feb 11, 2026
Jan 14, 2026
Feb 28, 2026
Feb 27, 2026
Feb 23, 2026
Feb 23, 2026
Feb 12, 2026
Mar 6, 2026
Feb 11, 2026
Jan 15, 2026
Feb 27, 2026
Feb 17, 2026
Feb 17, 2026
Feb 16, 2026
Apr 8, 2026
Apr 20, 2026
Feb 17, 2026
Feb 23, 2026
Feb 23, 2026
Feb 16, 2026
Feb 23, 2026
Feb 23, 2026
Feb 23, 2026
Feb 15, 2026
Feb 15, 2026
Feb 15, 2026
Feb 16, 2026
Feb 15, 2026
Feb 16, 2026
Feb 17, 2026
Feb 17, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Feb 24, 2026
Apr 28, 2026
Apr 28, 2026
Apr 1, 2026
Feb 16, 2026
Feb 15, 2026
Apr 2, 2026
Apr 2, 2026

Repository files navigation

██▓ ███▄    █ ▄▄▄█████▓ █    ██  ██▓▄▄▄█████▓ ██▓ ▒█████   ███▄    █    ▓█████  ███▄    █   ▄████  ██▓ ███▄    █ ▓█████
▓██▒ ██ ▀█   █ ▓  ██▒ ▓▒ ██  ▓██▒▓██▒▓  ██▒ ▓▒▓██▒▒██▒  ██▒ ██ ▀█   █    ▓█   ▀  ██ ▀█   █  ██▒ ▀█▒▓██▒ ██ ▀█   █ ▓█   ▀
▒██▒▓██  ▀█ ██▒▒ ▓██░ ▒░▓██  ▒██░▒██▒▒ ▓██░ ▒░▒██▒▒██░  ██▒▓██  ▀█ ██▒   ▒███   ▓██  ▀█ ██▒▒██░▄▄▄░▒██▒▓██  ▀█ ██▒▒███
░██░▓██▒  ▐▌██▒░ ▓██▓ ░ ▓▓█  ░██░░██░░ ▓██▓ ░ ░██░▒██   ██░▓██▒  ▐▌██▒   ▒▓█  ▄ ▓██▒  ▐▌██▒░▓█  ██▓░██░▓██▒  ▐▌██▒▒▓█  ▄
░██░▒██░   ▓██░  ▒██▒ ░ ▒▒█████▓ ░██░  ▒██▒ ░ ░██░░ ████▓▒░▒██░   ▓██░   ░▒████▒▒██░   ▓██░░▒▓███▀▒░██░▒██░   ▓██░░▒████▒
░▓  ░ ▒░   ▒ ▒   ▒ ░░   ░▒▓▒ ▒ ▒ ░▓    ▒ ░░   ░▓  ░ ▒░▒░▒░ ░ ▒░   ▒ ▒    ░░ ▒░ ░░ ▒░   ▒ ▒  ░▒   ▒ ░▓  ░ ▒░   ▒ ▒ ░░ ▒░ ░
 ▒ ░░ ░░   ░ ▒░    ░    ░░▒░ ░ ░  ▒ ░    ░     ▒ ░  ░ ▒ ▒░ ░ ░░   ░ ▒░    ░ ░  ░░ ░░   ░ ▒░  ░   ░  ▒ ░░ ░░   ░ ▒░ ░ ░  ░
 ▒ ░   ░   ░ ░   ░       ░░░ ░ ░  ▒ ░  ░       ▒ ░░ ░ ░ ▒     ░   ░ ░       ░      ░   ░ ░ ░ ░   ░  ▒ ░   ░   ░ ░    ░
 ░           ░             ░      ░            ░      ░ ░           ░       ░  ░         ░       ░  ░           ░    ░  ░

Intuition Engine System Documentation

Complete Technical Reference & User Guide

(c) 2024 - 2026 Zayn Otley

License: GPLv3 or later

ko-fi

See also: TUTORIAL.md - Step-by-step guide to building a complete demoscene intro with multiple CPU architectures.

IntuitionOS M16.2.1 note: public handler, device, and resource acquisition is now frozen as IPC through the kernel-serviced public exec.library port. SDK-facing AttachHandler, OpenDevice, and OpenResource wrappers use EXEC_MSG_* request/reply messages with one-page shared request buffers and opaque generation-aware tokens. This is ONLINE-only for non-library rows; it does not add public acquisition syscalls, non-library demand-load, PIE, relocation, ASLR, or third-party install policy.

Quick Links

Document Description
DEVELOPERS.md Build, test, toolchains, and contribution guide
CHANGELOG.md Release history
sdk/README.md SDK developer package with examples and build scripts
sdk/docs/TUTORIAL.md Step-by-step demoscene intro tutorial
sdk/docs/IE64_ISA.md IE64 instruction set reference
sdk/docs/IE64_COOKBOOK.md IE64 common patterns and recipes
sdk/docs/ehbasic_ie64.md EhBASIC language guide
sdk/docs/iemon.md Machine monitor (F9 debugger) reference
sdk/docs/iescript.md IEScript Lua automation reference
sdk/docs/sdk-getting-started.md SDK quick start
sdk/docs/toolchains.md Assembler toolchain reference
sdk/docs/demo-matrix.md Demo program coverage matrix
sdk/docs/platform-compatibility.md Platform support and build profiles
sdk/docs/release-process.md Release packaging guide
sdk/docs/ie_emutos.md EmuTOS integration guide (GEM desktop on IE)
sdk/docs/IntuitionOS/IExec.md IExec microkernel contract reference
sdk/docs/IntuitionOS/HostFS.md Bootstrap HostFS confinement and path-resolution contract

Table of Contents

  1. System Overview
  2. Architecture
    • 2.1 Unified Memory
    • 2.2 Hardware I/O
  3. Memory Map & Hardware Registers
    • 3.1 System Vector Table
    • 3.2 Program Space
    • 3.3 Video Registers
    • 3.4 Timer Registers
    • 3.5 Sound Registers (Legacy and Flexible)
    • 3.6 PSG Registers
    • 3.7 POKEY Registers
    • 3.8 SID Registers
    • 3.9 TED Registers
    • 3.10 TED Video Chip Registers
    • 3.11 AHX Module Player Registers
    • 3.12 MOD Player Registers
    • 3.13 Hardware I/O Memory Map by CPU
    • 3.14 VGA Video Chip
    • 3.15 ULA Video Chip (ZX Spectrum)
    • 3.16 ANTIC Video Chip (Atari 8-bit)
    • 3.17 GTIA Color Control (Atari 8-bit)
    • 3.18 File I/O Device
    • 3.19 Coprocessor Subsystem
    • 3.20 Voodoo 3D Graphics
  4. IE32 CPU Architecture
    • 4.1 Register Set
    • 4.2 Status Flags
    • 4.3 Addressing Modes
    • 4.4 Instruction Format
    • 4.5 Instruction Set
    • 4.6 Memory and I/O Integration
    • 4.7 Interrupt Handling
    • 4.8 Compatibility Notes
  5. MOS 6502 CPU
    • 5.1 Register Set
    • 5.2 Status Flags
    • 5.3 Addressing Modes
    • 5.4 Instruction Set
    • 5.5 Memory and I/O Integration
    • 5.6 Interrupts and Vectors
    • 5.7 Compatibility Notes
  6. Zilog Z80 CPU
    • 6.1 Register Set
    • 6.2 Status Flags
    • 6.3 Addressing Modes
    • 6.4 Instruction Set
    • 6.5 Memory and I/O Integration
    • 6.6 Interrupts
    • 6.7 Compatibility Notes
  7. Motorola 68020 CPU with FPU
    • 7.1 Register Set
    • 7.2 Status Flags
    • 7.3 Addressing Modes
    • 7.4 Instruction Set
    • 7.5 FPU (68881/68882) Features
    • 7.6 Memory and I/O Integration
    • 7.7 Interrupts and Exceptions
    • 7.8 Compatibility Notes
  8. Intel x86 CPU (32-bit)
    • 8.1 Register Set
    • 8.2 Status Flags
    • 8.3 Addressing Modes
    • 8.4 Instruction Set
    • 8.5 Memory and I/O Integration
    • 8.6 Interrupts
    • 8.7 Compatibility Notes
  9. IE64 CPU Architecture
    • 9.1 Register Set
    • 9.2 Instruction Format
    • 9.3 Addressing Modes
    • 9.4 Instruction Set
    • 9.5 FPU Logic
    • 9.6 Memory and I/O Integration
    • 9.7 Interrupt Handling
    • 9.8 Compatibility Notes
    • 9.9 EhBASIC IE64
    • 9.10 Memory Management Unit (MMU)
  10. Assembly Language Reference
    • 10.1 Basic Program Structure
    • 10.2 Assembler Directives
    • 10.3 Memory Access Patterns
    • 10.4 Stack Usage
    • 10.5 Interrupt Handlers
  11. Sound System
    • Custom Audio Chip Overview
    • 11.1 Sound Channel Types
    • 11.2 Modulation System
    • 11.3 Global Effects
    • 11.4 PSG Sound Chip (AY-3-8910/YM2149)
    • 11.5 POKEY Sound Chip
    • 11.6 SID Sound Chip
    • 11.7 TED Sound Chip
    • 11.8 AHX Sound Chip
    • 11.9 MOD Player (ProTracker)
  12. Video System
    • 12.1 Display Modes
    • 12.2 Framebuffer Organisation
    • 12.3 Dirty Rectangle Tracking
    • 12.4 Double Buffering and VBlank Synchronisation
    • 12.5 Direct VRAM Access Mode
    • 12.6 Copper List Executor
    • 12.7 DMA Blitter
    • 12.8 Raster Band Fill
    • 12.9 Video Compositor
  13. Developer's Guide
  14. Implementation Details
  15. Platform Support and Building
  16. EmuTOS Mode
  17. AROS Mode
  18. IntuitionOS

1. System Overview

The Intuition Engine is a virtual machine that emulates a complete retro-style computer system. It is a modern 64-bit RISC reimagining of the Commodore, Atari, Sinclair and IBM 8/16/32-bit home computers, with IE64 as the default core and five additional CPU cores.

CPU Options

CPU Architecture Registers Features
IE32 32-bit RISC 16 general-purpose (A-H, S-W, X-Z) Fixed 8-byte instructions, simple and fast
M68K 32-bit CISC 8 data (D0-D7), 8 address (A0-A7) 95%+ instruction coverage, FPU support
Z80 8-bit AF, BC, DE, HL + alternates, IX, IY Full instruction set, interrupt modes, x86-64 JIT with block chaining
6502 8-bit A, X, Y NMOS instruction set, fast inline interpreter with direct-page-bitmap memory fast path, x86-64 JIT with 7-9x speedup on compute-bound workloads
x86 32-bit EAX-EDX, ESI, EDI, EBP, ESP 8086 instructions + 32-bit registers, flat memory model
IE64 64-bit RISC 32 general-purpose (R0=zero, R31=SP) ARM64/x86-64 JIT compiler, native FP32/FP64 FPU, compare-and-branch, no flags register, MMU with paged virtual memory and user/supervisor privilege levels, atomic memory operations (CAS/XCHG/FAA), TLS register

Default core: IE64. Additional cores: IE32, M68K, x86, Z80, 6502.

Audio Capabilities

Custom Synthesizer:

  • 10-channel synth engine (4 base + 3 SID2 + 3 SID3 for multi-SID playback)
  • 5 legacy register blocks (square, triangle, sine, noise, sawtooth)
  • 4 flexible channels with selectable waveforms (uniform register interface)
  • ADSR envelopes, PWM, frequency sweep, hard sync, ring modulation
  • Global filter (LP/HP/BP), overdrive, reverb
  • 44.1kHz, 32-bit floating-point processing

Classic Sound Chips (register-mapped to custom synth):

  • AY/YM/PSG (AY-3-8910/YM2149) - Supports .ym, .ay, .vgm, .vgz, .vtx, .sndh, .pt3, .pt2, .pt1, .stc, .sqt, .asc, .ftc playback (VGM includes SN76489 conversion; tracker formats use Z80 emulation)
  • POKEY (Atari) - Supports .sap playback
  • SID (6581/8580) - Supports .sid playback
  • TED (Commodore Plus/4) - Supports .ted playback
  • Amiga AHX module playback
  • ProTracker MOD (.mod) - 4-channel Amiga module playback with A500/A1200 filter emulation
  • WAV PCM audio via SoundChip FLEX DAC mode

Video System

  • Resolutions: 640×480, 800×600, 1024×768, 1280×960 (default)
  • 32-bit RGBA framebuffer with double buffering
  • Copper coprocessor for raster effects
  • DMA blitter for fast copy/fill/line operations
  • Dirty rectangle tracking for efficient updates
  • Engines/chips: IEVideoChip, VGA, ULA, TED video, ANTIC/GTIA, 3DFX Voodoo

Scripting

  • IEScript (Lua 5.1) automation engine for programmatic control of the entire emulator
  • 11 API modules: cpu, mem, term, audio, video, repl, dbg, rec, coproc, media, sys
  • Frame-synchronised coroutine model, MP4+AAC recording via FFmpeg, interactive F8 REPL overlay
  • See iescript.md for the full reference

Quick Start

# Default: start EhBASIC on IE64
./bin/IntuitionEngine

# Run IE32 program
./bin/IntuitionEngine -ie32 program.iex

# Run M68K program
./bin/IntuitionEngine -m68k program.ie68

# Run x86 program
./bin/IntuitionEngine -x86 program.ie86

# Run IE64 program
./bin/IntuitionEngine -ie64 program.ie64

# Run EhBASIC interpreter
./bin/IntuitionEngine -basic

# Run EhBASIC with console terminal (no GUI window)
./bin/IntuitionEngine -basic -term

# Play PSG music
./bin/IntuitionEngine -psg music.ym
./bin/IntuitionEngine -psg track.vtx       # VTX (LHA-compressed YM)
./bin/IntuitionEngine -psg track.pt3       # ProTracker 3 (ZX Spectrum)

# Play SID music
./bin/IntuitionEngine -sid music.sid
./bin/IntuitionEngine -sid music.sid -sid-pal    # Force PAL timing
./bin/IntuitionEngine -sid music.sid -sid-ntsc   # Force NTSC timing

# Play POKEY/SAP music (Atari 8-bit)
./bin/IntuitionEngine -pokey music.sap

# Play TED music (Commodore Plus/4)
./bin/IntuitionEngine -ted music.prg

# Play AHX music (Amiga tracker)
./bin/IntuitionEngine -ahx module.ahx

# Play WAV audio
./bin/IntuitionEngine -wav audio.wav

# Play ProTracker MOD music
./bin/IntuitionEngine -mod music.mod

# Enhanced audio modes (4x oversampling, filtering, drive, ambience)
./bin/IntuitionEngine -psg+ music.ym
./bin/IntuitionEngine -sid+ music.sid
./bin/IntuitionEngine -pokey+ music.sap
./bin/IntuitionEngine -ted+ music.prg
./bin/IntuitionEngine -ahx+ module.ahx

# Boot EmuTOS (GEM desktop)
./bin/IntuitionEngine -emutos

# Boot AROS (Amiga Workbench)
./bin/IntuitionEngine -aros

# Run a Lua automation script alongside a program
./bin/IntuitionEngine -ie64 program.ie64 -script demo.ies

# Run with performance measurement (MIPS reporting)
./bin/IntuitionEngine -perf -ie64 program.ie64

# Disable JIT compiler (use interpreter only)
./bin/IntuitionEngine -nojit -ie64 program.ie64

# Display options
./bin/IntuitionEngine -scale 2 -ie32 program.iex      # 2x window scaling
./bin/IntuitionEngine -fullscreen -m68k program.ie68   # Start in fullscreen (F11 to toggle)
./bin/IntuitionEngine -width 800 -height 600 -ie64 program.ie64  # Override output resolution

# Version information
./bin/IntuitionEngine -version

# List compiled feature flags and runtime info
./bin/IntuitionEngine -features

CPU modes that execute binaries (-ie32, -ie64, -m68k, -m6502, -z80, -x86) require a filename unless -basic is used.

1.4 Ebiten Window Controls

  • F9: Open the Machine Monitor (debugger) - freezes all CPUs, shows registers and disassembly. See iemon.md for full documentation.
  • F10: Hard reset - performs a full power-on hardware reset (reloads EmuTOS if started in EmuTOS mode, otherwise boots IE64 BASIC)
  • F11: Toggle fullscreen mode
  • F12: Toggle the runtime status bar
  • Page Up / Page Down: Scroll terminal scrollback buffer
  • Mouse wheel: Scroll terminal scrollback buffer
  • Status bar semantics: CPU, VIDEO, and AUDIO device names are shown in green when active and gray when inactive.

1.5 Single-Instance Mode

Opening an *.ie* file while Intuition Engine is already running sends the file to the running instance via Unix domain socket IPC. For binary files, the running instance performs a full hardware reset and loads the new binary. If the file uses a different CPU architecture (e.g., opening a .ie80 Z80 binary while an IE32 program is running), the CPU mode switches automatically. For .ies files, the script is executed without a hardware reset.

Supported extensions: .ie32/.iex (IE32), .ie64 (IE64), .ie65 (6502), .ie68 (M68K), .ie80 (Z80), .ie86 (X86), .ies (IEScript Lua automation). EmuTOS (.tos/.img) and AROS boot are CLI-only modes (-emutos, -aros) and are not supported via single-instance IPC.

1.6 Desktop Integration

Register Intuition Engine as the default handler for *.ie* files:

# Install .desktop entry and MIME type (system-wide, requires root)
sudo make install-desktop-entry

# Set as default handler for .ie* files (per-user)
make set-default-handler

After registration, double-clicking any .ie* file in a file manager will open it in Intuition Engine (or send it to an already-running instance).

1.7 Machine Monitor

Press F9 at any time to freeze the entire system and enter the built-in Machine Monitor - a system-level debugger inspired by the Commodore 64/Amiga Action Replay cartridge and HRTMon. Press x or Esc to resume execution.

The monitor works with all six CPU types (IE64, IE32, M68K, Z80, 6502, X86) and handles multi-CPU scenarios including coprocessors.

Quick Reference

Command Description
r Show all registers (changed values highlighted in green)
r <name> <value> Set a register value
d [addr] [count] Disassemble instructions with branch annotations
m <addr> [count] Hex dump memory
s [count] Single-step one or more instructions
bs Backstep (undo last step, restores CPU + memory)
g [addr] Resume execution (optionally from a new address)
u <addr> Run until address
b <addr> [cond] Set breakpoint (with optional condition)
bc <addr> / bc * Clear breakpoint(s)
bl List all breakpoints
ww <addr> Set write watchpoint
wc <addr|*> / wl Clear/list watchpoints
bt [depth] Stack backtrace
f <addr> <len> <byte> Fill memory with a byte value
w <addr> <bytes..> Write bytes to memory
t <dst> <src> <len> Transfer (copy) memory
c <addr1> <addr2> <len> Compare two memory regions
h <start> <end> <bytes..> Search memory for byte pattern
save <s> <e> <file> Save memory range to file
load <file> <addr> Load file into memory
ss / sl [file] Save/load machine state
trace <count> Trace N instructions (+ write history)
io [device|all] I/O register viewer (use all for every device)
e <addr> Hex editor mode
script <file> Run command script
macro <name> ... Define command macro
cpu [n] Switch focus to CPU #n (for multi-CPU debugging)
fa / ta Audio freeze / thaw
x Exit monitor and resume all CPUs

Addresses accept $hex, 0xhex, bare hex, #decimal, or expressions like pc+$20.

The monitor is also accessible from Lua scripts via the dbg.* API module. See iescript.md for scripted debugging workflows.

Full documentation: iemon.md

2. Architecture

2.1 Unified Memory

All CPU cores (IE32, IE64, M68K, Z80, 6502, x86) share the same memory space through the MachineBus. This unified architecture ensures that:

  • Program data loaded by any CPU is immediately visible to all peripherals
  • Audio synthesis responds instantly to register writes from any CPU
  • DMA operations (blitter, copper, file players) can access any memory location
  • Memory-mapped I/O works consistently across all CPU types
  • Video compositing blends multiple video sources (VideoChip, VGA) into final output
┌─────────────────────────────────────────────────────────────────┐
│                       MachineBus Memory                         │
│                  (autodetected guest RAM)                       │
├─────────────────────────────────────────────────────────────────┤
│  0x000000 - 0x000FFF  │  System Vectors                         │
│  0x001000 - 0x09FFFF  │  Program Space (code + data)            │
│  0x0A0000 - 0x0AFFFF  │  VGA VRAM Window (64KB)                 │
│  0x0B8000 - 0x0BFFFF  │  VGA Text Buffer (32KB)                 │
│  0x0F0000 - 0x0F0FFF  │  Video/Audio I/O Registers              │
│  0x0F1000 - 0x0F13FF  │  VGA Registers                          │
│  0x0F2400 - 0x0F240F  │  SYSINFO RAM-size MMIO (total+active)   │
│  0x100000 - 0x5FFFFF  │  Video RAM (5MB, chunky RGBA)           │
│  0x600000 - top       │  Extended RAM (sized per profile/CPU)   │
└─────────────────────────────────────────────────────────────────┘
        │                       │                      │
        ▼                       ▼                      ▼
   ┌─────────┐            ┌────────────────┐     ┌────────────────┐
   │   CPU   │            │  Video System  │     │  Audio System  │
   │ (IE32/  │            │  ────────────  │     │  ──────────────│
   │  IE64/  │            │  VideoChip     │     │  Custom Synth  │
   │  M68K/  │            │  VGA Engine    │     │  PSG/POKEY/SID │
   │  Z80/   │            │  Compositor    │     │  File Players  │
   │  6502/  │            │  Blitter/Copper│     └────────────────┘
   │  x86)   │
   └─────────┘

The video compositor blends output from all enabled video sources (VideoChip=0, VGA=10, TED=12, ANTIC/GTIA=13, ULA=15, Voodoo=20) into a single display using layer ordering. The copper coprocessor can target any video system via SETBASE for per-scanline raster effects.

The custom audio synthesizer is the core of the sound system. PSG, POKEY, and SID registers are mapped to the custom synth, providing authentic register-level compatibility with high-quality 44.1kHz output. File players drive the synth via register writes: some formats (.ay, .sndh, .sid, .sap, .ted, tracker modules) execute embedded CPU code (Z80, M68K, or 6502), while others (.ym, .vgm/vgz) are rendered from parsed register events in Go. VGM files containing SN76489 data (Sega Master System, Game Gear) are automatically converted to AY register writes during parsing.

Bus Layers

  • MachineBus (Bus32/Bus64 interfaces): The global RAM + MMIO backbone shared by all CPUs and peripherals. Total guest RAM is autodetected from the host at boot (host /proc/meminfo minus a per-platform reserve, see memory_sizing.go); bootGuestRAMFromComputed builds the bus via NewMachineBusSized and attaches an mmap-backed Backing for the high-range guest RAM. len(bus.memory) is the legacy direct-slice low compatibility window, NOT the guest RAM size authority — IE64-family modes pin it at 256 MiB and the high Backing carries the full host-scale total. The bus.memory allocator on Linux/darwin uses anonymous mmap (demand-paged, no eager commit); MachineBus.Reset uses madvise so guest reset releases pages instead of touching every byte. Each CPU/profile sees an active visible RAM clamped via ApplyProfileVisibleCeiling. Guest software discovers sizes via the SYSINFO MMIO pairs (SYSINFO_TOTAL_RAM_LO/HI, SYSINFO_ACTIVE_RAM_LO/HI) and IE64 CR_RAM_SIZE_BYTES (read-only, live-read).
  • CPU bus interfaces (Z80Bus, X86Bus, Bus6502): Per-CPU contract shapes that define the read/write/port operations each CPU core expects.
  • CPU bus adapters (Z80BusAdapter, X86BusAdapter, Bus6502Adapter): Translate 8/16-bit CPU address spaces and port I/O into MachineBus calls, handling bank switching and sign extension.
  • Playback buses (SAPPlaybackBus6502, SIDPlaybackBus6502, TEDPlaybackBus6502, sndhPlaybackBus68K, ayPlaybackBusZ80): Standalone lightweight bus implementations for music file playback - provide just enough memory and I/O to run embedded CPU code that drives audio register writes.

2.2 Hardware I/O

All hardware is accessed through memory-mapped registers in the $F0000-$FFFFF range:

Subsystem Address Range Description
Video $F0000-$F049B Display control, copper, blitter, raster, CLUT8 palette, extended blitter
Custom Audio $F0800-$F0B7F Audio control, envelope shape, filter, legacy channels, flex synth
PSG $F0C00-$F0C1C AY-3-8910 registers and file playback
POKEY $F0D00-$F0D1D Atari POKEY registers and SAP playback
SID $F0E00-$F0E2D MOS 6581 registers and SID playback
MOD $F0BC0-$F0BD7 ProTracker .mod player with Amiga filters
Banking $F700-$F7F0 Bank window control (Z80/6502/x86)
VGA $F1000-$F13FF VGA mode, DAC, sequencer, CRTC, palette
File I/O $F2200-$F221F Host filesystem access (read/write); supports byte-level writes for 8-bit CPUs
AROS DOS $F2220-$F225F AROS DOS handler bridge (AROS mode only)
AROS Audio $F2260-$F22AF Paula-compatible 4-channel DMA audio (AROS mode only)
Media Loader $F2300-$F231F Media file auto-detection and loading
Bootstrap HostFS $F23E0-$F23FF Read-only host-backed boot bridge for IntuitionOS bootstrap
Coprocessor $F2340-$F238F Worker CPU lifecycle, async RPC, IRQ control
Coprocessor Ext $F23B0-$F23BF Monitor registers (ring depth, uptime, busy%, reset)

Additionally, VGA uses legacy PC-compatible memory windows:

  • $A0000-$AFFFF: VGA VRAM (64KB graphics memory)
  • $B8000-$BFFFF: VGA Text Buffer (32KB, char+attr pairs)

For 8-bit CPUs (Z80, 6502), addresses are mapped to the 16-bit range $F000-$FFFF or accessed via I/O ports.

3. Memory Map & Hardware Registers (Detailed)

The system's memory layout is designed to provide efficient access to both program space and hardware features while maintaining clear separation between different memory regions.

Address Notation: This document uses both 0x prefix (C-style) and $ prefix (assembly-style) for hexadecimal addresses. The notation varies to match each CPU's assembly dialect: 0x for IE32/IE64/general discussion, $ for M68K and 6502 assembly examples.

Memory Map Overview

0x000000 - 0x000FFF: System vectors (including interrupt vector)
0x001000 - 0x0EFFFF: Program space
0x0F0000 - 0x0F049B: Video registers (copper, blitter, raster, Mode7, extended blitter)
0x0F0700 - 0x0F072F: Terminal/Serial output
0x0F0730 - 0x0F073C: Mouse registers (X, Y, Buttons, Status)
0x0F0740 - 0x0F0748: Scancode registers (Code, Status, Modifiers)
0x0F0750:            RTC_EPOCH (host UTC seconds since Unix epoch)
0x0F0800 - 0x0F080C: Legacy timer alias window (deprecated, no stable MMIO control ABI)
0x0F0820 - 0x0F0834: Filter registers
0x0F0900 - 0x0F0A6F: Legacy synth registers (square/triangle/sine/noise/saw)
0x0F0A80 - 0x0F0B7F: Flexible 4-channel synth registers (preferred)
0x0F0B80 - 0x0F0B91: AHX module player registers
0x0F0BC0 - 0x0F0BD7: MOD player registers (ProTracker .mod playback)
0x0F0BD8 - 0x0F0BEB: WAV player registers (PCM .wav playback)
0x0F0C00 - 0x0F0C0D: PSG registers (AY/YM synthesis)
0x0F0C0E:            PSG+ control register
0x0F0C10 - 0x0F0C1C: PSG playback control (AY/YM/VGM/SNDH; VGM includes SN76489)
0x0F0D00 - 0x0F0D08: POKEY registers (Atari 8-bit audio)
0x0F0D09:            POKEY+ control register
0x0F0D10 - 0x0F0D1D: SAP playback control
0x0F0E00 - 0x0F0E18: SID registers (C64 audio synthesis)
0x0F0E19:            SID+ control register
0x0F0E1A - 0x0F0E1C: SID read-only registers (OSC3, ENV3)
0x0F0E20 - 0x0F0E2D: SID playback control (.SID file playback)
0x0F0E30 - 0x0F0E4C: SID2 registers (multi-SID, voices 4-6)
0x0F0E50 - 0x0F0E6C: SID3 registers (multi-SID, voices 7-9)
0x0F0F00 - 0x0F0F05: TED registers (Plus/4 audio)
0x0F0F10 - 0x0F0F1C: TED playback control
0x0F1000 - 0x0F13FF: VGA registers (IBM VGA emulation)
0x0F2200 - 0x0F221F: File I/O registers (see below)
0x0F2340 - 0x0F238F: Coprocessor MMIO registers
0x0F2390 - 0x0F23AF: Clipboard bridge (AROS mode, see below)
0x0F23B0 - 0x0F23BF: Coprocessor extended monitor registers
0x0A0000 - 0x0AFFFF: VGA VRAM window (Mode 13h/12h)
0x0B8000 - 0x0BFFFF: VGA text buffer
0x100000 - 0x5FFFFF: Video RAM (VRAM_START to VRAM_START + VRAM_SIZE)
0x800000 - 0x1DFFFFF: AROS Fast Memory (22MB, AROS mode only)
0x1E00000 - 0x1FFFFFF: AROS Video RAM (2MB, AROS mode only)
0x200000 - 0x27FFFF: Coprocessor worker region (IE32, 512KB)
0x280000 - 0x2FFFFF: Coprocessor worker region (M68K, 512KB)
0x300000 - 0x30FFFF: Coprocessor worker region (6502, 64KB)
0x310000 - 0x31FFFF: Coprocessor worker region (Z80, 64KB)
0x320000 - 0x39FFFF: Coprocessor worker region (x86, 512KB)
0x3A0000 - 0x41FFFF: Coprocessor worker region (IE64, 512KB)
0x400000 - 0x7FFFFF: User data buffers (coprocessor request/response data)
0x790000 - 0x7917FF: Coprocessor mailbox shared RAM (6KB, ring buffers)

3.1 System Vector Table (0x000000 - 0x000FFF)

The first 4KB of memory is reserved for system vectors. The most important vector is:

0x0000 - 0x0003: Interrupt Service Routine (ISR) vector

When interrupts are enabled via the SEI instruction, the CPU reads this vector to determine the ISR address. Programs must initialise this vector before enabling interrupts:

.org 0x0000
    .word isr_address    ; Store ISR location in vector

3.2 Program Space (0x001000 - onwards)

Programs begin loading at 0x1000, providing:

  • Protection of low memory from accidental overwrites
  • Clear separation from system areas
  • Space for program code and data

3.3 Video Registers (0x0F0000 - 0x0F0077)

0x0F0000: VIDEO_CTRL   - Video system control (0 = disabled, 1 = enabled)
0x0F0004: VIDEO_MODE   - Display mode selection
0x0F0008: VIDEO_STATUS - Video status (read-only, lock-free)
                         bit0 = hasContent (frame has been drawn to)
                         bit1 = inVBlank (safe to draw without flicker)
0x0F000C: COPPER_CTRL  - Copper control (bit0=enable, bit1=reset/rewind)
0x0F0010: COPPER_PTR   - Copper list base address (32-bit)
0x0F0014: COPPER_PC    - Copper program counter (read-only)
0x0F0018: COPPER_STATUS- Copper status (bit0=running, bit1=waiting, bit2=halted)
0x0F001C: BLT_CTRL     - Blitter control (bit0=start, bit1=busy, bit2=irq enable)
0x0F0020: BLT_OP       - Blitter op (copy/fill/line/masked copy/alpha/mode7)
0x0F0024: BLT_SRC      - Blitter source address (32-bit)
0x0F0028: BLT_DST      - Blitter dest address (32-bit)
0x0F002C: BLT_WIDTH    - Blit width (pixels)
0x0F0030: BLT_HEIGHT   - Blit height (pixels)
0x0F0034: BLT_SRC_STRIDE - Source stride (bytes/row)
0x0F0038: BLT_DST_STRIDE - Dest stride (bytes/row)
0x0F003C: BLT_COLOR    - Fill/line color (RGBA)
0x0F0040: BLT_MASK     - Mask address for masked copy (1-bit/pixel)
0x0F0044: BLT_STATUS   - Blitter status (bit0=error)
0x0F0048: VIDEO_RASTER_Y - Raster band start Y
0x0F004C: VIDEO_RASTER_HEIGHT - Raster band height (pixels)
0x0F0050: VIDEO_RASTER_COLOR - Raster band color (RGBA)
0x0F0054: VIDEO_RASTER_CTRL - Raster band control (bit0=draw)
0x0F0058: BLT_MODE7_U0 - Mode7 start U (signed 16.16)
0x0F005C: BLT_MODE7_V0 - Mode7 start V (signed 16.16)
0x0F0060: BLT_MODE7_DU_COL - Mode7 U delta per column pixel (signed 16.16)
0x0F0064: BLT_MODE7_DV_COL - Mode7 V delta per column pixel (signed 16.16)
0x0F0068: BLT_MODE7_DU_ROW - Mode7 U delta per row (signed 16.16)
0x0F006C: BLT_MODE7_DV_ROW - Mode7 V delta per row (signed 16.16)
0x0F0070: BLT_MODE7_TEX_W - Mode7 texture width mask (2^n-1)
0x0F0074: BLT_MODE7_TEX_H - Mode7 texture height mask (2^n-1)
0x0F0078: VIDEO_PAL_INDEX  - CLUT8 palette entry selector (auto-incrementing after data write)
0x0F007C: VIDEO_PAL_DATA   - CLUT8 palette data (0x00RRGGBB, increments index)
0x0F0080: VIDEO_COLOR_MODE - Color mode (0=RGBA32, non-zero=CLUT8 indexed)
0x0F0084: VIDEO_FB_BASE    - Framebuffer base address for indirect access
0x0F0088-0x0F0487: VIDEO_PAL_TABLE - 256 palette entries (direct access, 4 bytes each)
0x0F0488: BLT_FLAGS    - Extended flags: BPP (bits 0-1), draw mode (bits 4-7),
                         JAM1 (bit 8), invert template (bit 9), invert mode (bit 10)
0x0F048C: BLT_FG       - Foreground color for color expansion
0x0F0490: BLT_BG       - Background color for color expansion
0x0F0494: BLT_MASK_MOD - Template row modulo in bytes (color expansion)
0x0F0498: BLT_MASK_SRCX- Starting X bit offset in template (color expansion)

Available Video Modes:
MODE_640x480  = 0x00
MODE_800x600  = 0x01
MODE_1024x768 = 0x02
MODE_1280x960 = 0x03 (default)

Video Compositor: These registers control the VideoChip, which renders as layer 0 in the compositor. The VGA chip (section 3.11) renders as layer 10 on top. Both sources are composited together for final display output. The copper coprocessor can write to either device using the SETBASE instruction (see section 12.6).

Copper lists are stored as little-endian 32-bit words in RAM. The list format is:

  • WAIT: (0<<30) | (y<<12) | x (wait until raster Y/X reached)
  • MOVE: (1<<30) | (regIndex<<16) followed by a 32-bit value word
  • END: (3<<30)

regIndex is (register_address - VIDEO_REG_BASE) / 4, where VIDEO_REG_BASE = 0x0F0000. COPPER_PTR is latched on enable/reset; 8-bit CPUs should write bytes to COPPER_PTR+0..3.

Example (mid-frame mode switch):

; Copper list in RAM
    .long (0 << 30) | (100 << 12) | 0          ; WAIT y=100, x=0
    .long (1 << 30) | (1 << 16)                ; MOVE VIDEO_MODE (index 1)
    .long 0x01                                 ; MODE_800x600
    .long (3 << 30)                            ; END

3.4 CPU Timer State (Internal)

IE32 and IE64 use CPU-internal countdown timers managed by atomic fields on their CPU structs. The timer decrements once per SAMPLE_RATE instructions, can raise interrupts on expiry, and auto-reloads from its period when enabled.

Legacy timer symbols in some include files are retained for compatibility but are deprecated; there is no stable public MMIO timer control ABI in current runtime.

3.5 Sound Registers

Filter Registers (0x0F0820 - 0x0F0834)

0x0F0820: FILTER_CUTOFF     - Filter cutoff frequency (0-255)
0x0F0824: FILTER_RESONANCE  - Filter resonance (0-255)
0x0F0828: FILTER_TYPE       - Filter type selection
0x0F082C: FILTER_MOD_SOURCE - Filter modulation source
0x0F0830: FILTER_MOD_AMOUNT - Modulation depth (0-255)

Legacy Synth Block (0x0F0900 - 0x0F0A6F)

Square Wave Channel (0x0F0900 - 0x0F093F)

0x0F0900: SQUARE_FREQ     - Frequency (16.8 fixed-point Hz, value = Hz * 256)
0x0F0904: SQUARE_VOL      - Volume (0-255)
0x0F0908: SQUARE_CTRL     - Channel control
0x0F090C: SQUARE_DUTY     - Duty cycle control
0x0F0910: SQUARE_SWEEP    - Frequency sweep control
0x0F0922: SQUARE_PWM_CTRL - PWM modulation control
0x0F0930: SQUARE_ATK      - Attack time (ms)
0x0F0934: SQUARE_DEC      - Decay time (ms)
0x0F0938: SQUARE_SUS      - Sustain level (0-255)
0x0F093C: SQUARE_REL      - Release time (ms)

Triangle Wave Channel (0x0F0940 - 0x0F097F)

0x0F0940: TRI_FREQ  - Frequency (16.8 fixed-point Hz, value = Hz * 256)
0x0F0944: TRI_VOL   - Volume control
0x0F0948: TRI_CTRL  - Channel control
0x0F0914: TRI_SWEEP - Frequency sweep control
0x0F0960: TRI_ATK   - Attack time
0x0F0964: TRI_DEC   - Decay time
0x0F0968: TRI_SUS   - Sustain level
0x0F096C: TRI_REL   - Release time

Sine Wave Channel (0x0F0980 - 0x0F09BF)

0x0F0980: SINE_FREQ  - Frequency (16.8 fixed-point Hz, value = Hz * 256)
0x0F0984: SINE_VOL   - Volume control
0x0F0988: SINE_CTRL  - Channel control
0x0F0918: SINE_SWEEP - Frequency sweep control
0x0F0990: SINE_ATK   - Attack time
0x0F0994: SINE_DEC   - Decay time
0x0F0998: SINE_SUS   - Sustain level
0x0F099C: SINE_REL   - Release time

Noise Channel (0x0F09C0 - 0x0F09FF)

0x0F09C0: NOISE_FREQ  - Frequency (16.8 fixed-point Hz, value = Hz * 256)
0x0F09C4: NOISE_VOL   - Volume control
0x0F09C8: NOISE_CTRL  - Channel control
0x0F09D0: NOISE_ATK   - Attack time
0x0F09D4: NOISE_DEC   - Decay time
0x0F09D8: NOISE_SUS   - Sustain level
0x0F09DC: NOISE_REL   - Release time
0x0F09E0: NOISE_MODE  - Noise generation mode

Noise Modes:
NOISE_MODE_WHITE    = 0 // Standard LFSR noise
NOISE_MODE_PERIODIC = 1 // Periodic/looping noise
NOISE_MODE_METALLIC = 2 // "Metallic" noise variant

Sawtooth Wave Channel (0x0F0A20 - 0x0F0A3F)

0x0F0A20: SAW_FREQ  - Frequency (16.8 fixed-point Hz, value = Hz * 256)
0x0F0A24: SAW_VOL   - Volume control
0x0F0A28: SAW_CTRL  - Channel control
0x0F0A2C: SAW_SWEEP - Frequency sweep control
0x0F0A30: SAW_ATK   - Attack time
0x0F0A34: SAW_DEC   - Decay time
0x0F0A38: SAW_SUS   - Sustain level
0x0F0A3C: SAW_REL   - Release time

Channel Modulation Controls (0x0F0A00 - 0x0F0A6F)

0x0F0A00: SYNC_SOURCE_CH0 - Sync source for channel 0
0x0F0A04: SYNC_SOURCE_CH1 - Sync source for channel 1
0x0F0A08: SYNC_SOURCE_CH2 - Sync source for channel 2
0x0F0A0C: SYNC_SOURCE_CH3 - Sync source for channel 3

0x0F0A10: RING_MOD_SOURCE_CH0 - Ring mod source for channel 0
0x0F0A14: RING_MOD_SOURCE_CH1 - Ring mod source for channel 1
0x0F0A18: RING_MOD_SOURCE_CH2 - Ring mod source for channel 2
0x0F0A1C: RING_MOD_SOURCE_CH3 - Ring mod source for channel 3

0x0F0A60: SYNC_SOURCE_CH4     - Sync source for sawtooth channel
0x0F0A64: RING_MOD_SOURCE_CH4 - Ring mod source for sawtooth channel

Global Sound Effects (0x0F0A40 - 0x0F0A54)

0x0F0A40: OVERDRIVE_CTRL - Drive amount (0-255)
0x0F0A50: REVERB_MIX     - Dry/wet mix (0-255)
0x0F0A54: REVERB_DECAY   - Decay time (0-255)

Flexible 4-Channel Synth Block (0x0F0A80 - 0x0F0B7F)

The flexible synth block provides a modern, uniform interface for all four synthesis channels. Each channel occupies 0x40 bytes (64 bytes), supporting any waveform type.

Channel Base Addresses:
  Channel 0: 0x0F0A80
  Channel 1: 0x0F0AC0
  Channel 2: 0x0F0B00
  Channel 3: 0x0F0B40

Per-Channel Register Offsets:
  +0x00: FREQ       - Frequency (16.8 fixed-point Hz, value = Hz * 256)
  +0x04: VOL        - Volume (0-255)
  +0x08: CTRL       - Channel control (bit0=enable, bit1=gate)
  +0x0C: DUTY       - Duty cycle for square/pulse waves
  +0x10: SWEEP      - Frequency sweep control
  +0x14: ATK        - Attack time (ms)
  +0x18: DEC        - Decay time (ms)
  +0x1C: SUS        - Sustain level (0-255)
  +0x20: REL        - Release time (ms)
  +0x24: WAVE_TYPE  - Waveform selection (0=square, 1=triangle, 2=sine, 3=noise, 4=saw)
  +0x28: PWM_CTRL   - PWM modulation control
  +0x2C: NOISEMODE  - Noise mode (for noise waveform)
  +0x30: PHASE      - Reset phase position
  +0x34: RINGMOD    - Ring modulation source (bit7=enable, bits0-2=source channel)
  +0x38: SYNC       - Hard sync source (bit7=enable, bits0-2=source channel)
  +0x3C: DAC        - DAC mode: signed 8-bit sample (bypasses waveform and envelope)

Example: Configure channel 1 as a sawtooth wave at 440Hz:

; Using flexible synth registers
; Frequency is 16.8 fixed-point: 440 Hz * 256 = 112640
LOAD A, #112640
STORE A, @0x0F0AB0      ; CH1 FREQ
LOAD A, #200
STORE A, @0x0F0AB4      ; CH1 VOL
LOAD A, #4
STORE A, @0x0F0AD4      ; CH1 WAVE_TYPE = sawtooth
LOAD A, #1
STORE A, @0x0F0AB8      ; CH1 CTRL = enable

3.6 PSG Sound Chip Registers (0x0F0C00 - 0x0F0C1C)

0x0F0C00: PSG_REG_SELECT   - Register select
0x0F0C01: PSG_REG_DATA     - Register data
0x0F0C02-0x0F0C0D: PSG registers (direct access)
0x0F0C0E: PSG_PLUS_CTRL    - PSG+ mode (0=standard, 1=enhanced)

PSG Playback Control (supports .ym, .ay, .vgm, .vgz, .vtx, .sndh, .pt3, .pt2, .pt1, .stc, .sqt, .asc, .ftc):
0x0F0C10: PSG_PLAY_PTR    - Pointer to music file data in bus memory (32-bit)
0x0F0C14: PSG_PLAY_LEN    - Length of music file data (32-bit)
0x0F0C18: PSG_PLAY_CTRL   - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0C1C: PSG_PLAY_STATUS - Status (bit0=busy, bit1=error)

Embed the file data in your program, set PTR/LEN, then write to CTRL. Format is auto-detected from file headers. Formats using embedded CPU code (.ay, .sndh, tracker modules) execute via internal Z80 or M68K emulation. Register-dump formats (.ym) and timed-event formats (.vgm/.vgz) are rendered natively. VGM files containing SN76489 data are automatically converted to AY register writes.

3.7 POKEY Sound Chip Registers (0x0F0D00 - 0x0F0D1D)

0x0F0D00: POKEY_AUDF1    - Channel 1 frequency divider
0x0F0D01: POKEY_AUDC1    - Channel 1 control (distortion + volume)
0x0F0D02: POKEY_AUDF2    - Channel 2 frequency divider
0x0F0D03: POKEY_AUDC2    - Channel 2 control
0x0F0D04: POKEY_AUDF3    - Channel 3 frequency divider
0x0F0D05: POKEY_AUDC3    - Channel 3 control
0x0F0D06: POKEY_AUDF4    - Channel 4 frequency divider
0x0F0D07: POKEY_AUDC4    - Channel 4 control
0x0F0D08: POKEY_AUDCTL   - Master audio control
0x0F0D09: POKEY_PLUS_CTRL - POKEY+ mode (0=standard, 1=enhanced)

AUDCTL Bit Masks:
bit 0: Use 15kHz base clock (else 64kHz)
bit 1: High-pass filter ch1 by ch3
bit 2: High-pass filter ch2 by ch4
bit 3: Ch4 clocked by ch3 (16-bit mode)
bit 4: Ch2 clocked by ch1 (16-bit mode)
bit 5: Ch3 uses 1.79MHz clock
bit 6: Ch1 uses 1.79MHz clock
bit 7: Use 9-bit poly instead of 17-bit

AUDC Distortion Modes (bits 5-7):
0x00: 17-bit + 5-bit poly
0x20: 5-bit poly only
0x40: 17-bit + 4-bit poly (most metallic)
0x60: 5-bit + 4-bit poly
0x80: 17-bit poly only (white noise)
0xA0: Pure square wave
0xC0: 4-bit poly only (buzzy)
0xE0: 17-bit + pulse

SAP Player Registers (0x0F0D10 - 0x0F0D1D, supports .sap Atari 8-bit music):
0x0F0D10: SAP_PLAY_PTR    - Pointer to .sap file data in bus memory (32-bit)
0x0F0D14: SAP_PLAY_LEN    - Length of .sap file data (32-bit)
0x0F0D18: SAP_PLAY_CTRL   - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0D1C: SAP_PLAY_STATUS - Status (bit0=busy, bit1=error)
0x0F0D1D: SAP_SUBSONG     - Subsong selection (0-255)

SAP TYPE B files are supported: the embedded 6502 INIT routine is called once, then the PLAYER routine is called each frame to drive POKEY registers.

3.8 SID Sound Chip Registers (0x0F0E00 - 0x0F0E2D)

Voice 1 (0x0F0E00 - 0x0F0E06):
0x0F0E00: SID_V1_FREQ_LO  - Frequency low byte
0x0F0E01: SID_V1_FREQ_HI  - Frequency high byte
0x0F0E02: SID_V1_PW_LO    - Pulse width low byte
0x0F0E03: SID_V1_PW_HI    - Pulse width high (bits 0-3)
0x0F0E04: SID_V1_CTRL     - Control register
0x0F0E05: SID_V1_AD       - Attack/Decay
0x0F0E06: SID_V1_SR       - Sustain/Release

Voice 2 (0x0F0E07 - 0x0F0E0D):
0x0F0E07-0x0F0E0D: Same layout as Voice 1

Voice 3 (0x0F0E0E - 0x0F0E14):
0x0F0E0E-0x0F0E14: Same layout as Voice 1

Filter and Volume:
0x0F0E15: SID_FC_LO       - Filter cutoff low (bits 0-2)
0x0F0E16: SID_FC_HI       - Filter cutoff high byte
0x0F0E17: SID_RES_FILT    - Resonance (bits 4-7) + routing (bits 0-3)
0x0F0E18: SID_MODE_VOL    - Volume (bits 0-3) + filter mode (bits 4-7)
0x0F0E19: SID_PLUS_CTRL   - SID+ mode (0=standard, 1=enhanced)
0x0F0E1A: SID_OSC3        - Voice 3 oscillator output (read-only)
0x0F0E1B: SID_ENV3        - Voice 3 envelope output (read-only)
0x0F0E1C: (reserved)

Voice Control Register Bits:
bit 0: Gate (trigger envelope)
bit 1: Sync with previous voice
bit 2: Ring modulation
bit 3: Test bit (resets oscillator)
bit 4: Triangle waveform
bit 5: Sawtooth waveform
bit 6: Pulse waveform
bit 7: Noise waveform

Filter Mode Bits (SID_MODE_VOL bits 4-7):
bit 4: Low-pass filter
bit 5: Band-pass filter
bit 6: High-pass filter
bit 7: Disconnect voice 3 from output

SID Player Registers (0x0F0E20 - 0x0F0E2D, supports .sid C64 music):
0x0F0E20: SID_PLAY_PTR    - Pointer to .sid file data in bus memory (32-bit)
0x0F0E24: SID_PLAY_LEN    - Length of .sid file data (32-bit)
0x0F0E28: SID_PLAY_CTRL   - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0E2C: SID_PLAY_STATUS - Status (bit0=busy, bit1=error)
0x0F0E2D: SID_SUBSONG     - Subsong selection (0-255)

These registers allow CPU code to trigger .SID file playback from RAM, similar to the PSG and SAP player registers. The embedded 6502 code in the SID file is executed by the internal 6502 emulator at the correct frame rate (50Hz PAL or ~60Hz NTSC).

Multi-SID Registers (SID2/SID3)

For .SID files with Sid2Addr/Sid3Addr in PSID v3/v4 headers, the engine instantiates additional SID chips with independent register sets. Each secondary SID maps to 3 SoundChip channels (SID2 = channels 4-6, SID3 = channels 7-9). The SID model (6581/8580) is extracted per-chip from the header flags.

SID2 Registers (0x0F0E30 - 0x0F0E4C): Same layout as primary SID (voices 4-6)
SID3 Registers (0x0F0E50 - 0x0F0E6C): Same layout as primary SID (voices 7-9)

3.9 TED Sound Chip Registers (0x0F0F00 - 0x0F0F1F)

The TED (Text Editing Device) chip from the Commodore Plus/4 provides simple 2-voice square wave synthesis:

TED Sound Registers (0x0F0F00 - 0x0F0F05):
0x0F0F00: TED_FREQ1_LO   - Voice 1 frequency low byte
0x0F0F01: TED_FREQ2_LO   - Voice 2 frequency low byte
0x0F0F02: TED_FREQ2_HI   - Voice 2 frequency high (bits 0-1)
0x0F0F03: TED_SND_CTRL   - Control (DA/noise/ch2on/ch1on/volume)
0x0F0F04: TED_FREQ1_HI   - Voice 1 frequency high (bits 0-1)
0x0F0F05: TED_PLUS_CTRL  - TED+ enhanced audio mode (0=standard, 1=enhanced)

TED_SND_CTRL bits:
  Bit 7: D/A mode
  Bit 6: Voice 2 noise enable (replaces square wave with white noise)
  Bit 5: Voice 2 enable
  Bit 4: Voice 1 enable
  Bits 0-3: Volume (0-8, where 8 is maximum)

TED Player Registers (0x0F0F10 - 0x0F0F1C, supports .ted/.prg Plus/4 music):
0x0F0F10: TED_PLAY_PTR    - Pointer to .ted/.prg file data in bus memory (32-bit)
0x0F0F14: TED_PLAY_LEN    - Length of file data (32-bit)
0x0F0F18: TED_PLAY_CTRL   - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0F1C: TED_PLAY_STATUS - Status (bit0=busy, bit1=error)

Frequency formula: freq_hz = clock/8 / (1024 - register_value) Clock rates: 886724 Hz (PAL), 894886 Hz (NTSC)

These registers allow CPU code to trigger .TED file playback from RAM. The embedded 6502 code in the TED file is executed by the internal 6502 emulator at 50Hz (PAL).

3.10 TED Video Chip Registers (0x0F0F20 - 0x0F0F5F)

The TED chip also provides video capabilities from the Commodore Plus/4:

  • 40x25 text mode (8x8 character cells)
  • 320x200 pixel resolution (384x272 with border)
  • 121 colors (16 hues × 8 luminances)
  • Hardware cursor support
  • Compositor layer 12 (between VGA=10 and ULA=15)

Current limitations: Only text mode is implemented. Control register bits for bitmap mode (BMM), extended color mode (ECM), multicolor mode (MCM), fine scrolling (XSCROLL/YSCROLL), row/column select (RSEL/CSEL), and character/video base address registers are accepted but have no effect on rendering.

TED Video Registers (0x0F0F20 - 0x0F0F5F, 4-byte aligned for copper):
0x0F0F20: TED_V_CTRL1      - Control 1 (ECM/BMM/DEN/RSEL/YSCROLL)
0x0F0F24: TED_V_CTRL2      - Control 2 (RES/MCM/CSEL/XSCROLL)
0x0F0F28: TED_V_CHAR_BASE  - Character/bitmap base address
0x0F0F2C: TED_V_VIDEO_BASE - Video matrix base address
0x0F0F30: TED_V_BG_COLOR0  - Background color 0
0x0F0F34: TED_V_BG_COLOR1  - Background color 1 (multicolor)
0x0F0F38: TED_V_BG_COLOR2  - Background color 2 (multicolor)
0x0F0F3C: TED_V_BG_COLOR3  - Background color 3 (multicolor)
0x0F0F40: TED_V_BORDER     - Border color
0x0F0F44: TED_V_CURSOR_HI  - Cursor position high byte
0x0F0F48: TED_V_CURSOR_LO  - Cursor position low byte
0x0F0F4C: TED_V_CURSOR_CLR - Cursor color
0x0F0F50: TED_V_RASTER_LO  - Raster line low (read-only)
0x0F0F54: TED_V_RASTER_HI  - Raster line high (read-only)
0x0F0F58: TED_V_ENABLE     - Video enable (bit 0)
0x0F0F5C: TED_V_STATUS     - Status (bit 0 = VBlank)

TED_V_CTRL1 bits:
  Bit 6: ECM - Extended Color Mode
  Bit 5: BMM - Bitmap Mode
  Bit 4: DEN - Display Enable
  Bit 3: RSEL - Row Select (0=24 rows, 1=25 rows)
  Bits 0-2: YSCROLL - Vertical scroll (0-7)

TED_V_CTRL2 bits:
  Bit 5: RES - Reset
  Bit 4: MCM - Multicolor Mode
  Bit 3: CSEL - Column Select (0=38 cols, 1=40 cols)
  Bits 0-2: XSCROLL - Horizontal scroll (0-7)

Color byte format: Bits 4-6 = luminance (0-7), Bits 0-3 = hue (0-15)

TED Video CPU Mappings:

  • IE32/IE64/M68K: Direct access at 0x0F0F20-0x0F0F5F
  • 6502: Memory-mapped at $D620-$D62F
  • Z80: Port I/O via 0xF2/0xF3, indices 0x20-0x2F

3.11 AHX Module Player Registers (0x0F0B80 - 0x0F0B91)

The AHX engine provides Amiga AHX/THX module playback with 4-channel waveform synthesis:

AHX Control Registers (0x0F0B80):
0x0F0B80: AHX_PLUS_CTRL  - AHX+ enhanced audio mode (0=standard, 1=enhanced)

AHX Player Registers (0x0F0B84 - 0x0F0B91):
0x0F0B84: AHX_PLAY_PTR    - Pointer to .AHX data (32-bit)
0x0F0B88: AHX_PLAY_LEN    - Length of .AHX data (32-bit)
0x0F0B8C: AHX_PLAY_CTRL   - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0B90: AHX_PLAY_STATUS - Status (bit0=busy, bit1=error)
0x0F0B91: AHX_SUBSONG     - Subsong selection (0-255)

AHX+ mode provides enhanced audio processing:

  • 4x oversampling for cleaner waveforms
  • Second-order Butterworth lowpass (biquad, -12dB/oct anti-alias)
  • Subtle saturation (drive 0.16) for analog warmth
  • Allpass diffuser room ambience (mix 0.09, delay 120 samples)
  • Authentic Amiga stereo panning (L-R-R-L pattern)
  • Hardware PWM mapping SquarePos to duty cycle

3.12 MOD Player Registers (0x0F0BC0 - 0x0F0BD7)

The MOD player provides ProTracker .mod file playback using DAC mode on the SoundChip FLEX channels, with optional Amiga-style low-pass filter emulation:

MOD Player Registers (0x0F0BC0 - 0x0F0BD7):
0x0F0BC0: MOD_PLAY_PTR     - Pointer to MOD data in bus memory (32-bit)
0x0F0BC4: MOD_PLAY_LEN     - Length of MOD data (32-bit)
0x0F0BC8: MOD_PLAY_CTRL    - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0BCC: MOD_PLAY_STATUS  - Status (bit0=busy, bit1=error)
0x0F0BD0: MOD_FILTER_MODEL - Filter model (0=none, 1=A500 4.5kHz, 2=A1200 28kHz)
0x0F0BD4: MOD_POSITION     - Current song position (read-only)

Filter models emulate the original Amiga hardware low-pass characteristics:

  • 0 (None): No filtering, raw output
  • 1 (A500): Amiga 500 RC filter at ~4.5kHz cutoff with LED filter (~3.3kHz 2-pole Butterworth)
  • 2 (A1200): Amiga 1200 RC filter at ~28kHz cutoff with LED filter (~3.3kHz 2-pole Butterworth)

3.12b WAV Player Registers (0x0F0BD8 - 0x0F0BEB)

The WAV player provides PCM .wav file playback via the SoundChip FLEX DAC mode. Supports 8-bit unsigned and 16-bit signed PCM; stereo files are automatically downmixed to mono. Sample rate conversion uses linear interpolation.

WAV Player Registers (0x0F0BD8 - 0x0F0BEB):
0x0F0BD8: WAV_PLAY_PTR    - Pointer to WAV data in bus memory (32-bit)
0x0F0BDC: WAV_PLAY_LEN    - Length of WAV data (32-bit)
0x0F0BE0: WAV_PLAY_CTRL   - Control (bit0=start, bit1=stop, bit2=loop)
0x0F0BE4: WAV_PLAY_STATUS - Status (bit0=busy, bit1=error)
0x0F0BE8: WAV_POSITION    - Current playback position (read-only)

Playback Control:

  • Write 1 to WAV_PLAY_CTRL to start, 2 to stop, 5 to start with loop
  • Read WAV_PLAY_STATUS for busy/error flags
  • The entire WAV file must fit in bus memory (not streaming)

3.13 Hardware I/O Memory Map by CPU

All sound and video chips are accessible from all CPU architectures at different address ranges:

Sound Chips

Chip IE32/IE64/M68K Z80 x86 6502 Notes
Custom Synth 0x0F0820-0x0F0B7F Memory $F820-$FB7F Memory $F820-$FB7F Filter, legacy channels, flex channels
AHX 0x0F0B80-0x0F0B91 Memory $FB80-$FB91 Memory $FB80-$FB91 .ahx module player
MOD 0x0F0BC0-0x0F0BD7 Memory $FBC0-$FBD7 Memory $FBC0-$FBD7 .mod ProTracker player
WAV 0x0F0BD8-0x0F0BEB Memory $FBD8-$FBEB Memory $FBD8-$FBEB .wav PCM player
PSG 0x0F0C00-0x0F0C1C Ports 0xF0/0xF1 + Memory $FC10-$FC1C Ports 0xF0/0xF1 $D400-$D41C Synth + .ym/.ay/.vgm/.vgz/.vtx/.sndh/.pt3/.pt2/.pt1/.stc/.sqt/.asc/.ftc player
POKEY 0x0F0D00-0x0F0D1D Ports 0xD0/0xD1 + Memory $FD10-$FD1D Ports 0xD0-0xD3* $D200-$D21D Synth + .sap player
SID 0x0F0E00-0x0F0E6C Ports 0xE0/0xE1 + Memory $FE20-$FE6C Ports 0xE0/0xE1 $D500-$D56C Synth + .sid player + SID2/SID3 multi-SID
TED 0x0F0F00-0x0F0F1C Ports 0xF2/0xF3 + Memory $FF10-$FF1C Ports 0xF2/0xF3 $D600-$D61C Synth + .ted/.prg player

* x86 POKEY uses ports 0xD0-0xD3 and 0xD8-0xDF (0xD4-0xD7 reserved for ANTIC/GTIA)

Z80 and x86 access the custom synth and player control registers via memory-mapped I/O: addresses $F000-$FFFF translate to bus $F0000-$F0FFF. Classic sound chip synthesis registers (PSG, POKEY, SID, TED) use dedicated port I/O for register select/data access; their playback control registers use memory-mapped I/O.

Video Chips

Chip IE32/IE64/M68K Z80 x86 6502 Notes
VideoChip 0x0F0000-0x0F0077 Memory $F000-$F077 Memory $F000-$F077 Custom copper/blitter
TED Video 0x0F0F20-0x0F0F5F Ports 0xF2/0xF3 Ports 0xF2/0xF3 $D620-$D62F Plus/4 (idx 0x20-0x2F)
VGA 0x0F1000-0x0F13FF Ports 0xA0-0xAC Ports 0x3C4-0x3DA $D700-$D70A IBM VGA compatible
Voodoo 0x0F4000-0x0F43FF Ports 0xB0-0xB7 Memory Memory 3DFX SST-1 3D accelerator
ANTIC 0x0F2100-0x0F213F Ports 0xD4/0xD5 Ports 0xD4/0xD5 $D400-$D40F Atari 8-bit video
GTIA 0x0F2140-0x0F21B7 Ports 0xD6/0xD7 Ports 0xD6/0xD7 $D000-$D01F Atari 8-bit color + P/M
ULA 0x0F2000-0x0F200B Port 0xFE Port 0xFE $D800-$D80B ZX Spectrum compatible

Note: 6502 has PSG at $D400 which overlaps with ANTIC's authentic Atari address. Use M68K/Z80/x86 for ANTIC access when PSG is in use.

Access Methods

Z80 Port I/O: The first port selects the register index, the second reads/writes data. Example: OUT (0xF0),A selects PSG register, OUT (0xF1),A writes data.

6502 Memory-Mapped: Direct memory access following C64/Atari/Plus4 conventions. Example: STA $D400 writes to PSG register 0.

IE32/IE64/M68K Direct: Full 32-bit address space access. Example: MOVE.B D0,($F0C00).L writes to PSG register 0.

3.14 VGA Video Chip (0x0F1000 - 0x0F13FF)

The VGA chip provides IBM PC-compatible graphics modes, allowing classic PC demo effects and games:

Supported Modes

Mode Resolution Colors Type Description
0x03 80×25 16 Text Standard VGA text mode with 8×16 font
0x12 640×480 16 Planar High-res 4-plane graphics
0x13 320×200 256 Linear Classic "Mode 13h" for demos/games
0x14 320×240 256 Mode-X Unchained planar for page flipping

Register Map

VGA Control Registers (0x0F1000 - 0x0F100F):
0x0F1000: VGA_MODE        - Mode select (0x03/0x12/0x13/0x14)
0x0F1004: VGA_STATUS      - Status (bit 0=vsync, bit 3=retrace)
0x0F1008: VGA_CTRL        - Control (bit 0=enable)

Sequencer Registers (0x0F1010 - 0x0F101F):
0x0F1010: VGA_SEQ_INDEX   - Sequencer register index
0x0F1014: VGA_SEQ_DATA    - Sequencer register data
0x0F1018: VGA_SEQ_MAPMASK - Plane write mask (direct access)

CRTC Registers (0x0F1020 - 0x0F102F):
0x0F1020: VGA_CRTC_INDEX  - CRTC register index
0x0F1024: VGA_CRTC_DATA   - CRTC register data
0x0F1028: VGA_CRTC_STARTHI - Display start address high
0x0F102C: VGA_CRTC_STARTLO - Display start address low

Graphics Controller (0x0F1030 - 0x0F103F):
0x0F1030: VGA_GC_INDEX    - Graphics controller index
0x0F1034: VGA_GC_DATA     - Graphics controller data
0x0F1038: VGA_GC_READMAP  - Read plane select
0x0F103C: VGA_GC_BITMASK  - Bit mask for write operations

Attribute Controller (0x0F1040 - 0x0F104F):
0x0F1040: VGA_ATTR_INDEX  - Attribute index/data
0x0F1044: VGA_ATTR_DATA   - Attribute read

DAC/Palette (0x0F1050 - 0x0F105F):
0x0F1050: VGA_DAC_MASK    - Pixel mask (default 0xFF)
0x0F1054: VGA_DAC_RINDEX  - Read index
0x0F1058: VGA_DAC_WINDEX  - Write index
0x0F105C: VGA_DAC_DATA    - DAC data (R,G,B sequence, 6-bit values)

Palette RAM (0x0F1100 - 0x0F13FF):
0x0F1100: VGA_PALETTE     - 256 palette entries × 3 bytes (768 bytes)

VRAM Windows

0x0A0000 - 0x0AFFFF: VGA VRAM (64KB graphics memory)
0x0B8000 - 0x0BFFFF: VGA Text Buffer (32KB, char+attr pairs)

CPU Address Mappings

CPU VGA Registers VGA VRAM DAC Access
IE32/IE64/M68K 0x0F1000-0x0F13FF 0x0A0000-0x0AFFFF Memory-mapped
Z80 Ports 0xA0-0xAC Memory 0xA000 (banked) Port I/O
6502 $D700-$D70D $A000 (banked) Memory-mapped

Mode 13h Example (M68K)

    include "ie68.inc"

    ; Set Mode 13h (320x200x256)
    vga_setmode VGA_MODE_13H

    ; Set palette entry 1 to bright red
    move.b  #1,VGA_DAC_WINDEX    ; Select color 1
    move.b  #63,VGA_DAC_DATA     ; R = 63 (max)
    move.b  #0,VGA_DAC_DATA      ; G = 0
    move.b  #0,VGA_DAC_DATA      ; B = 0

    ; Draw pixel at (100, 50) = offset 100 + 50*320 = 16100
    move.b  #1,$A0000+16100      ; Write color 1

Mode 12h Planar Example (M68K)

    include "ie68.inc"

    ; Set Mode 12h (640x480x16 planar)
    vga_setmode VGA_MODE_12H

    ; Enable plane 0 only for writing
    move.b  #1,VGA_SEQ_MAPMASK

    ; Write to VRAM (affects only plane 0)
    move.b  #$FF,$A0000          ; Set 8 pixels in plane 0

Video Compositor Integration

The VGA chip integrates with the video compositor as a separate layer (layer 10) that renders on top of the VideoChip (layer 0). Both sources are blended together for the final display output.

Per-Scanline Rendering: The VGA supports scanline-aware rendering, meaning the copper coprocessor can modify VGA palette registers on a per-scanline basis. When the copper executes a SETBASE to target VGA DAC registers followed by palette MOVE operations, those changes affect VGA rendering from that scanline onward. This enables classic PC demo effects like raster bars and gradient backgrounds.

VSync Timing: The VGA provides time-based vsync status through VGA_STATUS. The status bit automatically calculates whether the display is in vertical retrace based on a 60Hz refresh cycle, requiring no explicit signaling from the compositor.

See section 12.9 (Video Compositor) for details on the compositing architecture and section 12.6 (Copper List Executor) for examples of copper-driven VGA palette manipulation.

3.15 ULA Video Chip - ZX Spectrum (0x0F2000 - 0x0F200B)

The ULA chip provides authentic ZX Spectrum video output, enabling classic Spectrum demos and games:

Display Specifications

Feature Value
Resolution 256×192 pixels
Border 32 pixels each side (320×256 total)
Color Cells 32×24 (8×8 pixels per cell)
Colors 15 unique (8 base + 8 bright, black can't brighten)
VRAM 6912 bytes (6144 bitmap + 768 attributes)
Flash Rate ~1.6Hz (toggle every 32 frames at 50Hz)

Register Map

ULA Control Registers (0x0F2000 - 0x0F200B):
0x0F2000: ULA_BORDER      - Border color (bits 0-2, values 0-7)
0x0F2004: ULA_CTRL        - Control (bit 0=enable)
0x0F2008: ULA_STATUS      - Status (bit 0=vblank)

VRAM Layout

The ULA uses the authentic ZX Spectrum memory layout at 0x4000:

0x4000 - 0x57FF: Bitmap (6144 bytes, non-linear Y addressing)
0x5800 - 0x5AFF: Attributes (768 bytes, 32×24 cells)

Non-Linear Bitmap Addressing

The ZX Spectrum uses a peculiar addressing formula for the bitmap. The screen is divided into three 64-line sections, with each section having interleaved line ordering:

Address = ((y & 0xC0) << 5) + ((y & 0x07) << 8) + ((y & 0x38) << 2) + (x >> 3)
Y Range Address Range Description
0-63 0x4000-0x47FF Top third of screen
64-127 0x4800-0x4FFF Middle third
128-191 0x5000-0x57FF Bottom third

Attribute Format

Each attribute byte controls an 8×8 pixel cell:

Bit 7: FLASH   - Swap INK/PAPER at ~1.6Hz
Bit 6: BRIGHT  - Intensify both INK and PAPER
Bits 5-3: PAPER (background color, 0-7)
Bits 2-0: INK   (foreground color, 0-7)

Color Palette

Index Normal RGB Bright RGB Color
0 (0,0,0) (0,0,0) Black
1 (0,0,205) (0,0,255) Blue
2 (205,0,0) (255,0,0) Red
3 (205,0,205) (255,0,255) Magenta
4 (0,205,0) (0,255,0) Green
5 (0,205,205) (0,255,255) Cyan
6 (205,205,0) (255,255,0) Yellow
7 (205,205,205) (255,255,255) White

CPU Address Mappings

CPU ULA Registers ULA VRAM Notes
IE32/IE64/M68K 0x0F2000-0x0F200B 0x4000-0x5AFF Direct 32-bit
Z80 Port 0xFE 0x4000-0x5AFF Authentic Spectrum
6502 $D800-$D80F $4000 (banked) Memory-mapped

Example: Drawing a Pixel (M68K)

    include "ie68.inc"

    ; Draw white pixel at (128, 96) with bright attribute
    ; First calculate bitmap address for y=96, x=128
    ; ((96 & $C0) << 5) + ((96 & $07) << 8) + ((96 & $38) << 2) + (128 >> 3)
    ; = ($40 << 5) + ($00 << 8) + ($00 << 2) + 16
    ; = $800 + $0 + $0 + $10 = $810

    move.b  #$80,ULA_VRAM+$810   ; Set bit 7 (leftmost pixel in byte)

    ; Set attribute for cell at (16, 12) = offset 12*32+16 = 400 = $190
    ; Attribute: BRIGHT=1, PAPER=0 (black), INK=7 (white) = $47
    move.b  #$47,ULA_VRAM+ULA_ATTR_OFFSET+$190

    ; Set border to blue
    ula_border 1

Example: Clear Screen (Z80)

    include "ie80.inc"

    ; Clear bitmap to zeros (all paper color)
    ld hl,ULA_VRAM
    ld de,ULA_VRAM+1
    ld bc,ULA_BITMAP_SIZE-1
    ld (hl),0
    ldir

    ; Set all attributes to white ink on blue paper
    ; Attribute: BRIGHT=0, PAPER=1 (blue), INK=7 (white) = $0F
    ld hl,ULA_ATTR_BASE
    ld de,ULA_ATTR_BASE+1
    ld bc,ULA_ATTR_SIZE-1
    ld (hl),$0F
    ldir

    ; Set border to blue
    ULA_SET_BORDER 1

Video Compositor Integration

The ULA integrates with the video compositor as layer 15, rendering above both the VideoChip (layer 0) and VGA (layer 10). This allows ZX Spectrum graphics to overlay other video sources.

The ULA provides its own frame timing through SignalVSync(), which handles the FLASH attribute timing (toggling every 32 frames). When disabled via ULA_CTRL, the chip returns nil frames to the compositor.

3.16 ANTIC Video Chip - Atari 8-bit (0x0F2100 - 0x0F213F)

The ANTIC (Alphanumeric Television Interface Controller) provides authentic Atari 8-bit computer video output, enabling classic Atari demos and games:

Display Specifications

Feature Value
Resolution 320×192 pixels
Border 32 pixels horizontal, 24 pixels vertical (384×240 total)
Colors 128 (16 hues × 8 luminances)
Display Modes 14 (text and graphics modes via display list)
Fine Scrolling 4-bit horizontal/vertical (0-15 pixels)

Register Map (IE32/IE64/M68K/x86)

All registers are 4-byte aligned for copper coprocessor compatibility:

ANTIC Registers (0x0F2100 - 0x0F213F):
0x0F2100: ANTIC_DMACTL    - DMA control (playfield width, DMA enables)
0x0F2104: ANTIC_CHACTL    - Character control (inverse, reflect)
0x0F2108: ANTIC_DLISTL    - Display list pointer low byte
0x0F210C: ANTIC_DLISTH    - Display list pointer high byte
0x0F2110: ANTIC_HSCROL    - Horizontal scroll (0-15)
0x0F2114: ANTIC_VSCROL    - Vertical scroll (0-15)
0x0F2118: ANTIC_PMBASE    - Player-missile base address (high byte)
0x0F211C: ANTIC_CHBASE    - Character set base address (high byte)
0x0F2120: ANTIC_WSYNC     - Wait for horizontal sync (write only)
0x0F2124: ANTIC_VCOUNT    - Vertical line counter (read only, /2)
0x0F2128: ANTIC_PENH      - Light pen horizontal (read only)
0x0F212C: ANTIC_PENV      - Light pen vertical (read only)
0x0F2130: ANTIC_NMIEN     - NMI enable register
0x0F2134: ANTIC_NMIST     - NMI status (read) / NMIRES (write)
0x0F2138: ANTIC_ENABLE    - Video enable (IE-specific)
0x0F213C: ANTIC_STATUS    - Status (VBlank flag, IE-specific)

6502 Register Map (Atari Authentic)

For 6502 compatibility, ANTIC uses authentic Atari addresses at 0xD400:

0xD400: DMACTL    0xD401: CHACTL    0xD402: DLISTL    0xD403: DLISTH
0xD404: HSCROL    0xD405: VSCROL    0xD407: PMBASE    0xD409: CHBASE
0xD40A: WSYNC     0xD40B: VCOUNT    0xD40C: PENH      0xD40D: PENV
0xD40E: NMIEN     0xD40F: NMIST

DMACTL Register Bits

Bit Name Description
0-1 Playfield 00=off, 01=narrow, 10=normal, 11=wide
2 Missile DMA Enable missile graphics DMA
3 Player DMA Enable player graphics DMA
4 PM Resolution 0=double-line, 1=single-line
5 DL Enable Enable display list DMA

Color System

ANTIC uses a 128-color palette with 16 hues and 8 luminance levels:

Hue Color Hue Color
0 Gray 8 Blue-2
1 Gold 9 Light Blue
2 Orange A Turquoise
3 Red-Orange B Green-Blue
4 Pink C Green
5 Purple D Yellow-Green
6 Purple-Blue E Orange-Green
7 Blue F Light Orange

Color format: HHHHLLLL where HHHH = hue (0-15), LLLL = luminance (0-15, but only 0-F used).

Display List

ANTIC is driven by a display list - a program that specifies what to render on each scanline. Similar to the Amiga's copper coprocessor, the display list is a sequence of instructions that control video output.

Blank Line Instructions

Opcode Constant Scanlines
0x00 DL_BLANK1 1 blank scanline
0x10 DL_BLANK2 2 blank scanlines
0x20 DL_BLANK3 3 blank scanlines
0x30 DL_BLANK4 4 blank scanlines
0x40 DL_BLANK5 5 blank scanlines
0x50 DL_BLANK6 6 blank scanlines
0x60 DL_BLANK7 7 blank scanlines
0x70 DL_BLANK8 8 blank scanlines

Jump Instructions

Opcode Constant Description
0x01 DL_JMP Jump to address (2 bytes follow)
0x41 DL_JVB Jump and wait for Vertical Blank

Graphics Mode Instructions

Opcode Constant Description
0x02 DL_MODE2 40 column text, 8 scanlines/row
0x03 DL_MODE3 40 column text, 10 scanlines/row
0x04 DL_MODE4 40 column text, 8 scanlines, multicolor
0x05 DL_MODE5 40 column text, 16 scanlines, multicolor
0x06 DL_MODE6 20 column text, 8 scanlines
0x07 DL_MODE7 20 column text, 16 scanlines
0x08 DL_MODE8 40 pixels, 8 scanlines/row (GRAPHICS 3)
0x09 DL_MODE9 80 pixels, 4 scanlines (GRAPHICS 4)
0x0A DL_MODE10 80 pixels, 2 scanlines (GRAPHICS 5)
0x0B DL_MODE11 160 pixels, 1 scanline (GRAPHICS 6)
0x0C DL_MODE12 160 pixels, 1 scanline (GRAPHICS 6+)
0x0D DL_MODE13 160 pixels, 2 scanlines (GRAPHICS 7)
0x0E DL_MODE14 160 pixels, 1 scanline, 4 colors
0x0F DL_MODE15 320 pixels, 1 scanline (GRAPHICS 8)

Instruction Modifiers (OR with mode)

Value Constant Description
0x40 DL_LMS Load Memory Scan (2 address bytes follow)
0x80 DL_DLI Display List Interrupt at end of line
0x10 DL_HSCROL Enable horizontal fine scrolling
0x20 DL_VSCROL Enable vertical fine scrolling

Example Display List (x86)

display_list:
    db DL_BLANK8                    ; 8 blank lines (top border)
    db DL_BLANK8                    ; 8 more blank lines
    db DL_BLANK8                    ; 8 more blank lines
    db DL_MODE2 | DL_LMS            ; Mode 2 text with LMS
    dw screen_memory                ; Screen memory address
    times 23 db DL_MODE2            ; 23 more mode 2 lines
    db DL_JVB                       ; Jump and wait for VBlank
    dw display_list                 ; Loop back to start

Video Compositor Integration

ANTIC integrates with the video compositor as layer 13, positioned between TED (layer 12) and ULA (layer 15). All copper commands can target ANTIC registers via SETBASE for per-scanline effects.

Example: Basic Setup (M68K)

    include "ie68.inc"

    ; Enable ANTIC with normal playfield and display list DMA
    move.b  #ANTIC_DMA_NORMAL|ANTIC_DMA_DL,ANTIC_DMACTL

    ; Point to display list
    lea     my_dlist,a0
    move.b  a0,ANTIC_DLISTL
    lsr.w   #8,d0
    move.b  d0,ANTIC_DLISTH

    ; Enable ANTIC video output
    antic_enable

    ; Wait for VBlank
    antic_wait_vblank

Example: WSYNC Raster Effect (6502)

    include "ie65.inc"

    ; Create color bars using WSYNC timing
    ldx #0
loop:
    stx GTIA_COLBK      ; Set background color via GTIA
    sta ANTIC_WSYNC     ; Wait for horizontal sync
    inx
    bne loop

3.17 GTIA Color Control (0x0F2140 - 0x0F216F)

The GTIA (Graphics Television Interface Adapter) companion chip handles color generation and player-missile graphics for Atari 8-bit systems. While ANTIC controls display timing and the display list, GTIA controls all color output.

Register Map (IE32/IE64/M68K/x86)

All registers are 4-byte aligned for copper coprocessor compatibility:

GTIA Registers (0x0F2140 - 0x0F21B7):
0x0F2140: GTIA_COLPF0   - Playfield color 0
0x0F2144: GTIA_COLPF1   - Playfield color 1
0x0F2148: GTIA_COLPF2   - Playfield color 2
0x0F214C: GTIA_COLPF3   - Playfield color 3
0x0F2150: GTIA_COLBK    - Background/border color
0x0F2154: GTIA_COLPM0   - Player/missile 0 color
0x0F2158: GTIA_COLPM1   - Player/missile 1 color
0x0F215C: GTIA_COLPM2   - Player/missile 2 color
0x0F2160: GTIA_COLPM3   - Player/missile 3 color
0x0F2164: GTIA_PRIOR    - Priority and GTIA modes
0x0F2168: GTIA_GRACTL   - Graphics control (bit 1=players, bit 0=missiles)
0x0F216C: GTIA_CONSOL   - Console switches (read only)
0x0F2170: GTIA_HPOSP0   - Player 0 horizontal position
0x0F2174: GTIA_HPOSP1   - Player 1 horizontal position
0x0F2178: GTIA_HPOSP2   - Player 2 horizontal position
0x0F217C: GTIA_HPOSP3   - Player 3 horizontal position
0x0F2180: GTIA_HPOSM0   - Missile 0 horizontal position
0x0F2184: GTIA_HPOSM1   - Missile 1 horizontal position
0x0F2188: GTIA_HPOSM2   - Missile 2 horizontal position
0x0F218C: GTIA_HPOSM3   - Missile 3 horizontal position
0x0F2190: GTIA_SIZEP0   - Player 0 size (0=normal, 1=double, 3=quad)
0x0F2194: GTIA_SIZEP1   - Player 1 size
0x0F2198: GTIA_SIZEP2   - Player 2 size
0x0F219C: GTIA_SIZEP3   - Player 3 size
0x0F21A0: GTIA_SIZEM    - Missile sizes (2 bits each)
0x0F21A4: GTIA_GRAFP0   - Player 0 graphics (8 pixels)
0x0F21A8: GTIA_GRAFP1   - Player 1 graphics
0x0F21AC: GTIA_GRAFP2   - Player 2 graphics
0x0F21B0: GTIA_GRAFP3   - Player 3 graphics
0x0F21B4: GTIA_GRAFM    - Missile graphics (2 bits each)

6502 Register Map (Atari Authentic)

For 6502 compatibility, GTIA uses authentic Atari addresses at 0xD000:

Player/Missile Position and Size:
0xD000: HPOSP0    0xD001: HPOSP1    0xD002: HPOSP2    0xD003: HPOSP3
0xD004: HPOSM0    0xD005: HPOSM1    0xD006: HPOSM2    0xD007: HPOSM3
0xD008: SIZEP0    0xD009: SIZEP1    0xD00A: SIZEP2    0xD00B: SIZEP3
0xD00C: SIZEM     0xD00D: GRAFP0    0xD00E: GRAFP1    0xD00F: GRAFP2
0xD010: GRAFP3    0xD011: GRAFM

Color and Control:
0xD012: COLPM0    0xD013: COLPM1    0xD014: COLPM2    0xD015: COLPM3
0xD016: COLPF0    0xD017: COLPF1    0xD018: COLPF2    0xD019: COLPF3
0xD01A: COLBK     0xD01B: PRIOR     0xD01D: GRACTL    0xD01F: CONSOL

Color Format

Colors use the ANTIC 128-color palette format:

Bits Field Description
7-4 Hue 16 hues (0=gray, 1-15=chromatic)
3-0 Luminance 8 levels (only even values 0-14 used)

PRIOR Register Bits

The PRIOR register controls display priority and special GTIA modes:

Bit Name Description
0-3 Priority Player/playfield priority selection
4 Multicolor Enable 5th player (missiles as single player)
5 Fifth Enable multicolor players
6-7 GTIA Mode 00=normal, 01=16 lum, 10=9 color, 11=16 hue

Example: Raster Bars (x86)

    %include "ie86.inc"

    ; Create smooth rainbow raster bars
    mov ecx, 192            ; 192 visible lines
.raster_loop:
    mov byte [ANTIC_WSYNC], 0   ; Wait for HSYNC
    mov eax, ecx
    shl eax, 4              ; Scale line to hue
    and eax, 0xF0           ; Mask hue bits
    or eax, 0x08            ; Medium luminance
    mov byte [GTIA_COLBK], al
    loop .raster_loop

Example: Set Playfield Colors (M68K)

    include "ie68.inc"

    ; Set up a typical Atari display palette
    move.b  #$94,GTIA_COLPF0    ; Light blue
    move.b  #$0F,GTIA_COLPF1    ; White
    move.b  #$C6,GTIA_COLPF2    ; Green
    move.b  #$46,GTIA_COLPF3    ; Red
    move.b  #$00,GTIA_COLBK     ; Black background

3.18 File I/O Device (0x0F2200 - 0x0F221F)

The File I/O device provides sandboxed host filesystem access. Programs can read and write files within the engine's working directory.

MMIO Registers

Address Name R/W Description
$F2200 FILE_NAME_PTR W Pointer to null-terminated filename in bus memory
$F2204 FILE_DATA_PTR W Pointer to data buffer in bus memory
$F2208 FILE_DATA_LEN W Data length in bytes (for WRITE operations)
$F220C FILE_CTRL W Write 1=READ, 2=WRITE (triggers the operation)
$F2210 FILE_STATUS R 0=OK, 1=ERROR
$F2214 FILE_RESULT_LEN R Number of bytes actually read
$F2218 FILE_ERROR_CODE R 0=OK, 1=NOT_FOUND, 2=PERMISSION, 3=PATH_TRAVERSAL

Byte-Level Access (Z80 / 6502)

All registers support byte-level writes for 8-bit CPUs. Each 32-bit register can be written one byte at a time (little-endian); the device assembles bytes internally. Only writing byte 0 of FILE_CTRL triggers the operation.

8-bit CPUs access File I/O via bank3 switching - set bank3 = $0079 to map the registers into the bank3 window at $6200. The ie80.inc and ie65.inc include files provide FIO_* byte-address constants and convenience macros (SET_FILE_IO_BANK, SET_FIO_PTR, FILE_READ, FILE_WRITE).

3.19 Coprocessor Subsystem (0x0F2340 - 0x0F238F, 0x0F23B0 - 0x0F23BF)

The coprocessor subsystem allows any CPU to launch worker CPUs (IE32, IE64, 6502, M68K, Z80, x86) that run service binaries. Workers poll a shared mailbox ring buffer for requests, process them, and write results. The caller manages worker lifecycle and request routing via MMIO registers. Available in all CPU modes.

MMIO Registers

Address Name R/W Description
0xF2340 COPROC_CMD W Command register (triggers action on byte-0 write)
0xF2344 COPROC_CPU_TYPE W Target CPU type (1=IE32, 2=IE64, 3=6502, 4=M68K, 5=Z80, 6=x86)
0xF2348 COPROC_CMD_STATUS R 0=ok, 1=error
0xF234C COPROC_CMD_ERROR R Error code (see below)
0xF2350 COPROC_TICKET R/W Ticket ID
0xF2354 COPROC_TICKET_STATUS R Per-ticket status
0xF2358 COPROC_OP W Operation code
0xF235C COPROC_REQ_PTR W Request data pointer
0xF2360 COPROC_REQ_LEN W Request data length
0xF2364 COPROC_RESP_PTR W Response buffer pointer
0xF2368 COPROC_RESP_CAP W Response buffer capacity
0xF236C COPROC_TIMEOUT W Timeout in ms
0xF2370 COPROC_NAME_PTR W Service filename pointer
0xF2374 COPROC_WORKER_STATE R Bitmask of running workers
0xF2378 COPROC_STATS_OPS R Total operations dispatched (all workers, counted at enqueue)
0xF237C COPROC_STATS_BYTES R Total bytes processed (all workers, counted at enqueue)
0xF2380 COPROC_IRQ_CTRL R/W Completion interrupt control (bit 0=enable, Level 6/INTB_COPER)
0xF2384 COPROC_DISPATCH_OVERHEAD R Calibrated dispatch overhead in nanoseconds
0xF2388 COPROC_COMPLETED_TICKET R Ticket ID of last completed job (for IRQ handler)

Extended Monitor Registers (0xF23B0 - 0xF23BF)

Address Name R/W Description
0xF23B0 COPROC_RING_DEPTH R IE64 ring buffer occupancy (0-16)
0xF23B4 COPROC_WORKER_UPTIME R Seconds since IE64 worker started (0 if stopped)
0xF23B8 COPROC_STATS_RESET W Write 1 to zero all Go-side stats + busy buckets
0xF23BC COPROC_BUSY_PCT R Worker busy % over last 1 second (0-100)

These registers are in a separate MMIO range after the clipboard bridge (0xF2390-0xF23AF). They are used by the IEWarpMon monitoring application. See sdk/docs/iewarpmon.md.

Byte-Level MMIO Access

All registers support byte-level reads and writes. This allows 8-bit CPUs to program 32-bit registers using four single-byte writes. Registers are aligned to 4-byte boundaries: sub-register byte offsets are computed as addr & 3. Writes to bytes 1-3 of a register perform read-modify-write on the shadow register. Command dispatch only fires when byte 0 of COPROC_CMD is written - writes to bytes 1-3 of COPROC_CMD do not trigger dispatch. This means the CMD register must be written last in any sequence.

16-bit CPU Gateway (0xF200 - 0xF24F)

Z80 and 6502 CPUs have 16-bit address spaces and cannot directly reach 0xF2340. A gateway window at 0xF200-0xF24F transparently redirects reads and writes to the coprocessor MMIO:

Gateway Address Maps To Register
0xF200 0xF2340 COPROC_CMD
0xF204 0xF2344 COPROC_CPU_TYPE
0xF208 0xF2348 COPROC_CMD_STATUS
0xF20C 0xF234C COPROC_CMD_ERROR
0xF210 0xF2350 COPROC_TICKET
0xF214 0xF2354 COPROC_TICKET_STATUS
0xF218 0xF2358 COPROC_OP
0xF21C 0xF235C COPROC_REQ_PTR
0xF220 0xF2360 COPROC_REQ_LEN
0xF224 0xF2364 COPROC_RESP_PTR
0xF228 0xF2368 COPROC_RESP_CAP
0xF22C 0xF236C COPROC_TIMEOUT
0xF230 0xF2370 COPROC_NAME_PTR
0xF234 0xF2374 COPROC_WORKER_STATE
0xF238 0xF2378 COPROC_STATS_OPS
0xF23C 0xF237C COPROC_STATS_BYTES
0xF240 0xF2380 COPROC_IRQ_CTRL
0xF244 0xF2384 COPROC_DISPATCH_OVERHEAD
0xF248 0xF2388 COPROC_COMPLETED_TICKET

IE32, IE64, M68K, and x86 CPUs access 0xF2340 directly (no gateway needed).

Commands (COPROC_CMD)

Value Name Description
1 START Load and start a worker from service binary file
2 STOP Stop a running worker
3 ENQUEUE Submit async request, returns ticket in COPROC_TICKET
4 POLL Check ticket status (non-blocking)
5 WAIT Block until ticket completes or timeout

Ticket Status (COPROC_TICKET_STATUS)

Value Meaning
0 Pending
1 Running
2 OK (completed successfully)
3 Error
4 Timeout
5 Worker down

Shared Mailbox RAM (0x790000 - 0x7917FF)

6KB ring buffer region shared between the Go manager and all worker CPUs. Contains 6 rings (one per supported CPU type), each 768 bytes with 16 request/response descriptor slots. Workers are loaded into dedicated, non-overlapping memory regions (0x200000-0x41FFFF). User data buffers for request/response payloads should be placed at 0x400000-0x7FFFFF.

Access by CPU Type

CPU Register Addresses Write Method Include File
IE32 0xF2340 direct 32-bit STORE ie32.inc
IE64 0xF2340 direct 32-bit store.l ie64.inc
M68K 0xF2340 direct 32-bit move.l ie68.inc
x86 0xF2340 direct 32-bit mov dword ie86.inc
Z80 0xF200 gateway 4x byte ld (addr),a ie80.inc
6502 $F200 gateway 4x byte sta addr ie65.inc

ASM Helper Macros

All include files except ie32.inc (constants only, no macro support) provide coprocessor helper macros with identical semantics:

Macro Arguments Description
coproc_start cpuType, namePtr Start a worker from service binary
coproc_stop cpuType Stop a running worker
coproc_enqueue cpuType, op, reqPtr, reqLen, respPtr, respCap Enqueue async request (ticket in COPROC_TICKET)
coproc_poll ticket Poll ticket status (non-blocking)
coproc_wait ticket, timeoutMs Block until ticket completes or timeout

For 16-bit CPUs (Z80/6502), macros use STORE32 internally to compose 32-bit values from 4 byte writes through the gateway.

Example (x86 - native 32-bit):

%include "ie86.inc"
    coproc_start COPROC_CPU_IE32, 0x400000    ; start IE32 worker
    coproc_enqueue COPROC_CPU_IE32, 1, 0x410000, 8, 0x410100, 4
    mov ebx, [COPROC_TICKET]                  ; save ticket
    coproc_poll ebx                           ; poll status
    mov eax, [COPROC_TICKET_STATUS]           ; read result

Example (Z80 - gateway + byte writes):

    .include "ie80.inc"
    coproc_start COPROC_CPU_M68K 0x400000     ; start M68K worker
    coproc_enqueue COPROC_CPU_M68K 1 0x410000 8 0x410100 4
    ld a,(COPROC_TICKET)                      ; read ticket byte 0

EhBASIC Interface

From BASIC, use the high-level commands instead of direct MMIO access:

COSTART 3, "svc_6502.ie65"         ' Start 6502 worker
T = COCALL(3, 1, &H1000, 8, &H2000, 16)  ' Async RPC (op=1, add)
COWAIT T, 5000                      ' Wait up to 5 seconds
IF COSTATUS(T) = 2 THEN PRINT "OK" ' Check result
COSTOP 3                            ' Stop worker

Full reference: ehbasic_ie64.md

Caller Examples

Complete caller examples are provided for all CPU architectures:

File Caller CPU Worker CPU Description
sdk/examples/asm/coproc_caller_ie32.asm IE32 IE32 Native 32-bit register access
sdk/examples/asm/coproc_caller_68k.asm M68K IE32 Uses coproc_start/coproc_enqueue macros
sdk/examples/asm/coproc_caller_x86.asm x86 IE32 Uses NASM %macro helpers
sdk/examples/asm/coproc_caller_z80.asm Z80 M68K Gateway access via STORE32 macros
sdk/examples/asm/coproc_caller_65.asm 6502 IE32 Gateway access via STORE32 macros

3.19b Clipboard Bridge (0x0F2390 - 0x0F23AF, AROS mode)

The clipboard bridge provides host OS clipboard access for AROS applications.

Address Name R/W Description
$F2390 CLIP_DATA_PTR W Guest RAM pointer for clipboard data
$F2394 CLIP_DATA_LEN W Data length in bytes
$F2398 CLIP_CTRL W Command: 1=read from host, 2=write to host
$F239C CLIP_STATUS R 0=ready, 1=busy, 2=empty, 3=error
$F23A0 CLIP_RESULT_LEN R Bytes actually read/written
$F23A4 CLIP_FORMAT W Format: 0=text, 1=IFF (currently text-only; IFF format accepted but not honored)

3.20 Voodoo 3D Graphics (0x0F4000 - 0x0F43FF)

The Voodoo chip emulates a 3DFX SST-1 graphics accelerator using High-Level Emulation (HLE). Instead of software rasterization, register writes are translated to GPU draw calls for hardware-accelerated 3D rendering with Vulkan (or software fallback).

Important: The Voodoo is disabled by default to allow per-scanline rendering for copper effects. Programs must explicitly enable it by writing 1 to VOODOO_ENABLE (0x0F4004) before using the 3D accelerator.

Features

  • Voodoo SST-1 register-compatible interface
  • Gouraud shaded triangles with per-vertex color interpolation
  • Z-buffering with all 8 depth compare functions (never, less, equal, lessequal, greater, notequal, greaterequal, always)
  • Alpha testing with 8 comparison functions and configurable reference value
  • Chroma key transparency (discard fragments matching key color)
  • Configurable alpha blending with 9 blend factors per source/dest
  • Texture mapping with per-vertex UV coordinates and color modulation
  • Color combine modes (iterated, texture, modulate, add, decal) via fbzColorPath (register stored; backend integration pending)
  • Depth-based fog with configurable fog color (register stored; backend integration pending)
  • Ordered dithering with 4x4 or 2x2 Bayer matrices for reduced banding
  • Point sampling with wrap/clamp addressing modes
  • Texture formats: ARGB8888 (default), with register definitions for paletted, ARGB1555, ARGB4444, and others (upload currently assumes RGBA)
  • Dynamic pipeline state with automatic pipeline caching for performance
  • Scissor clipping
  • 640x480 default, up to 800x600
  • Compositor layer 20 (renders on top of all 2D chips)

Register Map

Status/Control (0x0F4000 - 0x0F4007):
0x0F4000: VOODOO_STATUS      - Status (busy, vsync, fifo state) [read-only]
0x0F4004: VOODOO_ENABLE      - Write 1 to enable, 0 to disable (disabled by default)

Vertex Coordinates (0x0F4008 - 0x0F401F, 12.4 fixed-point):
0x0F4008: VOODOO_VERTEX_AX   - Vertex A X coordinate
0x0F400C: VOODOO_VERTEX_AY   - Vertex A Y coordinate
0x0F4010: VOODOO_VERTEX_BX   - Vertex B X coordinate
0x0F4014: VOODOO_VERTEX_BY   - Vertex B Y coordinate
0x0F4018: VOODOO_VERTEX_CX   - Vertex C X coordinate
0x0F401C: VOODOO_VERTEX_CY   - Vertex C Y coordinate

Vertex Attributes (0x0F4020 - 0x0F403F, 12.12 fixed-point):
0x0F4020: VOODOO_START_R     - Start red (1.0 = 0x1000)
0x0F4024: VOODOO_START_G     - Start green
0x0F4028: VOODOO_START_B     - Start blue
0x0F402C: VOODOO_START_Z     - Start Z depth (20.12 fixed-point)
0x0F4030: VOODOO_START_A     - Start alpha
0x0F4034: VOODOO_START_S     - Start S texture coord (14.18)
0x0F4038: VOODOO_START_T     - Start T texture coord (14.18)
0x0F403C: VOODOO_START_W     - Start W (perspective, 2.30)

Command Registers:
0x0F4080: VOODOO_TRIANGLE_CMD    - Submit triangle for rendering
0x0F4088: VOODOO_COLOR_SELECT    - Select vertex (0/1/2) for Gouraud shading
0x0F4104: VOODOO_FBZCOLOR_PATH   - Color combine mode configuration
0x0F4108: VOODOO_FOG_MODE        - Fog mode configuration
0x0F410C: VOODOO_ALPHA_MODE      - Alpha test/blend configuration
0x0F4110: VOODOO_FBZ_MODE        - Depth test/write/dither configuration
0x0F4118: VOODOO_CLIP_LEFT_RIGHT - Scissor rectangle X bounds
0x0F411C: VOODOO_CLIP_LOW_Y_HIGH - Scissor rectangle Y bounds
0x0F4120: VOODOO_NOP_CMD         - No operation (synchronization)
0x0F4124: VOODOO_FAST_FILL_CMD   - Clear framebuffer with COLOR0
0x0F4128: VOODOO_SWAP_BUFFER_CMD - Swap front/back buffers

Configuration:
0x0F41C4: VOODOO_FOG_COLOR   - Fog color (0x00RRGGBB)
0x0F41C8: VOODOO_ZA_COLOR    - Z/A constant color
0x0F41CC: VOODOO_CHROMA_KEY  - Chroma key color (0x00RRGGBB)
0x0F41D8: VOODOO_COLOR0      - Fill color for FAST_FILL_CMD (ARGB)
0x0F41DC: VOODOO_COLOR1      - Constant color 1
0x0F4214: VOODOO_VIDEO_DIM   - Video dimensions (width<<16 | height)

Texture Mapping (0x0F4300 - 0x0F433F):
0x0F4300: VOODOO_TEXTURE_MODE - Texture mode configuration
0x0F430C: VOODOO_TEX_BASE0    - Texture base address (LOD 0)
0x0F4330: VOODOO_TEX_WIDTH    - Texture width for upload (IE extension)
0x0F4334: VOODOO_TEX_HEIGHT   - Texture height for upload (IE extension)
0x0F4338: VOODOO_TEX_UPLOAD   - Write to trigger texture upload (IE extension, RGBA data)

Texture Memory (0x0F5000 - 0x0F5FFF):
0x0F5000: VOODOO_TEXMEM_BASE  - Texture memory base (64KB)
                              Write RGBA pixel data here, then trigger upload

Voodoo Access by CPU Type

x86 32-bit flat mode: Direct memory access to 0xF4000-0xF43FF works:

; x86 32-bit - direct access to Voodoo registers
mov dword [0xF4214], (640 << 16) | 480    ; VOODOO_VIDEO_DIM
mov dword [0xF4110], 0x0310               ; VOODOO_FBZ_MODE
mov dword [0xF4080], 0                    ; VOODOO_TRIANGLE_CMD

Z80 / x86 real mode: Cannot directly address 0xF4xxx. Use I/O ports 0xB0-0xB7:

Port Name Description
0xB0 ADDR_LO Register offset low byte (from VOODOO_BASE)
0xB1 ADDR_HI Register offset high byte
0xB2 DATA0 Data byte 0 (bits 0-7)
0xB3 DATA1 Data byte 1 (bits 8-15)
0xB4 DATA2 Data byte 2 (bits 16-23)
0xB5 DATA3 Data byte 3 (bits 24-31) - triggers 32-bit write
0xB6 TEXSRC_LO Texture source address low (RAM)
0xB7 TEXSRC_HI Texture source address high (RAM)

I/O Port Usage:

  1. Set register offset (from 0xF4000) via ports 0xB0-0xB1
  2. Write 4 data bytes to ports 0xB2-0xB5 (little-endian)
  3. Writing to port 0xB5 triggers the 32-bit write to Voodoo

Texture Upload via I/O Ports:

  1. Set texture dimensions via TEX_WIDTH/TEX_HEIGHT registers
  2. Set source address in RAM via ports 0xB6-0xB7
  3. Trigger upload via TEX_UPLOAD register - emulator copies from CPU RAM
; Z80 Example: Write 640x480 to VOODOO_VIDEO_DIM (offset 0x214)
    ld a, 0x14
    out (0xB0), a           ; Offset low = 0x14
    ld a, 0x02
    out (0xB1), a           ; Offset high = 0x02 (offset = 0x214)
    ld a, 0xE0
    out (0xB2), a           ; Height low (480 & 0xFF)
    ld a, 0x01
    out (0xB3), a           ; Height high (480 >> 8)
    ld a, 0x80
    out (0xB4), a           ; Width low (640 & 0xFF)
    ld a, 0x02
    out (0xB5), a           ; Width high - triggers write

Fixed-Point Formats

Format Shift Range Usage
12.4 4 -2048.0 to 2047.9375 Vertex coordinates
12.12 12 -2048.0 to 2047.999 Colors (0.0-1.0 range: 0x0000-0x1000)
20.12 12 Large range Z depth
14.18 18 0.0 to 16383.999 Texture coordinates

fbzMode Bits

Bit Name Description
0 CLIPPING Enable scissor clipping
1 CHROMAKEY Enable chroma key transparency
4 DEPTH_ENABLE Enable depth buffer test
5-7 DEPTH_FUNC Depth compare function (see table below)
8 DITHER Enable ordered dithering (4x4 Bayer matrix)
9 RGB_WRITE Enable RGB buffer write
10 DEPTH_WRITE Enable depth buffer write
11 DITHER_2X2 Use 2x2 Bayer matrix instead of 4x4

Depth Compare Functions

The depth function (bits 5-7 of fbzMode) controls Z-buffer testing. Shift these values left by 5 to position in fbzMode.

Value Name Description
0 NEVER Never pass (always discard fragment)
1 LESS Pass if new Z < buffer Z
2 EQUAL Pass if new Z == buffer Z
3 LESSEQUAL Pass if new Z <= buffer Z
4 GREATER Pass if new Z > buffer Z
5 NOTEQUAL Pass if new Z != buffer Z
6 GREATEREQUAL Pass if new Z >= buffer Z
7 ALWAYS Always pass (disable depth test)

alphaMode Bits

Bit Name Description
0 ALPHA_TEST_EN Enable alpha test
1-3 ALPHA_FUNC Alpha test function (see table below)
4 ALPHA_BLEND_EN Enable alpha blending
8-11 SRC_BLEND Source blend factor
12-15 DST_BLEND Destination blend factor
24-31 ALPHA_REF Alpha reference value (0-255)

Alpha Test Functions

The alpha test function (bits 1-3 of alphaMode) compares fragment alpha against the reference value (bits 24-31). If the test fails, the fragment is discarded. Shift function values left by 1 to position in alphaMode.

Value Name Description
0 NEVER Never pass (always discard fragment)
1 LESS Pass if alpha < reference
2 EQUAL Pass if alpha == reference
3 LESSEQUAL Pass if alpha <= reference
4 GREATER Pass if alpha > reference
5 NOTEQUAL Pass if alpha != reference
6 GREATEREQUAL Pass if alpha >= reference
7 ALWAYS Always pass (alpha test disabled)

Example: Discard fragments with alpha < 0.5 (reference=128):

    ; Enable alpha test with LESS function, reference = 128
    move.l  #(VOODOO_ALPHA_TEST_EN|(VOODOO_ALPHA_GREATER<<1)|(128<<24)),VOODOO_ALPHA_MODE

Alpha Blend Factors

Use these values in bits 8-11 (source) and 12-15 (destination) of alphaMode. The final color is computed as: result = src * srcFactor + dst * dstFactor

Value Name Description
0 ZERO Factor = 0
1 SRC_ALPHA Factor = source alpha
2 COLOR Factor = constant color
3 DST_ALPHA Factor = destination alpha
4 ONE Factor = 1
5 INV_SRC_A Factor = 1 - source alpha
6 INV_COLOR Factor = 1 - constant color
7 INV_DST_A Factor = 1 - destination alpha
15 SATURATE Factor = min(srcA, 1-dstA)

Common blending modes:

  • Standard alpha blend: srcFactor=SRC_ALPHA (1), dstFactor=INV_SRC_A (5)
  • Additive blend: srcFactor=ONE (4), dstFactor=ONE (4)
  • Pre-multiplied alpha: srcFactor=ONE (4), dstFactor=INV_SRC_A (5)

Chroma Key

Chroma keying discards fragments that match a specific color, creating transparency without alpha blending. This is useful for sprite-based rendering where a specific color represents "transparent."

To enable chroma keying:

  1. Set the key color in VOODOO_CHROMA_KEY (format: 0x00RRGGBB)
  2. Enable chroma keying by setting bit 1 (CHROMAKEY) in VOODOO_FBZ_MODE

When enabled, any fragment whose RGB color matches the chroma key color will be discarded.

Example: Use magenta (255, 0, 255) as transparent color:

    ; Set chroma key to magenta
    move.l  #$00FF00FF,VOODOO_CHROMA_KEY

    ; Enable chroma keying in fbzMode
    move.l  #(VOODOO_FBZ_CHROMAKEY|VOODOO_FBZ_RGB_WRITE),VOODOO_FBZ_MODE

textureMode Bits

Bit Name Description
0 TEX_ENABLE Enable texture mapping
1-3 TEX_MINIFY Minification filter (0=point)
4 TEX_MAGNIFY Magnification filter (0=point, 1=bilinear)
5 TEX_CLAMP_S Clamp S (U) coordinate (vs wrap)
6 TEX_CLAMP_T Clamp T (V) coordinate (vs wrap)
8-11 TEX_FORMAT Texture format (see table below)

Note: Only enable and clamp bits are currently forwarded to the rendering backend. Filtering defaults to point sampling; texture upload assumes RGBA data regardless of TEX_FORMAT.

Texture Formats

Value Name Description
0 8BIT_PALETTE 8-bit paletted texture
5 P8 8-bit palette (alternative)
8 ARGB1555 16-bit ARGB 1555
9 ARGB4444 16-bit ARGB 4444
10 ARGB8888 32-bit ARGB 8888 (default)

Note: Texture upload currently processes all data as RGBA. Format selection is stored but does not yet affect upload conversion.

Texture Coordinates

Texture coordinates (S and T) use 14.18 fixed-point format. Values represent positions in texture space where 1.0 (0x40000) equals the texture width/height. Coordinates outside 0.0-1.0 wrap or clamp depending on TEX_CLAMP_S/T bits.

Per-vertex texture coordinates work like per-vertex colors: use VOODOO_COLOR_SELECT to select the vertex (0/1/2), then write to VOODOO_START_S and VOODOO_START_T.

fbzColorPath (Color Combine)

The VOODOO_FBZCOLOR_PATH register (0x0F4104) controls how texture and vertex (iterated) colors are combined. This allows for various rendering effects from simple flat colors to complex texture blending.

fbzColorPath Bits

Bit Name Description
0-1 RGB_SELECT RGB source select (see table below)
2-3 A_SELECT Alpha source select (see table below)
4-6 CC_MSELECT Color combine function mode
27 TEXTURE_ENABLE Enable texture in color path

Color Source Select Values

Value Name Description
0 ITERATED Use iterated (vertex) color
1 TEXTURE Use texture color
2 COLOR1 Use constant color1
3 LFB Use linear framebuffer color

Color Combine Functions (CC_MSELECT)

Value Name Description
0 ZERO Output zero (black)
1 CSUB_CL cother - clocal (subtract)
2 ALOCAL clocal * alocal (modulate by local alpha)
3 AOTHER clocal * aother (modulate by other alpha)
4 CLOCAL clocal only (pass through)
5 ALOCAL_T alocal * texture
6 CLOC_MUL clocal * cother (multiply/modulate)
7 AOTHER_T aother * texture

Simplified Combine Modes

For convenience, pre-computed values are provided for common operations:

Value Name Description
0x00 COMBINE_ITERATED Vertex color only (default when no texture)
0x01 COMBINE_TEXTURE Texture color only
0x61 COMBINE_MODULATE Texture × vertex color (most common for textured geometry)
0x81 COMBINE_ADD Texture + vertex color (clamped, for glow effects)
0x41 COMBINE_DECAL Texture with vertex alpha

Example: Enable texture modulation (texture color multiplied by vertex color):

    ; Set color combine to MODULATE mode (tex * vert)
    move.l  #VOODOO_COMBINE_MODULATE,VOODOO_FBZCOLOR_PATH

fogMode (Depth-Based Fog)

The VOODOO_FOG_MODE register (0x0F4108) enables depth-based fog blending. When enabled, fragment colors are linearly blended with the fog color based on the vertex Z coordinate (depth). Objects further from the camera appear more "fogged".

fogMode Bits

Bit Name Description
0 FOG_ENABLE Enable fog processing
1 FOG_ADD Add fog color to output (vs. blend)
2 FOG_MULT Multiply fog factor by alpha
3 FOG_ZALPHA Use Z alpha for fog (vs. iterated)
4 FOG_CONSTANT Use constant fog alpha
5 FOG_DITHER Apply dithering to fog
6 FOG_ZONES Enable fog zones (table-based fog)

The fog color is set in VOODOO_FOG_COLOR (0x0F41C4) using the format 0x00RRGGBB.

Fog blending formula: output.rgb = mix(color.rgb, fogColor.rgb, fogFactor)

Where fogFactor is derived from the vertex Z coordinate (0.0 = near/no fog, 1.0 = far/full fog).

Example: Enable gray fog for distance fade effect:

    ; Set fog color to gray
    move.l  #$00808080,VOODOO_FOG_COLOR

    ; Enable fog
    move.l  #VOODOO_FOG_ENABLE,VOODOO_FOG_MODE

Dithering

Ordered dithering reduces color banding artifacts by applying a threshold pattern to pixel colors. The Voodoo supports two dither modes controlled by fbzMode bits:

  • 4x4 Bayer matrix (default): Higher quality, 16 threshold levels
  • 2x2 Bayer matrix: Faster, 4 threshold levels

Enable dithering by setting bit 8 (DITHER) in VOODOO_FBZ_MODE. For 2x2 mode, also set bit 11 (DITHER_2X2).

Example: Enable 4x4 dithering:

    ; Enable depth test, RGB write, and 4x4 dithering
    move.l  #(VOODOO_FBZ_DEPTH_ENABLE|VOODOO_FBZ_RGB_WRITE|VOODOO_FBZ_DITHER|(VOODOO_DEPTH_LESS<<5)),VOODOO_FBZ_MODE

Example: Fog with dithering for smooth distance fade:

    ; Set fog color
    move.l  #$00404040,VOODOO_FOG_COLOR

    ; Enable fog
    move.l  #VOODOO_FOG_ENABLE,VOODOO_FOG_MODE

    ; Enable depth, RGB write, and dithering
    move.l  #(VOODOO_FBZ_DEPTH_ENABLE|VOODOO_FBZ_RGB_WRITE|VOODOO_FBZ_DITHER|(VOODOO_DEPTH_LESS<<5)),VOODOO_FBZ_MODE

Gouraud Shading

The Voodoo supports per-vertex colors for smooth Gouraud shading. Use VOODOO_COLOR_SELECT to select which vertex (0, 1, or 2 for vertices A, B, C) will receive subsequent writes to the VOODOO_START_* attribute registers:

  1. Write vertex index (0/1/2) to VOODOO_COLOR_SELECT
  2. Write R/G/B/A values to VOODOO_START_R/G/B/A - these are stored for the selected vertex
  3. Repeat for each vertex with different colors
  4. Submit triangle - colors will be smoothly interpolated across the surface

When VOODOO_COLOR_SELECT is not used (or set to 0), flat shading is applied using the last written color values.

Example: Flat Shaded Triangle (M68K)

    include "ie68.inc"

    ; Enable the Voodoo graphics card (disabled by default)
    move.l  #1,VOODOO_ENABLE

    ; Clear screen to black
    move.l  #$FF000000,VOODOO_COLOR0
    move.l  #0,VOODOO_FAST_FILL_CMD

    ; Set up depth test (less-than, write enabled)
    move.l  #(VOODOO_FBZ_DEPTH_ENABLE|VOODOO_FBZ_RGB_WRITE|VOODOO_FBZ_DEPTH_WRITE|(VOODOO_DEPTH_LESS<<5)),VOODOO_FBZ_MODE

    ; Define triangle vertices (12.4 fixed-point: value << 4)
    move.l  #(320<<4),VOODOO_VERTEX_AX   ; Top center (320, 100)
    move.l  #(100<<4),VOODOO_VERTEX_AY
    move.l  #(420<<4),VOODOO_VERTEX_BX   ; Bottom right (420, 300)
    move.l  #(300<<4),VOODOO_VERTEX_BY
    move.l  #(220<<4),VOODOO_VERTEX_CX   ; Bottom left (220, 300)
    move.l  #(300<<4),VOODOO_VERTEX_CY

    ; Set red color (12.12 fixed-point: 1.0 = $1000)
    move.l  #$1000,VOODOO_START_R        ; R = 1.0
    move.l  #$0000,VOODOO_START_G        ; G = 0.0
    move.l  #$0000,VOODOO_START_B        ; B = 0.0
    move.l  #$1000,VOODOO_START_A        ; A = 1.0 (opaque)
    move.l  #$800000,VOODOO_START_Z      ; Z = 0.5

    ; Submit triangle
    move.l  #0,VOODOO_TRIANGLE_CMD

    ; Present frame
    move.l  #0,VOODOO_SWAP_BUFFER_CMD

Example: Gouraud Shaded Triangle (M68K)

Per-vertex colors are set using VOODOO_COLOR_SELECT to specify which vertex (0/1/2) receives the subsequent VOODOO_START_* writes. The colors are smoothly interpolated across the triangle.

    include "ie68.inc"

    ; Enable the Voodoo graphics card (disabled by default)
    move.l  #1,VOODOO_ENABLE

    ; Clear screen to black
    move.l  #$FF000000,VOODOO_COLOR0
    move.l  #0,VOODOO_FAST_FILL_CMD

    ; Enable depth test and RGB write
    move.l  #(VOODOO_FBZ_DEPTH_ENABLE|VOODOO_FBZ_RGB_WRITE|VOODOO_FBZ_DEPTH_WRITE|(VOODOO_DEPTH_LESS<<5)),VOODOO_FBZ_MODE

    ; Define triangle vertices (12.4 fixed-point)
    move.l  #(320<<4),VOODOO_VERTEX_AX   ; Top center (320, 100)
    move.l  #(100<<4),VOODOO_VERTEX_AY
    move.l  #(420<<4),VOODOO_VERTEX_BX   ; Bottom right (420, 300)
    move.l  #(300<<4),VOODOO_VERTEX_BY
    move.l  #(220<<4),VOODOO_VERTEX_CX   ; Bottom left (220, 300)
    move.l  #(300<<4),VOODOO_VERTEX_CY

    ; Set vertex 0 (A) to RED
    move.l  #0,VOODOO_COLOR_SELECT       ; Select vertex 0
    move.l  #$1000,VOODOO_START_R        ; R = 1.0
    move.l  #$0000,VOODOO_START_G        ; G = 0.0
    move.l  #$0000,VOODOO_START_B        ; B = 0.0
    move.l  #$1000,VOODOO_START_A        ; A = 1.0

    ; Set vertex 1 (B) to GREEN
    move.l  #1,VOODOO_COLOR_SELECT       ; Select vertex 1
    move.l  #$0000,VOODOO_START_R        ; R = 0.0
    move.l  #$1000,VOODOO_START_G        ; G = 1.0
    move.l  #$0000,VOODOO_START_B        ; B = 0.0
    move.l  #$1000,VOODOO_START_A        ; A = 1.0

    ; Set vertex 2 (C) to BLUE
    move.l  #2,VOODOO_COLOR_SELECT       ; Select vertex 2
    move.l  #$0000,VOODOO_START_R        ; R = 0.0
    move.l  #$0000,VOODOO_START_G        ; G = 0.0
    move.l  #$1000,VOODOO_START_B        ; B = 1.0
    move.l  #$1000,VOODOO_START_A        ; A = 1.0

    ; Submit triangle (colors will interpolate smoothly)
    move.l  #0,VOODOO_TRIANGLE_CMD

    ; Present frame
    move.l  #0,VOODOO_SWAP_BUFFER_CMD

Example: Alpha Blending (M68K)

This example demonstrates configuring alpha blending with source alpha and inverse source alpha factors (standard transparency).

    include "ie68.inc"

    ; Enable alpha blending: src*srcA + dst*(1-srcA)
    move.l  #(VOODOO_ALPHA_BLEND_EN|(VOODOO_BLEND_SRC_ALPHA<<8)|(VOODOO_BLEND_INV_SRC_A<<12)),VOODOO_ALPHA_MODE

    ; Draw opaque background triangle first
    ; ... (set vertices and full alpha color)
    move.l  #$1000,VOODOO_START_A        ; Alpha = 1.0 (opaque)
    move.l  #0,VOODOO_TRIANGLE_CMD

    ; Draw semi-transparent overlay triangle
    move.l  #$0800,VOODOO_START_A        ; Alpha = 0.5 (50% transparent)
    move.l  #$1000,VOODOO_START_R        ; Red
    move.l  #0,VOODOO_START_G
    move.l  #0,VOODOO_START_B
    move.l  #0,VOODOO_TRIANGLE_CMD

    ; Present frame
    move.l  #0,VOODOO_SWAP_BUFFER_CMD

Example: Rotating Cube with Z-Buffer (M68K)

    include "ie68.inc"

    ; Main render loop
.frame_loop:
    ; Clear framebuffer
    move.l  #$FF000000,VOODOO_COLOR0
    move.l  #0,VOODOO_FAST_FILL_CMD

    ; Draw 12 triangles (6 faces x 2 triangles each)
    ; Front face - red
    bsr     draw_face_front

    ; Back face - blue (will be Z-rejected when behind front)
    bsr     draw_face_back

    ; Present frame
    move.l  #VOODOO_SWAP_VSYNC,VOODOO_SWAP_BUFFER_CMD

    ; Update rotation angle
    add.w   #2,rotation_angle
    bra     .frame_loop

Example: Textured Triangle (M68K)

This example demonstrates texture-mapped rendering with per-vertex UV coordinates.

    include "ie68.inc"

    ; Enable texturing with point sampling and wrap mode
    move.l  #VOODOO_TEX_ENABLE,VOODOO_TEXTURE_MODE

    ; Set up depth test and RGB write
    move.l  #(VOODOO_FBZ_DEPTH_ENABLE|VOODOO_FBZ_RGB_WRITE|VOODOO_FBZ_DEPTH_WRITE|(VOODOO_DEPTH_LESS<<5)),VOODOO_FBZ_MODE

    ; Define triangle vertices (12.4 fixed-point)
    move.l  #(320<<4),VOODOO_VERTEX_AX   ; Top center
    move.l  #(100<<4),VOODOO_VERTEX_AY
    move.l  #(420<<4),VOODOO_VERTEX_BX   ; Bottom right
    move.l  #(300<<4),VOODOO_VERTEX_BY
    move.l  #(220<<4),VOODOO_VERTEX_CX   ; Bottom left
    move.l  #(300<<4),VOODOO_VERTEX_CY

    ; Vertex 0 (A): UV = (0.5, 0.0) - top center of texture
    move.l  #0,VOODOO_COLOR_SELECT
    move.l  #$1000,VOODOO_START_R        ; White color modulation
    move.l  #$1000,VOODOO_START_G
    move.l  #$1000,VOODOO_START_B
    move.l  #$1000,VOODOO_START_A
    move.l  #$20000,VOODOO_START_S       ; S = 0.5 (14.18 format: 0.5 * 0x40000)
    move.l  #$00000,VOODOO_START_T       ; T = 0.0

    ; Vertex 1 (B): UV = (1.0, 1.0) - bottom right
    move.l  #1,VOODOO_COLOR_SELECT
    move.l  #$1000,VOODOO_START_R
    move.l  #$1000,VOODOO_START_G
    move.l  #$1000,VOODOO_START_B
    move.l  #$1000,VOODOO_START_A
    move.l  #$40000,VOODOO_START_S       ; S = 1.0
    move.l  #$40000,VOODOO_START_T       ; T = 1.0

    ; Vertex 2 (C): UV = (0.0, 1.0) - bottom left
    move.l  #2,VOODOO_COLOR_SELECT
    move.l  #$1000,VOODOO_START_R
    move.l  #$1000,VOODOO_START_G
    move.l  #$1000,VOODOO_START_B
    move.l  #$1000,VOODOO_START_A
    move.l  #$00000,VOODOO_START_S       ; S = 0.0
    move.l  #$40000,VOODOO_START_T       ; T = 1.0

    ; Submit triangle
    move.l  #0,VOODOO_TRIANGLE_CMD

    ; Present frame
    move.l  #0,VOODOO_SWAP_BUFFER_CMD

4. IE32 CPU Architecture

The IE32 implements a 32-bit RISC-like architecture with fixed-width instructions and a clean, orthogonal instruction set.

4.1 Register Set

The CPU provides 16 general-purpose 32-bit registers organised in two logical banks:

First Bank (A-H):

A - Primary accumulator/general purpose
B - General purpose
C - General purpose
D - General purpose
E - General purpose
F - General purpose
G - General purpose
H - General purpose

Second Bank (S-Z):

S - General purpose/stack operations
T - General purpose
U - General purpose
V - General purpose
W - General purpose
X - General purpose/index
Y - General purpose/index
Z - General purpose/index

While the register naming suggests traditional roles (like X/Y/Z for indexing), all registers are fully general-purpose and can be used interchangeably.

Special Registers:

PC - Program Counter (32-bit)
SP - Stack Pointer (32-bit, initialised to 0x9F000)

4.2 Status Flags

The IE32 uses implicit status flags based on the result of the last operation:

  • Operations that produce a zero result set an internal zero flag
  • Comparison instructions (used by conditional jumps) compare register values directly

4.3 Addressing Modes

The system supports five addressing modes:

Immediate (ADDR_IMMEDIATE = 0x00)

LOAD A, #42        ; Load value 42 into register A

Register (ADDR_REGISTER = 0x01)

ADD A, X           ; Add X register to A register

Register Indirect (ADDR_REG_IND = 0x02)

LOAD A, [X]        ; Load from address in X
LOAD A, [X+4]      ; Load from address in X plus 4

Memory Indirect (ADDR_MEM_IND = 0x03)

LOAD A, [0x1000]   ; Load from address stored at memory location 0x1000

Direct (ADDR_DIRECT = 0x04)

STORE A, @0xF0900  ; Store A's value directly to memory address 0xF0900
LOAD A, @0x1000    ; Load value directly from memory address 0x1000

The direct addressing mode is used for memory-mapped I/O operations, providing efficient access to hardware registers without double indirection.

4.4 Instruction Format

Every instruction is exactly 8 bytes long, providing a consistent and easy-to-decode format:

Byte 0: Opcode
Byte 1: Register specifier
Byte 2: Addressing mode
Byte 3: Reserved (must be 0)
Bytes 4-7: 32-bit operand value

4.5 Instruction Set

Data Movement Instructions

; Traditional load/store
LOAD  (0x01) ; Load value into register
STORE (0x02) ; Store register to memory

; Register-specific loads
LDA (0x20) ; Load to A
LDB (0x3A) ; Load to B
LDC (0x3B) ; Load to C
LDD (0x3C) ; Load to D
LDE (0x3D) ; Load to E
LDF (0x3E) ; Load to F
LDG (0x3F) ; Load to G
LDH (0x4C) ; Load to H
LDS (0x4D) ; Load to S
LDT (0x4E) ; Load to T
LDU (0x40) ; Load to U
LDV (0x41) ; Load to V
LDW (0x42) ; Load to W
LDX (0x21) ; Load to X
LDY (0x22) ; Load to Y
LDZ (0x23) ; Load to Z

; Register-specific stores
STA (0x24) ; Store from A
STB (0x43) ; Store from B
STC (0x44) ; Store from C
STD (0x45) ; Store from D
STE (0x46) ; Store from E
STF (0x47) ; Store from F
STG (0x48) ; Store from G
STH (0x4F) ; Store from H
STS (0x50) ; Store from S
STT (0x51) ; Store from T
STU (0x49) ; Store from U
STV (0x4A) ; Store from V
STW (0x4B) ; Store from W
STX (0x25) ; Store from X
STY (0x26) ; Store from Y
STZ (0x27) ; Store from Z

; Increment/Decrement
INC (0x28) ; Increment
DEC (0x29) ; Decrement

; Stack operations
PUSH (0x12) ; Push register to stack
POP  (0x13) ; Pop from stack to register

Arithmetic Instructions

ADD (0x03) ; Add
SUB (0x04) ; Subtract
MUL (0x14) ; Multiply
DIV (0x15) ; Divide
MOD (0x16) ; Modulus

Logical Instructions

AND (0x05) ; Bitwise AND
OR  (0x09) ; Bitwise OR
XOR (0x0A) ; Bitwise XOR
NOT (0x0D) ; Bitwise NOT
SHL (0x0B) ; Shift left
SHR (0x0C) ; Shift right

Control Flow Instructions

JMP (0x06) ; Unconditional jump
JNZ (0x07) ; Jump if not zero
JZ  (0x08) ; Jump if zero
JGT (0x0E) ; Jump if greater than
JGE (0x0F) ; Jump if greater or equal
JLT (0x10) ; Jump if less than
JLE (0x11) ; Jump if less or equal
JSR (0x18) ; Jump to subroutine
RTS (0x19) ; Return from subroutine

Interrupt Management Instructions

SEI (0x1A) ; Set Enable Interrupts
CLI (0x1B) ; Clear Interrupt Enable
RTI (0x1C) ; Return from Interrupt

System Control Instructions

WAIT (0x17) ; Wait for specified cycles
NOP  (0xEE) ; No operation
HALT (0xFF) ; Stop execution

4.6 Memory and I/O Integration

  • The IE32 uses the shared MachineBus; visible RAM is the 32-bit profile ceiling clamped against the autodetected active visible RAM
  • All memory-mapped devices (video, audio, PSG/POKEY/SID, terminal) are accessible
  • I/O region: 0x0F0000 - 0x0FFFFF
  • VRAM access: 0x100000 - 0x5FFFFF (direct 32-bit addressing)
  • Stack grows downward from 0x9F000

4.7 Interrupt Handling

The system implements a simple but effective interrupt system:

  1. Interrupt Vector

    • Located at address 0x0000
    • Contains the address of the interrupt service routine (ISR)
    • Must be initialised before enabling interrupts
  2. Interrupt Control

    • SEI enables interrupts
    • CLI disables interrupts
    • Interrupts automatically disabled during ISR execution
  3. Interrupt Processing When an interrupt occurs:

    • Current PC is pushed onto stack
    • CPU jumps to ISR address from vector
    • Interrupts are disabled until RTI
  4. Timer Interrupts IE32/IE64 can generate periodic interrupts from their internal countdown timer. Programs should initialise the IRQ vector and enable interrupts with SEI.

4.8 Compatibility Notes

  • Little-endian byte order for memory bus operations
  • Fixed 8-byte instruction size
  • All registers are 32-bit
  • Word-aligned memory access recommended for performance

5. MOS 6502 CPU

The Intuition Engine includes an NMOS 6502 core for running raw 8-bit binaries. The 6502 shares the same memory-mapped I/O and device map as IE32 and M68K, so hardware registers behave identically across CPU modes.

5.1 Register Set

The 6502 core exposes the classic NMOS register file:

A  - Accumulator (8-bit) for arithmetic and logic
X  - Index register X (8-bit)
Y  - Index register Y (8-bit)
SP - Stack Pointer (8-bit), stack page fixed at 0x0100-0x01FF
PC - Program Counter (16-bit)
SR - Status Register (8-bit flags)

5.2 Status Flags

The status register follows NMOS 6502 semantics:

Bit 7: N - Negative flag
Bit 6: V - Overflow flag
Bit 5: - - Unused (always 1 when pushed)
Bit 4: B - Break command
Bit 3: D - Decimal mode
Bit 2: I - IRQ Disable
Bit 1: Z - Zero flag
Bit 0: C - Carry flag

5.3 Addressing Modes

Supported addressing modes match the NMOS 6502 set used by common assemblers:

Mode Syntax Description
Immediate #$nn Operand is the byte following the opcode
Zero Page $nn 8-bit address in page zero
Zero Page,X $nn,X Zero page indexed by X
Zero Page,Y $nn,Y Zero page indexed by Y
Absolute $nnnn 16-bit address
Absolute,X $nnnn,X Absolute indexed by X
Absolute,Y $nnnn,Y Absolute indexed by Y
Indirect ($nnnn) Indirect (JMP only)
(Indirect,X) ($nn,X) Indexed indirect
(Indirect),Y ($nn),Y Indirect indexed
Relative $nn Signed offset for branches
Accumulator A Operand is accumulator
Implied Operand implied by instruction

5.4 Instruction Set

The 6502 implements all 56 documented NMOS instructions plus unofficial opcodes:

Load/Store: LDA, LDX, LDY, STA, STX, STY Transfer: TAX, TAY, TSX, TXA, TXS, TYA Stack: PHA, PHP, PLA, PLP Arithmetic: ADC, SBC, INC, INX, INY, DEC, DEX, DEY Logical: AND, EOR, ORA, BIT Shift/Rotate: ASL, LSR, ROL, ROR Compare: CMP, CPX, CPY Branch: BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS Jump/Return: JMP, JSR, RTS, RTI, BRK Flags: CLC, CLD, CLI, CLV, SEC, SED, SEI No-op: NOP

5.5 Memory and I/O Integration

  • The 6502 uses the shared system bus and all memory-mapped devices
  • Native 16-bit address space (0x0000-0xFFFF)
  • VRAM access via banking at 0x8000-0xBFFF (16KB window)
  • Bank select register at 0xF7F0
  • VRAM banking is disabled until the first write to the bank register
  • Extended bank windows for IE65:
    • Bank 1: 0x2000-0x3FFF (8KB, sprite data)
    • Bank 2: 0x4000-0x5FFF (8KB, font data)
    • Bank 3: 0x6000-0x7FFF (8KB, general/AY data)
  • Bank control registers: 0xF700-0xF705

Audio Chip Access (6502 native addresses):

Chip Address Range
PSG $D400-$D40D
POKEY $D200-$D209
SID $D500-$D51C

5.6 Interrupts and Vectors

Vector locations follow standard 6502 layout:

0xFFFA-0xFFFB: NMI vector
0xFFFC-0xFFFD: RESET vector
0xFFFE-0xFFFF: IRQ/BRK vector

The loader initializes these vectors for raw binaries; custom binaries may overwrite them.

5.7 Compatibility Notes

  • NMOS 6502 only: 65C02 opcodes are not supported
  • Decimal mode is fully implemented
  • Cycle-accurate instruction timing
  • Use Klaus tests to validate D-flag behavior
  • Use -m6502 flag to run 6502 binaries
  • --load-addr sets the load address (default 0x0600 for raw binaries, 0x0800 for .ie65 files)
  • --entry sets the entry address (defaults to load address)

6. Zilog Z80 CPU

The Intuition Engine includes a Z80 core for running raw 8-bit binaries. It shares the same memory map and device registers as the other CPU modes.

6.1 Register Set

The Z80 provides a rich register set with shadow registers:

Main Registers:

A  - Accumulator (8-bit)
F  - Flags (8-bit)
B, C - General purpose (8-bit each, BC as 16-bit pair)
D, E - General purpose (8-bit each, DE as 16-bit pair)
H, L - General purpose (8-bit each, HL as 16-bit pair)

Shadow Registers:

A', F', B', C', D', E', H', L' - Alternate register set

Index Registers:

IX - Index register X (16-bit)
IY - Index register Y (16-bit)

Special Registers:

SP - Stack Pointer (16-bit)
PC - Program Counter (16-bit)
I  - Interrupt Vector (8-bit)
R  - Refresh Counter (8-bit)

6.2 Status Flags

The F register contains:

Bit 7: S  - Sign flag
Bit 6: Z  - Zero flag
Bit 5: Y  - Undocumented (copy of bit 5 of result)
Bit 4: H  - Half-carry flag
Bit 3: X  - Undocumented (copy of bit 3 of result)
Bit 2: P/V - Parity/Overflow flag
Bit 1: N  - Add/Subtract flag
Bit 0: C  - Carry flag

6.3 Addressing Modes

Mode Syntax Description
Immediate n 8-bit immediate value
Immediate Extended nn 16-bit immediate value
Register r Single register
Register Pair rr 16-bit register pair (BC, DE, HL, SP)
Indirect (HL) Memory at address in HL
Indexed (IX+d), (IY+d) Indexed with signed displacement
Extended (nn) Direct 16-bit address
Relative e Signed 8-bit offset for jumps
Bit b Bit number (0-7)

6.4 Instruction Set

The Z80 implements a comprehensive instruction set including:

8-bit Load: LD r,r' / LD r,n / LD r,(HL) / LD (HL),r 16-bit Load: LD rr,nn / LD (nn),HL / LD HL,(nn) / PUSH/POP Exchange: EX DE,HL / EX AF,AF' / EXX Arithmetic: ADD, ADC, SUB, SBC, AND, OR, XOR, CP, INC, DEC 16-bit Arithmetic: ADD HL,rr / ADC HL,rr / SBC HL,rr / INC/DEC rr Rotate/Shift: RLCA, RRCA, RLA, RRA, RLC, RRC, RL, RR, SLA, SRA, SRL Bit Operations: BIT, SET, RES Jump: JP, JR, DJNZ Call/Return: CALL, RET, RETI, RETN, RST Input/Output: IN, OUT Block Transfer: LDI, LDIR, LDD, LDDR Block Search: CPI, CPIR, CPD, CPDR Block I/O: INI, INIR, IND, INDR, OUTI, OTIR, OUTD, OTDR

6.5 Memory and I/O Integration

  • The Z80 uses the shared system bus
  • Native 16-bit address space (0x0000-0xFFFF)
  • Z80 IN/OUT ports map to the 16-bit address space as memory-mapped registers
  • VRAM access via banking at 0x8000-0xBFFF (16KB window, bank register at 0xF7F0)
  • Extended bank windows: Bank 1 (0x2000, 8KB), Bank 2 (0x4000, 8KB), Bank 3 (0x6000, 8KB)
  • Bank control registers: 0xF700-0xF705

Port-Based Chip Access:

Chip Ports Description
PSG 0xF0-0xF1 Register select, data
POKEY 0xD0-0xD1 Register select, data
SID 0xE0-0xE1 Register select, data
TED 0xF2-0xF3 Register select, data (audio + video indices 0x20-0x2F)
ANTIC 0xD4-0xD5 Register select, data
GTIA 0xD6-0xD7 Register select, data
ULA 0xFE Border color / key status
VGA 0xA0-0xAC VGA register access
Voodoo 0xB0-0xB7 Address/data ports for 32-bit Voodoo register writes

First port selects the register, second port reads/writes data (except ULA which uses a single port, and Voodoo which uses an address/data accumulator).

6.6 Interrupts

The Z80 supports three interrupt modes:

Mode 0: External device places instruction on data bus (typically RST) Mode 1: Jump to fixed address 0x0038 Mode 2: Vectored interrupts using I register as high byte

Interrupt control:

DI      ; Disable interrupts
EI      ; Enable interrupts
IM 0/1/2 ; Set interrupt mode

6.7 Compatibility Notes

  • Full Z80 instruction set including undocumented opcodes
  • Use -z80 flag to run Z80 binaries
  • --load-addr sets the load address (default 0x0000)
  • --entry sets the entry address (defaults to load address)
  • Shadow registers fully implemented
  • Block transfer and search instructions implemented
  • All interrupt modes supported

7. Motorola 68020 CPU with FPU

In addition to the IE32 instruction set, the Intuition Engine includes a complete Motorola 68020 CPU emulator with 68881/68882 FPU (Floating Point Unit) support.

7.1 Register Set

Data Registers:

D0-D7 - Eight 32-bit data registers
        Can be used as byte (.B), word (.W), or long (.L)

Address Registers:

A0-A6 - Seven 32-bit address registers
A7    - Stack pointer (SSP in supervisor mode, USP in user mode)

Special Registers:

PC   - Program Counter (32-bit)
SR   - Status Register (16-bit)
CCR  - Condition Code Register (low byte of SR)
USP  - User Stack Pointer
SSP  - Supervisor Stack Pointer
VBR  - Vector Base Register
SFC  - Source Function Code
DFC  - Destination Function Code
CACR - Cache Control Register
CAAR - Cache Address Register

FPU Registers (68881/68882):

FP0-FP7 - Eight 80-bit floating-point registers
FPCR    - Floating-Point Control Register
FPSR    - Floating-Point Status Register
FPIAR   - Floating-Point Instruction Address Register

7.2 Status Flags

Condition Code Register (CCR):

Bit 4: X - Extend (copy of carry for multi-precision)
Bit 3: N - Negative
Bit 2: Z - Zero
Bit 1: V - Overflow
Bit 0: C - Carry

System Byte:

Bit 15: T1 - Trace enable
Bit 14: T0 - Trace enable
Bit 13: S  - Supervisor state
Bits 10-8: IPL - Interrupt priority level mask

7.3 Addressing Modes

The 68020 supports 12 basic addressing modes plus extensions:

Mode Syntax Description
Data Register Direct Dn Data in register
Address Register Direct An Address in register
Address Register Indirect (An) Memory at address in An
Address Indirect Postincrement (An)+ Indirect, then increment An
Address Indirect Predecrement -(An) Decrement An, then indirect
Address Indirect with Displacement (d16,An) An + signed 16-bit offset
Address Indirect with Index (d8,An,Xn) An + Xn + signed 8-bit offset
Absolute Short (xxx).W 16-bit address, sign-extended
Absolute Long (xxx).L Full 32-bit address
PC with Displacement (d16,PC) PC + signed 16-bit offset
PC with Index (d8,PC,Xn) PC + Xn + signed 8-bit offset
Immediate # Immediate value

68020-Specific Extensions:

Mode Syntax Description
Memory Indirect Preindexed ([bd,An,Xn],od) Double indirection with preindex
Memory Indirect Postindexed ([bd,An],Xn,od) Double indirection with postindex
PC Memory Indirect ([bd,PC,Xn],od) PC-relative indirect
Scaled Indexing (d8,An,Xn*scale) Scale factor ×1, ×2, ×4, or ×8

7.4 Instruction Set

Data Movement: MOVE, MOVEA, MOVEM, MOVEQ, MOVEP, LEA, PEA, EXG, SWAP, LINK, UNLK

Arithmetic: ADD, ADDA, ADDI, ADDQ, ADDX SUB, SUBA, SUBI, SUBQ, SUBX MULU, MULS, DIVU, DIVS, DIVUL, DIVSL NEG, NEGX, CLR, CMP, CMPA, CMPI, CMPM TST, EXT, EXTB

Logical: AND, ANDI, OR, ORI, EOR, EORI, NOT

Shift and Rotate: ASL, ASR, LSL, LSR, ROL, ROR, ROXL, ROXR

Bit Manipulation: BTST, BCHG, BCLR, BSET

Bit Field (68020): BFTST, BFEXTU, BFEXTS, BFCHG, BFCLR, BFSET, BFFFO, BFINS

BCD Arithmetic: ABCD, SBCD, NBCD, PACK, UNPK

Program Control: Bcc (14 conditions), DBcc, Scc, JMP, JSR, RTS, RTE, RTR, RTD, TRAP, TRAPV, CHK, CHK2, TAS

System Control: MOVE to/from SR, MOVE USP, MOVEC, MOVES, RESET, STOP, NOP, ILLEGAL, ORI/ANDI/EORI to CCR/SR

Atomic Operations (68020): CAS, CAS2

7.5 FPU (68881/68882) Features

Data Types:

  • 80-bit extended precision (IEEE 754 compliant)
  • 8 floating-point registers (FP0-FP7)
  • Control registers: FPCR, FPSR, FPIAR

Basic Operations:

Instruction Description
FMOVE Move floating-point data
FADD Add
FSUB Subtract
FMUL Multiply
FDIV Divide
FNEG Negate
FABS Absolute value
FCMP Compare
FTST Test
FSQRT Square root
FINT Integer part
FINTRZ Integer part (round to zero)
FMOD Modulo
FREM IEEE remainder
FSCALE Scale by power of 2
FSGLDIV Single-precision divide
FSGLMUL Single-precision multiply
FGETEXP Extract exponent
FGETMAN Extract mantissa

Transcendental Functions:

Instruction Description
FSIN Sine
FCOS Cosine
FTAN Tangent
FASIN Arc sine
FACOS Arc cosine
FATAN Arc tangent
FATANH Inverse hyperbolic tangent
FSINH Hyperbolic sine
FCOSH Hyperbolic cosine
FTANH Hyperbolic tangent
FLOG10 Base-10 logarithm
FLOGN Natural logarithm
FLOGNP1 ln(1+x)
FLOG2 Base-2 logarithm
FETOX e^x
FETOXM1 e^x - 1
FTWOTOX 2^x
FTENTOX 10^x

ROM Constants (FMOVECR):

The FPU provides built-in constants:

  • Pi (π)
  • e (Euler's number)
  • log₂(e), log₁₀(e)
  • ln(2), ln(10)
  • Powers of 10 (10⁰ through 10⁴)

FPU Condition Codes:

  • N (Negative) - Result is negative
  • Z (Zero) - Result is zero
  • I (Infinity) - Result is infinite
  • NAN - Result is Not a Number

7.6 Memory and I/O Integration

  • Uses the shared MachineBus; visible RAM is the M68K 32-bit profile ceiling clamped against the autodetected active visible RAM (EmuTOS and AROS profiles further restrict via EmuTOS_PROFILE_TOP / AROS_PROFILE_TOP in profile_bounds.go)
  • 32-bit address bus (full 4 GiB architectural visible range; profile bounds may expose less)
  • Big-endian byte order
  • I/O region: 0x00F00000 - 0x00FFFFFF
  • VRAM: 0x00100000 - 0x004FFFFF (direct access)
  • Exception vector table: 0x00000000 (relocatable via VBR)
  • Default stack: 0x00FF0000

7.7 Interrupts and Exceptions

Exception Vector Table (256 vectors):

Vector 0: Initial SSP
Vector 1: Initial PC (reset)
Vector 2: Bus Error
Vector 3: Address Error
Vector 4: Illegal Instruction
Vector 5: Zero Divide
Vector 6: CHK/CHK2 Instruction
Vector 7: TRAPcc, TRAPV, cpTRAPcc
Vector 8: Privilege Violation
Vector 9: Trace
Vector 10: Line-A Emulator
Vector 11: Line-F Emulator (FPU)
Vectors 24-31: Spurious + Auto-vectored Interrupts
Vectors 32-47: TRAP #0-15
Vectors 48-63: FPU Exceptions
Vectors 64-255: User Defined

Interrupt Priorities:

  • Level 7: Non-maskable
  • Levels 1-6: Maskable (compared against SR IPL)
  • Level 0: No interrupt

7.8 Compatibility Notes

  • 95%+ instruction coverage (68020 + 68881/68882)
  • Full 68020 with 32-bit addressing (no MMU)
  • Big-endian byte order (converted from host)
  • F-line opcodes route to FPU when present
  • Use -m68k flag to run M68K binaries
  • File extension: .ie68

Not Implemented:

  • MMU and address translation
  • Coprocessor interface (beyond FPU)
  • Instruction cache emulation
  • Trace mode (T0/T1 bits defined but not enforced)
  • Dynamic bus sizing

8. Intel x86 CPU (32-bit)

The Intuition Engine includes an x86 core implementing the 8086 instruction set with 32-bit register extensions in a flat memory model. This provides 32-bit programming without the complexity of protected mode, segment descriptors, or paging.

8.1 Register Set

The x86 provides 32-bit general purpose registers with 16-bit and 8-bit access:

General Purpose Registers:

EAX (AX, AH, AL) - Accumulator
EBX (BX, BH, BL) - Base
ECX (CX, CH, CL) - Counter
EDX (DX, DH, DL) - Data
ESI (SI)         - Source Index
EDI (DI)         - Destination Index
EBP (BP)         - Base Pointer
ESP (SP)         - Stack Pointer

Special Registers:

EIP - Instruction Pointer (32-bit)
EFLAGS - Status flags (32-bit)

Segment Registers (for 8086 compatibility):

CS - Code Segment
DS - Data Segment
ES - Extra Segment
SS - Stack Segment
FS, GS - Additional segments (386+)

8.2 Status Flags

The EFLAGS register contains:

Bit 0:  CF - Carry flag
Bit 2:  PF - Parity flag
Bit 4:  AF - Auxiliary carry flag
Bit 6:  ZF - Zero flag
Bit 7:  SF - Sign flag
Bit 8:  TF - Trap flag
Bit 9:  IF - Interrupt enable flag
Bit 10: DF - Direction flag
Bit 11: OF - Overflow flag

8.3 Addressing Modes

Mode Syntax Description
Immediate imm8/imm16/imm32 Immediate value
Register reg Register operand
Direct [addr] Direct memory address
Register Indirect [reg] Memory at register address
Base+Displacement [reg+disp] Base register + offset
SIB [base+index*scale+disp] Full 386 addressing

8.4 Instruction Set

The x86 core implements the 8086 instruction set with 32-bit register support:

Data Transfer: MOV, PUSH, POP, XCHG, LEA, LES, LDS Arithmetic: ADD, ADC, SUB, SBB, MUL, IMUL, DIV, IDIV, INC, DEC, CMP, NEG Logical: AND, OR, XOR, NOT, TEST Shift/Rotate: SHL, SHR, SAL, SAR, ROL, ROR, RCL, RCR Control Flow: JMP, Jcc, CALL, RET, LOOP, LOOPE, LOOPNE String: MOVS, STOS, LODS, CMPS, SCAS with REP/REPE/REPNE prefixes I/O: IN, OUT (port-based I/O for audio chips) Flag Control: CLC, STC, CMC, CLD, STD, CLI, STI BCD: DAA, DAS, AAA, AAS, AAM, AAD

32-bit Register Extensions:

  • 32-bit register operations (EAX, EBX, etc.)
  • Operand size prefix (0x66) for 16/32-bit switching
  • Address size prefix (0x67)
  • SIB byte addressing for complex memory operands

Additional Instructions:

  • MOVZX, MOVSX (zero/sign extend)
  • SETcc (conditional byte set)
  • Bit test: BT, BTS, BTR, BTC, BSF, BSR
  • SHLD, SHRD (double-precision shifts)

8.5 Memory and I/O Integration

  • Full 32-bit flat address space; visible RAM is the x86 32-bit profile ceiling clamped against the autodetected active visible RAM
  • VGA VRAM at standard PC address 0xA0000-0xAFFFF
  • Hardware registers memory-mapped at 0xF0000+
  • Separate I/O port space for audio chips

Port-Based Audio Chip Access:

Chip Ports Description
PSG 0xF0-0xF1 Register select, data
POKEY 0xD0-0xDF Direct register access
SID 0xE0-0xE1 Register select, data
TED 0xF2-0xF3 Register select, data

Standard VGA Ports:

Port Description
0x3C4-0x3C5 Sequencer index/data
0x3C6-0x3C9 DAC mask, read/write index, data
0x3CE-0x3CF Graphics controller index/data
0x3D4-0x3D5 CRTC index/data
0x3DA Input status (VSync)

8.6 Interrupts

The x86 supports software interrupts:

INT n     ; Call interrupt n
INT 3     ; Breakpoint
INTO      ; Overflow interrupt
IRET      ; Return from interrupt

8.7 Compatibility Notes

  • Use -x86 flag to run x86 binaries
  • File extension: .ie86
  • Use NASM or FASM for assembly with ie86.inc include file
  • Programs always load at address 0x00000000 and begin execution there

Memory Model:

The x86 core uses a simplified flat memory model:

  • Segment registers exist but are ignored for address calculation
  • All memory accesses use the 32-bit offset directly (no segment:offset)
  • Full 32-bit address space accessible without segment arithmetic
  • This is neither true real mode (1MB limit) nor protected mode

x87 FPU (387 scope):

  • x87 escape opcodes D8-DF are implemented with stack-based floating-point operations
  • Data movement, arithmetic, compares, control ops, and ENV/SAVE/RESTORE paths are supported
  • Deliberate exclusions: FCMOV*, FCOMI/FCOMIP, FUCOMI/FUCOMIP, and SSE-family instructions
  • Deliberate deviations: large-argument trig reduction always completes (C2=0), FPREM/FPREM1 complete in one step (C2=0), and ENV/SAVE always use the 32-bit layout

Not Implemented:

  • Real mode segment:offset addressing
  • Protected mode (descriptor tables, privilege levels)
  • Virtual 8086 mode
  • Paging and virtual memory
  • Task switching

9. IE64 CPU Architecture

The IE64 is a custom 64-bit RISC processor designed for the Intuition Engine. It uses a clean load-store architecture with compare-and-branch semantics (no flags register), 32 general-purpose registers, and fixed 8-byte instructions.

9.1 Register Set

Register Width Description
R0 64-bit Hardwired zero (writes ignored)
R1–R30 64-bit General-purpose
R31 (SP) 64-bit Stack pointer
PC 64-bit Program counter (full 64-bit IE64 address; legacy 25-bit/32 MB mask retired in PLAN_MAX_RAM.md slice 3 — historical note)

No flags register. Conditional branches embed a register comparison directly (e.g., BEQ Rs, Rt, offset).

Initial State:

  • PC = $1000 (program start)
  • SP (R31) = $9F000 (top of stack, grows downward)
  • All other registers = 0

9.2 Instruction Format

All instructions are 8 bytes (64 bits), little-endian:

Byte 0:   Opcode      (8 bits)
Byte 1:   Rd[4:0]     (5 bits) | Size[1:0] (2 bits) | X (1 bit)
Byte 2:   Rs[4:0]     (5 bits) | unused    (3 bits)
Byte 3:   Rt[4:0]     (5 bits) | unused    (3 bits)
Bytes 4-7: imm32      (32-bit LE immediate)

Size codes: .B (8-bit), .W (16-bit), .L (32-bit), .Q (64-bit, default)

X bit: 0 = third operand is register (Rt), 1 = third operand is immediate (imm32)

9.3 Addressing Modes

Mode Syntax Description Example
Immediate #value Constant value move.l r1, #42
Register Rn Register contents add r1, r2, r3
Register-indirect (data) (Rs) Memory at address in Rs load.l r1, (r2)
Register-indirect (control) (Rs) Transfer control to address in Rs jmp (r5)
Displacement offset(Rs) Memory/control at Rs + offset load.l r1, 16(r2)
PC-relative label Branch target relative to PC bra loop

9.4 Instruction Set

Data Movement

Mnemonic Description Example
MOVE Move register/immediate to register move.l r1, #100
MOVT Move to upper 32 bits movt r1, #$DEAD
MOVEQ Move with sign-extend 32→64 moveq r1, #-1
LEA Load effective address lea r1, offset(r2)

Load/Store

Mnemonic Description Example
LOAD.x Load from memory (size suffix) load.l r1, (r2)
STORE.x Store to memory (size suffix) store.l r1, (r2)

Size suffixes: .b (8-bit), .w (16-bit), .l (32-bit), .q (64-bit)

Arithmetic

Mnemonic Description
ADD Add (register or immediate)
SUB Subtract
MULU Multiply unsigned
MULS Multiply signed
DIVU Divide unsigned
DIVS Divide signed
MOD Modulo
NEG Negate

Logical and Shifts

Mnemonic Description
AND Bitwise AND
OR Bitwise OR
EOR Bitwise exclusive OR
NOT Bitwise NOT
LSL Logical shift left
LSR Logical shift right
ASR Arithmetic shift right
CLZ Count leading zeros (32-bit)

Branches (Compare-and-Branch)

All conditional branches compare two registers directly - no flags register:

Mnemonic Condition Example
BRA Always bra loop
BEQ Rs == Rt beq r1, r2, equal
BNE Rs != Rt bne r1, r0, nonzero
BLT Rs < Rt (signed) blt r1, r2, less
BGE Rs >= Rt (signed) bge r1, r2, greater_eq
BGT Rs > Rt (signed) bgt r1, r2, greater
BLE Rs <= Rt (signed) ble r1, r2, less_eq
BHI Rs > Rt (unsigned) bhi r1, r2, higher
BLS Rs <= Rt (unsigned) bls r1, r2, lower_same
JMP Register-indirect jmp (r5) / jmp 16(r5)

Subroutine and Stack

Mnemonic Description
JSR Jump to subroutine - PC-relative (jsr label) or register-indirect (jsr (r5))
RTS Return from subroutine
PUSH Push register onto stack
POP Pop from stack to register

System

Mnemonic Description
NOP No operation
HALT Halt processor
SEI Set (enable) interrupts
CLI Clear (disable) interrupts
RTI Return from interrupt
WAIT Wait for specified microseconds

MMU / Privilege

Mnemonic Description
MTCR CRn, Rs Move register to control register (supervisor only)
MFCR Rd, CRn Move control register to register (supervisor only; CR6/TP is readable from user mode)
ERET Exception return: PC = FAULT_PC, switch to user mode (supervisor only)
TLBFLUSH Flush entire software TLB (supervisor only)
TLBINVAL Rs Invalidate TLB entry for virtual address in Rs (supervisor only)
SYSCALL #imm32 Trap to supervisor mode with syscall number in imm32
SMODE Rd Read current privilege mode into Rd (1=supervisor, 0=user)

Atomic Memory Operations

Mnemonic Description
CAS Rd, disp(Rs), Rt Compare-and-swap: if [addr]==Rd then [addr]=Rt; Rd=old value
XCHG Rd, disp(Rs), Rt Exchange: [addr]=Rt; Rd=old value
FAA Rd, disp(Rs), Rt Fetch-and-add: [addr]+=Rt; Rd=old value
FAND Rd, disp(Rs), Rt Fetch-and-and: [addr]&=Rt; Rd=old value
FOR Rd, disp(Rs), Rt Fetch-and-or: [addr]|=Rt; Rd=old value
FXOR Rd, disp(Rs), Rt Fetch-and-xor: [addr]^=Rt; Rd=old value

All atomic operations are 64-bit, sequentially consistent, and require 8-byte aligned addresses. Misaligned access traps with FAULT_MISALIGNED (cause 7).

Pseudo-Instructions

The ie64asm assembler provides these convenience pseudo-instructions:

Pseudo Expansion Description
la Rd, addr lea Rd, addr(r0) Load address into register
li Rd, #imm32 move.l Rd, #imm32 Load 32-bit immediate
li Rd, #imm64 move.l Rd, #lo32 + movt Rd, #hi32 Load full 64-bit immediate
beqz Rs, label beq Rs, r0, label Branch if zero
bnez Rs, label bne Rs, r0, label Branch if not zero
bltz Rs, label blt Rs, r0, label Branch if less than zero
bgez Rs, label bge Rs, r0, label Branch if greater or equal to zero
bgtz Rs, label bgt Rs, r0, label Branch if greater than zero
blez Rs, label ble Rs, r0, label Branch if less or equal to zero

Common Patterns

    include "ie64.inc"

start:
    ; Set up video
    la   r1, VIDEO_CTRL
    move.l r2, #1
    store.l r2, (r1)

    ; Loop with counter
    move.l r10, #0              ; counter
    move.l r11, #100            ; limit
.loop:
    add.l  r10, r10, #1
    blt    r10, r11, .loop      ; compare-and-branch

    ; Subroutine call
    jsr    my_func
    halt

my_func:
    push   r5
    ; ... work ...
    pop    r5
    rts

9.5 FPU Logic

The IE64 includes a dedicated Floating-Point Unit (FPU) for IEEE-754 single- precision and register-pair double-precision arithmetic.

FPU Register File

  • F0–F15: 16 dedicated 32-bit registers for floating-point bit patterns.
  • D0–D7: FP64 register pairs mapped as D0=F0:F1, D1=F2:F3, ... D7=F14:F15.
  • FPSR: Status register containing overwritten condition codes (N, Z, I, NaN) and sticky exception flags (IO, DZ, OE, UE).
  • FPCR: Control register for setting the rounding mode (Nearest, Zero, Floor, Ceil).

Native FPU Instructions

  • Arithmetic: FADD, FSUB, FMUL, FDIV, FMOD, FABS, FNEG, FSQRT, FINT
  • Compare: FCMP (three-way compare: returns -1, 0, or +1 in integer register Rd)
  • Transcendentals: FSIN, FCOS, FTAN, FATAN, FLOG, FEXP, FPOW
  • Movement/Conversion: FMOV, FLOAD, FSTORE, FCVTIF (int→float), FCVTFI (float→int), FMOVI, FMOVO (bitwise reinterpret)
  • Status/Constants: FMOVSR, FMOVCR, FMOVSC, FMOVCC, FMOVECR (load ROM Pi, e, etc.)
  • FP64 Arithmetic: DADD, DSUB, DMUL, DDIV, DMOD, DABS, DNEG, DSQRT, DINT
  • FP64 Movement/Conversion: DMOV, DLOAD, DSTORE, DCMP, DCVTIF, DCVTFI, FCVTSD, FCVTDS

FPU instructions are unsized; the assembler rejects size suffixes on both the f* and d* families. FP64 uses even-numbered operands because each double occupies an even-odd register pair.

9.6 Memory and I/O Integration

The IE64 shares the same MachineBus and memory-mapped device address space as all other Intuition Engine CPUs. IE64 sees the full active visible RAM (which may exceed 4 GiB on hosts with sufficient memory) — discoverable via CR_RAM_SIZE_BYTES and the SYSINFO_ACTIVE_RAM_LO/HI MMIO pair:

  • All hardware registers at $F0000–$FFFFF are accessible via LOAD/STORE
  • VGA VRAM at $A0000–$AFFFF, Video RAM at $100000+
  • VRAM direct-write fast path for stores to Video RAM ($100000+, attached VRAM window)
  • 64-bit bus operations (Read64/Write64) with I/O region split semantics for device registers

9.7 Interrupt Handling

The IE64 has an integrated timer and interrupt system:

  • Interrupt vector: Internal interruptVector field (set to 0 on reset)
  • Timer: Integrated CPU timer, decremented every 44100 cycles
  • Timer state: Internal CPU fields (not memory-mapped timer registers)

Interrupt flow:

  1. Timer counts down; when it reaches zero, an interrupt fires
  2. If interruptEnabled is true and not already in an ISR, CPU sets inInterrupt=true
  3. CPU pushes PC to stack and jumps to interruptVector
  4. ISR executes and returns via RTI (restores PC, clears inInterrupt)

Instructions:

  • SEI - Enable interrupts
  • CLI - Disable interrupts
  • RTI - Return from interrupt (pops PC, clears inInterrupt)

Note: The interrupt vector is currently set internally. Assembly-level vector programming is reserved for a future update.

9.8 Compatibility Notes

  • Use -ie64 flag to run IE64 binaries
  • File extension: .ie64
  • Use ie64asm assembler with ie64.inc include file
  • Little-endian byte order
  • Compare-and-branch model (no flags register - unlike IE32, M68K, Z80, 6502, x86)
  • R0 is hardwired to zero (reads always return 0, writes are silently ignored)
  • .l operations zero-mask to 32 bits; use .q for full 64-bit arithmetic
  • JIT compilation is enabled by default on supported platforms (x86-64 and ARM64); use -nojit to force interpreter mode. On x86-64, M68K, Z80, 6502, and x86 cores also have JIT backends
  • JIT and interpreter produce identical results for all programs (verified by test suite)
  • Full ISA reference: IE64_ISA.md
  • Assembly cookbook: IE64_COOKBOOK.md

9.9 EhBASIC IE64

The Intuition Engine includes a full port of Lee Davison's Enhanced BASIC (EhBASIC) for the IE64 CPU. The interpreter is a ground-up reimplementation in IE64 assembly, using IEEE 754 single-precision (FP32) arithmetic and providing direct access to all hardware subsystems from BASIC.

Running

# Run with embedded BASIC image (requires 'make basic' build)
./bin/IntuitionEngine -basic

# Run with a custom BASIC binary
./bin/IntuitionEngine -basic-image path/to/custom.ie64

# Boot EmuTOS from the BASIC prompt (requires 'make basic-emutos' build)
# At the Ready prompt, type: EMUTOS

Features

  • Core language: PRINT, LET, IF/THEN/ELSE, FOR/NEXT/STEP, WHILE/WEND, DO/LOOP, GOTO, GOSUB/RETURN, DATA/READ, INPUT, DIM (multi-dimensional arrays), string variables and operations
  • Built-in functions: ABS, INT, SQR, RND, SGN, SIN, COS, TAN, ATN, LOG, EXP, LEN, ASC, VAL, CHR$, LEFT$, RIGHT$, MID$, STR$, HEX$, BIN$, PEEK, POKE, USR, MAX, MIN, BITTST, FRE
  • Video commands: SCREEN, CLS, PLOT, LINE, CIRCLE, BOX, PALETTE, LOCATE, COLOR, SCROLL, VSYNC, plus ULA, TED, ANTIC/GTIA, and full Voodoo 3D pipeline (vertices, triangles, textures, Z-buffer, alpha blending, fog)
  • Audio commands: SOUND, ENVELOPE, GATE, WAVE, FILTER, REVERB, OVERDRIVE, SWEEP, SYNC, RINGMOD, plus PSG/SID/POKEY/TED/AHX/MOD playback and STATUS queries
  • System commands: CALL (machine code subroutine), USR (call with return value), POKE8/PEEK8, DOKE/DEEK, WAIT, BLIT, COPPER, TRON/TROFF (trace mode)
  • Coprocessor commands: COSTART, COSTOP, COWAIT (worker lifecycle); COCALL(), COSTATUS() (async cross-CPU RPC to IE32/6502/M68K/Z80/x86 workers)
  • Machine code interface: CALL and USR use register-indirect JSR to invoke IE64 assembly routines; R8 carries return values (EhBASIC convention; see IE64_ABI.md for IntuitionOS ABI)
  • Terminal editor: Insert mode with character shifting, key repeat, Ctrl shortcuts (A/E/K/U/L), Ctrl+Arrow word movement, command history (Ctrl+Up/Down), Page Up/Down and mouse wheel scrollback navigation, Shift+Arrow text selection with clipboard copy/cut/paste (Ctrl+Shift+C/X/V)

Common REPL Commands

  • SOUND PLAY "music.ext" [,subsong] starts music playback and auto-detects format by extension: .sid, .ym, .ay, .vgm, .vgz, .vtx, .sndh, .pt3, .pt2, .pt1, .stc, .sqt, .asc, .ftc, .sap, .ted, .prg, .ahx, .mod, .wav (stops any currently playing track first)
  • SOUND STOP stops current music playback (SOUND PLAY STOP is also accepted)
  • RUN executes the current BASIC program; RUN "program.ie64" (or .iex/.ie32/.ie68/.ie86/.ie80/.ie65) loads and launches an external binary

Example

10 SCREEN &H13
20 FOR I = 0 TO 199
30   PLOT 160, I, I AND 255
40 NEXT
50 VSYNC

Full reference: ehbasic_ie64.md

9.10 Memory Management Unit (MMU)

The IE64 includes a minimal MMU for memory protection and virtual address translation, designed to support microkernel operating systems.

  • Paged virtual memory: 4 KiB pages, single-level page table (8192 entries, 64 KiB)
  • Privilege levels: Supervisor (boot default) and User. Mode transitions only via trap entry (SYSCALL, faults) and ERET
  • Per-page permissions: Present, Read, Write, Execute, User-accessible, Accessed (A), Dirty (D)
  • A/D bits: Hardware-maintained Accessed and Dirty bits in each PTE for page reclamation and working-set estimation
  • W^X support: Pages with X=0 are non-executable - code pages are X=1,W=0; data/stack pages are W=1,X=0
  • Execute-only user text (M15.6 R4): executable user pages no longer imply readability; validate_user_exec_range keys on X, and ELF/descriptor loaders preserve PF_X without forcing R
  • SMEP/SMAP-equivalent guards (M15.6): SKEF (fault on supervisor fetch from user page) and SKAC (fault on supervisor data access to user page) bits in MMU_CTRL, plus an explicit SUA latch toggled by privileged SUAEN/SUADIS opcodes so kernel user-memory touches must bracket an access window
  • Architectural trap-frame stack (M15.6): nested-trap state (FAULT_PC, PREV_MODE, SAVED_SUA, FAULT_ADDR, FAULT_CAUSE) is preserved by the CPU across trap entry / ERET; handlers no longer need manual MFCR/MTCR save/restore to survive a nested synchronous trap
  • Zero-on-free confidentiality (M15.6): FreeMem and shared-memory last-reference teardown scrub backing pages before they return to the allocator pool, so a later task cannot observe prior-owner bytes
  • Per-task quotas (M15.6 G3): per-task quotas (pages, ports, waiters, shared mappings, grants) cap blast radius so one task cannot exhaust kernel-tracked resources needed by its siblings
  • MapShared permission preservation (M15.6): shared-memory consumers must now pass an explicit MAPF_READ / MAPF_WRITE mask, and the kernel preserves that requested access in the consumer PTEs instead of silently widening every mapping to RW
  • Stack guard pages (M15.6 R1): user stacks and the kernel stack each reserve one non-present page below the mapped stack floor, so overflow faults cleanly as FAULT_NOT_PRESENT
  • Minimal kernel stack canary (M15.6 R8): an optional KERNEL_STACK_CANARY_ENABLED build flag writes one sentinel word at the bottom of the mapped kernel stack page and checks it on trap / interrupt entry. This is a narrow diagnostic tripwire; the guard page remains the real hardening boundary.
  • Heap guard pages (M15.6 R2): AllocMem(MEMF_GUARD) reserves one non-present page on each side of the mapped body, and MapShared preserves that contract for guarded shared allocations, so one-page underruns/overruns fault as FAULT_NOT_PRESENT
  • Size-arithmetic hardening (M15.6 R3): allocation/page-count conversions are range-clamped before size + offset style arithmetic, so oversized AllocMem and trusted-ELF bridge requests fail cleanly instead of wrapping
  • Scoped atomic RMW groundwork (M15.6 R9): the kernel now ships named m16_atomic_row_try_transition, m16_atomic_list_push_head, and m16_atomic_list_detach_all helpers built on IE64 CAS/XCHG, freezing the exact helper layer M16 will use for registry-row transitions and waiters_head / opens_head updates without widening M15.6 into a general lock-free-kernel effort
  • Software TLB: 64-entry direct-mapped cache of page table translations
  • 15 control registers: Page table base (PTBR), fault address/cause/PC, trap vector, MMU control, thread pointer (TP), interrupt vector (INTR_VEC), kernel/user stack pointers (KSP/USP), timer period/count/control, previous mode (PREV_MODE), saved SUA (SAVED_SUA)
  • 9 MMU instructions: MTCR, MFCR, ERET, TLBFLUSH, TLBINVAL, SYSCALL, SMODE, SUAEN, SUADIS
  • Automatic stack switching: Kernel/user stack separation via KSP/USP on privilege transitions
  • Unified timer interrupts: ERET-based timer delivery when MMU is enabled (legacy RTI model when MMU is off)
  • JIT compatible: JIT compiler works with MMU enabled (Stage 1: memory ops bail to interpreter). Host JIT memory itself is W^X as of M15.6 (dual-mapped RW emit view + RX execution view, never simultaneously writable and executable)

Full reference: IE64_ISA.md §12 | Programming examples: IE64_COOKBOOK.md

10. Assembly Language Reference

This section documents the IE32 assembly language used with the ie32asm assembler. For IE64, 6502, Z80, M68K, and x86 programming, use their respective assemblers (ie64asm, ca65, vasmz80_std, vasmm68k_mot, NASM/FASM) with the include files documented in Section 13.4.

The Intuition Engine assembly language provides a straightforward way to program the system while maintaining access to all hardware features.

10.1 Basic Program Structure

Every assembly program follows this basic structure:

; Program header with description
; Example: Simple colour toggle program
.equ VIDEO_MODE, 0xF0004    ; Define hardware constants

start:                     ; Main entry point
    LOAD A, #0             ; Initialise mode index
    JSR set_video_mode
main_loop:
    JSR update_mode        ; Update mode state
    JMP main_loop          ; Continue main loop

; Subroutines follow main program
set_video_mode:
    STORE A, @VIDEO_MODE
    RTS

10.2 Assembler Directives

The assembler supports these directives:

.equ SYMBOL, VALUE   ; Define a constant
.word VALUE          ; Define a 32-bit word
.byte VALUE          ; Define an 8-bit byte
.space SIZE          ; Reserve bytes of space
.org ADDRESS         ; Set assembly address

The .org directive provides control over code placement:

; Example memory organisation
.org 0x0000               ; Start at vector table
    .word isr_handler     ; Set up interrupt vector

.org 0x1000               ; Place main program
start:
    JSR init
    SEI
    JMP main

10.3 Memory Access Patterns

When working with memory, consider alignment and efficiency:

; Efficient memory copy
copy_memory:
    LOAD X, #0           ; Source index
    LOAD Y, #0           ; Destination index
    LOAD Z, #100         ; Word count
copy_loop:
    LOAD A, [X]          ; Load from source
    STORE A, [Y]         ; Store to destination
    ADD X, #4            ; Next word (32-bit aligned)
    ADD Y, #4
    SUB Z, #1
    JNZ Z, copy_loop
    RTS

10.4 Stack Usage

The stack is essential for subroutines and temporary storage:

calculate:
    PUSH A              ; Save registers
    PUSH X

    ; Perform calculation
    LOAD X, #0
    ADD X, A

    POP X               ; Restore registers
    POP A               ; in reverse order
    RTS

10.5 Interrupt Handlers

Interrupt handlers must preserve register state:

isr_handler:
    PUSH A              ; Save registers
    PUSH X

    JSR process_irq_event

    POP X              ; Restore registers
    POP A
    RTI                ; Return from interrupt

11. Sound System

The Intuition Engine provides a powerful custom audio synthesizer alongside four emulated classic sound chips (PSG, POKEY, SID, TED). The custom audio chip offers modern synthesis capabilities while maintaining the retro aesthetic.

Custom Audio Chip Overview

The custom audio chip is a 10-channel synthesizer with advanced features:

  • 4 Base Channels: Square, Triangle, Sine, Noise (sawtooth available via legacy register alias on channel 0)
  • 6 SID Extension Channels: 3 SID2 + 3 SID3 for multi-SID .sid file playback
  • 4 Flexible Synth Channels: Uniform register interface, any waveform type per channel
  • Per-Voice ADSR Envelopes: 16-bit attack/decay/release times, 8-bit sustain level
  • Pulse Width Modulation: Variable duty cycle with automatic LFO
  • Frequency Sweep: Portamento and pitch bend effects
  • Hard Sync: Slave oscillator phase reset by master
  • Ring Modulation: Amplitude modulation between channels
  • Global Resonant Filter: Low-pass, high-pass, band-pass with resonance
  • Effects: Overdrive distortion and reverb

Signal Flow

Oscillators → Envelopes → Mix → Filter → Overdrive → Reverb → Output

Sample rate: 44.1kHz with 32-bit floating-point internal processing

Memory Map

Register Block IE32/IE64/M68K Address Z80/6502 Address Description
Global Control $F0800-$F0807 $F800-$F807 Master audio control, envelope shape
Filter $F0820-$F0833 $F820-$F833 Cutoff, resonance, type, modulation
Square Channel $F0900-$F093F $F900-$F93F Frequency, volume, ADSR, duty, PWM, sweep
Triangle Channel $F0940-$F097F $F940-$F97F Frequency, volume, ADSR, sweep
Sine Channel $F0980-$F09BF $F980-$F9BF Frequency, volume, ADSR, sweep
Noise Channel $F09C0-$F09FF $F9C0-$F9FF Frequency, volume, ADSR, noise mode
Sync Sources $F0A00-$F0A0F $FA00-$FA0F Hard sync source per channel
Ring Mod Sources $F0A10-$F0A1F $FA10-$FA1F Ring modulation source per channel
Sawtooth Channel $F0A20-$F0A5F $FA20-$FA5F Frequency, volume, ADSR, sweep
Overdrive $F0A40-$F0A43 $FA40-$FA43 Drive amount (0-255)
Reverb $F0A50-$F0A57 $FA50-$FA57 Mix level and decay time
Flex Channel 0 $F0A80-$F0ABF $FA80-$FABF Configurable waveform channel
Flex Channel 1 $F0AC0-$F0AFF $FAC0-$FAFF Configurable waveform channel
Flex Channel 2 $F0B00-$F0B3F $FB00-$FB3F Configurable waveform channel
Flex Channel 3 $F0B40-$F0B7F $FB40-$FB7F Configurable waveform channel

Register Reference

Global Registers

Offset IE32 Address Name Description
+$00 $F0800 AUDIO_CTRL Master audio control
+$04 $F0804 ENV_SHAPE Global envelope shape (0=ADSR, 1=Saw Up, 2=Saw Down, 3=Loop, 4=SID-style)

Filter Registers

Offset IE32 Address Name Range Description
+$00 $F0820 FILTER_CUTOFF 0-65535 Cutoff frequency (exponential 20Hz-20kHz)
+$04 $F0824 FILTER_RESONANCE 0-255 Resonance/Q factor
+$08 $F0828 FILTER_TYPE 0-3 0=Off, 1=Low-pass, 2=High-pass, 3=Band-pass
+$0C $F082C FILTER_MOD_SOURCE 0-3 Modulation source channel
+$10 $F0830 FILTER_MOD_AMOUNT 0-255 Modulation depth

Dedicated Channel Registers (Square/Triangle/Sine/Noise/Sawtooth)

Each dedicated channel has a similar register layout:

Offset Name Description
+$00 FREQ Frequency (16.8 fixed-point Hz, value = Hz * 256)
+$04 VOL Volume 0-255 (32-bit, only low byte used)
+$08 CTRL Control bits (see below)
+$0C ATTACK Attack time in ms (16-bit)
+$10 DECAY Decay time in ms (16-bit)
+$14 SUSTAIN Sustain level 0-255 (16-bit)
+$18 RELEASE Release time in ms (16-bit)
+$1C DUTY Duty cycle 0-65535 (square only, 32768=50%)
+$20 PWM_RATE PWM oscillation rate (square only)
+$24 PWM_DEPTH PWM depth 0-65535 (square only)
+$28 SWEEP_RATE Frequency sweep rate
+$2C SWEEP_DIR Sweep direction (0=down, 1=up)
+$30 SWEEP_AMT Sweep amount per step
+$34 TARGET Target frequency for sweep

Noise channel additional register:

Offset Name Values Description
+$1C NOISE_MODE 0-3 0=White, 1=Periodic, 2=Metallic, 3=PSG-style

Control Register Bits

Bit Name Description
0 GATE Trigger envelope (1=attack, 0=release)
1 PWM_EN Enable pulse width modulation
2 SWEEP_EN Enable frequency sweep
3 SYNC_EN Enable hard sync to source channel
4 RING_EN Enable ring modulation from source
5 FILTER_EN Route channel through global filter

Flexible Channel Registers

Each flexible channel is 64 bytes ($40) with full synthesis control:

Offset Name Description
+$00 FREQ Frequency (16.8 fixed-point Hz, value = Hz * 256)
+$04 VOL Volume 0-255 (32-bit)
+$08 CTRL Control bits (same as dedicated channels)
+$0C DUTY Duty cycle for square wave
+$10 SWEEP Frequency sweep control
+$14 ATK Attack time in ms (16-bit)
+$18 DEC Decay time in ms (16-bit)
+$1C SUS Sustain level 0-255 (16-bit)
+$20 REL Release time in ms (16-bit)
+$24 WAVE_TYPE Waveform selection (0=square, 1=triangle, 2=sine, 3=noise, 4=saw)
+$28 PWM_CTRL PWM modulation control
+$2C NOISEMODE Noise mode (0=white, 1=periodic, 2=metallic, 3=PSG-style)
+$30 PHASE Reset phase position
+$34 RINGMOD Ring modulation source (bit7=enable, bits0-2=source channel)
+$38 SYNC Hard sync source (bit7=enable, bits0-2=source channel)
+$3C DAC DAC mode: signed 8-bit sample (bypasses waveform and envelope)

Waveform Types:

Value Name Description
0 WAVE_SQUARE Square/pulse wave with PWM support
1 WAVE_TRIANGLE Triangle wave
2 WAVE_SINE Pure sine wave
3 WAVE_NOISE Noise generator
4 WAVE_SAWTOOTH Sawtooth wave

11.1 Sound Channel Types

Each channel offers different synthesis capabilities:

Square Wave Channel

Features:

  • Variable duty cycle control
  • PWM modulation
  • Frequency sweep
  • Ring modulation support
  • ADSR envelope

Configuration Example:

IE32:

setup_square:
    LOAD A, #112640         ; 440 Hz (16.8 fixed-point: 440*256)
    STORE A, @SQUARE_FREQ
    LOAD A, #128            ; 50% duty cycle
    STORE A, @SQUARE_DUTY
    LOAD A, #1              ; Enable PWM
    STORE A, @SQUARE_PWM_CTRL
    LOAD A, #10             ; 10ms attack
    STORE A, @SQUARE_ATK
    LOAD A, #20             ; 20ms decay
    STORE A, @SQUARE_DEC
    LOAD A, #192            ; 75% sustain
    STORE A, @SQUARE_SUS
    LOAD A, #100            ; 100ms release
    STORE A, @SQUARE_REL
    RTS

M68K:

setup_square:
    move.l  #112640,SQUARE_FREQ.l    ; 440 Hz (16.8 fixed-point: 440*256)
    move.l  #128,SQUARE_DUTY.l       ; 50% duty cycle
    move.l  #1,SQUARE_PWM_CTRL.l     ; Enable PWM
    move.l  #10,SQUARE_ATK.l         ; Attack
    move.l  #20,SQUARE_DEC.l         ; Decay
    move.l  #192,SQUARE_SUS.l        ; Sustain
    move.l  #100,SQUARE_REL.l        ; Release
    rts

Z80:

setup_square:
    STORE32 SQUARE_FREQ,112640       ; 440 Hz (16.8 fixed-point: 440*256)
    STORE32 SQUARE_DUTY,128          ; 50% duty cycle
    STORE32 SQUARE_PWM_CTRL,1        ; Enable PWM
    STORE32 SQUARE_ATK,10            ; Attack
    STORE32 SQUARE_DEC,20            ; Decay
    STORE32 SQUARE_SUS,192           ; Sustain
    STORE32 SQUARE_REL,100           ; Release
    ret

6502:

setup_square:
    STORE32 SQUARE_FREQ, 112640      ; 440 Hz (16.8 fixed-point: 440*256)
    STORE32 SQUARE_DUTY, 128         ; 50% duty cycle
    STORE32 SQUARE_PWM_CTRL, 1       ; Enable PWM
    STORE32 SQUARE_ATK, 10           ; Attack
    STORE32 SQUARE_DEC, 20           ; Decay
    STORE32 SQUARE_SUS, 192          ; Sustain
    STORE32 SQUARE_REL, 100          ; Release
    rts

Triangle Wave Channel

Features:

  • Pure harmonic content
  • Frequency sweep
  • Ring modulation support
  • ADSR envelope

Sine Wave Channel

Features:

  • Clean tonal output
  • Frequency sweep
  • Ring modulation support
  • ADSR envelope

Noise Channel

Features:

  • Three noise types:
    • White noise (LFSR-based)
    • Periodic noise
    • Metallic noise
  • Frequency sweep
  • ADSR envelope

Sawtooth Wave Channel

Features:

  • Classic sawtooth waveform (ramps from -1 to +1)
  • polyBLEP anti-aliasing for cleaner high-frequency output
  • Frequency sweep
  • Ring modulation support
  • ADSR envelope

Configuration Example:

IE32:

setup_sawtooth:
    LOAD A, #112640         ; 440 Hz (16.8 fixed-point: 440*256)
    STORE A, @SAW_FREQ
    LOAD A, #192            ; 75% volume
    STORE A, @SAW_VOL
    LOAD A, #10             ; Attack
    STORE A, @SAW_ATK
    LOAD A, #20             ; Decay
    STORE A, @SAW_DEC
    LOAD A, #192            ; Sustain
    STORE A, @SAW_SUS
    LOAD A, #100            ; Release
    STORE A, @SAW_REL
    LOAD A, #1              ; Enable
    STORE A, @SAW_CTRL
    RTS

M68K:

setup_sawtooth:
    move.l  #112640,SAW_FREQ.l        ; 440 Hz (16.8 fixed-point: 440*256)
    move.l  #192,SAW_VOL.l
    move.l  #10,SAW_ATK.l
    move.l  #20,SAW_DEC.l
    move.l  #192,SAW_SUS.l
    move.l  #100,SAW_REL.l
    move.l  #1,SAW_CTRL.l
    rts

Z80:

setup_sawtooth:
    STORE32 SAW_FREQ,112640           ; 440 Hz (16.8 fixed-point: 440*256)
    STORE32 SAW_VOL,192
    STORE32 SAW_ATK,10
    STORE32 SAW_DEC,20
    STORE32 SAW_SUS,192
    STORE32 SAW_REL,100
    STORE32 SAW_CTRL,1
    ret

6502:

setup_sawtooth:
    STORE32 SAW_FREQ, 112640          ; 440 Hz (16.8 fixed-point: 440*256)
    STORE32 SAW_VOL, 192
    STORE32 SAW_ATK, 10
    STORE32 SAW_DEC, 20
    STORE32 SAW_SUS, 192
    STORE32 SAW_REL, 100
    STORE32 SAW_CTRL, 1
    rts

11.2 Modulation System

The sound system supports complex inter-channel modulation for creating rich, evolving timbres.

Hard Sync

Hard sync forces a slave oscillator to reset its phase whenever the master oscillator completes a cycle. This creates complex harmonic content that changes with the frequency ratio between oscillators.

Sync Source Registers:

Register IE32 Address Description
SYNC_SOURCE_CH0 $F0A00 Sync source for channel 0
SYNC_SOURCE_CH1 $F0A04 Sync source for channel 1
SYNC_SOURCE_CH2 $F0A08 Sync source for channel 2
SYNC_SOURCE_CH3 $F0A0C Sync source for channel 3

Set source to channel number (0-3) or $FF to disable.

IE32:

; Sync channel 0 to channel 1 (ch1 is master)
    LOAD A, #1
    STORE A, @SYNC_SOURCE_CH0
    LOAD A, #CTRL_GATE | CTRL_SYNC_EN   ; Enable sync in control
    STORE A, @SQUARE_CTRL

M68K:

    move.l  #1,SYNC_SOURCE_CH0.l
    move.l  #CTRL_GATE|CTRL_SYNC_EN,SQUARE_CTRL.l

Ring Modulation

Ring modulation multiplies two signals together, producing sum and difference frequencies (sidebands). This creates metallic, bell-like tones.

Ring Mod Source Registers:

Register IE32 Address Description
RING_MOD_SOURCE_CH0 $F0A10 Ring mod source for channel 0
RING_MOD_SOURCE_CH1 $F0A14 Ring mod source for channel 1
RING_MOD_SOURCE_CH2 $F0A18 Ring mod source for channel 2
RING_MOD_SOURCE_CH3 $F0A1C Ring mod source for channel 3

IE32:

; Ring modulate channel 0 with channel 1
    LOAD A, #1
    STORE A, @RING_MOD_SOURCE_CH0
    LOAD A, #CTRL_GATE | CTRL_RING_EN
    STORE A, @SQUARE_CTRL

M68K:

    move.l  #1,RING_MOD_SOURCE_CH0.l
    move.l  #CTRL_GATE|CTRL_RING_EN,SQUARE_CTRL.l

Frequency Sweep

Automatic frequency changes for pitch bend, portamento, and laser effects.

Sweep Registers (per channel):

Offset Name Description
SWEEP_RATE Rate of frequency change
SWEEP_DIR Direction: 0=down, 1=up
SWEEP_AMT Amount per step
TARGET Target frequency (sweep stops here)

IE32:

; Sweep square channel up from 220Hz to 880Hz
    LOAD A, #220
    STORE A, @SQUARE_FREQ        ; Start frequency
    LOAD A, #880
    STORE A, @SQUARE_TARGET      ; End frequency
    LOAD A, #1
    STORE A, @SQUARE_SWEEP_DIR   ; Sweep up
    LOAD A, #10
    STORE A, @SQUARE_SWEEP_RATE  ; Sweep speed
    LOAD A, #CTRL_GATE | CTRL_SWEEP_EN
    STORE A, @SQUARE_CTRL        ; Enable sweep

Pulse Width Modulation (PWM)

For square wave channels, PWM automatically varies the duty cycle for a rich, animated sound.

IE32:

; Enable PWM on square channel
    LOAD A, #32768              ; 50% base duty cycle
    STORE A, @SQUARE_DUTY
    LOAD A, #512                ; PWM rate (Hz * 256)
    STORE A, @SQUARE_PWM_RATE
    LOAD A, #16384              ; PWM depth
    STORE A, @SQUARE_PWM_DEPTH
    LOAD A, #CTRL_GATE | CTRL_PWM_EN
    STORE A, @SQUARE_CTRL

11.3 Global Effects

The system provides global audio processing applied after channel mixing.

Filter System

A resonant state-variable filter with three modes:

Type Value Mode Description
0 Off Filter bypassed
1 Low-pass Attenuates frequencies above cutoff
2 High-pass Attenuates frequencies below cutoff
3 Band-pass Passes frequencies around cutoff

Filter Registers:

Register IE32 Address Range Description
FILTER_CUTOFF $F0820 0-65535 Cutoff frequency (exponential mapping)
FILTER_RESONANCE $F0824 0-255 Resonance/Q (higher = more emphasis)
FILTER_TYPE $F0828 0-3 Filter mode
FILTER_MOD_SOURCE $F082C 0-3 Channel to modulate cutoff
FILTER_MOD_AMOUNT $F0830 0-255 Modulation depth

The cutoff uses exponential mapping for musical control (human hearing is logarithmic). Value 0 maps to 20Hz, 65535 maps to 20kHz.

To route a channel through the filter, set bit 5 (CTRL_FILTER_EN) in its control register.

IE32:

    LOAD A, #32768          ; Cutoff (~1kHz)
    STORE A, @FILTER_CUTOFF
    LOAD A, #128            ; Medium resonance
    STORE A, @FILTER_RESONANCE
    LOAD A, #1              ; Low-pass mode
    STORE A, @FILTER_TYPE
    ; Route square channel through filter
    LOAD A, #CTRL_GATE | CTRL_FILTER_EN
    STORE A, @SQUARE_CTRL

M68K:

    move.l  #32768,FILTER_CUTOFF.l
    move.l  #128,FILTER_RESONANCE.l
    move.l  #1,FILTER_TYPE.l
    move.l  #CTRL_GATE|CTRL_FILTER_EN,SQUARE_CTRL.l

Z80:

    SET_FILTER FILT_LOWPASS,32768,128
    ; Enable filter on square channel
    FILTER_SQ_ON

6502:

    SET_FILTER FILT_LOWPASS, 32768, 128
    FILTER_SQ_ON

Overdrive

Soft-clipping distortion for adding grit and harmonics to the output.

Register IE32 Address Range Description
OVERDRIVE_CTRL $F0A40 0-255 0=off, 1-255=distortion amount

Higher values produce more aggressive clipping. Values around 32-64 add subtle warmth, while 128+ creates heavy distortion.

IE32:

    LOAD A, #64             ; Moderate overdrive
    STORE A, @OVERDRIVE_CTRL

M68K:

    move.l  #64,OVERDRIVE_CTRL.l

Z80:

    SET_OVERDRIVE 64

6502:

    SET_OVERDRIVE 64

Reverb System

Stereo reverb with adjustable mix and decay time.

Register IE32 Address Range Description
REVERB_MIX $F0A50 0-255 Dry/wet mix (0=dry, 255=wet)
REVERB_DECAY $F0A54 0-65535 Decay time in ms

IE32:

    LOAD A, #128            ; 50% wet/dry mix
    STORE A, @REVERB_MIX
    LOAD A, #2000           ; 2 second decay
    STORE A, @REVERB_DECAY

M68K:

    move.l  #128,REVERB_MIX.l
    move.l  #2000,REVERB_DECAY.l

Z80:

    SET_REVERB 128,2000

6502:

    SET_REVERB 128, 2000

11.4 PSG Sound Chip (AY-3-8910/YM2149)

The PSG chip emulates the General Instrument AY-3-8910 and Yamaha YM2149, providing three channels of square wave synthesis with noise and envelope capabilities. This chip powered the sound in countless 8-bit computers including the ZX Spectrum 128, Amstrad CPC, Atari ST, and MSX. VGM files targeting the Texas Instruments SN76489 (Sega Master System, Game Gear, BBC Micro, ColecoVision) are also supported via automatic conversion to AY register writes during VGM parsing.

Features:

  • Three independent square wave tone generators
  • One noise generator (shared across channels)
  • Hardware envelope generator with 8 shape patterns
  • Per-channel mixer control (tone/noise enable)
  • 4-bit volume control per channel (or envelope)
  • PSG+ enhanced audio processing mode
  • Support for .YM, .AY, .VGM, .VGZ, and .SNDH file playback (VGM supports both AY-3-8910 and SN76489 chip data; .AY files auto-detect ZX Spectrum, Amstrad CPC, and MSX with correct clock speeds)

Tone Generation:

Each channel has a 12-bit frequency divider:

  • Frequency = Clock / (16 × TP) where TP is the tone period (1-4095)
  • Channel A: Registers 0-1 (fine/coarse tune)
  • Channel B: Registers 2-3 (fine/coarse tune)
  • Channel C: Registers 4-5 (fine/coarse tune)

Noise Generator:

  • 5-bit noise period (Register 6)
  • Pseudo-random output from 17-bit LFSR
  • Can be mixed with any tone channel

Mixer Control (Register 7):

Bit 0: Channel A tone enable (0=on, 1=off)
Bit 1: Channel B tone enable
Bit 2: Channel C tone enable
Bit 3: Channel A noise enable (0=on, 1=off)
Bit 4: Channel B noise enable
Bit 5: Channel C noise enable
Bits 6-7: I/O port direction (directly mapped only)

Volume and Envelope:

  • Registers 8-10: Channel A/B/C amplitude (0-15, or bit 4 set for envelope)
  • Registers 11-12: Envelope period (16-bit)
  • Register 13: Envelope shape (8 patterns)

Envelope Shapes:

Value Shape Description
0-3 ╲____ Decay to zero, hold
4-7 /‾‾‾‾ Attack to max, hold
8 ╲╲╲╲╲ Repeating decay (sawtooth down)
9 ╲____ Decay to zero, hold
10 ╲/╲/╲ Repeating decay-attack (triangle)
11 ╲‾‾‾‾ Decay to zero, then hold max
12 ///// Repeating attack (sawtooth up)
13 /‾‾‾‾ Attack to max, hold
14 /╲/╲/ Repeating attack-decay (triangle)
15 /_____ Attack to max, then hold zero

Configuration Example:

Configure PSG channel A for a 440Hz tone with envelope:

IE32:

; Configure PSG channel A for a 440Hz tone with envelope
LOAD A, #0xFE          ; Tone period low byte (440Hz approx)
STORE A, @0x0F0C00     ; Register 0: Channel A fine tune
LOAD A, #0x00          ; Tone period high byte
STORE A, @0x0F0C01     ; Register 1: Channel A coarse tune
LOAD A, #0x3E          ; Enable tone A, disable noise
STORE A, @0x0F0C07     ; Register 7: Mixer
LOAD A, #0x10          ; Use envelope for volume
STORE A, @0x0F0C08     ; Register 8: Channel A amplitude
LOAD A, #0x00          ; Envelope period low
STORE A, @0x0F0C0B     ; Register 11: Envelope fine
LOAD A, #0x10          ; Envelope period high
STORE A, @0x0F0C0C     ; Register 12: Envelope coarse
LOAD A, #0x0E          ; Triangle envelope shape
STORE A, @0x0F0C0D     ; Register 13: Envelope shape

M68K:

; Configure PSG channel A for a 440Hz tone with envelope
    move.b  #$FE,$F0C00.l       ; Register 0: Channel A fine tune
    move.b  #$00,$F0C01.l       ; Register 1: Channel A coarse tune
    move.b  #$3E,$F0C07.l       ; Register 7: Mixer (tone A on)
    move.b  #$10,$F0C08.l       ; Register 8: Envelope mode
    move.b  #$00,$F0C0B.l       ; Register 11: Envelope fine
    move.b  #$10,$F0C0C.l       ; Register 12: Envelope coarse
    move.b  #$0E,$F0C0D.l       ; Register 13: Triangle shape

Z80:

; Configure PSG channel A for a 440Hz tone with envelope
; Z80 uses port I/O: port $F0 = register select, port $F1 = data
    ld   a,0               ; Select register 0 (fine tune)
    out  ($F0),a
    ld   a,$FE             ; Tone period low byte
    out  ($F1),a
    ld   a,1               ; Select register 1 (coarse tune)
    out  ($F0),a
    ld   a,$00             ; Tone period high byte
    out  ($F1),a
    ld   a,7               ; Select register 7 (mixer)
    out  ($F0),a
    ld   a,$3E             ; Enable tone A
    out  ($F1),a
    ld   a,8               ; Select register 8 (amplitude)
    out  ($F0),a
    ld   a,$10             ; Envelope mode
    out  ($F1),a
    ld   a,11              ; Select register 11 (envelope fine)
    out  ($F0),a
    ld   a,$00
    out  ($F1),a
    ld   a,12              ; Select register 12 (envelope coarse)
    out  ($F0),a
    ld   a,$10
    out  ($F1),a
    ld   a,13              ; Select register 13 (shape)
    out  ($F0),a
    ld   a,$0E             ; Triangle shape
    out  ($F1),a

6502:

; Configure PSG channel A for a 440Hz tone with envelope
; 6502 uses memory-mapped I/O at $D400-$D40D
    lda  #$FE
    sta  $D400             ; Register 0: Channel A fine tune
    lda  #$00
    sta  $D401             ; Register 1: Channel A coarse tune
    lda  #$3E
    sta  $D407             ; Register 7: Mixer (tone A on)
    lda  #$10
    sta  $D408             ; Register 8: Envelope mode
    lda  #$00
    sta  $D40B             ; Register 11: Envelope fine
    lda  #$10
    sta  $D40C             ; Register 12: Envelope coarse
    lda  #$0E
    sta  $D40D             ; Register 13: Triangle shape

File Playback

The PSG player supports multiple music file formats with automatic detection:

  • .ym - YM2149 register dump frames (50Hz playback)
  • .ay - ZXAYEMUL format with embedded Z80 code (ZX Spectrum 1.77 MHz, Amstrad CPC 1.0 MHz, MSX 1.79 MHz auto-detected)
  • .sndh - Atari ST format with embedded M68K code
  • .vgm - Video Game Music format with timed PSG events (AY-3-8910 native; SN76489 auto-converted to AY registers)

To play a file, embed the data in your program and set the player registers:

IE32:

; Play a .ym file with looping
    LOAD A, #1
    STORE A, @PSG_PLUS_CTRL      ; Enable PSG+ enhanced audio
    LOAD A, #music_data          ; Address of embedded music
    STORE A, @PSG_PLAY_PTR
    LOAD A, #music_data_end - music_data
    STORE A, @PSG_PLAY_LEN
    LOAD A, #5                   ; bit0=start, bit2=loop
    STORE A, @PSG_PLAY_CTRL

; Embedded music data
music_data:
    .incbin "music.ym"
music_data_end:

M68K:

; Play a .ym file with looping
    move.b  #1,PSG_PLUS_CTRL.l   ; Enable PSG+ enhanced audio
    lea     music_data,a0
    move.l  a0,PSG_PLAY_PTR.l
    move.l  #music_data_end-music_data,PSG_PLAY_LEN.l
    move.l  #5,PSG_PLAY_CTRL.l   ; bit0=start, bit2=loop

music_data:
    incbin  "music.ym"
music_data_end:

Z80:

; Play a .ym file with looping
    ld   a,1
    ld   (PSG_PLUS_CTRL),a       ; Enable PSG+ enhanced audio
    SET_PSG_PTR music_data
    SET_PSG_LEN (music_data_end-music_data)
    ld   a,5                     ; bit0=start, bit2=loop
    ld   (PSG_PLAY_CTRL),a

music_data:
    incbin  "music.ym"
music_data_end:

6502:

; Play a .ym file with looping
    lda  #1
    sta  PSG_PLUS_CTRL           ; Enable PSG+ enhanced audio
    STORE32 PSG_PLAY_PTR_0, music_data
    STORE32 PSG_PLAY_LEN_0, (music_data_end-music_data)
    lda  #5                      ; bit0=start, bit2=loop
    sta  PSG_PLAY_CTRL

music_data:
    .incbin "music.ym"
music_data_end:

Playback Control:

  • Write 1 to PSG_PLAY_CTRL to start playback
  • Write 2 to PSG_PLAY_CTRL to stop playback
  • Write 5 to PSG_PLAY_CTRL to start with looping (bit0 + bit2)
  • Read PSG_PLAY_STATUS bit 0 to check if playing (1=busy, 0=stopped)
  • Read PSG_PLAY_STATUS bit 1 to check for errors

11.5 POKEY Sound Chip

The POKEY chip emulates the Atari 8-bit computer's sound hardware, providing four channels of distinctive 8-bit audio with polynomial-based distortion.

Features:

  • Four independent frequency channels
  • Multiple distortion modes using polynomial counters (4-bit, 5-bit, 9-bit, 17-bit)
  • 16-bit channel linking for extended frequency range
  • High-pass filter clocking between channels
  • Volume-only mode for sample playback
  • POKEY+ enhanced audio processing mode

Distortion Modes:

The POKEY's signature sound comes from its polynomial-based distortion:

  • Pure Tone (0xA0): Clean square wave
  • Poly5 (0x20): 5-bit polynomial for buzzy tones
  • Poly4 (0xC0): 4-bit polynomial for harsh buzzy sounds
  • Poly17/Poly5 (0x00): Combined for complex timbres
  • Poly17 (0x80): White noise

16-bit Mode:

For higher frequency resolution, channels can be linked:

  • Ch1+Ch2 linked via AUDCTL bit 4
  • Ch3+Ch4 linked via AUDCTL bit 3

Configuration Example:

Configure POKEY for pure tone on channel 1:

IE32:

; Configure POKEY for pure tone on channel 1
LOAD A, #0x50          ; Frequency divider
STORE A, @0xF0D00      ; AUDF1
LOAD A, #0xAF          ; Pure tone + volume 15
STORE A, @0xF0D01      ; AUDC1

M68K:

; Configure POKEY for pure tone on channel 1
    move.b  #$50,$F0D00.l      ; AUDF1: Frequency divider
    move.b  #$AF,$F0D01.l      ; AUDC1: Pure tone + volume 15

Z80:

; Configure POKEY for pure tone on channel 1
; Z80 uses port I/O: port $D0 = register select, port $D1 = data
    ld   a,0               ; Select AUDF1
    out  ($D0),a
    ld   a,$50             ; Frequency divider
    out  ($D1),a
    ld   a,1               ; Select AUDC1
    out  ($D0),a
    ld   a,$AF             ; Pure tone + volume 15
    out  ($D1),a

6502:

; Configure POKEY for pure tone on channel 1
; 6502 uses memory-mapped I/O at $D200-$D209
    lda  #$50
    sta  $D200             ; AUDF1: Frequency divider
    lda  #$AF
    sta  $D201             ; AUDC1: Pure tone + volume 15

SAP File Playback

The POKEY player supports Atari 8-bit music files (.sap) which contain embedded 6502 code that drives the POKEY chip. SAP TYPE B files are supported where INIT is called once and PLAYER is called each frame.

IE32:

; Play a .sap file with looping
    LOAD A, #1
    STORE A, @POKEY_PLUS_CTRL    ; Enable POKEY+ enhanced audio
    LOAD A, #sap_data            ; Address of embedded SAP file
    STORE A, @SAP_PLAY_PTR
    LOAD A, #sap_data_end - sap_data
    STORE A, @SAP_PLAY_LEN
    LOAD A, #0
    STORE A, @SAP_SUBSONG        ; Select subsong 0
    LOAD A, #5                   ; bit0=start, bit2=loop
    STORE A, @SAP_PLAY_CTRL

sap_data:
    .incbin "music.sap"
sap_data_end:

M68K:

; Play a .sap file with looping
    move.b  #1,POKEY_PLUS_CTRL.l
    lea     sap_data,a0
    move.l  a0,SAP_PLAY_PTR.l
    move.l  #sap_data_end-sap_data,SAP_PLAY_LEN.l
    move.b  #0,SAP_SUBSONG.l     ; Subsong 0
    move.l  #5,SAP_PLAY_CTRL.l   ; bit0=start, bit2=loop

sap_data:
    incbin  "music.sap"
sap_data_end:

Z80:

; Play a .sap file with looping
    ld   a,1
    ld   (POKEY_PLUS_CTRL),a
    SET_SAP_PTR sap_data
    SET_SAP_LEN (sap_data_end-sap_data)
    xor  a
    ld   (SAP_SUBSONG),a         ; Subsong 0
    ld   a,5                     ; bit0=start, bit2=loop
    ld   (SAP_PLAY_CTRL),a

sap_data:
    incbin  "music.sap"
sap_data_end:

6502:

; Play a .sap file with looping
    lda  #1
    sta  POKEY_PLUS_CTRL
    STORE32 SAP_PLAY_PTR_0, sap_data
    STORE32 SAP_PLAY_LEN_0, (sap_data_end-sap_data)
    lda  #0
    sta  SAP_SUBSONG             ; Subsong 0
    lda  #5                      ; bit0=start, bit2=loop
    sta  SAP_PLAY_CTRL

sap_data:
    .incbin "music.sap"
sap_data_end:

Playback Control:

  • Write 1 to SAP_PLAY_CTRL to start, 2 to stop, 5 to start with loop
  • Set SAP_SUBSONG before starting to select a specific subsong (0-255)
  • Read SAP_PLAY_STATUS for busy/error flags

11.6 SID Sound Chip

The SID chip emulates the legendary MOS 6581/8580 from the Commodore 64, providing three voices of analog-style synthesis with the distinctive warm sound that defined a generation of computer music.

Features:

  • Three independent voices with full ADSR envelopes
  • Four waveforms per voice: triangle, sawtooth, pulse (with variable width), noise
  • Combined waveforms (AND-style mixing when multiple waveform bits set)
  • Ring modulation between voices
  • Hard sync for complex timbres (accurate sync timing)
  • Test bit support (resets oscillator phase and holds output)
  • OSC3 and ENV3 register readback (oscillator and envelope output)
  • Programmable resonant filter (low-pass, band-pass, high-pass, notch)
  • Rate counter ADSR with exponential decay curve
  • Per-chip model selection: 6581 (non-linear filter, DC offset, warmer sound) or 8580 (linear, cleaner), extracted from PSID v3/v4 header flags per SID chip
  • Multi-SID playback with up to 3 independent SID chips (9 voices total), using Sid2Addr/Sid3Addr from v3/v4 headers
  • SID+ enhanced audio processing mode
  • .SID file playback with embedded 6502 code execution

Waveform Selection:

Each voice can output one waveform at a time via the control register:

  • Triangle (0x10): Smooth, flute-like tone
  • Sawtooth (0x20): Bright, brassy tone with rich harmonics
  • Pulse (0x40): Square wave with variable duty cycle (PWM capable)
  • Noise (0x80): White noise for percussion and effects

ADSR Envelope:

Each voice has a dedicated ADSR envelope generator:

  • Attack: 2ms to 8 seconds (16 rates)
  • Decay: 6ms to 24 seconds (16 rates)
  • Sustain: 16 levels (0-15)
  • Release: 6ms to 24 seconds (16 rates)

Filter:

The SID's resonant filter can process any combination of voices:

  • 11-bit cutoff frequency control
  • 4-bit resonance control
  • Selectable low-pass, band-pass, high-pass modes (combinable for notch)

Configuration Example:

Configure SID voice 1 for a pulse wave with filter:

IE32:

; Configure SID voice 1 for a pulse wave with filter
LOAD A, #0x00
STORE A, @0xF0E00      ; Freq low
LOAD A, #0x1C          ; ~440Hz (A4)
STORE A, @0xF0E01      ; Freq high
LOAD A, #0x00
STORE A, @0xF0E02      ; Pulse width low
LOAD A, #0x08          ; 50% duty
STORE A, @0xF0E03      ; Pulse width high
LOAD A, #0x41          ; Pulse waveform + gate
STORE A, @0xF0E04      ; Control
LOAD A, #0x00          ; Fast attack, no decay
STORE A, @0xF0E05      ; Attack/Decay
LOAD A, #0xF0          ; Full sustain, no release
STORE A, @0xF0E06      ; Sustain/Release
LOAD A, #0x1F          ; Max volume + low-pass
STORE A, @0xF0E18      ; Mode/Volume

M68K:

; Configure SID voice 1 for a pulse wave with filter
    move.b  #$00,$F0E00.l      ; Freq low
    move.b  #$1C,$F0E01.l      ; Freq high (~440Hz)
    move.b  #$00,$F0E02.l      ; Pulse width low
    move.b  #$08,$F0E03.l      ; Pulse width high (50% duty)
    move.b  #$41,$F0E04.l      ; Pulse waveform + gate
    move.b  #$00,$F0E05.l      ; Attack/Decay (fast attack)
    move.b  #$F0,$F0E06.l      ; Sustain/Release (full sustain)
    move.b  #$1F,$F0E18.l      ; Mode/Volume (max + low-pass)

Z80:

; Configure SID voice 1 for a pulse wave with filter
; Z80 uses port I/O: port $E0 = register select, port $E1 = data
    ld   a,0               ; Select freq low register
    out  ($E0),a
    ld   a,$00
    out  ($E1),a
    ld   a,1               ; Select freq high register
    out  ($E0),a
    ld   a,$1C             ; ~440Hz
    out  ($E1),a
    ld   a,2               ; Select pulse width low
    out  ($E0),a
    ld   a,$00
    out  ($E1),a
    ld   a,3               ; Select pulse width high
    out  ($E0),a
    ld   a,$08             ; 50% duty
    out  ($E1),a
    ld   a,4               ; Select control register
    out  ($E0),a
    ld   a,$41             ; Pulse waveform + gate
    out  ($E1),a
    ld   a,5               ; Select attack/decay
    out  ($E0),a
    ld   a,$00             ; Fast attack
    out  ($E1),a
    ld   a,6               ; Select sustain/release
    out  ($E0),a
    ld   a,$F0             ; Full sustain
    out  ($E1),a
    ld   a,$18             ; Select mode/volume
    out  ($E0),a
    ld   a,$1F             ; Max volume + low-pass
    out  ($E1),a

6502:

; Configure SID voice 1 for a pulse wave with filter
; 6502 uses memory-mapped I/O at $D500-$D51C (native SID location)
    lda  #$00
    sta  $D500             ; Freq low
    lda  #$1C
    sta  $D501             ; Freq high (~440Hz)
    lda  #$00
    sta  $D502             ; Pulse width low
    lda  #$08
    sta  $D503             ; Pulse width high (50% duty)
    lda  #$41
    sta  $D504             ; Pulse waveform + gate
    lda  #$00
    sta  $D505             ; Attack/Decay (fast attack)
    lda  #$F0
    sta  $D506             ; Sustain/Release (full sustain)
    lda  #$1F
    sta  $D518             ; Mode/Volume (max + low-pass)

SID File Playback

The SID player handles Commodore 64 music files (.sid) which contain embedded 6502 code that drives the SID sound chip. The player executes the 6502 init routine once, then calls the play routine each frame at the correct rate.

IE32:

; Play a .sid file with looping
    LOAD A, #1
    STORE A, @SID_PLUS_CTRL      ; Enable SID+ enhanced audio
    LOAD A, #sid_data            ; Address of embedded SID file
    STORE A, @SID_PLAY_PTR
    LOAD A, #sid_data_end - sid_data
    STORE A, @SID_PLAY_LEN
    LOAD A, #0
    STORE A, @SID_SUBSONG        ; Select subsong 0
    LOAD A, #5                   ; bit0=start, bit2=loop
    STORE A, @SID_PLAY_CTRL

sid_data:
    .incbin "music.sid"
sid_data_end:

M68K:

; Play a .sid file with looping
    move.b  #1,SID_PLUS_CTRL.l
    lea     sid_data,a0
    move.l  a0,SID_PLAY_PTR.l
    move.l  #sid_data_end-sid_data,SID_PLAY_LEN.l
    move.b  #0,SID_SUBSONG.l     ; Subsong 0
    move.l  #5,SID_PLAY_CTRL.l   ; bit0=start, bit2=loop

sid_data:
    incbin  "music.sid"
sid_data_end:

Z80:

; Play a .sid file with looping
    ld   a,1
    ld   (SID_PLUS_CTRL),a
    SET_SID_PTR sid_data
    SET_SID_LEN (sid_data_end-sid_data)
    SET_SID_SUBSONG 0
    START_SID_LOOP               ; Macro: start with looping

sid_data:
    incbin  "music.sid"
sid_data_end:

6502:

; Play a .sid file with looping
    lda  #1
    sta  SID_PLUS_CTRL
    STORE32 SID_PLAY_PTR_0, sid_data
    STORE32 SID_PLAY_LEN_0, (sid_data_end-sid_data)
    lda  #0
    sta  SID_SUBSONG             ; Subsong 0
    lda  #5                      ; bit0=start, bit2=loop
    sta  SID_PLAY_CTRL

sid_data:
    .incbin "music.sid"
sid_data_end:

Playback Control:

  • Write 1 to SID_PLAY_CTRL to start, 2 to stop, 5 to start with loop
  • Set SID_SUBSONG before starting to select a specific subsong (0-255)
  • Read SID_PLAY_STATUS for busy/error flags
  • Many SID files contain multiple subsongs (tunes) - check the SID header for count

11.7 TED Sound Chip

The TED (Text Editing Device) chip emulates the sound capabilities of the Commodore Plus/4 and C16, providing simple 2-voice square wave synthesis. While simpler than the SID, the TED has a distinctive lo-fi character valued by demoscene musicians.

Features:

  • Two independent square wave voices
  • 10-bit frequency control per voice (0-1023)
  • Voice 2 can optionally produce white noise
  • Global 4-bit volume control (0-8)
  • TED+ enhanced audio processing mode
  • .TED file playback with embedded 6502 code execution

Voice Configuration:

Each voice outputs a square wave with 10-bit frequency resolution:

  • Frequency range: ~107 Hz to ~110 kHz (at PAL clock)
  • Formula: freq_hz = clock/8 / (1024 - register_value)
  • Clock: 886724 Hz (PAL), 894886 Hz (NTSC)

Control Register:

The TED_SND_CTRL register controls all audio output:

  • Bit 7: D/A mode (direct audio output)
  • Bit 6: Voice 2 noise enable (white noise instead of square)
  • Bit 5: Voice 2 enable
  • Bit 4: Voice 1 enable
  • Bits 0-3: Volume (0-8, where 8 is maximum)

Configuration Example:

Configure TED for two square wave voices:

IE32:

; Configure TED voice 1 at ~440Hz (A4)
LOAD A, #0xE3          ; Low byte of frequency
STORE A, @0xF0F00      ; TED_FREQ1_LO
LOAD A, #0x01          ; High byte (bits 0-1)
STORE A, @0xF0F04      ; TED_FREQ1_HI

; Configure TED voice 2 at ~880Hz (A5)
LOAD A, #0xF1          ; Low byte of frequency
STORE A, @0xF0F01      ; TED_FREQ2_LO
LOAD A, #0x02          ; High byte
STORE A, @0xF0F02      ; TED_FREQ2_HI

; Enable both voices, volume 8
LOAD A, #0x38          ; ch1on + ch2on + volume 8
STORE A, @0xF0F03      ; TED_SND_CTRL

M68K:

; Configure TED voice 1 at ~440Hz
    move.b  #$E3,$F0F00.l      ; Freq1 low
    move.b  #$01,$F0F04.l      ; Freq1 high

; Configure TED voice 2 at ~880Hz
    move.b  #$F1,$F0F01.l      ; Freq2 low
    move.b  #$02,$F0F02.l      ; Freq2 high

; Enable both voices, volume 8
    move.b  #$38,$F0F03.l      ; Control: ch1on + ch2on + vol8

Z80:

; Configure TED voice 1 at ~440Hz
; Z80 uses port I/O: port $F2 = register select, port $F3 = data
    ld   a,0               ; Select TED_FREQ1_LO
    out  ($F2),a
    ld   a,$E3             ; Low byte
    out  ($F3),a
    ld   a,4               ; Select TED_FREQ1_HI
    out  ($F2),a
    ld   a,$01             ; High byte
    out  ($F3),a

; Enable both voices
    ld   a,3               ; Select TED_SND_CTRL
    out  ($F2),a
    ld   a,$38             ; ch1on + ch2on + volume 8
    out  ($F3),a

6502:

; Configure TED voice 1 at ~440Hz
    lda  #$E3
    sta  TED_FREQ1_LO
    lda  #$01
    sta  TED_FREQ1_HI

; Configure TED voice 2 at ~880Hz
    lda  #$F1
    sta  TED_FREQ2_LO
    lda  #$02
    sta  TED_FREQ2_HI

; Enable both voices, volume 8
    lda  #$38              ; ch1on + ch2on + volume 8
    sta  TED_SND_CTRL

.TED File Playback:

Play Commodore Plus/4 music files (.ted format from HVTC collection):

IE32:

; Play a .ted file with TED+ enhancement
LOAD A, #1
STORE A, @0xF0F05          ; Enable TED+ mode
LOAD A, @ted_data          ; Point to embedded data
STORE A, @0xF0F10          ; TED_PLAY_PTR
LOAD A, @(ted_data_end - ted_data)
STORE A, @0xF0F14          ; TED_PLAY_LEN
LOAD A, #5                 ; Start with looping
STORE A, @0xF0F18          ; TED_PLAY_CTRL

ted_data:
    INCBIN "music.ted"
ted_data_end:

6502:

; Play a .ted file with looping
    lda  #1
    sta  TED_PLUS_CTRL
    STORE32 TED_PLAY_PTR_0, ted_data
    STORE32 TED_PLAY_LEN_0, (ted_data_end-ted_data)
    lda  #5                      ; bit0=start, bit2=loop
    sta  TED_PLAY_CTRL

ted_data:
    .incbin "music.ted"
ted_data_end:

Playback Control:

  • Write 1 to TED_PLAY_CTRL to start, 2 to stop, 5 to start with loop
  • Read TED_PLAY_STATUS for busy/error flags

11.8 AHX Sound Chip

The AHX engine provides Amiga AHX/THX module playback with 4-channel waveform synthesis. AHX modules use procedural synthesis rather than samples, creating rich sounds from simple waveforms (triangle, sawtooth, square, noise) with modulation.

Features:

  • 4-channel synthesis matching original Amiga hardware
  • Triangle, sawtooth, square, and noise waveforms
  • Per-voice filter modulation and square pulse width modulation
  • Vibrato, portamento, and envelope effects
  • AHX+ enhanced mode with stereo spread and audio processing
  • Subsong support for multi-tune modules

.AHX File Playback:

M68K:

; Play an .ahx file with looping and AHX+ enhancement
    PLAY_AHX_PLUS_LOOP ahx_data,ahx_data_end-ahx_data

ahx_data:
    incbin  "music.ahx"
ahx_data_end:

Z80:

; Play an .ahx file with looping
    ENABLE_AHX_PLUS              ; Enable enhanced mode
    SET_AHX_PTR ahx_data
    SET_AHX_LEN (ahx_data_end-ahx_data)
    START_AHX_LOOP               ; Start with looping

ahx_data:
    incbin  "music.ahx"
ahx_data_end:

6502:

; Play an .ahx file with looping
    lda  #1
    sta  AHX_PLUS_CTRL           ; Enable AHX+ mode
    STORE32 AHX_PLAY_PTR_0, ahx_data
    STORE32 AHX_PLAY_LEN_0, (ahx_data_end-ahx_data)
    lda  #5                      ; bit0=start, bit2=loop
    sta  AHX_PLAY_CTRL

ahx_data:
    .incbin "music.ahx"
ahx_data_end:

Playback Control:

  • Write 1 to AHX_PLAY_CTRL to start, 2 to stop, 5 to start with loop
  • Set AHX_SUBSONG before starting to select a specific subsong (0-255)
  • Read AHX_PLAY_STATUS for busy/error flags
  • Speed 0 command in the module signals end of song

11.9 MOD Player (ProTracker)

The MOD player provides full ProTracker .mod file playback with 4-channel sample-based audio. MOD files are the classic Amiga music format, using PCM samples mixed in real time. The player outputs through the SoundChip FLEX channels in DAC mode.

Features:

  • Full ProTracker .mod file parsing (M.K., 4CHN, FLT4, M!K! signatures)
  • 4-channel sample playback via SoundChip FLEX DAC mode
  • Amiga A500 and A1200 low-pass filter emulation with LED filter
  • ProTracker effects: arpeggio (0), portamento up/down (1/2), tone portamento (3), vibrato (4), vol+tone slide (5/6), sample offset (9), volume slide (A), position jump (B), set volume (C), pattern break (D), set speed/BPM (F)
  • Extended effects (Exy): LED filter (E0), fine porta up/down (E1/E2), set finetune (E5), retrigger (E9), fine vol slide (EA/EB), note cut (EC), note delay (ED), pattern delay (EE)
  • Song position tracking
  • Loop support
  • MediaLoader integration (auto-detected by .mod extension)
  • -mod command line flag for direct playback
  • Status bar indicator "Amiga MOD"

.MOD File Playback:

M68K:

; Play a .mod file with A500 filter and looping
    move.l  #1,MOD_FILTER_MODEL         ; A500 filter
    move.l  #mod_data,MOD_PLAY_PTR
    move.l  #(mod_data_end-mod_data),MOD_PLAY_LEN
    move.l  #5,MOD_PLAY_CTRL            ; start + loop

mod_data:
    incbin  "music.mod"
mod_data_end:

Z80:

; Play a .mod file with looping
    SET_MOD_FILTER 1                     ; A500 filter
    SET_MOD_PTR mod_data
    SET_MOD_LEN (mod_data_end-mod_data)
    START_MOD_LOOP                       ; Start with looping

mod_data:
    incbin  "music.mod"
mod_data_end:

6502:

; Play a .mod file with looping
    lda  #1
    sta  MOD_FILTER_MODEL               ; A500 filter
    STORE32 MOD_PLAY_PTR_0, mod_data
    STORE32 MOD_PLAY_LEN_0, (mod_data_end-mod_data)
    lda  #5                             ; bit0=start, bit2=loop
    sta  MOD_PLAY_CTRL

mod_data:
    .incbin "music.mod"
mod_data_end:

Playback Control:

  • Write 1 to MOD_PLAY_CTRL to start, 2 to stop, 5 to start with loop
  • Set MOD_FILTER_MODEL before starting to select a filter (0=none, 1=A500, 2=A1200)
  • Read MOD_PLAY_STATUS for busy/error flags
  • Read MOD_POSITION for the current song position

12. Video System

The video system provides flexible graphics output through a memory-mapped framebuffer design.

12.1 Display Modes

Four resolution modes are available:

  • 640x480 (MODE_640x480 = 0x00)
  • 800x600 (MODE_800x600 = 0x01)
  • 1024x768 (MODE_1024x768 = 0x02)
  • 1280x960 (MODE_1280x960 = 0x03, default)

Setting display mode:

IE32:

init_display:
    LOAD A, #0          ; MODE_640x480
    STORE A, @VIDEO_MODE
    LOAD A, #1          ; Enable display
    STORE A, @VIDEO_CTRL
    RTS

M68K:

init_display:
    move.l  #0,VIDEO_MODE.l        ; MODE_640x480
    move.l  #1,VIDEO_CTRL.l        ; Enable display
    rts

Z80:

init_display:
    xor  a                         ; MODE_640x480
    ld   (VIDEO_MODE),a
    ld   a,1                       ; Enable display
    ld   (VIDEO_CTRL),a
    ret

6502:

init_display:
    lda  #0
    sta  VIDEO_MODE                ; MODE_640x480
    lda  #1
    sta  VIDEO_CTRL                ; Enable display
    rts

12.2 Framebuffer Organisation

The framebuffer uses 32-bit RGBA colour format:

  • Start address: 0x100000 (VRAM_START)
  • Each pixel: 4 bytes (R,G,B,A)
  • Linear layout: y * width + x

Pixel address calculation:

Address = 0x100000 + (y * width + x) * 4

12.3 Dirty Rectangle Tracking

The system tracks changes in 32x32 pixel blocks:

  • Automatically marks modified regions
  • Updates only changed areas
  • Improves rendering performance

12.4 Double Buffering and VBlank Synchronisation

Video output uses double buffering to prevent tearing. The system provides a VBlank status bit for flicker-free animation:

  • VIDEO_STATUS (0x0F0008) bit 1 indicates VBlank period (safe to draw)
  • VBlank is read lock-free - no mutex contention during polling
  • Write to back buffer during VBlank to avoid screen flicker
  • Buffers swap automatically

VBlank Timing

The VBlank flag follows this timing:

  1. inVBlank = false when frame processing starts (active scan)
  2. inVBlank = true after frame is sent to display (CPU can safely draw)

Frame Synchronisation Pattern

For smooth animation, wait for a complete frame boundary (VBlank transition):

IE32:

.equ VIDEO_STATUS   0xF0008
.equ STATUS_VBLANK  2           ; bit 1

; Wait for exactly one frame - prevents animation running too fast
wait_frame:
    ; First wait for VBlank to END (active scan period)
.wait_not_vblank:
    LDA @VIDEO_STATUS
    AND A, #STATUS_VBLANK
    JNZ A, .wait_not_vblank

    ; Then wait for VBlank to START (new frame)
.wait_vblank_start:
    LDA @VIDEO_STATUS
    AND A, #STATUS_VBLANK
    JZ A, .wait_vblank_start
    RTS

M68K:

; Wait for exactly one frame
wait_frame:
.wait_not_vblank:
    move.l  VIDEO_STATUS.l,d0
    and.l   #STATUS_VBLANK,d0
    bne.s   .wait_not_vblank
.wait_vblank_start:
    move.l  VIDEO_STATUS.l,d0
    and.l   #STATUS_VBLANK,d0
    beq.s   .wait_vblank_start
    rts

Z80:

; Wait for exactly one frame
wait_frame:
.wait_not_vblank:
    ld   a,(VIDEO_STATUS)
    and  STATUS_VBLANK
    jr   nz,.wait_not_vblank
.wait_vblank_start:
    ld   a,(VIDEO_STATUS)
    and  STATUS_VBLANK
    jr   z,.wait_vblank_start
    ret

6502:

; Wait for exactly one frame
wait_frame:
    ; Wait for VBlank to END
@wait_not_vblank:
    lda  VIDEO_STATUS
    and  #STATUS_VBLANK
    bne  @wait_not_vblank
    ; Wait for VBlank to START
@wait_vblank_start:
    lda  VIDEO_STATUS
    and  #STATUS_VBLANK
    beq  @wait_vblank_start
    rts

Animation Loop Example

main_loop:
    JSR wait_frame      ; Wait for frame boundary (60 FPS)
    JSR erase_sprite    ; Erase old sprite position
    JSR update_position ; Calculate new position
    JSR draw_sprite     ; Draw at new position
    JMP main_loop

The wait_frame pattern ensures exactly one frame per loop iteration, giving smooth 60 FPS animation regardless of how fast the CPU runs.

12.5 Direct VRAM Access Mode

For fullscreen effects such as plasma, fire, or tunnel demos where every pixel is updated each frame, the system provides a direct VRAM access mode with lock-free dirty tracking that bypasses the standard memory bus. This delivers approximately 4.5x video throughput compared to bus-based access.

Performance Comparison

Mode Writes/sec Approx FPS Use Case
Bus-based ~1.4M ~9 Partial screen updates, sprites
Direct VRAM + Lock-free ~6.3M ~41 Fullscreen effects, demoscene

How It Works

Direct VRAM mode eliminates per-pixel overhead by:

  • Bypassing CPU and bus mutex locks
  • Skipping I/O region mapping lookups
  • Using lock-free atomic bitmap for dirty tile tracking
  • Employing compare-and-swap operations instead of mutex locks

API Usage (Go)

// Enable direct VRAM mode and get buffer pointer
vramBuffer := videoChip.EnableDirectMode()

// Attach buffer to CPU for fast writes
cpu.AttachDirectVRAM(vramBuffer, VRAM_START, VRAM_START+uint32(len(vramBuffer)))

// ... run demo ...

// Cleanup
cpu.DetachDirectVRAM()
videoChip.DisableDirectMode()

When to Use

  • Use direct mode for fullscreen effects where most pixels change every frame
  • Use bus mode for partial updates, sprites, or when dirty region tracking is beneficial

Direct VRAM mode is ideal for demoscene-style effects, real-time visualisations, and any application that redraws the entire screen each frame.

12.6 Copper List Executor

The video subsystem includes a simple copper-like list executor for mid-frame register updates. Copper lists are stored in RAM as little-endian 32-bit words and can WAIT on raster positions, MOVE values into video registers, and END the list. The copper restarts each frame while enabled.

Registers:

  • COPPER_CTRL (0x0F000C): bit0=enable, bit1=reset/rewind
  • COPPER_PTR (0x0F0010): list base address (latched on enable/reset)
  • COPPER_PC (0x0F0014): current list address (read-only)
  • COPPER_STATUS (0x0F0018): bit0=running, bit1=waiting, bit2=halted

List words:

  • WAIT: (0<<30) | (y<<12) | x - Wait until raster reaches Y/X position
  • MOVE: (1<<30) | (regIndex<<16) followed by a 32-bit value - Write to register
  • SETBASE: (2<<30) | (addr>>2) - Change base address for MOVE operations
  • END: (3<<30) - Halt copper for this frame

SETBASE Instruction

The SETBASE instruction allows the copper to write to any memory-mapped I/O device by changing the base address for subsequent MOVE operations. This enables cross-device effects like modifying VGA palette registers from the copper.

SETBASE Format:

  • Bits 30-31: Opcode (2)
  • Bits 0-23: Target address right-shifted by 2 (addresses are 4-byte aligned)

Pre-defined SETBASE values:

  • COP_SETBASE_VIDEO (0x8003C000) - IE video registers (default)
  • COP_SETBASE_VGA (0x8003C400) - VGA registers
  • COP_SETBASE_VGA_DAC (0x8003C416) - VGA DAC for palette writes

The base is reset to VIDEO_REG_BASE at the start of each frame.

Per-Scanline Execution

The copper executes synchronously with the video compositor's scanline rendering. When the compositor renders each scanline:

  1. The copper advances, executing instructions until it reaches a WAIT for a future scanline
  2. MOVE instructions take effect immediately for the current scanline
  3. VGA renders its scanline using the current palette state

This means copper-driven palette changes affect only the scanlines rendered after the change, enabling classic raster effects like:

  • Gradient backgrounds (changing palette entries per scanline)
  • Split-screen color schemes
  • Plasma and interference patterns

Cross-Device Copper Example (VGA Palette + IE Raster Bars)

IE32:

.include "ie32.inc"

copper_list:
    ; Switch to VGA DAC registers
    .long COP_SETBASE_VGA_DAC

    ; Set palette entry 32 to red
    .long COP_MOVE_VGA_WINDEX
    .long 32                          ; Palette index
    .long COP_MOVE_VGA_DATA
    .long 63                          ; R (6-bit VGA)
    .long COP_MOVE_VGA_DATA
    .long 0                           ; G
    .long COP_MOVE_VGA_DATA
    .long 0                           ; B

    ; Switch back to IE video chip
    .long COP_SETBASE_VIDEO

    ; Draw raster bar at Y=100
    .long COP_WAIT_MASK | (100 * COP_WAIT_SCALE)
    .long COP_MOVE_RASTER_Y
    .long 100
    .long COP_MOVE_RASTER_H
    .long 8
    .long COP_MOVE_RASTER_COLOR
    .long 0xFF00FFFF                  ; Cyan (RGBA)
    .long COP_MOVE_RASTER_CTRL
    .long 1

    .long COP_END

12.7 DMA Blitter

The DMA blitter performs rectangle copy/fill, line drawing, and color expansion. Registers are written via memory-mapped I/O. The blitter supports both RGBA32 (4 bytes/pixel) and CLUT8 (1 byte/pixel) modes, 16 raster draw modes, and template-based text rendering.

Synchronous Execution: The blitter runs immediately when BLT_CTRL is written with bit0 set. This ensures blitter operations complete before the CPU continues, allowing safe drawing during VBlank without race conditions.

Operations (BLT_OP):

  • 0: COPY
  • 1: FILL
  • 2: LINE (coordinates packed into BLT_SRC/BLT_DST; extended mode below)
  • 3: MASKED COPY (1-bit mask, LSB-first per byte)
  • 4: ALPHA (alpha-aware copy with source alpha blending)
  • 5: MODE7 (affine texture mapping with 16.16 UV coordinates)
  • 6: COLOR_EXPAND (1-bit template to colored pixels; see Extended Registers below)

Line coordinates:

  • BLT_SRC: x0 (low 16 bits), y0 (high 16 bits)
  • BLT_DST: x1 (low 16 bits), y1 (high 16 bits)

Mode7 Parameters

BLT_OP=5 uses these registers in addition to the normal blitter source/destination/size:

  • BLT_MODE7_U0, BLT_MODE7_V0: start coordinates in signed 16.16 fixed-point.
  • BLT_MODE7_DU_COL, BLT_MODE7_DV_COL: UV deltas per destination X pixel.
  • BLT_MODE7_DU_ROW, BLT_MODE7_DV_ROW: UV deltas applied when moving to next destination row.
  • BLT_MODE7_TEX_W, BLT_MODE7_TEX_H: wrap masks (must be 2^n-1, for example 255 for 256).
  • BLT_SRC_STRIDE: source row stride bytes. 0 means auto ((texWMask+1)*4).
  • BLT_DST_STRIDE: destination row stride bytes. 0 keeps normal auto behavior.

Example UV mapping values:

  • Identity sampling: duCol=0x00010000, dvCol=0, duRow=0, dvRow=0x00010000.
  • Rotation/zoom: precompute sine/cosine in software and write fixed-point deltas once per frame.

Blitter Example (fill a 16x16 block):

IE32:

    LOAD A, #1              ; BLT_OP_FILL
    STORE A, @BLT_OP
    LOAD A, #0x100000       ; VRAM_START
    STORE A, @BLT_DST
    LOAD A, #16
    STORE A, @BLT_WIDTH
    LOAD A, #16
    STORE A, @BLT_HEIGHT
    LOAD A, #0xFF00FF00     ; green (RGBA)
    STORE A, @BLT_COLOR
    LOAD A, #1
    STORE A, @BLT_CTRL      ; start

M68K:

    move.l  #BLT_OP_FILL,BLT_OP.l
    move.l  #VRAM_START,BLT_DST.l
    move.l  #16,BLT_WIDTH.l
    move.l  #16,BLT_HEIGHT.l
    move.l  #$FF00FF00,BLT_COLOR.l   ; green (RGBA)
    move.l  #1,BLT_CTRL.l            ; start

Z80:

    SET_BLT_OP BLT_OP_FILL
    SET_BLT_DST VRAM_START
    SET_BLT_WIDTH 16
    SET_BLT_HEIGHT 16
    SET_BLT_COLOR 0xFF00FF00         ; green (RGBA)
    START_BLIT

6502:

    lda  #BLT_OP_FILL
    sta  BLT_OP
    STORE32 BLT_DST_0, VRAM_START
    SET_BLT_WIDTH 16
    SET_BLT_HEIGHT 16
    SET_BLT_COLOR $FF00FF00          ; green (RGBA)
    START_BLIT

The blitter defaults BLT_SRC_STRIDE/BLT_DST_STRIDE to the current mode row bytes when the address is in VRAM, otherwise it uses width*4. If an unaligned VRAM address is used, BLT_STATUS.bit0 is set.

Extended Blitter Registers

The extended registers at 0xF0488-0xF049B add BPP mode selection, draw modes, and color expansion for hardware-accelerated text rendering and CLUT8 framebuffers.

Register Address Description
BLT_FLAGS 0xF0488 BPP mode, draw mode, and color expansion flags (see below)
BLT_FG 0xF048C Foreground color for color expansion
BLT_BG 0xF0490 Background color for color expansion
BLT_MASK_MOD 0xF0494 Template row modulo in bytes (color expansion)
BLT_MASK_SRCX 0xF0498 Starting X bit offset in template (color expansion)

BLT_FLAGS bit layout:

Bits Name Values
0-1 BPP mode 0 = RGBA32 (4 bpp, default), 1 = CLUT8 (1 bpp)
4-7 Draw mode 16 standard raster ops (0=Clear, 3=Copy, 6=Xor, 10=Invert, 15=Set)
8 JAM1 Color expand: skip BG pixels (transparent text)
9 Invert template Flip all template bits before processing
10 Invert mode XOR destination with all-ones for set template bits (ignore FG/BG)

When BLT_FLAGS=0 (default), the blitter uses Copy mode with RGBA32 - fully backward compatible with existing programs.

Color Expansion (op 6): Reads a 1-bit template from BLT_MASK and expands each bit to a colored pixel at BLT_DST. Template bits are MSB-first (Amiga convention: bit 7 of first byte = leftmost pixel). Three modes:

  • JAM2 (bits 8,10 clear): bit=1 writes FG color, bit=0 writes BG color
  • JAM1 (bit 8 set): bit=1 writes FG color, bit=0 leaves destination unchanged
  • Invert (bit 10 set): bit=1 XORs destination with all-ones, bit=0 unchanged

CLUT8 mode: When BPP=1, FILL writes 1 byte per pixel and COPY transfers 1 byte per pixel. BLT_COLOR/BLT_FG/BLT_BG use the low byte as a palette index.

Extended line mode (op 2 with BLT_FLAGS != 0): When BLT_FLAGS is non-zero, the LINE operation uses a different register layout to support arbitrary framebuffer addresses and draw modes:

Register Extended line usage
BLT_SRC Start point: (y0 << 16) | x0 (same as legacy)
BLT_WIDTH End point: (y1 << 16) | x1 (repurposed from width)
BLT_DST Framebuffer base address (not endpoint)
BLT_DST_STRIDE Row stride in bytes
BLT_COLOR Line color
BLT_FLAGS BPP + draw mode (must be non-zero to activate extended mode)

When BLT_FLAGS=0 (legacy mode), BLT_DST holds the endpoint and the line is drawn into the active VRAM framebuffer at VRAM_START.

Clipping: In extended mode the blitter does not perform viewport clipping - callers must provide pre-clipped coordinates. Pixel writes are still bounds-checked against VRAM limits (out-of-range addresses are silently dropped).

12.8 Raster Band Fill

VIDEO_RASTER_* registers draw a full-width horizontal band directly into the framebuffer. This is useful for copper-driven raster bars without adding a palette system.

Raster Band Example (draw 4-pixel band at Y=100):

IE32:

    LOAD A, #100
    STORE A, @VIDEO_RASTER_Y
    LOAD A, #4
    STORE A, @VIDEO_RASTER_HEIGHT
    LOAD A, #0xFFFF0000           ; blue (RGBA)
    STORE A, @VIDEO_RASTER_COLOR
    LOAD A, #1
    STORE A, @VIDEO_RASTER_CTRL

M68K:

    move.l  #100,VIDEO_RASTER_Y.l
    move.l  #4,VIDEO_RASTER_HEIGHT.l
    move.l  #$FFFF0000,VIDEO_RASTER_COLOR.l  ; blue (RGBA)
    move.l  #1,VIDEO_RASTER_CTRL.l

Z80:

    STORE32 VIDEO_RASTER_Y_LO,100
    STORE32 VIDEO_RASTER_HEIGHT_LO,4
    STORE32 VIDEO_RASTER_COLOR_0,0xFFFF0000  ; blue (RGBA)
    ld   a,1
    ld   (VIDEO_RASTER_CTRL),a

6502:

    STORE32 VIDEO_RASTER_Y_LO, 100
    STORE32 VIDEO_RASTER_HEIGHT_LO, 4
    STORE32 VIDEO_RASTER_COLOR_0, $FFFF0000  ; blue (RGBA)
    lda  #1
    sta  VIDEO_RASTER_CTRL

12.9 Video Compositor

The Intuition Engine uses a video compositor to blend multiple video sources into a single display output. This architecture enables layered rendering where different video devices (VideoChip, VGA) can contribute to the final frame.

Architecture

                    +-------------+
  CPU -> VGA VRAM -> |   VGAEngine | --+
                    +-------------+   |     +-------------+     +---------+
                                      +---> | Compositor  | --> | Display |
                    +-------------+   |     +-------------+     +---------+
  CPU -> Chip VRAM -> |  VideoChip  | --+
                    +-------------+

Layer Ordering

Each video source has a layer number that determines compositing order (higher layers render on top):

Source Layer Description
VideoChip 0 Base layer with copper coprocessor
VGA 10 IBM VGA compatible modes
TED Video 12 Commodore Plus/4 video
ANTIC/GTIA 13 Atari 8-bit video
ULA 15 ZX Spectrum video
Voodoo 3D 20 3DFX SST-1 accelerator

Per-Scanline Rendering

The compositor supports two rendering modes:

Full-Frame Mode: Each source renders its complete frame, then frames are composited. Simple but copper effects only affect video registers, not VGA palette.

Scanline-Aware Mode: Sources that implement the ScanlineAware interface render one scanline at a time. This enables:

  • Copper MOVE operations to affect the current scanline immediately
  • Per-scanline VGA palette changes via SETBASE
  • Classic demoscene raster effects (color cycling, plasma bars)

The compositor automatically uses scanline-aware rendering when all enabled sources support it. The render sequence per scanline is:

  1. VideoChip processes copper list up to current Y position
  2. VideoChip renders its scanline
  3. VGA renders its scanline using current palette state
  4. Scanlines are composited in layer order

Copper + VGA Integration

The copper's SETBASE instruction enables per-scanline VGA palette manipulation:

; Create a raster bar effect by changing VGA palette per scanline
copper_list:
    ; Wait for scanline 50
    .long COP_WAIT_MASK | (50 * COP_WAIT_SCALE)

    ; Switch to VGA DAC registers
    .long COP_SETBASE_VGA_DAC

    ; Set color 0 to red for this scanline
    .long COP_MOVE_VGA_WINDEX
    .long 0
    .long COP_MOVE_VGA_DATA
    .long 63                          ; R
    .long COP_MOVE_VGA_DATA
    .long 0                           ; G
    .long COP_MOVE_VGA_DATA
    .long 0                           ; B

    ; Wait for scanline 60
    .long COP_WAIT_MASK | (60 * COP_WAIT_SCALE)

    ; Set color 0 to blue
    .long COP_MOVE_VGA_WINDEX
    .long 0
    .long COP_MOVE_VGA_DATA
    .long 0                           ; R
    .long COP_MOVE_VGA_DATA
    .long 0                           ; G
    .long COP_MOVE_VGA_DATA
    .long 63                          ; B

    ; Return to video chip registers
    .long COP_SETBASE_VIDEO

    .long COP_END

This creates a horizontal band where the VGA background color (palette entry 0) changes from red to blue at specific scanlines.

13. Developer's Guide

For the complete developer guide covering building, testing, toolchains, include files, debugging, platform support, and contribution guidance, see DEVELOPERS.md.

Quick Build

make                    # Build VM + all SDK tools (assemblers, disassembler, transpiler)
make novulkan           # Build without Vulkan
make headless           # Build for CI/testing (no display/audio)
make basic              # Build with embedded EhBASIC interpreter
make basic-emutos       # Build with embedded BASIC + EmuTOS ROM
make sdk                # Sync includes + pre-assemble SDK demos
make release-all        # Build release archives for all platforms
go build ./...          # Quick compile check (output in cwd, not bin/)

See DEVELOPERS.md for the complete development workflow, running programs (all CPU modes and music playback), assembler include files, and debugging techniques. See also docs/include-files.md for a detailed include file reference.

14. Implementation Details

This section describes how the Intuition Engine emulates its hardware components.

14.1 CPU Emulation

IE32 Custom RISC CPU

The IE32 is a custom 32-bit RISC processor designed for simplicity and performance:

  • 16 general-purpose registers: A (accumulator), B-H, S-W, X-Z (all 32-bit, orthogonal access)
  • Fixed 8-byte instruction format: Simplifies fetch/decode
  • Load/store architecture: Memory access only via LOAD/STORE
  • Little-endian byte order: Matches system bus convention
  • Hardware interrupt support: Single vector at address 0

Execution cycle: Fetch (8 bytes) → Decode → Execute → Update PC → Check interrupts

Motorola 68020

The 68020 emulation provides 95%+ instruction coverage:

  • JIT compiler: Block-based native code compilation on x86-64; enabled by default on supported platforms, disable with -nojit
  • 8 data registers (D0-D7) and 8 address registers (A0-A7)
  • Full addressing mode support: All 18 68020 addressing modes
  • Supervisor/user modes: Privilege separation
  • Exception handling: Bus error, address error, illegal instruction
  • FPU emulation (68881/68882): Floating-point operations

Variable instruction length (2-22 bytes) with complex decode logic.

Zilog Z80

The Z80 emulation provides complete instruction set support:

  • Main and alternate register sets: AF, BC, DE, HL with shadows
  • Index registers: IX, IY with displacement addressing
  • Interrupt modes: IM 0, IM 1, IM 2
  • I/O port access: IN/OUT instructions mapped to hardware
  • JIT compiler (x86-64): Block-based native compilation with block chaining, RTS cache for CALL/RET, lazy flag elimination, and unchecked memory access for qualifying DJNZ loops. 2.7x-10.9x speedup over interpreter.

MOS 6502

The 6502 emulation covers the original NMOS instruction set:

  • Accumulator, X, Y registers: 8-bit operations
  • Zero page optimisation: Fast access to first 256 bytes
  • Stack at $0100-$01FF: Hardware stack page
  • Status flags: N, V, B, D, I, Z, C

Intel x86 (32-bit)

The x86 emulation provides a 32-bit flat memory model:

  • 8 general-purpose registers: EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP
  • Segment-free flat model: Direct 32-bit addressing
  • 8086 core instruction set: Integer instructions with 32-bit register extensions
  • I/O port access: IN/OUT instructions mapped to hardware

IE64 Custom 64-bit RISC CPU

The IE64 is a 64-bit RISC processor with a clean load-store architecture:

  • JIT compiler: Block-based native code compilation (ARM64 and x86-64) with backward branch optimisation for native loops. Enabled by default on supported platforms (amd64/arm64 Linux); disable with -nojit
  • 32 general-purpose registers: R0 (hardwired zero) through R31 (stack pointer), all 64-bit
  • Fixed 8-byte instruction format: Consistent fetch/decode
  • Compare-and-branch model: No flags register; conditional branches embed register comparison
  • Size-polymorphic operations: .B/.W/.L/.Q suffixes on most instructions
  • 64-bit bus support: Read64/Write64 with I/O region split semantics

Execution: JIT compiles basic blocks to native ARM64, caches them, and re-executes directly. Backward branches within a block execute as native loops with budget-based timer safety. Falls back to the interpreter for I/O, transcendental FPU ops, and unsupported platforms.

14.2 Memory Architecture

Unified Memory Bus

All CPUs share the autodetected guest RAM through the MachineBus. Total guest RAM is selected at boot from host /proc/meminfo minus a per-platform reserve (memory_sizing.go); each CPU/profile then sees an active visible RAM clamped to its own ceiling. Guest software discovers sizes via the SYSINFO MMIO pairs (SYSINFO_TOTAL_RAM_LO/HI for total guest RAM, SYSINFO_ACTIVE_RAM_LO/HI for active visible RAM). IE64 also reports its active visible RAM through CR_RAM_SIZE_BYTES.

  • Direct memory array: Fast non-I/O access via cached pointer
  • Page-based I/O callbacks: Handlers for memory-mapped registers
  • Bank switching: 8KB/16KB windows for 8-bit CPUs to access full memory

Memory-Mapped I/O Regions

Region Address Range Description
System $000000-$000FFF Vectors, system data
Program $001000-$0EFFFF User program space
I/O $0F0000-$0FFFFF Hardware registers
VRAM $100000-$5FFFFF Video RAM (5MB)

Bank Windows (8-bit CPUs)

Z80 and 6502 access the full address space through bank windows:

Window CPU Address Size Control Register
Bank 1 $2000 8KB $F700-$F701
Bank 2 $4000 8KB $F702-$F703
Bank 3 $6000 8KB $F704-$F705
VRAM $8000 16KB $F7F0

14.3 Audio System Architecture

Sample Generation

Audio is synthesised at 44.1kHz with 32-bit floating-point precision:

  1. Oscillators: Generate raw waveforms (square, triangle, sine, noise, sawtooth)
  2. Envelopes: Shape amplitude over time (ADSR)
  3. Modulation: Apply sync, ring mod, PWM
  4. Mixing: Combine all channels
  5. Effects: Apply filter, overdrive, reverb
  6. Output: Convert to 16-bit stereo PCM

Anti-Aliasing

PolyBLEP (polynomial bandlimited step) anti-aliasing is applied to square and sawtooth waveforms to reduce high-frequency aliasing artifacts.

PSG/POKEY/SID Register Mapping

The classic sound chips are implemented via register mapping to the custom audio synthesizer:

  • PSG (AY-3-8910): Registers at $F0C00-$F0C0D map to square wave channels with hardware envelope emulation
  • POKEY: Registers at $F0D00-$F0D09 map to channels with polynomial counter and distortion emulation
  • SID (6581/8580): Registers at $F0E00-$F0E1C map to channels with filter, ring mod, and sync

This approach provides accurate register-level compatibility while leveraging the custom synth's high-quality output (44.1kHz, anti-aliased waveforms, 32-bit processing). VGM playback also supports SN76489 data via automatic conversion to these AY registers.

File playback (.ym, .ay, .sndh, .vgm, .sap, .sid) executes embedded CPU code that writes to the mapped registers, driving the synthesis in real-time.

15. Platform Support and Building

For detailed build instructions, all build profiles/tags, toolchain setup, testing, and platform compatibility details, see DEVELOPERS.md.

Supported Platforms

Platform Architecture Status Notes
Linux x86_64, aarch64 Official full, novulkan, headless, headless-novulkan
Windows x86_64, ARM64 Official Pure-Go novulkan release builds
macOS x86_64, arm64 Official Pure-Go novulkan release builds, amd64 guest JIT parity on Intel Macs, IE64 native JIT on Apple Silicon

Release builds embed EhBASIC, EmuTOS, and the AROS ROM via embed_basic embed_emutos embed_aros, and package the full sdk/ tree plus the staged AROS/ system tree beside the binary. make release-macos builds both darwin-amd64 and darwin-arm64 archives.

Graphics: Ebiten (OpenGL on Linux, DirectX on Windows, Metal on macOS). Audio: Oto (44.1kHz stereo). Both have headless stubs for CI.

See docs/platform-compatibility.md for build profile requirements and known limitations.

Quick Start

# Default: start EhBASIC on IE64
./bin/IntuitionEngine

# Run an IE32 program
./bin/IntuitionEngine -ie32 program.iex

# Run an M68K program
./bin/IntuitionEngine -m68k program.ie68

# Play SID music
./bin/IntuitionEngine -sid tune.sid

# Play PSG music (YM/AY/VGM/VTX/SNDH/PT3/STC/...)
./bin/IntuitionEngine -psg track.ym

# Or from the EhBASIC prompt, load and run any binary:
# RUN "program.iex"
# RUN "demo.ie68"

# Version and features
./bin/IntuitionEngine -version
./bin/IntuitionEngine -features

See DEVELOPERS.md for all CPU modes, music playback options, enhanced audio modes, build commands, and testing instructions.

SDK

The sdk/ directory contains a curated developer package with example programs, include files, and build scripts for all six CPU architectures. Run make sdk to sync include files from the canonical source and pre-assemble demos into sdk/examples/prebuilt/. See sdk/README.md for the full SDK documentation and docs/demo-matrix.md for the demo coverage matrix.

Further Documentation

16. EmuTOS Mode

Intuition Engine runs EmuTOS directly on the IE M68K core, providing a complete GEM desktop environment with file browser, mouse/keyboard input, and host filesystem access.

Quick Start

# Boot EmuTOS (embedded ROM, or auto-discovers etos256us.img / emutos.img locally)
./bin/IntuitionEngine -emutos

# External ROM image
./bin/IntuitionEngine -emutos-image etos256us.img

# Map a specific host directory as drive U: (default: ~/)
./bin/IntuitionEngine -emutos -emutos-drive /path/to/files

# Boot EmuTOS from the EhBASIC prompt (requires 'make basic-emutos' build)
# At the Ready prompt, type: EMUTOS

# Load .tos/.img files through the ProgramExecutor
# RUN "emutos.img"

EmuTOS-Specific Hardware

EmuTOS runs on the IE M68K core with full access to all IE hardware: VideoChip (640x480 RGBA, set via VIDEO_MODE=0), terminal MMIO for keyboard/mouse, timer-driven interrupts, and the complete audio/video peripheral set (SoundChip, PSG, SID, POKEY, TED, AHX, VGA, ULA, TED video, ANTIC/GTIA, Voodoo 3D). TOS .PRG programs can drive any MMIO register in the hardware map via ie68.inc.

Mouse MMIO (0xF0730 - 0xF073C):

Address Register Description
0xF0730 MOUSE_X Absolute X position (16-bit)
0xF0734 MOUSE_Y Absolute Y position (16-bit)
0xF0738 MOUSE_BUTTONS Bit 0=left, 1=right, 2=middle
0xF073C MOUSE_STATUS Bit 0=changed since last read (clears on read)

Scancode MMIO (0xF0740 - 0xF0748):

Address Register Description
0xF0740 SCAN_CODE Raw ST-style scancode (dequeues on read)
0xF0744 SCAN_STATUS Queue depth (0 = empty)
0xF0748 SCAN_MODIFIERS Bit 0=shift, 1=ctrl, 2=alt, 3=capslock

Interrupts: Timer IRQ level 5 at 200Hz, VBlank IRQ level 4 at 60Hz.

GEMDOS drive U:: Host directory (default ~/) is mapped as drive U: in the GEM desktop via TRAP #1 interception, providing file browsing, opening, and .PRG execution from the host filesystem.

Build Targets

make emutos             # VM with embedded EmuTOS ROM
make basic-emutos       # VM with embedded EhBASIC + EmuTOS ROM
make emutos-rom         # Build EmuTOS ROM from source (auto-clones if needed)

See sdk/docs/ie_emutos.md for the full hardware map, build instructions, and GEM programming guide.

17. AROS Mode

Intuition Engine runs AROS (Amiga Research Operating System) on the IE M68K core, providing a full Amiga Workbench desktop with Shell, file management, and host filesystem access.

# Boot AROS (embedded ROM, or auto-discovers sdk/roms/aros-ie-m68k.rom)
./bin/IntuitionEngine -aros

# Interpreter-only validation path used for M68K AROS bring-up
./bin/IntuitionEngine -aros -nojit

# Boot from external ROM image
./bin/IntuitionEngine -aros-image sdk/roms/aros-ie-m68k.rom

# Map a host directory as the IE: volume
./bin/IntuitionEngine -aros -aros-drive /path/to/files

Interpreter Validation Workflow

For M68K AROS interpreter work, the stable bring-up path is -aros -nojit. The bounded clean-boot contract is:

  • the machine reaches the AROS ready probe without timing out
  • no structured M68K fault record is emitted first
  • no interpreter-originated illegal/Line-A/Line-F/bus/address exception is seen on the way there

The repo now exposes that workflow in two layers:

Typical triage commands:

go test -tags headless -run 'Test(M68KFaultManifest|ProbeAROSReadyState|AROSBootHarness_)' ./...
./bin/IntuitionEngine -aros -nojit -script scripts/m68k_aros_ready_probe.ies
./bin/IntuitionEngine -aros -nojit -script scripts/m68k_aros_fault_capture.ies

AROS Memory Layout

Region Range Size Notes
Chip RAM A 0x000000-0x09DFFF 630KB
Chip RAM B 0x200000-0x6FFFFF 5MB Upper 1MB overlaid by ROM
ROM 0x600000-0x7FFFFF 2MB Overlays 0x600000-0x6FFFFF of Chip RAM B
Fast RAM 0x800000-0x1DFFFFF 22MB
VRAM 0x1E00000-0x1FFFFFF 2MB

Total: 27.6MB (5.6MB chip + 22MB fast memory). ROM overlays the top of the Chip RAM B address range, similar to real Amiga hardware. VRAM is separate.

What Works

  • Full Workbench desktop with 12+ tasks and graphical file browser
  • Shell commands (Path, Dir, List, Copy, MakeDir, Delete, Rename, etc.)
  • DOS handler: host filesystem bridge as IE: volume via MMIO at 0xF2220
  • Multi-resolution display: 640x480, 800x600, 1024x768, 1280x960 (CLUT8/RGBA32)
  • Amiga rawkey scancode input, software cursor overlay
  • battclock.resource via RTC_EPOCH MMIO register (0xF0750)
  • Paula-compatible 4-channel audio DMA (see registers below)
  • iewarp.library: AROS shared library that offloads compute to the IE64 coprocessor

Paula DMA Audio Registers (0xF2260 - 0xF22AF)

Paula-compatible 4-channel sample DMA. The M68K audio.device writes sample pointers, lengths, periods, and volumes to per-channel registers, then enables DMA via DMACON. The engine reads sample bytes from guest RAM at the correct rate and outputs them via SoundChip FLEX DAC at 44.1kHz.

Per-Channel Registers (4 channels × 16 bytes, CH0=0xF2260, CH1=0xF2270, CH2=0xF2280, CH3=0xF2290):

Offset Name Description
+$00 AUDxPTR Sample pointer in guest RAM (32-bit)
+$04 AUDxLEN Length in words (1 word = 2 bytes, Paula-style)
+$08 AUDxPER Period (frequency = 3546895 / period, PAL clock)
+$0C AUDxVOL Volume (0-64, Paula-compatible)

Global Registers:

Address Name Description
$F22A0 DMACON DMA control: bit 15=set/clear mode, bits 0-3=channel enables
$F22A4 STATUS Completion flags: bits 0-3 per channel (write-to-clear)
$F22A8 INTENA Interrupt enable: bit 15=set/clear, bits 0-3=channel enables

Buffer completion triggers a Level 3 interrupt (M68K autovector 27) when the corresponding INTENA bit is set.

Build Targets

make aros-rom           # Build AROS ROM + filesystem from source
make aros               # Build VM with embedded AROS ROM

18. IntuitionOS

IExec.library is an Amiga Exec-inspired protected microkernel for the IE64 CPU. Unlike classic Amiga Exec, which ran everything in flat supervisor space with no memory protection, IExec uses the IE64 MMU to enforce hardware-backed user/supervisor privilege separation with per-task page tables and W^X memory policy. The design preserves the Amiga programming model (signals, message ports, priority scheduling) while adding the isolation guarantees of a modern protected-mode OS.

M14 phase 5 status - native ELF contract frozen, dos.library seglist loading/shipped launch landed, shell command dispatch uses the real DOS loader path, and the retained GUI demos run through that same shipped path:

  • ELF is now both documented and live through DOS. M14 phase 1 froze the native subset (ELF64, little-endian, ET_EXEC, EM_IE64 = 0x4945, PT_LOAD only, page-aligned segments, no dynamic linker, no HUNK); phase 2 added DOS_LOADSEG / DOS_UNLOADSEG; phase 3 added descriptor-based execution on top of those seglists; phase 4 routed shell command dispatch through that same DOS loader path; phase 5 makes the visible C: command/demo path native ELF and proves the end-to-end demo flow on it.
  • dos.library now owns native executable parsing and DOS-side launch. LoadSeg resolves the name through DOS naming, walks the file's extent chain into a temporary contiguous image, validates the strict M14 ELF subset, allocates a DOS-owned seglist object, and copies each PT_LOAD segment into DOS-owned memory with the file's final target VA, size, page count, R/W/X flags, and preserved ELF entry point recorded in the seglist. DOS_RUNSEG turns that seglist into a launch descriptor and starts the child through the dual-mode ExecProgram bridge.
  • The shell now goes through the DOS loader path. DOS_RUN prefers the native ELF seglist path for strict M14 ELF commands and falls back to the legacy flat-image launch path only for compatibility inputs. With M14.1 phase 4, the shipped C: command/demo path and the service binaries under LIBS:, DEVS:, and RESOURCES: are all native ELF. From the user's perspective, command names, lookup rules, args, and unknown-command behavior stay the same.
  • Seglist lifetime is explicit and test-covered. LoadSeg returns a DOS-owned seglist object; UnLoadSeg frees it. Successful launch copies the image into private child mappings, so UnLoadSeg after launch does not break the running task. Failed launch leaves the seglist intact.
  • Boot and command execution stay source-compatible. The old flat-image ExecProgram ABI still works for compatibility, but shipped boot/services now launch from the internal embedded-manifest ELF path and the visible shell path plus retained GUI demos exercise the real M14 DOS loader.
  • M14.1 is the follow-on full-ELF service migration, not a public API change. M14 shipped the public file-backed DOS loader API for C: commands and apps. The planned M14.1 service migration moves boot/services to an internal embedded-manifest ELF path, replaces bootstrap-grant keying with internal manifest-entry IDs, and keeps that service source path internal rather than turning it into a second public DOS loader API.
  • M14.1 phase 5 completes the shipped full-ELF runtime path and locks the visible regressions. The kernel prepares staged strict-M14 ELF rows for the internal embedded boot manifest and boots console.handler plus dos.library from that manifest. Once DOS is online, dos.library launches Shell, hardware.resource, input.device, graphics.library, and intuition.library from the same internal manifest source, and the LIBS: / DEVS: / RESOURCES: binaries are now emitted as native ELF too. This remains internal-only: the public DOS loader API is unchanged.
  • Phase 5 user-visible coverage is explicit. The full full-ELF runtime now has named end-to-end regressions for the clean-boot service census, shell command path (Version, Avail, Dir, Type, Echo, unknown command), and both retained GUI demos: TestIExec_M141_Phase5_FullBootStack_ServiceCensus, ..._CommandPathRegression, ..._ShellUnknownRegression, ..._GfxDemoRegression, and ..._AboutRegression.
  • The contract is executable, not just prose. Focused host-side ELF validator tests remain, and phases 2-5 add red-first runtime tests for LoadSeg, UnLoadSeg, descriptor launch, preserved ELF entry, target-VA-sensitive execution, startup-page initialization, args passing, seglist lifetime across launch, shell command dispatch through the new loader, full visible boot-stack stability, and C:GfxDemo / C:About regression coverage.
  • Reference docs. The native subset and current loader status live in sdk/docs/IntuitionOS/ELF.md; the broader kernel/runtime contract remains in sdk/docs/IntuitionOS/IExec.md.

M14.2 phase 1 status - execution is now ELF-only at the public runtime boundary:

  • ExecProgram is descriptor-only. The old flat-image ExecProgram(image_ptr, image_size, args_ptr, args_len) ABI is removed; the surviving launch contract is the M14 descriptor path used by DOS RunSeg.
  • DOS_RUN no longer falls back to legacy flat-image command files. Name-based command dispatch still uses the same DOS UX, but non-ELF executable content is rejected instead of being reflatted and launched.
  • DOS_LOADSEG remains the strict native loader. The public DOS loader surface is unchanged: LoadSeg / UnLoadSeg / RunSeg stay Amiga-shaped and ELF-backed.

M15 status - DOS expansion + system layout are now live on top of the preserved M14.2 ELF-only runtime boundary:

  • dos.library now owns the canonical M15 layout. The built-in assign table exposes RAM:, C:, L:, LIBS:, DEVS:, T:, S:, and RESOURCES:. RAM: remains the root/no-prefix compatibility view; fully qualified names resolve through the assign table; bare command search stays C:-only.
  • DOS_ASSIGN is part of the live DOS surface. DOS now supports assign list/query/set for the mutable user assign rows while keeping the built-in layout stable. RAM: remains listed and queryable for compatibility but is not remappable through DOS_ASSIGN.
  • L: is a qualified helper namespace, not part of bare command fallback. Bare command names do not probe L:. Direct qualified helper access stays available through L:.
  • The visible text-mode OS is richer without changing the loader contract. The shipped command/demo path now includes Version, Avail, Dir, Type, Echo, Assign, List, Which, Help, GfxDemo, and About, all still launched through the DOS naming model and the M14/M14.2 ELF-backed loader path.
  • T: is the writable temporary namespace. Temporary files round-trip through the same RAM-backed DOS store and existing open/write/read/close operations; M15 does not add hierarchical directories or persistent storage.
  • Boot and command execution remain ELF-only at the public boundary. The M14.2 guarantees above still hold: ExecProgram is descriptor-only, DOS_RUN does not accept legacy flat images, and DOS_LOADSEG / DOS_RUNSEG remain the public executable path.

M15.2 host-backed boot status - the shipped runtime now boots from the generated host-backed system tree:

  • SYS: is the mounted host-backed boot volume. SYS: is now the live bootstrap root for the shipped IntuitionOS runtime rather than a future direction.
  • IOSSYS: is the built-in system assign rooted at SYS:IOSSYS. IOSSYS: as the built-in system assign rooted at SYS:IOSSYS is the canonical system tree name, while the functional assigns (C:, S:, L:, LIBS:, DEVS:, RESOURCES:) keep their short M15-style public DOS_ASSIGN view.
  • DOS_ASSIGN stays compatibility-shaped. Public list/query rows remain name[16], target[16]; SYS: and IOSSYS: are built-in resolver roots rather than mutable long chained rows; canonical functional assigns still expose short targets like C/ and LIBS/.
  • exec.library remains the only ROM-resident runtime component. The bootstrap mounts hostfs as SYS:, console.handler now boots from IOSSYS:L/console.handler, and dos.library now boots from IOSSYS:LIBS/dos.library.
  • Shell now boots from IOSSYS:Tools/Shell. Commands, libraries, devices, resources, and S:Startup-Sequence now come from the generated sdk/intuitionos/system/SYS/IOSSYS tree.
  • RAM: and T: remain the writable in-memory namespaces. Their reads and writes stay on the provider-backed DOS path while hostfs remains read-only in M15.2.
  • ASSIGN ADD is still deferred. M15.2 keeps bare command search C:-only and does not add ordered-search assign semantics yet.

M15.3 layered assigns + writable SYS: overlay - M15.3 lifts the M15.2 ASSIGN-ADD restriction and lights up a layered model on top of the host-backed boot:

  • Canonical assigns are now layered. Each of C:, S:, L:, LIBS:, DEVS:, and RESOURCES: resolves through a built-in base list [SYS:X, IOSSYS:X] plus a per-slot mutable overlay list.
  • DOS_ASSIGN grows three new sub-ops. DOS_ASSIGN_LAYERED_QUERY (3) returns the FULL effective ordered list as count × target[32]. DOS_ASSIGN_ADD (4) appends to the mutable overlay (duplicate-add is a no-op). DOS_ASSIGN_REMOVE (5) removes from the overlay only - built-in base entries cannot be removed.
  • Old ABI keeps working unchanged. DOS_ASSIGN_LIST / DOS_ASSIGN_QUERY keep the M15.2 name[16], target[16] row shape and project the first effective public target so existing callers keep seeing whatever the user just SET.
  • DOS_ASSIGN_SET mirrors into both layers. A SET on a canonical layered slot replaces the mutable overlay with [TARGET] and writes the same target into the table entry, so dos_assign_lookup (and therefore the hostfs path resolver) keeps returning the user's chosen target without breaking M15.2 backward compat.
  • Hostfs now supports a writable SYS: overlay. New device commands BOOT_HOSTFS_CREATE_WRITE (6) and BOOT_HOSTFS_WRITE (7) let dos.library create/write under the host-backed root. Any path whose first component resolves to IOSSYS is rejected by the device - IOSSYS: is always read-only.
  • DOS_OPEN reads now fall through SYS:IOSSYS:. A new dos_hostfs_layered_relpath_for_resolved_name helper prefers the writable SYS overlay (e.g. hostRoot/C/Foo) when the file is present and falls back to the embedded read-only IOSSYS path otherwise.
  • ASSIGN shell command grows ADD / REMOVE / show. ASSIGN NAME: displays the assign's full effective ordered list. ASSIGN ADD NAME: TARGET: appends to the overlay. ASSIGN REMOVE NAME: TARGET: removes one overlay entry. ASSIGN NAME: TARGET: keeps its M15.2 replace semantics.
  • Non-layered assigns stay non-layered. RAM: is non-mutable. T: is single-target writable. SYS: and IOSSYS: are built-in roots and reject ADD/REMOVE outright.
  • Unchanged execution boundary. M15.3 makes no changes to ExecProgram, ELF/seglist contracts, or the M14.2 ELF-only command boundary.

M15.5 substrate status - M15.5 is the loader/toolchain/process groundwork milestone that closes the gap between M15.4 hardening and M16 protected modules:

  • full TDD remains mandatory for every M15.5 phase. Docs, focused regressions, implementation, and verification all ship together; the milestone is not treated as optional cleanup.
  • PIE-capable codegen contract. The canonical IOS-native rules now live in sdk/docs/IntuitionOS/Toolchain.md, so hand-written IE64 assembly and future compiler backends target the same forward-compatible addressing contract.
  • strict ET_EXEC runtime contract remains unchanged. M15.5 does not add ET_DYN, runtime relocation, ASLR, or KASLR; it tightens substrate and diagnostics without widening the executable format boundary.
  • Internal teardown and fault substrate are now explicit. Exec now has ordered internal task-exit hooks, richer hardening diagnostics, and documented MMIO carve-outs/grant-path assumptions as groundwork for M16 rather than user-visible module behavior.

M16 protected module subsystem status - the protected library lifecycle is now shipped, documented, and test-covered:

  • OpenLibrary / CloseLibrary are now the canonical programmer-facing library lifecycle. Shipped library clients acquire runtime libraries through the registry-backed path first and only resolve compat ports afterward as internal transport, so module open-count and death-notification tracking stays exec-owned.
  • graphics.library and intuition.library are no longer launched from S:Startup-Sequence. dos.library remains the first disk-backed bootstrap library opened by exec; graphics.library and intuition.library now demand-load on first OpenLibrary.
  • Exec owns the protected module registry. Library rows track identity, state, version, generation, owner task, compat port, open count, flags, waiter queue, and expunge deadline so crash/expunge/reload cycles stay generation-safe.
  • Library registration and expunge are explicit. Library tasks self-register through AddLibrary, exec drives LIB_OP_EXPUNGE, and RESIDENT shell command pin/unpin flows toggle MODF_RESIDENT without turning libraries back into startup-sequence commands.
  • Crash handling is observable to clients. If an online library task dies, exec tears the row down, releases the loader bookkeeping, and signals openers with SIGF_MODDEAD before the next OpenLibrary triggers a clean reload.

M16.1 IOSM / VERSION status - version metadata is now universal and queryable:

  • Every rebuilt runtime ELF carries IOS PT_NOTE metadata. The stripped, section-header-free runtime images carry a 128-byte IOS-MOD / IOSM descriptor that records kind, public name, version/revision/patch, flags, message ABI, build date, and the fixed copyright string.
  • Resident version queries use existing IPC. Persistent services answer MSG_GET_IOSM through caller-allocated shared memory, and exec.library exposes a public port for its own IOSM plus resident public-port enumeration.
  • Resident inventory is explicit IPC. C:Resident lists exec-owned resident inventory through EXEC_MSG_LIST_RESIDENT_INVENTORY on the public exec.library port, and ADD/REMOVE now print visible status or diagnostics while preserving SYS_SET_RESIDENT as the narrow mutation primitive.
  • VERSION no longer reports stale milestone text. The default command output is IntuitionOS 1.16.6, exec.library 1.16.6 (2026-04-25), and Copyright © 2026 Zayn Otley.

M16.2 protected non-library module status - handlers, devices, and resources now use the protected-module lifecycle internally without absorbing PIE/ASLR work:

  • Handlers, devices, and resources are first-class protected module classes internally. console.handler, input.device, and hardware.resource self-register through class-correct AddHandler, AddDevice, and AddResource aliases on the same exec-owned registry and state machine used by libraries.
  • Boot policy replaces startup-script module launches. console.handler remains the only early-launch source exception, while dos.library runs the eager post-DOS policy for hardware.resource and input.device before Shell. S:Startup-Sequence is command/configuration-only.
  • Module semantics come before public API and loader relocation work. M16.2 owns internal registration, naming, ownership, eager policy, hardware-resource broker generation checks, compat-port policy, and registry transitions for non-library classes; public AttachHandler / OpenDevice / OpenResource acquisition APIs are reserved for M16.2.1.
  • PIE and ASLR stay separate. M16.3 is reserved for making the shipped ELF surface consistently PIE-capable; M16.4 is reserved for real relocation and ASLR/randomized placement. M16.2 keeps the M14.2 ET_EXEC placement contract unchanged.
  • See sdk/docs/IntuitionOS/M16.2-plan.md for the full TDD milestone plan and handoff notes.

M16.4.1 PT_NOTE runtime ELF / ASLR status - MODF_ASLR_CAPABLE is now operational:

  • All runtime ELFs are stripped self-contained ET_DYN. Shipped commands, libraries, devices, handlers, resources, host-provided DOS ELFs, and third-party DOS-loadable runtime ELFs use zero-relative PT_LOAD addresses, one PT_NOTE carrying IOS-MOD plus optional IOS-REL, and no section header table. Section-header-only metadata is rejected.
  • Relocation is local and bounded. IntuitionOS accepts local runtime relocation metadata without adding dynamic linking, PT_INTERP, PT_DYNAMIC, DT_NEEDED, imported-symbol lookup, PLT/GOT binding, lazy binding, or a shared-object namespace. Protected .library use remains port/message based.
  • Userland ASLR is enabled. Runtime images load at chosen user image bases; M16.4.1 only readies the system for KASLR. M16.5 still owns fixed kernel VA blockers including KERN_PAGE_TABLE, KERN_DATA_BASE, KERN_STACK_TOP, supervisor identity mapping, trap/fault paths, scheduler state access, panic/debug paths, and task page-table copying of kernel mappings.
  • Trusted protected-module autoload is provenance-bound. Trusted-internal launch authority requires trusted read-only system source provenance plus validated IOSM metadata, not a writable overlay filename.
  • Hardening rules stay intact. W^X, SKEF/SKAC/SUA discipline, bounded inputs, and non-executable shared-memory MAPF_READ / MAPF_WRITE transfers remain mandatory.

M15.1 source layout status - the IntuitionOS sources are no longer forced to live in one monolithic assembly body, and the hostfs runtime now builds separately from the kernel image:

  • sdk/intuitionos/iexec/iexec.s remains the kernel assembly entrypoint and the top-level exec.library image/layout file.
  • sdk/intuitionos/iexec/runtime_builder.s assembles the standalone hostfs runtime artifacts that are exported into sdk/intuitionos/system/SYS/IOSSYS.
  • Command sources now live under sdk/intuitionos/iexec/cmd/.
  • console.handler, input.device, hardware.resource, graphics.library, and intuition.library now live in per-component sources under sdk/intuitionos/iexec/handler/, sdk/intuitionos/iexec/dev/, sdk/intuitionos/iexec/resource/, and sdk/intuitionos/iexec/lib/.
  • sdk/intuitionos/iexec/handler/shell.s owns prog_shellprog_shell now lives in a dedicated handler source file.
  • sdk/intuitionos/iexec/lib/dos_library.s owns prog_doslibprog_doslib now lives in its own library source file.
  • sdk/intuitionos/iexec/cmd/gfxdemo.s, sdk/intuitionos/iexec/cmd/about.s, and sdk/intuitionos/iexec/assets/elfseg_fixture.s are built as standalone runtime artifacts through runtime_builder.s.
  • sdk/intuitionos/iexec/boot/bootstrap.s and sdk/intuitionos/iexec/boot/strings.s keep the root bootstrap tables and boot strings out of the main kernel body.
  • iexec.s now keeps the root boot/image wiring in boot/ includes.
  • make intuitionos still builds everything from source, but only exec.library remains ROM-resident at runtime.
  • IntuitionOS still lives under sdk/ in M15.1 for repository-history reasons; this refactor changes source ownership, not repo packaging.

M13 phase 5 status - startup block ABI + dynamic task-image placement + live-task ceiling removal to the current ABI bound, with full boot-stack and GUI regressions green (implemented and tested):

  • Booted services no longer self-locate from CURRENT_TASK * USER_SLOT_STRIDE. The kernel now allocates a dedicated startup page for each launched task, writes the 64-byte startup block there, and places the startup-page base VA at 0(sp), so the booted M12 stack reads task identity/layout from that page instead of recomputing addresses from the task ID.
  • Startup block fields are explicit and test-covered. Version, size, task ID, flags, and the actual code/data/stack base/page counts are written by load_program and CreateTask. TestIExec_M13_StartupBlock_BootTaskPresent and TestIExec_M13_StartupBlock_CreateTaskPresent lock that contract in before the later dynamic-task-model phases land.
  • Source compatibility preserved for existing services. Services now discover the startup page from the initial stack contract rather than assuming a reserved window inside their first data page.
  • Task image placement is no longer derived from task_id * USER_SLOT_STRIDE. load_program and CreateTask allocate code/stack/data/startup pages dynamically, using the legacy fixed image window first and then spilling into allocator-pool pages as needed. PT backing likewise uses the legacy fixed PT window first and then spills into allocator-pool-backed 64 KiB PT blocks.
  • The old 32-live-task ceiling is gone. The remaining fixed task-state tables now run to the current 8-bit internal-slot ABI ceiling (MAX_TASKS = 255, with 0xFF reserved in a few byte-sized kernel fields). Focused phase-4 tests now prove both >32 live CreateTask success and public task IDs advancing beyond 255 under churn.
  • Phase 5 boot-stack/demo validation is explicit. The final M13 gate now has named regressions for the full visible boot stack (TestIExec_M13_Phase5_FullBootStack_ServiceCensus) and for both retained GUI demos (TestIExec_M13_Phase5_GfxDemoRegression, TestIExec_M13_Phase5_AboutRegression). The older milestone tests still exist, but M13 no longer relies on them implicitly.

Milestone 12.8 status - dos.library variable-size file storage + load_program cap removal (implemented and tested):

  • DOS_FILE_SIZE is gone. Per-file storage in dos.library migrates from a fixed 16 KiB AllocMem block to a chain of 4 KiB extents allocated on demand. Each file's entry.file_va is now the head of an extent chain whose total length is bounded only by the kernel allocator pool. The previous bucket-B DOS_FILE_SIZE = 16384 cap (bumped 4096 → 8192 → 16384 across M11/M12) has been deleted entirely.
  • Atomic-swap-on-rewrite. DOS_WRITE's most load-bearing change: a new chain is allocated and populated before the old chain is freed, so an allocation failure during a rewrite leaves the previous file content fully intact. The handler never observes a half-rewritten state. .dos_extent_alloc's internal .dea_fail_partial cleanup ensures that any partially-allocated new chain is freed before the error reply, so the steady-state memory usage on a failed write is identical to the pre-write state. TestIExec_DosM128_RewriteShrinks and TestIExec_DosM128_RewriteGrows exercise both directions.
  • Four canonical extent operations in iexec.s: .dos_extent_alloc(byte_count) walks up ceil(byte_count / DOS_EXT_PAYLOAD) extents and links them into a chain (or returns 0 on failure with the partial chain freed); .dos_extent_free(first_va) walks the chain calling SYS_FREE_MEM on each extent; .dos_extent_walk(first_va, dst, byte_count) reads bytes from the chain into dst; .dos_extent_write(first_va, src, byte_count) writes bytes from src into the chain. The four are the only paths that touch the new DOS_EXT_* layout - every dos.library handler that used to memcpy into a fixed body now goes through them.
  • DOS_RUN walks extents into a temp buffer before passing them to SYS_EXEC_PROGRAM. The kernel still requires a contiguous image pointer, so the handler AllocMems a temp buffer sized exactly to the program image, walks the extent chain into it, calls SYS_EXEC_PROGRAM, then FreeMems the temp once the kernel has copied the image into the new task image. Steady-state dos.library memory is unaffected.
  • Two more arbitrary product caps removed as a prerequisite. When the dead-code skeleton bumped dos.library's code section from 8016 to 8712 bytes, load_program rejected the image with ERR_BADARG because of hardcoded code_size <= 8192 and data_size <= 49152 checks. Those were arbitrary product limits, not architectural bounds. Both caps were deleted in M12.8, and M13 phase 2 completes the cleanup by removing slot-derived task placement entirely: load_program now allocates code/stack/data/PT backing dynamically inside the reserved image/PT windows, so the real failure mode is allocator/window exhaustion rather than a fake per-image byte ceiling.
  • Robust dos.library preamble. The other Phase 1 surprise was that dos.library's preamble hardcoded add r29, r29, #0x3000 to compute its data-page base - an offset that assumed exactly 2 code pages. Bumping dos.library to 3 code pages broke the preamble silently (it pointed at the stack page instead of the data section). Phase 1 fixed that by removing the hardcoded offset; the current M13 shape writes data_base at the top of the initial stack page, so after sub sp, sp, #16 the preamble recovers it with load.q r29, 8(sp) without assuming stack/data adjacency. dos.library can grow without touching its preamble again.
  • Stale "IntuitionOS M11" shell banner removed. The shell printed an outdated milestone label between the service-online lines and the canonical version banner. Killed in Phase 1 - the shell no longer emits its own banner; the canonical version banner from VERSION (run by S/Startup-Sequence) is the only one.
  • No new syscalls. No new DOS opcodes. No widened ABI fields. No DOS protocol changes. The M12.5/M11.5 admission rule still holds. Existing dos.library clients (the shell, every M9–M12 example, every test) continue to work without recompilation. The visible change is that DOS_WRITE no longer rejects byte counts above 16384, and reads/writes of multi-extent files transit a small chain walk inside dos.library that's invisible to clients. DOS_DELETE, DOS_TRUNCATE, DOS_SEEK, and DOS_APPEND are explicitly not added - each would be a separate future protocol-extension milestone if and when the need arises.
  • Test coverage. Three new tests in iexec_test.go exercise the M12.8 storage shape:
    • TestIExec_DosM128_FileLargerThanOldCap - 32 KiB write/read round-trip, byte-for-byte verified across 9 extents (2× the previous 16 KiB cap). Load-bearing test for the per-file cap removal AND the multi-extent walker.
    • TestIExec_DosM128_RewriteShrinks - write 8 KiB, then rewrite the same file with 1 KiB; readback expects 1 KiB of new content. Proves atomic-swap on shrink.
    • TestIExec_DosM128_RewriteGrows - write 1 KiB, then rewrite with 8 KiB; readback expects 8 KiB of new content. Proves atomic-swap on grow. Four other tests from the M12.8 plan are skipped with documented rationale (covered by existing tests, or test allocator-pool exhaustion which requires fragile state mocking). The full M12.5/M12.6 hardening test suite remains green throughout - no kernel data structure changes, no new ABI fields.
  • Audit refresh. IExec.md §5.13.1 row for DOS_FILE_SIZE is now (removed) - B → ✓ with a description of the slab/extent allocator and the atomic-swap rule. Two new (removed) rows for IMG_OFF_CODE_SIZE/IMG_OFF_DATA_SIZE document the load_program cap removal and explain why those rows were originally misclassified into bucket B. The §5.13 prose is updated to note that bucket B has lost its load-bearing entries - the remaining bucket-B rows are configured-policy values (KD_PORT_FIFO_SIZE, USER_DYN_PAGES, DATA_ARGS_*), not arbitrary product limits.
  • Boot integration. The long milestone summary line is gone. Boot now shows compact per-service version tags in each task banner, VERSION prints the plain OS version string IntuitionOS 0.18, and S:Startup-Sequence prints Type HELP for commands and ASSIGN for layout.
  • Demo boot output:
    exec.library M11 boot
    console.handler M11.5 [Task 0]
    dos.library M14 [Task 1]
    Shell M10 [Task 2]
    hardware.resource M12.5 [Task 3]
    input.device M11 [Task 4]
    graphics.library M11 [Task 5]
    intuition.library M12 [Task 6]
    IntuitionOS 0.18
    Type HELP for commands and ASSIGN for layout
    1>
    
    M12 GUI demo runs unchanged from a user perspective - same About app, same close-gadget interaction. The task-model work remains mostly invisible to user-space code; the visible change is that the boot text now reflects M13 directly without replaying older milestone-summary ECHO lines.

Full kernel contract reference: sdk/docs/IntuitionOS/IExec.md (see §5.13 for the cap-removal policy and §12 Milestone 12.8 for the storage refactor)

Milestone 12.6 status (complete) - full hard-cap sweep across DOS, shmem, ports, and tasks

Milestone 12.6 status - full hard-cap sweep across DOS, shmem, ports, and tasks (implemented and tested):

  • Bucket C of the IExec audit is empty. Every previously bucket-C cap was removed (chain-allocator conversion) or reclassified into bucket A/B with a recorded reason. The four phases worked in order DOS → SHMEM → PORT → TASKS, gating on a green full-suite run between each.

  • Phase A - dos.library file/handle caps removed. DOS_MAX_FILES = 16 and DOS_MAX_HANDLES = 8 are gone. Both tables are now linked lists of AllocMem'd 4 KiB pages (85 file metadata entries or 510 handle entries per page). Each file body is its own AllocMem(DOS_FILE_SIZE) allocation rather than a packed offset into one big region. The old storage_va packed-storage region is gone. dos.library's DOS_OPEN/READ/WRITE/CLOSE/DIR/RUN handlers all walk the chain. TestIExec_NoCap_DosFilesAndHandlesGrow opens 24 distinct files and keeps all 24 handles open simultaneously - proving both old caps are gone in one test.

  • Phase B - KD_SHMEM_MAX = 16 removed. The system shmem table is unbounded: inline-first-16 + global overflow chain reachable through KD_SHMEM_OFLOW_HDR. New helpers kern_shmem_alloc_slot and kern_shmem_addr_for_id. All five shmem walkers (SYS_ALLOC_MEM, SYS_FREE_MEM, SYS_MAP_SHARED, kill_task_cleanup inline + overflow paths) updated. The 1-byte shmem ID field caps total slots at 255 (0xFF reserved). TestIExec_NoCap_ShmemMaxRemoved creates 32 shmem regions and proves the chain head was actually allocated.

  • Phase C - KD_PORT_MAX = 32 removed. The global port table is unbounded: inline-first-32 + global overflow chain reachable through KD_PORT_OFLOW_HDR. Three new helpers (kern_port_alloc_slot, kern_port_addr_for_id, kern_port_find_public). All eight port walkers updated: .do_create_port, .do_find_port, .do_put_msg, .do_get_msg, .do_wait_port, .restore_waitport, check_name_unique, and the kill_task_cleanup port walk (which now walks inline + every chain page via a .ktc_port_clear subroutine). The 1-byte port ID ABI ceiling is 255 (WAITPORT_NONE = 0xFF reserved). TestIExec_NoCap_PortMaxRemoved creates 64 ports in one task and verifies all 64 IDs are distinct, with at least one in the chain range. Critical structural fix landed in this phase: build_user_pt now copies the allocator pool mapping (supervisor-only) into every user PT, so chain helpers can run on the user PT without per-handler PT switches. The supervisor-only flag survives the copy because the source kernel PT entries do not have the U bit set; the user PT inherits the same flags. This shortcut is only safe in combination with Phase E's disjoint-VPN layout fix below - if user SYS_ALLOC_MEM could allocate at a VPN inside the pool range, the supervisor copies would be silently overwritten and the chain walkers would dereference attacker-controlled memory.

  • Phase D - MAX_TASKS bumped from 16 to 32. The cap was bumped via a full layout adjustment (NOT a chain conversion) because the real bound on task count is the user-space slot region size, not an arbitrary kernel constant. The user-space layout was reorganized: USER_PT_BASE moved from 0x700000 to 0x800000 (PT region grows from 1 MiB to 2 MiB), the allocator pool shifted from PPN 0x800 to PPN 0xA00 (lost 512 pages = 2 MiB to make room for the doubled PT region), and USER_DYN_BASE shifted from 0x800000 to 0xA00000. Every kernel data field after the TCB and region tables also shifted (TCBs grew from 512 to 1024 bytes, region tables from 2048 to 4096 bytes, etc.). MAX_TASKS is reclassified C → B (layout-bound, not arbitrary) with a named replacement plan: future slot-layout redesign milestone if 32 tasks ever isn't enough. The chain-allocator pattern was deliberately not applied here because the actual ceiling is the layout, not the kernel data structure. TestIExec_NoCap_MaxTasksBumpedTo32 calls SYS_CREATE_TASK 16 times and observes max task ID = 18 (proving the old MAX_TASKS = 16 cap is gone). Phase D's intermediate layout (which reused the same 0xA00..0x1FFF VPN range for both USER_DYN_BASE..USER_DYN_END and the allocator pool) was later split apart by the Phase E security fix below - the final post-M12.6 layout has the pool at PPN 0x1200..0x1FFF and the user-dyn window at VA 0xA00000..0x1200000.

  • Phase E - disjoint user-dyn / allocator-pool VPN ranges (security fix). A pre-merge security review caught a privilege-escalation path in the Phase D layout: USER_DYN_BASE..USER_DYN_END (0xA00000..0x2000000) overlapped exactly with the allocator pool VPN range (PPN 0xA00..0x1FFF), so an unprivileged task could call SYS_ALLOC_MEM to overwrite the supervisor-only pool PTE that build_user_pt had copied into its user PT, then call SYS_CREATE_PORT to allocate a port chain page at the same physical PPN, then call SYS_ALLOC_MEM again at that VPN to remap it to attacker-RW memory. The supervisor-mode port chain walkers (which run on the user PT, not the kernel PT, per Phase C's design) would then dereference attacker-controlled bytes - yielding arbitrary kernel read/write from a pure unprivileged task. Fix: split the two ranges at the layout level. The user-dyn window now occupies the bottom half (USER_DYN_BASE..USER_DYN_END = 0xA00000..0x1200000, 8 MiB, VPN 0xA00..0x11FF); the allocator pool occupies the top half (ALLOC_POOL_BASE = 0x1200, ALLOC_POOL_PAGES = 3584, PPN 0x1200..0x1FFF, 14 MiB). User SYS_ALLOC_MEM calls now map fresh pool PPNs at user-dyn VPNs that are never in the pool VPN range, so the supervisor copies are immutable from user space. The pool shrunk from 22 MiB to 14 MiB, but the user-dyn window grew from 0 (it was aliased and unusable as a separate range) to a true 8 MiB - net usable user-controlled memory is roughly the same. TestIExec_PortChain_DisjointFromUserDyn reproduces the exact attack pattern (AllocMem → 33×CreatePort → AllocMem) and asserts that both AllocMem VAs land inside the user-dyn window AND outside the pool VPN range, and that the port chain head PPN lands inside the disjoint pool range. Two files touched (iexec.inc constants, iexec_test.go test constants + new test) - the kernel .s file did not need any edits because every layout reference goes through named constants.

  • Honest summary banner. The boot banner gains one new line:

    Core OS objects: fixed product limits removed where practical; remaining limits are architectural or layout-bound

    Not the more ambitious wording from the original M12.6 plan ("memory or ABI-width exhaustion"). Tasks are layout-bound, so claiming "memory exhaustion" would have been misleading.

  • Audit refresh. IExec.md §5.13.1 is rewritten end-to-end. Every line number refreshed (the .inc file shifted ~50 lines from M12.6 edits). Bucket C is now empty. Every previously-C row has a recorded fate: removed (Phase A), reclassified C → A as a legacy alias for the inline range (Phases B/C and M12.5's KD_REGION_MAX), or reclassified C → B with replacement plan (Phase D's MAX_TASKS). DOS_FILE_SIZE was never in scope for the chain-allocator sweep - it's a per-file byte size, not a count - and waits on M12.8's dos.library variable-size storage refactor.

  • Boot integration. Version banner bumped to IntuitionOS 0.14 (exec.library M11.6 / intuition.library M12 / hardware.resource M12.5 / cap sweep M12.6).

  • Demo boot output:

    exec.library M11 boot
    console.handler ONLINE [Task 0]
    dos.library ONLINE [Task 1]
    Shell ONLINE [Task 2]
    IntuitionOS M11
    hardware.resource ONLINE [Task 3]
    input.device ONLINE [Task 4]
    graphics.library ONLINE [Task 5]
    intuition.library ONLINE [Task 6]
    IntuitionOS 0.14 (exec.library M11.6 / intuition.library M12 / hardware.resource M12.5 / cap sweep M12.6)
    Core OS objects: fixed product limits removed where practical; remaining limits are architectural or layout-bound
    IntuitionOS M12.6 ready
    All visible services are running in user space
    1>
    

    M12 GUI demo runs unchanged from a user perspective - same About app, same close-gadget interaction. Only the boot banner and the kernel data structures underneath have changed.

Milestone 12.5 status (complete) - `hardware.resource` + minimal trust model + first cap removal

Milestone 12.5 status - hardware.resource + minimal trust model + first cap removal (implemented and tested):

  • hardware.resource ships as a user-space MMIO arbiter. A new public message port ("hardware.resource") hosts the first IntuitionOS broker. Owns the policy mapping from 4-byte region tags ('CHIP', 'VRAM') to physical PPN ranges. Clients send HWRES_MSG_REQUEST naming a tag; the broker uses the kernel-trusted sender task ID (R7 from SYS_WAIT_PORT/SYS_GET_MSG, populated from KD_MSG_SRC) - never anything client-supplied - and calls SYS_HWRES_OP/HWRES_CREATE to write a kernel grant row covering the right PPN range. Reply is HWRES_MSG_GRANTED whose data0 carries (ppn_base<<32) | page_count.
  • SYS_MAP_IO is de-publicized. The hardcoded allowlist ((0xF0, 1) + [0x100..0x5FF] VRAM range) is gone. The handler now consults a kernel grant chain - only callers holding a covering grant entry succeed; everyone else gets ERR_PERM. SYS_MAP_IO is reclassified from nucleus to bootstrap, trusted-internal - gated by KD_GRANT_TABLE. Net public ABI shrinks: one nucleus syscall leaves the public set, one trusted-internal slot enters.
  • Minimal trust model with full lifecycle cleanup. A single kernel byte KD_HWRES_TASK (initially 0xFF/sentinel) holds the broker task ID. The first task to call SYS_HWRES_OP/HWRES_BECOME claims broker identity; subsequent claims return ERR_EXISTS. HWRES_CREATE is gated by current_task == KD_HWRES_TASK. kill_task_cleanup clears the broker identity if the exiting task is the broker, walks the kernel grant chain to release all rows belonging to the exiting task, and the broker itself runs a stale-owner scrub on every request via HWRES_TASK_ALIVE so a recycled task slot can never inherit broker privilege, granted MMIO, or per-tag owner state. There are no groups, ACLs, users, or capability masks beyond this single distinction. M12.5 deliberately stops at one privileged identity because no second consumer exists yet - full multiuser/login waits for persistent storage.
  • One new syscall slot, fresh number, four verbs. SYS_HWRES_OP lives at slot 38, multiplexed by an R6 verb selector: HWRES_BECOME (claim broker identity) / HWRES_CREATE (write a grant row, broker-only) / HWRES_REVOKE (reserved for M13) / HWRES_TASK_ALIVE (broker queries kernel TCB state to reclaim stale per-tag owner slots, broker-only). Slot 37 stays a reserved hole forever per the M11.5 contract - TestIExec_HWRes_Slot37StillReserved makes the contract executable so a future patch cannot quietly recycle it.
  • SYS_GET_MSG / SYS_WAIT_PORT return R7 = sender task ID. The kernel populates R7 from KD_MSG_SRC (which PutMsg filled in with the actual sender - unforgeable from user space). The broker uses R7 as its trusted sender identity instead of trusting any client-supplied data1 field. This is an extension of existing return-register usage, not a new syscall slot - net public ABI is still +1 net (one out, one in).
  • Bootstrap grant table. A small immutable list keyed by program-table boot index (NOT task ID, because task IDs do not exist at kern_init). The boot-load loop resolves each row to a live grant row immediately after load_program returns the assigned task ID. M12.5 ships with exactly one bootstrap row: (console.handler, 'CHIP', PPN 0xF0) - this is what lets console.handler map its serial-port MMIO before hardware.resource is alive, breaking the chicken-and-egg of hardware.resource depending on console.handler for output.
  • Migrated callers. input.device and graphics.library no longer call SYS_MAP_IO ambiently. Each spins on FindPort("hardware.resource") until the broker is up, sends one HWRES_MSG_REQUEST per region needed (input.device wants 'CHIP'; graphics.library wants 'CHIP' and 'VRAM'), waits for the reply, and only then calls SYS_MAP_IO. console.handler is the only kernel-bootstrap-granted task.
  • KD_REGION_MAX cap removed. The first M12.5 cap removal - exactly one pre-existing fixed cap, locked in (not deferred). The per-task region table grows from a fixed 8-row block to inline-rows-plus-overflow-chain: rows 0..7 still live inline at KD_REGION_TABLE for the fast path, rows 9+ live in allocator-backed chain pages reached through a per-task overflow header at KD_REGION_OVERFLOW_HEAD. All six region walkers (AllocMem, FreeMem, MapShared, SYS_MAP_IO, find_free_va, kill_task_cleanup) iterate inline first, then walk the overflow chain. Only failure mode is real ERR_NOMEM from the page allocator. TestIExec_NoCap_RegionMaxRemoved proves a 9th allocation succeeds; TestIExec_HWRes_GrantTableChainGrows exercises the same chain-allocator pattern against the grant table.
  • Named hard-cap policy lands as IExec.md §5.13:

    IntuitionOS does not use arbitrary fixed product limits for core OS objects where dynamic allocation is practical. Remaining limits must be justified by architecture, ABI width, hardware constraints, or explicitly configured resource policy. Every existing cap is classified into A (architectural - keep), B (temporary - replace later), or C (arbitrary - remove now). M12.5 removes one C-bucket cap (KD_REGION_MAX). (Historical M12.5 framing - the M12.6 sweep is now complete; see the M12.6 status block above for the final state. Bucket C is empty.)

  • Boot integration. S/Startup-Sequence extended with RESOURCES:hardware.resource as the FIRST line (before DEVS:input.device) so it has its public port registered before any client calls FindPort. The version string is now IntuitionOS 0.13 (exec.library M11.6 / intuition.library M12 / hardware.resource M12.5).
  • Demo boot output:
    exec.library M11 boot
    console.handler ONLINE [Task 0]
    dos.library ONLINE [Task 1]
    Shell ONLINE [Task 2]
    IntuitionOS M11
    hardware.resource ONLINE [Task 3]         <- new in M12.5
    input.device ONLINE [Task 4]              <- now grants its CHIP MMIO via hardware.resource
    graphics.library ONLINE [Task 5]          <- now grants its CHIP+VRAM MMIO via hardware.resource
    intuition.library ONLINE [Task 6]
    IntuitionOS 0.13 (exec.library M11.6 / intuition.library M12 / hardware.resource M12.5)
    IntuitionOS M12.5 ready
    All visible services are running in user space
    1>
    
    M12 GUI demo runs unchanged from a user perspective - same About app, same close-gadget interaction. Only the boot banner and the architecture underneath have changed.
Milestone 12 status (complete) - intuition.library + single-window compositor + structural cap cleanup
  • intuition.library ships as a user-space service. A new public message port ("intuition.library") hosts the first IntuitionOS windowing layer. Lazy display ownership: text mode persists until the first INTUITION_OPEN_WINDOW, at which point intuition.library opens graphics.library at 800×600 RGBA32, allocates its own 1920000-byte screen surface, registers as the SOLE graphics.library client, subscribes to input.device, and MapShareds the client's window backing buffer. Closing the (single) window tears down the entire graphics-mode subsystem (SYS_FREE_MEM of the screen + the client mapping, INPUT_CLOSE only if intui actually owns the subscription, GFX_UNREGISTER_SURFACE, GFX_CLOSE_DISPLAY) and returns to text mode. Single-window only - second INTUITION_OPEN_WINDOW returns INTUI_ERR_BUSY. M12.x will add z-order.

  • Window protocol: INTUITION_OPEN_WINDOW / INTUITION_CLOSE_WINDOW / INTUITION_DAMAGE. intuition.library composites the (mapped) client buffer into its 800×600 screen surface and paints Magic Workbench-style window decoration on top:

    • 1-pixel 3D bevel - WHITE highlight on top + left edges, BLACK shadow on bottom + right edges (raised look)
    • 16-pixel title bar pinstripes alternating two shades of Amiga blue (light steel blue 0xFFB89878 + medium steel blue 0xFF905030)
    • Close gadget at top-left: light grey fill 0xFFCCCCCC, black 1px outline, recessed black 4×4 centre square
    • Depth gadget at top-right: light grey fill, black outline, two overlapping rectangle outlines (the classic AmigaOS front/back depth icon)

    All decoration is drawn via a small .intui_fillrect jsr/rts helper. After painting, intuition.library calls GFX_PRESENT (full-frame - graphics.library protocol unchanged). No new syscalls. No graphics.library protocol changes. The full M11.5 admission rule held.

  • IDCMP-style event delivery: IDCMP_RAWKEY / IDCMP_MOUSEMOVE / IDCMP_MOUSEBUTTONS / IDCMP_CLOSEWINDOW. intuition.library subscribes to input.device's existing raw event stream and routes events to each window's IDCMP port with screen→window-local coordinate translation. Both Esc and a mouse-button-down inside the close-gadget rect are intercepted into IDCMP_CLOSEWINDOW.

  • prog_about demo client. A user-space program shipped as C/About in the RAM: filesystem that demonstrates the full open→damage→close cycle and renders text via an embedded bitmap font. Allocates a 256000-byte (320×200 RGBA32) backing buffer, fills it with a dark teal backdrop, then renders five lines of white text into the content area using the embedded Topaz 8×16 bitmap font (the AmigaOS Topaz Plus font lives at sdk/include/topaz.raw and is embedded into the About app's data section via incbin "topaz.raw" - full 256-glyph × 16-byte = 4096 bytes). Glyph rendering is done by tiny draw_char / draw_string subroutines in the About program itself. Lines rendered:

    About IntuitionOS
    Microkernel + intuition.library
    M12 demonstration window
    Press Esc to close
    (C) 2024-2026 Zayn Otley
    

    About sends INTUITION_OPEN_WINDOW with the buffer's share handle (window centered on the 800×600 screen at (240, 200), size 320×200), sends DAMAGE, services IDCMP, exits cleanly on IDCMP_CLOSEWINDOW. Reachable from the shell as C:About - not auto-launched at boot, so the existing M11 GfxDemo task budget is preserved.

  • Structural caps cleaned up. M11 inherited several arbitrary 8-of-everything caps from M5/M7 that had become tight as services accumulated. M12 quadrupled the port cap, doubled the task and shared-object caps, doubled port name length, and bumped the dos.library file slot size:

    • MAX_TASKS: 8 → 16. Unblocked by removing the per-task USER_DYN_STRIDE window - the dynamic VA range (USER_DYN_BASE..USER_DYN_END) is now GLOBAL, with each task's own page table providing isolation. The M11 design pre-allocated each task a 3 MiB slice of the 32 MiB VA space and saturated at exactly 8 tasks; the global VA design has no such limit. Slot region shifted from 0x600000..0x67FFFF to 0x600000..0x6FFFFF, user-PT region from 0x680000 to 0x700000, ALLOC_POOL_BASE from PPN 0x700 to 0x800.
    • KD_PORT_MAX: 8 → 32. 4× bump. M11 hit the 8-port wall as soon as services started creating their own anonymous reply ports.
    • KD_SHMEM_MAX: 8 → 16. Doubled.
    • PORT_NAME_LEN: 16 → 32. Doubled. M11's 16-byte cap was just barely big enough for "graphics.library" (exactly 16 chars, no null terminator) and outright too small for "intuition.library" (17 chars). Service names can now use the full Amiga-style name.library/name.device/name.handler form.
    • DOS_FILE_SIZE: 4096 → 8192. Doubled so the intuition.library service image fits in a single RAM: slot. The fixed-stride slot table is still arbitrary in principle - replacing it with a packed-heap allocator was attempted in M12, hit a runtime bug, and was reverted to the simpler bump pending a focused follow-up.
  • Boot integration. S:Startup-Sequence extended with LIBS:intuition.library. The version string is now IntuitionOS 0.12 (exec.library M11.6 / intuition.library M12).

  • Demo boot output (no user input):

    exec.library M11 boot
    console.handler ONLINE [Task 0]
    dos.library ONLINE [Task 1]
    Shell ONLINE [Task 2]
    IntuitionOS M11
    input.device ONLINE [Task 3]              <- launched by S:Startup-Sequence
    graphics.library ONLINE [Task 4]          <- launched by S:Startup-Sequence
    intuition.library ONLINE [Task 5]         <- launched by S:Startup-Sequence (M12, lazy display)
    IntuitionOS 0.12 (exec.library M11.6 / intuition.library M12)
    IntuitionOS M12 ready
    All visible services are running in user space
    1>
    

    Then 1> C:About opens a window, blits the backdrop, waits for IDCMP. Esc (or a click on the close gadget) sends IDCMP_CLOSEWINDOW, About sends INTUITION_CLOSE_WINDOW, intuition.library tears down the display, the system returns to text mode at the prompt.

Milestone 11.6 status (complete) - `SYS_EXEC_PROGRAM` legacy index path removed
  • Legacy R1 < USER_CODE_BASE index branch deleted from SYS_EXEC_PROGRAM. The dual-mode discriminator (if R1 >= USER_CODE_BASE → new ABI; else → table-lookup index path) is gone. The handler now begins with blt r1, USER_CODE_BASE → ERR_BADARG and falls directly into the validated image-pointer ABI body. Sub-USER_CODE_BASE values hard-fail with ERR_BADARG.
  • Resolves the one item M11.5 deferred. M11.5 documented the legacy branch as legacy and punted removal on the (incorrect) assumption that ~41 tests depended on it. The actual count was 6 test functions; 4 use the new ABI and survived unchanged, 2 tested the legacy helper directly and were deleted.
  • Guarded by TestIExec_ExecProgram_LegacyIndexReturnsBadarg - calls SYS_EXEC_PROGRAM with R1 = 0 (formerly the valid index for prog_console) and asserts ERR_BADARG plus the absence of console.handler ONLINE in the output.
  • Kernel binary shrinks ~624 bytes (45620 → 44996). program_table itself remains because the kernel boot path still uses it directly to load console.handler / dos.library / Shell into task slots at init; that's separate from the syscall path.
  • Standalone milestone, landed before M12 opened so the M12 (intuition.library) branch carries no test churn unrelated to windowing.
Milestone 11.5 status (complete) - Exec boundary cleanup
  • Exec boundary frozen. Syscall admission rule documented; surviving syscalls categorized as nucleus / bootstrap / legacy in sdk/include/iexec.inc and the IExec contract reference. New syscalls only land if they require kernel-privileged state AND cannot be expressed as a message to a user-space service.
  • 16 dead or redundant syscall slots removed. 15 reserved-but-unimplemented constants (SYS_ALLOC_SHARED, SYS_DELETE_TASK, SYS_FIND_TASK, SYS_SET_TASK_PRI, SYS_SET_TP, SYS_GET_TASK_INFO, SYS_PEEK_PORT, SYS_ADD_TIMER, SYS_REM_TIMER, SYS_CLOSE_HANDLE, SYS_DUP_HANDLE, SYS_MAP_VRAM, SYS_DEBUG, SYS_SEND_MSG_BULK, SYS_RECV_MSG_BULK) are deleted from the header; the slot numbers remain unallocated holes that fall through to ERR_BADARG. SYS_READ_INPUT (slot 37) is also removed - see below.
  • SYS_OPEN_LIBRARY collapsed to SYS_FIND_PORT. SYS_OPEN_LIBRARY is now a source-level alias (equ SYS_FIND_PORT) in iexec.inc. Slot 36 in the kernel dispatcher is retained as a one-instruction binary-compat redirect to .do_find_port, so any IE64 binary hardcoded to syscall number 36 still works. New code uses SYS_FIND_PORT directly. Guarded by TestIExec_OpenLibrary_DispatcherCollapse.
  • Console.handler now owns terminal MMIO directly. Previously the kernel exposed SYS_READ_INPUT (slot 37), which read TERM_LINE_STATUS / TERM_STATUS / TERM_IN from page 0xF0 on behalf of console.handler. M11.5 deletes the kernel handler. Console.handler now calls SYS_MAP_IO(0xF0, 1) at init time, caches the returned VA, and inlines the MMIO read loop into its CON_MSG_READLINE path. No client-visible change - the readline message protocol is unchanged. Slot 37 falls through to ERR_BADARG, guarded by TestIExec_ReadInput_RemovedReturnsBadarg.
  • SYS_MAP_IO allowlist documented as a known impurity. SYS_MAP_IO is a legitimate nucleus primitive, but its allowlist hardcodes IEVideoChip-specific knowledge (page 0xF0, VRAM range [0x100..0x5FF]) inside Exec - that is policy leaking into the nucleus. The pure-microkernel solution is a future hardware.resource user-space service that arbitrates physical regions; its bootstrap-ordering problem keeps it out of M11.5. The wart is documented and constrains future device additions to extend the existing allowlist rather than invent new MMIO syscalls.
  • SYS_EXEC_PROGRAM legacy index path documented as legacy and deferred (as of M11.5 - subsequently removed in M11.6; see the current status block above). M11.5 punted on removal because ~41 tests appeared to touch ExecProgram and the discriminator (R1 < 0x600000 → table lookup) was assumed to be load-bearing for tests that loaded programs by index. M11.6 reaudited and found 6 actual test functions, deleted the 2 that depended on the legacy helper, and removed the branch standalone before M12 began.
  • Startup-Sequence extended. The shipped S:Startup-Sequence adds one trailing ECHO All visible services are running in user space line - the milestone's user-visible artifact at the prompt.
  • No new syscalls. No protocol changes. No renumbering of any surviving syscall. IE64 binaries are compiled against numbers, not names, so the surviving syscall numbers remain stable forever.
  • Demo boot output (no user input):
    exec.library M11 boot
    console.handler ONLINE [Task 0]
    dos.library ONLINE [Task 1]
    Shell ONLINE [Task 2]
    IntuitionOS M11
    input.device ONLINE [Task 3]              <- launched by S:Startup-Sequence
    graphics.library ONLINE [Task 4]          <- launched by S:Startup-Sequence
    IntuitionOS 0.11 (exec.library M11.5)     <- VERSION run by S:Startup-Sequence
    IntuitionOS M11 ready                     <- ECHO run by S:Startup-Sequence
    All visible services are running in user space  <- ECHO run by S:Startup-Sequence (M11.5)
    1>
    
Milestone 11 status (complete) - input.device + graphics.library + fullscreen demo
  • Everything from M1-M10: self-sufficient boot, per-task page tables with W^X, trap dispatch, preemptive round-robin, signals, named message ports with FindPort discovery, request/reply messaging, dynamic task creation/exit, AllocMem/FreeMem/MapShared with capability handles and explicit shared-mapping permission masks, GURU MEDITATION fault messages, console.handler, dos.library RAM: filesystem with assigns, interactive shell, S:Startup-Sequence execution, DOS-loaded programs
  • SYS_MAP_IO extended to take a page count (R1=base_ppn, R2=page_count → R1=mapped_va, R2=err). Range-aware allowlist: (0xF0, 1) for chip MMIO and [0x100, 0x5FF] for any contiguous slice of the 5 MB VRAM range. One region slot per mapping (graphics.library maps 300 VRAM pages in a single call). Backwards-compatible: R2=0 is treated as R2=1 for M9/M10 callers.
  • USER_DYN_PAGES bumped from 256 to 768 (USER_DYN_STRIDE 1 MB → 3 MB). Required to fit graphics.library's 1 chip + 300 VRAM + 300 surface = 601 page mapping. The 8×3MB layout uses VA 0x800000-0x2000000 - the top of the 32 MB VA space. (Historical M11 layout snapshot. The current post-M12.6 layout is different: USER_DYN_BASE..USER_DYN_END = 0xA00000..0x1200000 (8 MiB), with the allocator pool split off into a disjoint VPN range at PPN 0x1200..0x1FFF by the M12.6 Phase E security fix. See the M12.6 status block above for the current values.)
  • load_program data-size cap raised from 16384 to 20480 (5 data pages). Required for dos.library to grow to 5 data pages embedding the new M11 service images.
  • dos.library assigns extended: LIBS:, DEVS:, RESOURCES: added to the assign table. Resolved by extending .dos_resolve_has_colon with 4-char (LIBS/DEVS) and 9-char (RESOURCES) length checks.
  • Three new embedded service images in dos.library's data section: LIBS/graphics.library, DEVS/input.device, C/GfxDemo. Installed into the RAM file table at init time.
  • S:Startup-Sequence updated: now launches DEVS:input.device and LIBS:graphics.library before VERSION, so the M11 services are running by the time the user gets the prompt.
  • input.device - keyboard/mouse event service. Maps page 0xF0 once via SYS_MAP_IO, polls SCAN_*/MOUSE_* registers, push-delivers INPUT_EVENT messages to a single registered subscriber port. Event format: mn_Data0 = (event_type<<24)|(code<<16)|(modifiers<<8)|flags, mn_Data1 = (mouse_x16<<48)|(mouse_y16<<32)|event_seq32. Mouse-move coalescing is a free property of the polling architecture.
  • graphics.library - fullscreen RGBA32 display service. Maps page 0xF0 (chip) and a 300-page VRAM range. Object model: enumerable adapters/modes, opaque display/surface handles, single-display owner, single-surface for M11. Client allocates surface buffer with MEMF_PUBLIC, hands the share_handle to graphics.library via GFX_REGISTER_SURFACE. Present is a synchronous CPU memcpy from the mapped surface to VRAM, replying with a per-surface monotonic present_seq counter. mn_Data1 reserves a packed dirty rect for future rect-bounded copies.
  • C/GfxDemo - minimal graphics client. Opens the M11 services, allocates a 1.2 MB surface, registers it, fills with a solid color, presents once, then waits for Escape and exits cleanly. Exercises the full M11 stack end-to-end.
  • Forward-compatible by design: present takes a packed dirty rect (M11 always sends 0 = full frame), mode table queryable via GFX_ENUMERATE_MODES/GFX_GET_MODE_INFO, opaque handles, explicit per-surface stride, GFX_WAIT_VBLANK and GFX_PRESENT_ASYNC reserved in opcode space.
  • Demo boot output (no user input):
    exec.library M11 boot
    console.handler ONLINE [Task 0]
    dos.library ONLINE [Task 1]
    Shell ONLINE [Task 2]
    IntuitionOS M11
    input.device ONLINE [Task 3]              <- launched by S:Startup-Sequence
    graphics.library ONLINE [Task 4]          <- launched by S:Startup-Sequence
    IntuitionOS 0.11 (exec.library M11)        <- VERSION run by S:Startup-Sequence
    IntuitionOS M11 ready                       <- ECHO run by S:Startup-Sequence
    1>
    

About

A modern 64-bit RISC re-imagining of Commodore/Atari/Sinclair/BBC/Amstrad/MSX/IBM 8/16/32-bit home computers with 6 heterogeneous CPU cores (6502/Z80/68020/x86/IE32/IE64) & 3DFX Voodoo/VGA/VideoChip/SID/PSG(AY/YM/SN)/POKEY/ULA/TED/Amiga AHX/ANTIC+GTIA video and audio chips.

Resources

License

Stars

Watchers

Forks

Sponsor this project