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


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:

ArchitectureDescription
autoDefault — auto-detects architecture from file headers and signatures
68kMotorola 680x0 CPU series (68000–68060 and ColdFire)
dspMotorola 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>)

ModelAliasesTarget / Notes
auto—Default — decodes with 68030 capabilities; emits minimal required CPU directive
68000m68000, 68k, 000Motorola 68000 baseline (ST)
68010m68010, 010Motorola 68010
68020m68020, 020Motorola 68020
68030m68030, 030Motorola 68030 (Falcon030)
68040m68040, 040Motorola 68040 (integrated FPU)
68060m68060, 060Motorola 68060 (integrated FPU)
coldfirecf, cfv4e, cf5208, cf548xColdFire architecture (ISA_A / ISA_B / ISA_C)

Target FPUs (--fpu <MODEL>)

ModelAliasesTarget / 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
coldfirecfColdFire 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>)

MachineHardware & Chip Annotations
stBaseline ST hardware (YM2149 PSG, Shifter, MFP 68901)
steBlitter, STE DMA sound, extended palette
msteCache / 16 MHz mode control, VME bus
ttTT Shifter, TT SCU, SCC 8530 serial
falconVIDEL, 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:

FormatFMTDetected by
GEMDOS .TOS/.PRGtos0x601A magic + GEMDOS header shape
CPX Control Panel Extensioncpx512-byte header + 0x601A magic at offset 512
DRI relocatable .odri0x601A magic + fixed-size reloc bitmap tail
ELF32-BE object/executableelf\x7fELF magic
a.out OMAGIC (VBCC)aout0x0107 at bytes 2–3
Devpac GST objectgstleading $FB directive
ar static archivear!<arch> magic
Floppy disk imagediskMSA $0E $0F / Pasti RSY\0 magic, or a .st/.msa/.stx name the image loads as
DSP P56 executablep56P56\0 magic header (Falcon DSP binary)
DSP LOD ASCII imagelodMotorola _DATA P/X/Y memory records
DSP COFF/OUT binaryoutMotorola COFF DSP object format
Cooked DSP blockcookedGODLIB cooked DSP program block
Raw binary imagerawfallback 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:

ContainerTypeDescription
.STDisk imageFlat raw sector dump of a FAT12 floppy disk
.MSADisk imageMagic Shadow Archiver compressed sector floppy image
.STXDisk imagePasti floppy disk image with preserved sector headers
.ZIPArchiveStandard ZIP archive container
.LZHArchiveLHA / 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:

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:

Standalone DSP binaries (.p56 / .lod / .out / cooked)

rg-dis automatically identifies standalone DSP formats from file headers and extensions:

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:

  1. Switches processor modes cleanly: Emits pushcpu 56001, packed, noeven at the start of the DSP code block, switching the assembler into DSP mode without disturbing surrounding 68k section bounds.
  2. Restores 68k mode: Emits popcpu at the conclusion of the DSP block to return to the host 680x0 context with automatic alignment intact.
  3. Splits microkernel boundaries: When adjacent DSP programs are placed contiguously in memory, rg-dis references XBIOS Dsp_ExecProg and Dsp_LoadProg code pointers to split them into discrete routines rather than merging them into a single block.
  4. 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:

FieldWidthDescription
Space24 bits (3 bytes)Target memory space: 0 = P (program), 1 = X (data), 2 = Y (data)
Org24 bits (3 bytes)Base load address in the target memory space
Count24 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:

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`)
    PEA _L000104.L
    MOVE.W #9,-(A7)
    TRAP #1
    ADDQ.L #6,A7
    MOVE.L #_L000120,($000070).W
    RTS

_L000104:
    dc.b "Hello World",13,10,0

_L000120:
    ADDQ.L #1,($000466).W
    RTE
    PEA _STR_HELLO_WORLD.L
    MOVE.W #9,-(A7)
    TRAP #1
    ADDQ.L #6,A7
    MOVE.L #_VBL_HANDLER,($000070).W
    RTS

_STR_HELLO_WORLD:
    dc.b "Hello World",13,10,0

_VBL_HANDLER:
    ADDQ.L #1,($000466).W
    RTE

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):

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 listingNormalised (`--normalise`)
.text
_tick:
    MOVEQ #$0000,D0
    LEA ($000002).L,A0
_wait:
    TST.W (A0)
    BNE.S $000008
    ADDQ.W #$0001,D0
    RTS
.text
_tick:
    MOVEQ #$0,D0
    LEA .bss+2,A0
_wait:
    TST.W (A0)
    BNE _wait
    ADDQ.W #$1,D0
    RTS

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:

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:

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):

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

OptionValuesDefaultPurpose
--strings-min-len <N>integer (hex/dec)4Minimum consecutive character run length to extract
--strings-min-score <N>0..10040Minimum plausibility score threshold (0 disables every filter)
--strings-charset <SET>ascii, printable, atariasciiCharacter set: standard ASCII, whitespace-extended (printable), or 8-bit Atari ST
--strings-include-codeflagoffReport 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
OptionValuesDefaultPurpose
--dialect <DIALECT>rg-asm (default), devpac, purec, lattice, turboasm, gas, madmac, rmac, alcyon, sozobon, josy, mwc, assempro, gfaasm, brainstorm, metacomco, gstasm, seka, aztec, mri, genpcrg-asmTarget assembler export syntax and directive dialect
--imm-format <MODE>auto, hex, decautoInteger # immediate format: small values decimal, large/bitmask hex
--float-format <MODE>text (decimal, dec), raw (hex)textFPU # immediate format: decimal literal (#1.0) vs IEEE hex (#{$3F800000})
--data-width <WIDTH>auto, byte, word, longautoUnit width for raw data directives (dc.b, dc.w, dc.l)
--data-pack / --no-data-packflagonPack multiple comma-separated data values per line
--spacing / --no-spacingflagonInsert blank lines around logical subroutines and system calls
--spaces [N]integer (optional)4Indent lines using spaces (default 4; e.g. --spaces=2)
--tabs [N]integer (optional)1Indent lines using tabs (default 1; e.g. --tabs=2)
--case <MODE>upper, lower, mixedupperCasing for mnemonics, registers, and directives (--lower / --upper shorthand)
--spflagoffEmit SP / sp register alias instead of A7 / a7
--no-abs-parensflagoffOmit parentheses on absolute addresses ($FFFF8240.W vs ($FFFF8240).W)
--unused-equates / --no-unused-equatesflagoffKeep 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
DialectCLI AliasesSection DirectivesGlobal ExportCPU / Options HeaderTarget Assembler
rg-asm (Default)rgasm, vasm.text, .data, .bss.globl <name>.cpu <cpu>, .fpu <fpu>, .prgflagsReservoir Gods rg-asm / vasm (Motorola)
devpac3devpac, genst, genst3SECTION TEXT/DATA/BSSXDEF <name>OPT D-,X+, OPT P=<cpu>, OPT F=<fpu>HiSoft Devpac ST v3 / GenST3
devpac1genst1SECTION TEXT/DATA/BSSXDEF <name>OPT D-,X+, OPT P=<cpu>HiSoft Devpac ST v1 / GenST
devpac2genst2SECTION TEXT/DATA/BSSXDEF <name>OPT D-,X+, OPT P=<cpu>HiSoft Devpac ST v2 / GenST2
purecpure-c, pccSECTION TEXT,code, DATA,data, BSS,bssGLOBL <name>OPT P=<cpu>, OPT F=<fpu>Pure C Assembler (PASM)
latticelc, lc5CSECT text,code, data,data, bss,bssXDEF <name>OPT P=<cpu>Lattice C ASM (HiSoft LC5 ASM.TTP)
turboasmturbo-ass, turboSECTION TEXT/DATA/BSSXDEF <name>OPT P=<cpu>Turbo Assembler (Markus Fritze)
gasgnu.text, .data, .bss.globl <name>.cpu <cpu>, .fpu <fpu>, .orgGNU 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
alcyonas68.text, .data, .bss.globl <name>.cpu <cpu>Digital Research AS68 / Alcyon
sozobonjas.text, .data, .bss.globl <name>.cpu <cpu>Sozobon jas (Joe's Assembler)
josy—.text, .data, .bss.globl <name>.cpu <cpu>Josy (Hemsen)
mwcmarkwilliams.text, .data, .bss.globl <name>.cpu <cpu>Mark Williams C as (Lexicon)
assempro—SECTION TEXT/DATA/BSSXDEF <name>.cpu <cpu>AssemPro (Data Becker / Abacus)
gfaasmgfa-asm, gfa-assemblerSECTION TEXT/DATA/BSSXDEF <name>.cpu <cpu>GFA-Assembler
brainstormassembleSECTION TEXT/DATA/BSSXDEF <name>OPT D-,X+, OPT P=<cpu>Brainstorm Assemble
metacomco—SECTION TEXT/DATA/BSSXDEF <name>.cpu <cpu>Metacomco Macro Assembler
gstasmgstSECTION TEXT/DATA/BSSXDEF <name>.cpu <cpu>GST-ASM
sekak-sekaSECTION TEXT/DATA/BSSXDEF <name>.cpu <cpu>K-Seka / A-SEKA
aztecmanxCSEG, DSEG, BSSPUBLIC <name>.cpu <cpu>Aztec C M68k Assembler
mrimicrotecSECTION TEXT/DATA/BSSXDEF <name>.cpu <cpu>Microtec ASM68K / GAS MRI
genpcsc68.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`)
_draw_player:
    MOVE.L    D0,(A0)+
    MOVE.L    D1,(A0)+
    RTS

_show_score:
    MOVE.W    #9,-(A7)
    TRAP      #1
    ADDQ.L    #6,A7

    RTS
_draw_player:
    MOVE.L    D0,(A0)+
    MOVE.L    D1,(A0)+
    RTS
_show_score:
    MOVE.W    #9,-(A7)
    TRAP      #1
    ADDQ.L    #6,A7
    RTS

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`)
_palette:
    dc.w    $0000,$0700,$0070,$0007
    dc.w    $0770,$0707,$0077,$0777
_palette:
    dc.w    $0000
    dc.w    $0700
    dc.w    $0070
    dc.w    $0007

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)
_start:
    MOVE.L    4(A7),A5
    PEA       _STR_HELLO.L
    TRAP      #1
    ADDQ.L    #6,A7
    RTS
_start:
    move.l    4(a7),a5
    pea       _STR_HELLO.l
    trap      #1
    addq.l    #6,a7
    rts
_start:
    MOVE.l    4(a7),a5
    PEA       _STR_HELLO.l
    TRAP      #1
    ADDQ.l    #6,a7
    RTS

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
DefaultFlat addresses (--no-abs-parens)
    CLR.W     ($FFFF8240).W
    MOVE.L    #_vbl,($000070).W
    MOVE.W    ($00044E).W,D0
    CLR.W     $FFFF8240.W
    MOVE.L    #_vbl,$000070.W
    MOVE.W    $00044E.W,D0

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

GroupOptions
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

FlagMeaning
-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-decisionsPopulate the rule column in --inspect regions with the deciding check
--normaliseLayout-invariant listing for A/B diffing (objects/archives; alias --normalize)
--jsonMachine-readable JSON envelope on stdout (all modes)
-q, --quietSuppress 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
--asciiDraw 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-dspScan Falcon GEMDOS binaries for embedded DSP code blocks and disassemble inline (on by default; --no-embedded-dsp disables)
--jump-tables, --no-jump-tablesReconstruct 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-labelsContextual 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-spacingInsert 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
--lowerEmit listing in lowercase mnemonics, registers, and directives (shorthand for --case lower)
--upperEmit listing in uppercase mnemonics, registers, and directives (shorthand for --case upper; default)
--spEmit SP / sp register alias instead of A7 / a7 for the stack pointer
--no-abs-parensOmit parentheses on absolute addresses (e.g. $FFFF8240.W vs ($FFFF8240).W)
--data-pack, --no-data-packPack 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-codeStrings: also report runs inside decoded instruction regions (default excludes them)
--bootsectorExtract + 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
--unpackAutomatically 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-targetEnable (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-equatesKeep unreferenced equates in the listing preamble (--no-unused-equates prunes them; default)
--annotate, --no-annotateEnable (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-trapsComment 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-basepageIdentify 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-stdlibIdentify 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-gfaIdentify GFA-BASIC runtime routines from GFA3BLIB / GFARUN and annotate call arguments (on by default; --no-annotate-gfa disables)
--annotate-hw, --no-annotate-hwComment 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-externsComment external symbol references and relocations in object files (on by default; --no-annotate-externs disables)
--annotate-vt52, --no-annotate-vt52Comment VT-52 terminal escape sequences in string data (on by default; --no-annotate-vt52 disables)
--annotate-cycles, --no-annotate-cyclesComment each instruction line with its right-aligned CPU cycle execution cost (off by default; --annotate-cycles enables)
--annotate-offsets, --no-annotate-offsetsComment 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-vdiComment and format VDI parameter blocks (VDIPB) and pointer tables (on by default; --no-annotate-vdi disables)
--annotate-aes, --no-annotate-aesComment and format AES parameter blocks (AESPB) and pointer tables (on by default; --no-annotate-aes disables)
--annotate-embedded-executables, --no-annotate-embedded-executablesFormat embedded GEMDOS executable headers ($601A) symbolically and decode entry jump vectors into code (on by default; --no-annotate-embedded-executables disables)
-V, --versionPrint version (honours --json)
-h, --helpFull 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 codeMeaningOutput streamNotes
0SuccessstdoutDisassembly listing or inspect summary emitted successfully
1Runtime errorstderr (or JSON stdout)Missing input files, parse failures, unreadable images (rg-dis: error: …)
2Usage errorstderrClap 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.

CommandNormal output (stdout)--json output (stdout)
Disassembly (default)Assembly source listingJSON envelope with listings[].text
--inspectFormatted summary tableJSON envelope with summaries[] metadata
--inspect symbols / relocs / …Formatted section tableJSON envelope with requested slices
--versionVersion textJSON envelope with version info
-o <FILE>Writes assembly file; prints summaryWrites assembly file; outputs JSON envelope
Runtime errorError message on stderr (exit 1)JSON error envelope on stdout (exit 1)
Usage errorUsage 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": []
}
FieldTypeDescription
schemanumberSchema version (currently 1)
toolstringAlways "rg-dis"
versionstringrg-dis version string
statusstring"ok" or "error"
errorobject?Error details when status is "error" (code, message)
commandobjectCommand-line options used for this run
outputsarrayOutput file details when -o is used
summariesarrayInspection metadata and section tables
listingsarrayDisassembly text and source identifiers
diagnosticsarrayDiagnostic 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):

ViewTOS / Raw binaryELF / DRI / a.out / GSTDSP (.P56 / .LOD / .OUT / Cooked)
(default)header, layout, countsheader (if present), sections, countsformat, entry, word counts
headerheader, prgflagsheaderformat, entry
sectionslayoutsections[]blocks[]
symbolssymbols[]symbols[]symbols[]
relocsrelocs[]relocs[]—
functionsfunctions[]functions[]—
cyclescycles[]cycles[]—
stringsstrings[]strings[]—
cpucpucpu—
diskdisk, geometry, selection, byte_span——
embedded-dspembedded_dsp[]——
allAll available viewsAll available viewsAll 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 }
}
FieldDescription
header.text_size / data_size / bss_sizeSection sizes on disk and in memory
header.sym_sizeEmbedded symbol table size in bytes
header.flagsRaw GEMDOS PRGFLAGS longword
prgflags.*Decoded Atari TOS memory and load flags
layout.*_start / *_endMemory 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
countsSummary 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:

FieldTypeDescriptionTerminal Table Mapping
textstringExtracted string contentstring column
addrnumberGuest address at run timeaddr column
segmentstringSection or segment name ("Text", "Data", etc.)seg column
bytes_lennumberRun length in byteslen column
referencedbooleanWhether a relocated pointer or code reference targets this addressref column (Y / -)
labelstring?Symbol name defined at this address, if anylabel column
file_offnumberRaw byte offset in the input file on diskExtended JSON metadata
regionstringLightweight classification ("Data", "Code", "Unclassified")Extended JSON metadata
termstringTerminator type ("Nul", "Eol", "Boundary", "Truncated")Extended JSON metadata
scorenumberPlausibility 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 ♥