rg-dis
"dis gunna b gud!"
rg-dis is a 680x0 series and Motorola DSP56001 disassembler for Atari binaries.
This is part of the Reservoir Gods cross-platform toolchain, and is a command-line tool that runs on Linux, macOS, and Windows.
When I first started learning how to program, the disassembler was my main teacher. I spent hours in mon st, stepping through programs, watching registers change, understanding memory access, getting familiar with system calls and the whole Atari memory map.
Easy Rider allowed me to convert that stream of hex bytes into readable, understandable assembly language. I pored over listings.
For anyone involved in the hacking scene - disassembling stuff is a large part of your life.
Once you know how to take things apart, you begin to understand how to put them together again. How to build things. How to build new things.
It all started with a disassembly. Wouldn't it be nice to have a new Atari disassembler, that would run on modern machines, be a simple single binary with no dependencies and have a raft of cool new features — that would be grand, wouldn't it?
Welcome to the wide world of rg-dis. Pull up a chair. Put the kettle on. Break out the biscuits. And get disassembling.
This is a command-line tool. No loading and playing with GUIs, emulators, or GEM. You simply launch the tool with input and output arguments. Being part of the Reservoir Gods cross-platform toolchain, this works natively on Linux, macOS, and Windows. You can disassemble your Atari programs at home, on a train OR ANYWHERE.
It also isn't limited to just PRG and TOS files. You can use this to peel apart object files like DRI and GST, ar archives and ELF executables.
You have a raw binary blob? No problem. rg-dis handles that too.
If your files exist in a zip file or other archive, rg-dis can automatically pull them out of there and work on them.
It also supports raw disk access, so it can disassemble boot sectors or arbitrary tracks/sectors of a floppy image.
It handles disassembly of the full range of 680x0 CPUs from the 68000–68060, FPU instructions, and Motorola DSP56001 digital signal processors.
It not only shows you the assembly code; rg-dis has deep knowledge of the full range of Atari TOS system calls - AES, BIOS, GEMDOS, VDI, XBIOS. It can annotate the system call name, all the input arguments and output arguments.
The full range of Atari hardware addresses is also annotated, including low memory vectors and system variables. It is immediately clear what any hardware access is doing.
You can also pull out all readable strings from an executable.
It also automatically identifies standard C library functions (strlen, strcpy, strcmp, memset, memcpy, sprintf, printf, malloc, free) and compiler runtime helpers (__divu, _lmul, _CXM33) across major Atari toolchains (Pure C, VBCC, Lattice C, AHCC, Alcyon C, Sozobon C, Mintlib), and annotates function arguments right at their call sites.
But one of the most powerful features is the smart labelling. As it understands the instruction set, hardware addresses and system calls, it can give meaningful names to function labels and variables, which makes the whole output disassembly a lot more readable.
Contents
- Quick start
- Target architecture, CPU, FPU & machine
- Binary formats & disk containers
- Motorola DSP56001 disassembly
- Smart labelling & annotations
- Inspection, strings & diffing
- Listing layout & options
- Command-line options reference
- JSON output
Part 1 — Command-line tool interface
Install
rg-dis is distributed as a single standalone executable with zero runtime dependencies. Prebuilt zip packages for macOS, Linux, and Windows are available from rg.atari.org.
Simply download the zip for your platform, extract the executable and place it somewhere on your PATH.
Quick start
rg-dis game.tos -o game.s # disassemble GEMDOS executable → .s
rg-dis game.tos --inspect # short summary (header, sections, counts)
rg-dis game.tos --cpu 68030 --fpu 68882 # target specific CPU and FPU models
rg-dis dsp_prog.p56 # disassemble DSP56001 binary (.p56 / .lod / .out)
rg-dis dsp_prog.lod --dialect motorola # Motorola DSP56k assembler syntax
rg-dis game.tos --inspect embedded-dsp # scan Falcon binary for embedded DSP programs
rg-dis AUTO/GAME.PRG --container game.msa # extract & disassemble from inside disk/archive
rg-dis cuddly.msa --bootsector # extract & disassemble floppy boot sector
rg-dis game.st --disk 10/0/1..10/1/9 # disassemble floppy track & sector range
rg-dis tos.img --org 0xFC0000 --sym tos.sym # ROM dump with DRI/GST symbol map
rg-dis module.o # disassemble object file (ELF / DRI / GST / a.out)
Target architecture (--arch <ARCH>)
rg-dis supports both Motorola 680x0 CPUs and Motorola DSP56001 processors found in Atari machines:
| Architecture | Description |
|---|---|
auto | Default — auto-detects architecture from file headers and signatures |
68k | Motorola 680x0 CPU series (68000–68060 and ColdFire) |
dsp | Motorola DSP56001 digital signal processor |
When targeting DSP binaries (--arch dsp or auto-detected .p56, .lod, .out, or cooked DSP binaries), rg-dis disassembles P, X, and Y memory spaces, decodes parallel ALU moves, hardware DO loops, and bit instructions, formatting output in --dialect motorola (default) or --dialect a56.
Target CPU, FPU, and machine
By default, rg-dis operates in auto-target mode (--cpu auto, --fpu auto, or --auto-target). It decodes using the 68030 + 68882 superset (ensuring all ST, TT, and Falcon instructions are decoded faithfully), then inspects the decoded instructions to calculate and emit the minimal required CPU model (e.g. .cpu 68000 for plain ST code, .cpu 68020 for 68020+ code) and FPU requirement (omitting .fpu when no FPU instructions are present, or emitting .fpu M68882 when FPU instructions are used).
To force specific directives or override auto-detection, pass an explicit CPU/FPU model or --no-auto-target.
Target CPUs (-m, --cpu <MODEL>)
| Model | Aliases | Target / Notes |
|---|---|---|
auto | — | Default — decodes with 68030 capabilities; emits minimal required CPU directive |
68000 | m68000, 68k, 000 | Motorola 68000 baseline (ST) |
68010 | m68010, 010 | Motorola 68010 |
68020 | m68020, 020 | Motorola 68020 |
68030 | m68030, 030 | Motorola 68030 (Falcon030) |
68040 | m68040, 040 | Motorola 68040 (integrated FPU) |
68060 | m68060, 060 | Motorola 68060 (integrated FPU) |
coldfire | cf, cfv4e, cf5208, cf548x | ColdFire architecture (ISA_A / ISA_B / ISA_C) |
Target FPUs (--fpu <MODEL>)
| Model | Aliases | Target / Notes |
|---|---|---|
auto | — | Default — decodes with 68882 capabilities; emits .fpu only if FPU instructions are found |
68882 | — | Motorola 68882 floating-point coprocessor |
68881 | — | Motorola 68881 floating-point coprocessor |
coldfire | cf | ColdFire on-chip FPU (default on ColdFire targets) |
none | — | Disable FPU disassembly (unsupported ops emit dc.w) |
Note: On 68040 and 68060 targets, integrated FPU instructions are decoded automatically.
Target machine (--machine <M>)
| Machine | Hardware & Chip Annotations |
|---|---|
st | Baseline ST hardware (YM2149 PSG, Shifter, MFP 68901) |
ste | Blitter, STE DMA sound, extended palette |
mste | Cache / 16 MHz mode control, VME bus |
tt | TT Shifter, TT SCU, SCC 8530 serial |
falcon | VIDEL, DSP 56001 host interface, audio crossbar, IDE |
Passing --machine <M> focuses hardware register comments when disassembling memory-mapped I/O routines with --annotate-hw.
Binary input formats
Binary formats are auto-detected by inspecting file headers:
| Format | FMT | Detected by |
|---|---|---|
GEMDOS .TOS/.PRG | tos | 0x601A magic + GEMDOS header shape |
| CPX Control Panel Extension | cpx | 512-byte header + 0x601A magic at offset 512 |
DRI relocatable .o | dri | 0x601A magic + fixed-size reloc bitmap tail |
| ELF32-BE object/executable | elf | \x7fELF magic |
| a.out OMAGIC (VBCC) | aout | 0x0107 at bytes 2–3 |
| Devpac GST object | gst | leading $FB directive |
ar static archive | ar | !<arch> magic |
| Floppy disk image | disk | MSA $0E $0F / Pasti RSY\0 magic, or a .st/.msa/.stx name the image loads as |
| DSP P56 executable | p56 | P56\0 magic header (Falcon DSP binary) |
| DSP LOD ASCII image | lod | Motorola _DATA P/X/Y memory records |
| DSP COFF/OUT binary | out | Motorola COFF DSP object format |
| Cooked DSP block | cooked | GODLIB cooked DSP program block |
| Raw binary image | raw | fallback when no structured header matches |
Override auto-detection anytime with --input-format <FMT> (auto, tos, elf, dri, gst, aout, ar, cpx, disk, p56, lod, out, cooked, raw) — alias --format <FMT> — an override is a request, not a hint, so raw disassembles a floppy container's bytes and disk insists on the floppy pipeline.
GEMDOS executables (.TOS / .PRG)
TOS executables disassemble to re-assemblable source, preserving program header flags (.prgflags $HEX) and symbolic pointer table relocations in .data. Feed the listing back to rg-asm and you get the executable's code and data back:
rg-dis game.tos -o game.s
rg-asm --prg --no-opt-size game.s -o game-rebuilt.tos
cmp game.tos game-rebuilt.tos # .text + .data identical for the corpus below
Byte-identical re-assembly of those two sections is the bar, and it is enforced rather than assumed.
A regression ratchet re-assembles a corpus of real binaries (dis → rg-asm → rg-link) and
byte-compares their text and data against a committed baseline manifest — 684 binaries pass
today. Two things the comparison deliberately does not cover: a reassembled binary carries no copy
of the original DRI symbol table, so the header's symbol-table size word and the trailing table
differ from an input that had one, and symbol names are normalized away for the same reason.
It is a ratchet on purpose: a recorded class may not grow, and a binary outside the corpus can
reassemble to equivalent rather than identical bytes. cmp your own binary before assuming
byte-identity — that is what the comparison above is for.
Control Panel Extensions (.CPX)
Atari CPX modules are auto-detected (or forced with --input-format cpx). rg-dis parses the 512-byte CPXHEAD header (ID, title, icon text, version, and execution flags such as set_only, boot_init, and resident), displays CPX header fields in --inspect, and disassembles the embedded GEMDOS executable payload starting at offset 512 (0x200):
rg-dis modem.cpx --inspect # inspect CPX header fields and embedded sections
rg-dis modem.cpx -o modem.s # disassemble embedded CPX executable
Raw images & ROM dumps
For headerless binaries (TOS ROMs, raw data, memory dumps):
rg-dis tos.img --org 0xFC0000 # set base address for absolute references
rg-dis slice.bin --offset 0x100 --len 0x400 # disassemble a byte slice
rg-dis tos.img --org 0xFC0000 --sym tos.sym # inject DRI/GST symbol table sidecar
PC-relative code (music drivers, depackers) re-assembles identically at any base because PC displacements are recomputed from labels.
Linkable object files & archives (.o / .a)
rg-dis decodes relocatable object files and static library archives:
rg-dis module.o # DRI, GST, ELF, or a.out object
rg-dis libvc.a --inspect # list member summary
rg-dis libvc.a --archive-member printf.o # disassemble one member
rg-dis libvc.a # disassemble all members
Containers & disk images
rg-dis can inspect, extract, and disassemble directly from container packages and floppy disk images without unpacking them first:
| Container | Type | Description |
|---|---|---|
.ST | Disk image | Flat raw sector dump of a FAT12 floppy disk |
.MSA | Disk image | Magic Shadow Archiver compressed sector floppy image |
.STX | Disk image | Pasti floppy disk image with preserved sector headers |
.ZIP | Archive | Standard ZIP archive container |
.LZH | Archive | LHA / LZH compressed archive |
Floppy disk images & bootsectors (.ST / .MSA / .STX)
Floppy images are routed to the disk pipeline by default: a .MSA/.STX proves itself from
its magic, and a .ST from its name once the image loads. Passing one used to decode every
512-byte sector as 68000 code — 96.7 s and 114,074 lines of nonsense for a 720 KiB .MSA, against
0.04 s for the sector listing. Use --input-format raw when you really do want the bytes treated as
one headerless image:
rg-dis cuddly.msa # whole floppy image (auto-routed)
rg-dis cuddly.msa --bootsector # 512-byte boot sector (track 0/side 0/sector 1)
rg-dis disk.stx --bootsector # boot sector from Pasti STX image
rg-dis game.st --disk # entire floppy disk image
rg-dis game.st --disk 10 # track 10 only (all sides & sectors)
rg-dis game.st --disk 10/0/1..10/1/9 # explicit track / side / sector CHS range
rg-dis cuddly.msa --input-format raw # opt out: the container's bytes as one image
Autodetect only ever adds a route, so it never turns a working command into an error: when a
floppy image cannot be loaded (unusual geometry, corrupt header), rg-dis says so on stderr and
falls back to the route it would have taken before. --disk, --bootsector and --input-format disk are requests: they refuse loudly instead.
Containers (--container)
Pass --container <FILE> to reach directly inside archives or FAT12 disk images:
rg-dis BIN/GAME.TOS --container demo.zip # file inside a ZIP
rg-dis GAME.TOS --container demo.lzh --input-format raw # file inside an LZH
rg-dis AUTO/GAME.PRG --container game.st # file inside a FAT12 .ST disk
rg-dis --container demo.zip # list container contents
Automatic depacking (--unpack)
Many Atari ST executables and data files are packed with heritage crunchers. rg-dis integrates rg-pack to detect and transparently decompress packed binaries on the fly when --unpack is specified:
- Ice Packer (1.1, 2.0, 2.4, 3.0)
- Atomik Packer (3.5, 3.6)
- Automation / Pompey Packer / SpeedPacker
- Fire Packer
rg-dis packed_game.prg --unpack -o game.s # decompress and disassemble
rg-dis packed_game.prg --unpack --inspect # inspect decompressed payload
Motorola DSP56001 disassembly
rg-dis provides unified disassembly for the Motorola DSP56001 digital signal processor used in the Atari Falcon030, covering standalone DSP binaries and microkernels embedded directly inside 680x0 Atari executables.
The DSP56001 uses a 24-bit word architecture across independent memory spaces:
- Program memory (
P:): 24-bit instruction words, exception vectors, interrupt routines, and execution code. - X data memory (
X:): 24-bit coefficients, lookup tables, and on-chip peripheral registers (X:$FFC0–X:$FFFF). - Y data memory (
Y:): 24-bit audio/graphics sample buffers and operational data. - Long data memory (
L:): Concatenated 48-bitX:Ydata word pairs.
Standalone DSP binaries (.p56 / .lod / .out / cooked)
rg-dis automatically identifies standalone DSP formats from file headers and extensions:
- XBIOS
.p56: Multi-segment binary executable stream (standard Atari Falcon XBIOS format produced byDsp_LodToBinaryand executed byDsp_ExecProg). - Motorola
.LOD: ASCII object files formatted as_DATA P/X/Ymemory records. - Motorola COFF
.OUT: Relocatable or absolute DSP COFF object binaries. - Cooked DSP (
sDspBinary): Structured Falcon DSP binaries with discrete code, X data, and Y data blocks.
rg-dis dsp_prog.p56 # disassemble DSP56001 binary (.p56 / .lod / .out)
rg-dis dsp_prog.lod --dialect motorola # Motorola DSP56k assembler syntax (default)
rg-dis dsp_prog.p56 --dialect a56 # a56 assembler syntax
rg-dis dsp_prog.p56 --entry-symbol main_entry # configure custom entry label (default: start)
rg-dis decodes parallel ALU moves (arithmetic operations executed concurrently with dual register transfers), hardware DO loops, and bit-manipulation instructions:
start:
CLR A X:(R0)+,X0 Y:(R4)+,Y0
REP #64
MAC X0,Y0,A X:(R0)+,X0 Y:(R4)+,Y0
RTS
Embedded DSP disassembly
Atari Falcon software frequently embeds DSP microkernels inside 680x0 GEMDOS executables (sound drivers, 3D graphics engines, tracker replays). rg-dis automatically detects embedded DSP programs in .text and .data sections, disassembling them inline into heterogeneous multi-target source:
rg-dis game.tos --inspect embedded-dsp # inspect discovered embedded DSP blocks
rg-dis game.tos -o game.s # disassemble 68k executable with inline DSP blocks
rg-dis game.tos --no-embedded-dsp # disable embedded DSP decoding (emit as raw data)
When an embedded DSP block is encountered, rg-dis:
- Switches processor modes cleanly: Emits
pushcpu 56001, packed, noevenat the start of the DSP code block, switching the assembler into DSP mode without disturbing surrounding 68k section bounds. - Restores 68k mode: Emits
popcpuat the conclusion of the DSP block to return to the host 680x0 context with automatic alignment intact. - Splits microkernel boundaries: When adjacent DSP programs are placed contiguously in memory,
rg-disreferences XBIOSDsp_ExecProgandDsp_LoadProgcode pointers to split them into discrete routines rather than merging them into a single block. - Inherits annotations: Embedded DSP code blocks inherit active command-line options (
--annotate-cycles,--annotate-hw,--annotate-offsets) from the host 68k disassembly pass.
XBIOS / P56 headers & multi-block streams
The 9-byte block header format was established by Atari for the Falcon030 XBIOS DSP subsystem. It is the native binary stream format produced by Dsp_LodToBinary (XBIOS 111) when converting a Motorola .LOD file, and consumed directly by Dsp_ExecProg (XBIOS 109) and Dsp_LoadProg (XBIOS 108) to load code and data into the DSP without the overhead of runtime ASCII text parsing. Developers commonly saved these binary streams to disk with a .p56 file extension or embedded them directly into 68k program data sections.
An XBIOS / .p56 binary consists of a multi-block stream that loads code and data into distinct memory spaces. Each block begins with a 9-byte header composed of three 24-bit words:
| Field | Width | Description |
|---|---|---|
| Space | 24 bits (3 bytes) | Target memory space: 0 = P (program), 1 = X (data), 2 = Y (data) |
| Org | 24 bits (3 bytes) | Base load address in the target memory space |
| Count | 24 bits (3 bytes) | Number of 24-bit words in the block payload |
rg-dis formats these headers symbolically with word count equates and memory space labels, keeping non-contiguous origins and initialized data spaces intact without zero-fill bloat:
; --- XBIOS / P56 DSP block: Space 2 (Y data), Org $1000, Count 13 words (39 bytes) ---
.WORDS_10CB4 equ (.END_10CB4-.START_10CB4)/3
dc24 2 ; Space: Y (data)
dc24 $1000 ; Org: $1000
dc24 .WORDS_10CB4 ; Count: 13 words (39 bytes)
.START_10CB4:
dc24 $000000 ; Y:$1000 = $000000 (0)
dc24 $000001 ; Y:$1008 = $000001 (1)
...
.END_10CB4:
; --- XBIOS / P56 DSP block: Space 0 (P program), Org $0040, Count 161 words (483 bytes) ---
.WORDS_10CE4 equ (.END_10CE4-.START_10CE4)/3
dc24 0 ; Space: P (program)
dc24 $0040 ; Org: $0040
dc24 .WORDS_10CE4 ; Count: 161 words (483 bytes)
.START_10CE4:
pushcpu 56001, packed, noeven
org p:$40
MOVEC #$FFFFFF,M0
...
RTS
popcpu
.END_10CE4:
Dynamic dc24 word macros & collision avoidance
Because Motorola 680x0 assemblers do not natively provide a 24-bit data directive (supporting only 8-bit dc.b, 16-bit dc.w, and 32-bit dc.l), embedded 24-bit DSP words are traditionally stored as triplets of bytes.
To produce clean, readable listings, rg-dis synthesizes a dynamic dc24 macro directly within the output stream:
dc24 macro
dc.b ((\1)>>16)&$FF,((\1)>>8)&$FF,(\1)&$FF
endm
Symbol collision avoidance: Before emitting the macro definition, rg-dis checks the binary's symbol table and existing labels. If dc24 is already used as a symbol or label by the original program, rg-dis automatically disambiguates the macro name sequentially (dc24_p56, dc24_p56_2, etc.) to prevent symbol collisions during reassembly.
DSP jump tables & symbolic reconstruction
rg-dis detects indirect DSP jumps (JMP (R0), JSR (R0)) and data dispatch tables stored in program (P:) and data (X:, Y:) spaces.
When jump tables are detected, address constants are reconstructed into symbolic labels (_p0040, _p0046, etc.) pointing to their respective routine targets. This preserves control-flow relationships and relocatability across DSP address shifts.
Pass --no-jump-tables to disable symbolic jump table reconstruction and output raw address values.
DSP semantic annotations & hardware registers
rg-dis extends its semantic annotation pipeline to Motorola DSP56001 code:
- Hardware registers (
--annotate-hw): Annotates accesses to memory-mapped DSP peripheral registers ($FFC0..$FFFF) and their control bitfields:- Host Interface (HI): HCR (Host Control Register), HSR (Host Status Register), HRX (Host Receive Data), HTX (Host Transmit Data). Status flags such as
HSR.HRDF(Host Receive Data Full) andHSR.HTDE(Host Transmit Data Empty) are annotated on polling loops (JCLR #1,X:$FFE9,wait). - Serial Communication Interface (SCI): SCR, SSR, SCCR, STX, SRX registers.
- Synchronous Serial Interface (SSI): CRA, CRB, SR, TX, RX registers for Falcon audio crossbar routing.
- Host Interface (HI): HCR (Host Control Register), HSR (Host Status Register), HRX (Host Receive Data), HTX (Host Transmit Data). Status flags such as
- Interrupt vectors: Annotates program memory interrupt vectors (
$0000..$003F), including hardware Reset, Stack Error, Trace, SWI, IRQA/B, and Host Commands (vectors$0026..$003E). - Instruction execution cycles (
--annotate-cycles): Annotates execution cycle counts for DSP56001 instructions, taking parallel ALU moves and multi-word instruction fetches into account. - Per-line memory addresses (
--annotate-offsets): Displays memory space addresses (P:$0040,X:$1000,Y:$1000) in comment columns. - DO loop layout: Hardware
DOloops format with clear start and termination comments, preserving loop nesting and column alignment.
Smart labelling
Instead of having to deal with obtuse machine generated labels like _L0012A4, rg-dis can analyse control flow, OS calls, vector writes, and string tables to generate meaningful semantic labels automatically:
| Synthetic labels (`--no-smart-labels`) | Smart labelling (`--smart-labels`) |
|---|---|
|
|
Smart labelling is enabled by default. Pass --no-smart-labels to restore raw address labels (_L12A4).
Semantic annotations
rg-dis includes built-in annotations for system calls, hardware registers, and object relocations.
rg-dis game.tos --annotate # enable all annotations
rg-dis game.tos --annotate-traps # GEMDOS/BIOS/XBIOS calls & arguments
rg-dis game.tos --annotate-basepage # startup basepage reading & Mshrink sizing
rg-dis game.tos --annotate-vdi # VDI parameter blocks & array pointers
rg-dis game.tos --annotate-aes # AES parameter blocks & array pointers
rg-dis game.tos --annotate-embedded-executables # embedded GEMDOS PRG headers & jump tables
rg-dis game.tos --annotate-hw # hardware registers and system vectors
rg-dis game.tos --annotate-vt52 # VT-52 terminal escape sequences in strings
rg-dis game.tos --annotate-cycles --cpu 68000 # per-instruction CPU cycle costs (68000)
rg-dis game.tos --annotate-offsets # per-line memory addresses
rg-dis trap.o --annotate-externs # object external references & relocs
rg-dis game.tos --no-annotate # disable all annotations (plain listing)
rg-dis game.tos --annotate-hw --machine falcon # narrow hardware comments to Falcon
Instruction cycle costs (--annotate-cycles)
Annotate each instruction line with its right-aligned CPU cycle execution cost matching the target CPU architecture (configured via --cpu <model>, which defaults to 68030 for full instruction decoding).
For 68000 (rg-dis game.tos --annotate-cycles --cpu 68000):
NOP ; 4 |
MOVE.W #5,-(A7) ; 12 | Setscreen (XBIOS 5)
TRAP #14 ; 34 | XBIOS Setscreen
RTS ; 16 |
For the default 68030 target (rg-dis game.tos --annotate-cycles):
NOP ; 2 |
MOVE.W #5,-(A7) ; 7 | Setscreen (XBIOS 5)
TRAP #14 ; 20 | XBIOS Setscreen
RTS ; 4 |
Per-line memory addresses (--annotate-offsets)
Comment each line with its address in memory — the byte's position in the loaded image, so a TOS
executable's first .text byte is $000000 (its 28-byte header is not loaded) and a raw image
starts at its --org. The field is one width for the whole listing, sized from the highest address
printed, so a column of addresses reads straight down the page:
BRA.S _start ; $000000 |
dc.b "RGCC" ; $000002 |
MOVEA.L 4(A7),A5 ; $000006 |
MOVE.L D1,-(A7) ; $00000A | newsiz - new size in bytes
With --annotate-cycles the cycle field follows the address, separated by a bar so the two numbers
never run together:
MOVE.L D1,-(A7) ; $00000A | 5 | newsiz - new size in bytes
MOVE.W #74,-(A7) ; $00000C | 7 | Mshrink - shrink a memory block
TRAP #1 ; $000010 | 20 | GEMDOS #74 (Mshrink)
Lines that occupy no memory — blank lines, bare labels, equates — carry no field. Off by default;
--annotate turns it on, or pass --annotate-offsets.
Decoded OS trap calls (--annotate-traps)
TRAP #1, #13, and #14 calls inspect the stack to comment functions and parameters:
MOVE.W #1,-(A7) ; rez - screen resolution: ST medium
MOVE.L #$78000,-(A7) ; physbase - physical screen base
MOVE.L #$78000,-(A7) ; logbase - logical screen base
MOVE.W #5,-(A7) ; Setscreen (XBIOS 5)
TRAP #14 ; XBIOS Setscreen
VDI & AES parameter blocks (--annotate-vdi, --annotate-aes)
TRAP #2 calls for VDI (D0 = $0073) and AES (D0 = $00C8) inspect the D1 parameter block pointer. The instruction loading D1 is commented with the parameter block name (VDIPB / AESPB), and data tables defining the parameter blocks are formatted cleanly as pointer arrays with individual array labels (contrl, global, intin, ptsin, intout, ptsout, addrin, addrout):
MOVE.L #vdi_pb,D1 ; VDI parameter block (VDIPB)
MOVEQ #115,D0
TRAP #2 ; VDI call
...
vdi_pb:
.dc.l vdi_contrl ; contrl pointer
.dc.l vdi_intin ; intin pointer
.dc.l vdi_ptsin ; ptsin pointer
.dc.l vdi_intout ; intout pointer
.dc.l vdi_ptsout ; ptsout pointer
Embedded GEMDOS executables (--annotate-embedded-executables)
Embedded TOS/GEMDOS executables (such as sound drivers, tracker replays, and overlays with $601A magic headers) located inside .text or .data sections have their 28-byte header structured symbolically with detailed field comments, and their entry point jump vectors (BRA.W init, BRA.W stop, etc.) disassembled into clean code routines:
; Embedded GEMDOS executable header
dc.w $601A ; magic (BRA.B +$1C)
dc.l $00000FB0 ; .text size (4016 bytes)
dc.l $00001D30 ; .data size (7472 bytes)
dc.l $00000000 ; .bss size (0 bytes)
dc.l $00000000 ; symbol table size (0 bytes)
dc.l $00000000 ; reserved / format
dc.l $00000000 ; flags (PRGFLAGS)
dc.w $0001 ; relocation flag (1 = relocs present)
BRA.W _L0DDC ; init driver
BRA.W _L0F56 ; stop driver
BRA.W _L1012 ; replay tick
Hardware registers & vectors (--annotate-hw)
Accesses to memory-mapped I/O ($FF8000+), interrupt vectors ($0000..$03FF), and low-memory OS variables ($0400..$05FF) are all annotated:
MOVE.W ($00044C).W,-(A7) ; sshiftmod - Copy of $FF8260 shift mode
BTST.B #0,($FFFFFC00).W ; ikbd_ctrl - IKBD ACIA status / control
VT-52 terminal escape sequences (--annotate-vt52)
Strings containing VT-52 terminal escape codes (cursor positioning, screen clearing, inverse video, text wrapping) are decoded into human-readable summaries on data directives and string arguments:
; Clear screen and home cursor
VT52_CLEAR_SCREEN:
.dc.b $1B,"E",0 ; VT52: [Clear & home]
; Direct cursor positioning (row 10, col 20)
VT52_CURSOR_POS:
.dc.b $1B,"Y",42,52,"SCORE:",0 ; VT52: [Pos (10,20)] "SCORE:"
; Inverse video styling
VT52_STATUS:
.dc.b $1B,"p","PAUSED",$1B,"q",0 ; VT52: [Inverse on] "PAUSED" [Inverse off]
When passing VT-52 string buffers to GEMDOS console calls like Cconws, the call site argument is annotated as well:
PEA VT52_CLEAR_SCREEN(PC) ; buf -> VT52: [Clear & home]
MOVE.W #9,-(A7) ; Cconws (GEMDOS 9)
TRAP #1 ; GEMDOS Cconws
Standard C libraries & compiler runtimes (--annotate-stdlib)
When reverse engineering compiled Atari C programs, subroutines in the standard C library and compiler runtime helpers are often statically linked into .text.
rg-dis automatically scans subroutine byte patterns against known signature banks across major Atari C toolchains by default (use --no-annotate-stdlib to disable):
- Pure C / Turbo C:
PCSTDLIB.LIB,PCEXTLIB.LIB,PCTOSLIB.LIB(fastcallregister convention) - VBCC:
libvc.a(standard stackcdecl),libvcs.a(fastcall), and math intrinsics - Lattice C 5.x:
LC.LIB,LCM.LIB,LCS.LIB(cdecland__regargs),_CXM33/_CXD33math helpers - AHCC:
AHCCSTDI.LIB,AHCCTOSI.LIB - Sozobon C:
dlibs.a(cdeclwith frame-pointer linkage) - Alcyon C: Atari TOS DRI compiler libraries
LIBV88.A,OSLIB.A(_ladd,_lsub,_lmul,_ldiv) - Megamax C / Laser C:
MLIB.LIB - Mark Williams C:
libc.a,libm.a - Mintlib / GCC:
libc.a,libgcc.aruntime math helpers (__mulsi3,__divsi3,__udivsi3) - Standard OS wrappers: C library wrapper stubs for GEMDOS (
Fopen,Fread,Fwrite,Fclose,Cconws,Malloc,Mfree), AES (form_do,objc_draw,appl_init), and VDI (v_gtext,v_pline).
1. Canonical Function Naming & Subroutine Headers
Anonymous subroutine labels (_L004820) are promoted to canonical symbol names with informative comments explaining routine purpose and compiler family:
; compute length of string (VBCC standard)
_strlen:
MOVEA.L 4(A7),A1
MOVEQ #0,D0
MOVEA.L A1,A0
ADDQ.L #1,A1
TST.B (A0)
BEQ.S .L000010
ADDQ.L #1,D0
MOVEA.L A1,A0
ADDQ.L #1,A1
TST.B (A0)
BNE.S .L000008
.L000010:
RTS
2. Call-Site Argument Tracing & Value Decoding
rg-dis performs backward static analysis from BSR and JSR call sites to identify and annotate function arguments, mapping constant parameters (such as Fopen access modes, Fseek origins, Setscreen resolutions, and arithmetic operands) to their human-readable enumeration names and values:
Standard Stack Passing (cdecl — VBCC, Lattice, Mintlib, Sozobon):
PEA STR_SRC(PC) ; strcpy src: "SCORE: 0000"
PEA -100(A6) ; strcpy dest: [a6-100]
BSR.W _strcpy ; call _strcpy(dest=[a6-100], src="SCORE: 0000")
ADDQ.L #8,A7
MOVE.W #128,-(A7) ; memset count: 128
CLR.W -(A7) ; memset c: 0
PEA (A0) ; memset dest: (a0)
JSR _memset ; call _memset(dest=(a0), c=0, count=128)
LEA 8(A7),A7
OS Wrapper Stubs & Enumeration Modes (_Fopen, _Fseek, _Setscreen):
When functions take numeric mode constants, rg-dis decodes the constant to its symbolic enumeration name:
MOVE.W #0,-(A7) ; Fopen mode: read-only (0)
PEA STR_CONFIG(PC) ; Fopen filename: "CONFIG.INF"
BSR.W _Fopen ; call _Fopen(filename="CONFIG.INF", mode=read-only (0))
ADDQ.L #6,A7
MOVE.W #2,-(A7) ; Fseek mode: from end (2)
MOVE.L #0,-(A7) ; Fseek offset: 0
MOVE.W D0,-(A7) ; Fseek handle: D0
BSR.W _Fseek ; call _Fseek(handle=D0, offset=0, mode=from end (2))
LEA 8(A7),A7
Fastcall Register Passing (Pure C, AHCC, Lattice __regargs):
LEA STR_NAME(PC),A0 ; strlen s: "PLAYER1" [fastcall A0]
JSR _strlen ; call _strlen(s="PLAYER1")
LEA STR_DEST(PC),A0 ; strcpy dest: STR_DEST [fastcall A0]
LEA STR_SRC(PC),A1 ; strcpy src: STR_SRC [fastcall A1]
BSR.W _strcpy ; call _strcpy(dest=STR_DEST, src=STR_SRC)
Compiler Runtime Math Intrinsics & Values:
MOVE.L D3,D0 ; __divu dividend: D3
MOVE.L #320,D1 ; __divu divisor: 320
BSR.W ___divu ; call ___divu(dividend=D3, divisor=320)
MOVE.L (A0),D0 ; __lmul factor1: (A0)
MOVE.L #1000,D1 ; __lmul factor2: 1000
BSR.W ___lmul ; call ___lmul(factor1=(A0), factor2=1000)
GFA-BASIC runtime libraries (--annotate-gfa)
When reverse engineering compiled GFA-BASIC binaries (GFA-BASIC 2.x: v2.00, v2.0F, v2.02, v2.50, and GFA-BASIC 3.x: v3.02, v3.50, v3.50F, v3.60, v3.6DE, v3.6TT, v3.6TTB), rg-dis probes compiler validation headers (CMPI.L #'GfAB'), Run-Only preambles (GFA BASIC RUN ONLY), and A6 ABI dispatches to link runtime routines and annotate call sites.
rg-dis identifies the compiled runtime version, decodes function arguments backwards from call sites, maps workspace structures, and annotates runtime routines with documented command syntax and curated descriptions (use --no-annotate-gfa to disable):
1. A4 Vector Dispatch & Call-Site Argument Tracing
GFA-BASIC v3 dispatches graphics, text, GUI, and system commands via negative A4 offsets (JSR -disp(A4)). rg-dis performs backward static analysis over preceding register loads to annotate command parameters right at the call site:
LEA STR_TITLE(PC),A2 ; TEXT string pointer: "RESERVOIR GODS"
MOVEQ #14,D0 ; string length: 14
MOVE.W #166,D2 ; Y coordinate: 166
MOVE.W #288,D1 ; X coordinate: 288
JSR -$5E7A(A4) ; GFA call: TEXT_XY(x=288, y=166, len=14, s="RESERVOIR GODS")
MOVEQ #50,D0 ; delay: 50 frames (1.00s)
JSR -$5018(A4) ; GFA call: PAUSE (1.00s)
MOVEQ #3,D0 ; color index: 3
JSR -$6460(A4) ; COLOR C
2. Floating-Point Loop & Descriptor Tracing
For floating-point FOR loops, rg-dis decodes GFA 80-bit real representation constants stored into loop descriptors:
LEA -$7FC0(A5),A0 ; loop descriptor (init)
MOVE.W #$8000,(A0)+ ; loop start: 1.0
CLR.L (A0)+ ; loop start: 1.0
MOVE.W #$3FF,(A0)+ ; loop start: 1.0
MOVE.L #$90000000,D0 ; loop limit: 36.0
MOVEQ #0,D1 ; loop limit: 36.0
MOVE.W #$404,D2 ; loop limit: 36.0
LEA -$7FC0(A5),A0 ; loop descriptor
JSR -$5EE2(A4) ; FOR float loop init (start=1.0, limit=36.0)
3. Vector Dispatch & Symbolic Call Equates
Runtime identity headers and vector equates are emitted in the listing preamble, and calls via negative A6 or A4 offsets are annotated with documented command forms or curated routine descriptions:
; gfa: GFA-BASIC runtime, v3 frame ABI, library 3.50 (244 signature matches)
; Equates emitted in preamble:
; GFA_CALL_DFREE EQU -$0160
; GFA_CALL_RANDOMIZE EQU -$4F04
; GFA_CALL_CLS EQU -$4BF6
JSR GFA_CALL_DFREE(A6) ; DFREE: query free disk space
JSR GFA_CALL_RANDOMIZE(A4) ; RANDOMIZE: seed random number generator
JSR GFA_CALL_CLS(A4) ; CLS: clear screen
4. A4 Workspace Members & A5 Semantic Variable Equates
Memory accesses via A4 (workspace members) and A5 (global variables) emit named equates and deduce variable names from referenced data strings:
; Equates emitted in preamble:
; GFA_WS_SCREEN_LOGIC EQU $24
; GFA_SCORE EQU -$7E1C
MOVE.L GFA_WS_SCREEN_LOGIC(A4),D0 ; logical screen base
MOVE.L GFA_SCORE(A5),D0 ; GFA global variable (int32)
5. A6 Procedure Frames (Parameters & Local Variables)
Within GFA user procedure bodies, A6 stack frame displacements identify procedure parameters and local variables:
MOVE.L $0008(A6),D0 ; GFA param: arg_8 (+8(A6))
MOVE.L -$0004(A6),D0 ; GFA local: loc_-4 (-4(A6))
A/B diffing with --normalise
When comparing two object files, disassembly diffs get cluttered by compiler trivia: 16-bit vs 32-bit reloc representations, short vs word branches, and local label numbering.
--normalise eliminates that noise by generating a layout-invariant listing: operands become symbolic symbol+addend references, branch targets become logical labels, and width suffixes are stripped:
diff <(rg-dis --normalise a.o) <(rg-dis --normalise b.o)
| Default listing | Normalised (`--normalise`) |
|---|---|
|
|
Change ADDQ.W #1,D0 to ADDQ.W #2,D0, and diff highlights only that single instruction.
Startup basepage & Mshrink sizing (--annotate-basepage)
When TOS loads an executable, it builds a 256-byte ($100) basepage describing the program environment and pushes a pointer to this basepage onto the stack at 4(SP) / 4(A7) before jumping to the program entry point (or passes A0 = 0 for desk accessories).
Nearly all Atari executables begin with a preamble that reads this basepage pointer, calculates the program's total memory requirement (p_tlen + p_dlen + p_blen + $100 basepage + stack reservation), and issues a GEMDOS Mshrink ($4A) call to release unused memory back to TOS.
rg-dis automatically detects this startup preamble at .text entry by default (use --no-annotate-basepage to disable), annotating basepage field offsets, size calculations, stack relocation, and Mshrink parameter setup:
MOVEA.L 4(A7),A5 ; TOS basepage pointer from stack
LEA SAVED_BASEPAGE,A0
MOVE.L A5,(A0) ; save basepage pointer
MOVEA.L $18(A5),A0 ; basepage.p_bbase (bss segment base)
ADDA.L $1C(A5),A0 ; + basepage.p_blen = end of bss segment
ADDA.L #$8000,A0 ; + stack reservation (32768 bytes)
ADDQ.L #1,A0 ; align stack to even address
ANDI.B #-2,A0 ; align stack to even address
MOVEA.L A0,A7 ; relocate stack pointer
SUBA.L A5,A0 ; total retained size relative to basepage
MOVE.L A0,-(A7) ; newsiz - retained program size
MOVE.L A5,-(A7) ; block - basepage pointer
CLR.W -(A7) ; zero - reserved, must be 0
MOVE.W #$4A,-(A7) ; Mshrink - shrink memory block
TRAP #1 ; GEMDOS #74 (Mshrink) - release unused memory
LEA 12(A7),A7 ; restore stack frame
For compilers that sum segment lengths directly:
MOVEA.L 4(A7),A5 ; TOS basepage pointer from stack
MOVE.L $C(A5),D0 ; basepage.p_tlen (text segment size)
ADD.L $14(A5),D0 ; + basepage.p_dlen (data segment size)
ADD.L $1C(A5),D0 ; + basepage.p_blen (bss segment size)
ADDI.L #$100,D0 ; + $0100 bytes (basepage size)
MOVE.L D0,PROGRAM_SIZE ; save total program size
MOVE.L D0,-(A7) ; newsiz - retained program size
PEA (A5) ; block - basepage pointer
CLR.W -(A7) ; zero - reserved, must be 0
MOVE.W #$4A,-(A7) ; Mshrink - shrink memory block
TRAP #1 ; GEMDOS #74 (Mshrink) - release unused memory
LEA 12(A7),A7 ; restore stack frame
rg-dis also assigns semantic smart labels to variables that store basepage values:
SAVED_BASEPAGE: Variable storing the basepage pointer (MOVE.L An, var).PROGRAM_SIZE: Variable storing the calculated program size.SAVED_ENV_PTR: Variable storing the environment string pointer ($2C(An)/p_env).SAVED_CMDLINE: Variable storing the command-line argument tail pointer ($80(An)/p_cmdlin).APP_FLAG: Variable storing the application vs desk accessory flag (TST.L A0/MOVE.L A0, var).
Metadata inspection & strings
Inspecting binary metadata (--inspect)
Quickly inspect binary headers, section metrics, symbol tables, function cycle counts, and CPU requirements without generating a full disassembly:
rg-dis game.tos --inspect # summary overview (header, sections, counts)
rg-dis game.tos --inspect symbols # symbol table listing
rg-dis game.tos --inspect relocs # relocation table listing
rg-dis game.tos --inspect functions,cycles # function sizes & M68000 cycle totals
rg-dis game.tos --inspect cpu # minimum CPU model & FPU requirement analysis
rg-dis game.tos --inspect strings # extracted human-readable text strings
rg-dis game.st --inspect disk # floppy filesystem summary and directory tree
rg-dis game.tos --inspect all # complete report across all metadata views
Interactive runs format metadata into aligned boxed tables (or ASCII borders with --ascii):
header · load base 0x00010000
┌──────────────┬──────────────────────────────────┐
│ field │ value │
├──────────────┼──────────────────────────────────┤
│ magic │ 0x601A (TOS executable) │
│ text │ 294,020 bytes │
│ data │ 136,672 bytes │
│ bss │ 34,404 bytes │
│ symbols │ 94,094 bytes (6,721 entries) │
│ flags │ 0x00000000 │
│ fastload │ no (clear heap on load) │
│ ttramload │ no (load into ST-RAM only) │
│ ttrammem │ no (malloc from ST-RAM) │
│ protect │ private (MiNT memory protection) │
│ relocation │ present │
└──────────────┴──────────────────────────────────┘
sections
┌─────────┬────────────┬────────────┬─────────┐
│ section │ start │ end │ bytes │
├─────────┼────────────┼────────────┼─────────┤
│ .text │ 0x00010000 │ 0x00057C84 │ 294,020 │
│ .data │ 0x00057C84 │ 0x00079264 │ 136,672 │
│ .bss │ 0x00079264 │ 0x000818C8 │ 34,404 │
└─────────┴────────────┴────────────┴─────────┘
Symbols and relocations (--inspect symbols, --inspect relocs)
Inspects DRI, GST, or object symbol tables and GEMDOS relocation fixups:
rg-dis game.tos --inspect symbols
rg-dis game.tos --inspect relocs
symbols · 3,035 entries
┌────────────┬──────┬────────┬───────────────────────────┐
│ address │ seg │ scope │ name │
├────────────┼──────┼────────┼───────────────────────────┤
│ 0x00010006 │ TEXT │ global │ _start │
│ 0x00010046 │ TEXT │ global │ _main │
│ 0x0001D9F6 │ TEXT │ global │ @AsciiToS32 │
│ 0x00047886 │ TEXT │ global │ @AsmSprite_Create │
│ 0x00057D94 │ DATA │ global │ _STR_TITLE │
└────────────┴──────┴────────┴───────────────────────────┘
relocs · 9,517 entries
┌────────────┬──────────────────────────────────────────┐
│ field │ target │
├────────────┼──────────────────────────────────────────┤
│ 0x0001000C │ __BasPag │
│ 0x0001003E │ ___main │
│ 0x00010048 │ @main │
│ 0x0001014E │ @GemDos_Super │
│ 0x000102AE │ 0x00079268 │
└────────────┴──────────────────────────────────────────┘
Functions and cycles (--inspect functions, --inspect cycles)
Lists function boundaries, byte extents, and estimated M68000 CPU cycle execution costs:
rg-dis game.tos --inspect functions,cycles
functions · 2,313 Text extents
┌────────────┬──────────────────────────────────────────┬────────┐
│ address │ name │ bytes │
├────────────┼──────────────────────────────────────────┼────────┤
│ 0x00010006 │ _start │ 60 │
│ 0x00010046 │ _main │ 260 │
│ 0x0001014A │ @GodLib_Game_Main │ 318 │
│ 0x00010820 │ @Board_TileGenerate │ 582 │
└────────────┴──────────────────────────────────────────┴────────┘
cycles · 2,313 Text extents · M68000
┌────────────┬──────────────────────────────────────────┬────────┐
│ address │ name │ cycles │
├────────────┼──────────────────────────────────────────┼────────┤
│ 0x00010006 │ _start │ 232 │
│ 0x00010046 │ _main │ 1,130 │
│ 0x0001014A │ @GodLib_Game_Main │ 1,322 │
│ 0x00010820 │ @Board_TileGenerate │ 2,450 │
└────────────┴──────────────────────────────────────────┴────────┘
Floppy disks (--inspect disk)
Inspects floppy disk images (.ST, .MSA, .STX) and archives directly from filesystem metadata without disassembling instructions:
- Disk geometry & boot sector: Reports track, side, and sector counts, media descriptor density, and executable boot sector status.
- FAT12 filesystem status: Summarises cluster allocation (used, free, bad, and reserved space), FAT table consistency, and filesystem health.
- Directory tree: Provides a recursive file listing with file sizes, timestamps, DOS attributes, and executable format detection.
rg-dis game.st --inspect disk # filesystem summary and file listing
rg-dis cuddly.msa --inspect disk,strings # filesystem inspection plus string table
rg-dis game.st --inspect disk --json # structured JSON disk report
disk image · ST · 80 tracks (0-79), 2 sides, 9 sectors/track
┌────────────┬──────────────────┐
│ property │ value │
├────────────┼──────────────────┤
│ container │ ST │
│ image size │ 737,280 bytes │
│ sectors │ 1,440 │
│ format │ TOS/FAT12 volume │
└────────────┴──────────────────┘
boot sector · checksum 0x1234 · executable
┌──────────┬─────────────────────────────────────┐
│ property │ value │
├──────────┼─────────────────────────────────────┤
│ checksum │ 0x1234 │
│ bootable │ yes (executable) │
│ media │ 0xF9 — 720 KB double-sided 3.5-inch │
│ serial │ 0x67183049 │
└──────────┴─────────────────────────────────────┘
files · 4 file(s) · 1 folder(s) · 0 deleted
┌──────────────┬─────────┬────────────┬──────┬─────────────────────┐
│ path │ size │ date │ attr │ notes │
├──────────────┼─────────┼────────────┼──────┼─────────────────────┤
│ AUTO/ │ │ 1991-04-12 │ D │ │
│ LOADER.PRG │ 48,120 │ 1991-04-12 │ - │ executable (GEMDOS) │
│ MAIN.PRG │ 117,170 │ 1991-04-12 │ - │ executable (GEMDOS) │
│ GRAPHICS.DAT │ 84,200 │ 1991-04-12 │ - │ │
│ README.TXT │ 2,450 │ 1991-04-12 │ - │ │
└──────────────┴─────────┴────────────┴──────┴─────────────────────┘
When an image contains structural defects (such as conflicting FAT tables or invalid geometry), rg-dis reports the specific defects as diagnostics. Non-disk containers and archives fall back to reporting their member tables.
CPU requirements (--inspect cpu)
Detect the minimum CPU architecture (68000, 68010, 68020, 68030, 68040, 68060, ColdFire) and FPU coprocessor requirements across executable code. rg-dis performs reachability traversal from program entry points, following branch targets, subroutine calls, jump tables, and vector table installations, ensuring embedded string literals and non-code data tables in .text do not corrupt architecture detection.
The report details the minimum CPU and FPU model, total instruction counts, and the reachable analysis scope:
rg-dis game.tos --inspect cpu # inspect minimum CPU & FPU requirements
rg-dis game.tos --inspect cpu --json # JSON output with non-68000 instruction list
cpu requirements · minimum 68000 · fpu: none
┌────────────────────────┬─────────────────────────────────────────────────────┐
│ property │ value │
├────────────────────────┼─────────────────────────────────────────────────────┤
│ minimum cpu │ 68000 │
│ fpu required │ no │
│ total instructions │ 17,833 │
│ non-68000 instructions │ 0 │
│ analysis scope │ reachable code only (57108 of 294020 section bytes) │
└────────────────────────┴─────────────────────────────────────────────────────┘
Strings (--inspect strings)
rg-dis scans binaries to find and extract human-readable text strings across loadable sections, disk images, and archives. Unlike raw byte-dump tools like strings(1), rg-dis leverages full disassembler context—mapping each string to its exact guest runtime address, segment (TEXT, DATA, or named section), associated symbol labels, and code vs data region classification.
How it works & Plausibility Scoring
On 68k platforms, basic ASCII scanning produces heavy instruction noise (e.g. NOP sleds decode as "NqNq..." and 68k opwords often fall into printable ranges). To filter out noise while preserving real game text and identifiers, rg-dis evaluates each candidate run with a plausibility score (0–100):
- Positive score signals (+10 to +25) include C-style NUL-terminators, high alphanumeric/space ratios, and natural vowel/word patterns.
- Penalties (-10 to -25) apply for heavy punctuation, repeated single-byte runs, unaligned non-NUL strings, or runs located inside decoded instruction regions.
- Runs inside decoded instruction regions are excluded by default. That is where the opcode noise lives:
NOP/RTSopwords land in the printable range asNq/Nu. Pass--strings-include-codeto report them anyway. - The default threshold is
--strings-min-score 40, which drops low-quality runs that survive the region filter. Passing--strings-min-score 0disables heuristic filtering — including the region filter — to extract all raw printable runs like traditionalstrings(1).
rg-dis game.tos --inspect strings # data-region strings (score >= 40)
rg-dis game.tos --inspect strings --strings-min-score 90 # high-confidence prose & dialogue only
rg-dis game.tos --inspect strings --strings-include-code # add decoded-instruction regions
rg-dis game.tos --inspect strings --strings-min-score 0 # unfiltered strings (like strings(1))
rg-dis game.tos --inspect strings --strings-charset atari # decode 8-bit Atari ST character set
strings · score >= 40 · data regions only
┌─────────────────────────┬────────────┬──────┬─────┬─────┬───────────────────┐
│ string │ addr │ seg │ len │ ref │ label │
├─────────────────────────┼────────────┼──────┼─────┼─────┼───────────────────┤
│ Retro Game Demo Title │ 0x00057D94 │ DATA │ 32 │ Y │ _STR_TITLE │
│ Press SPACE to start │ 0x00057E62 │ DATA │ 24 │ Y │ │
│ HIGH SCORE: 99999 │ 0x00057F81 │ DATA │ 16 │ Y │ │
└─────────────────────────┴────────────┴──────┴─────┴─────┴───────────────────┘
String Extraction Options
| Option | Values | Default | Purpose |
|---|---|---|---|
--strings-min-len <N> | integer (hex/dec) | 4 | Minimum consecutive character run length to extract |
--strings-min-score <N> | 0..100 | 40 | Minimum plausibility score threshold (0 disables every filter) |
--strings-charset <SET> | ascii, printable, atari | ascii | Character set: standard ASCII, whitespace-extended (printable), or 8-bit Atari ST |
--strings-include-code | flag | off | Report runs inside decoded instruction regions as well (--strings-min-score 0 implies it) |
Each extracted string is reported in a structured table (or JSON envelope with --json) showing decoded text contents, guest runtime address, segment, length in bytes, relocation reference status, and symbol labels.
Classification provenance (--explain, --record-decisions)
Investigate why specific addresses or byte ranges were classified as code, data, or strings:
rg-dis game.tos --explain '$10000' # explain classification at address $10000
rg-dis game.tos --inspect regions --record-decisions # list regions with the deciding heuristic rule
--explain <addr> prints the covering region and traces every classifier evaluation in order (including rule name, voting outcome, and deciding condition).
Listing layout & output options
Customise the assembly syntax, numeric formats, indentation, and listing layout:
rg-dis game.tos --dialect devpac # HiSoft Devpac compatible assembly
rg-dis game.tos --dialect purec # Pure C PASM compatible assembly
rg-dis game.tos --spaces 2 # 2-space indentation
rg-dis game.tos --tabs 1 # tab indentation
rg-dis game.tos --lower # lower-case mnemonics, registers, directives
rg-dis game.tos --sp # emit SP instead of A7 for stack pointer
rg-dis game.tos --no-abs-parens # emit $FFFF8240.w without parentheses
rg-dis game.tos --no-spacing # dense output without subroutine spacing
rg-dis game.tos --no-data-pack # single data item per line
rg-dis game.tos --data-width long # force 32-bit dc.l data directives
rg-dis game.tos --imm-format hex # force all integer immediates to hex
rg-dis game.tos --float-format raw # force raw IEEE hex for FPU constants
rg-dis game.tos --unused-equates # retain unreferenced equates in preamble
| Option | Values | Default | Purpose |
|---|---|---|---|
--dialect <DIALECT> | rg-asm (default), devpac, purec, lattice, turboasm, gas, madmac, rmac, alcyon, sozobon, josy, mwc, assempro, gfaasm, brainstorm, metacomco, gstasm, seka, aztec, mri, genpc | rg-asm | Target assembler export syntax and directive dialect |
--imm-format <MODE> | auto, hex, dec | auto | Integer # immediate format: small values decimal, large/bitmask hex |
--float-format <MODE> | text (decimal, dec), raw (hex) | text | FPU # immediate format: decimal literal (#1.0) vs IEEE hex (#{$3F800000}) |
--data-width <WIDTH> | auto, byte, word, long | auto | Unit width for raw data directives (dc.b, dc.w, dc.l) |
--data-pack / --no-data-pack | flag | on | Pack multiple comma-separated data values per line |
--spacing / --no-spacing | flag | on | Insert blank lines around logical subroutines and system calls |
--spaces [N] | integer (optional) | 4 | Indent lines using spaces (default 4; e.g. --spaces=2) |
--tabs [N] | integer (optional) | 1 | Indent lines using tabs (default 1; e.g. --tabs=2) |
--case <MODE> | upper, lower, mixed | upper | Casing for mnemonics, registers, and directives (--lower / --upper shorthand) |
--sp | flag | off | Emit SP / sp register alias instead of A7 / a7 |
--no-abs-parens | flag | off | Omit parentheses on absolute addresses ($FFFF8240.W vs ($FFFF8240).W) |
--unused-equates / --no-unused-equates | flag | off | Keep unreferenced equates in listing preamble |
Assembler export dialects (--dialect)
rg-dis can emit source code tailored for any major Atari ST and 68000 assembler dialect. The default dialect is rg-asm (Motorola syntax matching rg-asm and vasm).
rg-dis program.tos --dialect devpac > program.s # export for HiSoft Devpac 3 / GenST
rg-dis program.tos --dialect purec > program.s # export for Pure C PASM
rg-dis program.tos --dialect gas > program.s # export for GNU Assembler
| Dialect | CLI Aliases | Section Directives | Global Export | CPU / Options Header | Target Assembler |
|---|---|---|---|---|---|
rg-asm (Default) | rgasm, vasm | .text, .data, .bss | .globl <name> | .cpu <cpu>, .fpu <fpu>, .prgflags | Reservoir Gods rg-asm / vasm (Motorola) |
devpac3 | devpac, genst, genst3 | SECTION TEXT/DATA/BSS | XDEF <name> | OPT D-,X+, OPT P=<cpu>, OPT F=<fpu> | HiSoft Devpac ST v3 / GenST3 |
devpac1 | genst1 | SECTION TEXT/DATA/BSS | XDEF <name> | OPT D-,X+, OPT P=<cpu> | HiSoft Devpac ST v1 / GenST |
devpac2 | genst2 | SECTION TEXT/DATA/BSS | XDEF <name> | OPT D-,X+, OPT P=<cpu> | HiSoft Devpac ST v2 / GenST2 |
purec | pure-c, pcc | SECTION TEXT,code, DATA,data, BSS,bss | GLOBL <name> | OPT P=<cpu>, OPT F=<fpu> | Pure C Assembler (PASM) |
lattice | lc, lc5 | CSECT text,code, data,data, bss,bss | XDEF <name> | OPT P=<cpu> | Lattice C ASM (HiSoft LC5 ASM.TTP) |
turboasm | turbo-ass, turbo | SECTION TEXT/DATA/BSS | XDEF <name> | OPT P=<cpu> | Turbo Assembler (Markus Fritze) |
gas | gnu | .text, .data, .bss | .globl <name> | .cpu <cpu>, .fpu <fpu>, .org | GNU Assembler (GAS m68k) |
madmac | — | .text, .data, .bss | .globl <name> | .<cpu> (e.g. .68000) | Atari MADMAC |
rmac | — | .text, .data, .bss | .globl <name> | .<cpu> (e.g. .68000) | Modern RMAC |
alcyon | as68 | .text, .data, .bss | .globl <name> | .cpu <cpu> | Digital Research AS68 / Alcyon |
sozobon | jas | .text, .data, .bss | .globl <name> | .cpu <cpu> | Sozobon jas (Joe's Assembler) |
josy | — | .text, .data, .bss | .globl <name> | .cpu <cpu> | Josy (Hemsen) |
mwc | markwilliams | .text, .data, .bss | .globl <name> | .cpu <cpu> | Mark Williams C as (Lexicon) |
assempro | — | SECTION TEXT/DATA/BSS | XDEF <name> | .cpu <cpu> | AssemPro (Data Becker / Abacus) |
gfaasm | gfa-asm, gfa-assembler | SECTION TEXT/DATA/BSS | XDEF <name> | .cpu <cpu> | GFA-Assembler |
brainstorm | assemble | SECTION TEXT/DATA/BSS | XDEF <name> | OPT D-,X+, OPT P=<cpu> | Brainstorm Assemble |
metacomco | — | SECTION TEXT/DATA/BSS | XDEF <name> | .cpu <cpu> | Metacomco Macro Assembler |
gstasm | gst | SECTION TEXT/DATA/BSS | XDEF <name> | .cpu <cpu> | GST-ASM |
seka | k-seka | SECTION TEXT/DATA/BSS | XDEF <name> | .cpu <cpu> | K-Seka / A-SEKA |
aztec | manx | CSEG, DSEG, BSS | PUBLIC <name> | .cpu <cpu> | Aztec C M68k Assembler |
mri | microtec | SECTION TEXT/DATA/BSS | XDEF <name> | .cpu <cpu> | Microtec ASM68K / GAS MRI |
genpc | sc68 | .text, .data, .bss | .globl <name> | .cpu <cpu> | sc68 GenPC |
Contextual subroutine spacing (--spacing, --no-spacing)
By default (--spacing), rg-dis analyses control flow to insert blank lines around logical subroutine boundaries, RTS/RTE exits, and system call argument blocks:
| Default spacing (`--spacing`) | Dense listing (`--no-spacing`) |
|---|---|
|
|
Indentation style (--spaces, --tabs)
Choose between space or tab indentation and configure indent width:
rg-dis game.tos --spaces 2 # 2-space column indentation
rg-dis game.tos --spaces 4 # 4-space column indentation (default)
rg-dis game.tos --tabs 1 # tab indentation (1 tab)
; --spaces 2
_start:
MOVE.L 4(A7),A5
RTS
; --spaces 4 (default)
_start:
MOVE.L 4(A7),A5
RTS
Data packing & width (--data-pack, --data-width)
Data regions default to packed, comma-separated values up to 80 columns (--data-pack). Use --no-data-pack to emit one directive per line, or --data-width to control element sizes:
rg-dis game.tos --no-data-pack # one element per line
rg-dis game.tos --data-width byte # force dc.b bytes
rg-dis game.tos --data-width word # force dc.w words
rg-dis game.tos --data-width long # force dc.l longwords
| Packed data (default) | Single-element (`--no-data-pack`) |
|---|---|
|
|
Integer immediate formatting (--imm-format)
Configure integer # immediate literal formatting:
rg-dis game.tos --imm-format auto # small values decimal, large/masks hex (default)
rg-dis game.tos --imm-format hex # all immediates in hex (#$000A, #$00FF)
rg-dis game.tos --imm-format dec # all immediates in decimal (#10, #255)
; --imm-format auto (default)
MOVEQ #0,D0
MOVE.W #10,D1
ANDI.W #$00FF,D1
; --imm-format hex
MOVEQ #$00,D0
MOVE.W #$000A,D1
ANDI.W #$00FF,D1
Floating-point formatting (--float-format)
Controls 68881/68882/68040 FPU constant formatting:
rg-dis math.tos --float-format text # decimal literals (e.g. #3.14159) [default]
rg-dis math.tos --float-format raw # IEEE-754 hex literals (e.g. #{$400921FB})
; --float-format text (default)
FMOVE.D #3.141592653589793,FP0
FMOVE.S #1.0,FP1
; --float-format raw
FMOVE.D #{$400921FB,$54442D18},FP0
FMOVE.S #{$3F800000},FP1
Letter casing (--case, --lower, --upper)
Control letter casing across instruction mnemonics, registers, and assembler directives:
rg-dis game.tos --lower # lowercase mnemonics, registers, directives
rg-dis game.tos --upper # uppercase mnemonics, registers, directives (default)
rg-dis game.tos --case mixed # uppercase mnemonics, lowercase directives & registers
Uppercase (default / --upper) | Lowercase (--lower) | Mixed (--case mixed) |
|---|---|---|
|
|
|
Stack pointer register naming (--sp)
By default, the 68000 stack pointer is disassembled using standard address register notation A7 (or a7 with --lower). Pass --sp to emit SP / sp instead:
rg-dis game.tos --sp # emit SP instead of A7
rg-dis game.tos --lower --sp # emit sp instead of a7
; default
MOVE.W #9,-(A7)
TRAP #1
ADDQ.L #6,A7
; --sp
MOVE.W #9,-(SP)
TRAP #1
ADDQ.L #6,SP
Absolute address parentheses (--no-abs-parens)
By default, absolute memory addresses are enclosed in parentheses following standard Motorola / vasm convention (e.g. ($FFFF8240).W). Pass --no-abs-parens to emit flat addresses:
rg-dis game.tos --no-abs-parens # emit $FFFF8240.W without parentheses
| Default | Flat addresses (--no-abs-parens) |
|---|---|
|
|
Equate preamble filtering (--unused-equates)
rg-dis automatically identifies Atari hardware registers, OS variables, and vector offsets. By default, unreferenced equates are pruned from the preamble to keep listings clean. Pass --unused-equates to retain the entire definition table in the header.
User-defined labels (--labels, --label)
Supply custom label names for addresses or rename existing generated labels:
rg-dis game.tos --labels game.labels.tsv # TSV file
rg-dis game.tos --label '$1234=MAIN' --label _L12A4=DrawSprite # inline, repeatable
The --labels option accepts a tab-separated values (TSV) file containing <LOCATION>\t<NAME> rows:
# game.labels.tsv
$000100 _start
$0002A0 DrawPlayer
_L000300 UpdateScore
Each row maps a guest address ($1234 / 0x1234 or decimal) or an existing symbol name to a custom label. User-defined labels are emitted as labels in the disassembly listing and take precedence over generated names. Inline --label flags accept the same key-value pairs (LOCATION=NAME or OLD_NAME=NEW_NAME). Available for raw images and TOS/PRG executables.
User-defined equates (--equates, --equate)
Define custom constants to be emitted in the listing preamble:
rg-dis game.tos --equates game.equ.tsv # TSV file
rg-dis game.tos --equate MFP_GPDR=$FFFA01 --equate SCREEN_BASE=0x0 # inline, repeatable
The --equates option accepts a tab-separated values (TSV) file containing <NAME>\t<VALUE> rows:
# game.equ.tsv
MFP_GPDR $FFFA01
SCREEN_BASE $000000
MAX_PLAYERS 4
Values can be specified in hex ($FFFA01 / 0xFFFA01) or decimal. Equates are formatted using the active assembler dialect's syntax (EQU or .equ) and are retained in the preamble even if unreferenced in code. Inline --equate flags accept the same key-value pairs (NAME=VALUE). Available for raw images and TOS/PRG executables.
User-defined code/data sections (--regions, --region-code, --region-data)
The classifier decides which bytes are instructions and which are data, and it is right far more often than not — but sometimes you know better. A table that happens to decode as instructions, or a routine that a heuristic demoted to a data blob, can be settled outright:
rg-dis game.tos --regions game.regions.tsv # TSV file
rg-dis game.tos --region-data '$1234:$1240' # inline, repeatable
rg-dis game.tos --region-code '$2000:$2010'
The --regions option accepts a tab-separated values (TSV) file containing <START>\t<END>\t<KIND> rows:
# game.regions.tsv
$0001D0 $0001E8 data
$000300 $000320 code
Each row forces the half-open range [START, END) to code or data (case-insensitive). data
means the bytes are emitted as dc.* even where a byte pattern would decode as an instruction;
code means they are walked as instructions even where a heuristic would have demoted them to a
data blob. Addresses are guest addresses ($1234 / 0x1234 or decimal), the same domain as
--label and --inspect regions. # whole-line comments and blank lines are skipped. The inline
flags carry the kind in the flag name and take the range as START:END.
The two rows above may not cover the same byte — an overlap is a hard error naming both rows, not a
last-one-wins. Ranges that merely touch ($1000-$1010 then $1010-$1020) are fine. A kind of
string is refused: a string is a data range the renderer chose to print as text, so data
already covers it. Available for raw images and TOS/PRG executables; --inspect regions reports an
overridden span with the rule user.
Options at a glance
| Group | Options |
|---|---|
| Input / output | [FILE] · -o / --output · --inspect [VIEW…] · --explain <ADDR> · --record-decisions · --normalise · --json |
| Target architecture | --arch · --dialect · --entry-symbol · --embedded-dsp |
| Display | -q / --quiet · --color <WHEN> · --ascii · --banner <STYLE> |
| Listing layout | --[no-]smart-labels · --[no-]spacing · --spaces [N] · --tabs [N] · --case <MODE> · --lower · --upper · --sp · --no-abs-parens · --[no-]data-pack · --data-width · --imm-format · --float-format · --[no-]unused-equates |
| Container & packing | --input-format · --format · --container · --unpack · --bootsector · --disk · --sym · --labels · --label · --equates · --equate · --regions · --region-code · --region-data |
| Target model | --cpu · --fpu · --machine |
| Disk images | --disk [RANGE] · --bootsector |
| Raw images | --org · --offset · --len |
| Object files & archives | --archive-member |
| Annotations | --[no-]annotate · --[no-]annotate-traps · --[no-]annotate-hw · --[no-]annotate-externs · --[no-]annotate-vt52 · --[no-]annotate-cycles · --[no-]annotate-offsets · --[no-]annotate-vdi · --[no-]annotate-aes · --[no-]annotate-embedded-executables |
Strings (with --inspect strings) | --strings-min-len · --strings-charset · --strings-min-score · --strings-include-code |
| Info | -h / --help · -V / --version |
Mutually exclusive: --inspect and --normalise; --bootsector with
--normalise (or --sym on a full disk image); --bootsector and --disk; --spaces and --tabs.
Reference
| Flag | Meaning |
|---|---|
-o, --output <FILE> | Write the artifact to FILE instead of stdout |
--inspect [VIEW…] | Metadata report: bare = summary; header/sections/symbols/functions/cycles/relocs/strings/cpu/embedded-dsp/all |
--explain <ADDR> | Explain code/data classification at guest address (hex $…/0x… or decimal) |
--record-decisions | Populate the rule column in --inspect regions with the deciding check |
--normalise | Layout-invariant listing for A/B diffing (objects/archives; alias --normalize) |
--json | Machine-readable JSON envelope on stdout (all modes) |
-q, --quiet | Suppress all startup banners, progress spinners, and format-autodetect notes on stderr |
--color <WHEN> | When to colourise output (auto [default], always, never; honours NO_COLOR). --color=always forces pretty boxed tables and ANSI escapes even when piped |
--ascii | Draw inspect tables and banners with ASCII characters instead of Unicode box-drawing glyphs |
--banner <STYLE> | Startup masthead on stderr: logo (default on colour TTY), line, none (default when piped or quiet) |
--arch <ARCH> | Target architecture: auto (default), 68k, dsp |
--dialect <DIALECT> | Assembler syntax dialect (sc68 [default for 68k], vasm, devpac, gnu, motorola [default for DSP], a56) |
--entry-symbol <NAME> | Entry point symbol name for DSP disassembly (default: start) |
--embedded-dsp, --no-embedded-dsp | Scan Falcon GEMDOS binaries for embedded DSP code blocks and disassemble inline (on by default; --no-embedded-dsp disables) |
--jump-tables, --no-jump-tables | Reconstruct indirect jump tables into symbolic labels (on by default; --no-jump-tables outputs raw addresses) |
--input-format <FMT> | Container format override: auto (default), tos, elf, dri, gst, aout, ar, cpx, disk, p56, lod, out, cooked, raw |
--format <FMT> | Alias for --input-format |
--smart-labels, --no-smart-labels | Contextual smart-labelling (on by default): replace opaque synthetic address labels with semantic names from strings, OS variables, trap returns, and vectors (--no-smart-labels restores synthetic _L12A4 labels) |
--spacing, --no-spacing | Insert contextual blank lines around logical code blocks (subroutine/ISR terminations and system calls; on by default, --no-spacing disables) |
--spaces [N] | Indent lines using spaces (default 4; optional count N, e.g. --spaces=2; conflicts with --tabs) |
--tabs [N] | Indent lines using tabs (default 1; optional count N, e.g. --tabs=2; conflicts with --spaces) |
--case <MODE> | Casing style for mnemonics, registers, and directives: upper (default), lower, or mixed |
--lower | Emit listing in lowercase mnemonics, registers, and directives (shorthand for --case lower) |
--upper | Emit listing in uppercase mnemonics, registers, and directives (shorthand for --case upper; default) |
--sp | Emit SP / sp register alias instead of A7 / a7 for the stack pointer |
--no-abs-parens | Omit parentheses on absolute addresses (e.g. $FFFF8240.W vs ($FFFF8240).W) |
--data-pack, --no-data-pack | Pack multiple comma-separated data values per line (up to 80 cols / 16 bytes; on by default, --no-data-pack emits one item per line) |
--data-width <WIDTH> | Unit width for data directives: auto (default), byte, word, long |
--strings-min-len <N> | Strings: shortest run to report (default 4; accepts hex or decimal) |
--strings-charset <SET> | Strings: ascii (default), printable (adds tab/CR/LF), atari (adds the ST high range) |
--strings-min-score <N> | Strings: drop runs scoring under N of 100 (default 40; 0 disables every filter) |
--strings-include-code | Strings: also report runs inside decoded instruction regions (default excludes them) |
--bootsector | Extract + disassemble floppy boot sector from .ST/.MSA/.STX (org $0) |
--disk [<RANGE>] | Disassemble floppy image (.ST, .MSA, .STX); optional RANGE: track (10), track range (10..12), sector range (0/0/1..0/1/9), or boot |
--container <FILE> | Resolve the positional FILE as a path inside a ZIP/LZH archive or FAT12 .ST/.MSA/.STX image |
--unpack | Automatically detect and decompress packed executables and data containers (Ice, Atomik, Automation, Pompey, SpeedPacker, Fire, etc.) before disassembling |
-m, --cpu <MODEL> | Target CPU model: auto (default; emits minimal required CPU directive), 68000, 68010, 68020, 68030, 68040, 68060, coldfire (aliases cf, cfv4e, cf5208, cf548x) |
--fpu <MODEL> | FPU model: auto (default; emits .fpu only if FPU ops are present), none, 68881, 68882, coldfire (alias cf) |
--auto-target, --no-auto-target | Enable (default) or disable minimal required CPU/FPU directive calculation and emission (--no-auto-target emits exact configured target) |
--machine <M> | Target Atari machine (st, ste, mste, tt, falcon) to focus hardware annotations (--annotate-hw) |
--org <ADDR> | Raw-image base address (hex 0x…/$… or decimal; default 0) |
--offset <N> | Skip the first N bytes (base advances with it; accepts hex or decimal) |
--len <N> | Disassemble only N bytes (accepts hex or decimal) |
--sym <FILE> | DRI/GST symbol sidecar for named labels (raw images and TOS executables) |
--labels <FILE> | Add user-defined labels from a TSV file: one <LOCATION>\t<NAME> row per line (raw images and TOS executables; repeatable) |
--label <LOCATION>=<NAME> | Add one user-defined label inline (= or whitespace separates; repeatable) |
--equates <FILE> | Add user-defined equates from a TSV file: one <NAME>\t<VALUE> row per line (raw images and TOS executables; repeatable) |
--equate <NAME>=<VALUE> | Add one user-defined equate inline (= or whitespace separates; repeatable) |
--regions <FILE> | Add user-defined code/data section overrides from a TSV file: one <START>\t<END>\t<KIND> row per line, KIND = code/data (raw images and TOS executables; repeatable) |
--region-code <START>:<END> | Force one section range to code inline (START:END; repeatable, raw images and TOS executables) |
--region-data <START>:<END> | Force one section range to data inline (START:END; repeatable, raw images and TOS executables) |
--archive-member <NAME|INDEX> | Archive: --inspect or disassemble one member (bare --inspect lists members) |
--float-format <MODE> | FPU # immediate formatting: text (default decimal literal #1.0) or raw (IEEE hex #{$3F800000}) |
--imm-format <MODE> | Integer # immediate formatting: auto (default), hex, or dec |
--unused-equates, --no-unused-equates | Keep unreferenced equates in the listing preamble (--no-unused-equates prunes them; default) |
--annotate, --no-annotate | Enable (default) or disable every annotation family (--annotate-traps, --annotate-hw, --annotate-basepage, --annotate-stdlib, --annotate-gfa, --annotate-externs, --annotate-vt52, --annotate-cycles, --annotate-offsets, --annotate-vdi, --annotate-aes, --annotate-embedded-executables) |
--annotate-traps, --no-annotate-traps | Comment GEMDOS/BIOS/XBIOS calls and stack arguments, AES/VDI selectors and Line-A entries (on by default; --no-annotate-traps disables) |
--annotate-basepage, --no-annotate-basepage | Identify and annotate TOS program startup basepage reading (4(SP) or A0), size calculation, and Mshrink sizing (on by default; --no-annotate-basepage disables) |
--annotate-stdlib, --no-annotate-stdlib | Identify standard C library functions (strlen, strcpy, memset, memcpy, etc.), runtime math helpers (__divu, _lmul, _CXM33), and OS stubs across Pure C, VBCC, Lattice C, AHCC, Sozobon, Alcyon, and Mintlib, annotating call-site arguments (on by default; --no-annotate-stdlib disables) |
--annotate-gfa, --no-annotate-gfa | Identify GFA-BASIC runtime routines from GFA3BLIB / GFARUN and annotate call arguments (on by default; --no-annotate-gfa disables) |
--annotate-hw, --no-annotate-hw | Comment accesses to documented Atari address meanings (memory-mapped I/O, vectors, low-memory state, DSP on-chip peripherals; on by default; --no-annotate-hw disables) |
--annotate-externs, --no-annotate-externs | Comment external symbol references and relocations in object files (on by default; --no-annotate-externs disables) |
--annotate-vt52, --no-annotate-vt52 | Comment VT-52 terminal escape sequences in string data (on by default; --no-annotate-vt52 disables) |
--annotate-cycles, --no-annotate-cycles | Comment each instruction line with its right-aligned CPU cycle execution cost (off by default; --annotate-cycles enables) |
--annotate-offsets, --no-annotate-offsets | Comment each line with its memory address, one uniform width for the listing; with --annotate-cycles the address leads and a bar separates it from the cycle cost (off by default; --annotate-offsets or --annotate enables) |
--annotate-vdi, --no-annotate-vdi | Comment and format VDI parameter blocks (VDIPB) and pointer tables (on by default; --no-annotate-vdi disables) |
--annotate-aes, --no-annotate-aes | Comment and format AES parameter blocks (AESPB) and pointer tables (on by default; --no-annotate-aes disables) |
--annotate-embedded-executables, --no-annotate-embedded-executables | Format embedded GEMDOS executable headers ($601A) symbolically and decode entry jump vectors into code (on by default; --no-annotate-embedded-executables disables) |
-V, --version | Print version (honours --json) |
-h, --help | Full usage (-h summary, --help long form) |
Errors & exit status
Diagnostics are plain text on stderr (rg-dis: error: <message>), or formatted inside the JSON envelope on stdout when --json is specified.
| Exit code | Meaning | Output stream | Notes |
|---|---|---|---|
0 | Success | stdout | Disassembly listing or inspect summary emitted successfully |
1 | Runtime error | stderr (or JSON stdout) | Missing input files, parse failures, unreadable images (rg-dis: error: …) |
2 | Usage error | stderr | Clap argument parsing errors, unknown flags, invalid values, incompatible options |
Part 2 — JSON output
Global --json mode
Passing --json outputs structured JSON on stdout instead of terminal text. Terminal banners, progress indicators, and decorative formatting are suppressed.
| Command | Normal output (stdout) | --json output (stdout) |
|---|---|---|
| Disassembly (default) | Assembly source listing | JSON envelope with listings[].text |
--inspect | Formatted summary table | JSON envelope with summaries[] metadata |
--inspect symbols / relocs / … | Formatted section table | JSON envelope with requested slices |
--version | Version text | JSON envelope with version info |
-o <FILE> | Writes assembly file; prints summary | Writes assembly file; outputs JSON envelope |
| Runtime error | Error message on stderr (exit 1) | JSON error envelope on stdout (exit 1) |
| Usage error | Usage message on stderr (exit 2) | Usage message on stderr (exit 2) |
# Disassemble to JSON and extract the listing text
rg-dis game.tos --json | jq -r '.listings[0].text'
# Inspect metadata and symbol counts
rg-dis game.tos --inspect --json | jq '.summaries[0].counts'
# Extract symbols
rg-dis game.tos --inspect symbols --json | jq '.summaries[0].symbols[].name'
# Disassemble to an output file while capturing JSON stats
rg-dis game.tos -o game.s --json | jq '.listings[0].source'
Envelope structure
Every --json response uses this top-level envelope:
{
"schema": 1,
"tool": "rg-dis",
"version": "0.7.3",
"status": "ok",
"command": { "input": "game.tos", "inspect": false },
"outputs": [],
"summaries": [{ "source": "game.tos", "kind": "disassembly", "layout": {} }],
"listings": [{ "source": "game.tos", "kind": "disassembly", "text": "…" }],
"diagnostics": []
}
| Field | Type | Description |
|---|---|---|
schema | number | Schema version (currently 1) |
tool | string | Always "rg-dis" |
version | string | rg-dis version string |
status | string | "ok" or "error" |
error | object? | Error details when status is "error" (code, message) |
command | object | Command-line options used for this run |
outputs | array | Output file details when -o is used |
summaries | array | Inspection metadata and section tables |
listings | array | Disassembly text and source identifiers |
diagnostics | array | Diagnostic messages, notes, and warnings |
When an error occurs (such as a missing or corrupt file), status is "error" and details are provided under error and diagnostics:
{
"schema": 1,
"tool": "rg-dis",
"version": "0.7.3",
"status": "error",
"error": { "code": "io", "message": "No such file or directory" },
"command": { "input": "missing.tos" },
"outputs": [],
"summaries": [],
"listings": [],
"diagnostics": [{ "severity": "error", "message": "missing.tos: No such file or directory" }]
}
Inspect views
When --inspect is used with --json, the results appear under summaries[]. Specific views can be requested individually or in combination (e.g. --inspect header,symbols):
| View | TOS / Raw binary | ELF / DRI / a.out / GST | DSP (.P56 / .LOD / .OUT / Cooked) |
|---|---|---|---|
| (default) | header, layout, counts | header (if present), sections, counts | format, entry, word counts |
header | header, prgflags | header | format, entry |
sections | layout | sections[] | blocks[] |
symbols | symbols[] | symbols[] | symbols[] |
relocs | relocs[] | relocs[] | — |
functions | functions[] | functions[] | — |
cycles | cycles[] | cycles[] | — |
strings | strings[] | strings[] | — |
cpu | cpu | cpu | — |
disk | disk, geometry, selection, byte_span | — | — |
embedded-dsp | embedded_dsp[] | — | — |
all | All available views | All available views | All available views |
Payload details
TOS / Raw binary
{
"input_kind": "Tos",
"header": { "text_size": 4096, "data_size": 256, "bss_size": 512, "sym_size": 140, "flags": 0, "has_relocs": true },
"prgflags": { "raw": 0, "fastload": false, "ttram_load": false, "ttram_mem": false, "shared_library": false, "mem_protect": "global", "shared_text": false, "tpa_nibble": 0, "tpa_bytes": 0, "has_reserved_bits": false },
"layout": { "text_start": 65536, "text_end": 69632, "data_start": 69632, "data_end": 69888, "bss_start": 69888, "bss_end": 70400 },
"symbols": [{ "name": "_main", "segment": "Text", "global": true, "value": 65540, "type_word": 0 }],
"relocs": [65544, 65548],
"functions": [{ "address": 65540, "name": "_main", "size": 128 }],
"cycles": [{ "address": 65540, "name": "_main", "cycles": 842 }],
"strings": [{ "file_off": 8192, "addr": 73728, "segment": "Data", "region": "Data", "bytes_len": 12, "term": "Nul", "score": 90, "label": null, "referenced": true, "text": "Hello\\x00" }],
"counts": { "symbols": 10, "relocs": 42, "functions": 8, "strings": 3 }
}
| Field | Description |
|---|---|
header.text_size / data_size / bss_size | Section sizes on disk and in memory |
header.sym_size | Embedded symbol table size in bytes |
header.flags | Raw GEMDOS PRGFLAGS longword |
prgflags.* | Decoded Atari TOS memory and load flags |
layout.*_start / *_end | Memory addresses for .text, .data, and .bss |
symbols[] | Symbol names, segments, visibility, and values |
relocs[] | Addresses of relocatable references |
functions[] | Function entry points, names, and byte sizes |
cycles[] | Estimated 68000 CPU cycle costs per function |
counts | Summary totals when full tables are omitted |
Object files (ELF / DRI / a.out / GST)
{
"input_kind": "Elf",
"header": { "e_type": 1, "type_name": "REL", "entry": 0 },
"sections": [{ "name": ".text", "kind": "Text", "addr": 0, "size": 128, "align": 4, "flags": 6 }],
"symbols": [{ "name": "_tick", "value": 0, "size": 0, "bind": "Global", "section": ".text" }],
"relocs": [{ "section": ".text", "offset": 4, "symbol": "_state", "rtype": "R_68K_32", "addend": 0 }],
"functions": [],
"cycles": [],
"strings": [],
"counts": { "symbols": 1, "relocs": 1, "functions": 0, "strings": 0 }
}
Floppy boot sector
{
"input_kind": "BootSector",
"disk": "MSA",
"org": 0,
"size": 512,
"checksum": 4660,
"executable": true,
"strings": []
}
Static library archives (.a)
{
"input_kind": "Archive",
"members": [{ "name": "libvc.o", "kind": "a.out OMAGIC object", "bytes": 4096 }],
"warnings": []
}
DSP binary (.P56 / .LOD / .OUT / Cooked)
{
"source": "dsp_prog.lod",
"kind": "inspect",
"entry": { "address": 0, "name": "start" },
"format": "LOD",
"p_words": 295,
"x_words": 0,
"y_words": 320,
"symbols": 0
}
Embedded DSP microkernels (--inspect embedded-dsp)
{
"source": "game.tos",
"kind": "inspect",
"input_kind": "Tos",
"embedded_dsp": [
{
"format": "p56",
"offset": 3280,
"length": 540,
"entry": 0,
"p_words": 161,
"x_words": 0,
"y_words": 13
}
]
}
String scanner fields
When --json is requested with --inspect strings, the strings[] array preserves the full set of scanner metadata fields:
| Field | Type | Description | Terminal Table Mapping |
|---|---|---|---|
text | string | Extracted string content | string column |
addr | number | Guest address at run time | addr column |
segment | string | Section or segment name ("Text", "Data", etc.) | seg column |
bytes_len | number | Run length in bytes | len column |
referenced | boolean | Whether a relocated pointer or code reference targets this address | ref column (Y / -) |
label | string? | Symbol name defined at this address, if any | label column |
file_off | number | Raw byte offset in the input file on disk | Extended JSON metadata |
region | string | Lightweight classification ("Data", "Code", "Unclassified") | Extended JSON metadata |
term | string | Terminator type ("Nul", "Eol", "Boundary", "Truncated") | Extended JSON metadata |
score | number | Plausibility score (0–100%) | Extended JSON metadata |
Greetings
No comprehensive technical manual would be complete without a list of greetings.
So big shout outs to the folks still keeping the atari scene ticking over in the 2k26
Aggression · Avena · Cerebral Vortex · Cream · Defence Force · Dekadence · DHS
Dune · Effect · Ephidrena · Evolution · Extream · HMD · Holocaust · KÜA
Lamers · LineOut · LoUD · Marquee Design · MEC · MPS · MSB · New Beat · Newline
NoExtra · Omega · OVR · Oxygene · Paradox · PHF · Sector One · smfx · SYNC
TPT · XiA
Final Notes
This is an early release of rg-dis, so of course there are likely to be bugs and issues.
Please reach out and report any that you find or share any suggestions you have for improvements.
License
rg-dis is dual-licensed under the MIT License or the Apache License 2.0, at your option.
The full license texts ship with this release as LICENSE-MIT and LICENSE-APACHE. You may use,
copy, modify, and redistribute rg-dis under either license's terms. Contributions are accepted
under the same dual license unless stated otherwise.
Copyright (c) 1993–2026 Reservoir Gods
♥ made with love for the scene ♥