SAS/C Extensions to C and C++

Vol 1 Ch.11 catalogs the SAS/C-specific keywords and pragmas that go beyond standard C. Some of these are essential for AmigaOS programming (__saveds, __interrupt); others are convenient but optional. Source: Vol 1 Ch.11, pp. 169–186.

Storage-class and calling-convention keywords

__saveds — preserve the data segment

void __saveds my_callback(void)
{
    /* A4 (data segment base) is loaded and valid */
    /* safe to access globals via base-relative addressing */
}

Mandatory for: - Hook entry functions (struct Hook → h_SubEntry) - Interrupt handlers (AddIntServer callback) - Timer callbacks - BOOPSI dispatcher functions - ARexx host command handler

Without __saveds, the function may access the wrong data segment — silent corruption or crashes.

The NDK examples consistently apply __saveds to every function that may be called from a different task context. There is no portable alternative; GCC uses __attribute__((saveds)) for the same purpose.

__interrupt — interrupt handler

void __interrupt my_irq_handler(void)
{
    /* no stack switch, must preserve all registers */
    /* cannot call most library functions */
    /* typically: read hardware, clear interrupt, set flag, return */
}

The function: - Does not get a normal stack frame (or gets a minimal one) - Must save and restore all registers it uses (A0–A6, D0–D7) - Cannot safely call exec.library or any other library - Must clear the interrupting hardware before returning - Should be as short as possible (microseconds, not milliseconds)

__interrupt may be combined with __saveds if the handler also needs to access globals.

__regargs / __stdargs

Keyword ABI Notes
__stdargs (default) Standard C: stack-based args, callee saves All C-to-C internal calls
__regargs All args in registers (D0–D2, A0–A1) Faster; non-standard; useful for callbacks
__regargs int fast_add(int a, int b)
{
    return a + b;
}

Internal C-to-C calls use __stdargs by default. Library calls (via #pragma libcall) use the register-based ABI because that's how the library jump table works.

__regargs is also a SAS/C extension; GCC uses __attribute__((regparm(3))) for a similar effect.

__aligned — variable alignment

__aligned(16) char buffer[256];   /* 16-byte aligned */
__aligned(8)  APTR pointer;        /* 8-byte aligned */

Useful for blitter-aligned data, DMA buffers, or for matching the alignment of a structure that the hardware requires.

__attribute__ — GCC-style attributes (subset)

SAS/C accepts a subset of GCC's __attribute__ for portability:

Attribute Effect
__attribute__((saveds)) Equivalent to __saveds
__attribute__((interrupt)) Equivalent to __interrupt
__attribute__((regparm(N))) Like __regargs with N register args
__attribute__((aligned(N))) Like __aligned
__attribute__((packed)) Not supported in SAS/C — manually arrange the struct
__attribute__((noreturn)) Equivalent to __noreturn
__attribute__((format(printf, N, M))) Format-string check for printf-like functions
__attribute__((unused)) Suppress "unused" warning
__attribute__((deprecated)) Mark as deprecated

This makes the same source code compatible with both SAS/C and GCC builds.

__noreturn — function does not return

__noreturn void fatal_error(const char *msg)
{
    /* print msg, abort */
    while (1) ;   /* never returns */
}

The compiler knows not to emit code after calls to fatal_error().

Pragmas

#pragma libcall — register-based library call

#pragma libcall DosBase Open 1E 801
LONG Open(STRPTR name, LONG accessMode);

The form is: #pragma libcall <BaseName> <FuncName> <Offset> <Tags>. The NDK proto/ headers include the right pragmas; you only write them manually when porting from Aztec C or when adding custom library calls.

#pragma flibcall — FFP library call

For functions that take or return FFP-format floating-point values:

#pragma flibcall MathIeeeDoubBase IEEEDPFlt 0 9802
double IEEEDPFlt(double x);

The flibcall variant reserves the FFP register (D2 for FFP, D0/D1 for IEEE-double on 68040+).

#pragma amicall — Aztec C compatibility

For source code written for Aztec C:

#pragma amicall MyBase MyFunc 12 901
void MyFunc(APTR data);

The semantics are slightly different from libcall — Aztec's ABI is similar but not identical. Use only when porting legacy Aztec source.

#pragma regcall — register-based internal call

For internal C-to-C calls in registers:

#pragma regcall my_func 3 1002
int my_func(int a, int b, int c);   /* args in D0, D1, D2 */

This is what __regargs does implicitly. The pragma form lets you call functions from C with a different calling convention than the default.

#pragma tagcall — TagItem-aware call

For functions that take a TagList:

#pragma tagcall IntuitionBase OpenScreenTagList 1E 801
struct Screen *OpenScreenTagList(struct NewScreen *, struct TagItem *);

The compiler emits a wrapper that builds the tag list from the variadic args you pass. Used in the NDK proto/intuition.h for taglist functions.

#pragma msg — diagnostic message control

#pragma msg 123 warn       /* make message 123 a warning (default severity) */
#pragma msg 123 ignore     /* suppress message 123 */
#pragma msg 123 error      /* make message 123 an error */
#pragma msg 123 warn push  /* save current severity, then set to warn */
#pragma msg 123 pop        /* restore previous severity */

The push/pop form enables scoped changes:

#pragma msg 154 warn push
/* code where msg 154 should be a warning */
#pragma msg 154 pop

Only works on messages 1–299 (C compiler messages). 300+ are linker/runtime messages and use different mechanisms.

Compiler built-ins

__builtin_constant_p and __builtin_expect (portability hints)

if (__builtin_expect(error, 0))   /* branch-prediction hint: error is unlikely */
    handle_error();

These are GCC built-ins; SAS/C accepts them but treats them as no-ops. The hint is for the optimizer — at level ≥ 3 it can rearrange hot/cold paths.

offsetof and container_of

offsetof(struct, member) is in <stddef.h> and works in both SAS/C and GCC.

For container_of (derive a containing struct from a pointer to a member), SAS/C has no built-in; either write a macro or use a GCC extension.

__builtin_rol and __builtin_ror (V6.58+)

Rotate-left and rotate-right helpers, added in the 6.58 patch:

x = __builtin_rol(a, b, size);
x = __builtin_ror(a, b, size);
Argument Meaning
a Value to rotate
b Number of bit positions (modulo operand size)
size Optional constant: 0 = byte, 1 = word, 2 (or omitted) = long

If b is the constant 16 and size is long, the compiler emits a SWAP instruction instead of a multi-bit rotate — useful on 68000-class CPUs where 16-bit rotates are not directly available. The builtin is recognized at all optimization levels and maps to the appropriate M68k ROR/ROL (or SWAP) instruction.

Inline assembly

sc supports inline assembly via __asm, asm(), and #asm/#endasm. See SAS/C Assembly Language.

C++ support

SAS/C 6.58 includes a C++ compiler (sc with cplus.o startup). It supports:

  • Classes, inheritance, virtual functions
  • C++ exception handling (try/catch/throw)
  • Templates (limited)
  • RTTI (with rtti option)
  • STL (via third-party or NDK extras)

It does not support:

  • C++11/14/17/20 features (auto, lambdas, move semantics, etc.) — these are 1993-era C++
  • <thread>, <chrono>, etc. — no C++11 standard library
  • Variadic templates

For modern C++ on Amiga, you need GCC (for AmigaOS 4, MorphOS, AROS) or a third-party compiler.

See Also


Sources: SAS Institute Inc., "SAS/C Development System User's Guide, Volume 1" Ch.11, Version 6.50 (1993), pp. 169–186. Raw: raw/sasc/sasc-extensions-extract.md Updated: 2026-10-02