mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
exec_os: enrich all stubs to bootcamp-quality reference articles
Complete rewrite of 14 exec_os articles from stubs to comprehensive deep-dive technical references with architecture diagrams, pitfalls, and best practices. New: multitasking.md (scheduler, IPC, memory safety, real-world scenarios) Enriched: exec_base, tasks_processes, library_system, library_vectors, interrupts, exceptions_traps, memory_management, message_ports, signals, semaphores, io_requests, lists_nodes, resident_modules Updated indexes: 06_exec_os/README.md, root README.md
This commit is contained in:
parent
4d136b0672
commit
59929047d4
16 changed files with 4463 additions and 678 deletions
|
|
@ -4,7 +4,44 @@
|
|||
|
||||
## Overview
|
||||
|
||||
AmigaOS ROM and disk-resident modules (libraries, devices, resources) identify themselves via a **RomTag** structure. At boot, exec scans the ROM and loaded segments for RomTags and initialises every module it finds.
|
||||
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 initialises 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
|
||||
|
||||
```mermaid
|
||||
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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -26,82 +63,239 @@ struct Resident {
|
|||
};
|
||||
```
|
||||
|
||||
### Magic Word
|
||||
### Field Reference
|
||||
|
||||
`rt_MatchWord = $4AFC` is the 68k opcode for `ILLEGAL` — a deliberate trap instruction chosen so that an accidental execution of a RomTag causes an immediate CPU exception rather than silent corruption.
|
||||
| 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
|
||||
---
|
||||
|
||||
## RTF_ Flags
|
||||
|
||||
```c
|
||||
#define RTF_AUTOINIT (1<<7) /* use rt_Init as pointer to InitTable */
|
||||
#define RTF_SINGLETASK (1<<1) /* init runs in single-task context */
|
||||
#define RTF_COLDSTART (1<<0) /* init on cold boot only */
|
||||
#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 Initialisation
|
||||
|
||||
When `RTF_AUTOINIT` is set, `rt_Init` points to an **InitTable** rather than a bare function:
|
||||
When `RTF_AUTOINIT` is set, `rt_Init` points to an `InitTable`:
|
||||
|
||||
```c
|
||||
struct InitTable {
|
||||
ULONG it_DataSize; /* size of library instance struct */
|
||||
APTR *it_FuncTable; /* pointer to function pointer table */
|
||||
APTR it_DataTable; /* pointer to INITBYTE/INITWORD/INITLONG table */
|
||||
APTR it_InitRoutine;/* pointer to actual LibInit() function */
|
||||
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 */
|
||||
};
|
||||
```
|
||||
|
||||
exec uses `MakeLibrary()` to allocate the library, install the JMP table, and initialise the data, then calls `it_InitRoutine`. For most libraries, the author only needs to provide `it_FuncTable` and `it_DataTable` and `RTF_AUTOINIT` handles the rest automatically.
|
||||
### 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
|
||||
|
||||
```c
|
||||
/* 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 initialisation:
|
||||
|
||||
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
|
||||
|
||||
```c
|
||||
struct Resident *res = FindResident("dos.library"); /* LVO -60 */
|
||||
if (res) {
|
||||
printf("Found: %s v%d\n", res->rt_Name, res->rt_Version);
|
||||
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 `SysBase->ResModules` — the list of all RomTag pointers collected at boot.
|
||||
`FindResident` scans the `SysBase→ResModules` array — the list of all RomTag pointers collected at boot.
|
||||
|
||||
---
|
||||
|
||||
## ROM Scan at Boot
|
||||
## Disk-Resident Modules
|
||||
|
||||
During exec initialisation, the ROM scanner walks from `$F80000` (Kickstart base) upward looking for the `$4AFC` magic word. For each match it verifies `rt_MatchTag == &rt` (self-referential pointer), confirms `rt_EndSkip` is beyond the RomTag, and adds valid entries to `ResModules`.
|
||||
Libraries not in ROM are loaded from `LIBS:` when first opened:
|
||||
|
||||
The same scan is applied to any loaded segment when `AddResidentModule` is called.
|
||||
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)
|
||||
## Writing a Minimal RomTag
|
||||
|
||||
### Assembly
|
||||
|
||||
```asm
|
||||
; Minimal ROM tag for a library:
|
||||
dc.w $4AFC ; rt_MatchWord
|
||||
dc.l _RomTag ; rt_MatchTag (self-ref)
|
||||
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 when AUTOINIT)
|
||||
_Name: dc.b "mylib.library", 0
|
||||
_IdString: dc.b "mylib.library 1.0 (23.4.2026)", 13, 10, 0
|
||||
; 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)
|
||||
|
||||
```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
|
||||
|
||||
```asm
|
||||
; 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
|
||||
|
||||
```asm
|
||||
; 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
|
||||
|
||||
```c
|
||||
/* 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/execbase.h`
|
||||
- ADCD 2.1: `FindResident`, `InitResident`, `AddResidentModule`
|
||||
- NDK39: `exec/resident.h`, `exec/initializers.h`, `exec/execbase.h`
|
||||
- ADCD 2.1: `FindResident`, `InitResident`, `MakeLibrary`, `InitStruct`
|
||||
- See also: [Library System](library_system.md) — how OpenLibrary uses RomTags
|
||||
- See also: [Library Vectors](library_vectors.md) — JMP table construction
|
||||
- *Amiga ROM Kernel Reference Manual: Exec* — resident modules chapter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue