AmigaOS Code Patterns and Idioms

This article distills the recurring patterns found across the ~150 example source files on the Developer CD. These are the structural templates an agent should follow when writing AmigaOS C code.

Standard program structure

Every AmigaOS C program follows this skeleton:

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <intuition/intuition.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/alib_protos.h>      /* amiga.lib */

#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/intuition_pragmas.h>

extern struct ExecBase *SysBase;
struct Library *DOSBase;
struct Library *IntuitionBase;
struct Library *GfxBase;

int main(int argc, char **argv)
{
    /* 1. Open libraries with version checks */
    if (!(IntuitionBase = OpenLibrary("intuition.library", 37)))
        return RETURN_FAIL;
    if (!(GfxBase = OpenLibrary("graphics.library", 37)))
        { CloseLibrary(IntuitionBase); return RETURN_FAIL; }

    /* 2. Open screen/window */
    struct Screen *scr = OpenScreenTags(NULL, SA_Depth, 4, TAG_DONE);
    struct Window *win = OpenWindowTags(NULL,
        WA_CustomScreen, (ULONG)scr,
        WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_GADGETUP,
        WA_Flags,  WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET,
        TAG_DONE);

    /* 3. Event loop */
    BOOL running = TRUE;
    while (running)
    {
        struct IntuiMessage *msg;
        while ((msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
        {
            switch (msg->Class)
            {
                case IDCMP_CLOSEWINDOW: running = FALSE; break;
                case IDCMP_VANILLAKEY:
                    if (msg->Code == 0x1b) running = FALSE; /* ESC */
                    break;
            }
            ReplyMsg((struct Message *)msg);
        }
        Wait(1 << win->UserPort->mp_SigBit);
    }

    /* 4. Cleanup (reverse order) */
    CloseWindow(win);
    CloseScreen(scr);
    CloseLibrary(GfxBase);
    CloseLibrary(IntuitionBase);
    return RETURN_OK;
}

Rules: Always check OpenLibrary() return. Always ReplyMsg() every GetMsg(). Always close in reverse order. Always close libraries.

Include file conventions

  • <exec/types.h> — basic types (ULONG, APTR, BOOL, etc.)
  • <clib/*_protos.h> — function prototypes for each library
  • <pragmas/*_pragmas.h> — inline-call pragmas (SAS/C optimization)
  • <alib_protos.h> — amiga.lib utility prototypes (HookEntry, BeginIO, etc.)

Command-line argument parsing with ReadArgs

#define TEMPLATE "FILE/A,PORT/K/N,QUIET/S"
LONG opts[3];
struct RDArgs *rdargs = ReadArgs(TEMPLATE, opts, NULL);
if (rdargs)
{
    STRPTR file = (STRPTR)opts[0];   /* /A required string */
    LONG  *port = (LONG *)opts[1];   /* /K/N optional number */
    BOOL  quiet = opts[2];           /* /S switch */
    /* ... use args ... */
    FreeArgs(rdargs);
}

Async I/O double-buffering pattern

The AsynchIO/asyncio.c example implements high-performance async file reading by double-buffering DOS packets:

  1. SendPacket() fills buffer A while the application processes buffer B.
  2. WaitPacket() waits for the packet reply and brings up retry requesters via ErrorReport() on errors.
  3. The two buffers alternate — while one is being filled by the filesystem, the other is being consumed by the application.

Key detail: uses Remove() instead of GetMsg() to retrieve the packet, because only one packet can be in the port at a time. If multiple packets were possible, GetMsg() would be required for correct arbitration.

DataType object creation (sound, picture, text)

Object *o = NewDTObject(name,
    DTA_SourceType,  DTST_FILE,
    DTA_GroupID,     GID_SOUND,
    SDTA_Volume,     64,
    SDTA_SignalTask, (ULONG)FindTask(NULL),
    SDTA_SignalBit,  (ULONG)SIGBREAKB_CTRL_F,
    TAG_DONE);

struct dtTrigger dtt = { DTM_TRIGGER, NULL, STM_PLAY, NULL };
DoMethodA(o, (Msg)&dtt);
Wait(SIGBREAKB_CTRL_F);     /* async completion */
DisposeDTObject(o);

Pattern: NewDTObject loads any supported format → DoMethodA with DTM_TRIGGER + STM_PLAY plays it → Wait() for the async signal → DisposeDTObject() cleans up. The SDTA_SignalTask/SDTA_SignalBit pair provides async completion notification.

GadTools listview with custom callback rendering

struct Hook drawHook;
drawHook.h_Entry = HookEntry;
drawHook.h_SubEntry = drawFunc;   /* your custom render function */

gad = CreateGadgetA(LISTVIEW_KIND, gad, &ng,
    GTLV_Labels,    NULL,
    GTLV_CallBack,  (ULONG)&drawHook,
    GTLV_MaxPen,    drawInfo->dri_NumPens,
    TAG_DONE);

The draw hook receives the rastport, the entry data, bounds rectangle, and selection state. This enables rendering images, icons, or multi-line text in listview rows instead of plain strings.

BOOPSI custom class as a shared library

The boopsi/myclassinit.c example shows the full lifecycle:

  1. myLibInit() — set SysBase, store seglist.
  2. myLibOpen() — first open: openAll() (open IntuitionBase, GfxBase, UtilityBase) + MakeClass() to create the class. AddClass() to make it public.
  3. myLibClose() — last close: if marked for expunge, RemoveClass() + FreeClass().
  4. myLibExpunge()FreeClass() + CloseLibrary() + FreeMem().
  5. Dispatcher — switch on method ID (OM_NEW, OM_SET, OM_GET, OM_DISPOSE, custom methods). Always call DoSuperMethodA() first for inherited behavior.

IFF file loading pattern

struct IFFHandle *iff = AllocIFF();
iff->iff_Stream = Open(filename, MODE_OLDFILE);
InitIFFasDOS(iff);
OpenIFF(iff, IFFF_READ);
PropChunk(iff, ID_ILBM, ID_BMHD);    /* collect header */
PropChunk(iff, ID_ILBM, ID_CMAP);    /* collect colormap */
StopChunk(iff, ID_ILBM, ID_BODY);    /* stop at body */
/* scan handler reads BODY via ReadChunkBytes */
ParseIFF(iff, IFFPARSE_SCAN);
CloseIFF(iff);
Close(iff->iff_Stream);
FreeIFF(iff);

Commodities application structure

  1. CxBroker() creates the broker with name, description, flags.
  2. AttachCxObj(broker, filter) builds the input-processing object tree.
  3. ActivateCxObj(broker, TRUE) starts processing.
  4. Wait for broker signal + window signal.
  5. DeleteCxObjAll(broker) recursively deletes the entire tree on cleanup.

Where to find examples by topic

Topic Location
Intuition V39 demos NDK_3.1/Examples1/intuition/
GadTools (listview callback) NDK_3.1/Examples1/gadtools/ListView.c
ASL requesters + hooks NDK_3.1/Examples1/asl/
Color wheel + gradient slider NDK_3.1/Examples1/colorwheel/WheelGrad.c
Async IO (double-buffered) NDK_3.1/Examples1/AsynchIO/asyncio.c
Locale + self-loading catalogs NDK_3.1/Examples1/locale/
AmigaGuide embedding NDK_3.1/Examples2/AmigaGuide/
DataTypes (sound, picture, ClipView) NDK_3.1/Examples2/DataTypes/
IFF ILBM loading/saving NDK_3.1/Examples2/IFF/
BOOPSI custom classes Extras/Development/Example_Code_v37/Libraries/Intuition/boopsi/
Commodities Extras/Development/Example_Code_v37/Libraries/Commodities/
BOOPSI image/gadget classes Extras/BOOPSI/

See Also


Sources: ESCOM AG / Commodore-Amiga / David N. Junod, example code (1985-1996). Raw: raw/examples/code-idioms.md Updated: 2026-08-04