โ Home ยท Linking & Libraries
Compiler Inline Stubs
Overview
AmigaOS library functions are not called via the C standard ABI. Every call goes through a negative-offset JMP table relative to a library base pointer held in an address register (conventionally A6). Compiler vendors each solve this differently:
- SAS/C โ
#pragmaheaders andinline/headers - GCC (m68k-amigaos) โ inline-assembly
__attribute__((regparm))stubs - VBCC โ
__reg()storage class and module pragmas
All approaches ultimately compile to the same machine code:
MOVEA.L _DOSBase, A6
JSR -LVO(A6) ; e.g. JSR -138(A6) for dos.library Output()
SAS/C: Pragma-Based Stubs
Pragma Syntax
SAS/C uses #pragma directives to declare register assignments:
#pragma amicall(DOSBase, 0x008A, Open(D1, D2))
DOSBaseโ global holding the library base (placed in A6 automatically)0x008Aโ LVO offset (hex)Open(D1, D2)โ function name and register allocation for arguments
Include Structure (NDK39)
NDK39/
include/
pragmas/
dos_pragmas.h โ #pragma amicall directives for dos.library
exec_pragmas.h โ exec.library pragmas
graphics_pragmas.h
...
inline/
dos.h โ Alternative: inline-macro stubs (SAS/C 6.x)
Usage:
#include <clib/dos_protos.h> /* ANSI prototypes */
#include <pragmas/dos_pragmas.h> /* register pragmas */
extern struct DosLibrary *DOSBase;
BPTR fh = Open("foo", MODE_OLDFILE); /* expands to JSR -30(A6) */
Pragma-Generated Code
; Open("foo", MODE_OLDFILE)
MOVE.L #MODE_OLDFILE, D2
MOVE.L #_str_foo, D1
MOVEA.L _DOSBase, A6
JSR -30(A6)
; Return value in D0
No stack frame involved โ pure register passing.
GCC (m68k-amigaos): Inline Assembly Stubs
Modern Approach (GCC 6.x+ / bebbo cross-compiler)
The NDK provides <proto/dos.h> which pulls in compiler-specific stubs. Under bebbo GCC:
/* auto-generated stub in clib2 / libnix */
static inline BPTR __attribute__((always_inline))
Open(CONST_STRPTR name, LONG accessMode)
{
register BPTR __ret __asm("d0");
register struct DosLibrary *const __DOSBase __asm("a6") = DOSBase;
register CONST_STRPTR __name __asm("d1") = name;
register LONG __accessMode __asm("d2") = accessMode;
__asm volatile ("jsr %%a6@(-30:W)"
: "=r"(__ret)
: "r"(__DOSBase), "r"(__name), "r"(__accessMode)
: "d1", "a0", "a1", "fp0", "fp1", "cc", "memory");
return __ret;
}
Key points:
- __asm("a6") forces the library base into A6
- __asm("d1"), __asm("d2") โ per-function register assignments from .fd file
- "jsr %%a6@(-30:W)" โ 16-bit signed displacement form (most efficient)
- Clobbers declared explicitly to prevent register allocation conflicts
Older GCC (< 6.x): __OLDSTYLE__ macros
#define Open(name, mode) \
({ BPTR _r; \
__asm volatile ("movea.l %2,a6; jsr -30(a6)" : "=d"(_r) : \
"d"(name), "d"(mode), "m"(DOSBase) : "a6"); \
_r; })
Less elegant โ explicitly moves DOSBase to A6 inside the macro.
VBCC: __reg() Storage Class
VBCC uses a compiler extension to place variables in specific registers:
/* vbcc style */
BPTR __reg("d0") Open(__reg("d1") CONST_STRPTR name,
__reg("d2") LONG mode);
#pragma amicall(DOSBase, 0x1E, Open(d1,d2))
VBCC automatically inserts MOVEA.L DOSBase,A6 and JSR -30(A6).
Module pragma file (dos.fd-derived):
##base DOSBase
##bias 30
Open(name,accessMode)(d1,d2)
##bias 36
Close(file)(d1)
...
Generated Machine Code (All Compilers)
For Write(fh, buf, len) at LVO โ48 ($30):
MOVEA.L _DOSBase, A6 ; load library base into A6
MOVE.L fh_var, D1 ; BPTR filehandle โ D1
MOVE.L buf_ptr, D2 ; buffer address โ D2
MOVE.L len_val, D3 ; byte count โ D3
JSR -48(A6) ; call Write()
; D0 = bytes actually written, or -1 on error
Register Allocation Convention
All AmigaOS library functions follow this invariant:
| Register | Role |
|---|---|
| A6 | Library base (always) |
| D0 | Return value (32-bit) |
| D0+D1 | 64-bit return (rare: DivideU) |
| D1โD7, A0โA3 | Arguments (per .fd file) |
| A4, A5 | Scratch in OS (do not rely on preservation) |
| D2โD7, A2โA3 | Preserved by OS calls (callee-saved) |
A compiler stub must:
1. Load arguments into exact registers from the .fd specification
2. Load the library base into A6
3. Execute JSR -LVO(A6)
4. Collect the return value from D0
Stub Generation Tools
| Tool | Input | Output |
|---|---|---|
fd2pragma (SAS) |
.fd file |
#pragma amicall header |
fd2inline |
.fd file |
GCC inline-asm header |
fd2sfd |
.fd file |
SFD (Amiga DevKit) format |
cvinclude.pl |
.fd + .sfd |
VBCC pragma headers |
NDK39 ships pre-generated pragmas/ and inline/ directories โ you only need to run these tools when writing a new library.
References
- NDK39:
include/pragmas/,include/inline/,fd/ - SAS/C 6.x Programmer's Reference Manual โ chapter on pragmas
- GCC for Amiga (bebbo):
m68k-amigaos-gccrepo,libnixstubs - VBCC manual: http://www.compilers.de/vbcc.html โ register specification chapter
- vlink documentation: http://sun.hasenbraten.de/vlink/