Explorar o código

feat(compiler): STATEMEN.MOD draft v1 + frame-mechanics fixes

830 lines: offset math, all 10 statement parsers, dispatch, guarded
seqs, CASE tail, block tails, ParseBeginBlock. Proves proc5=CASE /
proc6=FOR swap, full 121-130 dispatch, param1=last-pushed frame rule;
fixes Express.MatchOpClass order + CentralError arity.
Eric Streit hai 1 semana
pai
achega
9ed0e9a1ef

+ 16 - 10
src/compiler/EXPRESS.MOD

@@ -15,6 +15,12 @@
    groups 2/3's words 3/4 (spill save slots) are unified as spillTop/spillFlag;
    both readings involve saving a top — MCD diff decides.
 
+   Frame mechanics (proven 2026-10-03 from MCode_specification/Stack.mod:
+   NewFrame puts fp^[3]=LAST pushed word; opcode 03H=LoadParam(3) printed
+   as "param1"): "param1" = LAST pushed word, so multi-param body readings
+   assuming param1=formal1 are backwards; call sites from pushes stay
+   order-correct. Applied: MatchOpClass(allowed, errCode) [was swapped],
+   CentralError(ok, code) [was 1-param]. Full per-proc signature audit PENDING.
    Numbered-module mappings applied (verified against .DEF orders):
    - Compiler.word6..26 = IntType..compilationActive (DEF order = word order).
    - Scanner.word2/5/6/7/8/9/10/11/12 = scanOptions/curSymbol/isLiteral/
@@ -75,7 +81,7 @@ VAR
     exprPool: ADDRESS;    (* word9 *)
 
 (* ---- unresolved EXPRESS-internal helpers: FORWARD = work list ---- *)
-(*FIXME-NYC*) PROCEDURE CentralError(code: CARDINAL); FORWARD; (* proc1 *)
+PROCEDURE CentralError(ok: BOOLEAN; code: CARDINAL); FORWARD; (* proc1: proven (cond, code) via STMT @058a [bool,20] frame evidence; 1-arg call sites below need re-read *)
 (*FIXME-NYC*) PROCEDURE PushOperand; FORWARD; (* proc2 *)
 (*FIXME-NYC*) PROCEDURE PopOperand; FORWARD; (* proc3 *)
 (*FIXME-NYC*) PROCEDURE EmitOp; FORWARD; (* proc6 *)
@@ -114,7 +120,7 @@ PROCEDURE PopExprDesc(d: DescPtr); FORWARD; (* FIXME: 0-arg call sites *)
 PROCEDURE PushExprDesc(src: DescPtr; dst: DescPtr); FORWARD; (* FIXME arities *)
 PROCEDURE ConstToCard(errPos: CARDINAL; c: DescPtr); FORWARD;
 PROCEDURE ConstToInt(errPos: CARDINAL; c: DescPtr); FORWARD;
-PROCEDURE MatchOpClass(errCode: CARDINAL; allowed: BITSET); FORWARD;
+PROCEDURE MatchOpClass(allowed: BITSET; errCode: CARDINAL); FORWARD;
 PROCEDURE FoldConstOp(resTyp: DescPtr); FORWARD; (* FIXME: kind-result sites *)
 PROCEDURE EmitCompare; FORWARD;
 PROCEDURE EmitRangeCheck; FORWARD;
@@ -336,7 +342,7 @@ BEGIN
 END ConstToInt;
 
 (* proc9 @02be — ~MatchOpClass *)
-PROCEDURE MatchOpClass(errCode: CARDINAL; allowed: BITSET);
+PROCEDURE MatchOpClass(allowed: BITSET; errCode: CARDINAL);
 BEGIN
   {/* 02be */} (* operator class check on current expression type *)
   IF NOT (curDesc.typ^.word4 IN allowed) THEN
@@ -577,7 +583,7 @@ BEGIN
   IF p2 # 0 THEN ParseDesignatorTail; NormalizeOperand; END; (*{066f-0675}*)
   Scanner.PushWithScope; (*{0677}*)
   IF (Scanner.identKind = 5) & (p3 = 9) THEN (*{0679-0683} stdfunc misuse *)
-    CentralError(67); ParseActualList; Scanner.GetSym; RETURN; (*{068a-0697}*)
+    CentralError(TRUE (*FIXME: cond TBD*), 67); ParseActualList; Scanner.GetSym; RETURN; (*{068a-0697}*)
   END;
   BoolCondHelper; (*{0699} local proc12 *)
   ParseSelector(p3, p2, p1); (*{069b-069e} local proc20 *)
@@ -715,10 +721,10 @@ BEGIN
   IF Scanner.AcceptSymbol(1) THEN (*{08e9-08ec}*)
     PushOperand; (*{08ee}*)
     IF ~ MatchOpClass(1024, 1) THEN (*{08f1-08f5}*) END;
-    IF first # 0 THEN CentralError(63) END; (*{08f6-08fd} GetConstVal guard *)
+    IF first # 0 THEN CentralError(TRUE (*FIXME: cond TBD*), 63) END; (*{08f6-08fd} GetConstVal guard *)
     GetConstVal; ConstToCard(curDesc.raw[2], 1); (*{08fe-0902}*)
     LOOP (*{0903-0912} ','-separated consts *)
-      IF first # 0 THEN CentralError(63) END; (*{090a-0911}*)
+      IF first # 0 THEN CentralError(TRUE (*FIXME: cond TBD*), 63) END; (*{090a-0911}*)
       GetConstVal;
       IF ~ Scanner.AcceptSymbol(1) THEN EXIT END; (*{0917-091e}*)
     END;
@@ -735,14 +741,14 @@ BEGIN
   LOOP (*{0944} FindIdent retry loop, error 54/56 *)
     id := anchor; found := Scanner.FindIdent(id, pat, 9 IN Scanner.scanOptions); (*{0944-094e}*)
     id := STACK[0]; (*{0950-0952} FIXME: raw stack *)
-    IF id = 0 THEN CentralError(54-std) END; (*{0953-0959} unknown *)
+    IF id = 0 THEN CentralError(TRUE (*FIXME: cond TBD*), 54-std) END; (*{0953-0959} unknown *)
     IF found # 0 THEN
       IF found[4] # 5 THEN (*{095a-0965} not a var? *)
       ELSE EXIT END;
     END;
   END;
   IF ~ ParseDesignatorBase(found, Compiler.allocateSignature, found) THEN (*{0968-096d}*)
-    CentralError(56-std);
+    CentralError(TRUE (*FIXME: cond TBD*), 56-std);
   END;
   IF 6 # found[3] THEN CodeGen.EmitSystemCall(20) END; (*{0978-097e} address *)
   ParseDesignatorTail; NormalizeOperand; (*{097f-0981}*)
@@ -757,7 +763,7 @@ END ExpandStdProc45;
 PROCEDURE ExpandStdProc46;
 BEGIN
   IF (48 IN curDesc.raw[4]) OR ((curDesc.raw[4] = 2) & (Compiler.rangeCheckEnabled # 0)) THEN (*{09a4-09b3}*)
-    IF (curDesc.raw[4] # 5) OR (curDesc.raw[0] # 1) THEN CentralError(96) END; (*{09b5-09c0}*)
+    IF (curDesc.raw[4] # 5) OR (curDesc.raw[0] # 1) THEN CentralError(TRUE (*FIXME: cond TBD*), 96) END; (*{09b5-09c0}*)
     NormalizeOperand; curDesc.raw[4] := 2; curDesc.raw[3] := 0; (*{09c3-09ca}*)
   END;
   IF curDesc.raw[4] = 2 THEN CodeGen.EmitStandardOp(8) END; (*{09cb-09d2} ORD-class emit *)
@@ -831,7 +837,7 @@ BEGIN
             END;
         | 100: ParseDesignatorTail; (*{0b00} CAP-class? *)
             IF constMode # 0 THEN (*{0b01}*)
-            ELSIF curDesc.raw[4] > 9 THEN CentralError(97); (*{0b02-0b0b}*)
+            ELSIF curDesc.raw[4] > 9 THEN CentralError(TRUE (*FIXME: cond TBD*), 97); (*{0b02-0b0b}*)
             END;
             NormalizeOperand; (*{0b0c-0b0e}*)
         | 101, 102:

+ 17 - 0
src/compiler/FRONTEND.md

@@ -140,3 +140,20 @@ 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.

+ 2 - 1
src/compiler/README.md

@@ -15,7 +15,8 @@ No `0x1A`/`0x00` padding, LF line endings.
 | `SYMTAB.DEF` | expanded (words 10–27 mapped) + `StringPtr` + draft signatures |
 | `SYMTAB.MOD` | **DRAFT v1, unverified**: full bodies assembled from 4 group passes; see header for conflicts/FIXMEs |
 | `FRONTEND.md` | export map for `PASS1/STATEMEN/EXPRESS` (+ newly found EXPRESS internals) |
-| `EXPRESS.MOD` | **DRAFT v1, unverified**: full bodies (helpers/designators/term+factor/set+call/std-procs/relations/`ParseExpression`/assignment); 20 FORWARD work-list helpers |
+| `EXPRESS.MOD` | **DRAFT v1, unverified** (CentralError 2-param + MatchOpClass order fixed 2026-10-03) |
+| `STATEMEN.MOD` | **DRAFT v1, unverified**: 830 lines — offset math, all 10 statement parsers, dispatch, guarded seqs, CASE tail, block tails, `ParseBeginBlock`; frame-mechanics proof in header |
 
 Full old->new tables: `RENAME-MAP.md` (module level; params/locals renamed
 per-procedure in the bodies — see the `VAR` blocks and headers inline).

+ 8 - 0
src/compiler/RENAME-MAP.md

@@ -215,3 +215,11 @@ RestoreAfterEdit. Heap/feature globals: modStage/scopeChain/ownNamePtr/
 symHeapBase/scopeHeapBase/strHeapBase/symTop/scopeTop/strTop/exportHead/
 stageNameTotal/savedSymInfo/savedStrInfo/symCap/scopeCap/strCap/stageFlag/
 checksumTemp. See SYMTAB.MOD header for resolved conflicts + open FIXMEs.
+
+## STATEMEN (STATEMEN.MOD draft v1; all new, no upstream source)
+CalcMemOffset/SyncToStatStart/ParseCaseStmt/ParseFor/ParseWhile/ParseRepeat/
+ParseIf/ParseLoop/ParseExit/ParseResult/ParseWith/ParseReturn/NextSymWrap/
+ParseStatSeqLoop/EmitJumpFixup/ParseStatement/GuardedStatSeq/ParseCaseTail/
+CloseBlockChain/EmitNameHash/EmitComBytes/ParseBeginBlock.
+Globals: needEpilogue/withPending/nestDepth/exitDepth/curBlockNode/caseActive/
+withDesc/exitEmitted (words 2-9).

+ 830 - 0
src/compiler/STATEMEN.MOD

@@ -0,0 +1,830 @@
+(* STATEMEN.MOD — DRAFT v1, assembled 2026-10-03, NOT verified.
+   Reconstructed from Reversing-Turbo-Modula2-main/MCode_disassembly/statement.txt
+   (1793 lines) by two parallel passes (group 1 @0001-0566, group 2 @0566-end).
+   Does NOT yet recompile to the original MCD; verification via unassemble.c
+   diff is still required (see src/compiler/README.md).
+
+   FRAME MECHANICS (proven from MCode_specification/Stack.mod +
+   Instruction.mod + Extended.mod, settles all arg-order disputes):
+   - Callers push args in source order (spec Extended.Move pops size,dst,src).
+   - NewFrame pushes oldFP,outerFP above retAddr; fp^[0]=outerFP, [1]=oldFP,
+     [2]=retAddr, [3]=LAST pushed word. Opcode 03H..07H = LoadParam(3..7), and
+     the disassembler prints fp^[3] as "param1". Hence "param1" = LAST pushed
+     word (modulo multi-word/VAR params: SYMTAB proc8 SymbolAssert reads
+     "param2"=cond,"param1"=code for source (cond, code) — consistent).
+   - Consequence: a body reading "param1"=first-formal is backwards; the
+     correct source order is first-pushed=formal1. Call sites reconstructed
+     from pushes are order-correct as written.
+   - Proven by this: Express proc9 @02be tests `IN param2` and reports via
+     ERRORS.proc14(param2, X, param1)=(want,got,ctx); STMT @05d9 pushes
+     (512, 43): (allowed={9}, code=43) is the only sane reading, so
+     Express.MatchOpClass is (allowed, errCode) — NOT (errCode, allowed) as
+     EXPRESS.MOD draft v1 declares. FIX APPLIED HERE (call shapes unchanged);
+     EXPRESS.MOD's own declaration still needs the same swap (next edit).
+   - Same proof: Express.proc1 takes (cond, code) — STMT @058a shows
+     [bool,20] with code on top. EXPRESS.MOD's 1-param CentralError FORWARD
+     is stale; widened here via Express.CentralError(ok, code) usage and
+     flagged there.
+
+   CORRECTIONS to docs/compiler/60-frontend.md + FRONTEND.md (body-proven):
+   - proc5 = CASE (jump table via EmitWord, OF, `|`, ELSE), proc6 = FOR
+     (`:=`/TO/BY, control-var checks) — the map had them swapped.
+   - STMT dispatch: 121 Loop, 122 Exit, 123 Result, 124 If, 125 For,
+     126 Repeat, 127 While, 128 With, 129 Return, 130 Case (125 = CASE default).
+   - TWO case-shaped procedures exist: proc5 (STMT-driven, full jump-table
+     parser) vs proc16 (BODY-driven via Accept(14), label-table emitter).
+     Kept distinct: ParseCaseStmt vs ParseCaseTail. Why BODY re-enters case
+     parsing on symbol 14 is unexplained — MCD diff must decide.
+   - 123/129 = RESULT/RETURN inferred from store-vs-epilogue behavior.
+
+   CONVENTIONS: Words(X)^[N] = numeric-word pun on ADDRESS/T1 handles
+   (MCode is untyped; original emissive code TBD). GOTO kept as faithful
+   jump rendering (restructure to LOOP/EXIT in verification).
+   Scanner.word22/word23/word25 read as codeSize/listingEnabled/tokenPos
+   (SCANNER.DEF positional mapping; listingEnabled assignments flagged).
+   BITSET{..} literal sets on CARDINAL curSymbol kept verbatim (cast TBD). *)
+
+IMPLEMENTATION MODULE Statemen;
+IMPORT Compiler, Scanner, Errors, CodeGen, Express, SymTab, Files, Texts, Loader;
+FROM SYSTEM IMPORT ADDRESS, ADR, MOVE, WORD;
+FROM STORAGE IMPORT MARK, RELEASE;
+
+TYPE Words = POINTER TO ARRAY [0..15] OF WORD;
+
+VAR
+  needEpilogue: CARDINAL;    (* word2: set from body-nonEmpty + tail paths *)
+  withPending: CARDINAL;     (* word3: nonzero = with-base load pending *)
+  nestDepth: CARDINAL;       (* word4: LOOP/CASE/FOR nesting depth *)
+  exitDepth: CARDINAL;       (* word5: LOOP-entry depth for EXIT (= group2 loopExitMark) *)
+  curBlockNode: ADDRESS;     (* word6: BODY param1 (= group1 curRoutine) *)
+  caseActive: CARDINAL;      (* word7: proc16 sets 1 (= group1 caseSeen) *)
+  withDesc: ADDRESS;         (* word8: pending operand for EmitStandardOp(25) *)
+  exitEmitted: CARDINAL;     (* word9: proc17 sets 1 *)
+
+(* ---- helpers shared across both groups ---- *)
+PROCEDURE ReserveWords(n: CARDINAL): ADDRESS; FORWARD; (* FIXME: = Scanner.Allocate? *)
+PROCEDURE MoveWords(dst, src: ADDRESS; words: CARDINAL); FORWARD; (* FIXME: = MOVE? *)
+(* ---- group-1 procedures (FORWARDs; bodies below) ---- *)
+PROCEDURE CalcMemOffset(): CARDINAL; FORWARD;
+PROCEDURE SyncToStatStart; FORWARD;
+PROCEDURE ParseCaseStmt; FORWARD;
+PROCEDURE ParseFor; FORWARD;
+PROCEDURE ParseWhile; FORWARD;
+PROCEDURE ParseRepeat; FORWARD;
+PROCEDURE ParseIf; FORWARD;
+PROCEDURE ParseLoop; FORWARD;
+PROCEDURE ParseExit; FORWARD;
+PROCEDURE ParseResult; FORWARD;
+PROCEDURE ParseWith; FORWARD;
+PROCEDURE ParseReturn; FORWARD;
+(* ---- group-2 procedures (FORWARDs; bodies below) ---- *)
+PROCEDURE NextSymWrap; FORWARD;
+PROCEDURE ParseStatSeqLoop(stop: BITSET): BOOLEAN; FORWARD;
+PROCEDURE EmitJumpFixup; FORWARD;
+PROCEDURE ParseStatement; FORWARD;
+PROCEDURE GuardedStatSeq(follow: BITSET): BOOLEAN; FORWARD;
+PROCEDURE ParseCaseTail; FORWARD;
+PROCEDURE CloseBlockChain(VAR node: ADDRESS); FORWARD;
+PROCEDURE EmitNameHash(VAR name: ARRAY OF CHAR); FORWARD;
+PROCEDURE EmitComBytes; FORWARD;
+PROCEDURE ParseBeginBlock(tag: CARDINAL; VAR rest: CARDINAL); FORWARD;
+
+(* STATEMEN group 1 — procedures @0001-0566 *)
+
+(* proc2 @0001 — ~CalcMemOffset. 0 params: reads curBlockNode global *)
+PROCEDURE CalcMemOffset(): CARDINAL;
+VAR p, q: ADDRESS;
+    flag: CARDINAL;
+BEGIN
+  {/* 0001 */}
+  p := Words(curBlockNode)^[6]; (*{0003-0005}*)
+  IF p = NIL THEN (*{0006-0008} *)
+    RETURN 0; (*{000a-000b} *)
+  END;
+  WHILE Words(p)^[0] # NIL DO (*{000d-0010} *)
+    p := Words(p)^[0]; (*{0012-0014}*)
+  END;
+  q := Words(p)^[2]; (*{0017-0019}*)
+  IF (Words(q)^[4] > 9) OR (4 IN Words(p)^[3]) THEN (*{001a-0024} *)
+    flag := 1; (*{0026-0027} *)
+  ELSE
+    flag := (Words(q)^[3] + 1) DIV 2; (*{002a-002f} shr 1 *)
+  END;
+  RETURN Words(p)^[5] DIV 2 + flag - 3; (*{0030-0038} *)
+END CalcMemOffset;
+
+(* proc3 @003b — ~SyncToStatStart *)
+PROCEDURE SyncToStatStart;
+BEGIN
+  {/* 003b */}
+  IF Express.spillFlag # 0 THEN (*{003d-0040} *)
+    IF withPending # 0 THEN (*{0042-0043} *)
+      Express.PushOperand(withDesc); (*{0045-0046} FIXME: 1-arg vs 0-arg FORWARD *)
+      CodeGen.EmitStandardOp(25); (*{0048-004a} *)
+    END;
+    IF Words(curBlockNode)^[2] = NIL THEN (*{004c-004f}*)
+      CodeGen.EmitMiscOp(0, (*{0051-0052} *)
+        CalcMemOffset() + ORD(Express.spillFlag = 1) * 128); (*{0053-005a} shl 7 *)
+    ELSIF 8 IN Scanner.scanOptions THEN (*{005e-0062} *)
+      CodeGen.QueueConst(9); (*{0064-0065} *)
+      CodeGen.QueueConst(0); (*{0067-0068} *)
+      CodeGen.QueueConst(0); (*{006a-006b} *)
+      CodeGen.EmitStandardOp(14); (*{006d-006e} *)
+    END;
+  ELSE (*{0072} *)
+    CodeGen.EmitMiscOp(5, 2); (*{0073-0074} *)
+  END;
+END SyncToStatStart;
+
+(* proc5 @0078, STMT case 130 — ParseCaseStmt. Selector + OF + label ranges
+   with jump-table emission, `|`-separated arms, ELSE. *)
+PROCEDURE ParseCaseStmt;
+VAR head: ADDRESS;
+    chain: ADDRESS;
+    buf: ADDRESS;
+    tail: ADDRESS;
+    prev: ADDRESS;
+    selTyp: ADDRESS;
+    lo: CARDINAL;
+    hi: CARDINAL;
+    endFix: CARDINAL;
+    topFix: ADDRESS;
+    armFix: CARDINAL;
+BEGIN
+  INC(nestDepth); (*{007a-007d} *)
+  Express.BoolCondHelper; (*{007d} parse CASE selector *)
+  Express.MatchOpClass(7, 40); (*{007f-0082} *)
+  selTyp := Express.curDesc.raw[0]; (*{0084-0087} FIXME: typ pun *)
+  Scanner.ExpectSymbol(41); (*{0088-008a} OF *)
+  CodeGen.EmitSystemCall(21); (*{008c-008e} *)
+  CodeGen.EmitStandardOp(15); (*{008f-0092} jump to dispatch *)
+  CodeGen.OpenFixup(topFix, TRUE); (*{0094-0097} *)
+  head := NIL; (*{0099-009a} *)
+  tail := NIL; (*{009b-009c} *)
+  WHILE NOT (Scanner.curSymbol IN BITSET{8,10,13}) DO (*{009d-00a4} *)
+    armFix := CodeGen.MarkCodePos(14); (*{00a6-00aa} *)
+    Express.GetTypeDesc(selTyp, ADR(lo), ADR(hi)); (*{00ac-00b1} FIXME: (dest,mode,src) roles *)
+    prev := NIL; (*{00b3-00b4} *)
+    chain := head; (*{00b5-00b6} *)
+    WHILE (chain # NIL) AND (Words(chain)^[1] < lo) DO (*{00b7-00be} *)
+      prev := chain; (*{00c1-00c2} *)
+      chain := Words(chain)^[0]; (*{00c3-00c6} *)
+    END;
+    Scanner.Allocate(buf, 6); (*{00c8-00cb} *)
+    Words(buf)^[0] := chain; (*{00cd-00cf} *)
+    Words(buf)^[1] := hi; (*{00d0-00d2} *)
+    Words(buf)^[2] := armFix; (*{00d3-00d6} *)
+    IF (chain # NIL) AND (Words(chain)^[1] = 0) THEN (*{00d7-00d9} *)
+      tail := buf; (*{00db-00dc} *)
+    END;
+    IF (chain # NIL) AND (Words(chain)^[1] > hi) THEN (*{00df-00e2} *)
+      Express.CentralError(FALSE, 47); (*{00e3-00e5} *)
+    END;
+    chain := buf; (*{00e7-00e8} *)
+    DEC(hi); (*{00e9-00eb} *)
+    WHILE hi >= lo DO (*{00ec-00ef} expand label range; duplicate check TBD *)
+    END;
+    IF prev # NIL THEN (*{00f1-00f3} *)
+      Words(prev)^[0] := buf; (*{00f5-00f8} *)
+    ELSE
+      head := buf; (*{00fa-00fb} *)
+    END;
+    Scanner.TestSymbolInSet(BITSET{1,2}); (*{00fc-00fd} `,`/`:` *)
+    IF Scanner.AcceptSymbol(1) THEN (*{00ff-0100} `,`: another label, loop to 00ac *)
+    END;
+    Scanner.GetSym; (*{0105} consume `:` *)
+    WHILE NOT ParseStatSeqLoop(BITSET{8,10,13}) DO END; (*{0107-010b} arm body *)
+    CodeGen.EmitStandardOp(20); (*{010d-010f} jump to end *)
+  END;
+  IF NOT Scanner.AcceptSymbol(8) THEN (*{0111-0115} `|`: more arms loop to 009d *);
+  END;
+  DEC(nestDepth); (*{0117-0119} *)
+  endFix := CodeGen.InsertFixup(topFix, TRUE); (*{011a-011e} *)
+  IF head # NIL THEN (*{011f-0121} emit jump table over label chain *)
+    lo := Words(head)^[1]; (*{0123-0125} *)
+    CodeGen.EmitStandardOp(19); (*{0126-0128} *)
+    CodeGen.EmitWord(lo + 32768); (*{012a-012f} *)
+    CodeGen.EmitWord(tail - lo); (*{0131-0135} FIXME: ADDRESS pun *)
+    CodeGen.OpenFixup(topFix, FALSE); (*{0137-013a} *)
+    WHILE Words(head)^[0] = lo DO (*{013c-0140} FIXME *)
+      CodeGen.CloseFixup(Words(head)^[2] + ORD(NOT endFix), FALSE); (*{0142-0148} *)
+      head := Words(head)^[0]; (*{014a-014c} *)
+    ELSE
+      CodeGen.EmitWord((tail - lo) * 2 + 1); (*{014f-0156} *)
+    END;
+    INC(lo); (*{0158-015a} *)
+    IF head = NIL THEN END; (*{015b-015d} loop to 013c while head#NIL *)
+  END;
+  IF Scanner.AcceptSymbol(10) THEN (*{015f-0160} ELSE *)
+    WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{0164-0168} *)
+  END;
+  Scanner.GetSym; (*{0187} consume END *)
+  IF 8 IN Scanner.scanOptions THEN (*{016c-0170} range-check variant epilogue *)
+    CodeGen.QueueConst(13); (*{0172-0173} *)
+    CodeGen.QueueConst(0); (*{0175-0176} *)
+    CodeGen.QueueConst(0); (*{0178-0179} *)
+    CodeGen.EmitStandardOp(14); (*{017b-017c} *)
+  END;
+  IF tail # NIL THEN (*{017e-0180} *)
+    endFix := CodeGen.InsertFixup(topFix, FALSE); (*{0182-0186} *)
+  END;
+END ParseCaseStmt;
+
+(* proc6 @018b, STMT case 125 — ParseFor. Control var, `:=` lower TO
+   upper [BY const] DO body END. Largest frame (4 desc buffers). *)
+PROCEDURE ParseFor;
+VAR ctrl: ADDRESS;
+    loD: ADDRESS;
+    hiD: ADDRESS;
+    tmpD: ADDRESS;
+    step: CARDINAL;
+    cflag: CARDINAL;
+    isInt: CARDINAL;
+    dummy: CARDINAL;
+    sub: CARDINAL;
+    topMark: CARDINAL;
+    fixA: ADDRESS;
+    markB: CARDINAL;
+BEGIN
+  {/* 018b */}
+  Scanner.Allocate(ctrl, 10); (*{018d-018f} FIXME: group wrote ReserveWords *)
+  Scanner.Allocate(loD, 10); (*{0190-0192} *)
+  Scanner.Allocate(hiD, 10); (*{0193-0195} *)
+  Scanner.Allocate(tmpD, 10); (*{0196-0198} *)
+  Express.EvalConstExpr; (*{0199} control var head *)
+  Express.MatchOpClass(7, 28); (*{019c-019f} *)
+  IF Express.curDesc.raw[0] = Compiler.ByteType THEN (*{01a1-01a7} *)
+    Express.MatchOpClass(5, 28); (*{01aa-01ad} *)
+  END;
+  Express.CentralError(NOT (12 IN Words(Express.curDesc.raw[0])^[4]), 24); (*{01af-01b7} FIXME: pun *)
+  MOVE(Express.curDesc, ctrl, 10); (*{01b9-01bd} FIXME: group wrote MoveWords *)
+  sub := ORD(Words(Express.curDesc.raw[0])^[4] = 4); (*{01be-01c3} FIXME *)
+  IF sub # 0 THEN (*{01c4-01c5} rotate saved descs *)
+    MOVE(ctrl, tmpD, 10); (*{01c7-01cb} FIXME: direction TBD by diff *)
+    Express.PushExprDesc(ctrl, tmpD); (*{01cb-01ce} FIXME: arity/shape *)
+    MOVE(tmpD, ctrl, 10);
+  END;
+  Express.EmitOp; (*{01d0} proc6 *)
+  isInt := ORD(Express.curDesc.raw[0] = Compiler.IntType); (*{01d2-01d8} FIXME *)
+  Scanner.ExpectSymbol(27); (*{01d9-01db} `:=` *)
+  Express.BoolCondHelper; (*{01dd} lower bound *)
+  Express.ConstToCard(Words(ctrl)^[0], 27); (*{01df-01e5} FIXME: ctrlDescTyp *)
+  MOVE(Express.curDesc, loD, 10); (*{01e5-01e9} *)
+  Express.PopExprDesc(ctrl); (*{01ea-01eb} FIXME: ADDRESS arg *)
+  Scanner.ExpectSymbol(42); (*{01ed-01ef} TO *)
+  Express.BoolCondHelper; (*{01f1} upper bound *)
+  Express.ConstToCard(Words(ctrl)^[0], 42); (*{01f3-01f7} *)
+  MOVE(Express.curDesc, hiD, 10); (*{01f9-01fd} *)
+  step := 1; (*{01fe-01ff} *)
+  IF Scanner.AcceptSymbol(29) THEN (*{0200-0204} BY *)
+    Express.GetConstVal; (*{0206} proc13 *)
+    Express.ConstToCard(Compiler.IntType, 29); (*{0208-020c} *)
+    step := Express.curDesc.raw[2]; (*{020e-0211} const step *)
+  END;
+  (*{0212-0269} bound/step validation + counter init (const-fold vs runtime
+    paths); faithful to raw, minutiae FIXME *)
+  IF sub # 0 THEN (*{02e4-02e5} subrange control: normalize *)
+    Express.NormalizeOperand; (*{02e7-02e8} FIXME: group passed tmpD *)
+    Express.PushOperand(ctrl); (*{02ea-02eb} FIXME: 1-arg vs 0-arg FORWARD *)
+    CodeGen.SetPendingOp(5, 2, 0, 0); (*{02ed-02f1} *)
+  END;
+  WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{02f3-02f7} body to END *)
+  Scanner.GetSym; (*{02f9} consume END *)
+  (*{02fb-033d} loop-back + fixup close-out FIXME *)
+  Scanner.GetSym;
+END ParseFor;
+
+(* proc7 @0341, STMT case 127 — ParseWhile. WHILE cond DO body END. *)
+PROCEDURE ParseWhile;
+VAR top: CARDINAL;
+    fix: ADDRESS;
+    endMark: CARDINAL;
+BEGIN
+  top := CodeGen.MarkCodePos(13); (*{0343-0347} *)
+  Express.BoolCondHelper; (*{0348} *)
+  Express.ConstToCard(Compiler.BooleanType, 37); (*{034a-034e} *)
+  Scanner.ExpectSymbol(30); (*{0350-0352} DO *)
+  CodeGen.EmitStandardOp(16); (*{0354-0356} jump-if-false *)
+  CodeGen.OpenFixup(fix, TRUE); (*{0358-035b} *)
+  WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{035d-0361} body to END *)
+  Scanner.GetSym; (*{0363} consume END *)
+  CodeGen.EmitStandardOp(15); (*{0365-0366} jump back to top *)
+  endMark := CodeGen.nextEmitPos; (*{0368-036a} *)
+  CodeGen.CloseFixup(top, TRUE); (*{036b-036d} *)
+  IF NOT CodeGen.InsertFixup(fix, TRUE) THEN (*{036f-0374} *)
+    CodeGen.AdjustFixup(endMark + 1); (*{0376-0378} *)
+  END;
+END ParseWhile;
+
+(* proc8 @037c, STMT case 126 — ParseRepeat. REPEAT body UNTIL cond. *)
+PROCEDURE ParseRepeat;
+VAR top: CARDINAL;
+BEGIN
+  top := CodeGen.MarkCodePos(12); (*{037e-0382} *)
+  WHILE NOT ParseStatSeqLoop(BITSET{12}) DO END; (*{0383-0387} to UNTIL *)
+  Scanner.GetSym; (*{0389} consume UNTIL *)
+  Express.BoolCondHelper; (*{038b} *)
+  Express.ConstToCard(Compiler.BooleanType, 12); (*{038d-0392} *)
+  CodeGen.EmitStandardOp(16); (*{0392-0394} jump-if-false to top *)
+  CodeGen.CloseFixup(top, TRUE); (*{0396-0398} *)
+END ParseRepeat;
+
+(* proc9 @039c, STMT case 124 — ParseIf. IF cond THEN stats
+   [ELSIF cond THEN stats]... [ELSE stats] END. ELSIF via recursion. *)
+PROCEDURE ParseIf;
+VAR elseFix: ADDRESS;
+    endFix: ADDRESS;
+    savedEmit: CARDINAL;
+    constCond: BOOLEAN;
+    chained: CARDINAL;
+    skipGen: CARDINAL;
+BEGIN
+  Express.BoolCondHelper; (*{039e} *)
+  Express.ConstToCard(Compiler.BooleanType, 34); (*{03a0-03a4} *)
+  savedEmit := ORD(CodeGen.emitEnabled); (*{03a6-03a8} FIXME: BOOLEAN pun *)
+  skipGen := 0; (*{03a9-03aa} *)
+  constCond := (Express.curDesc.raw[1] = 0); (*{03ab-03af} mode=const *)
+  IF constCond THEN (*{03b0-03b1} *)
+    CodeGen.DiscardPending; (*{03b3} *)
+    skipGen := 2; (*{03b5-03b6} *)
+    IF Express.curDesc.raw[2] = 0 THEN (*{03b7-03bb} *)
+    ELSE
+      skipGen := 1; (*{03bd-03be} *)
+    END;
+    CodeGen.emitEnabled := FALSE; (*{03bf-03c0} suppress dead arm *)
+  END;
+  Scanner.ExpectSymbol(35); (*{03c2-03c4} THEN *)
+  IF NOT constCond THEN (*{03c6-03c8} *)
+    CodeGen.EmitStandardOp(16); (*{03ca-03cc} jump-if-false *)
+    CodeGen.OpenFixup(elseFix, TRUE); (*{03ce-03d1} *)
+  END;
+  WHILE NOT ParseStatSeqLoop(BITSET{10,11,13}) DO END; (*{03d3-03d7} *)
+  IF Scanner.AcceptSymbol(13) THEN (*{03d9-03dc} END: done *)
+    IF NOT constCond THEN (*{03de-03e0} *)
+      chained := ORD(CodeGen.InsertFixup(elseFix, TRUE)); (*{03e2-03e6} FIXME pun *)
+    END;
+  END;
+  IF NOT constCond THEN (*{03e9-03eb} *)
+    CodeGen.EmitStandardOp(15); (*{03ed-03ee} jump over else *)
+    CodeGen.OpenFixup(endFix, TRUE); (*{03f0-03f3} *)
+    IF NOT CodeGen.InsertFixup(elseFix, TRUE) THEN (*{03f5-03fa} *)
+      INC(endFix); (*{03fc-03fe} FIXME: ADDRESS pun *)
+    END;
+  END;
+  CodeGen.emitEnabled := savedEmit # 0; (*{0400-0405} FIXME: exact condition TBD *)
+  IF Scanner.AcceptSymbol(11) THEN (*{0407-040a} ELSIF: nested IF *)
+    ParseIf(); (*{040c} recursion *)
+  ELSE
+    Scanner.GetSym; (*{040f} consume ELSE *)
+    WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{0411-0415} else stats *)
+    Scanner.GetSym; (*{0417} consume END *)
+  END;
+  IF NOT constCond THEN (*{0419-041a} InsertFixup(endFix, TRUE), result dropped *)
+  END;
+  CodeGen.emitEnabled := savedEmit # 0; (*{0426-0427} tail *)
+END ParseIf;
+
+(* proc10 @042b, STMT case 121 — ParseLoop. LOOP body END with EXIT support. *)
+PROCEDURE ParseLoop;
+VAR savedExit: CARDINAL;
+    fix: ADDRESS;
+    top: CARDINAL;
+    endFix: CARDINAL;
+BEGIN
+  savedExit := exitDepth; (*{042d-042e} *)
+  INC(nestDepth); (*{042f-0431} *)
+  exitDepth := nestDepth; (*{0432-0433} *)
+  CodeGen.EmitStandardOp(21); (*{0434-0436} loop-head marker *)
+  CodeGen.OpenFixup(fix, FALSE); (*{0438-043b} *)
+  top := CodeGen.MarkCodePos(11); (*{043d-0441} *)
+  WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{0442-0446} body to END *)
+  Scanner.GetSym; (*{0448} consume END *)
+  CodeGen.EmitStandardOp(15); (*{044a-044b} jump back *)
+  CodeGen.CloseFixup(top, TRUE); (*{044d-044f} *)
+  endFix := ORD(CodeGen.InsertFixup(fix, FALSE)); (*{0451-0455} FIXME pun *)
+  DEC(nestDepth); (*{0456-0458} *)
+  exitDepth := savedExit; (*{0459-045a} *)
+END ParseLoop;
+
+(* proc11 @045d, STMT case 122 — ParseExit. Unwind to enclosing LOOP. *)
+PROCEDURE ParseExit;
+VAR level: CARDINAL;
+BEGIN
+  Express.CentralError(exitDepth # 0, 26); (*{045f-0463} *)
+  level := nestDepth; (*{0465-0466} *)
+  WHILE level > exitDepth DO (*{0467-046a} *)
+    CodeGen.EmitStandardOp(23); (*{046c-046e} unwind one level *)
+    DEC(level); (*{0470-0472} *)
+  END;
+  CodeGen.EmitStandardOp(20); (*{0475-0477} jump out *)
+END ParseExit;
+
+(* proc12 @047b, STMT case 123 — ParseResult. RESULT expr: store value
+   to function result slot; no exit jump. *)
+PROCEDURE ParseResult;
+VAR size: CARDINAL;
+    desc: ADDRESS;
+BEGIN
+  Express.CentralError((Words(curBlockNode)^[4] = 5) AND (Express.spillFlag # 0), 27); (*{047d-0488} *)
+  Express.CheckAndStore(desc); (*{0491-0493} FIXME: 0-arg FORWARD *)
+  Express.BoolCondHelper; (*{0496} result value *)
+  Express.ConstToInt(desc, 33); (*{0498-049b} FIXME: ADDRESS arg *)
+  Express.PopExprDesc(desc); (*{049d-049e} FIXME: ADDRESS arg *)
+  IF withPending # 0 THEN (*{04a0-04a1} *)
+    Express.PushOperand(withDesc); (*{04a3-04a4} FIXME arity *)
+    CodeGen.EmitStandardOp(25); (*{04a6-04a8} *)
+  END;
+  size := 0; (*{04aa-04ab} *)
+  IF desc # NIL THEN (*{04ac-04ae} *)
+    size := (Words(desc)^[3] + 1) DIV 2; (*{04b0-04b5} *)
+  END;
+  CodeGen.EmitMiscOp(size, (*{04b6} staged *)
+    CalcMemOffset() + ORD(Express.spillFlag = 1) * 128); (*{04b7-04bf} *)
+END ParseResult;
+
+(* proc13 @04c3, STMT case 128 — ParseWith. WITH rec DO body END. *)
+PROCEDURE ParseWith;
+VAR saved: ADDRESS;
+BEGIN
+  Express.ParseDesignatorTail; (*{04c5} *)
+  saved := Express.curDesc.raw[0]; (*{04c7-04c9} FIXME: typ pun *)
+  Express.MatchOpClass(1024, 38); (*{04ca-04cf} *)
+  IF Words(saved)^[4] IN BITSET{2,3,5} THEN (*{04d1-04de} FIXME set TBD *)
+    Scanner.OpenScope(SymTab.currentScope, Words(saved)^[0]); (*{04d9-04dd} FIXME *)
+  END;
+  IF Words(Express.curDesc.raw[0])^[0] # Compiler.CardType THEN (*{04e0-04e8} FIXME *)
+    Express.PushOperand(ADR(Express.curDesc)); (*{04e0-04e2} FIXME arity *)
+    Express.PopExprDesc(ADR(Express.curDesc)); (*{04ea-04ec} FIXME *)
+  END;
+  MOVE(Words(SymTab.currentScope)^[2], ADR(Express.curDesc), 10); (*{04ee-04f5} FIXME *)
+  Scanner.ExpectSymbol(30); (*{04f6-04f8} DO *)
+  WHILE NOT ParseStatSeqLoop(BITSET{13}) DO END; (*{04fa-04fe} body to END *)
+  SymTab.currentScope := Words(SymTab.currentScope)^[0]; (*{0500-0503} FIXME pun *)
+  Scanner.GetSym; (*{0505} consume END *)
+END ParseWith;
+
+(* proc14 @0509, STMT case 129 — ParseReturn. RETURN [value]: routine
+   exit epilogue with optional value. *)
+PROCEDURE ParseReturn;
+VAR info: CARDINAL;
+BEGIN
+  Scanner.PushWithScope; (*{050b} *)
+  IF caseActive = 0 THEN (*{050d-050f} *)
+    Scanner.ExpectIdentKind(2); (*{0511-0512} *)
+  END;
+  IF Scanner.identKind = 2 THEN (*{0514-0518} function context *)
+    IF 1 IN Scanner.followSet THEN (*{051a-051e} *)
+      info := Words(Scanner.curNode)^[6] + 2; (*{0520-0525} *)
+    ELSE
+      info := ORD(1 IN Scanner.followSet) - ORD(6 IN Scanner.followSet); (*{0528-052e} *)
+    END;
+    CodeGen.QueueConst(Words(Scanner.curNode)^[5] + info * 256); (*{052f-0536} *)
+    Scanner.GetSym; (*{0538} *)
+    IF Scanner.AcceptSymbol(1) THEN (*{053a-053d} `,` before value *)
+      Express.BoolCondHelper; (*{053f} *)
+      Express.ParseSelector(FALSE, Compiler.charArrayDesc, NIL); (*{0541-0546} FIXME: 39 dropped *)
+    ELSE
+      CodeGen.QueueConst(0); (*{054b-054c} *)
+      CodeGen.QueueConst(0); (*{054e-054f} *)
+    END;
+  ELSE
+    CodeGen.QueueConst(0); (*{0553-0554} *)
+    CodeGen.QueueConst(0); (*{0556-0557} *)
+    CodeGen.QueueConst(0); (*{0559-055a} *)
+  END;
+  CodeGen.EmitStandardOp(14); (*{055c-055d} *)
+END ParseReturn;
+(* STATEMEN group 2 — STMT/STATSE/tails/BODY + internals *)
+
+PROCEDURE NextSymWrap;
+(* ~proc1: no head, no call site in statement.txt; GetSym wrapper by name *)
+BEGIN
+  Scanner.GetSym;
+END NextSymWrap;
+
+PROCEDURE ParseStatSeqLoop(stop: BITSET): BOOLEAN;
+(* ~proc4: every driver is REPEAT proc4(set) UNTIL TRUE with empty body,
+   so proc4 parses ONE step; 14 pre-emption for nested CASE. *)
+BEGIN
+  IF Scanner.curSymbol IN stop THEN RETURN TRUE END;
+  IF Scanner.curSymbol = 14 THEN (* CaseSym FIXME *)
+    ParseCaseTail;
+  ELSE
+    ParseStatement;
+  END;
+  RETURN Scanner.curSymbol IN stop;
+END ParseStatSeqLoop;
+
+PROCEDURE EmitJumpFixup;
+(* ~proc15: sole real site STATSE after mark-save, before THEN-expect. *)
+BEGIN
+  CodeGen.DiscardPending;
+END EmitJumpFixup;
+
+PROCEDURE ParseStatement; (* STMT @0566 *)
+VAR
+  kw: CARDINAL;
+  saveSpill: CARDINAL;
+  saveNest: CARDINAL;
+  saveLoop: CARDINAL;
+  saveScope: ADDRESS;
+BEGIN
+  saveSpill := Express.spillTop; (*{0568} FIXME: group wrote spillTop; word3 spill save *)
+  saveNest := nestDepth; (*{056b} *)
+  saveLoop := exitDepth; (*{056d} FIXME: group wrote loopExitMark *)
+  saveScope := SymTab.currentScope; (*{056f} FIXME: ADDRESS pun *)
+  IF Scanner.curSymbol = 0 THEN GOTO identLed END; (*{0572} *)
+  IF Scanner.curSymbol IN BITSET{8..14} THEN GOTO done END; (*{0577} *)
+  Express.CentralError((121 <= Scanner.curSymbol) AND (Scanner.curSymbol <= 130), 20); (*{0580} *)
+  kw := Scanner.curSymbol; (*{058c} *)
+  Scanner.GetSym; (*{058f} *)
+  CASE kw OF (*{0591-05a6} default (125) -> ParseFor *)
+    121: ParseLoop
+  | 122: ParseExit
+  | 123: ParseResult
+  | 124: ParseIf
+  | 126: ParseRepeat
+  | 127: ParseWhile
+  | 128: ParseWith
+  | 129: ParseReturn
+  | 130: ParseCaseStmt
+  ELSE ParseFor (* 125 *)
+  END;
+  GOTO done;
+identLed:
+  Scanner.PushWithScope; (*{05c4} *)
+  IF Scanner.identKind = 4 THEN (*{05c6} proc call *)
+    Express.ParseDesignatorTail; (*{05cc} *)
+    IF Scanner.AcceptSymbol(43) THEN (*{05d0} '(' *)
+      Express.MatchOpClass(512, 43); (*{05d9} *)
+      Express.CentralError(Express.curDesc.raw[2] = 0, 61); (*{05db} FIXME: scratch2 TBD *)
+      Express.StoreOperand; (* proc19 *) (*{05e9?} *)
+    ELSE
+      Scanner.ExpectSymbol(27); (*{05e9} ':=' *)
+      Express.ParseAssignment; (*{05f2?} FIXME: group wrote ParseAssignRhs=proc17 *)
+    END;
+  ELSIF Scanner.identKind = 5 THEN (*{05f2} *)
+    Express.CentralError(Scanner.literalType = NIL, 61); (*{05f8} FIXME: pun *)
+    Express.LoadOperand; (* proc18 *) (*{05e9?} *)
+  ELSIF Scanner.identKind = 0 THEN (*{0604} *)
+    Errors.ReportErrorWithText(128, Scanner.tokenBuffer); (*{0609} *)
+  ELSE
+    Errors.ReportError(21); (*{0612} *)
+  END;
+done:
+  Express.spillTop := saveSpill; (*{0616} *)
+  (* 061a unwind path (exceptions only): restores exitDepth/currentScope, RAISE *)
+END ParseStatement;
+
+PROCEDURE GuardedStatSeq(follow: BITSET): BOOLEAN; (* STATSE @0634 *)
+VAR
+  saveOpts: BITSET;
+  savePos: CARDINAL;
+  mark: ADDRESS;
+  saveCode: CARDINAL;
+  savePool: CARDINAL;
+BEGIN
+  saveOpts := Scanner.scanOptions; (*{0636} *)
+  savePos := Scanner.tokenPos; (*{0639} FIXME: group wrote word25 *)
+  Scanner.GetStackMark(mark); (*{063d} *)
+  saveCode := CodeGen.nextEmitPos; (*{0641} *)
+  savePool := SymTab.stringPoolPtr; (*{0644} *)
+  EmitJumpFixup; (* ~proc15 *) (*{0647} *)
+  Scanner.TestSymbolInSet(follow + BITSET{9}); (*{0648} entry assert *)
+  IF Scanner.AcceptSymbol(9) THEN (* guarded-seq introducer *)
+    REPEAT UNTIL ParseStatSeqLoop(follow) END; (*{0654} *)
+  END;
+  RETURN TRUE;
+  (* 065b error-recovery exit: restore scanOptions/tokenPos/mark/codepos/pool,
+     Errors.RollbackToMark, CodeGen.OpenEmitter, RETURN FALSE *)
+END GuardedStatSeq;
+
+PROCEDURE ParseCaseTail; (* proc16 @0683, called only from BODY via ed *)
+VAR
+  tab, cnt: CARDINAL;
+  step: CARDINAL;
+  fix1, elseSeen: CARDINAL;
+  fix2: CARDINAL;
+  labelAddr: ADDRESS;
+BEGIN
+  caseActive := 1; (*{0685} *)
+  cnt := 0; (*{0687} *)
+  tab := 0; (*{0689} *)
+  IF NOT (Scanner.curSymbol IN BITSET{8,10,13}) THEN (*{068b} *)
+    fix1 := CodeGen.MarkCodePos(15); (*{0694} *)
+    Scanner.PushWithScope; (*{0699} *)
+    Scanner.ExpectIdentKind(2); (*{069b} *)
+    LOOP
+      Scanner.Allocate(labelAddr, 6); (*{06a0} FIXME: operand slots TBD *)
+      IF 1 IN Scanner.followSet THEN (* const-label class *)
+        step := Words(Scanner.curNode)^[0] + 2; (*{06a5?} FIXME *)
+      ELSE
+        step := 1 - ORD(6 IN Scanner.followSet);
+      END;
+      WHILE tab <> 0 DO
+        Express.CentralError(Words(tab)^[1] = Words(tab)^[1], 47); (*{06cd} FIXME: dup-label slots TBD *)
+        tab := Words(tab)^[0]; (* advance FIXME *)
+      END;
+      INC(cnt);
+      Scanner.GetSym;
+      Scanner.TestSymbolInSet(BITSET{1,2}); (*{06e0} *)
+      IF NOT Scanner.AcceptSymbol(1) THEN EXIT END; (*{06e3} *)
+    END;
+    Scanner.GetSym;
+    REPEAT UNTIL ParseStatSeqLoop(BITSET{8,10,13}) END; (*{06eb} arm seq *)
+    SyncToStatStart; (* ~proc3 *)
+  END;
+  IF NOT Scanner.AcceptSymbol(8) THEN GOTO noMoreArms END; (*{06f2} '|' *)
+  GOTO caseLoop; (* back to {068b} FIXME: restructure to LOOP *)
+noMoreArms:
+  elseSeen := ORD(Scanner.AcceptSymbol(10)); (*{06f8} ELSE FIXME pun *)
+  IF elseSeen # 0 THEN
+    INC(cnt);
+    fix1 := CodeGen.MarkCodePos(15);
+    REPEAT UNTIL ParseStatSeqLoop(BITSET{13,14}) END; (*{06fb} FIXME set TBD *)
+    SyncToStatStart;
+  END;
+  fix2 := CodeGen.InsertFixup(Scanner.codeSize - 2, FALSE); (*{070e} FIXME: word22 TBD *)
+  CodeGen.Emit1(cnt);
+  WHILE tab <> 0 DO
+    CodeGen.EmitWord(tab);
+    CodeGen.CloseFixup(tab, FALSE);
+    tab := Words(tab)^[0]; (* advance FIXME *)
+  END;
+  IF elseSeen # 0 THEN
+    CodeGen.EmitWord(0);
+    CodeGen.CloseFixup(fix1, FALSE);
+  END;
+END ParseCaseTail;
+
+PROCEDURE CloseBlockChain(VAR node: ADDRESS); (* proc17 @0738, display-call *)
+VAR isFirst: BOOLEAN; desc: CARDINAL;
+BEGIN
+  WHILE node # NIL DO (*{073a} *)
+    IF NOT (0 IN Words(node)^[3]) THEN GOTO advance END; (*{073e} FIXME pun *)
+    IF 5 IN Words(node)^[3] THEN (*{0745} *)
+      Scanner.FindIdent(SymTab.currentScope, node, 9 IN Scanner.scanOptions); (*{074b} FIXME: group wrote Proc22 6-push id-check; arity TBD *)
+      IF Scanner.FindIdent(SymTab.currentScope, node, 9 IN Scanner.scanOptions) = node THEN
+        Errors.ReportErrorWithText(128, Scanner.tokenBuffer); (*{0758} FIXME: raw (5, node, s1, 128) *)
+      END;
+      GOTO advance;
+    END;
+    IF Words(node)^[4] = 4 THEN (*{0765} *)
+      IF (Words(node)^[3] - BITSET{3} = BITSET{}) THEN (*{076b} FIXME: s4 > 9 conjunct TBD *)
+        CloseBlockChain(node); (*{077e} recurse FIXME: node^^ *)
+        IF Express.spillFlag <> 0 THEN (*{0780} *)
+          needEpilogue := 1;
+          desc := Express.curDesc.raw[0]; (*{079c?} FIXME *)
+          Express.LoadIndirect; (* proc11 *)
+          IF 3 IN Words(node)^[3] THEN (*{079c} *)
+            Express.NormalizeOperand; (* proc10 *)
+            IF Express.spillFlag = 1 THEN CodeGen.EmitMiscOp(5, 23)
+            ELSE CodeGen.EmitStandardOp(10) END;
+          ELSE CodeGen.EmitStandardOp(9) END;
+          Express.PopOperand; (* proc3 *)
+        END;
+        RETURN;
+      END;
+      GOTO advance;
+    END;
+    IF Words(node)^[4] = 6 THEN (*{07c7} *)
+      isFirst := NOT exitEmitted;
+      exitEmitted := 1;
+      CloseBlockChain(node); (* FIXME: node^ pun *)
+      IF Words(node)^[5] # NIL THEN
+        needEpilogue := 1;
+        CodeGen.EmitStandardOp(15);
+        CodeGen.CloseFixup(Words(node)^[2], FALSE);
+        IF NOT CodeGen.InsertFixup(Words(node)^[5], FALSE) THEN END;
+      END;
+      IF isFirst THEN CodeGen.EmitSystemCall(24) END;
+      RETURN;
+    END;
+advance:
+    node := Words(node)^[0]; (*{07f7} FIXME: next link via scratch; pun *)
+  END;
+  IF exitEmitted = 0 THEN CodeGen.EmitSystemCall(24) END; (*{07fc} *)
+END CloseBlockChain;
+
+PROCEDURE EmitNameHash(VAR name: ARRAY OF CHAR); (* proc18 @0808 FIXME: 2nd param HIGH=128 *)
+VAR h, m3, acc: CARDINAL; chunk, i: CARDINAL; c: CARDINAL;
+BEGIN
+  acc := 0; chunk := 0;
+  REPEAT
+    h := 0; m3 := 0; c := 0; i := 1; (*{080a-0812} *)
+    WHILE h <> 37 DO
+      c := ORD(name[chunk + m3]);
+      IF c = 0C THEN h := 37;
+      ELSIF c <= ORD("9") THEN h := c - 22C; (* FIXME: exact bounds TBD *)
+      ELSE h := c - ORD("A") - ORD(NOT ODD((c AND 64) DIV 2)); (* case-fold FIXME *)
+      END;
+    END;
+    acc := acc + h * i;
+    i := i * 38; INC(m3);
+    IF m3 <= 2 THEN (* redo *) END;
+    CodeGen.EmitWord(acc);
+    chunk := chunk + 3;
+  UNTIL chunk > 3;
+END EmitNameHash;
+
+PROCEDURE EmitComBytes; (* proc19 @0863, display-call reading BODY locals *)
+VAR f: CARDINAL; n: CARDINAL; saveHeap: ADDRESS;
+BEGIN
+  MARK(saveHeap);
+  CodeGen.EmitMiscOp(3, ORD(Express.spillFlag = 1));
+  Files.FindFileOnDrives(f, Scanner.tokenBuffer, "COM"); (*{0863?} FIXME: raw (addr-2, buf, 128) *)
+  n := 0;
+  REPEAT
+    CodeGen.Emit1(0); (* FIXME: bodyL5 byte; Files.FileProc09 TBD *)
+    INC(n);
+  UNTIL n = 0; (* FIXME: bodyL3 count TBD *)
+  RELEASE(saveHeap);
+END EmitComBytes;
+
+PROCEDURE ParseBeginBlock(tag: CARDINAL; VAR rest: CARDINAL); (* BODY @08a4 *)
+VAR
+  nonEmpty, wasVar: CARDINAL;
+  fix: CARDINAL;
+  s2, s5: CARDINAL;
+BEGIN
+  curBlockNode := tag; (*{08a6} *)
+  Express.spillTop := SymTab.stringPoolPtr; (*{08a8} FIXME: group wrote spillTop *)
+  nestDepth := 0; exitDepth := 0;
+  Scanner.listingEnabled := TRUE; (*{08b8?} FIXME: group wrote word23 *)
+  caseActive := 0; exitEmitted := 0;
+  Scanner.codeSize := CodeGen.nextEmitPos; (*{08b8} FIXME: group wrote word22 *)
+  wasVar := ORD(Scanner.identKind = 5); (*{08bd} *)
+  nonEmpty := ORD(Scanner.curSymbol = 23); (*{08c2} body-lead FIXME *)
+  IF nonEmpty # 0 THEN
+    Scanner.GetSym;
+    WHILE NOT Scanner.AcceptSymbol(9) DO Scanner.GetSym END; (*{08cd} skip past BEGIN *)
+    nonEmpty := ORD(Scanner.curSymbol <> 13);
+  END;
+  needEpilogue := nonEmpty; (*{08d8} *)
+  IF (Words(curBlockNode)^[4] = 5) AND (Express.spillFlag = 0) THEN (*{08da} *)
+    CodeGen.EmitExtCall3(3, curBlockNode, CalcMemOffset()); (*{08e0} FIXME: 3rd arg BYTEAt TBD *)
+    IF Compiler.rangeCheckEnabled THEN CodeGen.OpenFixup(fix, TRUE) END; (*{08ea} *)
+    IF 15 IN Scanner.scanOptions THEN CodeGen.Emit1(255) (*{08f3} *)
+    ELSE
+      EmitNameHash(Scanner.tokenBuffer); (*{08ff} FIXME: raw (g6, s1, 128) *)
+      CodeGen.EmitWord(0);
+    END;
+    Scanner.codeSize := CodeGen.nextEmitPos; (*{0908} FIXME *)
+    IF wasVar # 0 THEN
+      Scanner.GetSym;
+      Scanner.ExpectSymbol(43); (*{0912} '(' *)
+      Express.CentralError(Scanner.isLiteral AND (Scanner.literalType = Compiler.CharType), 33); (*{0916} *)
+      EmitComBytes; (* nested proc19 *)
+      Scanner.GetSym;
+      Scanner.ExpectSymbol(5); (*{0927} ')' *)
+      fix := ORD(Scanner.AcceptSymbol(9)); (*{092b} FIXME pun *)
+    ELSE
+      CodeGen.EmitStandardOp(22);
+      CodeGen.Emit1(255);
+      IF (Express.spillFlag <> 0) AND (withPending # 0) THEN (*{0938} FIXME: group wrote spare3 *)
+        Express.PushExprDesc(withDesc, Compiler.BitsetType); (*{0938?} FIXME: operand slots TBD *)
+        CodeGen.EmitStandardOp(24);
+        Express.PopOperand;
+      END;
+      CloseBlockChain(SymTab.currentScope); (*{094c} FIXME: ADDRESS pun *)
+      IF nonEmpty # 0 THEN REPEAT UNTIL ParseStatSeqLoop(BITSET{13,14}) END END; (*{0951} *)
+      SyncToStatStart;
+      IF Scanner.AcceptSymbol(14) THEN ParseCaseTail END; (*{095c} *)
+      IF Express.spillFlag <> 0 THEN (*{0962} *)
+        CodeGen.PokeCodeByte(SymTab.stringPoolPtr - 255, Scanner.codeSize + 1); (* FIXME *)
+      END;
+    END;
+  ELSE
+    CodeGen.EmitSystemCall(4); (*{0974} *)
+    IF Compiler.rangeCheckEnabled THEN CodeGen.OpenFixup(fix, FALSE) END; (*{0978} *)
+    Scanner.codeSize := CodeGen.nextEmitPos; (* FIXME *)
+    CloseBlockChain(SymTab.currentScope); (* FIXME pun *)
+    IF nonEmpty # 0 THEN REPEAT UNTIL ParseStatSeqLoop(BITSET{13}) END END; (*{098b} *)
+    IF needEpilogue # 0 THEN (*{0994} *)
+      CodeGen.EmitStandardOp(15);
+      s5 := CodeGen.nextEmitPos; CodeGen.EmitWord(0); (* FIXME *)
+      s2 := Scanner.codeSize; (* FIXME *)
+    ELSE
+      s5 := 0;
+    END;
+  END;
+  IF Compiler.rangeCheckEnabled THEN fix := ORD(CodeGen.InsertFixup(fix, FALSE)) END; (*{09ab} FIXME pun *)
+  Scanner.listingEnabled := FALSE; (* FIXME: group wrote word23 *)
+  Scanner.ExpectSymbol(13); (*{09b8} END *)
+  Scanner.ExpectStringLiteral(Scanner.tokenBuffer); (*{09bb} FIXME: group wrote Proc21; END-name match TBD *)
+  IF (Words(curBlockNode)^[4] = 5) AND (Express.spillFlag = 0) THEN (*{09c2} *)
+    Scanner.TestSymbolInSet(BITSET{3}); (* '.' module end *)
+  ELSE
+    Scanner.TestSymbolInSet(BITSET{9});
+  END;
+  IF (NOT Compiler.rangeCheckEnabled) AND (0 IN Scanner.scanOptions) THEN (*{09d7} *)
+    Texts.WriteString(3, "----- Size = "); (* FIXME: Texts.proc09 idiom *)
+    Texts.WriteCard(3, CodeGen.nextEmitPos - Scanner.codeSize, 0); (* FIXME: Texts.proc11 *)
+  END;
+  IF 4 IN Scanner.scanOptions THEN (*{0a02} *)
+    Loader.Call("PRINTCOD"); (* FIXME: +CARDINAL 7 *)
+  END;
+END ParseBeginBlock;
+
+BEGIN (* proc0 @0a25 module init *)
+END Statemen.