SAS/C Compiler Reference

SAS/C (Version 6.58) is the C compiler used to build all the NDK example code. This article documents its key features, extensions, and the knowledge needed to compile AmigaOS programs with it. Source: SAS/C User's Guide Volumes 1+2 (SAS Institute, 1993).

Version history

SAS Institute shipped the following public releases of the SAS/C Development System for AmigaDOS:

Version Date Notes
6.50 1993 Initial release. Two volumes of the printed User's Guide + System Library Reference.
6.55 1995 First patch release. Added asm flag, several bug fixes.
6.56 / 6.57 mid–late 1990s Interim patches (predominantly internal; not widely redistributed).
6.58 2005 Final public release. ~47 bug fixes plus two new builtins (__builtin_rol, __builtin_ror) and an extended SYMLOAD HUNKINFO directive. Development was officially terminated in 1995, but the 6.58 patch backports accumulated fixes from the in-house tree.

After 6.58, no further public patches were issued. The compiler remains the official 68k development toolchain shipped with the NDK 3.x; for new development, see The VBCC Compiler, which is open source and actively maintained.

Key changes introduced by the patches (6.50 → 6.58)

The cumulative effect of the 6.55, 6.56, 6.57, and 6.58 patch chain on day-to-day Amiga development falls into four areas:

Debugger (CPR / CPRX) stability — One of the more visible improvements for working developers is the cross-debugger (CPRX, the remote/cross-Amiga debugger session between two machines). The 6.58 release substantially reduced crashes and system lockups during debug sessions on complex setups or networked cross-debug links. The earlier 6.50 release had frequent Enforcer hits in line-mode CPR (e.g., the SHOW command trigger fixed in 6.58 item #8) and crashes when exiting CPR after debugging a Workbench-launched program with -s (fixed in 6.58 item #27).

Bitfield compatibility — Earlier versions (6.0 and the first 6.50) had bitfield layout that drifted from the long-standing SAS/C 5.x conventions, breaking code that relied on the historical bit ordering and packing. The patch chain progressively restored retro-compatibility, so the bitfield layout in 6.58 matches the 5.x reference once again. This matters for code that pokes at hardware registers or uses bitfields to define on-disk structures.

Optimizer (sc) correctness — The aggressive optimizer combination OPT + OPTGO + OPTPEEP could in rare cases mis-schedule 68k register usage, producing correct-looking but broken code. The patch series corrected the register-tracking issues and related cases (e.g., the NEG <ea> memory-modification fix in 6.58 item #9, the memcpy alignment fix in 6.58 item #47). Most users will only notice the optimizer in the form of "weird crashes" that vanished with 6.58.

NDK alignment and static link libraries — sc.lib and amiga.lib were updated to interface cleanly with the NDK shipped alongside AmigaOS 3.1 and later. In particular the C runtime started honouring 16-bit-abs relocations correctly (broken in 6.57, fixed in 6.58 item #6: move foo.w,a0 once again assembles to the intended movw form). At the same time the linker (slink) gained the warning for constructor/destructor priority overflow (6.58 item #17) and the proper handling of the LIBFD startup option (startup=devinit / startup=devinitr, 6.58 item #24).

The compilation chain

source.c → sc (compiler) → object.o → slink (linker) → executable
source.c → sc link (one-step compile+link) → executable
Tool Purpose
sc C compiler (also does assembly via .s input)
slink Linker
smake Make utility (reads smakefile)
cdbg Source-level debugger (CodeProbe)
bum Library maintenance
oml Object module librarian

Key sc compiler options

Option Effect
link Compile + link in one step
optimize=N Optimization level 0–4 (0=none, 4=max)
define=SYM Define preprocessor symbol
ignore=NN Suppress warning number NN
nostackcheck Disable stack overflow checking
structureequivalence Allow struct assignment by structure type
verbose Verbose compilation output
includedir=dir Additional include path
code=small Small code model
data=far Far data model
math=standard Standard math library

smakefile patterns

An smakefile (SAS/C makefile) uses tab-indented rules like standard make:

CFLAGS = optimize=2 structureequivalence

all: myapp helper

myapp: myapp.c
    sc link $(CFLAGS) $*

helper: helper.c
    sc $(CFLAGS) $*.c
    slink lib:c.o helper.o myapp.o lib:sc.lib lib:amiga.lib to myapp

$* = target name without extension. $< = first dependency. $@ = full target name.

All NDK smakefiles follow the pattern: sc link structureequivalence $*.

SAS/C-specific keywords

These are critical for correct AmigaOS code:

__saveds — load data segment

void __saveds my_callback(void)
{
    /* Data segment (A4) is loaded — safe to access globals */
}

Mandatory for: interrupt handlers, hook callbacks, timer callbacks, any function called from a different task context or supervisor mode. Without __saveds, the function may access the wrong data segment, causing silent corruption or crashes.

The NDK examples consistently use __saveds on: - Hook entry functions - Interrupt handlers (AddIntServer callbacks) - Timer callbacks - BOOPSI dispatcher functions

__interrupt — interrupt handler

Marks a function as an interrupt handler (no stack switch, must preserve all registers). Combined with __saveds.

__regargs / __stdargs

  • __regargs — pass all arguments in CPU registers (D0–D2, A0–A1) instead of on the stack. Faster but non-standard ABI.
  • __stdargs — standard C calling convention (stack-based). Default.

Amiga library functions use the register-based ABI via the library's jump table and #pragma libcall.

Startup modules

SAS/C provides startup code that runs before main():

  1. OS calls the program entry point with:
  2. A6 = ExecBase
  3. A0 = BCPL return vector
  4. A2 = argument BSTR
  5. Startup module (e.g., c.o) opens DOSBase, parses arguments into argc/argv
  6. Calls main(argc, argv)
  7. After main() returns, startup closes DOSBase and exits

Without a startup module (for libraries, device drivers):

void __saveds __startup entry(void)
{
    /* A6 = ExecBase guaranteed */
    /* Must manually open DOSBase if needed */
}

#pragma statements

Seven pragmas control library calls and diagnostics:

Pragma Purpose
#pragma libcall Call library function via register ABI
#pragma flibcall Library call with floating-point registers
#pragma amicall Aztec C-compatible library call
#pragma regcall Register-based call (Aztec compat)
#pragma syscall System call
#pragma tagcall Tag-based (TagItem) call
#pragma msg Control diagnostic message severity

#pragma msg — diagnostic control

#pragma msg 123 warn      /* make message 123 a warning */
#pragma msg 123 ignore    /* suppress message 123 */
#pragma msg 123 error     /* make message 123 an error */
#pragma msg 123 warn push /* save current state, then change */
#pragma msg 123 pop       /* restore previous state */

Only works on messages numbered 1–299 (C compiler messages, not C++ translator).

Stack management

Default stack: 4000 bytes (stkneed = 400). This is often enough but can overflow with deep recursion or large local arrays.

Solution How When
stackext keyword Per-function stack extension Deep recursion in one function
stackext option Entire program Many functions need more stack
Increase stkneed extern long stkneed = 8192; All functions need more
nostackcheck Disable checking Release builds (minor speed gain)

Never set stkneed below 400. The stackext keyword adds overhead (extra register + entry code), so use selectively.

CodeProbe debugger (cdbg)

Source-level debugger with:

cdbg program          # load program
Command Action
break function Breakpoint at function
break file.c:42 Breakpoint at source line
watch variable Break when variable changes
step / next Single step (into/over)
continue Resume execution
print expr Evaluate expression
backtrace Call stack
list Show source around PC

CodeProbe supports debugging shared libraries (Chapter 5) and multitasking programs (Chapter 6) — both essential for AmigaOS where most programs spawn multiple tasks.

Optimization levels

Level Effect Recommended use
0 No optimization Debugging
1 Basic peephole Quick dev builds
2 Standard Development builds
3 Aggressive (inlining, loop opts) Release candidates
4 Maximum (may alter FP precision) Release builds

Trade-off: higher optimization = slower compilation, harder to debug.

See Also

Sub-articles

This article is the entry point for the SAS/C 6.58 documentation set on the wiki. Each sub-article covers a specific aspect of the SAS/C Development System; navigate to the one you need.

Editor, source workflow

Language extensions

  • SAS/C Extensions to C and C++ — __saveds, __interrupt, __regargs / __stdargs, __aligned, #pragma libcall, #pragma msg, GCC-style __attribute__ subset, C++ support notes.
  • SAS/C Assembly Language — sc as assembler, inline __asm / #asm / asm(), register mapping, calling conventions, hardware access patterns, cache pitfalls.
  • SAS/C Portable C — three-layer portability model (standard C / SAS extensions / AmigaOS), wrapping patterns, porting to/from GCC.

Debugging

Toolchain

  • SAS/C Utilities — smake, slink, oml, bum, blazemgr, fpsp, resdent, Commodore, tr.
  • SAS/C Optimization — levels 0–4, peephole, CSE, inlining, FP precision, profiling and measuring, debug interaction.

Library reference overview

  • SAS/C System Library Reference — Commodore libraries (exec, dos, intuition, graphics, layers, utility, ...), SAS/C libraries (sc.lib, amiga.lib, debug.lib), NDK header layout, predefined data, library-open patterns.

Sources: SAS Institute Inc., "SAS/C Development System User's Guide" Volumes 1+2, Version 6.50 (1993). Raw: raw/sasc/sasc-reference-extract.md; raw/sasc/sasc-syslib-ref.md Updated: 2026-10-02