Reference
The dialect tour covers every assembler syntax rg-asm can
read. This page covers what it can write: GEMDOS executables,
linkable objects for classic and modern toolchains, raw flat images, and the symbol tables
embedded in a .PRG.
Everything rg-asm emits is one of three kinds, selected by a mutually exclusive flag. The difference is who resolves addresses:
| Kind | Flag | Default name | Relocations resolved by |
|---|---|---|---|
| Executable | --prg (default) |
<input>.PRG |
GEMDOS loader (PEXEC relocation table) |
| Object | --obj[=KIND] |
<input>.o |
The linker (rg-asm built-in or rg-link / vlink) |
| Raw | --raw |
<input>.BIN |
None (bytes must already be final) |
Atari-bound artifacts use UPPERCASE extensions (.PRG, .BIN); objects
stay lowercase .o because they are host toolchain intermediates. An explicit
-o path is honoured verbatim: GAME.TOS, DESK.ACC, and
TOOL.TTP are the same GEMDOS container; only the desktop launch hint differs.
The Atari ST executable format, also used for .APP, .GTP,
.ACC, and .TTP. All multi-byte fields are big-endian.
The file begins with magic $601A, literally a bra.s over
the header, which a debugger can step over.
| Offset | Size | Field | Notes |
|---|---|---|---|
0x00 | word | magic | $601A |
0x02 | long | text size | bytes of code segment in file |
0x06 | long | data size | bytes of initialised data in file |
0x0A | long | BSS size | uninitialised RAM (not stored in file) |
0x0E | long | symbol table size | 0 when stripped (--strip) |
0x12 | long | reserved | 0 (Pure C expects this) |
0x16 | long | flags | GEMDOS PRGFLAGS (see below) |
0x1A | word | absflag | 0 = relocation table follows; non-zero = no relocs |
--prg-flags)The flags longword at offset 0x16 controls load behaviour. Memory protection tokens share bits 4-7; pick one mode for --prg-flags.
| CLI token | Bit | Effect |
|---|---|---|
fastload | 0 | Clear BSS only on load, not the whole heap |
fastram | 1 | May load into TT-RAM |
fastalloc | 2 | Malloc may use TT-RAM |
privateglobalsuperreadable
|
4-7 |
Private memory protection (mode 0) Global memory protection (mode 1) Supervisor memory protection (mode 2) Readable memory protection (mode 3) |
| Region | Presence | Notes |
|---|---|---|
| Header | Always | 28 bytes; segment sizes and flags (see tables above). |
| Text segment | Always | Code; length from header text size. |
| Data segment | Always | Initialised data; length from header data size. BSS follows at load time only. |
| Symbol table | Optional | Embedded debug symbols; omitted with --strip or when header symbol size is 0. |
| Relocation table | Conditional | Present when absflag is 0; GEMDOS fixup stream (see below). |
When absflag is 0, GEMDOS loads the program at an arbitrary base and patches
absolute longwords in the combined text+data image using a compact byte stream appended after
the optional symbol table. The table starts with a longword offset to the first fixup site;
0 means no relocations. Fixup offsets must be even (68000 alignment).
| Table byte | Action |
|---|---|
$00 | End of relocation table. |
$01 | Advance the fixup offset by 254 bytes without patching (long gap). |
$02…$FE (even) | Advance the fixup offset by this many bytes, then add the load base to the longword at that offset in the image. |
Loader pseudocode
// image: text || data loaded at `base`; rt points at the relocation table in the file offset = be32_read(rt) if offset == 0: return p = rt + 4 loop: b = u8_read(p++) if b == 0: break if b == 1: offset += 254 continue offset += b u32_at(base + offset) += base
The long-gap byte is $01, not $FE; $FE (254) is a valid even step that
also performs a patch. rg-link and rg-asm emit absflag 0 with a full table (TurboAss /
GEMDOS style). GenST 1.01 uses absflag $FFFF and a single trailer $00 instead.
Executables can carry an embedded symbol table for debuggers and profilers, controlled by
--sym=KIND (default gst). Use --strip to omit it entirely.
--sym | Format | Where it lives | Typical consumer |
|---|---|---|---|
dri |
DRI (8-char names) | Embedded in .PRG |
Classic Atari debuggers, Hatari (limited) |
gst |
GST (DRI + long-name continuations) | Embedded in .PRG |
Devpac/HiSoft profilers, extended debuggers |
extern |
Plain text (ADDR t name, nm-style) |
Sidecar .sym file |
Hatari, godbox source-level debug |
Provenance: DRI and GST symbol tables come from the Digital Research GEMDOS / CP/M-68K toolchain; GST extended them for names longer than 8 characters (Devpac/HiSoft). Hatari and godbox are consumers, not authors, of these formats.
DRI symbols use 14-byte records (8-char name + type word + value). GST adds continuation records
for long names. The embedded table is separate from linkable object formats: it
lives inside the finished executable, not in a .o file.
--obj=KIND)
--obj emits one relocatable object per source file. The KIND selects
which linker ecosystem the object targets. rg-asm's built-in linker and rg-link ingest
ELF natively and transcode GST, a.out, Pure C, and DRI objects to ELF at read time, so mixed
links work through one pipeline.
KIND | Magic / marker | Typical consumer | Notes |
|---|---|---|---|
elf (default) |
ELF | vlink, vbcc, gcc-binutils | ELF32 big-endian m68k; modern toolchain path |
gst |
$FB directive stream | Devpac, HiSoft, GenST linkers | Classic Atari .O; vasm -Fgst oracle |
dri |
$601A + 28-byte header | Alcyon / DR AS68, RMAC family | Word-parallel reloc bitmap; 8-char DRI symbols |
purec (pc) |
$4EFA | Pure C, Lattice C, AHCC (PLINK / AHCL) |
Atari "group 1" object; AHCL-validated emit |
aout |
$0107 (OMAGIC) | MiNT, BSD a.out toolchains | Round-trip oracle via vlink -baoutmint |
lout (mwc) |
$0107 LE | Mark Williams C as |
Coherent-family l.out; minimal emit |
seka |
$A001 framing | Andelos / K-Seka | Framed linkable (A0 01 …) |
hunk (amiga) |
HUNK_* | Lattice Amiga | Amiga hunk format, not GST |
Default object format. Sections (.text, .data, .bss) carry
standard ELF relocations (R_68K_32, PC-relative variants, etc.). This is the format
vbcc and gcc emit when targeting vlink, and what rg-asm's linker consumes directly.
A flat byte stream of directives. Any byte that is not part of a directive is
literal section data. Directives begin with escape byte $FB; a literal
$FB in data is written as $FB $FB.
| Directive | Layout | Meaning |
|---|---|---|
FB 01 | len.b name[len] | Source file name (debug) |
FB 04 | id.w | Select current section |
FB 06 | len.b name[len] value.l secid.w | Defined symbol (export) |
FB 07 | addend.l size.b flag.b target.w | Relocated field |
FB 10 | id.w len.b name[len] | Section (negative id) or external xref (positive id) |
FB 13 | (none) | End of object |
Library archives use magic $FB01 (GMAGIC) with GST members inside.
vlink does not read GST objects. Correctness is verified by byte-matching vasm
-Fgst output.
Shares the executable magic $601A but is disambiguated by layout: a 28-byte header (same field order as PRG) followed by text, data, a symbol table, and a word-parallel relocation bitmap with one 16-bit slot per image word.
| Reloc slot | Meaning |
|---|---|
$0000 | No relocation at this word |
$0001 / $0002 | Low word, internal reference (GenST vs vasm differ) |
$0005 | High word of a 32-bit absolute relocation |
n×8+4 | Low word, external reference to symbol n |
DRI library archives use magic $FF65 (DLMAGIC).
Emitted with --obj=dri; pair with --dialect=alcyon or RMAC-family dialects.
The object format shared by Pure C, Lattice C, and AHCC. It is mutually incompatible with gcc/vbcc ELF
at the object level, but interoperable among group-1 tools. Magic
$4EFA (PMAGIC); the second word is fixed at
$001C (PPMAGIC). All fields are big-endian.
| Offset | Size | Field | Notes |
|---|---|---|---|
0x00 | word | magic | $4EFA (PMAGIC) |
0x02 | word | he | $001C (PPMAGIC), always this value |
0x04 | long | limage | Image length (text + data concatenated) |
0x08 | long | lfixup | Fixup stream length in bytes |
0x0C | long | lnames | Names section length |
0x10 | long | lsym | Symbol section length (0 in plain objects) |
0x14 | long | res1 | Reserved (0) |
0x18 | long | res2 | Reserved (0) |
0x1C | long | hekp | Reserved (0) |
| Region | Size | Notes |
|---|---|---|
| Header | 32 bytes | PC_H fields above. |
| Image | limage |
Text then data, back-to-back; no per-segment headers. BSS has no file bytes. |
| Fixup stream | lfixup |
Sequence of 4-byte FIX records (see below); positional walk over the image. |
| Names | lnames |
Length-prefixed Pascal strings; index 0…n referenced by fixups. |
| Symbols | lsym |
Optional debug symbols; usually 0 in assembler objects. |
FIX, 4 bytes)| Offset | Size | Field | Notes |
|---|---|---|---|
+0 | byte | ty | Fixup type (FIX_text, FIX_labs, …) |
+1 | byte | sk | Skip: byte delta from previous record's position to this one |
+2 | word | nnr | Name index into the names section, or -1 if none |
ty | Name | Meaning |
|---|---|---|
1 | FIX_end | End of fixup stream |
2 | FIX_step | Large position advance (24-bit × 256) |
3 | FIX_text | Start of TEXT segment |
4 | FIX_data | Start of DATA segment |
5 | FIX_bss | Start of BSS segment |
7 | FIX_glmod | Global module (exported) start |
9 | FIX_lcmod | Local module start |
11 | FIX_labs | 32-bit absolute relocation at current position |
12 | FIX_wabs | 16-bit absolute relocation |
13 | FIX_lpcrel | 32-bit PC-relative relocation |
14 | FIX_wpcrel | 16-bit PC-relative relocation |
17 | FIX_wbra | Word branch (PC-relative) |
18 | FIX_sbra | Short branch (PC-relative) |
rg-asm emits Pure C objects (--obj=purec) validated by AHCC's real
AHCL.TTP linker under emulation. It reads both Pure C ($4EFA) and DRI
($601A) inputs when linking.
Classic Unix a.out relocatable (OMAGIC $0107, big-endian on Atari).
Same broad shape as many host toolchains: fixed exec header, segment blobs, relocation records,
then symbol and string tables. Oracle-validated through vlink -baoutmint round-trips
with vasm -Faout.
| Offset | Size | Field | Notes |
|---|---|---|---|
0x00 | long | a_info | $00010107 (OMAGIC in low word) |
0x04 | long | a_text | Text segment size |
0x08 | long | a_data | Data segment size |
0x0C | long | a_bss | BSS size (not stored in file) |
0x10 | long | a_syms | Symbol table size (12 × n symbols) |
0x14 | long | a_entry | Entry point (0 for objects) |
0x18 | long | a_trsize | Text relocation size (8 × n) |
0x1C | long | a_drsize | Data relocation size (8 × n) |
| Region | Size | Notes |
|---|---|---|
| Exec header | 32 bytes | Eight big-endian longwords (above). |
| Text | a_text | Code segment. |
| Data | a_data | Initialised data. |
| Text relocations | a_trsize | 8 × n byte records for text fixups. |
| Data relocations | a_drsize | 8 × n byte records for data fixups. |
| Symbol table | a_syms | nlist entries, 12 bytes each. |
| String table | variable | Leading u32 length + NUL-terminated names. |
| Offset | Field | Notes |
|---|---|---|
+0 | r_address | Offset of the field within its segment |
+4 | r_info | Symbol/segment index in bits 31-8; flags in low byte (pcrel, length, extern) |
Section-relative fields hold seg_base + addend; external fields hold the addend alone.
MiNT a.out executables (GEMDOS-wrapped) are a separate follow-up; the modern MiNT/GNU path already consumes ELF from rg-asm.
l.out, Seka, and Amiga hunk sit outside the main GST / DRI / Pure C / a.out line.
They do not share one on-disk layout: a.out and l.out are header+segment cousins; Seka is a framed
byte stream; hunk is a tagged longword sequence. The matrix below is an at-a-glance map; each
format has its own detail section.
| Aspect | a.out |
l.out |
Seka | hunk |
|---|---|---|---|---|
| Magic / marker | $0107 OMAGIC (BE) | $0107 (LE) | $A001 frame words | HUNK_* tags |
| Header | 32-byte exec header | 48-byte Coherent header | None (inline framing) | HUNK_UNIT … HUNK_END |
| Segments | text, data, bss (implied) | text, data (minimal emit) | TEXT frame, optional DATA frame | CODE / DATA / BSS hunks |
| Relocations | 8-byte records per segment | Not emitted yet | A1 02 inline tags |
Not emitted yet |
| Symbols | nlist + string table |
Not emitted yet | A3 GLOBL records |
In hunk stream (Lattice) |
| rg-asm emit | Full | Text/data only | TEXT/DATA + relocs | TEXT (+DATA/BSS basic) |
Coherent-family relocatable used by MWC as (--obj=lout). Shares the
$0107 magic with a.out but is little-endian and uses a 48-byte header.
rg-asm currently emits text (+ optional data) only; symbol and relocation tables are still growing.
| Region | Notes |
|---|---|
| Header | Magic $0107 (LE) at 0x00; text size at +0x0A (or +0x0E after .prvi layout directives). |
| Text | Code bytes immediately after the header. |
| Data | Optional initialised data following text. |
Framed linkable for K-Seka (--obj=seka). No fixed header: the file is a byte stream
of 16-bit frame words and payload. Words in the $A000…$AFFF
range are escaped so they cannot be mistaken for control tags.
| Phase | Notes |
|---|---|
| Exports | Optional A3 GLOBL symbol records before the TEXT frame. |
| TEXT | Opened with A0 01, payload (with inline relocs), closed with A0 01. |
| DATA | If non-empty, wrapped in A0 02 … A0 02. |
| Tag | Layout | Meaning |
|---|---|---|
A0 00 | word | Escape: literal payload word in $A000…$AFFF |
A0 01 | (none) | TEXT section open / close frame |
A0 02 | (none) | DATA section open / close frame |
A1 02 | zero.l | Absolute relocation site (placeholder patched at link time) |
A3 | name… | Exported GLOBL symbol |
Lattice Amiga object format (--obj=hunk), not GST despite the shared vendor. A sequence
of big-endian longword tags and payloads; minimal emit is HUNK_UNIT → named CODE (or
DATA/BSS) → HUNK_END.
| Tag | Name | Meaning |
|---|---|---|
$03E7 | HUNK_UNIT | Start translation unit (+ name) |
$03E8 | HUNK_NAME | Name the following hunk |
$03E9 | HUNK_CODE | Initialised executable bytes (longword-padded) |
$03EA | HUNK_DATA | Initialised data |
$03EB | HUNK_BSS | BSS size (longwords, no bytes) |
$03F2 | HUNK_END | End of hunk block |
.BIN
--raw writes a flat memory image: no header, no symbol table, no relocation records.
Addresses must be final at assembly time. Use PC-relative code or ORG for absolute
placement. ORG is honoured for raw output and ignored for --prg and
--obj.
Raw output enforces two constraints: one effective section (sections are
concatenated into a single buffer) and no undefined symbols (nothing left for a
linker to resolve). The boot-sector idiom text / org $600 / … is the classic use case.
For every CLI flag, worked examples, and JSON machine output, see the full documentation. For input syntax history, see the dialect tour.