amiga-bootcamp/06_exec_os/lists_nodes.md

159 lines
4 KiB
Markdown
Raw Normal View History

[← Home](../README.md) · [Exec Kernel](README.md)
# Lists and Nodes — MinList, List, Node, MinNode
## Overview
AmigaOS uses **intrusive doubly-linked lists** throughout exec: the task list, library list, device list, memory list, port list, and more all use the same `List`/`Node` structures defined in `exec/lists.h`.
---
## Structures
```c
/* exec/nodes.h — NDK39 */
struct Node {
struct Node *ln_Succ; /* pointer to next node (NULL at tail sentinel) */
struct Node *ln_Pred; /* pointer to prev node (NULL at head sentinel) */
UBYTE ln_Type; /* node type — NT_TASK, NT_LIBRARY, NT_MEMORY... */
BYTE ln_Pri; /* scheduling priority (used by Enqueue) */
char *ln_Name; /* optional name string (NULL = anonymous) */
};
struct MinNode {
struct MinNode *mln_Succ;
struct MinNode *mln_Pred;
/* no type, priority, or name — minimal overhead */
};
```
```c
/* exec/lists.h — NDK39 */
struct List {
struct Node *lh_Head; /* first node (or tail sentinel if empty) */
struct Node *lh_Tail; /* always NULL — marks end of list */
struct Node *lh_TailPred; /* last node (or head sentinel if empty) */
UBYTE lh_Type; /* list type */
UBYTE lh_pad;
};
struct MinList {
struct MinNode *mlh_Head;
struct MinNode *mlh_Tail; /* always NULL */
struct MinNode *mlh_TailPred;
};
```
### Node Type Constants
```c
/* exec/nodes.h */
#define NT_UNKNOWN 0
#define NT_TASK 1 /* exec Task */
#define NT_INTERRUPT 2 /* Interrupt server */
#define NT_DEVICE 3 /* Device */
#define NT_MSGPORT 4 /* MsgPort */
#define NT_MESSAGE 5 /* Message */
#define NT_FREEMSG 6
#define NT_REPLYMSG 7
#define NT_RESOURCE 8
#define NT_LIBRARY 9 /* Library */
#define NT_MEMORY 10 /* MemHeader */
#define NT_SOFTINT 11
#define NT_FONT 12
#define NT_PROCESS 13 /* dos.library Process */
#define NT_SEMAPHORE 14
#define NT_SIGNALSEM 15 /* SignalSemaphore */
#define NT_BOOTNODE 16
#define NT_KICKMEM 17
#define NT_GRAPHICS 18
#define NT_DEATHMESSAGE 19
```
---
## Initialising a List
```c
/* Stack-allocated list: */
struct List myList;
NewList(&myList); /* sets up sentinel pointers — mandatory */
/* Or use NEWLIST() macro: */
NEWLIST(&myList);
```
---
## Adding and Removing Nodes
```c
/* Add at head (highest LRU position): */
AddHead(&myList, &myNode); /* LVO -240 */
/* Add at tail: */
AddTail(&myList, &myNode); /* LVO -246 */
/* Remove from wherever it is (no list pointer needed): */
Remove(&myNode); /* LVO -252 */
/* Priority-ordered insert (by ln_Pri, high first): */
Enqueue(&myList, &myNode); /* LVO -270 */
```
---
## Walking a List
```c
struct Node *node, *next;
for (node = myList.lh_Head; node->ln_Succ != NULL; node = node->ln_Succ) {
/* process node */
}
```
Safe removal while iterating (save next before removing):
```c
for (node = myList.lh_Head; (next = node->ln_Succ) != NULL; node = next) {
if (should_remove(node)) Remove(node);
}
```
---
## Finding a Node by Name
```c
struct Node *found = FindName(&SysBase->LibList, "dos.library");
/* Returns NULL if not found */
/* Always call under Forbid() if the list may change */
```
---
## How the Sentinel Works
The AmigaOS list design uses a **3-pointer layout** that avoids special-casing empty lists and end-of-list checks:
```
lh_Head ──→ [ Node A ]──→ [ Node B ]──→ [ tail sentinel ]
lh_Tail = NULL (always)
lh_TailPred ──────────────────────────→ [ Node B ]
Empty list:
lh_Head ──→ [ tail sentinel ]
lh_TailPred ──→ [ head sentinel ]
```
Walking stops when `ln_Succ == NULL` — that is the tail sentinel's `lh_Tail` field.
---
## References
- NDK39: `exec/nodes.h`, `exec/lists.h`
- ADCD 2.1: `AddHead`, `AddTail`, `Remove`, `Enqueue`, `FindName`, `NewList`
- *Amiga ROM Kernel Reference Manual: Exec* — lists chapter