# Turbo Modula-2 — Compiler Reverse Engineering Reverse-engineering the Turbo Modula-2 v1.00 compiler into readable, renamed Modula-2 sources, verified by recompiling with the **original v1.00 toolchain** and diffing the resulting `.MCD` binaries procedure by procedure against the originals. ## Method 1. Disassemble original `.MCD` files (`Reversing-Turbo-Modula2-main/`, git-ignored upstream input) with `unassemble.c`. 2. Rewrite each module in readable Modula-2 (`src/compiler/`), renaming `globalN`/`procN`/`localN` per `src/compiler/RENAME-MAP.md`. 3. Recompile with unmodified Turbo Modula-2 v1.00 (`M2.COM` + `m2.ovr`, run under `cpmulator`), driven hands-free by `tools/m2drive.py` + `tools/chain.sh`. 4. Disassemble our `.MCD`, compare mnemonic streams (absolute jump targets masked, `00` gap bytes stripped, dep-slot renumbering accepted as benign). Score = identical procedures / total. ## Layout - `src/compiler/` — renamed `*.DEF`/`*.MOD` under verification, plus `RENAME-MAP.md` (canonical names), `FRONTEND.md` (export map with MCode offsets), `GENZ80.md`. - `docs/compiler/` — module analyses (`00-overview` … `70-rename-plan`), `disasm/` (backend disassemblies), `TAGGED-SUMMARY.md`. - `tools/` — `m2drive.py` (pty driver for the M2 shell), `chain.sh` (fresh-boot-per-file batch compiles), `mcdiff.sh` (stage-1 static checks). - `tmp/` — local CP/M working drives and scratch (git-ignored, never commit). - `SESSION.md` — running log + next-session plan. `SUMMARY.md` — milestones. - Upstream inputs (`Reversing-*/`, `Turbo-*Reloaded*/`, CP/M images) stay git-ignored; only analysis + renamed sources are versioned. ## Verification status (Oct 2026) | module | result | tag | |---|---|---| | COMPILER.MOD | 18/19 byte-identical (linkage names only) | stage2-v1 | | SCANNER.MOD | 34/41 mnemonic-identical | scanner-verify-01 | | ERRORS.MOD | 28/30 mnemonic-identical | errors-verify-01 | | CODEGEN.MOD | 34/37 mnemonic-identical | codegen-verify-01 | | SYMTAB.MOD | WIP: compiles through GetAttr, wall in EnterIdent | symtab-wip-02 | | EXPRESS/STATEMEN/PASS1/GENZ80/NATIVE | drafts, unverified | *-draft-v1 | Remaining diffs are all accounted for in `SESSION.md` (clean-room Z80 replacements, forced literal folding, VarAddr shim, gap-filler/FATDIR divergences, trailing address tables). ## v1.00 toolchain rules (all proven on hardware via ZZ probes) - `DEF`-imported names leak into the `MOD` scope; re-listing is a hard error. - `DEF`/`MOD` parameter names must match exactly. - No `CAST`/`VAL`; no `CARDINAL→BITSET` assignment (use `BITSET{n}` literals). - `IN` needs real sets; open-array `VAR` accepts any variable (VarAddr trick). - Transfer results can't be indexed/dereferenced (`Words(x)^[i]`, `P(x)^.f`) — retype params to view types, transfer (zero-cost) at call sites. - `= 0` emits unary `eq0`; `CHR(ORD())`/`ORD()`/`CHR()` are zero-cost no-ops. - `OUT OF MEMORY` often masks type errors — isolate suspects in fresh-heap ZZ probes. Heap budget is per-proc nodes; ELSIF-nesting pins conditions. - Retail M2.COM is Version 1.00 (writes SYM v3); shipped originals carry SYM v2 (Borland-internal toolchain) — explains laxer original spellings. ## Reproduce ```sh ./tools/mcdiff.sh # stage-1 static checks (no VM needed) ./tools/chain.sh tmp/stage/drive SCANNER.MOD # compile under v1.00 tmp/stage/unassemble tmp/stage/drive/SCANNER.MCD # then diff vs original ``` See `SESSION.md` → “Next session plan” for what comes next (currently: SYMTAB per-proc loop, then EXPRESS → STATEMEN → PASS1).