[← Home](../README.md) · [Exec Kernel](README.md) # ExecBase — Full Structure Reference ## Overview `ExecBase` is the root structure of AmigaOS, located at absolute address `$4`. It is a `struct Library` extended with all exec kernel state: memory lists, task queues, interrupt vectors, library lists, and hardware abstraction fields. Every system call goes through `ExecBase` — it is the single point of truth for the entire running system. --- ## Locating ExecBase ```c /* C — standard method */ struct ExecBase *SysBase = *((struct ExecBase **)4); /* Or use the auto-open variable (SAS/C, GCC): */ extern struct ExecBase *SysBase; /* Linker resolves from startup code */ ``` ```asm ; Assembly — canonical method MOVEA.L 4.W,A6 ; A6 = SysBase (short absolute addressing) ; All exec LVO calls use JSR offset(A6) ``` > **Why address $4?** The 68000 stores the initial Program Counter at address $4 in the exception vector table. During cold boot, the CPU reads this address to find the ROM entry point. After boot, Exec overwrites it with a pointer to ExecBase. This is the one absolute address every Amiga program can rely on. --- ## Structure Layout ```mermaid graph TB EB["ExecBase at $4"] --> LIB["Library Header
(struct Library)
Offsets 0–33"] EB --> SOFT["Software Config
SoftVer, ColdCapture,
WarmCapture, SysStkUpper"] EB --> TASK["Task Scheduling
TaskReady, TaskWait,
ThisTask, IDNestCnt,
TDNestCnt, Quantum"] EB --> MEM["Memory Management
MemList, MaxLocMem,
MaxExtMem"] EB --> LISTS["System Lists
LibList, DeviceList,
ResourceList, PortList,
IntrList"] EB --> HW["Hardware Detection
AttnFlags, ChipRevBits0,
PowerSupplyFrequency,
VBlankFrequency"] style EB fill:#e8f4fd,stroke:#2196f3,color:#333 style TASK fill:#e8f5e9,stroke:#4caf50,color:#333 style HW fill:#fff3e0,stroke:#ff9800,color:#333 ``` --- ## Key Field Groups ### Library Header (Offset 0–33) Every library starts with this — ExecBase is exec.library's own base: ```c struct Library LibNode; /* ln_Name = "exec.library" */ /* lib_Version = 40 (OS 3.1), 45 (OS 3.2), 47 (OS 3.2.2) */ /* lib_OpenCnt = open count */ ``` ### Boot and Configuration | Offset | Field | Type | Description | |---|---|---|---| | `$22` | `SoftVer` | `UWORD` | Kickstart software revision | | `$24` | `LowMemChkSum` | `WORD` | Checksum of vectors $0–$200 | | `$26` | `ChkBase` | `ULONG` | ExecBase self-checksum | | `$2A` | `ColdCapture` | `APTR` | Cold reboot intercept vector | | `$2E` | `CoolCapture` | `APTR` | Warm reboot intercept vector (after Diag) | | `$32` | `WarmCapture` | `APTR` | Keyboard reset intercept vector | | `$36` | `SysStkUpper` | `APTR` | Top of supervisor stack | | `$3A` | `SysStkLower` | `APTR` | Bottom of supervisor stack | | `$3E` | `MaxLocMem` | `ULONG` | Top of Chip RAM (e.g., `$200000` for 2 MB) | | `$42` | `DebugEntry` | `APTR` | Entry point for ROM debugger (Wack) | | `$46` | `DebugData` | `APTR` | Data area for ROM debugger | | `$4A` | `AlertData` | `APTR` | Last Alert data | | `$4E` | `MaxExtMem` | `APTR` | Top of Extended RAM (or NULL) | ### Task Scheduling | Offset | Field | Type | Description | |---|---|---|---| | `$126` | `IDNestCnt` | `BYTE` | Interrupt disable nesting (−1 = enabled) | | `$127` | `TDNestCnt` | `BYTE` | Task disable nesting (−1 = enabled) | | `$128` | `ThisTask` | `APTR` | Pointer to currently running Task | | `$12C` | `Quantum` | `UWORD` | Time slice for equal-priority round-robin | | `$12E` | `Elapsed` | `UWORD` | Ticks elapsed in current quantum | | `$130` | `SysFlags` | `UWORD` | Internal scheduler flags | | `$132` | `TaskReady` | `List` | Tasks ready to run (sorted by priority) | | `$146` | `TaskWait` | `List` | Tasks blocked on `Wait()` | ### Memory | Offset | Field | Type | Description | |---|---|---|---| | `$15A` | `MemList` | `List` | All memory regions (`MemHeader` chain) | ### System Lists | Offset | Field | Type | Description | |---|---|---|---| | `$17A` | `LibList` | `List` | Loaded libraries (`NT_LIBRARY`) | | `$18E` | `DeviceList` | `List` | Loaded devices (`NT_DEVICE`) | | `$1A2` | `IntrList` | `List` | Interrupt server lists | | `$1B6` | `ResourceList` | `List` | System resources | | `$1CA` | `PortList` | `List` | Public message ports | | `$1DE` | `SemaphoreList` | `List` | Public semaphores | ### Hardware Detection | Offset | Field | Type | Description | |---|---|---|---| | `$128` | `AttnFlags` | `UWORD` | CPU and FPU capability flags | | `$212` | `VBlankFrequency` | `UBYTE` | VBL rate: 50 (PAL) or 60 (NTSC) | | `$213` | `PowerSupplyFrequency` | `UBYTE` | Mains: 50 or 60 Hz | | `$21E` | `ChipRevBits0` | `UBYTE` | Chip revision detection | --- ## AttnFlags — CPU Detection | Bit | Constant | Meaning | |---|---|---| | 0 | `AFF_68010` | 68010 or better detected | | 1 | `AFF_68020` | 68020 or better | | 2 | `AFF_68030` | 68030 or better | | 3 | `AFF_68040` | 68040 or better | | 4 | `AFF_68881` | 68881 FPU detected | | 5 | `AFF_68882` | 68882 FPU detected | | 6 | `AFF_FPU40` | 68040 internal FPU | | 7 | `AFF_68060` | 68060 detected (OS 3.1+) | | 10 | `AFF_PRIVATE` | Exec private — do not use | ### Usage ```c /* Check for 68020+ */ if (SysBase->AttnFlags & AFF_68020) { /* Can use CACHE instructions, 32-bit multiply, etc. */ } /* Check for FPU */ if (SysBase->AttnFlags & (AFF_68881 | AFF_68882 | AFF_FPU40)) { /* Floating-point hardware available */ } ``` --- ## Detecting Chipset Revision ```c /* via graphics.library */ struct GfxBase *gfx = (struct GfxBase *)OpenLibrary("graphics.library", 36); if (gfx) { if (gfx->ChipRevBits0 & GFXB_AA_ALICE) /* AGA Alice */ if (gfx->ChipRevBits0 & GFXB_AA_LISA) /* AGA Lisa */ if (gfx->ChipRevBits0 & GFXB_HR_AGNUS) /* ECS Agnus */ if (gfx->ChipRevBits0 & GFXB_HR_DENISE) /* ECS Denise */ } ``` --- ## Enumerating System Lists ### Walking the Library List ```c Forbid(); struct Node *node; for (node = SysBase->LibList.lh_Head; node->ln_Succ != NULL; node = node->ln_Succ) { struct Library *lib = (struct Library *)node; Printf(" %s V%ld.%ld (open: %ld)\n", lib->lib_Node.ln_Name, lib->lib_Version, lib->lib_Revision, lib->lib_OpenCnt); } Permit(); ``` ### Walking the Task Lists ```c Forbid(); /* Currently running */ Printf("Running: %s\n", SysBase->ThisTask->tc_Node.ln_Name); /* Ready to run */ for (node = SysBase->TaskReady.lh_Head; node->ln_Succ; node = node->ln_Succ) { Printf(" Ready: %s (pri %ld)\n", node->ln_Name, node->ln_Pri); } /* Waiting */ for (node = SysBase->TaskWait.lh_Head; node->ln_Succ; node = node->ln_Succ) { Printf(" Wait: %s (pri %ld)\n", node->ln_Name, node->ln_Pri); } Permit(); ``` --- ## ExecBase Safety Rules | Rule | Reason | |---|---| | **Never write to ExecBase fields** | Corrupts kernel state for all tasks | | **Use `Forbid()` when walking lists** | Lists change as libraries open/close, tasks start/stop | | **Don't cache pointers from lists** | Nodes may be removed between accesses | | **Always use LVO functions** | Direct field manipulation bypasses safety checks | | **Verify `SysBase` after warm reboot** | `ColdCapture`/`CoolCapture` may have altered it | --- ## ExecBase in IDA Pro / Ghidra After loading Kickstart ROM: 1. **Create a segment** at `$000000–$000400` (exception vectors) 2. **Mark `$4` as a pointer** — follow it to the ExecBase in ROM 3. **Apply `struct ExecBase` type** from NDK39 headers - IDA: `File → Parse C header` with `exec/execbase.h` - Ghidra: Import C headers via Data Type Manager 4. **All `N(A6)` offsets** in exec code auto-annotate as field names 5. **Label the system lists** — `LibList`, `DeviceList`, `PortList` are entry points for understanding boot order --- ## References - NDK39: `exec/execbase.h` — authoritative field definitions - ADCD 2.1: exec.library autodoc - See also: [Library System](library_system.md) — how libraries relate to ExecBase - See also: [Multitasking](multitasking.md) — TaskReady/TaskWait and scheduler internals - See also: [Kickstart Boot Diagnostics](../02_boot_sequence/kickstart-boot-diagnostics.md) — ColdStart ExecBase allocation, chip RAM sizing - *Amiga ROM Kernel Reference Manual: Exec* — ExecBase chapter