Przeglądaj źródła

wip(symtab): SYMTAB through GetAttr, EnterIdent heap cliff under bisection

TryCompile = savedEditPos>savePos (saveErr fiction removed); allocHook
appended to COMPILER.DEF (PASS1-set dword27 hook, byte-exact call shape);
scopeChain/currentScope/RestoreAfterEdit identities; Words-view param
retypes + temps for transfer-index/deref bans; impl-var abbreviations;
EnterIdent ELSIF chain flattened. Falsified as heap drivers: identifier
chars, comments, slots, forward counts. OOM masks type errors - ZZ-probe
every suspect in fresh heap. RENAME-MAP amendments + next-session plan.
Eric Streit 4 dni temu
rodzic
commit
ccc231d52d
5 zmienionych plików z 237 dodań i 128 usunięć
  1. 32 0
      SESSION.md
  2. 16 0
      SUMMARY.md
  3. 1 0
      src/compiler/COMPILER.DEF
  4. 32 0
      src/compiler/RENAME-MAP.md
  5. 156 128
      src/compiler/SYMTAB.MOD

+ 32 - 0
SESSION.md

@@ -77,3 +77,35 @@ Next: GENZ80 media hunt; stage-2 pty-driven MCD diff per module.
   (Q1/Q2-proven ADDRESS:=pointer assigns) at 2 use sites, +2mn each.
 - proc0 trailing region = embedded absolute-address tables shifted by the
   +6 growth (every byte -4): consequential, not semantic.
+
+## SYMTAB WIP checkpoint (2026-10-12, uncommitted -> committing as WIP)
+- SYMTAB.MOD compiles through GetAttr, dies inside EnterIdent (heap).
+- Banked fixes: TryCompile = savedEditPos>savePos (saveErr was fiction);
+  allocHook (COMPILER.dword27, appended last); word8=scopeChain,
+  word9=currentScope, SYMTAB.proc7=RestoreAfterEdit; transfer-index/deref
+  bans -> Words-view params + temps; `= 0`/`CHR(ORD())`/`ORD()` zero-cost
+  adaptations; unclosed comment @254 (pre-existing) fixed; DEF-leak
+  (Files/ADDRESS) + DEF-FORWARD collisions fixed; impl-var abbreviations.
+- OOM masks type errors (VAR-mismatch, Allocate(ADR()) both OOM) — always
+  isolate suspects in fresh-heap ZZ probes. Nesting depth tips razor-margin:
+  flat IF+call passes, same content nested OOMs; ELSIF chain flattened.
+- Identifier chars/comments/slots/forwards all FALSIFIED as heap drivers
+  (5604-char truncation ~1 proc; 17KB comment strip zero; +12 dummy forwards
+  fit). Standing theory: per-scope node budget; Borland wrote smaller
+  statements. Names/chars exonerated — readability preserved.
+- Drive holds full chain (COMPILER.SYM now WITH allocHook); ZZ probes
+  (W1-W8, U1-U2, V1-V4, T1-T5, B-series, F1, M1, E1-E2, P1, S1-S6, Q1-Q3,
+  H1-H2) document v1.00 codegen rules. tmp/ ignored by git (local only).
+
+## Next session plan
+1. Fold EnterIdent's `IF >= 5` guard into single-depth `AND` (or split the
+   arm); recompile; expect UnlinkScope/InitHeaps next.
+2. Same isolate-and-flatten loop per proc to end of SYMTAB.MOD; full MCD
+   diff vs Reloaded SYMTAB.MCD; commit; tag `symtab-verify-01`.
+3. Then EXPRESS (1490 lines, biggest risk) → STATEMEN → PASS1 same loop.
+   Budget for ZZ probes: each suspect construct gets a fresh-heap probe
+   BEFORE bending src around it.
+4. If a module genuinely exceeds retail heap with clean nodes: timebox 2h,
+   then switch to vm_linux instrumentation (heap high-water log) for ground
+   truth instead of blind bisection. Do NOT rename everything terse.
+5. Refresh SUMMARY.md + retag at each verify tag.

+ 16 - 0
SUMMARY.md

@@ -41,3 +41,19 @@ function/procedure/variable a bare number (`procN`/`globalN`/`paramN`/`localN`).
 Decompile in dependency order: `SYMTAB` (`EnterIdent/LookupChain/ImportAll`)
 → `EXPRESS` helpers → `STMT/STATSE` → `CASE/FOR` → Pass1 types → decls →
 module heads → SYM writer (`docs/compiler/70-rename-plan.md`, phase 4).
+
+## Verification status (Oct 2026, v1.00 retail toolchain + cpmulator)
+Proven by recompile + MCD mnemonic diff (jump targets masked, 00-gaps
+stripped; dep-slot renumbering benign, proven via slot formula):
+* `COMPILER.MOD` 18/19 byte-identical (linkage names only).
+* `SCANNER.MOD` 34/41 (`scanner-verify-01`; OOM was FLOAT-of-DIV expr spike,
+  fixed by `1L`/`2L` folding; proc23/24 clean-room, COMPIL VarAddr shim).
+* `ERRORS.MOD` 28/30 (`errors-verify-01`; heap was import weight — dropped
+  unused Compiler/ComLine; EditPos module + `EDITERR` overlay load decoded).
+* `CODEGEN.MOD` 34/37 (`codegen-verify-01`; keywordTable low-byte overlay).
+* `SYMTAB.MOD` WIP: compiles through GetAttr, dies in EnterIdent (heap);
+  genuine fixes banked (TryCompile logic, allocHook, view-typed params).
+Toolchain rules proven on hardware: DEF-leak, exact DEF/MOD param names,
+no FORWARD of DEF-declared procs, nominal array checks, transfer-index
+and transfer-deref bans, zero-cost `CHR(ORD())`/`ORD()`/`CHR()`/`= 0`,
+OOM-masking-type-errors. See SESSION.md + RENAME-MAP.md amendments.

+ 1 - 0
src/compiler/COMPILER.DEF

@@ -45,6 +45,7 @@ VAR
     storageScopeDesc: RecordPtr;
     nativeCodeRequested: BOOLEAN;
     compilationActive: BOOLEAN;
+    allocHook: PROCEDURE(ADDRESS, ADDRESS); (* hook set by PASS1 (store dword27), called by SYMTAB (load dword27 + extern_proc_call); appended last so no slot shifts *)
 
 (* Exported drivers: SCANNER.Compile calls OpenSourceAndOutput (opens the
    source file); PASS1's main calls InitCompiler once per compile (rebuilds

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

@@ -216,6 +216,38 @@ symHeapBase/scopeHeapBase/strHeapBase/symTop/scopeTop/strTop/exportHead/
 stageNameTotal/savedSymInfo/savedStrInfo/symCap/scopeCap/strCap/stageFlag/
 checksumTemp. See SYMTAB.MOD header for resolved conflicts + open FIXMEs.
 
+### SYMTAB stage-2 amendments (v1.00 hardware proofs, Oct 2026)
+* `COMPILER.dword27` -> `allocHook: PROCEDURE(ADDRESS, ADDRESS)` (appended
+  last in COMPILER.DEF, no slot shifts). PASS1 stores a hook address
+  (`store COMPILER.dword27`); SYMTAB calls it (`load dword27 +
+  extern_proc_call`, byte-exact shape proven). Was misread as
+  `allocateSignature` (a type symbol) — wrong target.
+* `ERRORS.word2` = `savedEditPos` ONLY (edit-position save in DoError).
+  `ERRORS.word5` = last-error-code (private `errorNumber`, stored by
+  PrintOverlayMessage @0677, printed by FindFileOnDrives). TryCompile's
+  `saveErr` was fiction: 0b4c compares `savedEditPos > savePos` (edit
+  happened -> rollback+retry, else HALT). RENAME-MAP `global5->errorNumber`
+  refers to this word5 (correct as-is).
+* `SYMTAB.word8` = `scopeChain`, `word9` = `currentScope`,
+  `SYMTAB.proc7` = `RestoreAfterEdit` (all proven from ERRORS.MCD call sites).
+* Heap-budget abbreviations (MOD-private impl vars only; DEF/external names
+  untouched; comments keep long forms): symHeapBase->symHeap,
+  scopeHeapBase->scpHeap, strHeapBase->strHeap, exportHead->expHead,
+  stageNameTotal->namTotal, savedSymInfo->savSym, savedStrInfo->savStr,
+  stageFlag->stgFlag, stageSpare11->spare11, stageSpare26->spare26,
+  checksumTemp->chkTemp.
+* v1.00 bans (all probe-proven): indexing/dereferencing transfer results
+  (`Words(x)^[i]`, `P(x)^.f`) -> view-typed params (PutAttr/AllocSym/
+  EnterIdent `scope`/`src` retyped to Words, zero-cost transfers at call
+  sites) or temps; CARDINAL compares for T1-vs-RecordPtr; `curSym = 0`
+  for `eq0`; `keyHash` DEF is `(ADDRESS, CHAR)` (original's
+  `(Identifier, CHAR)` unsatisfiable under retail nominal checks; U-probes
+  prove identical pushes; `CHR(ORD())`/`ORD()`/`CHR()` are zero-cost).
+* OOM often masks type errors (VAR-mismatch, `Allocate(ADR(..))` OOM instead
+  of erroring) — isolate suspects in fresh-heap ZZ probes, never trust the
+  cliff position alone. ELSIF-nesting pins condition heap; tail-positioned
+  chains flattened to sequential IF+RETURN (EnterIdent).
+
 ## STATEMEN (STATEMEN.MOD draft v1; all new, no upstream source)
 CalcMemOffset/SyncToStatStart/ParseCaseStmt/ParseFor/ParseWhile/ParseRepeat/
 ParseIf/ParseLoop/ParseExit/ParseResult/ParseWith/ParseReturn/NextSymWrap/

+ 156 - 128
src/compiler/SYMTAB.MOD

@@ -32,8 +32,8 @@
    - TEXTS handle literal 3 used throughout (screen?); kept as-is. *)
 
 IMPLEMENTATION MODULE SymTab;
-IMPORT Compiler, Files, Loader, Texts, Errors, Scanner, CodeGen;
-FROM SYSTEM IMPORT ADDRESS, ADR, MOVE;
+IMPORT Compiler, Loader, Texts, Errors, Scanner, CodeGen; (* Files comes via DEF import (leak rule); re-listing is a hard error *)
+FROM SYSTEM IMPORT ADR, MOVE; (* ADDRESS/WORD come via DEF import (leak rule) *)
 
 (* ---- heap model (groups B + C) ---- *)
 (* MCode words are untyped; CARDINAL()/T1()/ADDRESS() punning mirrors the
@@ -47,23 +47,23 @@ TYPE Words = POINTER TO ARRAY [0..7] OF CARDINAL;
    Numbering: DEF vars occupy words 2..9, these follow. *)
 VAR
     (* word 10 *) ownNamePtr: ADDRESS;
-    (* word 11 *) stageSpare11: CARDINAL; (* use TBD *)
-    (* word 12 *) symHeapBase: ADDRESS;
-    (* word 13 *) scopeHeapBase: ADDRESS;
-    (* word 14 *) strHeapBase: ADDRESS;
+    (* word 11 *) spare11: CARDINAL; (* use TBD *)
+    (* word 12 *) symHeap: ADDRESS;
+    (* word 13 *) scpHeap: ADDRESS;
+    (* word 14 *) strHeap: ADDRESS;
     (* word 15 *) symTop: CARDINAL;
     (* word 16 *) scopeTop: CARDINAL;
     (* word 17 *) strTop: CARDINAL;
-    (* word 18 *) exportHead: CARDINAL;
-    (* word 19 *) stageNameTotal: CARDINAL;
-    (* word 20 *) savedSymInfo: CARDINAL;
-    (* word 21 *) savedStrInfo: CARDINAL;
+    (* word 18 *) expHead: CARDINAL;
+    (* word 19 *) namTotal: CARDINAL;
+    (* word 20 *) savSym: CARDINAL;
+    (* word 21 *) savStr: CARDINAL;
     (* word 22 *) symCap: CARDINAL;
     (* word 23 *) scopeCap: CARDINAL;
     (* word 24 *) strCap: CARDINAL;
-    (* word 25 *) stageFlag: CARDINAL; (* nestedProcFlag / compileActive readings *)
-    (* word 26 *) stageSpare26: CARDINAL; (* use TBD *)
-    (* word 27 *) checksumTemp: CARDINAL;
+    (* word 25 *) stgFlag: CARDINAL; (* nestedProcFlag / compileActive readings *)
+    (* word 26 *) spare26: CARDINAL; (* use TBD *)
+    (* word 27 *) chkTemp: CARDINAL;
 
 (* 14-byte symbol-heap entry = 7 words (group C names). *)
 TYPE SymEntryPtr = POINTER TO SymEntry;
@@ -126,18 +126,18 @@ CONST symReadError = 790; (* RAISE code in LibRead {/*0497*/} *)
 
 (* ---- FORWARDs for the PutAttr/GetAttr <-> EnterChained/AllocSym/
         UnlinkScope/EnterIdent recursion cycle (group B) ---- *)
-PROCEDURE SymbolAssert(cond: BOOLEAN; code: CARDINAL); FORWARD;
+(* SymbolAssert/DefineDefinitionModule are DEF-declared: no FORWARD (v1.00 rejects
+   a FORWARD duplicating a DEF declaration, proven on hardware). *)
 PROCEDURE CheckNode(node: T1); FORWARD;
 PROCEDURE EnterChained(scope: ADDRESS; node: T1); FORWARD;
-PROCEDURE AllocSym(scope: ADDRESS; node: T1); FORWARD;
+PROCEDURE AllocSym(scope: Words; node: T1); FORWARD;
 PROCEDURE UnlinkScope(scope: ADDRESS; idx: CARDINAL); FORWARD;
-PROCEDURE EnterIdent(scope: ADDRESS; idx: CARDINAL); FORWARD;
-PROCEDURE PutAttr(VAR slot: CARDINAL; src: T1); FORWARD;
+PROCEDURE EnterIdent(scope: Words; idx: CARDINAL); FORWARD;
+PROCEDURE PutAttr(VAR slot: CARDINAL; src: Words); FORWARD;
 PROCEDURE GetAttr(VAR node: T1; idx: CARDINAL); FORWARD;
 PROCEDURE LookupChain(scopeNo: CARDINAL; key: CARDINAL): T1; FORWARD;
 PROCEDURE InitHeaps; FORWARD;
 PROCEDURE EnterStandards; FORWARD;
-PROCEDURE DefineDefinitionModule(isImpl: CARDINAL); FORWARD;
 PROCEDURE ImportOne(anchor: ADDRESS; symIdx: CARDINAL); FORWARD;
 
 (* ================= group A: module/scope search (@0001-00d4) ================= *)
@@ -151,9 +151,11 @@ END SymbolAssert;
 PROCEDURE FindModule(slot: CARDINAL): CARDINAL;
 (* proc9 @000c: 16 = self, 17 = not found *)
 VAR i: CARDINAL;
+    key: ModuleSymPtr;
 BEGIN
   (*{/*0016*/} key.name = ownNamePtr^ -> self *)
-  IF modStage^[slot].name = ModuleSymPtr(ownNamePtr)^.name THEN RETURN 16 END;
+  key := ownNamePtr; (* ADDRESS->pointer assignment is legal on v1.00; transfer-call deref is not *)
+  IF modStage^[slot].name = key^.name THEN RETURN 16 END;
   i := 0;
   WHILE i < moduleCount DO
     IF modStage^[slot].name = moduleTable^[i].name THEN RETURN i END;
@@ -221,7 +223,7 @@ BEGIN
     len := Scanner.StrLen(src, 128) + 1; (*{/*00de*/}* original pushes bound 128 *)
     INC(strTop, len);
     SymbolAssert(strTop + 8 <= strCap, 85); (*{/*00ec*/}*)
-    MOVE(src, strHeapBase + off, len); (*{/*00f6*/}*)
+    MOVE(src, strHeap + off, len); (*{/*00f6*/}*)
   ELSE
     off := 0; (*{/*0100*/}*)
   END;
@@ -229,36 +231,41 @@ END NewString;
 
 PROCEDURE GetName(VAR name: ADDRESS; off: CARDINAL);
 (* proc21 @0268 — inverse of NewString: heap copy of pooled string *)
+VAR cs: StringPtr;
 BEGIN
   IF off <> 0 THEN (*{/*026a*/}*)
-    Scanner.CopyStr(name, StringPtr(strHeapBase + off)^); (*{/*026e*/}*)
+    cs := strHeap + off; (* ADDRESS arithmetic into pointer; transfer-deref is not a VAR actual *)
+    Scanner.CopyStr(name, cs^); (*{/*026e*/}*)
   ELSE
     name := NIL; (*{/*027b*/}*)
   END;
 END GetName;
 
-PROCEDURE PutAttr(VAR slot: CARDINAL; src: T1);
-(* proc18 @0106 — store a type/attr node into the scope-descriptor table *)
+PROCEDURE PutAttr(VAR slot: CARDINAL; src: Words);
+(* proc18 @0106 — store a type/attr node into the scope-descriptor table.
+   src is Words-typed (not T1): v1.00 rejects indexing a transfer result,
+   so callers transfer (zero-cost, W-probes) and the body indexes directly. *)
 VAR i: CARDINAL; e: Words; sw: Words;
 BEGIN
   slot := 0; (*{/*0108*/}*)
   IF src = NIL THEN RETURN END; (*{/*010b*/}*)
-  slot := Words(src)^[1]; (*{/*010f*/}*)
+  slot := src^[1]; (*{/*010f*/}*)
   IF slot <> 0 THEN RETURN END; (* named: referenced by name, no entry *)
-  IF CARDINAL(src) < CARDINAL(symHeapBase) THEN (* static node: dedup {/*0118*/}
+  IF CARDINAL(src) < CARDINAL(symHeap) THEN (* static node: dedup {/*0118*/} *)
      (* group B read this bound as global word2; reconciled to symHeapBase:
         word2 is symOptions: BITSET per SYMTAB.SYM, and the heap base is the
         only agreed boundary for "points outside the dynamic heap". *)
     i := 1; (*{/*011d*/}*)
     WHILE i <= scopeTop DO
-      IF Words(scopeHeapBase + 16 * i)^[1] = CARDINAL(src) THEN (*{/*011f*/}*)
+      e := Words(scpHeap + 16 * i); (* temp: transfer results can't be indexed *)
+      IF e^[1] = CARDINAL(src) THEN (*{/*011f*/}*)
         slot := i; RETURN;
       END;
       INC(i); (*{/*012e*/}*)
     END;
     SymbolAssert(i <= scopeCap, 86); (*{/*0137*/}*)
     scopeTop := i;
-    e := Words(scopeHeapBase + 16 * scopeTop);
+    e := Words(scpHeap + 16 * scopeTop);
     e^[1] := CARDINAL(src); (*{/*0141*/}*)
     slot := scopeTop; (*{/*0149*/}*)
     RETURN;
@@ -266,10 +273,10 @@ BEGIN
   SymbolAssert(scopeTop < scopeCap, 86); (*{/*014f*/}*)
   INC(scopeTop);
   slot := scopeTop; (*{/*015c*/}*)
-  e := Words(scopeHeapBase + 16 * scopeTop);
-  sw := Words(src);
+  e := Words(scpHeap + 16 * scopeTop);
+  sw := src;
   e^[1] := 0; (*{/*016b*/}*)
-  PutAttr(e^[2], T1(sw^[2])); (* copy link2 chain {/*016e*/} *)
+  PutAttr(e^[2], Words(sw^[2])); (* copy link2 chain {/*016e*/} *)
   e^[3] := sw^[3]; (*{/*0176*/}*)
   e^[4] := sw^[4]; (* kind {/*017a*/} *)
   CASE sw^[4] OF (* kind switch {/*01b3*/} *)
@@ -279,10 +286,10 @@ BEGIN
     CheckNode(T1(sw^[5])); (*{/*0192*/}*)
     EnterChained(ADR(e^[5]), T1(sw^[5])); (*{/*0197*/}*)
   | 101:
-    PutAttr(e^[5], T1(sw^[5])); (*{/*0189*/}*)
+    PutAttr(e^[5], Words(sw^[5])); (*{/*0189*/}*)
   | 102:
-    PutAttr(e^[5], T1(sw^[5])); (*{/*019e*/}*)
-    PutAttr(e^[6], T1(sw^[6]));
+    PutAttr(e^[5], Words(sw^[5])); (*{/*019e*/}*)
+    PutAttr(e^[6], Words(sw^[6]));
     e^[7] := sw^[7];
   ELSE (* 92..98: name/link/attr words suffice *)
   END;
@@ -290,11 +297,11 @@ END PutAttr;
 PROCEDURE GetAttr(VAR node: T1; idx: CARDINAL);
 (* proc22 @0281 — load a scope entry back into a heap node (inverse of PutAttr).
    Entry word1 = static node pointer or memoized node (returned as-is). *)
-VAR e: Words; new: T1; nw: Words;
+VAR e: Words; new: T1; nw: Words; sub: T1;
 BEGIN
   node := NIL; (*{/*0283*/}*)
   IF idx = 0 THEN RETURN END; (*{/*0286*/}*)
-  e := Words(scopeHeapBase + 16 * idx); (*{/*028a*/}*)
+  e := Words(scpHeap + 16 * idx); (*{/*028a*/}*)
   node := T1(e^[1]); (*{/*0290*/}*)
   IF node <> NIL THEN RETURN END; (*{/*0294*/}*)
   (* Allocator size classes exact: Scanner.NewSized(kind) =
@@ -311,10 +318,10 @@ BEGIN
   | 99, 100:
     UnlinkScope(ADR(nw^[5]), e^[5]); (*{/*02c4*/}*)
   | 101:
-    GetAttr(nw^[5], e^[5]); (*{/*02bb*/}*)
+    sub := T1(nw^[5]); GetAttr(sub, e^[5]); nw^[5] := CARDINAL(sub); (* in-place reload; VAR needs a real variable (v1.00 OOMs on CARDINAL actual) *)
   | 102:
-    GetAttr(nw^[5], e^[5]); (*{/*02cc*/}*)
-    GetAttr(nw^[6], e^[6]);
+    sub := T1(nw^[5]); GetAttr(sub, e^[5]); nw^[5] := CARDINAL(sub);
+    sub := T1(nw^[6]); GetAttr(sub, e^[6]); nw^[6] := CARDINAL(sub);
     nw^[7] := e^[7];
   ELSE (* 92..98: nothing beyond words 1..4 *)
   END;
@@ -323,31 +330,33 @@ END GetAttr;
 (* NOTE (group B): the brief asked for AllocSym(nodeKind): T1, but the MCode
    (proc14 @01d7, ProcLeave(2), uses param1+param2) is a 2-param PROCEDURE
    chaining a 14-byte entry onto scope and filling it from node. *)
-PROCEDURE AllocSym(scope: ADDRESS; node: T1);
-(* proc14 @01d7 — chain-allocate a 14-byte symbol-table entry from node *)
+PROCEDURE AllocSym(scope: Words; node: T1);
+(* proc14 @01d7 — chain-allocate a 14-byte symbol-table entry from node.
+   scope is Words-typed (not ADDRESS): v1.00 rejects indexing a transfer
+   result; callers transfer (zero-cost). *)
 VAR s: Words; n: T1; nw: Words; sw: Words;
 BEGIN
   SymbolAssert(symTop < symCap, 87); (*{/*01d9*/}*)
   INC(symTop);
-  s := Words(symHeapBase + 14 * symTop); (*{/*01e3*/}*)
+  s := Words(symHeap + 14 * symTop); (*{/*01e3*/}*)
   n := T1(s); nw := s; sw := Words(node);
-  s^[0] := Words(scope)^[0]; (*{/*01e9*/}*)
-  Words(scope)^[0] := symTop; (*{/*01ed*/}*)
+  s^[0] := scope^[0]; (*{/*01e9*/}*)
+  scope^[0] := symTop; (*{/*01ed*/}*)
   NewString(s^[1], node^.link1); (*{/*01f0*/}*)
-  PutAttr(s^[2], node^.link2); (*{/*01f7*/}*)
+  PutAttr(s^[2], Words(node^.link2)); (*{/*01f7*/}*)
   n^.word3 := node^.word3; (*{/*01fe*/}*)
   nw^[5] := sw^[5]; nw^[6] := sw^[6]; (* dword {/*0202*/} *)
   n^.word4 := node^.word4; (* kind {/*0208*/} *)
-  IF node^.link2 = Compiler.charArrayDesc THEN (* COMPILER.word12 {/*020c*/} *)
+  IF CARDINAL(node^.link2) = CARDINAL(Compiler.charArrayDesc) THEN (* COMPILER.word12 {/*020c*/}; CARDINAL compare: T1 vs RecordPtr rejected *)
     NewString(nw^[5], ADDRESS(sw^[5])); (*{/*0213*/}*)
   ELSIF nw^[4] >= 5 THEN (*{/*021c*/}*)
     CheckNode(T1(sw^[6])); (*{/*0222*/}*)
     nw^[6] := 0; (*{/*0226*/}*)
     EnterChained(ADR(nw^[6]), T1(sw^[6])); (*{/*0229*/}*)
-  ELSIF (nw^[4] = 1) AND (node^.link2 = Compiler.LongrealType) THEN
+  ELSIF (nw^[4] = 1) AND (CARDINAL(node^.link2) = CARDINAL(Compiler.LongrealType)) THEN
     (* COMPILER.word11: 8-byte real constant {/*0231*/} *)
     SymbolAssert(strTop + 8 <= strCap, 85); (*{/*023e*/}*)
-    MOVE(ADDRESS(sw^[5]), strHeapBase + strTop, 8); (*{/*0248*/}*)
+    MOVE(ADDRESS(sw^[5]), strHeap + strTop, 8); (*{/*0248*/}*)
     INC(strTop, 8); (*{/*0251*/}*)
   END;
 END AllocSym;
@@ -357,59 +366,74 @@ PROCEDURE EnterChained(scope: ADDRESS; node: T1);
 BEGIN
   IF node <> NIL THEN (*{/*025b*/}*)
     EnterChained(scope, node^.link0); (*{/*025f*/}*)
-    AllocSym(scope, node); (*{/*0263*/}*)
+    AllocSym(Words(scope), node); (*{/*0263*/}*)
   END;
 END EnterChained;
 
-PROCEDURE EnterIdent(scope: ADDRESS; idx: CARDINAL);
-(* proc20 @0305 — declare symbol-table entry idx into scope (dup check + attrs) *)
+PROCEDURE EnterIdent(scope: Words; idx: CARDINAL);
+(* proc20 @0305 — declare symbol-table entry idx into scope (dup check + attrs).
+   scope is Words-typed (same transfer-index rule as PutAttr/AllocSym). *)
 VAR sentry: Words;          (* symHeapBase + idx*14 *)
     new: T1; nw: Words;
-    existing: T1;
     mark: ADDRESS;
     nm, nm2: ADDRESS;       (* heap names via GetName *)
     nmt: StringPtr;
+    vw: Words;              (* temp for T1-field views *)
+    rp: Compiler.RecordPtr; (* temp: transfer results can't be dereferenced; also FindIden result *)
 BEGIN
-  sentry := Words(symHeapBase + 14 * idx); (*{/*0307*/}*)
+  sentry := Words(symHeap + 14 * idx); (*{/*0307*/}*)
   new := Scanner.NewNode(sentry^[4]); (* kind; always 14 bytes {/*030d*/} *)
   nw := Words(new);
-  nw^[0] := Words(scope)^[0]; (* chain at head {/*0312*/} *)
+  nw^[0] := scope^[0]; (* chain at head {/*0312*/} *)
   GetName(nm, sentry^[1]); (* heap copy of name {/*0316*/} *)
   nw^[1] := CARDINAL(nm);
   nmt := nm;
-  existing := Scanner.FindIden(Scanner.List(scope), nm, 9 IN Scanner.scanOpt);
+  nm2 := CARDINAL(Scanner.List(scope)); (* stash List word; transfers are zero-cost *)
+  rp := Scanner.FindIden(Scanner.List(nm2), nm, 9 IN Scanner.scanOpt);
   (* SCANNER.proc22 + scanOptions {/*031f*/} *)
-  IF (existing <> NIL) AND NOT (5 IN Compiler.RecordPtr(existing)^.word3) THEN
-    (* FIXME: word3 access needs Compiler.Record layout; original reads the
-       found node's flags word. See Compiler.Record (word3: BITSET). *)
+  IF (rp <> NIL) AND NOT (5 IN rp^.word3) THEN
+    (* original reads the found node's flags word (Compiler.Record word3: BITSET). *)
     Errors.ReportErrorWithText(1, nmt^); (*{/*033a*/}*)
   END;
-  Words(scope)^[0] := CARDINAL(new); (*{/*0341*/}*)
+  scope^[0] := CARDINAL(new); (*{/*0341*/}*)
   GetAttr(new^.link2, sentry^[2]); (*{/*0344*/}*)
   new^.word3 := BITSET(sentry^[3]); (*{/*034b*/}*)
   new^.word4 := sentry^[4]; (*{/*034f*/}*)
+  (* NOTE: original chains these as IF/ELSIF; v1.00 retail pins all pending
+     ELSIF conditions in expression heap and OOMs. Flattened to sequential
+     IF+RETURN (equivalent: arms exclusive-first-match and tail-positioned). *)
   IF sentry^[4] = 3 THEN (* constant: back-link value node {/*0353*/} *)
-    IF Words(new^.link2)^[0] = 0 THEN (*{/*0359*/}*)
-      Words(new^.link2)^[0] := CARDINAL(new); (*{/*035f*/}*)
+    vw := Words(new^.link2);
+    IF vw^[0] = 0 THEN (*{/*0359*/}*)
+      vw^[0] := CARDINAL(new); (*{/*035f*/}*)
     END;
-  ELSIF new^.link2 = Compiler.charArrayDesc THEN (* COMPILER.word12 {/*0365*/} *)
+    RETURN;
+  END;
+  IF CARDINAL(new^.link2) = CARDINAL(Compiler.charArrayDesc) THEN (* COMPILER.word12 {/*0365*/} *)
     GetName(nm2, sentry^[5]); (*{/*0372*/}*)
     nw^[5] := CARDINAL(nm2);
-  ELSIF sentry^[4] >= 5 THEN (*{/*037b*/}*)
-    IF existing = NIL THEN (*{/*0381*/}*)
+    RETURN;
+  END;
+  IF sentry^[4] >= 5 THEN (*{/*037b*/}*)
+    IF rp = NIL THEN (*{/*0381*/}*)
       Scanner.GetStack(mark); (*{/*0385*/}*)
       nw^[6] := 0; (*{/*0389*/}*)
-      UnlinkScope(ADR(nw^[6]), sentry^[6]); (*{/*038c*/}*)
-      IF (sentry^[4] = 5) AND (stageFlag <> 0) THEN (*{/*0393*/} global25 *)
-        Compiler.allocateSignature(new, mark); (* COMPILER.dword27 gate {/*039d*/} *)
+      nm2 := ADDRESS(nw) + 12; (* address of nw^[6] without ADR(indexed) (expression-heap spike on v1.00) *)
+      UnlinkScope(nm2, sentry^[6]); (*{/*038c*/}*)
+      IF (sentry^[4] = 5) AND (stgFlag <> 0) THEN (*{/*0393*/} global25 *)
+        Compiler.allocHook(ADDRESS(new), mark); (* hook ADDRESS set by PASS1 (store dword27); was allocateSignature (a type symbol) — wrong target {/*039d*/} *)
       END;
     ELSE
-      nw^[6] := Words(existing)^[6]; (*{/*03a5*/}*)
+      vw := Words(rp);
+      nw^[6] := vw^[6]; (*{/*03a5*/}*)
     END;
-  ELSIF (sentry^[4] = 1) AND (new^.link2 = Compiler.LongrealType) THEN
+    RETURN;
+  END;
+  IF (sentry^[4] = 1) AND (CARDINAL(new^.link2) = CARDINAL(Compiler.LongrealType)) THEN
     (* COMPILER.word11; 8-byte real value {/*03ab*/} *)
-    Scanner.Allocate(ADR(nw^[5]), 8); (*{/*03b8*/}*)
-    MOVE(strHeapBase + sentry^[5], ADDRESS(nw^[5]), 8); (*{/*03be*/}*)
+    nm2 := ADR(nw^[5]); (* temp: VAR formal rejects ADR() results (proven "Variable expected"); original pushes computed address directly *)
+    Scanner.Allocate(nm2, 8); (*{/*03b8*/}*)
+    MOVE(strHeap + sentry^[5], ADDRESS(nw^[5]), 8); (*{/*03be*/}*)
   END;
 END EnterIdent;
 
@@ -418,9 +442,9 @@ PROCEDURE UnlinkScope(scope: ADDRESS; idx: CARDINAL);
 VAR e: Words;
 BEGIN
   IF idx <> 0 THEN (*{/*03cb*/}*)
-    e := Words(symHeapBase + 14 * idx);
+    e := Words(symHeap + 14 * idx);
     UnlinkScope(scope, e^[0]); (*{/*03cf*/}*)
-    EnterIdent(scope, idx); (*{/*03d8*/}*)
+    EnterIdent(Words(scope), idx); (*{/*03d8*/}*)
   END;
 END UnlinkScope;
 
@@ -434,13 +458,13 @@ BEGIN
     CodeGen.FlushCodeWindow;
   END;
   freeBytes := 4096 - (CodeGen.nextEmitPos - CodeGen.windowBase); (*{/*03ee*/}*)
-  symHeapBase := Scanner.codeBuf + CodeGen.nextEmitPos - CodeGen.windowBase; (*{/*03f8*/}*)
+  symHeap := Scanner.codeBuf + CodeGen.nextEmitPos - CodeGen.windowBase; (*{/*03f8*/}*)
   symCap := freeBytes DIV 28 - 1; (*{/*0402*/} DIV (2*14) *)
-  scopeHeapBase := symHeapBase + (symCap + 1) * 14; (*{/*040a*/}*)
+  scpHeap := symHeap + (symCap + 1) * 14; (*{/*040a*/}*)
   scopeCap := freeBytes DIV 64 + 7; (*{/*0412*/} DIV (4 SHL 4) *)
-  strHeapBase := scopeHeapBase + (scopeCap + 1) * 16; (*{/*041b*/}*)
-  strCap := symHeapBase + freeBytes - strHeapBase - 1; (*{/*0423*/}*)
-  FILL(symHeapBase, freeBytes, 0); (*{/*042b*/} SYSTEM.FILL unofficial — kept *)
+  strHeap := scpHeap + (scopeCap + 1) * 16; (*{/*041b*/}*)
+  strCap := symHeap + freeBytes - strHeap - 1; (*{/*0423*/}*)
+  FILL(symHeap, freeBytes, 0); (*{/*042b*/} SYSTEM.FILL unofficial — kept *)
 END InitHeaps;
 
 PROCEDURE EnterStandards;
@@ -451,7 +475,7 @@ VAR stdNo: CARDINAL; (* slot 0 reserved *)
   (* proc25 @0432, nested *)
   VAR ent: ScopeEntryPtr;
   BEGIN
-    ent := scopeHeapBase + stdNo * 16;
+    ent := scpHeap + stdNo * 16;
     ent^.link := t; (* word 1 {/*0434*/} *)
     INC(stdNo); (*{/*043e*/}*)
   END InsertBuiltin;
@@ -501,11 +525,11 @@ BEGIN
   SymbolAssert(scopeTop <= scopeCap, 86);
   strTop := header[3]; (*{/*04db*/}*)
   SymbolAssert(strTop <= strCap, 85);
-  exportHead := header[4]; (*{/*04e7*/}*)
-  stageNameTotal := header[0]; (*{/*04eb*/}*)
-  savedSymInfo := header[5]; (*{/*04ef*/}*)
-  savedStrInfo := header[6]; (*{/*04f3*/}*)
-  LibRead(modStage, stageNameTotal * 10); (* module-name directory {/*04f7*/} *)
+  expHead := header[4]; (*{/*04e7*/}*)
+  namTotal := header[0]; (*{/*04eb*/}*)
+  savSym := header[5]; (*{/*04ef*/}*)
+  savStr := header[6]; (*{/*04f3*/}*)
+  LibRead(modStage, namTotal * 10); (* module-name directory {/*04f7*/} *)
   dest := modStage + 160; (* slot 16 {/*04fe*/} *)
   FOR i := 0 TO 7 DO dest^.name[i] := 0C END; (*{/*0505*/}*)
   i := 0;
@@ -514,9 +538,9 @@ BEGIN
     INC(i);
   END;
   dest^.tag := header[7]; (*{/*052c*/}*)
-  LibRead(symHeapBase + 14, symTop * 14); (* symbol entries 1.. {/*0530*/} *)
-  LibRead(scopeHeapBase + 240, (scopeTop - 14) * 16); (* scope entries 15.. {/*053a*/} *)
-  LibRead(strHeapBase + 1, strTop - 1); (* string pool {/*0547*/} *)
+  LibRead(symHeap + 14, symTop * 14); (* symbol entries 1.. {/*0530*/} *)
+  LibRead(scpHeap + 240, (scopeTop - 14) * 16); (* scope entries 15.. {/*053a*/} *)
+  LibRead(strHeap + 1, strTop - 1); (* string pool {/*0547*/} *)
   EnterStandards; (* rewire builtin slots 1..14 {/*054f*/} *)
 END LoadSymFile;
 PROCEDURE ImportAll(importRec: ADDRESS);
@@ -545,8 +569,8 @@ VAR
   BEGIN
     idx := start;
     WHILE idx # 0 DO
-      e := symHeapBase + idx * 14; (*{/*0559*/}*)
-      IF Scanner.StrCmp(StringPtr(symHeapBase + strHeapBase + e^.nameOff)^,
+      e := symHeap + idx * 14; (*{/*0559*/}*)
+      IF Scanner.StrCmp(StringPtr(symHeap + strHeap + e^.nameOff)^,
                         StringPtr(name)^, 9 IN Scanner.scanOpt) THEN (*{/*0568*/}*)
         RETURN idx;
       END;
@@ -560,14 +584,14 @@ VAR
   VAR e: SymEntryPtr;
   BEGIN
     IF first # 0 THEN
-      e := symHeapBase + first * 14;
+      e := symHeap + first * 14;
       ListExports(e^.link); (*{/*0588*/}*)
       IF 0 IN e^.flags THEN (*{/*058a*/}*)
         IF colCount = 5 THEN (*{/*0590*/}*)
           Texts.WriteLn(3); colCount := 0;
         END;
         Texts.SetCol(3, colCount * 16); (*{/*05a0*/}*)
-        Texts.WriteString(3, StringPtr(strHeapBase + e^.nameOff)^); (*{/*05af*/}*)
+        Texts.WriteString(3, StringPtr(strHeap + e^.nameOff)^); (*{/*05af*/}*)
         INC(colCount); (*{/*05b4*/}*)
       END;
     END;
@@ -579,10 +603,10 @@ VAR
   VAR e: SymEntryPtr;
   BEGIN
     IF symIdx # 0 THEN
-      e := symHeapBase + symIdx * 14; (*{/*05c4*/}*)
+      e := symHeap + symIdx * 14; (*{/*05c4*/}*)
       ImportOne(anchor, e^.link); (*{/*05ca*/}*)
       IF 0 IN e^.flags THEN (*{/*05d0*/}*)
-        EnterIdent(anchor, symIdx); (*{/*05d6*/}*)
+        EnterIdent(Words(anchor), symIdx); (*{/*05d6*/}*)
       END;
     END;
   END ImportOne;
@@ -613,14 +637,14 @@ BEGIN
     END;
     RETURN;
   END;
-  stageFlag := 1; (* word 25 {/*0635*/}; group C reads compileActive *)
+  stgFlag := 1; (* word 25 {/*0635*/}; group C reads compileActive *)
   InitHeaps; (*{/*0638*/}*)
   LoadSymFile(StringPtr(imp^.name)^);
   modIdx := EnterModule(16); (*{/*0640*/}*)
   SymbolAssert(modIdx # 16, 48); (*{/*0644*/}*)
-  expIdx := exportHead; (*{/*064b*/}*)
+  expIdx := expHead; (*{/*064b*/}*)
   WHILE expIdx # 0 DO (*{/*064e*/}*)
-    e := symHeapBase + expIdx * 14;
+    e := symHeap + expIdx * 14;
     IF e^.flags * BITSET{1,6} # BITSET{} THEN (* AND 42H {/*0659*/} *)
       e^.flags := e^.flags - BITSET{0}; (*{/*0663*/}*)
     ELSE
@@ -636,7 +660,7 @@ BEGIN
   END;
   scopeIdx := 15;
   WHILE scopeIdx <= scopeTop DO (*{/*069a*/}*)
-    s := scopeHeapBase + scopeIdx * 16;
+    s := scpHeap + scopeIdx * 16;
     matched := LookupChain(FindModule(ORD(s^.slot)), ORD(s^.fresh)); (*{/*06ab*/}*)
     IF matched # 0 THEN (*{/*06b2*/}*)
       s^.link := matched; s^.fresh := 0C;
@@ -644,32 +668,32 @@ BEGIN
     INC(scopeIdx);
   END;
   IF importEvery THEN (*{/*06c4*/}*)
-    ImportOne(ADR(imp^.anchor), exportHead); (* NOTE order: see ImportOne note *)
+    ImportOne(ADR(imp^.anchor), expHead); (* NOTE order: see ImportOne note *)
   ELSE
     REPEAT
       Scanner.NeedID; (*{/*06d0*/}*)
-      findIdx := FindExport(exportHead, ADR(Scanner.tokBuf)); (*{/*06d2*/}*)
-      e := symHeapBase + findIdx * 14;
+      findIdx := FindExport(expHead, ADR(Scanner.tokBuf)); (*{/*06d2*/}*)
+      e := symHeap + findIdx * 14;
       IF (findIdx = 0) OR NOT (0 IN e^.flags) THEN (*{/*06e0*/}*)
         Errors.PrintOverlayMessage(3, Scanner.tokBuf); (*{/*06ed*/}*)
         Texts.WriteLn(3);
         Texts.WriteLn(3);
         Texts.WriteString(3, "Exported are:"); (*{/*06f9*/}*)
         colCount := 5; (*{/*070d*/}*)
-        ListExports(exportHead); (*{/*070e*/}*)
+        ListExports(expHead); (*{/*070e*/}*)
         Texts.WriteLn(3);
         Errors.AskEditOrQuit; (*{/*0715*/}*)
       END;
-      IF (e^.kind = 3) AND (ScopeEntryPtr(scopeHeapBase + e^.ref * 16)^.state = 0) THEN (*{/*0717*/}*)
+      IF (e^.kind = 3) AND (ScopeEntryPtr(scpHeap + e^.ref * 16)^.state = 0) THEN (*{/*0717*/}*)
         sib := e^.link; (*{/*0729*/}*)
         WHILE sib # 0 DO
-          e2 := symHeapBase + sib * 14;
+          e2 := symHeap + sib * 14;
           IF (e2^.ref # e^.ref) OR NOT (9 IN e2^.flags) THEN EXIT END; (*{/*072d*/}*)
-          EnterIdent(ADR(currentScope^.link1), sib); (*{/*0747*/}*)
+          EnterIdent(Words(ADR(currentScope^.link1)), sib); (*{/*0747*/}*)
           sib := e2^.link;
         END;
       END;
-      EnterIdent(ADR(currentScope^.link1), findIdx); (*{/*0756*/}*)
+      EnterIdent(Words(ADR(currentScope^.link1)), findIdx); (*{/*0756*/}*)
       Scanner.GetSym; (*{/*075c*/}*)
       Scanner.TestSet(BITSET{1,9}); (*{/*075e*/}*)
     UNTIL NOT Scanner.AcceptSy(1); (* "," {/*0763*/} *)
@@ -677,7 +701,7 @@ BEGIN
   END;
   scopeIdx := 15;
   WHILE scopeIdx <= scopeTop DO (*{/*076b*/}*)
-    s := scopeHeapBase + scopeIdx * 16;
+    s := scpHeap + scopeIdx * 16;
     IF (s^.link # 0) AND (s^.fresh # 0C) THEN (*{/*077a*/}*)
       modIdx := EnterModule(ORD(s^.slot)); (*{/*0787*/}*)
       Scanner.Allocate(tmp, 6); (*{/*078d*/}*)
@@ -807,7 +831,7 @@ BEGIN
   InitHeaps;
   symTop := 0; scopeTop := 0; strTop := 1;
   symOptions := opt;
-  exportHead := 0; (* FIXME: group wrote modExpCount := 0; unified with exportHead (word18) *)
+  expHead := 0; (* FIXME: group wrote modExpCount := 0; unified with exportHead (word18) *)
   IF imports <> NIL THEN (*{/*0928*/}*)
     WHILE imports <> NIL DO
       currentScope^.link1 :=
@@ -887,7 +911,7 @@ BEGIN
     Scanner.TestSet(BITSET{3});
   END;
   InitHeaps; EnterStandards;
-  symTop := 0; scopeTop := 14; strTop := 1; exportHead := 0;
+  symTop := 0; scopeTop := 14; strTop := 1; expHead := 0;
   currentScope^.link1 := MatchSym(desc, save^.link1);
   i := 0;
   WHILE i < moduleCount DO
@@ -902,6 +926,7 @@ END CompileBody;
 PROCEDURE CheckForwards;
 (* proc36 @0aa7. FIXME sketch, see CompileBody note. *)
 VAR i: CARDINAL; base: T1; slot: ADDRESS;
+    wslot, wbase, wchain: Words; (* temps: transfer results can't be indexed *)
 BEGIN
   Compiler.compilationActive := FALSE; (*{/*0aa7*/}*)
   InitHeaps;
@@ -909,32 +934,35 @@ BEGIN
      second arg unknown. Omitted pending re-read. *)
   i := 1;
   WHILE i <= symTop DO (*{/*0ac2*/}*)
-    base := T1(symHeapBase + i * 14);
+    base := T1(symHeap + i * 14);
     IF base^.word4 = 5 THEN INCL(base^.word3, 5) END;
     INC(i);
   END;
   UnlinkScope(currentScope^.link1, CARDINAL(currentScope^.link1)); (* FIXME arg roles TBD *)
   i := 15;
   WHILE i <= scopeTop DO (*{/*0ae8*/}*)
-    base := T1(scopeHeapBase + i * 16);
+    base := T1(scpHeap + i * 16);
     Scanner.Allocate(slot, 6);
-    Words(slot)^[0] := Words(scopeChain^[Words(base)^[0]])^[0];
-    Words(slot)^[1] := Words(base)^[1];
-    Words(slot)^[2] := CARDINAL(base^.link1);
-    scopeChain^[Words(base)^[0]] := slot;
+    wslot := Words(slot); wbase := Words(base);
+    wchain := Words(scopeChain^[wbase^[0]]);
+    wslot^[0] := wchain^[0];
+    wslot^[1] := wbase^[1];
+    wslot^[2] := CARDINAL(base^.link1);
+    scopeChain^[wbase^[0]] := slot;
     INC(i);
   END;
 END CheckForwards;
 
 PROCEDURE TryCompile(): BOOLEAN;
-(* IMDCLS @0b1c. FIXME sketch: frame-37 loop + error-growth recovery. *)
+(* IMDCLS @0b1c. Recovery: on exception, if the user edited (editPos advanced
+   past savePos) roll back to the mark and retry; else HALT. Proven: 0b4c
+   compares ERRORS.word2 (savedEditPos) against saved tokPos. *)
 VAR saveOpt: BITSET; savePos: CARDINAL; saveMark: ADDRESS;
-    saveScope: T1; saveCount, saveErr: CARDINAL;
+    saveScope: T1; saveCount: CARDINAL;
 BEGIN
   saveOpt := Scanner.scanOpt; savePos := Scanner.tokPos;
   Scanner.GetStack(saveMark);
   saveScope := currentScope^.link1; saveCount := moduleCount;
-  saveErr := Errors.errorNumber;
   IF (Scanner.curSym - 15) IN BITSET{3} THEN
     DoImportList;
     LOOP
@@ -947,7 +975,7 @@ BEGIN
   RETURN TRUE;
 EXCEPTION
   moduleCount := saveCount; currentScope^.link1 := saveScope;
-  IF Errors.errorNumber > saveErr THEN (*{/*0b4c*/}*)
+  IF Errors.savedEditPos > savePos THEN (*{/*0b4c*/}*)
     Scanner.tokPos := savePos; Scanner.scanOpt := saveOpt;
     Scanner.CheckStk(saveMark);
     Errors.RollbackToMark;
@@ -976,10 +1004,10 @@ PROCEDURE Checksum7w(ptr: ADDRESS; nwords: CARDINAL);
 VAR acc: CARDINAL;
 BEGIN
   WHILE nwords >= 14 DO (* FIXME: group condition ptr^ >= 14 kept as count form *)
-    acc := checksumTemp + Words(ptr)^[0] + Words(ptr)^[1] * 2 + Words(ptr)^[2] * 4
+    acc := chkTemp + Words(ptr)^[0] + Words(ptr)^[1] * 2 + Words(ptr)^[2] * 4
          + Words(ptr)^[3] * 8 + Words(ptr)^[4] * 16 + Words(ptr)^[5] * 32
          + Words(ptr)^[6] * 64;
-    checksumTemp := acc;
+    chkTemp := acc;
     ptr := ptr + 14; nwords := nwords - 14;
   END;
 END Checksum7w;
@@ -988,20 +1016,20 @@ PROCEDURE SaveSymbolFile;
 (* proc6 @0bdb *)
 VAR hdr: ARRAY [0..15] OF WORD;
 BEGIN
-  checksumTemp := 0;
+  chkTemp := 0;
   Checksum7w(ADR(moduleTable^), moduleCount * 10);
-  Checksum7w(symHeapBase + 14, symTop * 14);
-  Checksum7w(scopeHeapBase + 240, (scopeTop - 14) * 16);
-  Checksum7w(strHeapBase + 1, strTop - 1);
+  Checksum7w(symHeap + 14, symTop * 14);
+  Checksum7w(scpHeap + 240, (scopeTop - 14) * 16);
+  Checksum7w(strHeap + 1, strTop - 1);
   hdr[0] := moduleCount; hdr[1] := symTop;
   hdr[2] := scopeTop; hdr[3] := strTop;
-  hdr[4] := exportHead; hdr[5] := savedSymInfo; hdr[6] := savedStrInfo;
-  hdr[7] := checksumTemp;
+  hdr[4] := expHead; hdr[5] := savSym; hdr[6] := savStr;
+  hdr[7] := chkTemp;
   Files.WriteBytes(Scanner.codeFile, ADR(hdr), 7);
   Files.WriteBytes(Scanner.codeFile, ADR(moduleTable^), moduleCount * 10);
-  Files.WriteBytes(Scanner.codeFile, symHeapBase + 14, symTop * 14);
-  Files.WriteBytes(Scanner.codeFile, scopeHeapBase + 240, (scopeTop - 14) * 16);
-  Files.WriteBytes(Scanner.codeFile, strHeapBase + 1, strTop - 1);
+  Files.WriteBytes(Scanner.codeFile, symHeap + 14, symTop * 14);
+  Files.WriteBytes(Scanner.codeFile, scpHeap + 240, (scopeTop - 14) * 16);
+  Files.WriteBytes(Scanner.codeFile, strHeap + 1, strTop - 1);
 END SaveSymbolFile;
 
 PROCEDURE RestoreAfterEdit;