Assemblers & disassemblers

Asm198x

One modern, statically-linked toolchain for the retro CPUs of the 198x era — source-compatible with the dialects you already use, validated byte-for-byte against the reference tools, built to still run in ten years.

# one binary, the dialects you already use
$ asm198x --dialect acme  countdown.s -o countdown.bin
$ asm198x --dialect vasm  --exe game.s -o game
$ asm198x --dialect ca65  game.asm -o game.nes
$ asm198x --disasm --dialect 6502 --org 0x0200 game.bin

Why

One toolchain, not a museum of dead binaries.

The period assemblers are dead-OS binaries — you need DOSBox or a full emulator just to invoke them. The modern community tools mostly work, but they're fragmented: a different tool, dialect, and build dance per machine, much of it unmaintained C. Asm198x is one statically-linked, cross-platform toolchain that spans the family's CPUs. The rule is source-compatibility — real source assembles unchanged, and output is checked byte-for-byte against the reference tools. Rescue beats replace.

Convert

Move source between dialects, with proof.

Decades of code is written for one assembler's dialect. Because Asm198x parses many dialects of a CPU into one source-preserving tree, it can re-emit a program in a different one — labels, names, and comments intact. The conversion is self-verifying: it assembles the input under the source dialect and the output under the target dialect, and emits nothing unless the two images are byte-identical. What you see below is real output, and the same check also holds under real pasmo and real sjasmplus.

# flash.asm — pasmo
        org 8000h

border  macro colour
        ld a, colour
        out (0feh), a
        endm

; flash the border, then rest
start:  border 2
        rept 8
        halt
        endm
        border 5
        ret
$ asm198x convert --from pasmo --to sjasmplus flash.asm
        org 8000h
border  macro colour
        ld a, colour
        out (0feh), a
        endm
; flash the border, then rest
start:  border 2
        rept 8
        halt
        endr
        border 5
        ret
converted flash.asm (pasmo -> sjasmplus, verified byte-identical)

Look at the two highlighted ENDMs: the same word, twice. One closes a macro, one closes a REPT — and sjasmplus refuses that second spelling, so the converter rewrites the repetition's closer to ENDR and leaves the macro's alone. That is a decision made from the program's structure, not a search-and-replace — and if it ever gets one wrong, the byte-compare refuses to emit anything at all. Today it converts pasmo and pasmonext source to sjasmplus; more pairs are tracked. See the CLI reference.

The CPU surface

Broad 8- and 16-bit coverage, today.

Source-compatible front-ends and spec-driven disassemblers across these instruction sets — each a declarative spec that both the assembler and the disassembler share.

Front doors

Match a dialect, not a house syntax.

C64 · 6502

acme

All 138 C64 curriculum sources assemble byte-identical to acme -f cbm.

NES · 6502 / 65816

ca65

Assembles and links a finished .nes ROM (iNES + PRG + CHR); all 51 NES sources match ca65 + ld65.

ZX Spectrum · Z80

pasmo / sjasmplus

The full documented Z80 plus the Next’s Z80N; all 161 Spectrum sources match both pasmonext and sjasmplus.

Amiga · 68000

vasm

Motorola syntax to a loadable hunk executable with --exe; all 69 Amiga sources match vasmm68k_mot.

Dragon / CoCo · 6809

lwasm

The full 6809 including the computed indexed modes; validated against lwasm --6809 --raw.

Architecture

Four crates, split only where a boundary is real.

The engine ↔ dialect ↔ spec seam is what lets one binary span many CPUs and dialects: a dialect is a front-end module, a CPU is a spec, and the disassembler decodes against the same specs the assembler emits from. Three of the boundaries proved real enough to leave the building: the specs and the debug format now live in sibling projects — Isa198x and Debug198x — because Emu198x consumes them too, and a thing two tools share is owned by neither.

isa198x

Dependency-free declarative instruction-set specs — the encoding truth Asm198x and Emu198x share, now a neutral sibling project.

isa198x-disasm

Spec-driven disassemblers that decode against isa198x without pulling in the assembler.

debug198x

A cross-CPU debug-info sidecar — the assembler writes it, Emu198x reads it, and neither end owns the format.

asm198x

The assembler library, dialect front-ends, shared engine, linked-output paths, and the CLI.

Install

Grab a binary, or build it.

Asm198x ships as a prebuilt, statically-linked binary on GitHub Releases for macOS, Linux, and Windows — no runtime, no toolchain. Or build it from source with a single cargo build; the CLI is featherweight, with no GUI or graphics dependencies.

# prebuilt binaries
github.com/asm198x/asm198x/releases

# or build from source
git clone https://github.com/asm198x/asm198x
cd asm198x && cargo build --release

Source-compatible

The dialect you already use.

Each front-end matches an existing assembler's syntax where compatibility matters — acme, ca65, pasmo, sjasmplus, vasm, lwasm. Real-world source assembles unchanged; there's no house syntax to relearn.

Validated

Byte-for-byte against the references.

Output is checked against the period-correct reference tools and frozen fixtures, per CPU and per dialect — and where a disassembler exists, assemble → disassemble → reassemble round-trips stay identical.

Built to last

One binary, spanning the family.

A single statically-linked, cross-platform toolchain covers the whole CPU surface — no per-machine build dance, no dead-OS binaries, documented to still run in ten years.