Selaa lähdekoodia

docs(compiler): outline unreadable procN/globalN, rename plan

Resident Compiler/Scanner/SymTab/Errors/CodeGen + transient
COMPILE=Pass1/Statemen/Express. Upstream dirs git-ignored.
Eric Streit 1 viikko sitten
commit
c7ba734cc4

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+# Upstream / vendored sources — do not commit
+# These are the original reverse-engineering inputs, kept locally only.
+Reversing-Turbo-Modula2-main/
+Turbo-Modula-2-Reloaded-main/
+*.dsk
+*.MCD
+*.SYM
+*~

+ 62 - 0
docs/compiler/00-overview.md

@@ -0,0 +1,62 @@
+# Turbo Modula-2 — Compiler outline
+
+Target: give meaning back to `procN` / `globalN` in the
+`Reversing-Turbo-Modula2-main/decompiled/COMPILER/` sources,
+which compile but are unreadable.
+
+Status: outline only, no semantic change to upstream sources.
+Upstream folders are git-ignored (see `../.gitignore`);
+only `docs/compiler/` is versioned.
+
+## Layout
+
+```text
+docs/compiler/
+  00-overview.md        this file, map + reading order
+  10-folders.md         what each upstream folder contains
+  20-compiler-module.md Compiler.MOD/DEF: Record descriptor + Init procs
+  30-scanner-module.md  Scanner: buffer, GetSym, Accept/Expect/Test
+  40-symtab-errors.md   SymTab scopes + Errors reporting/recovery
+  50-codegen.md         CodeGen MCode emitter + 2KB window + fixups
+  60-frontend.md        COMPILE link: Pass1 + Statement + Express (no .MOD yet)
+  70-rename-plan.md     ordered rename work plan procN/globalN -> real names
+  TAGGED-SUMMARY.md     one-page tagged snapshot for `git tag`
+```
+
+## Compiler shape (recall)
+
+Resident, overlaid in M2.COM:
+
+* `Compiler` + `Scanner` + `SymTab` + `Errors` + `CodeGen`
+
+Dynamically loaded via `Loader.Call`:
+
+* `COMPILE` = link of `Pass1` + `Statemen` + `Express`
+* `GENZ80` = native-code backend (if requested)
+
+Drivers:
+
+* `C.MOD` (`MODULE M2C`): `ReadString -> inName -> Scanner.Compile`
+* `Scanner.Compile` (`proc30`): `MARK, Compiler.proc2, Call("COMPILE")`,
+  then `Call("GENZ80")` if `Compiler.global25`
+* `ComLine`: `commandLine, inName, outName, codepos, execute`
+
+## Reading order (suggested)
+
+1. `20-compiler-module.md` — the `Record` descriptor unlocks everything.
+2. `30-scanner-module.md` — token codes, `GetSym/Accept/Expect`.
+3. `50-codegen.md` — opcode table + `Emit/Peek/Poke/Fixup`.
+4. `40-symtab-errors.md` — scopes, `.SYM` import/export, error recovery.
+5. `60-frontend.md` — parsers that consume 1-4.
+6. `70-rename-plan.md` — execute renames in that dependency order.
+
+Note on sources: `decompiled/COMPILER/*.{DEF,MOD}` are CP/M text
+(`CRLF + 0x1A`). Plain readers fail; use:
+
+```sh
+tr -d '\032\r' < Reversing-Turbo-Modula2-main/decompiled/COMPILER/SCANNER.MOD | less
+```
+
+`Turbo-Modula-2-Reloaded-main/disk_content/COMPILER.DIR/SCANNER.MOD`
+is a stub (30x `PROCnn; BEGIN END`); the real logic is in the
+`Reversing-.../MCode_disassembly/*.txt` disassemblies.

+ 41 - 0
docs/compiler/10-folders.md

@@ -0,0 +1,41 @@
+# Folder analysis
+
+Working dir: `Turbo-modula2/` (this repo, newly initialised).
+Upstream inputs below are **local only**, listed here for reference,
+ignored by git.
+
+## `Reversing-Turbo-Modula2-main/`
+
+* `README.md:1-11` — method: Z80 disasm -> MCode spec -> MCode disasm ->
+  manual decompile. Kernel/shell/editor/syslib fully decompiled,
+  compiler only partially.
+* `Z80_disassembly/m2_z80.txt` — raw Z80 (MCode interpreter + fast paths).
+* `MCode_specification/` — MCode opcode spec as a Modula-2 interpreter.
+* `MCode_disassembly/` — output of `unassemble.c`:
+  `m2_mcd.txt` (13669 lines, whole system),
+  `compiler.txt` (1350), `scanner.txt` (1901), `symtab.txt` (2365),
+  `errors.txt` (1421), `codegen.txt` (1746),
+  `compile.txt` (74, link stub), `pass1.txt` (2975),
+  `statement.txt` (1793), `express.txt` (3898),
+  plus `shell/editor/files/texts/...` for the rest of the system.
+* `decompiled/` — `KERNEL/`, `SHELL/`, `EDITOR/`, `SYSLIB/` usable;
+  `COMPILER/` = `README.md` + `COMPILER|SCANNER|ERRORS|CODEGEN.{DEF,MOD}`
+  in the "works but `procN/globalN`" state this project addresses.
+  No `SYMTAB.MOD`, no `PASS1/STATEMEN/EXPRESS.MOD` yet.
+* `unassemble.c` — the disassembler itself.
+
+## `Turbo-Modula-2-Reloaded-main/`
+
+* `README.md` — pedagogical 64KB system: MCode VM (`vm_linux/`) + FAT32
+  `m2.dsk`. Boot with `./m2_disk m2.dsk`.
+* `m2.dsk` + `disk_content/` — same tree as the disk image.
+* `disk_content/COMPILER.DIR/` — buildable compiler:
+  `COMPILER|SCANNER|ERRORS|CODEGEN|SYMTAB.{DEF,MCD,SYM}`,
+  `ERRORS1.MOD`, `C.MOD` (driver `MODULE M2C`), `M2.MCD`,
+  `COMPILE.DIR/{COMPILE,PASS1,STATEMEN,EXPRESS}.MCD`.
+  `SCANNER.MOD` here is a stub; `COMPILER.MOD/ERRORS.MOD/CODEGEN.MOD`
+  mirror the `Reversing-.../decompiled/` versions.
+
+## This repo (`docs/compiler/`)
+
+Only this is committed. Everything else is input data.

+ 67 - 0
docs/compiler/20-compiler-module.md

@@ -0,0 +1,67 @@
+# Compiler module (`COMPILER.DEF/MOD`, `compiler.txt` 1350 lines)
+
+Core idea: `Compiler` owns the **symbol descriptor** and the
+predefined environment. Everything else (`Scanner/SymTab/Express/...`)
+allocates and links these nodes.
+
+## `Record / RecordPtr / ArrayPtr`
+
+```modula2
+RecordPtr = POINTER TO Record;
+Record = RECORD
+  word0: RecordPtr;  (* scope chain link / backlink *)
+  word1: RecordPtr;  (* name string *)
+  word2: RecordPtr;  (* type / return type *)
+  word3: BITSET;     (* kind: 64 const/type, 192 proc, 256 indexed var, 1089 module *)
+  word4: CARDINAL;   (* node class 0..11, module class 6 *)
+  word5: WORD;       (* const value / stdproc no. / elem type *)
+  high:  WORD;       (* max / param list / scope list *)
+END;
+ArrayPtr = POINTER TO ARRAY [0..5] OF WORD; (* param lists *)
+```
+
+`PROC15(cnt,ret,first)` builds `p^[0]=cnt, p^[1]=ret, p^[2]=first`
+into `global35` (last list); caller patches `global35^[3..5]`.
+
+## Globals (proposed names)
+
+| old | proposed | role |
+|---|---|---|
+| global2 | `rangeCheckOpt` | `2 IN Scanner.global2` |
+| global3/4 | `curScopeAnchor/outerScope` | scope list heads |
+| global5 | `keywordHashTab` | `PROC13` table (730,731,1245,...) |
+| IntType/CardType/AddressType/RealType | same | `PROC9/PROC14` |
+| LongintType/LongrealType/CharType/BooleanType/BitsetType/ProcType | same | — |
+| WordType/ByteType/global17 | same / `addrAlias` | `BYTE^.word2:=WORD` |
+| global20/21 | `systemMod/storageMod` | module descriptors (`word4=6,word3=1089`) |
+| global22 | `allocSig` | `PROC15(2,2,CARD)+ADDRESS` |
+| global23/24 | `systemScope/storageScope` | `word0=global4,word1=list` |
+| global25/26 | `genZ80Requested/inCompile` | `FALSE/TRUE` init in `PROC1` |
+| global27-30 | (reserved Pass1/Stmt/Expr temps) | — |
+| global31/32/33 | `curScope/saveScope/baseScope` | insertion head |
+| global34 | `bitsetRange` | `PROC8(1,1),word2=CARD,high=15` |
+| global35 | `lastParamList` | see `PROC15` |
+| global12 | `charElemDesc` | `PROC8(0,11),word5=CHAR` |
+
+## Procedures (proposed names)
+
+| old | proposed | behaviour |
+|---|---|---|
+| PROC3 | `InsertIdent` | `Scanner.proc15+proc16`, chain on `global31` |
+| PROC4 | `DeclConst` | `TRUE/FALSE/NIL, IORESULT/HLRESULT, ...` |
+| PROC5 | `DeclStdProc` | `CAP/HALT/TRUNC/BREAK/TRANSFER/...` |
+| PROC6 | `DeclProc` | `READ/WRITE/NEW/DISPOSE/INC/DEC/INCL/EXCL/CODE`, `word3:=192` |
+| PROC7 | `DeclFunc` | `CARD/INT/LONG/FLOAT/ABS/.../ADR/TSIZE` + return type |
+| PROC8 | `NewTypeDesc` | `Scanner.proc14` + `word3:=size` |
+| PROC9 | `DeclType` | `INTEGER/CHAR/BOOLEAN/CARD/REAL/LONGREAL/BITSET/PROC/...`, double-linked |
+| PROC10/11 | `InitOperators` | fills `ERRORS.global3` (`:=`, `..`, `<=`, `>=`, `<>`, `(.`, `.)`, `(:`, `:)` ) |
+| PROC12 | `InitCharClass` | `A-Z->10, 0-9->11, ...` into `Scanner.global24` |
+| PROC13 | `InitKeywords` | 30 hash codes into `global5` |
+| PROC14 | `DeclarePredefTypes` | all base types + `PROCESS`, saves `global32/33` |
+| PROC15 | `MakeParamList` | see above |
+| PROC16 | `DeclareStdNames` | `TRUE/FALSE/NIL` + std procs/funcs + `global4` scope |
+| PROC17 | `DeclareSYSTEM` | `IORESULT/HLRESULT`, `CODE/ADR/TSIZE/.../BDOS/BIOS/MOVE/FILL/INP/OUT`, builds `global20/23` |
+| PROC18 | `DeclareSTORAGE` | `ALLOCATE/DEALLOCATE/MARK/RELEASE/FREEMEM`, builds `global21/24` |
+| PROC1 | `InitCompiler` | `PROC10/12/13/14/16/17/18`, `%L` option, `FREEMEM` print, `SymTab` allocs |
+| GETINP | `OpenSourceOutput` | `Files.Open(inName)`, `RedirectOutput`, `@@@` output, `BDOS 29`, `UseError` handler |
+| proc19-28 | imported | actually `Scanner.proc7/14/15/16/17/25/28`, `SymTab`, `Texts` — not real `Compiler` procs; the `COMPILER.DEF` `proc1..28` list is an obfuscated stub |

+ 57 - 0
docs/compiler/30-scanner-module.md

@@ -0,0 +1,57 @@
+# Scanner (`scanner.txt` 1901 lines, `SCANNER.DEF/MOD`)
+
+Lexer + source window + tiny allocators. All parsers call
+`GetSym/Accept/Expect/Test`.
+
+## State
+
+| old | proposed | role |
+|---|---|---|
+| global2 | `scanOptions` | `0=list, 9=caseSens, 12=noHalt` |
+| global3 | `curChar` | `377C`=EOF |
+| global5 | `curSym` | token 0..60; `10`=ident |
+| global6 | `isLiteral` | after number/string |
+| global7 | `cardVal` | integer literal |
+| global8/9 | `curNode/identKind` | found node / `0=ident,1=num/str,6=with,7=other` |
+| global11 | `tokenBuf[0..128]` | ident/number text + `0C` |
+| global12 | `curType` | `CARD/REAL/LONGREAL/...` of literal |
+| global13 | `curScopeWalk` | `SymTab.global9` chain |
+| global14/17 | `realVal/longVal` | via `Doubles.StrToDouble` |
+| global16 | `srcFile` | `Files.ReadBytes/SetPos/Open` |
+| global4 | `codeFile` | output of `Compile` |
+| global21/22/23 | `codeBuf/codeSize/listing` | `GENZ80` 4K buffer |
+| global24 | `charClass[0..255]` | `10`=letter, `11`=digit, `12`=quote ... |
+| global25/26 | `tokPos/tokCol` | start of token |
+| procPtr27 | `keywordHash` | `(ADR(tokenBuf),case)->CARD` |
+| buffer/bufIndex/filePos/column | same | 128B window at `0080H/006CH/006EH/0070H` |
+| stackLimit | same | heap guard at `0316H`, `FREEMARKER=3AE3H` |
+
+Pipeline: `OpenSource(proc29) -> InitScan(proc28) -> NextChar(proc2) ->
+scanNextToken(proc39) -> GetSym(proc1)` + `Number/SkipComment` helpers.
+
+128B window: `bufIndex=128` = empty; `NextChar` calls `Refill(proc34)`
+(`ReadBytes 128`, pad `EOT=032C`), else `buffer[bufIndex++]`,
+`INC(filePos)`, echo if `0 IN options` (`EchoChar proc33`).
+
+## Procedures (proposed)
+
+| old | proposed |
+|---|---|
+| proc1 | `GetSym` |
+| proc39 | `ScanNextToken` |
+| NUMBER | `ParseNumber` (base `*10/*8/*16`, `.`->real, `D/E` exp, `H/O/C/L` suffix) |
+| proc38 | `SkipComment` (nested `(* *)`, `$L+-` toggles options) |
+| proc2/34/28/29 | `NextChar/RefillBuffer/InitScan/OpenSource` |
+| proc32/33 | `ListPrefix/EchoChar` |
+| proc4/8/9/10/11 | `Accept/Expect/TestSet/Test40/Test13` |
+| proc12/13/21 | `NeedIdent/ExpectKind/ExpectString` |
+| proc3 | `PushWithScope` |
+| proc25 | `AddExtension` |
+| proc26 | `ScanError` -> `Errors.proc1` |
+| proc27 | `EnterSymtabKey` |
+| proc7=Allocate | `HeapAlloc` (was Z80) |
+| proc5/6 | `GetStackMark/CheckStack` |
+| proc22/23/24=`FindIdent/StrCmp/StrLen` | keyword/scope search (were Z80) |
+| proc16/15/14 | `CopyString/NewNode/NewSizedNode` |
+| proc18/19/20/17 | `MkIdent/DeclIdent/InsertSym/NewScope` |
+| proc30=`COMPIL` | `Compile` (top driver) |

+ 51 - 0
docs/compiler/40-symtab-errors.md

@@ -0,0 +1,51 @@
+# SymTab + Errors (`symtab.txt` 2365 lines, `errors.txt` 1421 lines)
+
+## SymTab (`SYMTAB.DEF` + disasm; no `.MOD` decompiled yet)
+
+```modula2
+Symbol = RECORD name: ARRAY [0..7] OF CHAR; word: CARDINAL END;
+T1 = POINTER TO R1;
+R1 = RECORD link0:T1; link1:POINTER TO ARRAY [0..128] OF CHAR;
+            link2:T1; word3:BITSET; word4:CARDINAL END;
+```
+
+| old | proposed |
+|---|---|
+| global3 | `modCount` |
+| global4/5 | `symFile/strPoolPtr` |
+| global6[0..99] | `moduleTab` (`Find/EnterModule proc9/10`) |
+| global7/8 | `scopeStack/rollbackStack[0..16]` |
+| global9 | `curScope` (`{w0,link1}`) |
+| global15/16/17 | `symTop/scopeTop/strTop` |
+| proc8 | `Assert` |
+| proc9/10 | `FindModule/EnterModule` |
+| proc11/12 | `LookupChain/MatchSym` |
+| proc14/13 | `AllocSym/EnterChained` |
+| proc17/21/22/18/20/19 | `NewString/GetName/GetAttr/PutAttr/EnterIdent/Unlink` |
+| proc23 | `InitHeaps` (slices `CODEGEN` memory) |
+| proc25/24 | `InsertBuiltin/EnterStandards` (14 builtins) |
+| proc27/26 | `LibRead/LoadSymFile` |
+| proc29/30/31/28 | `FindExport/ListExports/ImportOne/ImportAll` |
+| proc32/2/3/4 | `SkipQualident/DoDefinition/DoProgram/InitCompile` |
+| proc34/33/35/36 | `NoteImported/DoImportList/CompileBody/CheckForwards` |
+| proc1 | `Compile` dispatch; proc6/38/7 `SaveSym/Checksum/Unlock` |
+
+## Errors
+
+| old | proposed |
+|---|---|
+| global2 | `savedEditPos` |
+| global3 | `opCharTab` (2-char tokens) |
+| global4 | `doSymtabRollback` |
+| global5/6/7 | `errNo/curDrive/firstInit` |
+| proc1/2 | `Error(n)/ErrorMsg(n,msg)` |
+| proc4/12 | `WriteString/Fatal(HALT+Delete output)` |
+| proc5 | `FindFile(ext)` (drive cycle, `ERRMSGS.OVR` special case) |
+| DoError | `EditQuit('E'/'Q')` (`CBACKUP.$$$`, `Editor.Edit`, `usererror`/`DISK FULL`) |
+| proc11/19 | `RollbackScope/TruncScope` (`global8[0..16]`) |
+| proc6/27/28/26 | `KindName/TypeName/WriteType/NthSlash` |
+| proc24/25 | `NextBit/DerefAlias` |
+| proc29/7/8 | `WriteToken/WriteFound/WritePos` |
+| proc9/3/10 | `AskEditQuit/OvrError/PrintOvr` |
+| proc16/30 | `Expected(set)/Required` |
+| proc14/15/13 | `TypeMismatch/IncompatTypes/AssignMismatch` |

+ 39 - 0
docs/compiler/50-codegen.md

@@ -0,0 +1,39 @@
+# CodeGen (`codegen.txt` 1746 lines, `CODEGEN.DEF/MOD`)
+
+MCode emitter with peephole folding + 2KB sliding window on the output
+file (`Scanner.global4`) + fixup queue for jumps.
+
+Opcodes: see `CODEGEN.MOD` const block (`OPLLD=08H ... OPCALL=0EDH ...`),
+consumed by `MCode_specification/*.mod`.
+
+## State
+
+| old | proposed |
+|---|---|
+| global2/3/4 | `nextEmit/winBase/winLimit` (init 16/0/4096) |
+| global5 | `emitEnabled` |
+| global6 | `window[0..2047]` (`= ADR(Scanner.global21)`) |
+| global7/8/9/10 | `pendMode/pendSize/pendDisp/pendOff` |
+| global11/12 | `fixQueue[0..15]/fixCount` (`{kind,word1/word2\|ptr\|long}`) |
+| global14/15 | `pendFlag/checkOverflow` (`execute=4`) |
+
+## Procedures (proposed)
+
+| old | proposed |
+|---|---|
+| Emit1/Emit2/EmitWord/EmitString | same (guard `OverflowGuard`, flush if full) |
+| proc19/20 | `PeekByte/PokeByte` (window or `SetPos+Read/WriteByte`) |
+| proc23 | `FlushWindow` (write 2048, slide tail) |
+| proc28 | `SeekWindow` (512-align, refill) |
+| proc29 | `OverflowGuard` (`nextEmit >= ComLine.codepos -> RAISE 281`) |
+| proc26 | `Assert` |
+| proc22/24 | `OpenEmit/InitCode` |
+| proc4 | `DiscardPend` |
+| proc5/30/31/32 | `SetPend/FlushPendOp/BaseOp/FlushQueue` (peephole: `LDIX/LONGREAL/LI/LB...`) |
+| proc8/9 | `QueueConst/QueueLong` |
+| proc6 | `EmitOp(sub,type)` (fold `NEG/power2/AND/EQ0`, types 0..5: long/short/DCOMP/REALCMP/set/extern) |
+| proc7 | `EmitExt` |
+| proc10 | `EmitStd` (`LIMIT` fold) |
+| proc11 | `EmitMisc` |
+| proc17/18 | `EmitSys/EmitExt3` |
+| proc12/13/14/15/16 | `OpenFix/CloseFix/InsertFix/AdjustFix/MarkFix` (short `224`-case vs word fixups) |

+ 72 - 0
docs/compiler/60-frontend.md

@@ -0,0 +1,72 @@
+# Frontend: COMPILE = Pass1 + Statemen + Express
+
+No `.MOD` decompiled yet — only
+`compile.txt` (74) / `pass1.txt` (2975) / `statement.txt` (1793) /
+`express.txt` (3898). Names below are inferred (`~` = internal, no header).
+
+## COMPILE dispatcher
+
+`COMPILE.DIR/ = COMPILE.MCD` (link stub) + `PASS1/STATEMEN/EXPRESS.MCD`.
+`compile.txt` is a char-compare + byte-copy loop with `call .proc24`
+(`~FindKeyword/~StrCmp`), i.e. the link stub's keyword-hash helper,
+not a parser. Loaded by `Scanner.Compile`; `GENZ80` loaded afterwards
+if `Compiler.global25`.
+
+Token codes used everywhere: `Scanner.word5` = current symbol,
+`word9` = ident class, `word11` = text, `word12` = type.
+
+## Pass1 — declarations/scopes/types
+
+| proc | proposed |
+|---|---|
+| proc4/5 | `~PushTempMark/~PopTempMark` (bracket `EXPRESS.proc1(err 82)`) |
+| proc6/7 | `~AllocSymEntry/~LinkSymEntry` |
+| proc1 | `~InsertSetConst` |
+| proc2/3 | `~CondInsert/~InsertEntry` |
+| proc8 | `~OpenScopeEmit` (`CODEGEN.proc18/21/3`) |
+| proc12 | `~TypeSizeRec` (recursive, self-call) |
+| proc13 | `~GetIdent` (returns `Scanner.word12`) |
+| proc16 | `~ParseImportItem` (`Expect(42)`=FROM?) |
+| proc17 | `~ParseConstDecl` |
+| proc18 | `~ParseVarDecl` |
+| proc19 | `~ParseType` (largest: enum/subrange/pointer/PROC/record/array) |
+| proc14 | `~ParseRecordType` (incl. variant) |
+| proc15/20/21 | `~ParseFormals/~ParseValParam/~ParseVarParam` |
+| proc22 | `~ParseFieldList` |
+| proc23 | `~EvalConstExpr` |
+| proc25 | `~CloseScopeFixup` |
+| PROCDE | `ParseProcedureDecl` |
+| MODULD/MODBLO/MAINDE | `ParseModuleHead/ParseBlock/ParseMain(DEFINITION/IMPLEMENTATION/PROGRAM)` |
+| proc28 | `~ParseExportList` |
+| DCL/DCLS/IMEXDC | `ParseOneDecl/ParseDecls/ParseImports` (`ERRORS.proc11`, `CODEGEN.proc22` recovery) |
+| proc9/33/35/34/36/PASS1 | `~WriteSym/~WriteMcdHeader/~EmitConst/~EmitType/~CheckForwards/Main` |
+
+## Statemen — statements
+
+* Internal: `proc1 ~NextSymWrap`, `proc4 ~ParseStatSeqLoop`, `proc15 ~EmitJumpFixup`, `proc2 ~CalcMemOffset`, `proc3 ~SyncToStatStart`.
+* `STMT = ParseStatement`: `switch(word5)`: `121..130 -> proc10..proc5`
+  (IF/WHILE/REPEAT/LOOP/WITH/CASE/...); ident-led: `word9=4` call
+  (`EXPRESS.proc15`+`Expect(43)`=`(`), `=5` assign
+  (`EXPRESS.proc17/18`+`Expect(27)`=`:=`), `=0` error 128.
+* `proc5 = ParseFOR` (largest frame), `proc16 = ParseCASE`,
+  `proc6..14 = ParseIF/WHILE/REPEAT/...`, `proc17/18/19` block tails,
+  `BODY = ParseBeginBlock`, `STATSE = GuardedStatSeq` (saves
+  `Scanner.word2/25, CODEGEN.word2, SYMTAB.word5`; `ERRORS.proc11` recovery).
+
+## Express — expressions
+
+* `EXPRES = ParseExpression`: `proc48 SimpleExpr` then relops `51..57`
+  (`=/#/</<=/>/>=/IN`), `CODEGEN.proc17(23)+proc6`, `proc35`-fold,
+  `proc27`-join; const path `CODEGEN.proc4`, general `CODEGEN.proc10(4)`.
+* Helpers: `proc48 ~ParseSimpleExpr`, `proc37/38/40/41/42 ~Term/Factor/Set/Call/Cast`,
+  `proc21/20/15 ~DesignatorBase/Selector/Tail`,
+  `ASSIGN ~ParseAssign` (stmt + func-result),
+  `proc35 ~FoldConstOp`, `proc9 ~MatchOpClass`,
+  `proc7/8/13/14/16 ~ConstToCard/Int/GetVal/GetType/EvalConst`,
+  `proc43..46 ~ExpandStdProc`, `proc47/51/50/49 ~FoldRel/CheckRel/EmitCmp/EmitRange`,
+  `proc18/19 ~Load/StoreOperand`, `proc4/5 ~Push/PopExprDesc`.
+
+Shared: `Scanner.proc1/4/8/9/13/17/19/20` (Get/Accept/Expect/Test/OpenScope/entries),
+`EXPRESS.proc1` central error (47/52/61/82/73/74), `CODEGEN.proc1/3/4/6/10/12/13/14/16/17/18/19/21/22`.
+Decompile order: `EXPRES/proc48/proc35/proc9 -> STMT/STATSE/proc4 -> CASE/FOR ->
+Pass1 proc13/19/14 -> DCL/DCLS/IMEXDC -> PROCDE/MODBLO/MAINDE -> WriteSym`.

+ 48 - 0
docs/compiler/70-rename-plan.md

@@ -0,0 +1,48 @@
+# Rename work plan (procN/globalN -> meaningful names)
+
+Do in dependency order; verify by recompiling to 100% MCode match
+(the repo's existing check: decompiled sources must reproduce the `.MCD`).
+
+## Phase 1 — `Compiler` + `Scanner` (types known)
+
+1. `Compiler.Record/RecordPtr/ArrayPtr`: document `word0..word5/high`
+   once; all later renames refer to it.
+2. `Compiler PROC3..18, PROC1, GETINP, PROC10..13` per `20-compiler-module.md`.
+   Mechanical: `global31->curScope`, `global32/33->saveScope/baseScope`,
+   `global35->lastParamList`, `global20/21->systemMod/storageMod`.
+3. `Scanner` globals + `GetSym/Accept/Expect/Test/NextChar/Refill`
+   per `30-scanner-module.md`. Keep Z80 helpers (`Allocate/FindIdent/
+   StrCmp/StrLen`) byte-identical (padding comments are load-bearing).
+4. Rebuild + `unassemble.c` diff against `compiler.txt/scanner.txt`.
+
+## Phase 2 — `CodeGen` (no parser dependency)
+
+5. Rename buffer/fixup state + `Emit*/Peek/Poke/Flush/OpenFix/CloseFix`
+   per `50-codegen.md`. Opcodes already named; do not renumber.
+6. Diff against `codegen.txt`.
+
+## Phase 3 — `SymTab` + `Errors` (needs 1+2)
+
+7. Reconstruct `SYMTAB.MOD` header from `SYMTAB.DEF` + `symtab.txt`
+   proc boundaries; rename `moduleTab/curScope/symTop/...` per
+   `40-symtab-errors.md`.
+8. Rename `Errors` procs per same file; keep `DoError` stack/heap
+   `MOVE` arithmetic exact (`CBACKUP.$$$`, `FREEMARKER`, `030CH/0316H`).
+9. Diff against `symtab.txt/errors.txt`.
+
+## Phase 4 — `Express -> Statemen -> Pass1` (needs 1-3)
+
+10. `Express`: `EXPRES/ParseSimpleExpr/FoldConstOp/MatchOpClass/Designator*`
+    first; they are called by the other two.
+11. `Statemen`: `STMT/STATSE/ParseStatSeq/CASE/FOR` next.
+12. `Pass1`: `GetIdent/ParseType/ParseRecord/DCL/DCLS/PROCDE/MAINDE/WriteSym` last.
+13. Keep `COMPILE` link order `Pass1+Statemen+Express`; `GENZ80` untouched
+    until MCode output is stable.
+
+## Conventions
+
+* `~` prefix in docs = inferred, not yet confirmed by recompile.
+* One module per commit; keep `tr -d '\032\r'` normalisation out of diffs
+  (convert once, or keep CRLF — decide once, stay consistent).
+* Each rename commit must state: files touched, old->new table,
+  `unassemble` match result (yes/no + differing offsets).

+ 41 - 0
docs/compiler/TAGGED-SUMMARY.md

@@ -0,0 +1,41 @@
+# TAGGED SUMMARY — compiler outline v1 [tag: compiler-outline-v1]
+
+One-page snapshot. Details in `00-overview.md` .. `70-rename-plan.md`.
+
+## What
+
+Turbo Modula-2 compiler decompiles and runs, but
+`decompiled/COMPILER/` identifiers are `procN/globalN`.
+This folder restores the map without touching upstream sources
+(upstream dirs are git-ignored).
+
+## Map
+
+* Resident: `Compiler` (symbol descriptor + predefs) + `Scanner`
+  (128B window, `GetSym/Accept/Expect`) + `SymTab` (scopes, `.SYM`
+  import/export) + `Errors` (`ERRMSGS.OVR`, `Edit/Quit`, rollback) +
+  `CodeGen` (MCode emit, 2KB window, `OpenFix/CloseFix`).
+* Transient (`Loader.Call`): `COMPILE = Pass1` (decls/types/`WriteSym`) +
+  `Statemen` (`STMT` dispatch 121..130, `FOR/CASE/IF/...`) +
+  `Express` (`EXPRES`, designators, const-fold, std-proc expand) +
+  `GENZ80` (native, later).
+* Drivers: `C.MOD M2C`, `Scanner.Compile`, `ComLine`.
+
+## Key renames (proposed, `~` = to confirm by recompile)
+
+`Compiler`: `PROC3/4/5/6/7 InsertIdent/DeclConst/DeclStdProc/DeclProc/DeclFunc`,
+`PROC14/16/17/18 DeclarePredefTypes/DeclareStdNames/DeclareSYSTEM/DeclareSTORAGE`,
+`PROC1 InitCompiler`, `GETINP OpenSourceOutput`.
+`Scanner`: `proc1/2/4/8 GetSym/NextChar/Accept/Expect`, `proc30 Compile`.
+`CodeGen`: `Emit1/2/Word/String`, `Peek/Poke`, `FlushWindow/SeekWindow`,
+`OpenFix/CloseFix/InsertFix`.
+`SymTab`: `Find/EnterModule`, `LookupChain`, `ImportAll`, `SaveSym`.
+`Errors`: `Error/Msg/Fatal/FindFile/EditQuit/RollbackScope/Expected/TypeMismatch`.
+Frontend: `ParseType/ParseRecord/ParseFormals/ParseProcedure/ParseMain`,
+`ParseStatement/ParseStatSeq/ParseCASE/ParseFOR`,
+`ParseExpression/ParseSimpleExpr/FoldConstOp`.
+
+## Next
+
+Follow `70-rename-plan.md` phases 1..4; one module per commit with
+`unassemble` match result. Tag for this snapshot: `compiler-outline-v1`.