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

5.8 KiB
Raw Blame History

← Home · Libraries

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

/* 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

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
/* 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

/* 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

/* 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)
/* 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

#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 — BOOPSI uses hooks extensively