# Frontend export map: PASS1 + STATEMEN + EXPRESS (+ SYMTAB bodies) No upstream `.MOD` exists for these — only `MCode_disassembly/{pass1,statement,express,symtab,compile}.txt`. This file pins every **exported** head (MCode offset, old name, proposed name, role, evidence) so body decompilation can proceed procedure by procedure. Names with `~` are internal (no head entry, called via single-byte `f1..ff`) and listed where identified. Notation: `word5` = `Scanner.curSymbol`, `word9` = ident class, `word11` = token text, `word12` = ident type. ## COMPILE link (`compile.txt`, 74 lines) `COMPILE.DIR/ = COMPILE.MCD` (link stub) + `PASS1/STATEMEN/EXPRESS.MCD`, loaded by `Scanner.Compile` (`Loader.Call("COMPILE")`), then `GENZ80` if `Compiler.nativeCodeRequested`. `compile.txt` itself is a char-compare loop over two strings (bound 67 = keyword-table size) plus a byte-copy loop with one `call .proc24 (~StrCmp)` — the link stub's keyword-hash helper, not a parser. ## PASS1 — declarations/scopes/types (`pass1.txt`, 2975 lines) Deps: `COMPILER SCANNER FILES EXPRESS STATEMEN CODEGEN SYMTAB ERRORS TEXTS COMLINE LOADER`. | offset | old | proposed | evidence | |---|---|---|---| | 0001 | proc4 | `~PushTempMark` | brackets `EXPRESS.proc1(err 82)`; saves `SYMTAB.word4` vs `global word3` | | 0016 | proc5 | `~PopTempMark` | mirror of proc4 (`±1/±2` restore) | | 0032/0051 | proc6/proc7 | `~AllocSymEntry/~LinkSymEntry` | tiny allocators; proc7 feeds proc1 | | 007c | proc1 | `~InsertSetConst` | `SYMTAB.proc5` bitset build, `SCANNER.proc6/7` alloc+copy, `MOVE` blocks | | 00f4/0113 | proc2/proc3 | `~CondInsert/~InsertEntry` | proc2 = `if param4 then proc3 else 0` | | 017b | proc8 | `~OpenScopeEmit` | `CODEGEN.proc18/21/3`, `SCANNER.proc24`; calls proc4 | | 01a1 | proc12 | `~TypeSizeRec` | self `call_with_frame proc12` (recursive size walk) | | 0201 | proc13 | `~GetIdent` | `SCANNER.proc3` + `proc13(3)` + `proc1`, returns `Scanner.word12` | | 0211 | proc16 | `~ParseImportItem` | `Expect(42)` (FROM), `SCANNER.proc14(5)`, `proc19`, `word9/word13` checks | | 023e | proc17 | `~ParseConstDecl` | `EXPRESS.proc7/9/13` const-eval + `SCANNER.proc8` sync | | 027b | proc18 | `~ParseVarDecl` | nested-called from proc19 loop; `SCANNER.proc4` tail | | 02e6 | proc19 | `~ParseType` | largest (~290 ins): enum/subrange (`EXPRESS.proc9/13/7/14`), pointer/`PROC`/record/array, `SCANNER.proc17/18/19/20` entry building | | 0411 | proc15 | `~ParseFormalParams` | `SCANNER.proc19`, nested `proc18`, `proc17/19` helpers | | 04f4/050b | proc20/proc21 | `~ParseValParam/~ParseVarParam` | — | | 0548 | proc22 | `~ParseFieldList` | record fields; `proc8`, `SCANNER.proc12/20` sync | | 060a | proc23 | `~EvalConstExpr` | `ERRORS.proc2`, `EXPRESS.proc7/13`, `SCANNER.proc8/9` | | 0687 | proc24 | `~CheckConformant` | short, `EXPRESS.proc1` + `SCANNER.proc4/8` | | 06a7 | proc14 | `~ParseRecordType` | incl. variant part (~160 ins, `SCANNER.proc15/19`, `EXPRESS.proc7/13`) | | 0783 | proc25 | `~CloseScopeFixup` | single `SCANNER.proc4` + adjust | | 07a8 | PROCDE | `ParseProcedureDecl` | header, formals, local DCLS, body, `END ident` match | | 0878 | MODULD | `ParseModuleHead` | `SCANNER.proc17` scope open, name capture | | 08b2 | proc28 | `~ParseExportList` | `SCANNER.proc8` loop | | 08f1 | DCL | `ParseOneDecl` | dispatch single CONST/TYPE/VAR/PROCEDURE decl | | 0943 | DCLS | `ParseDeclarations` | `sym 13/23 -> STATEMEN.*` body hook, `loop proc11 … proc29`, guarded recovery (`ERRORS.proc11`, `CODEGEN.proc22`, save/restore `SCANNER.word2/25, CODEGEN.word2, SYMTAB.word5`) | | 09b9 | IMEXDC | `ParseImportFrom` | `sym 15/16 -> SYMTAB.proc2 + proc30`; `sym 23/13 -> STATEMEN.*`; `sym 17 -> SYMTAB.proc3` | | 0a0c | MODBLO | `ParseModuleBlock` | `SCANNER.proc17` open, nested `proc30`, `SYMTAB.proc4` link | | 0a56 | proc9 | `~WriteSymFile` | `.SYM` output; file/emit calls tail | | 0ae5 | MAINDE | `ParseMainModule` | ~280 ins: DEFINITION vs IMPLEMENTATION vs PROGRAM, `SYMTAB.proc2/3`, `proc30` frames | | 0c93 | proc33 | `~WriteMcdHeader` | tiny, `CODEGEN` linkait calls | | 0d17/0d2e | proc35/proc34 | `~EmitConstVal/~EmitTypeDesc` | paired; proc34 calls proc35 frame | | 0db2 | proc32 | `~SkipToDeclStart` | small sync helper before proc33 | | 0e93 | proc36 | `~CheckForwardResolved` | final sweep before close | | 1027 | PASS1 | `Pass1Main` | sets up globals, calls DCLS/MAINDE, triggers `.SYM`/`.MCD` output | | 105b | 4NAZBA | module init | MCode init body | ## STATEMEN — statements (`statement.txt`, 1793 lines) Deps: `COMPILER LOADER FILES TEXTS SYMTAB EXPRESS CODEGEN SCANNER ERRORS`. | offset | old | proposed | evidence | |---|---|---|---| | 0001 | proc2 | `~CalcMemOffset` | bit/arith descriptor math, no scanner calls | | 003b | proc3 | `~SyncToStatStart` | — | | 0078 | proc5 | `ParseFor` | frame −23, largest: `EXPRESS.proc12/9/14`, `Accept(1)`, `CODEGEN.proc10/12/14/16/17`, `BY` loop, nesting via `global4` | | 018b | proc6 | `~ParseIf` | ~350 ins, ELSIF/ELSE chains | | 0341..0509 | proc7..proc14 | `~ParseWhile/~ParseRepeat/~ParseLoop/~ParseWith/~ParseAssign/~ParseCall/~ParseReturn/~ParseExit` | order follows keyword codes 121–130 (confirm vs keyword table) | | 0566 | STMT | `ParseStatement` | `switch (Scanner.word5)`: 121→proc10 … 130→proc5; ident-led: `word9`=4 call (`EXPRESS.proc15`+`Expect(43)`=`(`), =5 assign (`EXPRESS.proc17/18`, `Expect(27)`=`:=`), =0 error 128 | | 0634 | STATSE | `GuardedStatSeq` | saves `SCANNER.word2/25+CODEGEN.word2+SYMTAB.word5`; `proc15`+`Expect(9)`; loop proc4; `ERRORS.proc11`+`CODEGEN.proc22` recovery | | 0683 | proc16 | `ParseCase` | `CODEGEN.proc16` fixup list, `,` loops, `ELSE`@10 tail | | 0738/0808/0863 | proc17/18/19 | block tails | ~90–120 ins each: `END`-match, fixup close, result check | | 08a4 | BODY | `ParseBeginBlock` | `BEGIN StatSeq END [ident]` | | 0a25 | proc0 | module init | — | | — | (internal proc1/proc4/proc15) | `~NextSymWrap/~ParseStatSeqLoop/~EmitJumpFixup` | no head entry; proc4 called in every stat-seq loop | ## EXPRESS — expressions (`express.txt`, 3898 lines) Deps: `COMPILER SCANNER ERRORS CODEGEN SYMTAB PASS1 DOUBLES`. | offset | old | proposed | evidence | |---|---|---|---| | 004a/00c0 | proc4/proc5 | `~PushExprDesc/~PopExprDesc` | — | | 0095/017d/01c5/0212 | proc32/33/34/35 | `~IsOrdinal/~IsSet/~BaseTypeOf/~FoldConstOp` | proc35 called from EXPRES after each operator | | 0275/02ac/02be | proc7/8/9 | `~ConstToCard/~ConstToInt/~MatchOpClass` | proc9 called with immediates 16/501/389/405… | | 02d0 | proc21 | `~ParseDesignatorBase` | frame −17, `SCANNER.proc8` sync | | 0358 | proc20 | `~ParseSelector` | `(`/`[`/`.`/`^` chains, `CODEGEN.proc17` | | 032f | proc36 | `~CheckAssignable` | — | | 0470 | proc16 | `~EvalConstExpr` | — | | 0510 | proc15 | `~ParseDesignatorTail` | also called from STMT assign branch | | 06a2/0657/0704/06bb/06da | proc37/38/40/41/42 | `~ParseTerm/~ParseFactor/~ParseSetCons/~ParseFuncCall/~ParseTypeCast` | — | | 087f..09a2 | proc43..46 | `~ExpandStdProc` | INC/DEC/INCL/EXCL/… | | 09e0 | proc39 | `~ParseSetOrCastTail` | — | | 0c61/0c74/0d1a | proc47/51/50 | `~FoldRelation/~CheckRelationTypes/~EmitCompare` | — | | 0e45 | proc49 | `~EmitRangeCheck` | frame −17 | | 1025 | proc48 | `~ParseSimpleExpression` | nested-called by EXPRES | | 0bd2/0c0c | proc18/19 | `~LoadOperand/~StoreOperand` | `CODEGEN.proc17/18` | | 1221 | EXPRES | `ParseExpression` | `proc48`, then relops 51–57 (`=`/`#`/`<`/`<=`/`>`/`>=`/`IN`); const-fold via `CODEGEN.proc4`, general `CODEGEN.proc10(4)`; shared exit @13df | | 13f3/1400 | proc13/14 | `~GetConstVal/~GetTypeDesc` | — | | 144c/1471 | ASSIGN | `ParseAssignment` | two entries: statement-assign + function-result assign | | 1516 | proc0 | module init | — | | fda9/ff1a/ff6b… | AAAAAA ×13 | Z80 helpers | address-ordered stubs (removed names); map to `Allocate/FindIdent`-style helpers during body work | ## SYMTAB bodies (`symtab.txt`, 2365 lines; `SYMTAB.DEF` done) `SYMTAB.MOD` does not exist upstream. Head map (offset → name from `docs/compiler/40-symtab-errors.md`): `0001 proc8 SymbolAssert`, `000c/0040 proc9/10 FindModule/EnterModule`, `0083/00a6 proc11/12 LookupChain/MatchSym`, `01d7/0259 proc14/13 AllocSym/EnterChained`, `00d4/0268/0281/0106/0305/03c9 proc17/21/22/18/20/19 NewString/GetName/GetAttr/PutAttr/EnterIdent/Unlink`, `03de proc23 InitHeaps`, `0432/0448 proc25/24 InsertBuiltin/EnterStandards`, `048a/04a0 proc27/26 LibRead/LoadSymFile`, `0553/0579/05be/05dd proc29/30/31/28 FindExport/ListExports/ImportOne/ImportAll`, `07ad proc32 SkipQualident`, `07d8/089a/0914 proc2/3/4 DoDefinition/DoProgram/InitCompile`, `0997/09c8 proc34/33 NoteImported/DoImportList`, `0a0c/0aa7 proc35/36 CompileBody/CheckForwards`, `0b1c IMDCLS TryCompile`, `0b6e proc1 CompileDispatch`, `0bdb/0b99/0c5e proc6/38/7 SaveSymFile/Checksum7w/Unlock`, `0c62 proc0` init. ## Shared cross-module procedures (decompile once) * Scanner API: `GetSym/AcceptSymbol/ExpectSymbol/TestSymbolInSet/ExpectIdentKind/OpenScope/NewSizedNode/NewNode/DeclareIdentifier/InsertSymbol/MakeIdentifierNode`. * `EXPRESS.proc1` = central error (codes 47/52/61/82/73/74); `proc9` = op/set test; `proc7/13/14` = const/type fetch; `proc17/18/19` = Load/Store helpers; `proc12` = boolean-cond helper. * CodeGen emitters: `proc1/3/4/6/10/12/13/14/16/17/18/19/21/22` (see `50-codegen.md`). ## Decompile order (unchanged) `EXPRES/proc48/proc35/proc9` → `STMT/STATSE/proc4` → CASE/FOR → Pass1 `proc13` (ident) → `proc19/14` (types) → DCL/DCLS/IMEXDC → PROCDE/MODBLO/MAINDE → `proc9` (SYM writer). SYMTAB bodies interleave as needed (`EnterIdent/LookupChain/ImportAll` first). ## EXPRESS internals found during draft v1 (not in the head map above) Group 2 reverse-engineered the head convention (`leave NN` operand = param count) and identified: proc1 CentralError, proc2/3 PushOperand/PopOperand, proc6 EmitOp, proc10 NormalizeOperand, proc11 LoadIndirect, proc12 BoolCondHelper (= group3's ParseCondValue), proc22 CheckAndStore, proc25 GetRangeBase, proc26 IsPointer, proc27 TypeKindOf (≠ proc34 BaseTypeOf!), proc28 EmitAddress, proc29 ParseActualList, proc31 EmitIndexed/EmitOperand31. Declared FORWARD in EXPRESS.MOD = remaining body work. AAAAAA heads = Z80 stubs. ## Corrections 2026-10-03 (STATEMEN body evidence + frame mechanics proof) * proc5 = CASE, proc6 = FOR (were swapped in the tables above). * STMT dispatch: 121 Loop, 122 Exit, 123 Result, 124 If, 125 For, 126 Repeat, 127 While, 128 With, 129 Return, 130 Case (125 = default arm). * proc5 (STMT-driven, STMT case 130) vs proc16 (BODY-driven via Accept(14)): kept distinct as ParseCaseStmt vs ParseCaseTail — why BODY re-enters case parsing on symbol 14 is unexplained. * Frame mechanics (MCode_specification/Stack.mod: fp^[3] = LAST pushed word = disassembler's "param1"): multi-param body readings assuming param1=formal1 are backwards; call sites from pushes stay order-correct. Proven instances: Express.MatchOpClass(allowed, errCode) [was swapped], Express.proc1 CentralError(ok, code) [was 1-param]. FULL per-proc signature audit across SYMTAB/EXPRESS drafts still PENDING (next workstream). * STATEMEN globals: word2 needEpilogue, word3 withPending, word4 nestDepth, word5 exitDepth, word6 curBlockNode, word7 caseActive, word8 withDesc, word9 exitEmitted. ## Corrections 2026-10-03 (PASS1 body evidence) * D1/D2 (re-read verdict 2026-10-04): arity is frame-correct as mapped (0-param @0411, 3-param @02e6 — only @02e6 can host the 3-push call sites); dispute is NAMES-only (proc15=ParseType vs ParseFormalParams). Map kept. * D3/D4: proc17 builds ARRAY descriptors, proc18 parses [lo..hi] SUBRANGEs (kept as ParseConstDecl/ParseVarDecl pending const/var-decl cross-check). * proc9 = ParseProcBody (parses procedure bodies, NO file calls) — NOT the .SYM writer; the writer is probably proc32 @0db2 (NOT decompiled yet). * Missing/stubbed: proc10/11/26/27/29/30 (no bytes anywhere), proc36 body (patched over by link-stub hash helper), PASS1 @1027 / 4NAZBA @105b. * Discovery: SCANNER.proc24 = 2-param length-prefix function (CappedStrLen); CodeGen EmitWord=proc3 / EmitString=proc21 body-proven. * PASS1 globals: word2 emitBase, word3 emitTop, word4 nestDepth. * ConstToCard/ConstToInt call shapes (type-first) vs EXPRESS.DEF order (errPos, c) disagree — signature-audit item (frame rule applied). ## Signature audit 2026-10-03 (all four drafts; frame rule applied) Method per proc: body paramK roles vs frame arity vs call-site push shapes. STMT+PASS1: all KEEP (only change: ParseBeginBlock drops unused VAR rest; CappedStrLen = SCANNER F4FCT2 StrLen with explicit HIGH). D1/D2 swap REFUTED by arity (@0411 is 0-param) — map names stand. SYMTAB applied SWAPs (accumulator-first): NewString/GetName/PutAttr/GetAttr/ AllocSym/EnterChained/EnterIdent/UnlinkScope; rest KEEP; DefineProgramModule UNKNOWN (now (setAddr, tag) + FIXME). EXPRESS applied: PushExprDesc/IsSet/BaseTypeOf/ConstToCard/ConstToInt/ ParseTerm/ParseSetCons/GetTypeDesc/ParseSelector swaps (def headers; bodies and push-order call sites untouched by construction); CheckRelationTypes drops unused op; ParseDesignatorBase (6->4) + ParseFactor (mirror) recorded as FIXME (need frame re-read); FoldRelation LONGREAL-param noted. proc32 = WriteOutputFiles (refutes ~SkipToDeclStart); called from Pass1Main path.