Skip to main content

Extensible tracing disassembler for classic CPUs, with CPU, renderer and environment extension points.

Project description

Dasmos — an extensible tracing disassembler for classic CPUs

An extensible tracing disassembler for classic CPUs, version 1.14.0.

PyPI Release CI Documentation Python versions

Read the documentation »

From Ancient Greek δασμός (dasmós, "division"), from δαίω (daíō, "to divide, share").

Dasmos turns binary images — ROM dumps, cartridge contents, captured snapshots of memory — into readable, annotated assembly source. Starting from one or more entry points, the tracing core follows reachable code paths to classify which bytes are instructions and which are data, then renders the result via a chosen assembler-syntax back-end (or as structured JSON for downstream tooling). The round-trip property — binary → driver → assembly → binary where binary == binary — is checked in CI against real ROMs, so the disassembly is byte-faithful, not just plausible.

You drive Dasmos either as a one-shot CLI command or via a Python driver script that accretes labels, comments, data classifications, subroutine banners, and cross-references as you grow your understanding of the code; re-running the script regenerates the listing. CPUs, assembler-syntax back-ends, and target environments all ship as composable plug-ins, so adding support for a new chip family or a new assembler dialect is a self-contained extension, without needing to modify the original code.

Install

The uv and uvx commands shown below come from Astral's uv. If you don't have it yet, see the uv installation guide — one-line installers are available for macOS, Linux, and Windows.

For one-shot CLI use, no install needed — uvx fetches and runs in a transient environment:

uvx dasmos disassemble myrom.bin --load-addr '&8000'

To add dasmos to a project (required for driver scripts that import dasmos):

uv add dasmos

Or with pip:

pip install dasmos

Dasmos's round-trip / parity tests assemble back to bytes via beebasm; beebasm on PATH (or via the BEEBASM env var) activates them. CI builds beebasm from source per matrix cell so the round-trip is part of the gate.

Programmatic API

Every CLI capability is also reachable through the package. The typical driver-script flow is: pick a CPU plug-in, load a binary, register entry points / labels / classifications / annotations, disassemble, then render via a renderer plug-in.

from dasmos import Disassembler, Align

d = Disassembler.create(cpu="6502")
d.load("rom.bin", 0x8000)
d.entry(0x8000, name="start")
d.label(0x8006, "show", description="Display routine")
d.comment(0x8000, "Entry point.")
d.comment(0x8000, "magic", align=Align.INLINE)

ir = d.disassemble()
print(str(ir.render("beebasm")))

That produces beebasm-assemblable source. Re-assembling it via the real beebasm binary yields a binary byte-identical to the input — the load-bearing round-trip property Dasmos's test suite exercises against real Acorn ROMs (the 8 KB Econet Bridge and the 2 KB-mapped 6502 Tube Client both round-trip end-to-end with full py8dis annotation-content parity).

CLI

$ dasmos --help
Usage: dasmos [OPTIONS] COMMAND [ARGS]...

  An extensible tracing disassembler.

Options:
  --version  Show the version and exit.
  --help     Show this message and exit.

Commands:
  describe-cpu          Describe a specific CPU plug-in.
  describe-environment  Describe a specific environment plug-in.
  describe-renderer     Describe a specific renderer plug-in.
  disassemble           Disassemble ROM and write the rendered output.
  init                  Scaffold a starter dasmos driver at DRIVER_PATH.
  list-cpus             List the available CPU plug-ins.
  list-environments     List the available environment plug-ins.
  list-renderers        List the available renderer plug-ins.

The CLI commands inherit a uniform --as display | tsv | json story (plus --report, --header, --detailed) from asyoulikeit, so any command's structured output drives downstream tooling cleanly.

Discovering plug-ins

Two namespaces are populated by the bundled extensions; third-party packages register additional entries the same way.

$ dasmos list-cpus
                CPUs registered under 'dasmos.cpu'                
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Name  ┃ Description                                            ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ 6502  │ The classic NMOS 6502.                                 │
│ 65C02 │ The CMOS 65C02 — 8 extra mnemonics on top of the 6502. │
└───────┴────────────────────────────────────────────────────────┘
$ dasmos list-renderers
 Renderers registered under 'dasmos.renderer' 
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Name    ┃ Description                      ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ beebasm │ Beebasm-syntax renderer.         │
│ json    │ JSON structured-output renderer. │
└─────────┴──────────────────────────────────┘
$ dasmos list-environments
               Environments registered under 'dasmos.environment'               
┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Name                   ┃ Description                                         ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ acorn_fdc_1770         │ Acorn WD1770 floppy-disc-controller Environment.    │
│ acorn_fdc_8271         │ Acorn 8271 floppy-disc-controller Environment.      │
│ acorn_master_hardware  │ Acorn BBC Master hardware-register Environment.     │
│ acorn_model_b_hardware │ Acorn BBC Model B / B+ hardware-register            │
│                        │ Environment.                                        │
│ acorn_mos              │ Acorn MOS environment.                              │
│ acorn_sideways_rom     │ Acorn sideways ROM environment.                     │
│ bbc_basic_6502         │ Registers BBC BASIC (6502) language-specific data   │
│                        │ types.                                              │
└────────────────────────┴─────────────────────────────────────────────────────┘

Environments layer onto a disassembler additively — a driver can activate any number of them, in either the constructor's environments=[…] kwarg or via repeated d.use_environment(…) calls.

describe-cpu (and the matching describe-renderer) shows the full docstring of a single plug-in:

$ dasmos describe-cpu 6502
6502: The classic NMOS 6502.

16-bit address space; the 56 documented mnemonics across 13
addressing modes; 151 documented opcodes (undocumented opcodes
deliberately omitted).
$ dasmos list-cpus --help
Usage: dasmos list-cpus [OPTIONS]

  List the available CPU plug-ins.

  Produces reports:
    cpus  Registered CPU (processor) plug-ins with one-line descriptions.

Options:
  Report Output Options: 
    --no-reports              Suppress all report output. The handler still runs
                              (useful for action commands whose reports are
                              incidental); only rendering is skipped. Mutually
                              exclusive with --report and --all-reports.
    --all-reports             Render every report the handler returns,
                              regardless of the command's default_reports.
                              Useful for commands whose default is a subset (or
                              silent) but where you want the full picture this
                              time. Mutually exclusive with --report and --no-
                              reports.
    --report [cpus]           Report name(s) to display (can be specified
                              multiple times). Shows all if omitted. Valid
                              values: cpus.
    --header / --no-header    Include column headers in output. Overrides each
                              report's default. Format-specific: TSV prefixes
                              first cell with '#', display omits
                              headers/title/caption, JSON ignores this flag.
    --detailed / --essential  Include detailed columns or only essential
                              columns. Auto-detects based on output format if
                              not specified.
    --as [display|json|tsv]   Output format for tabular data. Defaults to
                              'display' for terminals, 'tsv' for pipes.
  --help                      Show this message and exit.

Migrating a py8dis driver

scripts/py8dis2dasmos.py is an AST-based porter that translates a py8dis driver script into the equivalent Dasmos call shape:

uv run python scripts/py8dis2dasmos.py path/to/disasm_<rom>.py > ported.py

It rewrites the wildcard import, swaps load(addr, file, cpu) order, maps move()d.add_move(...), threads inline=Truealign=Align.INLINE, recognises subroutine(..., is_entry_point=False) as a label-plus-banner pair, expands hook_subroutine and the bundled hooks (stringhi_hook, …) through dasmos.hooks, and configures the render() call so the output matches py8dis's defaults (boundary_label_prefix='pydis_', byte_column=True).

Every transformation is covered by unit tests, plus a load-bearing end-to-end test that ports the unmodified Econet Bridge driver and asserts the resulting beebasm source re-assembles to the original ROM bytes.

Testing

pytest -v runs the suite. Tests marked @pytest.mark.beebasm auto-skip when beebasm isn't on PATH; the rest run anywhere Python and uv are installed. CI exercises the full matrix (ubuntu/macos/windows × earliest+latest declared Python) against the installed wheel, not the source tree, so packaging regressions (missing entry points, omitted py.typed markers, unshipped sub-packages) fail loud.

Layout

src/dasmos/                 the package
src/dasmos/cli.py           Click entry point + asyoulikeit reports
src/dasmos/disassembler.py  Disassembler (driver-script API)
src/dasmos/core/            Memory / labels / moves / classifications
src/dasmos/cpu.py           Cpu base + Opcode shape
src/dasmos/renderer.py      Renderer base
src/dasmos/ext/cpus/        Bundled CPU plug-ins (cpu6502, cpu65c02)
src/dasmos/ext/renderers/   Bundled renderer plug-ins (beebasm)
src/dasmos/hooks.py         Subroutine hooks (stringhi_hook, …)
scripts/py8dis2dasmos.py    py8dis → dasmos AST porter
scripts/generate_readme.py  This README's generator
docs/design/                Architecture decisions & sweep memos
tests/                      Unit + round-trip + py8dis-parity tests
tests/fixtures/             Vendored ROM + driver + reference output

Related projects

  • py8dis (fork) — the predecessor Dasmos is replacing. Driver scripts written against this fork port via scripts/py8dis2dasmos.py.
  • The four sibling Acorn ROM disassembly repositories under the acornaeology umbrella that drive Dasmos's round-trip / parity validation: acorn-econet-bridge, acorn-6502-tube-client, acorn-nfs, acorn-adfs.
  • beebasm — the BBC-Micro-style assembler used as the round-trip oracle.
  • asyoulikeit — the CLI-output framework Dasmos's reports are built on.

Lineage, Credit and Acknowledgements

Dasmos is a ground-up rewrite and reimagining of a heavily modified fork of py8dis — Steven Flintham's original programmable tracing disassembler for the 6502 family. Dasmos owes the whole core idea to Steven and to the py8dis project; this project organises a tracing disassembler as a core algorithm customised through plug-in extensions which provide knowledge of CPUs, different assembly syntaxes, and target environments. The core of the essential design vocabulary — driver scripts, traced classification, label/comment/banner annotations — is all inspired by py8dis.

py8dis was itself heavily influenced by Phill Harvey-Smith's BeebDis, an earlier 6502 disassembler for the BBC Micro ecosystem; py8dis modelled its command surface on BeebDis where it could. Dasmos therefore stands at the end of a chain of work — BeebDis → py8dis → the acornaeology fork → Dasmos — and gratefully acknowledges the contribution each step made to the next.

Driver scripts written for py8dis can be ported automatically to Dasmos with the bundled scripts/py8dis2dasmos.py.


This README is generated from scripts/readme_template.md.j2 by scripts/generate_readme.py. Do not edit it directly — edit the template (or the generator, or the source files whose output it captures) and re-run uv run python scripts/generate_readme.py. The pre-commit hook and the readme-check CI job both run the generator's --check mode and will refuse stale READMEs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dasmos-1.14.0.tar.gz (295.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dasmos-1.14.0-py3-none-any.whl (200.5 kB view details)

Uploaded Python 3

File details

Details for the file dasmos-1.14.0.tar.gz.

File metadata

  • Download URL: dasmos-1.14.0.tar.gz
  • Upload date:
  • Size: 295.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for dasmos-1.14.0.tar.gz
Algorithm Hash digest
SHA256 a20e30181ca38a724fd9f06f93ef2c7425d10eb09bb88a70e1b133f9420b9adf
MD5 c6fc0e31dfc66114317841430e0c803e
BLAKE2b-256 c29ff64ce613d30cc14a5e376168a4085cdfac9411220b4583af3544ff5c5180

See more details on using hashes here.

File details

Details for the file dasmos-1.14.0-py3-none-any.whl.

File metadata

  • Download URL: dasmos-1.14.0-py3-none-any.whl
  • Upload date:
  • Size: 200.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for dasmos-1.14.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1e4ea2512d5f3d3f6de779fa64b3e9ea179a3f648012f7b91def342c7a477fcd
MD5 775fb5c71339af3a8ff1ec1e68fd7a32
BLAKE2b-256 83f8d9b05470ff098dfd80003793d74c2bf68b55678003400bd874507a6916e0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page