amiga-bootcamp/04_linking_and_libraries/inline_stubs.md
Ilia Sharin 21751c0025 docs(amiga): complete AmigaOS 3.1/3.2 developer reference — 172 files across 17 sections
Comprehensive technical documentation covering:
- Hardware: OCS/ECS/AGA custom chip registers, Copper & Blitter deep dives
- Boot sequence: cold boot through startup-sequence
- Binary format: HUNK executable spec, relocation, debug info
- Linking & ABI: .fd files, LVO tables, register calling conventions
- Exec kernel: tasks, interrupts, memory, signals, semaphores
- AmigaDOS: file I/O, FFS/OFS layout, CLI/Shell scripting
- Graphics: planar bitmaps, Copper programming, HAM/EHB modes
- Intuition: screens, windows, IDCMP, BOOPSI
- Devices: trackdisk, SCSI, serial, timer, audio, keyboard
- Libraries: utility, expansion, IFFParse, locale, ARexx
- Networking: bsdsocket API, SANA-II, TCP/IP stack comparison
- Toolchain: GCC, vasm/vlink, SAS/C, NDK, debugging
- Reverse engineering: IDA/Ghidra setup, compiler fingerprints, case studies
- CPU & MMU: 68040/060 emulation libs, PMMU, cache management
- Driver development: SANA-II, Picasso96/RTG, AHI audio

All files include breadcrumb navigation. No local paths or proprietary content.
2026-04-23 12:17:35 -04:00

198 lines
5.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[← Home](../README.md) · [Linking & Libraries](README.md)
# 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** — `#pragma` headers and `inline/` 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:
```asm
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:
```c
#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:
```c
#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
```asm
; 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:
```c
/* 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
```c
#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:
```c
/* 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`):
```asm
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`) |
| D1D7, A0A3 | Arguments (per `.fd` file) |
| A4, A5 | Scratch in OS (do not rely on preservation) |
| D2D7, A2A3 | **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-gcc` repo, `libnix` stubs
- VBCC manual: http://www.compilers.de/vbcc.html — register specification chapter
- vlink documentation: http://sun.hasenbraten.de/vlink/