exec.library Reference

exec.library is the AmigaOS kernel — the lowest-level system library, always present and resident in ROM. It provides task scheduling, memory management, message-passing IPC, interrupt handling, library/device/resource management, signals, semaphores, and timers. All exec functions are called through the global SysBase pointer (memory location $4).

Cache SysBase in a local variable rather than re-reading $4 on every call — it's a documented performance win. Per the FutureOS rules, SysBase->ThisTask is private; use FindTask(NULL) instead.

Tasks vs. Processes

This is the single most important distinction in exec:

  • Task (struct Task) — the exec-level scheduling unit. Created via AddTask(). Tasks cannot call dos.library or anything that calls it. They have no DOS file handles, no CLI structure, no current directory. Useful for low-level background work, interrupt servicing, device IO.
  • Process (struct Process, extends struct Task) — created via dos.library/CreateProc() or CreateNewProc(). Processes have DOS context: current directory, CLI structure, file handles, environment variables. Most application code runs as a process.

Minimum stack sizes per the autodoc: ~256 bytes if only calling exec, 4K if calling anything in the system. "DO NOT UNDERESTIMATE."

Memory allocation

void *AllocMem(ULONG byteSize, ULONG attributes);
void  FreeMem(void *memoryBlock, ULONG byteSize);
void *AllocVec(ULONG byteSize, ULONG attributes);    /* V36+ — tracks size */
void  FreeVec(void *memoryBlock);                     /* no size needed */

Key memory flags: - MEMF_CHIP — chip memory, reachable by custom-chip DMA. Must be set for bitmaps, audio data, copper lists, sprites — anything the hardware touches directly. Without this flag, you may get fast memory and the hardware can't see it. - MEMF_FAST — fast (CPU-only) memory. - MEMF_CLEAR — zero the block before returning it. - MEMF_PUBLIC — memory visible to all tasks; never combine with MEMF_CHIP or MEMF_FAST.

Prefer AllocVec/FreeVec (V36+) over AllocMem/FreeMemAllocVec records the allocation size so FreeVec doesn't need it, eliminating a class of bugs.

ULONG AvailMem(ULONG attributes);

Returns free memory. Add MEMF_LARGEST to get the size of the largest single free block (useful for knowing if an allocation will succeed).

Signals and Wait

Signals are the fundamental IPC primitive — a 32-bit bitmask per task. Up to 16 user signals (SIGB_USER through SIGB_USER+15) can be allocated; the rest are system signals.

BYTE AllocSignal(ULONG signalNum);    /* -1 = any available */
void FreeSignal(BYTE signalNum);
ULONG Wait(ULONG signalSet);          /* blocks until any signal fires */
void Signal(struct Task *task, ULONG signalSet);
ULONG SetSignal(ULONG newSignals, ULONG signalMask);

Wait() cannot be called from interrupt or supervisor mode. It also breaks any active Forbid() or Disable(). If a signal was already set before Wait() is called, the function returns immediately — no race.

The standard message-port notification signal is obtained automatically when you create a port with CreateMsgPort() (V36+).

Message ports and messages

The core IPC pattern for AmigaOS — message ports (struct MsgPort) with queues:

struct MsgPort *CreateMsgPort(void);     /* V36 — allocates signal */
void  DeleteMsgPort(struct MsgPort *);
void  PutMsg(struct MsgPort *port, struct Message *msg);
struct Message *GetMsg(struct MsgPort *port);  /* non-blocking; NULL if empty */
struct Message *WaitPort(struct MsgPort *port);/* blocking */
void  ReplyMsg(struct Message *msg);           /* return to sender */

Pattern: sender PutMsg()s a message to a port. Receiver WaitPort()s on the port, then GetMsg()s the message. After processing, receiver ReplyMsg()s it back. Sender WaitPort()s on the reply port to know it's done.

Never modify a message after sending it until you've received the reply.

IO Requests (device I/O)

struct IORequest *CreateIORequest(struct MsgPort *ioReplyPort, ULONG size);
void  DeleteIORequest(struct IORequest *);
LONG  DoIO(struct IORequest *ioReq);    /* synchronous — returns when done */
void  SendIO(struct IORequest *ioReq);  /* asynchronous — returns immediately */
BOOL  CheckIO(struct IORequest *ioReq); /* non-blocking completion check */
LONG  WaitIO(struct IORequest *ioReq);  /* wait for async IO */
LONG  AbortIO(struct IORequest *ioReq); /* try to abort pending IO */

DoIO = blocking convenience (send + wait). SendIO + WaitIO = async pattern (fire off multiple IOs, wait later).

Semaphores (V36+)

The correct synchronization primitive for shared data, per the FutureOS rules:

void  InitSemaphore(struct SignalSemaphore *sigSem);
void  ObtainSemaphore(struct SignalSemaphore *sigSem);       /* exclusive */
void  ObtainSemaphoreShared(struct SignalSemaphore *sigSem); /* shared/read */
LONG  AttemptSemaphore(struct SignalSemaphore *sigSem);      /* non-blocking */
void  ReleaseSemaphore(struct SignalSemaphore *sigSem);

Semaphores are reentrant from the same task for exclusive locks — obtaining twice from the same task is fine; you must release twice. Shared locks allow multiple concurrent readers.

Avoid Forbid()/Disable() for synchronization — use semaphores instead. Forbid() only disables task switching (not interrupts); Disable() disables interrupts entirely. Both are called out as dangerous in the FutureOS compatibility notes.

Library management

struct Library *OpenLibrary(STRPTR libName, ULONG minVersion);
void  CloseLibrary(struct Library *library);

minVersion = the minimum library version your code requires (e.g., 37 for V37/V2.04 features). Returns NULL if not found or version too old. Always check the return value.

OldOpenLibrary() ignores the version requirement and will eventually Alert() in future OS versions — do not use.

Alert() (Guru Meditation)

void Alert(ULONG alertNum);

Alert number format: the high bit (AT_DeadEnd, 0x80000000) determines whether the system halts (Guru Meditation) or the alert is recoverable. Alert() is the last-resort error mechanism for unrecoverable errors.

SAD — Simple Amiga Debugging Kernel (V39+)

Built into exec from V39. Communicates via the motherboard serial port at 9600 baud. Requires only that ExecBase be valid — does not need the full OS running. Supports read/write memory, get register frame, JSR to address, and return-to-system. Used by external debuggers like wack.

Status: Outdated (V46.37, AmigaOS 3.1.4/3.2). SAD was removed: "The sad story of SAD ends here. ROMWack is back." ROMWack replaced SAD in exec V46.37. On OS 3.2 use ROMWack / mmu.library-based MuForce instead. See AmigaOS 3.2 — What Changed for Developers.

AmigaOS 3.2 (V47) exec changes

If you target OS 3.2, several exec internals documented above changed (see AmigaOS 3.2 — What Changed for Developers):

  • Task trees and AVL trees were removed. Any use ends in a recoverable alert and returns zero — they were never fully functional.
  • Memory pools reverted to the V40 puddle scheme ("just without the V40 bugs"); released puddles bubble to the top immediately.
  • Coldstart detects a 68060 FPU and suppresses the bogus FPU-present report (the 68060.library owns FPU handling).
  • On 68040+, ExitIntr does an extra custom-register access to suppress spurious interrupts.

exec functions to avoid (per FutureOS rules)

These exec functions are on the forbidden/restricted list for future compatibility: AllocAbs, AllocTrap, Disable, Enable, Forbid, FreeTrap, GetCC, ObtainQuickVector, OldOpenLibrary, Permit, SetExcept, SetFunction, SetIntVector, SetSR, SetTaskPri, SuperState, Supervisor, UserState.

See Also


Sources: Commodore-Amiga / ESCOM AG, exec.library Autodoc + Function Descriptors (1985-1996); Hyperion Entertainment, NDK 3.2 exec RelNotes (2016-2021). Raw: raw/exec/exec-library.md; raw/architecture/os-3.2-release-notes.md; raw/tutorials/amiga-system-prog-guide.md Updated: 2026-08-09 Updated: 2026-08-08