C64 · 6502
acme
All 138 C64 curriculum sources assemble byte-identical to acme -f cbm.
Assemblers & disassemblers
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.binWhy
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
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
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
C64 · 6502
All 138 C64 curriculum sources assemble byte-identical to acme -f cbm.
NES · 6502 / 65816
Assembles and links a finished .nes ROM (iNES + PRG + CHR); all 51 NES sources match ca65 + ld65.
ZX Spectrum · Z80
The full documented Z80 plus the Next’s Z80N; all 161 Spectrum sources match both pasmonext and sjasmplus.
Amiga · 68000
Motorola syntax to a loadable hunk executable with --exe; all 69 Amiga sources match vasmm68k_mot.
Dragon / CoCo · 6809
The full 6809 including the computed indexed modes; validated against lwasm --6809 --raw.
Architecture
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.
Dependency-free declarative instruction-set specs — the encoding truth Asm198x and Emu198x share, now a neutral sibling project.
Spec-driven disassemblers that decode against isa198x without pulling in the assembler.
A cross-CPU debug-info sidecar — the assembler writes it, Emu198x reads it, and neither end owns the format.
The assembler library, dialect front-ends, shared engine, linked-output paths, and the CLI.
Install
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 --releaseSource-compatible
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
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
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.