mirror of
https://github.com/alfishe/amiga-bootcamp.git
synced 2026-06-13 00:26:28 +00:00
Graphics: text_fonts (bitmap layout, styles), sprites (DMA, multiplexing), gfx_base (chipset detection), rastport (draw modes, clipping), ham_ehb (mermaid fixes), display_modes (HAM palettes) Devices: scsi (per-model interfaces, Gayle limits, CD-ROM, native vs vendor drivers), console (ANSI sequences, CON:/RAW:), parallel (CIA registers, pinout), timer (resource exhaustion), gameport (quadrature, XOR state) Libraries: workbench (WBStartup, AppWindow/Icon/MenuItem), rexxsyslib (ARexx port hosting, command parsing), diskfont (font directory, colour fonts), keymap (rawkey codes, dead keys), locale (catalogue system, date formatting), layers (ClipRect, refresh types), utility (TagItem chains), icon (DiskObject, ToolTypes), iffparse (IFF structure, ByteRun1), expansion (Zorro AutoConfig) Networking: tcp_ip_stacks (major rewrite - Amiga vs Unix architecture, SANA-II pipeline, PPP/SLIP dial-up, Ethernet cards, MiSTer), bsdsocket (pure API ref), sana2 (buffer hooks, driver requirements), protocols (all code examples). Deduplicated overlap between the three files. Toolchain: debugging (Enforcer patterns, SnoopDOS, GDB remote, kprintf checklist), sasc (pragma encoding, __saveds idioms), stormc (NEW - StormC IDE, C++, PowerPC) References: error_codes (DOS, Exec, trackdisk, Intuition error tables) Driver development: rtg_driver (Native driver analysis, P96 tuning) All 22 README indexes updated. Root README synced with stormc.md entry.
203 lines
5.8 KiB
Markdown
203 lines
5.8 KiB
Markdown
[← Home](../README.md) · [Libraries](README.md)
|
||
|
||
# utility.library — TagItems, Hooks, Date Utilities
|
||
|
||
## Overview
|
||
|
||
`utility.library` (OS 2.0+) provides the universal tag-based parameter passing system, callback hooks, and date/time utilities used throughout AmigaOS. Nearly every OS 2.0+ API uses TagItem lists for extensible parameter passing — understanding this library is essential.
|
||
|
||
---
|
||
|
||
## TagItem System
|
||
|
||
### Structure
|
||
|
||
```c
|
||
/* utility/tagitem.h — NDK39 */
|
||
struct TagItem {
|
||
ULONG ti_Tag; /* tag identifier */
|
||
ULONG ti_Data; /* tag value (ULONG — often cast from pointer) */
|
||
};
|
||
```
|
||
|
||
### Special Tags
|
||
|
||
| Tag | Value | Purpose |
|
||
|---|---|---|
|
||
| `TAG_DONE` | 0 | Terminates a tag list — must be the last entry |
|
||
| `TAG_IGNORE` | 1 | Skip this tag (placeholder) |
|
||
| `TAG_MORE` | 2 | `ti_Data` = pointer to another TagItem array (chain lists) |
|
||
| `TAG_SKIP` | 3 | Skip next `ti_Data` tags in the list |
|
||
| `TAG_USER` | `1<<31` | User-defined tags start at this value |
|
||
|
||
### How Tag Lists Work
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph "Tag List (array)"
|
||
T1["SA_Width<br/>640"] --> T2["SA_Height<br/>480"]
|
||
T2 --> T3["SA_Depth<br/>8"]
|
||
T3 --> TM["TAG_MORE<br/>→ extraTags"]
|
||
end
|
||
|
||
subgraph "Chained List"
|
||
E1["SA_Title<br/>'My Screen'"] --> E2["TAG_DONE<br/>0"]
|
||
end
|
||
|
||
TM --> E1
|
||
|
||
style TM fill:#fff9c4,stroke:#f9a825,color:#333
|
||
style E2 fill:#ffcdd2,stroke:#c62828,color:#333
|
||
```
|
||
|
||
```c
|
||
/* Typical usage: */
|
||
struct Screen *scr = OpenScreenTags(NULL,
|
||
SA_Width, 640,
|
||
SA_Height, 480,
|
||
SA_Depth, 8,
|
||
SA_Title, (ULONG)"My Screen",
|
||
SA_ShowTitle, TRUE,
|
||
TAG_DONE);
|
||
|
||
/* Tag list as array: */
|
||
struct TagItem tags[] = {
|
||
{ SA_Width, 640 },
|
||
{ SA_Height, 480 },
|
||
{ SA_Depth, 8 },
|
||
{ TAG_DONE, 0 }
|
||
};
|
||
struct Screen *scr = OpenScreenTagList(NULL, tags);
|
||
```
|
||
|
||
### Tag Utility Functions
|
||
|
||
| Function | Description |
|
||
|---|---|
|
||
| `FindTagItem(tag, tagList)` | Find first matching tag; returns `TagItem *` or NULL |
|
||
| `GetTagData(tag, default, tagList)` | Get tag value, or default if tag not found |
|
||
| `NextTagItem(&tagListPtr)` | Iterator — handles TAG_MORE, TAG_SKIP, TAG_IGNORE transparently |
|
||
| `TagInArray(tag, array)` | Check if a tag ID is in a ULONG array |
|
||
| `FilterTagItems(tagList, filter, logic)` | Remove/keep tags matching a filter array |
|
||
| `CloneTagItems(tagList)` | Allocate a copy of the entire tag list |
|
||
| `FreeTagItems(tagList)` | Free a cloned tag list |
|
||
| `MapTags(tagList, mapList, flags)` | Remap tag IDs (for converting between APIs) |
|
||
| `PackBoolTags(initialFlags, tagList, boolMap)` | Convert boolean tags to a flags word |
|
||
|
||
### Iterating a Tag List
|
||
|
||
```c
|
||
/* The correct way to iterate — handles all special tags: */
|
||
struct TagItem *tag;
|
||
struct TagItem *tstate = tagList;
|
||
|
||
while ((tag = NextTagItem(&tstate)))
|
||
{
|
||
switch (tag->ti_Tag)
|
||
{
|
||
case MY_WIDTH: width = tag->ti_Data; break;
|
||
case MY_HEIGHT: height = tag->ti_Data; break;
|
||
case MY_TITLE: title = (char *)tag->ti_Data; break;
|
||
}
|
||
}
|
||
```
|
||
|
||
> [!IMPORTANT]
|
||
> **Never iterate tag lists manually** with `for` loops. Always use `NextTagItem()` — it correctly handles `TAG_MORE` chains, `TAG_SKIP`, and `TAG_IGNORE`. Manual iteration will break on chained or filtered lists.
|
||
|
||
---
|
||
|
||
## Hook System
|
||
|
||
Hooks provide a standardised callback mechanism used throughout AmigaOS — Intuition, BOOPSI, layers.library, and locale.library all use them.
|
||
|
||
### Structure
|
||
|
||
```c
|
||
/* utility/hooks.h */
|
||
struct Hook {
|
||
struct MinNode h_MinNode; /* for linking into lists */
|
||
ULONG (*h_Entry)(void); /* assembler entry point */
|
||
ULONG (*h_SubEntry)(void); /* C function pointer */
|
||
APTR h_Data; /* user data */
|
||
};
|
||
```
|
||
|
||
### Register Convention
|
||
|
||
When a hook is called, registers are set up as:
|
||
- **A0** = pointer to the `Hook` itself
|
||
- **A2** = the "object" (context-dependent)
|
||
- **A1** = the "message" (context-dependent)
|
||
|
||
```c
|
||
/* SAS/C / GCC with register args: */
|
||
ULONG __saveds __asm MyHookFunc(
|
||
register __a0 struct Hook *hook,
|
||
register __a2 APTR object,
|
||
register __a1 APTR message)
|
||
{
|
||
struct MyData *data = (struct MyData *)hook->h_Data;
|
||
/* ... process callback ... */
|
||
return 0;
|
||
}
|
||
|
||
/* Initialise the hook: */
|
||
struct Hook myHook;
|
||
myHook.h_Entry = (HOOKFUNC)HookEntry; /* utility.library glue */
|
||
myHook.h_SubEntry = (HOOKFUNC)MyHookFunc;
|
||
myHook.h_Data = myPrivateData;
|
||
```
|
||
|
||
### Common Hook Uses
|
||
|
||
| API | Object (A2) | Message (A1) |
|
||
|---|---|---|
|
||
| BOOPSI `OM_SET` | BOOPSI object | `opSet` message |
|
||
| `InstallLayerHook` | Layer | `struct BackFillMessage` |
|
||
| Locale `FormatString` | — | Format data |
|
||
| `DoMethod` (MUI) | MUI object | Method message |
|
||
|
||
---
|
||
|
||
## Date Utilities
|
||
|
||
```c
|
||
#include <utility/date.h>
|
||
|
||
struct ClockData {
|
||
UWORD sec; /* 0–59 */
|
||
UWORD min; /* 0–59 */
|
||
UWORD hour; /* 0–23 */
|
||
UWORD mday; /* 1–31 */
|
||
UWORD month; /* 1–12 */
|
||
UWORD year; /* 1978+ */
|
||
UWORD wday; /* 0=Sunday */
|
||
};
|
||
|
||
/* Convert Amiga timestamp to date components: */
|
||
struct ClockData cd;
|
||
Amiga2Date(seconds, &cd);
|
||
Printf("%02d/%02d/%04d %02d:%02d:%02d\n",
|
||
cd.mday, cd.month, cd.year,
|
||
cd.hour, cd.min, cd.sec);
|
||
|
||
/* Convert date to Amiga timestamp: */
|
||
cd.year = 2024; cd.month = 3; cd.mday = 15;
|
||
cd.hour = 14; cd.min = 30; cd.sec = 0;
|
||
ULONG secs = Date2Amiga(&cd);
|
||
|
||
/* Validate a date and get timestamp: */
|
||
ULONG secs = CheckDate(&cd); /* returns 0 if invalid */
|
||
```
|
||
|
||
> [!NOTE]
|
||
> The Amiga epoch is **January 1, 1978 00:00:00 UTC**. To convert to/from Unix timestamps (epoch 1970-01-01), add/subtract **252,460,800** seconds (8 years + 2 leap days).
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- NDK39: `utility/tagitem.h`, `utility/hooks.h`, `utility/date.h`
|
||
- ADCD 2.1: utility.library autodocs
|
||
- See also: [boopsi.md](../09_intuition/boopsi.md) — BOOPSI uses hooks extensively
|