Completion of Fabrice hard work on decompiling TurboModula-2 (CP/M version)
- Modula-2 96.2%
- Shell 2.2%
- Python 1.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docs/compiler | ||
| src/compiler | ||
| tools | ||
| .gitignore | ||
| README.md | ||
| SESSION.md | ||
| SUMMARY.md | ||
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
- Disassemble original
.MCDfiles (Reversing-Turbo-Modula2-main/, git-ignored upstream input) withunassemble.c. - Rewrite each module in readable Modula-2 (
src/compiler/), renamingglobalN/procN/localNpersrc/compiler/RENAME-MAP.md. - Recompile with unmodified Turbo Modula-2 v1.00 (
M2.COM+m2.ovr, run undercpmulator), driven hands-free bytools/m2drive.py+tools/chain.sh. - Disassemble our
.MCD, compare mnemonic streams (absolute jump targets masked,00gap bytes stripped, dep-slot renumbering accepted as benign). Score = identical procedures / total.
Layout
src/compiler/— renamed*.DEF/*.MODunder verification, plusRENAME-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 | prefix 7/14 exact, tail by construction (retail heap wall) | symtab-wip-04 |
| EXPRESS.MOD | per-proc ZZ: 12 exact + 6 near-exact (honest FIXMEs) | express-nested-01 |
| STATEMEN.MOD | per-proc ZZ: 13 exact + 6 near-exact, 19/20 + proc0 gap | statemen-verify-03 |
| PASS1.MOD | per-proc ZZ: 35/36 body-procs exact or near-exact (honest FIXMEs; proc36 keyword subsystem deferred, see docs/compiler/80-keywords.md) | pass1-verify-05 |
| 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 theMODscope; re-listing is a hard error.DEF/MODparameter names must match exactly.- No
CAST/VAL; noCARDINAL→BITSETassignment (useBITSET{n}literals). INneeds real sets; open-arrayVARaccepts 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. = 0emits unaryeq0;CHR(ORD())/ORD()/CHR()are zero-cost no-ops.OUT OF MEMORYoften masks type errors — isolate suspects in fresh-heap ZZ probes. Heap budget is per-proc nodes; ELSIF-nesting pins conditions.- SYM export slots count declarations, but REAL/LONGREAL globals consume extra slots (probed: Scanner.codeSize=22, tokPos=25, tokCol=26).
- Retail M2.COM is Version 1.00 (writes SYM v3); shipped originals carry SYM v2 (Borland-internal toolchain) — explains laxer original spellings.
Reproduce
./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:
PASS1 nesting moves + dead-tail pass + proc36 keyword subsystem, then
GENZ80/NATIVE).