# Inventory of `disasm_tools/` across the four sibling projects Living document. Captures the state of the four `disasm_tools/` packages at the time of fantasm's initial consolidation, and notes the strategy for each module. | Project | path | |----------------------|----------------------------| | acorn-6502-tube-client | `src/disasm_tools/` | | acorn-adfs | `src/disasm_tools/` | | acorn-econet-bridge | `src/disasm_tools/` | | acorn-nfs | `src/disasm_tools/` | All four packages share a near-identical surface, but `acorn-nfs` has absorbed the most additions over time and is the working baseline for most modules. A handful of modules exist in only one fork. ## Per-module status Line counts and diff-line counts (versus NFS) collected on 2026-05-01. | Module | NFS | ADFS | EBR | TUBE | Status | Port | |------------------|----:|-----:|----:|-----:|---------------------------------------------|------| | `__init__.py` | 0 | 1 | 1 | 1 | Trivial | ✅ done | | `mos6502.py` | 152 | 97 | 98 | 99 | NFS adds CMOS (65C02) overrides + dispatcher | ✅ done — ROM_BASE/ROM_SIZE moved out, set up for `[rom] cpu` config | | `paths.py` | 28 | 32 | 32 | 36 | Project-specific PREFIX; NFS supports two | ✅ done — refactored against ProjectContext; `[versions]` config | | `asm_extract.py` | 105 | 100 | 100 | 100 | NFS has 5 extra lines (relocation indexing) | ✅ done — typed extract_section() returning AsmSection | | `audit.py` | 828 | 828 | 828 | 828 | **Byte-identical** | ⚠️ partial — pure-logic + load_subroutines + find_undeclared_subs ported; `format_*` and top-level `audit()` deferred to CLI | | `backfill.py` | 621 | 621 | 621 | 621 | **Byte-identical** | ⚠️ partial — build_confidence_map + parse_annotations + translate_* ported; VERSION_CHAIN and chained_map deferred (NFS-specific data → fantasm.toml) | | `cfg.py` | 415 | 415 | 415 | 415 | NFS = ADFS = TUBE; EBR has 4 diff lines | ⚠️ partial — build_call_graph + resolve_sub_node ported with KEY FIX (uses audit.build_memory_regions(meta), not hardcoded NFS regions); format_* deferred | | `comment_check.py` | 677 | 677 | 677 | 677 | **Byte-identical** | ⚠️ partial — every check function + run_checks ported; format_findings + comment_check() deferred | | `compare.py` | 470 | 319 | 319 | 319 | NFS has ~150 extra lines of capability | ✅ done — Instruction/RomHeader dataclasses, parse_rom_header, disassemble_linear, compare_roms; rom_base parameterised, "NFS" hardcode removed | | `context.py` | 459 | 459 | 459 | 459 | **Byte-identical** | ⚠️ partial — compute_call_depths ported; generate_context (file IO) deferred | | `insert_point.py`| 183 | 183 | 183 | 183 | **Byte-identical** | ⚠️ partial — parse_subroutine_declarations + find_main_block + new compute_insert_point ported; print/IO wrapper deferred | | `labels.py` | 495 | 495 | 495 | 495 | **Byte-identical** | ⚠️ partial — classify_labels + sort_labels + collect_auto_labels (was `_collect_auto_labels`); generate_labels file-IO deferred | | `lint.py` | 555 | 522 | 522 | 492 | NFS most capable; check others for unique rules | ⚠️ partial — extract_annotations (multi-line aware, with names) + valid_addresses_from_data + address_ranges_from_data + markdown helpers ported; ROM size now a parameter; lint_*() and lint() top-level deferred | | `rename_labels.py`| 284 | 284 | 284 | 284 | **Byte-identical** | ⚠️ partial — parse_label_declarations + find_rename_section + find_insert_position + new apply_renames_to_lines pure transformer; rename_labels() and show_sub_labels() file-IO deferred | | `verify.py` | 102 | 102 | 102 | 112 | Trivial diffs (project-name strings); TUBE has 24 extra | ✅ done — verify_round_trip() returns VerifyResult dataclass; BeebasmNotFoundError raised | | `cli.py` | 417 | 401 | 445 | 358 | All diverge — Click sub-commands | ⏳ in progress — `fantasm project` group landed; rest under design | | `promote.py` | – | 235 | 235 | – | ADFS+EBR identical; not in NFS or TUBE | ⚠️ partial — analyze_labels + load_and_analyze_labels ported; format_promote_report deferred to CLI | | `find_shared.py` | – | – | 254 | – | EBR only | ⚠️ partial — sweep_opcodes / find_matching_spans / parse_rom_spec / load_rom ported; report_matches + argparse main deferred | | `blockmatch.py` | 299 | – | – | – | NFS only | ✅ done — primary + supplementary opcode mapping; rom_base parameterised | | `fingerprint.py` | 112 | – | – | – | NFS only | ✅ done — opcode_fingerprint, fingerprint_blocks, find_duplicate_blocks | ### Porting strategy that emerged For each ported module the same shape recurs: lift the pure-logic surface into `fantasm.api.` with type hints, frozen dataclasses for results, typed exceptions instead of `sys.exit`/stderr-print, and `warnings.warn` for soft diagnostics. Tests are characterisation tests against hand-crafted in-memory data — small enough to read, complete enough to pin behaviour for refactors. The `format_*` and file-reading top-level entry functions (the CLI side) are intentionally deferred for each module. They will land alongside the `fantasm ` Click sub-commands in a follow-up pass, where the project-specific strings and `rom_prefix(version_dirpath)` calls can be threaded through a `ProjectContext` once. Legend: NFS = `acorn-nfs`, ADFS = `acorn-adfs`, EBR = `acorn-econet-bridge`, TUBE = `acorn-6502-tube-client`. Em-dash (`–`) means the module is absent in that fork. ## Cross-cutting refactors A handful of concerns appear in multiple modules and need to be lifted out during consolidation: - **Project layout assumptions.** `paths.py` knows the directory naming convention (`{PREFIX}-{version_id}/`) and the ROM-name prefix. fantasm expresses these in `fantasm.toml` instead of hardcoded module constants. - **ROM bank constants.** `ROM_BASE` and `ROM_SIZE` live in `mos6502.py` today, but they are properties of how a ROM was *banked*, not of the 6502. They move into `fantasm.toml` (and a future `fantasm.api.rom` module) so the same disassembly tools can target the BBC's 16K sideways ROMs, the 8K NFS sideways "half-bank", and other layouts. - **`SystemExit` from library code.** Several siblings call `sys.exit(1)` on error inside library functions (e.g. `paths.resolve_version_dirpath`). The fantasm port raises a typed exception (e.g. `VersionNotFound`) and the CLI layer translates it to a clean exit code with a useful message. - **Console-script names.** Sibling code references the project-specific `acorn-{x}-disasm-tool` console-script name in error messages. fantasm uses `fantasm`. ## Porting order Foundational first, so later modules can build on a stable base: 1. `mos6502` (no deps; opcode tables are the bedrock) 2. `paths` (refactored against `ProjectContext` + `fantasm.toml`) 3. `labels` (used by many) 4. `comment_check` 5. `compare` 6. `audit`, `lint`, `verify` 7. `cfg`, `context`, `insert_point` 8. `rename_labels`, `asm_extract`, `backfill` 9. Specialised: `blockmatch`, `fingerprint`, `promote`, `find_shared` CLI integration happens alongside each module: a `fantasm ` group is added with the relevant sub-commands, and the public surface is re-exported from `fantasm.api.__init__` so callers can either `from fantasm.api import …` or reach into `fantasm.api.`. Tests are characterisation tests: small hand-crafted inputs whose expected output is captured from the working code, plus round-trip tests against `beebasm` for any module that emits assembly source.