amiga-bootcamp/06_exec_os/resident_modules.md
2026-04-26 14:46:18 -04:00

9.6 KiB
Raw Permalink Blame History

← Home · Exec Kernel

Resident Modules — RomTag, RTF_AUTOINIT, FindResident

Overview

AmigaOS ROM and disk-resident modules (libraries, devices, resources) identify themselves via a RomTag structure. At boot, exec scans the Kickstart ROM and any loaded segments for RomTags and initializes every module it finds. The RomTag system is how the kernel discovers and bootstraps itself — exec.library, graphics.library, dos.library, and all ROM-resident code use this mechanism.


Architecture

graph TB
    subgraph "Boot Sequence"
        COLD["Cold Start<br/>CPU reads $4"]
        SCAN["ROM Scanner<br/>Search for $4AFC magic"]
        SORT["Sort by rt_Pri<br/>(higher = init first)"]
        INIT["InitResident()<br/>for each module"]
    end

    subgraph "RomTag"
        RT["struct Resident<br/>rt_MatchWord = $4AFC"]
        AUTO["RTF_AUTOINIT?"]
    end

    subgraph "Initialization"
        FUNC["Direct: rt_Init<br/>is a function pointer"]
        TABLE["AutoInit: rt_Init<br/>is an InitTable pointer"]
        MAKE["MakeLibrary()<br/>alloc + JMP table + init"]
    end

    COLD --> SCAN
    SCAN --> SORT
    SORT --> INIT
    INIT --> RT
    RT --> AUTO
    AUTO -->|No| FUNC
    AUTO -->|Yes| TABLE
    TABLE --> MAKE

    style SCAN fill:#e8f4fd,stroke:#2196f3,color:#333
    style RT fill:#fff3e0,stroke:#ff9800,color:#333

struct Resident (RomTag)

/* exec/resident.h — NDK39 */
struct Resident {
    UWORD  rt_MatchWord;    /* always $4AFC — magic identifier */
    struct Resident *rt_MatchTag; /* pointer back to this struct (self-ref) */
    APTR   rt_EndSkip;      /* pointer past end of this module's code */
    UBYTE  rt_Flags;        /* RTF_* flags */
    UBYTE  rt_Version;      /* module version number */
    UBYTE  rt_Type;         /* NT_LIBRARY, NT_DEVICE, NT_RESOURCE, ... */
    BYTE   rt_Pri;          /* initialization priority (higher = earlier) */
    char  *rt_Name;         /* module name string, e.g. "dos.library" */
    char  *rt_IdString;     /* human-readable ID, e.g. "dos.library 40.1" */
    APTR   rt_Init;         /* init function or InitTable pointer */
};

Field Reference

Field Description
rt_MatchWord $4AFC — the 68k ILLEGAL opcode. If CPU accidentally executes a RomTag, it traps immediately instead of running garbage
rt_MatchTag Self-referential pointer — must point back to this struct. Used to verify the match isn't a false positive
rt_EndSkip Address past the end of this module's code/data. Scanner skips to here after finding the tag
rt_Flags RTF_AUTOINIT, RTF_SINGLETASK, RTF_COLDSTART, RTF_AFTERDOS
rt_Version Module version number — used by FindResident version checks
rt_Type Node type: NT_LIBRARY, NT_DEVICE, NT_RESOURCE
rt_Pri Initialization priority. Higher = initialized earlier in boot. exec.library has the highest
rt_Name Module name — matches the OpenLibrary/OpenDevice name
rt_IdString Human-readable string with version, date. Convention: "name version.revision (dd.mm.yy)\r\n"
rt_Init If RTF_AUTOINIT: pointer to InitTable. Otherwise: pointer to init function

RTF_ Flags

#define RTF_AUTOINIT   (1<<7)   /* rt_Init → InitTable (auto library creation) */
#define RTF_AFTERDOS   (1<<2)   /* init after DOS is available (OS 2.0+) */
#define RTF_SINGLETASK (1<<1)   /* init in single-task mode (before multitasking) */
#define RTF_COLDSTART  (1<<0)   /* init on cold boot only (not warm reset) */
Flag Timing Use Case
RTF_SINGLETASK Before multitasking starts exec.library, expansion.library
RTF_COLDSTART During cold boot, multitasking active graphics.library, intuition.library
RTF_AFTERDOS After dos.library is initialized diskfont.library, workbench.library

RTF_AUTOINIT — Automatic Initialization

When RTF_AUTOINIT is set, rt_Init points to an InitTable:

struct InitTable {
    ULONG  it_DataSize;     /* AllocMem size for library base struct */
    APTR  *it_FuncTable;    /* function pointer array (for MakeFunctions) */
    APTR   it_DataTable;    /* InitStruct data (INITBYTE/INITWORD/INITLONG) */
    APTR   it_InitRoutine;  /* called after base is allocated and populated */
};

What AUTOINIT Does

  1. AllocMem(it_DataSize, MEMF_PUBLIC | MEMF_CLEAR) — allocate library base
  2. MakeFunctions(base, it_FuncTable, NULL) — build JMP table
  3. InitStruct(it_DataTable, base, 0) — fill in default field values
  4. it_InitRoutine(base) — library-specific initialization
  5. AddLibrary(base) — add to SysBase→LibList

This automates the boilerplate that every library needs.

Data Initialization Macros

/* exec/initializers.h */
#define INITBYTE(offset, value)  0xE000|(offset), (value)
#define INITWORD(offset, value)  0xD000|(offset), (value)
#define INITLONG(offset, value)  0xC000|(offset), (value)

/* Example data table: */
UWORD dataTable[] = {
    INITBYTE(OFFSET(Library, lib_Node.ln_Type), NT_LIBRARY),
    INITLONG(OFFSET(Library, lib_Node.ln_Name), (ULONG)"mylib.library"),
    INITBYTE(OFFSET(Library, lib_Flags), LIBF_SUMUSED | LIBF_CHANGED),
    INITWORD(OFFSET(Library, lib_Version), 1),
    INITWORD(OFFSET(Library, lib_Revision), 0),
    0  /* terminator */
};

ROM Scan at Boot

During exec initialization:

  1. Walk from Kickstart base ($F80000 for 512K ROM, $FC0000 for 256K) upward
  2. Search for the $4AFC magic word at even addresses
  3. For each match, verify:
    • rt_MatchTag points back to itself (self-referential)
    • rt_EndSkip is past the RomTag structure
  4. Add valid entries to SysBase→ResModules (a NULL-terminated array of Resident pointers)
  5. Sort by rt_Pri (highest first)
  6. Call InitResident() for each, in priority order

Boot Priority Order

Priority Module Why First
126 exec.library Kernel — must be first
120 expansion.library Autoconfig hardware detection
105 graphics.library Custom chip initialization
100 layers.library Display layer management
70 intuition.library GUI system
50 cia.resource CIA chip management
0 dos.library File system
50 ram-handler RAM disk
120 workbench.library Desktop environment

Finding a Resident by Name

struct Resident *res = FindResident("dos.library");   /* LVO -96 */
if (res)
{
    Printf("Found: %s V%ld (pri %ld)\n",
        res->rt_Name, res->rt_Version, res->rt_Pri);
}

FindResident scans the SysBase→ResModules array — the list of all RomTag pointers collected at boot.


Disk-Resident Modules

Libraries not in ROM are loaded from LIBS: when first opened:

  1. OpenLibrary("mylib.library", 0) — not found in LibList or ResModules
  2. Exec searches LIBS: assign path
  3. LoadSeg("LIBS:mylib.library") — loads the HUNK executable
  4. Scan loaded segments for RomTag ($4AFC)
  5. InitResident() — creates the library
  6. Add to LibList — subsequent opens find it in memory

Writing a Minimal RomTag

Assembly

; Minimal ROM tag for a library
        CNOP    0,4              ; long-word align
_RomTag:
        dc.w    $4AFC            ; rt_MatchWord (ILLEGAL opcode)
        dc.l    _RomTag          ; rt_MatchTag (self-reference)
        dc.l    _EndTag          ; rt_EndSkip
        dc.b    RTF_AUTOINIT     ; rt_Flags
        dc.b    1                ; rt_Version
        dc.b    NT_LIBRARY       ; rt_Type
        dc.b    0                ; rt_Pri
        dc.l    _Name            ; rt_Name
        dc.l    _IdString        ; rt_IdString
        dc.l    _InitTable       ; rt_Init → InitTable

_Name:
        dc.b    "mylib.library",0
_IdString:
        dc.b    "mylib.library 1.0 (23.4.2026)",13,10,0
        even

_InitTable:
        dc.l    MYLIB_SIZE       ; it_DataSize
        dc.l    _FuncTable       ; it_FuncTable
        dc.l    _DataTable       ; it_DataTable
        dc.l    _InitRoutine     ; it_InitRoutine

_FuncTable:
        dc.l    _LibOpen
        dc.l    _LibClose
        dc.l    _LibExpunge
        dc.l    _LibNull         ; Reserved
        dc.l    _MyFunction1
        dc.l    _MyFunction2
        dc.l    -1               ; terminator

_EndTag:

C (with SAS/C)

struct Resident myRomTag = {
    RTC_MATCHWORD,
    &myRomTag,        /* self-reference */
    &endTag,
    RTF_AUTOINIT,
    1,                /* version */
    NT_LIBRARY,
    0,                /* priority */
    "mylib.library",
    "mylib.library 1.0 (23.4.2026)\r\n",
    &initTable
};

Pitfalls

1. Missing Self-Reference

; BUG — rt_MatchTag doesn't point back to the RomTag
_RomTag:
    dc.w    $4AFC
    dc.l    0           ; WRONG — must be _RomTag

Scanner won't recognize this as a valid RomTag.

2. Incorrect EndSkip

; BUG — EndSkip points inside the module code
    dc.l    _RomTag+20  ; WRONG — scanner may skip data it shouldn't

rt_EndSkip must point past ALL code and data belonging to this module.

3. Wrong Priority

/* BUG — initializing before a dependency is ready */
.rt_Pri = 100,  /* Higher than graphics.library */
/* If your library opens graphics.library in init, it's not loaded yet */

References

  • NDK39: exec/resident.h, exec/initializers.h, exec/execbase.h
  • ADCD 2.1: FindResident, InitResident, MakeLibrary, InitStruct
  • See also: Library System — how OpenLibrary uses RomTags
  • See also: Library Vectors — JMP table construction
  • Amiga ROM Kernel Reference Manual: Exec — resident modules chapter