|
|
@@ -0,0 +1,73 @@
|
|
|
+# 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).
|