amiga-bootcamp/11_libraries/utility.md
2026-04-26 14:46:18 -04:00

203 lines
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[← 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;
}
/* Initialize 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; /* 059 */
UWORD min; /* 059 */
UWORD hour; /* 023 */
UWORD mday; /* 131 */
UWORD month; /* 112 */
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