Explorar el Código

feat(compiler): renamed readable sources in src/compiler + map

Module-level procN/globalN -> meaningful names (COMPILER, SCANNER,
ERRORS, CODEGEN, SYMTAB.DEF); DEF params renamed; MOD params/locals
kept for reviewable diff. Semantics unchanged, NUL/0x1A stripped.
Eric Streit hace 1 semana
padre
commit
5ad9d81eaa

+ 36 - 0
src/compiler/CODEGEN.DEF

@@ -0,0 +1,36 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+DEFINITION MODULE CODEGEN;
+FROM SYSTEM IMPORT BYTE, WORD, ADDRESS;
+
+VAR
+(*  2 *) nextEmitPos : CARDINAL;
+(*  3 *) windowBase : CARDINAL;
+(*  4 *) windowLimit : CARDINAL;
+(*  5 *) emitEnabled : BOOLEAN;
+
+PROCEDURE Emit1(op: BYTE);
+PROCEDURE Emit2(op2, op1: BYTE);
+PROCEDURE EmitWord(w: WORD);
+PROCEDURE DiscardPending;
+PROCEDURE SetPendingOp(mode, size, disp, off: CARDINAL);
+PROCEDURE EmitTypedOp(subOp, typeKind: CARDINAL);
+PROCEDURE EmitExtendedOp(subOp, typeKind: CARDINAL);
+PROCEDURE QueueConst(value: CARDINAL);
+PROCEDURE QueueLongConst(kind: CARDINAL; value: LONGINT);
+PROCEDURE EmitStandardOp(stdNo: CARDINAL);
+PROCEDURE EmitMiscOp(subOp, n: CARDINAL);
+PROCEDURE OpenFixup(VAR fixPos: CARDINAL; shortJump: BOOLEAN);
+PROCEDURE CloseFixup(fixPos: CARDINAL; shortJump: BOOLEAN);
+PROCEDURE InsertFixup(fixPos: CARDINAL; shortJump: BOOLEAN): BOOLEAN;
+PROCEDURE AdjustFixup(fixPos: CARDINAL);
+PROCEDURE MarkCodePos(n: CARDINAL): CARDINAL;
+PROCEDURE EmitSystemCall(sysNo: CARDINAL);
+PROCEDURE EmitExtCall3(a, b, c: CARDINAL);
+PROCEDURE PeekCodeByte(pos: CARDINAL): CARDINAL;
+PROCEDURE PokeCodeByte(value: BYTE; pos: CARDINAL);
+PROCEDURE EmitString(s: ADDRESS);
+PROCEDURE OpenEmitter;
+PROCEDURE FlushCodeWindow;
+PROCEDURE InitCodeGenerator;
+
+END CODEGEN.

+ 853 - 0
src/compiler/CODEGEN.MOD

@@ -0,0 +1,853 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+IMPLEMENTATION MODULE CodeGen;
+IMPORT Scanner, Files, Compiler;
+FROM ComLine IMPORT codepos, execute;
+FROM SYSTEM IMPORT ADR, MOVE;
+
+CONST
+    OPINVALID =   0;
+    OPRAISE   =   1;
+    OPLDPROC  =   2;
+    OPLDPARAM =   3;
+    OPLLD     =   8;
+    OPLGD     =   9;
+    OPLSD     =  0AH;
+    OPLED     =  0BH;
+    OPLDEXT   =  0CH;
+    OPLXB     =  0DH;
+    OPLXW     =  0EH;
+    OPLXD     =  0FH;
+
+    OPLDIX    =  10H;
+    OPLDIXN   =  11H;
+    OPLONGREAL=  12H;
+    OPSETPARM =  13H;
+    OPSLD     =  18H;
+    OPSGD     =  19H;
+    OPSSD     =  1AH;
+    OPSED     =  1BH;
+    OPSTEXT   =  1CH;
+    OPXSB     =  1DH;
+    OPSXW     =  1EH;
+    OPSXD     =  1FH;
+
+    OPDUP     =  20H;
+    OPSWAP    =  21H;
+    OPLLW2    =  22H;
+    OPLLWN    =  2CH;
+    OPLGWN    =  2DH;
+    OPLSWN    =  2EH;
+    OPLEWN    =  2FH;
+    
+    OPMOVB    =  30H;
+    OPMOVS    =  31H;
+    OPSLW2    =  32H;
+    OPSLWN    =  3CH;
+    OPSGWN    =  3DH;
+    OPSSWN    =  3EH;
+    OPSEWN    =  3FH;
+
+    OPEXTENDED=  40H;
+    OPLSD0    =  41H;
+    OPLGW2    =  42H;
+
+    OPENDPROG =  50H;
+    OPSSD0    =  51H;
+    OPSGW2    =  52H;
+ 
+    OPLSW0    =  60H;
+    OPSSW0    =  70H;
+
+    OPLLA     =  80H;
+    OPLGA     =  81H;
+    OPLSA     =  82H;
+    OPLEA     =  83H;
+    OPLEAVE   =  84H;
+    OPFLEAVE  =  85H;
+    OPLFLEAVE =  86H;
+    OPASM     =  87H;
+    OPLEAVE0  =  88H;
+    OPCALLREL =  8CH;
+    OPLIB     =  8DH;
+    OPLIW     =  8EH;
+    OPLID     =  8FH;
+
+    OPLI0     =  90H;
+    OPLI15    =  9FH;
+
+    OPEQUAL   = 0A0H;
+    OPNEQ     = 0A1H;
+    OPLESS    = 0A2H;
+    OPGREATER = 0A3H;
+    OPLESSEQ  = 0A4H;
+    OPGREATEQ = 0A5H;
+    OPADD     = 0A6H;
+    OPSUB     = 0A7H;
+    OPMUL     = 0A8H;
+    OPDIV     = 0A9H;
+    OPMOD     = 0AAH;
+    OPEQ0     = 0ABH;
+    OPINC     = 0ACH;
+    OPDEC     = 0ADH;
+    OPADDN    = 0AEH;
+    OPSUBN    = 0AFH;
+
+    OPSHL     = 0B0H;
+    OPSHR     = 0B1H;
+    OPILESS   = 0B2H;
+    OPIGREATER= 0B3H;
+    OPILESSEQ = 0B4H;
+    OPIGREATEQ= 0B5H;
+    OPNOT     = 0B6H;
+    OPCOMPL   = 0B7H;
+    OPIMUL    = 0B8H;
+    OPIDIV    = 0B9H;
+    OPLG2CARD = 0BAH;
+    OPLG2INT  = 0BBH;
+    OPABS     = 0BCH;
+    OPINT2LG  = 0BDH;
+    OPLG2FLOAT= 0BEH;
+    OPFLOAT2LG= 0BFH;
+
+    OPADDOV   = 0C0H;
+    OPSUBOV   = 0C1H;
+    OPMULOV   = 0C2H;
+    OPSYSTEM  = 0C3H;
+    OPSTRCOMP = 0C4H;
+    OPDCOMP   = 0C5H;
+    OPDADD    = 0C6H;
+    OPDSUB    = 0C7H;
+    OPDDIV    = 0C8H;
+    OPDMOD    = 0C9H;
+    OPNEQ0    = 0CAH;
+    OPDABS    = 0CBH;
+    OPCASE    = 0CDH;
+    OPRETURN  = 0CEH;
+    OPPUSHREL = 0CFH;
+
+    OPIADDOV  = 0D0H;
+    OPISUBOV  = 0D1H;
+    OPSTKRES  = 0D2H;
+    OPSTRRES  = 0D3H;
+    OPENTER   = 0D4H;
+    OPREALCMP = 0D5H;
+    OPREALADD = 0D6H;
+    OPREALSUB = 0D7H;
+    OPREALMUL = 0D8H;
+    OPREALDIV = 0D9H;
+    OPRANGE   = 0DAH;
+    OPIRANGE  = 0DBH;
+    OPLIMIT   = 0DCH;
+    OPPOSITIV = 0DDH;
+    OPANDJP   = 0DEH;
+    OPORJP    = 0DFH;
+
+    OPJP      = 0E0H;
+    OPJPCOND  = 0E1H;
+    OPJPF     = 0E2H;
+    OPJPFCOND = 0E3H;
+    OPJPB     = 0E4H;
+    OPJPBCOND = 0E5H;
+    OPBITOR   = 0E6H;
+    OPBITIN   = 0E7H;
+    OPBITAND  = 0E8H;
+    OPBITXOR  = 0E9H;
+    OPPOWER2  = 0EAH;
+    OPEXTCALLS= 0EBH;
+    OPINTCALL = 0ECH;
+    OPCALL    = 0EDH;
+    OPCALLFRM = 0EEH;
+    OPEXTCALL2= 0EFH;
+
+    OPEXTCALL1= 0F0H;
+    OPCALL1   = 0F1H;
+
+TYPE Record = RECORD 
+                word0: CARDINAL;
+                CASE : CARDINAL OF
+                | 0: word1,word2: CARDINAL; 
+                | 1: ptr1: POINTER TO ARRAY [0..255] OF CHAR;
+                | 2: long1: LONGINT;
+                END;
+              END;
+     RecordPtr = POINTER TO Record;
+
+VAR 
+(*  6 *) codeWindow : POINTER TO ARRAY [0..2047] OF BYTE;
+(*  7 *) pendMode : [0..9];
+(*  8 *) pendSize : [0..5];
+(*  9 *) pendDisp : CARDINAL;
+(* 10 *) pendOffset: CARDINAL;
+(* 11 *) fixupQueue: ARRAY [0..15] OF Record;
+(* 12 *) fixupCount: [0..16];
+(* 13 *) spare13: WORD;
+(* 14 *) pendActive: BOOLEAN;
+(* 15 *) checkOverflow: BOOLEAN;
+(* 16 *) reservedBytes: ARRAY [0..11] OF BYTE;
+(* 17 *) reservedWord: WORD;
+
+EXCEPTION errorfound;
+
+(* $[+ remove procedure names *)
+
+PROCEDURE CodeAssert(param1: BOOLEAN);
+EXCEPTION CE;
+BEGIN
+  IF NOT param1 THEN RAISE CE END;
+END CodeAssert;
+
+PROCEDURE FlushCodeWindow;
+BEGIN 
+  Files.SetPos(Scanner.codeFile, LONG(windowBase));
+  Files.WriteBytes(Scanner.codeFile, ADDRESS(codeWindow), 2048);
+  INC(windowBase, 2048);
+  INC(windowLimit, 2048);
+  MOVE(ADDRESS(codeWindow) + 2048, ADDRESS(codeWindow), nextEmitPos - windowBase);
+END FlushCodeWindow;
+
+PROCEDURE SeekCodeWindow;
+VAR
+    local2 : CARDINAL;
+    local3 : CARDINAL;
+    local4 : CARDINAL;
+BEGIN
+  local2 := nextEmitPos DIV 512;
+  local3 := (local2 - ORD(local2 <> 0)) * 512;
+  IF local3 < windowBase THEN
+    local4 := windowBase - local3;
+    IF nextEmitPos > windowBase THEN
+      MOVE(ADDRESS(codeWindow), ADDRESS(codeWindow)+local4, nextEmitPos-windowBase);
+    END;
+    Files.SetPos(Scanner.codeFile, LONG(local3));
+    CodeAssert(Files.ReadBytes(Scanner.codeFile, ADDRESS(codeWindow), local4) = local4);
+    windowBase := local3;
+    windowLimit := windowBase + 4096;
+  END;
+END SeekCodeWindow;
+
+PROCEDURE PeekCodeByte(param1: CARDINAL): CARDINAL;
+VAR byte: BYTE;
+BEGIN
+  IF param1 >= windowBase THEN RETURN CARDINAL(codeWindow^[param1-windowBase]) END;
+  Files.SetPos(Scanner.codeFile, LONG(param1));
+  Files.ReadByte(Scanner.codeFile, byte);
+  RETURN CARDINAL(byte)
+END PeekCodeByte;
+
+PROCEDURE PokeCodeByte(param2: BYTE; param1: CARDINAL);
+BEGIN
+  IF param1 >= windowBase THEN
+    codeWindow^[param1-windowBase] := param2;
+    RETURN
+  END;
+  Files.SetPos(Scanner.codeFile, LONG(param1));
+  Files.WriteByte(Scanner.codeFile, param2);
+END PokeCodeByte;
+
+PROCEDURE CheckCodeOverflow;
+BEGIN
+  IF nextEmitPos >= codepos THEN RAISE errorfound END;
+END CheckCodeOverflow;
+
+PROCEDURE Emit1(param1: BYTE); 
+BEGIN 
+  IF nextEmitPos >= windowLimit THEN FlushCodeWindow END;
+  codeWindow^[nextEmitPos-windowBase] := param1;
+  INC(nextEmitPos);
+  IF checkOverflow THEN CheckCodeOverflow END;
+END Emit1;
+
+PROCEDURE Emit2(param2, param1: BYTE);
+BEGIN 
+  IF nextEmitPos + 1 >= windowLimit THEN FlushCodeWindow END;
+  codeWindow^[nextEmitPos - windowBase] := param2;
+  codeWindow^[nextEmitPos + 1 - windowBase] := param1;
+  INC(nextEmitPos, 2);
+  IF checkOverflow THEN CheckCodeOverflow END;
+END Emit2;
+
+PROCEDURE EmitWord(param1: WORD);
+VAR ptr: ADDRESS;
+BEGIN
+  IF nextEmitPos + 1 >= windowLimit THEN FlushCodeWindow END;
+  ptr := ADDRESS(codeWindow) + (nextEmitPos - windowBase);
+  ptr^ := param1;
+  INC(nextEmitPos, 2);
+  IF checkOverflow THEN CheckCodeOverflow END;
+END EmitWord;
+
+PROCEDURE EmitString(param1: ADDRESS);
+VAR ptr : POINTER TO ARRAY [0..1] OF BYTE;
+BEGIN 
+  ptr := param1;
+  REPEAT
+    Emit1(ptr^[0]);
+    ptr := ADDRESS(ptr) + 1;
+  UNTIL ORD(ptr^[0]) = 0;
+  IF checkOverflow THEN CheckCodeOverflow END;
+END EmitString;
+
+PROCEDURE FlushPendingOp;
+VAR local2: CARDINAL;
+  PROCEDURE BaseOpCode(param1: CARDINAL): CARDINAL;
+  BEGIN
+    CASE param1 OF
+    | 8    : RETURN  60H
+    | 9    : RETURN 0ECH
+    | 1, 5 : RETURN  2CH
+    | 2, 6 : RETURN  08H
+    | 3, 7 : RETURN 0
+    END;
+  END BaseOpCode;
+BEGIN
+  pendActive := FALSE;
+  local2 := 0;
+  IF pendMode <> 9 THEN
+    pendOffset := pendOffset DIV 2;
+    local2 := pendMode DIV 4 * (ORD((pendMode MOD 4) <> 3) * 12 + 4);
+  END;
+  IF pendSize = 4 THEN
+    IF pendDisp = 1 
+    THEN Emit1(OPLDIX) 
+    ELSE Emit2(OPLDIXN, pendDisp) 
+    END;
+    IF pendOffset >= 128 THEN (* $T+ *)
+      Emit2(OPSUBN, (256 - pendOffset) * 2);
+      pendOffset := 0; (* $T- *)
+    END;
+    pendSize := 2;
+  END;
+  IF pendMode IN {3,7} THEN Emit1(OPLONGREAL) END;
+  IF pendSize = 5 THEN
+    IF pendMode IN {3,7} 
+    THEN Emit2(pendMode DIV 4 + 8, 0)
+    ELSE Emit1(pendMode MOD 4 + local2 + 13)
+    END;
+  ELSE
+    IF pendMode IN {0,4} THEN INC(pendMode) END;
+    IF pendSize = 3 THEN
+      IF (pendOffset <= 15) AND (pendDisp <= 15) AND (pendMode IN {1,5,9})
+      THEN 
+        IF pendMode = 9 THEN local2 := 0E4H END;
+        Emit2(local2 + 12, pendDisp * 16 + pendOffset);
+        RETURN
+      END;
+      Emit2(BaseOpCode(pendMode)+local2+3, pendDisp);
+      Emit1(pendOffset);
+    ELSE
+      CASE pendMode OF
+      | 9:
+          IF (pendSize = 1) AND (pendOffset IN {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15})
+          THEN Emit1(pendOffset + OPEXTCALL1); RETURN
+          END;
+      | 8:
+          IF (pendSize = 2) AND (pendOffset = 0) THEN RETURN END;
+      | 1, 5:
+          IF (pendSize <> 2) OR NOT Compiler.rangeCheckEnabled THEN
+            IF pendSize = 0 THEN
+              IF pendOffset <= 7 THEN
+                Emit1(pendOffset + local2);
+                RETURN
+              ELSIF pendOffset >= 245 THEN
+                Emit1(local2 + 288 - pendOffset);
+                RETURN
+              END;
+            ELSE
+              IF (pendOffset >= (4 - pendSize * 2)) AND (pendOffset <= 15) THEN
+                Emit1((pendSize + 1) * 32 + pendOffset + local2);
+                RETURN
+              END;
+            END;
+          END;
+      | 2, 6:
+          IF (pendSize = 2) AND (pendOffset = 0) THEN
+            Emit1(local2 + (OPLGW2 - 1));
+            RETURN
+          END;
+      END;
+      Emit2(BaseOpCode(pendMode) + pendSize + local2, pendOffset);
+      IF Compiler.rangeCheckEnabled AND (pendMode = 8) AND (pendSize <= 1) THEN
+        Emit1(pendDisp)
+      END;
+    END;
+  END;
+END FlushPendingOp;
+
+PROCEDURE FlushConstQueue;
+VAR
+    local2 : CARDINAL;
+    local3 : CARDINAL;
+    local4 : CARDINAL;
+    local5 : POINTER TO Record;
+BEGIN
+  IF pendActive THEN FlushPendingOp
+  ELSE
+    IF fixupCount <> 0 THEN
+      local2 := 0;
+      WHILE local2 < fixupCount DO
+        local5 := ADR(fixupQueue[local2]);
+        CASE local5^.word0 OF
+        | 0: (* 02EE *)
+            IF local5^.word2 <> 0 THEN Emit2(2, local5^.word2)
+            ELSE Emit2(OPCALLREL, Scanner.StrLenHelper(local5^.word1, 128));
+                 EmitString(local5^.word1);
+            END;
+        | 1: (* 0308 *)
+            IF local5^.word1 <= 255 THEN
+              IF local5^.word1 <= 15
+              THEN Emit1(local5^.word1 + OPLI0)
+              ELSE Emit2(OPLIB, local5^.word1)
+              END;
+            ELSE (* 0323 *)
+              Emit1(OPLIW);
+              EmitWord(local5^.word1)
+            END; (* 0329 *)
+        | 2: (* 032A *)
+            Emit1(OPLID);
+            EmitWord(local5^.word1);
+            EmitWord(local5^.word2);
+        | 3: (* 0334 *)
+            local4 := 8;
+            REPEAT (* 0336 *)
+              DEC(local4, 4);
+              Emit1(OPLID);
+              local3 := 0;
+              REPEAT (* 033F *)
+                Emit1(local5^.ptr1^[local4 + local3]);
+                INC(local3);
+              UNTIL local3 > 3;
+            UNTIL local4 = 0;
+            IF Compiler.rangeCheckEnabled THEN Emit2(0, 22) END;
+        | 4: (* 035B *)
+            IF CARDINAL(ABS(INTEGER(local5^.word1))) <= 255 THEN
+              IF local5^.word1 <> NIL THEN
+                IF ABS(INTEGER(local5^.word1)) = 1 THEN
+                  Emit1(ORD(INTEGER(local5^.word1) < 0) + OPINC)
+                ELSE (* 0378 *)
+                  Emit2(ORD(INTEGER(local5^.word1) < 0) + OPADDN,
+                        ABS(INTEGER(local5^.word1)));
+                END;
+              END; (* 0382 *)
+            ELSE (* 0384 *)
+              Emit1(OPLIW);
+              EmitWord(local5^.word1);
+              Emit1(OPADD);
+            END; (* 038d *)
+(* $T+ generates ELSE RAISE CaseSelectError *)
+        END; (* CASE *)
+        INC(local2);
+      END; (* 03A8 *)
+      fixupCount := 0;
+    END (* 03AA *)
+  END (* 03aa *);
+END FlushConstQueue;
+
+PROCEDURE Reserved33(param1: WORD);
+BEGIN
+  (* commented contents ? *)
+END Reserved33;
+
+PROCEDURE Reserved34(param2, param1: WORD);
+VAR unused: WORD;
+BEGIN
+  (* commented contents ? *)
+END Reserved34;
+
+PROCEDURE DiscardPending;
+VAR unused: WORD;
+BEGIN 
+  IF emitEnabled THEN
+    fixupCount := fixupCount + ORD(pendActive) - 1;
+    pendActive := FALSE;
+  END;
+END DiscardPending;
+
+PROCEDURE SetPendingOp(param4, param3, param2, param1: CARDINAL);
+BEGIN 
+  IF emitEnabled THEN
+    FlushConstQueue;
+    pendMode  := param4;
+    pendSize  := param3;
+    pendDisp  := param2;
+    pendOffset := param1;
+    pendActive := TRUE;
+    IF param4 IN {4,5,6,7,9} THEN FlushPendingOp END;
+  END;
+END SetPendingOp;
+
+(* $T- *)
+
+PROCEDURE QueueConst(param1: CARDINAL);
+VAR ptr : POINTER TO Record;
+BEGIN 
+  IF emitEnabled THEN
+    IF pendActive THEN FlushPendingOp END;
+    IF fixupCount >= 16 THEN Scanner.ScannerError(90) END;
+    ptr := ADR(fixupQueue[fixupCount]);
+    ptr^.word0 := 1;
+    ptr^.word1 := param1;
+    INC(fixupCount);
+  END;
+END QueueConst;
+
+PROCEDURE QueueLongConst(param3: CARDINAL; param1: LONGINT);
+VAR ptr : POINTER TO Record;
+BEGIN 
+  IF emitEnabled THEN
+    IF pendActive THEN FlushPendingOp END;
+    IF fixupCount >= 16 THEN Scanner.ScannerError(90) END;
+    ptr := ADR(fixupQueue[fixupCount]);
+    ptr^.word0 := param3;
+    ptr^.long1 := param1;
+    INC(fixupCount);
+  END;
+END QueueLongConst;
+
+PROCEDURE EmitTypedOp(param2, param1 : CARDINAL);
+VAR ptr: POINTER TO Record;
+    local3 : CARDINAL;
+    ptr2: POINTER TO Record;  
+
+  PROCEDURE IsPowerOfTwo(param1: CARDINAL): BOOLEAN;
+  VAR
+    i: CARDINAL;
+    j: CARDINAL;
+  BEGIN
+    j := 1;
+    i := 0;
+    REPEAT
+      IF j = param1 THEN ptr := ADDRESS(i); RETURN TRUE END;
+      j := j * 2;
+      INC(i);
+    UNTIL i > 14;
+    RETURN FALSE  
+  END IsPowerOfTwo;
+
+  PROCEDURE Reserved36(): CARDINAL;
+  BEGIN
+    (* commented contents ? *)
+  END Reserved36;
+
+BEGIN
+  IF emitEnabled THEN
+    IF fixupCount <> 0 THEN
+      ptr2 := ADR(fixupQueue[fixupCount - 1]);
+      IF ptr2^.word0 = 1 THEN
+        IF (param2 IN {6,7}) AND (param1 <= 1) THEN
+          IF param2 = 7 THEN ptr2^.word1 := -INTEGER(ptr2^.word1) END;
+          IF fixupCount > 1 THEN
+            IF fixupQueue[fixupCount - 2].word0 IN {1,4} THEN
+              INC(fixupQueue[fixupCount - 2].word1, ptr2^.word1);
+              DEC(fixupCount); 
+            END; (* 04A9 *)
+          END; (* 04A9 *)
+          ptr2^.word0 := 4;
+          RETURN;
+        ELSE (* 04AF *)
+          IF (param2 IN {8,9}) AND (param1 = 0) AND IsPowerOfTwo(ptr2^.word1) THEN
+            DEC(fixupCount);
+            FlushConstQueue;
+            IF ptr <> NIL THEN Emit2(param2 + OPMUL, ptr) END; (* 04CE *)
+            RETURN
+          ELSE (* 04D1 *)
+            IF (param2 = 10) AND (param1 <= 1) AND IsPowerOfTwo(ptr2^.word1) THEN
+              DEC(ptr2^.word1);
+              FlushConstQueue;
+              Emit1(OPBITAND);
+              RETURN
+            ELSE (* 04EE *)
+              IF (NOT Compiler.rangeCheckEnabled) AND (ptr2^.word1 = 0) 
+              AND (param2 IN {0,1,3}) AND (param1 <= ORD(param2 <> 3)) THEN
+                DEC(fixupCount);
+                FlushConstQueue;
+                Emit1(OPEQ0 + ORD(param2 <> 0) * 32);
+                RETURN
+              END; (* 0512 *)
+            END; (* 0512 *)
+          END;
+        END;
+      END; (* 0512 *)
+    END; (* 0512 *)
+    FlushConstQueue;
+    IF (param1 = 5) OR (param2 = 18) THEN
+      local3 := Scanner.EnterModuleSymbol("DOUBLES", 9567H) * 16;
+    END; (* 0532 *)
+    CASE param1 OF
+    | 0: (* 0536 *)
+        IF param2 >= 15 THEN
+          IF Compiler.rangeCheckEnabled THEN Emit1(125) ELSE Emit2(144,33) END;
+          param1 := 2;
+        END; (* 054b *)
+    | 1: (* 054c *)
+        IF param2 >= 15 THEN
+          Emit1(189);
+          param1 := 2;
+        ELSE
+          IF param2 IN {0,1,6,7,10} THEN param1 := 0
+          ELSIF param2 = 11 THEN Emit2(OPCOMPL, OPINC); RETURN
+          END; (* 056E *)
+        END; (* 056E *)
+    | 2: (* 056f *)
+        IF param2 <= 5 THEN Emit1(OPDCOMP); EmitSystemCall(23); param1 := 0
+        ELSIF param2 = 11 THEN
+          Emit2(OPEXTENDED,3); RETURN
+        END; (* 0589 *)
+    | 3: (* 058a *)
+        IF param2 <= 5 THEN
+          Emit1(OPREALCMP); EmitSystemCall(23); param1 := 0
+        ELSIF param2 IN {11,12} THEN
+          IF Compiler.rangeCheckEnabled THEN Emit1(param2 + OPSSW0)
+          ELSE
+            Emit1(OPSWAP);
+            IF param2 = 11 THEN
+              Emit2(OPLI15, OPPOWER2); Emit1(OPBITXOR);
+            ELSE (* 05BD *)
+              Emit1(OPLIW); EmitWord(7FFFH); Emit1(OPBITAND); 
+            END; (* 05c7 *)
+            Emit1(OPSWAP);
+          END; (* 05ca *)
+          RETURN
+        ELSIF param2 IN {13,14,15} THEN
+          Emit1(OPFLOAT2LG);
+          param1 := 2;
+        END; (* 05d9 *)
+    | 4: (* 05da *)
+        IF param2 <= 5 THEN
+          IF param2 = 5 THEN
+            Emit1(OPSWAP);
+            param2 := 4;
+          END; (* 05E9 *)
+          IF param2 = 4 THEN Emit2(OPCOMPL, OPBITAND); Emit1(OPLI0); param2 := 0 END; (* 05F8 *)
+          param1 := 0
+        ELSIF param2 = 7 THEN
+          Emit1(OPCOMPL); param2 := 8
+        END; (* 0606 *)
+    | 5: (* 0607 *)
+        IF param2 <> 18 THEN
+          Emit1(OPEXTCALL1);
+          IF param2 <= 5 THEN Emit1(local3+5); param1 := 0
+          ELSIF param2 >= 13 THEN
+            param1 := ORD(param2 = 16) + 2;
+            Emit1(local3 + param1 - 1);
+          ELSE Emit1(local3 + param2)
+          END; (* 0634*)
+          IF Compiler.rangeCheckEnabled THEN
+            Emit2(ORD(param2 <= 9)+1, (ORD(param2 IN {6,7,8,9,10,11,12})+1)*4);
+            IF param2 <= 5 THEN EmitSystemCall(23) END;
+          END; (* 064E *)
+          IF param1 = 5 THEN RETURN END;
+        END; (* 0654 *)
+(* $T+ generate CaseSelectError exception *)
+    END; (* 066c *)
+(* $T- *)
+    IF param2 <= 12 THEN
+      Emit1(param2 + param1 * 16 + 160);
+    ELSIF param2 - 13 <> param1 THEN
+      IF param2 <= 14 THEN
+        IF param1 <= 1 THEN Emit1(221) ELSE Emit1(param2 + 173) END;
+      ELSIF param2 <= 16 THEN Emit1(param1 + 188)
+      ELSE (* 06A3 *) 
+        Emit2(OPEXTCALL1, local3 + param1 + 1);
+        IF Compiler.rangeCheckEnabled THEN Emit2(OPRAISE, 8) END;
+      END; (* 06B1 *)
+    END; (* 06B1 *)
+  END; (* 06b1 *)
+END EmitTypedOp;
+
+PROCEDURE EmitExtendedOp(param2, param1: CARDINAL);
+BEGIN 
+  IF emitEnabled THEN FlushConstQueue; Emit1(param1 * 16 + param2 + 186) END;
+END EmitExtendedOp;
+
+
+PROCEDURE EmitStandardOp(param1: CARDINAL);
+VAR ptr: RecordPtr;
+BEGIN 
+  IF emitEnabled THEN
+    IF (param1 = 0) AND (fixupCount <> 0) THEN
+      ptr := ADR(fixupQueue[fixupCount-1]);
+      IF (ptr^.word0 = 1) AND (ptr^.word1 = 0) THEN
+        DEC(fixupCount);
+        FlushConstQueue;
+        Emit1(OPLIMIT);
+        RETURN
+      END; (* 06EC *)
+    END; (* 06EC *)
+    FlushConstQueue;
+    IF param1 >= 23 THEN
+      Emit1(OPEXTENDED);
+    END; (* 06F7 *)
+(* $T+ *)
+    Emit1( Compiler.keywordTable[param1][0] );
+(* $T- *)
+  END; (* 0701 *)
+END EmitStandardOp;
+
+PROCEDURE EmitMiscOp(param2, param1: CARDINAL);
+BEGIN 
+  IF emitEnabled THEN
+    FlushConstQueue;
+    IF param2 = 5 THEN
+      IF param1 >= 10 THEN
+        Emit2(OPEXTENDED, param1 - 5)
+      ELSIF param1 = 6 THEN
+        IF Compiler.rangeCheckEnabled THEN Emit1(102)
+        ELSE
+            (* generates the bad CAP sequence *)
+          Emit2(OPDUP, OPLIB); Emit2(040H, OPBITAND);
+          Emit2(OPSHR, 1);  Emit2(OPCOMPL, OPBITAND);
+        END; (* 073D *)
+      ELSE (* 073F *)
+(* $T+ *)
+        Emit1(Compiler.keywordTable[param1+27][0]);
+(* $T- *)
+      END; (* 074B *)
+    ELSE (* 074D *)
+      IF param2 = 4 THEN
+        Emit2(OPLONGREAL, 10);
+        Emit1(param1);
+      ELSIF (param2 = 0) AND (param1 - 128 <= 3) AND (NOT Compiler.rangeCheckEnabled) THEN
+        Emit1(param1 + 8)
+      ELSE
+        Emit2(param2 + 132, param1)
+      END; (* 0775 *)
+    END; (* 0775 *)
+  END; (* 0775 *)
+END EmitMiscOp;
+
+PROCEDURE EmitSystemCall(param1: CARDINAL);
+BEGIN 
+  IF emitEnabled THEN
+    FlushConstQueue;
+    IF Compiler.rangeCheckEnabled AND (param1 <> 20) THEN Emit2(0,param1) END;
+  END;
+END EmitSystemCall;
+
+PROCEDURE EmitExtCall3(param3, param2, param1: CARDINAL); 
+BEGIN 
+  IF emitEnabled THEN
+    FlushConstQueue;
+    IF Compiler.rangeCheckEnabled THEN
+      IF param3 <> 19 THEN Emit2(0, param3) END;
+      Emit1(param2);
+      IF param3 >= 3 THEN Emit1(param1) END;
+    END; (* 07aa *)
+  END;
+END EmitExtCall3;
+
+PROCEDURE OpenFixup(VAR param2: CARDINAL; param1: BOOLEAN);
+VAR local2: CARDINAL;
+BEGIN 
+  IF emitEnabled THEN
+    param2 := nextEmitPos;
+    IF param1 THEN
+      local2 := PeekCodeByte(nextEmitPos - 1);
+      IF local2 - 224 <= 1 THEN
+        PokeCodeByte(local2 + 2, nextEmitPos - 1);
+      END;
+      Emit1(0)
+    ELSE EmitWord(0)
+    END;
+  END;
+END OpenFixup;
+
+PROCEDURE CloseFixup(param2: CARDINAL; param1: BOOLEAN); 
+BEGIN 
+  IF emitEnabled THEN
+    IF param1 AND (nextEmitPos < param2 + 254) THEN
+      PokeCodeByte(PeekCodeByte(nextEmitPos - 1) + 4, nextEmitPos - 1);
+      Emit1(nextEmitPos + 1 - param2);
+    ELSE
+      EmitWord(param2 - (nextEmitPos + 1))
+    END;
+  END;
+END CloseFixup;
+
+
+PROCEDURE InsertFixup(param2: CARDINAL; param1: BOOLEAN): BOOLEAN;
+VAR 
+    local2: BYTE;
+    local3: BYTE;
+    local4: CARDINAL;
+    local5: ADDRESS;
+BEGIN 
+  IF emitEnabled THEN
+    FlushConstQueue;
+    local4 := param2 + 1;
+    IF param1 THEN
+      IF nextEmitPos > local4 + 254 THEN
+        IF local4 < windowBase THEN
+          Files.SetPos(Scanner.codeFile, LONG(local4));
+          Files.ReadByte(Scanner.codeFile, local2);
+          INC(local4);
+          WHILE local4 < windowBase DO
+            local3 := local2;
+            Files.ReadByte(Scanner.codeFile, local2);
+            Files.SetPos(Scanner.codeFile, LONG(local4));
+            Files.WriteByte(Scanner.codeFile, local3);
+            INC(local4)
+          END; (* 0842 *)
+          MOVE(codeWindow, ADDRESS(codeWindow) + 1, nextEmitPos - windowBase);
+          codeWindow^[0] := local2;
+        ELSE (* 0850 *)
+          local5 := ADDRESS(codeWindow) + local4 - windowBase;
+          MOVE(local5, local5 + 1, nextEmitPos - local4);
+        END; (* 085e *)
+        INC(nextEmitPos);
+        PokeCodeByte( PeekCodeByte(param2 - 1) - 2, param2 - 1);
+        PokeCodeByte( (nextEmitPos - local4) MOD 256, param2);
+        PokeCodeByte( (nextEmitPos - local4) DIV 256, local4);
+        RETURN FALSE
+      END; (* 087f *)
+      PokeCodeByte(nextEmitPos - local4, param2);
+    ELSE (* 0887 *)
+      PokeCodeByte( (nextEmitPos - local4) MOD 256, param2);
+      PokeCodeByte( (nextEmitPos - local4) DIV 256, local4);
+    END; (* 0898 *)
+  END; (* 0898 *)
+  RETURN TRUE
+END InsertFixup;
+
+
+PROCEDURE AdjustFixup(param1: CARDINAL);
+VAR local2: CARDINAL;
+BEGIN 
+  IF emitEnabled THEN
+    IF PeekCodeByte(param1 - 1) <= 225 THEN
+      local2 := PeekCodeByte(param1) + PeekCodeByte(param1 + 1) * 256;
+      IF INTEGER(local2) > 0 THEN INC(local2) ELSE DEC(local2) END;
+      PokeCodeByte( local2 MOD 256, param1);
+      PokeCodeByte( local2 DIV 256, param1 + 1);
+    ELSE (* 08D2 *)
+      PokeCodeByte( PeekCodeByte(param1) + 1, param1);
+    END; (* 08D9 *)
+  END; (* 08D9 *)
+END AdjustFixup;
+
+
+PROCEDURE MarkCodePos(param1: CARDINAL): CARDINAL;
+BEGIN 
+  EmitSystemCall(param1);
+  RETURN nextEmitPos
+END MarkCodePos;
+
+PROCEDURE OpenEmitter;
+BEGIN 
+  emitEnabled  := TRUE;
+  pendActive := FALSE;
+  fixupCount := 0;
+  SeekCodeWindow;
+END OpenEmitter;
+
+
+PROCEDURE InitCodeGenerator;
+BEGIN 
+  checkOverflow := (execute = 4);
+  codeWindow  := ADDRESS(Scanner.codeBuffer);
+  windowBase  := 0;
+  windowLimit  := 4096;
+  nextEmitPos  := 16;
+  OpenEmitter;
+END InitCodeGenerator;
+
+END CodeGen.

+ 76 - 0
src/compiler/COMPILER.DEF

@@ -0,0 +1,76 @@
+(* Renamed for readability. Semantics unchanged.
+   NOTE: overlayProc01..28 below are the obfuscated link-stub imports
+   (originally overlayProc01..overlayProc28); the real Compiler API is the globals +
+   InitCompiler/InsertIdent/... in COMPILER.MOD. See RENAME-MAP.md. *)
+DEFINITION MODULE Compiler;
+FROM SYSTEM IMPORT BYTE,WORD,ADDRESS;
+
+TYPE CompilerImport = (COMPILER);
+TYPE RecordPtr = POINTER TO Record;
+     Record = RECORD
+                word0: RecordPtr;
+                word1: RecordPtr;
+                word2: RecordPtr;
+                word3: BITSET;
+                word4: CARDINAL;
+                word5: WORD;
+                high: WORD;
+              END;
+     ArrayPtr = POINTER TO ARRAY [0..5] OF WORD;
+
+VAR
+    rangeCheckEnabled: BOOLEAN;
+    scopeSpare3: RecordPtr;
+    stdScopeAnchor: RecordPtr;
+    keywordTable: ADDRESS;
+    IntType: RecordPtr;     (* INTEGER  type symbol *)
+    CardType: RecordPtr;     (* CARDINAL type symbol *)
+    AddressType: RecordPtr;     (* ADDRESS  type symbol *)
+    RealType: RecordPtr;     (* REAL     type symbol *)
+    LongintType: RecordPtr;    (* LONGINT  type symbol *)
+    LongrealType: RecordPtr;    (* LONGREAL type symbol *)
+    charArrayDesc: RecordPtr;
+    CharType: RecordPtr;    (* CHAR     type symbol *)
+    BooleanType: RecordPtr;    (* BOOLEAN  type symbol *)
+    BitsetType: RecordPtr;    (* BITSET   type symbol *)
+    ProcType: RecordPtr;    (* PROC     type symbol *)
+    addressAlias: RecordPtr;
+    WordType: RecordPtr;    (* WORD     type symbol *)
+    ByteType: RecordPtr;    (* BYTE     type symbol *)
+    systemModule: RecordPtr;
+    storageModule: RecordPtr;
+    allocateSignature: RecordPtr;
+    systemScopeDesc: RecordPtr;
+    storageScopeDesc: RecordPtr;
+    nativeCodeRequested: BOOLEAN;
+    compilationActive: BOOLEAN;
+
+PROCEDURE overlayProc01;
+PROCEDURE overlayProc02;
+PROCEDURE overlayProc03;
+PROCEDURE overlayProc04;
+PROCEDURE overlayProc05;
+PROCEDURE overlayProc06;
+PROCEDURE overlayProc07;
+PROCEDURE overlayProc08;
+PROCEDURE overlayProc09;
+PROCEDURE overlayProc10;
+PROCEDURE overlayProc11;
+PROCEDURE overlayProc12;
+PROCEDURE overlayProc13;
+PROCEDURE overlayProc14;
+PROCEDURE overlayProc15;
+PROCEDURE overlayProc16;
+PROCEDURE overlayProc17;
+PROCEDURE overlayProc18;
+PROCEDURE overlayProc19;
+PROCEDURE overlayProc20;
+PROCEDURE overlayProc21;
+PROCEDURE overlayProc22;
+PROCEDURE overlayProc23;
+PROCEDURE overlayProc24;
+PROCEDURE overlayProc25;
+PROCEDURE overlayProc26;
+PROCEDURE overlayProc27;
+PROCEDURE overlayProc28;
+END Compiler.

+ 402 - 0
src/compiler/COMPILER.MOD

@@ -0,0 +1,402 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+IMPLEMENTATION MODULE COMPILER;
+IMPORT SCANNER, Files, SYMTAB, ERRORS, Texts, Terminal, ComLine;
+FROM SYSTEM IMPORT ADDRESS, ADR, WORD, BDOS, IORESULT;
+FROM STORAGE IMPORT FREEMEM;
+
+(* $[+ remove procedure names *)
+
+VAR 
+    reserved27: RecordPtr;
+    reserved28: RecordPtr;
+    reserved29: RecordPtr;
+    reserved30: RecordPtr;
+    curScopeHead: RecordPtr;
+    savedScopeHead: RecordPtr;
+    baseScopeHead: RecordPtr;
+    bitsetRangeDesc: RecordPtr;
+    lastParamList: ArrayPtr;
+
+PROCEDURE InitCompiler; FORWARD;
+PROCEDURE OpenSourceAndOutput; FORWARD;
+
+PROCEDURE InsertIdent(keyword: ARRAY OF CHAR; n: CARDINAL):ADDRESS;
+VAR ptr : RecordPtr;
+BEGIN
+  ptr := SCANNER.NewNode(n);
+  SCANNER.CopyStringToHeap(ptr^.word1, keyword);
+  ptr^.word0 := curScopeHead;
+  ptr^.word3 := 64;
+  curScopeHead := ptr;
+  RETURN ptr
+END InsertIdent;
+
+PROCEDURE DeclConst(keyword: ARRAY OF CHAR; param3: RecordPtr;
+                param2: WORD; n:CARDINAL);
+VAR ptr: RecordPtr;
+BEGIN 
+  ptr := InsertIdent(keyword, n);
+  ptr^.word2 := param3;
+  ptr^.word5 := param2;
+END DeclConst;
+
+
+PROCEDURE DeclStdProc(keyword: ARRAY OF CHAR; param2,param1: WORD);
+VAR ptr: RecordPtr;
+BEGIN
+  ptr := InsertIdent(keyword, 5);
+  ptr^.word5 := param2;
+  ptr^.high := param1;
+END DeclStdProc;
+
+PROCEDURE DeclProc(keyword: ARRAY OF CHAR; param1: CARDINAL);
+BEGIN 
+  DeclStdProc(keyword, param1, 0);
+  curScopeHead^.word3 := 192;
+END DeclProc;
+
+
+PROCEDURE DeclFunc(keyword: ARRAY OF CHAR; param2: CARDINAL; param1: RecordPtr);
+BEGIN 
+  DeclProc(keyword, param2);
+  curScopeHead^.word2 := param1;
+END DeclFunc;
+
+
+PROCEDURE NewTypeDesc(param2,param1: CARDINAL): RecordPtr;
+VAR ptr : RecordPtr;
+BEGIN
+  ptr := SCANNER.NewSizedNode(param1);
+  ptr^.word3 := param2;
+  RETURN ptr
+END NewTypeDesc;
+
+
+PROCEDURE DeclType(keyword: ARRAY OF CHAR; tsize, id: CARDINAL): ADDRESS;
+VAR ptr, ptr2 : RecordPtr;
+BEGIN 
+  ptr := InsertIdent(keyword, 3);
+  ptr2:= NewTypeDesc(tsize, id);
+  ptr^.word2 := ptr2;
+  ptr2^.word0:= ptr;
+  RETURN ptr2
+END DeclType;
+
+
+PROCEDURE InitOperatorTable;
+  VAR n: CARDINAL;
+
+  PROCEDURE AddOperator(char1,char2: CHAR; param1: CARDINAL);
+  BEGIN
+    ERRORS.opCharTable^[n]   := char1;
+    ERRORS.opCharTable^[n+1] := char2;
+    ERRORS.opCharTable^[n+2] := CHR(param1);
+    INC(n,3);
+  END AddOperator;
+BEGIN 
+  SCANNER.HeapAlloc(ERRORS.opCharTable, 27);
+  n := 0;
+  AddOperator(':','=',27);
+  AddOperator('.','.', 4);
+  AddOperator('<','=',56);
+  AddOperator('>','=',57);
+  AddOperator('<','>',53);
+  AddOperator('(','.',44);
+  AddOperator('.',')', 6);
+  AddOperator('(',':',45);
+  AddOperator(':',')', 7);
+END InitOperatorTable;
+
+PROCEDURE InitCharClasses;
+CONST DEL = 177C;
+VAR i: CARDINAL;
+    local3: ARRAY [0..64] OF CHAR;
+    code : CARDINAL;
+BEGIN 
+  SCANNER.HeapAlloc(SCANNER.charClassTable, 128);
+  i := ORD('A');
+  REPEAT
+    SCANNER.charClassTable^[i]    := CHR(10);
+    SCANNER.charClassTable^[i+32] := CHR(10);
+    INC(i);
+  UNTIL i > ORD('Z');
+  i := ORD('0'); REPEAT SCANNER.charClassTable^[i] := CHR(11); INC(i) UNTIL i > ORD('9');
+  SCANNER.charClassTable^[ORD('!')] := CHR(8);
+  SCANNER.charClassTable^[ORD('~')] := CHR(66);
+  SCANNER.charClassTable^[ORD(DEL)] := CHR(4);
+  SCANNER.charClassTable^[ORD('"')] := CHR(12);
+  local3 := " ,:. )]}|;  '                              ([{^     =#<>  +-*  /&";
+  code := 0;
+  REPEAT
+    IF local3[code] <> ' ' THEN SCANNER.charClassTable^[ORD(local3[code])]:=CHR(code) END;
+    INC(code)
+  UNTIL code > 64;
+END InitCharClasses;
+
+PROCEDURE InitKeywordTable; 
+VAR p: POINTER TO ARRAY [0..29] OF CARDINAL;
+BEGIN 
+  SCANNER.HeapAlloc(keywordTable, 30*2);
+  p := keywordTable;
+  p^[ 0] :=  730;
+  p^[ 1] :=  731;
+  p^[ 2] := 1245;
+  p^[ 3] := 1206;
+  p^[ 4] :=  999;
+  p^[ 5] := 1258;
+  p^[ 6] := 1127;
+  p^[ 7] :=  708;
+  p^[ 8] := 1312;
+  p^[ 9] := 1234;
+  p^[10] :=  979;
+  p^[11] :=  990;
+  p^[12] :=  991;
+  p^[13] :=  747;
+  p^[14] :=  257;
+  p^[15] := 1248;
+  p^[16] :=  993;
+  p^[17] :=  304;
+  p^[18] :=   49;
+  p^[19] := 1229;
+  p^[20] := 1230;
+  p^[21] := 1231;
+  p^[22] := 1236;
+  p^[23] := 1024;
+  p^[24] := 1281;
+  p^[25] :=  770;
+  p^[26] :=  772;
+  p^[27] :=    0;
+  p^[28] :=  195;
+  p^[29] :=   80;
+END InitKeywordTable;
+
+
+PROCEDURE DeclarePredefinedTypes; 
+VAR p : RecordPtr;
+BEGIN 
+  curScopeHead := baseScopeHead;
+  IntType  := DeclType("INTEGER" ,2,2);
+  CharType := DeclType("CHAR"    ,1,0); CharType^.high := 255;
+  BooleanType := DeclType("BOOLEAN" ,1,0); BooleanType^.high := 1;
+  CardType  := DeclType("CARDINAL",2,2);
+  RealType  := DeclType("REAL"    ,4,8);
+  LongrealType := DeclType("LONGREAL",8,8);
+  BitsetType := DeclType("BITSET"  ,2,4);
+
+  bitsetRangeDesc := NewTypeDesc(1,1); bitsetRangeDesc^.word2 := CardType; bitsetRangeDesc^.high := 15;
+  BitsetType^.word2 := bitsetRangeDesc;
+
+  ProcType := DeclType("PROC"    ,4,9);
+  LongintType := DeclType("LONGINT" ,4,7);
+
+  baseScopeHead := curScopeHead;
+  curScopeHead := savedScopeHead;
+  WordType := DeclType("WORD"    ,2,3);
+  AddressType  := DeclType("ADDRESS" ,2,2);
+  ByteType := DeclType("BYTE"    ,1,1); ByteType^.word2 := WordType;
+  ByteType^.high := 255;
+
+  p := InsertIdent("PROCESS",3);
+  p^.word2 := AddressType;
+  addressAlias := AddressType;
+  savedScopeHead := curScopeHead;
+
+  charArrayDesc := NewTypeDesc(0,11); charArrayDesc^.word5 := CharType;
+END DeclarePredefinedTypes;
+
+
+PROCEDURE MakeParamList(param3: CARDINAL; param2: CARDINAL; param1: RecordPtr): ADDRESS;
+VAR p : ArrayPtr;
+BEGIN
+  SCANNER.HeapAlloc(p, (param3+2)*2);
+  p^[0] := param3;
+  p^[1] := param2;
+  p^[2] := param1;
+  lastParamList := p;
+  RETURN p
+END MakeParamList;
+
+PROCEDURE DeclareStandardNames; 
+BEGIN 
+  curScopeHead := baseScopeHead;
+  DeclConst("TRUE" ,BooleanType,1,1);
+  DeclConst("FALSE",BooleanType,0,1);
+  DeclConst("NIL"  ,AddressType ,0,1);
+
+  DeclProc("READ"    , 0);
+  DeclProc("READLN"  , 1);
+  DeclProc("WRITE"   , 2);
+  DeclProc("WRITELN" , 3);
+  DeclProc("NEW"     , 4);
+  DeclProc("DISPOSE" , 5);
+  DeclProc("INC"     , 6);
+  DeclProc("DEC"     , 7);
+  DeclProc("INCL"    , 8);
+  DeclProc("EXCL"    , 9);
+  DeclFunc("CARD"    , 0, CardType);
+  DeclFunc("INT"     , 1, IntType);
+  DeclFunc("LONG"    , 2, LongintType);
+  DeclFunc("FLOAT"   , 3, RealType);
+  DeclFunc("ABS"     , 4, CardType);
+  DeclFunc("DOUBLE"  , 5, LongrealType);
+  DeclFunc("ORD"     , 6, CardType);
+  DeclFunc("CHR"     , 7, CharType);
+  DeclFunc("ODD"     , 8, BooleanType);
+  DeclFunc("HIGH"    , 9, CardType);
+  DeclFunc("SIZE"    ,11, CardType);
+  DeclFunc("VAL"     ,13, CardType);
+  DeclFunc("MIN"     ,14, CardType);
+  DeclFunc("MAX"     ,15, CardType);
+  DeclStdProc("CAP"     , 6, MakeParamList(1, 0, CharType));
+  curScopeHead^.word2 := CharType;
+  DeclStdProc("HALT"    , 2, NIL);
+  DeclStdProc("TRUNC"   , 8, MakeParamList(1, 0, RealType));
+  curScopeHead^.word2 := CardType;
+  SCANNER.HeapAlloc(stdScopeAnchor, 14);
+  stdScopeAnchor^.word0  := NIL;
+  stdScopeAnchor^.word1  := curScopeHead;
+END DeclareStandardNames;
+
+PROCEDURE DeclareSystemModule; 
+VAR p: RecordPtr;
+BEGIN
+  curScopeHead := savedScopeHead;
+  DeclConst("IORESULT"     , CardType, 0300H, 4); curScopeHead^.word3 := 256;
+  DeclConst("HLRESULT"     , CardType, 0302H, 4); curScopeHead^.word3 := 256;
+  DeclConst("BADOVERLAY"   , NIL    , 1, 2);
+  DeclConst("OUTOFMEMORY"  , NIL    , 5, 2);
+  DeclConst("REALOVERFLOW" , NIL    ,11, 2);
+  DeclConst("OVERFLOW"     , NIL    ,12, 2);
+
+  DeclProc("CODE"      ,10);
+  DeclFunc("ADR"       ,10, AddressType);
+  DeclFunc("TSIZE"     ,12, CardType);
+  DeclStdProc("BREAK"     , 0, NIL);
+  DeclStdProc("TRANSFER"  ,15, MakeParamList(2, 3, addressAlias)); lastParamList^[3] := addressAlias;
+  DeclStdProc("IOTRANSFER",16, MakeParamList(3, 6, AddressType));
+  lastParamList^[3] := addressAlias;
+  lastParamList^[4] := addressAlias;
+  DeclStdProc("NEWPROCESS",17, MakeParamList(4, 1, addressAlias));
+  lastParamList^[3] := CardType;
+  lastParamList^[4] := AddressType;
+  lastParamList^[5] := ProcType;
+  p := MakeParamList(2, 0, WordType);
+  lastParamList^[3] := WordType;
+  DeclStdProc("BDOS"    , 1, p);
+  DeclStdProc("BIOS"    ,18, p);
+  DeclStdProc("MOVE"    ,19, MakeParamList(3, 0, CardType));
+  lastParamList^[3] := AddressType;
+  lastParamList^[4] := AddressType;
+  DeclStdProc("FILL"    ,20, MakeParamList(3, 0, ByteType));
+  lastParamList^[3] := CardType;
+  lastParamList^[4] := AddressType;
+  DeclStdProc("INP"     ,21, MakeParamList(1, 0, WordType)); curScopeHead^.word2 := CardType;
+  DeclStdProc("OUT"     ,22, p);
+
+  SCANNER.HeapAlloc(systemScopeDesc, 14);
+  systemScopeDesc^.word0 := stdScopeAnchor;
+  systemScopeDesc^.word1 := curScopeHead;
+
+  curScopeHead := NIL;
+  DeclStdProc("SYSTEM"  , 0, NIL);
+  curScopeHead^.word4 := 6;
+  curScopeHead^.word3 := 1089;
+  curScopeHead^.high := systemScopeDesc^.word1;
+  systemModule := curScopeHead;
+END DeclareSystemModule;
+
+
+
+PROCEDURE DeclareStorageModule; 
+VAR p: RecordPtr;
+BEGIN 
+  curScopeHead := NIL;
+  allocateSignature := MakeParamList(2,2,CardType);
+  lastParamList^[3] := AddressType;
+  p := MakeParamList(1,1,AddressType);
+  DeclStdProc("ALLOCATE"  , 10, allocateSignature);
+  DeclStdProc("DEALLOCATE", 11, allocateSignature);
+  DeclStdProc("MARK"      , 12, p);
+  DeclStdProc("RELEASE"   , 13, p);
+  DeclStdProc("FREEMEM"   , 14, NIL);
+  curScopeHead^.word2 := CardType;
+  SCANNER.HeapAlloc(storageScopeDesc, 14);
+  storageScopeDesc^.word0 := stdScopeAnchor;
+  storageScopeDesc^.word1 := curScopeHead;
+
+  curScopeHead := NIL;
+  DeclStdProc("STORAGE"   ,  0, NIL);
+  curScopeHead^.word4 := 6;
+  curScopeHead^.word3 := 1089;
+  curScopeHead^.high := storageScopeDesc^.word1;
+  storageModule := curScopeHead;
+END DeclareStorageModule;
+
+(* $[- retain procedure name because of exception handler *)
+PROCEDURE OpenSourceAndOutput; 
+CONST DRVROVEC = 29;
+VAR local2 : ARRAY [0..2] OF CHAR;
+    char : CHAR;
+BEGIN 
+  SCANNER.ProcType := NIL;
+  SCANNER.codeFile  := NIL;
+  Texts.ReadString(ComLine.commandLine, local2);
+  IF NOT Files.Open(SCANNER.ProcType, ComLine.inName) THEN
+    Terminal.WriteString(ComLine.inName);
+    Terminal.WriteString(" not found.");
+    Terminal.WriteLn;
+  ELSE
+    ComLine.RedirectOutput;
+    BDOS(DRVROVEC, 0);
+    IF ORD(ComLine.inName[0])-ORD('A') IN BITSET(IORESULT) THEN Files.ResetSys() END;
+    ComLine.outName := ComLine.inName;
+    SCANNER.ChangeFileExtension(ComLine.outName, "@@@", TRUE);
+    Texts.ReadString(ComLine.commandLine, local2);
+    IF (local2[1] = ':') AND (local2[2] = 0C) THEN
+      char := CAP(local2[0]);
+      IF ORD(char)-ORD('A') <= 15 THEN ComLine.outName[0] := char END;
+    END;
+    Files.Create(SCANNER.codeFile, ComLine.outName);
+    Files.NoTrailer(SCANNER.ProcType);
+    Files.ReadByte(SCANNER.ProcType, char);
+    Files.SetPos(SCANNER.ProcType, LONG(0));
+  END;
+EXCEPTION Files.UseError:
+  Files.ResetSys();
+  Files.Create(SCANNER.codeFile, ComLine.outName);
+END OpenSourceAndOutput;
+
+(* $[+ remove procedure names *)
+
+PROCEDURE InitCompiler; 
+VAR local2: ARRAY [0..2] OF CHAR;
+    option : CARDINAL;
+    compilerOptions[0348H] : BITSET;
+BEGIN 
+  nativeCodeRequested := FALSE;
+  compilationActive := TRUE;
+  baseScopeHead := NIL;
+  savedScopeHead := NIL;
+  InitOperatorTable;
+  InitCharClasses;
+  InitKeywordTable;
+  DeclarePredefinedTypes;
+  DeclareStandardNames;
+  DeclareSystemModule;
+  DeclareStorageModule;
+  SCANNER.scanOptions := compilerOptions;
+  rangeCheckEnabled := 2 IN SCANNER.scanOptions;
+  Texts.ReadString(ComLine.commandLine, local2);
+  IF local2[0] = '%' THEN
+    option := ORD(CAP(local2[1])) - ORD('L');
+    IF option <= 15 THEN INCL(SCANNER.scanOptions, option) END;
+  END;
+  ERRORS.doSymtabRollback := FALSE;
+  SCANNER.HeapAlloc(SYMTAB.IntType, 170);
+  SCANNER.HeapAlloc(SYMTAB.CardType, 170);
+  SCANNER.HeapAlloc(SYMTAB.AddressType,  34);
+  SCANNER.InitScanner;
+  SCANNER.OpenScope(stdScopeAnchor, 0);
+  IF 5 IN SCANNER.scanOptions THEN Texts.WriteCard(3, FREEMEM(), 5) END;
+END InitCompiler;
+END COMPILER.

+ 38 - 0
src/compiler/ERRORS.DEF

@@ -0,0 +1,38 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+DEFINITION MODULE ERRORS;
+FROM SYSTEM IMPORT ADDRESS, WORD;
+FROM Compiler IMPORT CompilerImport;
+FROM Scanner  IMPORT Ext, Buf;
+FROM Files    IMPORT FILE;
+FROM ComLine  IMPORT inName;
+FROM Texts    IMPORT TEXT;
+FROM Terminal IMPORT WriteChar;
+FROM Loader   IMPORT Call;
+FROM Editor   IMPORT editPosition;
+FROM SymTab   IMPORT T1;
+
+TYPE BufPtr = POINTER TO Buf;
+VAR
+    savedEditPos: CARDINAL;
+    opCharTable: POINTER TO ARRAY[0..2] OF CHAR;
+    doSymtabRollback: BOOLEAN;
+
+PROCEDURE ReportError(code: CARDINAL);
+PROCEDURE ReportErrorWithText(code: CARDINAL; VAR msg: ARRAY OF CHAR);
+PROCEDURE AskContinue(info: ADDRESS);
+PROCEDURE WriteString(msg: ARRAY OF CHAR);
+PROCEDURE FindFileOnDrives(VAR file: FILE; VAR name: ARRAY OF CHAR; ext: Ext);
+PROCEDURE WriteKindName(kind: CARDINAL);
+PROCEDURE WriteFoundToken;
+PROCEDURE ShowErrorPosition(mode: CHAR);
+PROCEDURE AskEditOrQuit;
+PROCEDURE PrintOverlayMessage(code: CARDINAL; VAR msg: ARRAY OF CHAR);
+PROCEDURE RollbackToMark;
+PROCEDURE FatalError(msg: ARRAY OF CHAR);
+PROCEDURE ReportAssignMismatch(dst, src: T1; ctx: ADDRESS);
+PROCEDURE ReportTypeMismatch(want: BITSET; got: ADDRESS; ctx: CARDINAL);
+PROCEDURE ReportIncompatibleTypes(t1, t2: T1; op: ARRAY OF CHAR);
+PROCEDURE ReportExpectedSet(valid: BITSET; base: INTEGER);
+PROCEDURE DoError(choice: CHAR); (* choice: user choice E(dit)/Q(uit) *)
+
+END ERRORS.

+ 557 - 0
src/compiler/ERRORS.MOD

@@ -0,0 +1,557 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+IMPLEMENTATION MODULE Errors;
+IMPORT Compiler, Scanner, Files, ComLine, Texts, Terminal, Loader, Editor, SymTab, CodeGen;
+FROM SYSTEM  IMPORT MOVE, ADR, BYTE;
+FROM STORAGE IMPORT MARK, RELEASE;
+
+CONST LF = 12C; CR = 15C; EOF = 32C; DEL = 177C;
+TYPE
+     FLAGS      = (OVERLAY, Z80, INIT);
+     ModDescPtr  = POINTER TO ModuleDesc;
+     ModuleDesc = RECORD
+                    reqModules: ARRAY [0..31] OF ADDRESS;
+                    link : ModDescPtr;
+                    name : ARRAY [0..7] OF CHAR;
+                    loadAddr,checksum,procsAddr: ADDRESS;
+                    CASE : BOOLEAN OF
+                    | TRUE : flags : SET OF FLAGS;
+                    | FALSE: bytes : ARRAY [0..1] OF BYTE;
+                    END;
+                    modExtra : CARDINAL;
+                  END;
+(* $[+ remove procedure names *)
+
+VAR errorNumber: CARDINAL;
+VAR currentDrive: CARDINAL;
+VAR driveInitDone: BOOLEAN;
+
+VAR errorMarkPtr [06EH]: CARDINAL;
+VAR compileOptions[034CH]: BITSET;
+VAR listPipeChar   [034EH]: CHAR;
+
+(* "proc" 18 *)
+EXCEPTION usererror;
+VAR drivesToSearch [034AH]: BITSET;
+
+PROCEDURE WriteString(msg: ARRAY OF CHAR);
+BEGIN
+  Terminal.WriteString(msg)
+END WriteString;
+
+PROCEDURE FatalError(msg: ARRAY OF CHAR);
+BEGIN
+  Terminal.WriteLn;
+  WriteString(msg);
+  Terminal.WriteLn;
+  Files.Delete(Scanner.codeFile);
+  HALT
+END FatalError;
+
+PROCEDURE FindFileOnDrives(VAR file: FILE; VAR name: ARRAY OF CHAR; ext: Ext);
+VAR currentDrive: CARDINAL;
+    local3: CARDINAL;
+    local4: ARRAY [0..13] OF CHAR;
+BEGIN
+  IF NOT driveInitDone THEN
+    currentDrive := Loader.firstDrive;
+    driveInitDone := TRUE;
+  END;
+  currentDrive := currentDrive;
+  local3 := HIGH(name);
+  IF local3 > 11 THEN local3 := 11 END;
+  MOVE(ADR(name), ADR(local4[2]), local3); 
+  local4[0] := CHR(currentDrive + ORD('A'));
+  local4[1] := ':';
+  Scanner.ChangeFileExtension(local4, ext, FALSE);
+  WHILE NOT Files.Open(file, local4) DO
+    REPEAT
+      currentDrive := (currentDrive + 1) MOD 16;
+      IF currentDrive = currentDrive THEN
+        IF ext[0] = 'O' THEN
+          ShowErrorPosition('E');
+          WriteString("ERRMSGS.OVR not found, No =");
+          Texts.WriteCard(3, errorNumber, 4);
+          AskEditOrQuit;
+        ELSE (* 00ac *)
+          ReportErrorWithText(10, name);
+        END;
+      END; (* 00B0 *)
+    UNTIL currentDrive IN drivesToSearch;
+    local4[0] := CHR(currentDrive + ORD('A'));
+  END; (* 00c0 *)
+END FindFileOnDrives;
+
+(* $[- retain procedure names *)
+(* proc 17 *)
+PROCEDURE DoError(param1: CHAR); (* param1: user choice E(dit)/Q(uit) *)
+CONST FREEMARKER = 3AE3H;
+VAR
+    local2: ADDRESS;
+    local3: CARDINAL;
+    local4: CARDINAL;
+    local5: CARDINAL;
+    local6: CARDINAL;
+    local7: CARDINAL;
+    local8: CARDINAL;
+    local9: CARDINAL;
+    local10: ARRAY [0..12] OF CHAR;
+    local11: FILE;
+    size: LONGINT;
+    local14: ModDescPtr;
+    mainModule[030CH]: ModDescPtr;
+    heap[0316H]: ADDRESS;
+BEGIN
+  IF param1 = 'E' THEN (* Edit *)
+    size := Files.FileSize(Scanner.sourceFile);
+    IF size > LONG(38500) THEN size := LONG(38500) END;
+    local5 := CARD(size) DIV 4 * 5 + 1500;
+    IF local5 < 5500 THEN local5 := 5500 END;
+    local7 := ADR(local10) - CARDINAL(Scanner.codeFile);
+    IF local5 > local7 THEN local5 := local7 END;
+    Files.Close(Scanner.sourceFile);
+    local14 := mainModule;
+    mainModule := local14^.link^.link^.link;
+    Scanner.GetStackMark(local2);
+    local9 := local2 - (Scanner.codeBuffer + 4096);
+    local3 := local2 - (CodeGen.windowLimit - CodeGen.nextEmitPos);
+    MOVE(local2-local9, local3-local9, local9);
+    Scanner.CheckStackMark(local3);
+    local6 := ADR(local10) - local3;
+    IF local5 > local6 THEN
+      local8 := local5 - local6;
+      local10 := "@:CBACKUP.$$$";
+      local10[0] := ComLine.inName[0];
+      Files.Create(local11, local10);
+      Files.WriteBytes(local11, local3-local8, local8);
+      Files.SetPos(local11, LONG(0));
+      Scanner.CheckStackMark(local3 - local8);
+    END; (* 018a *)
+    Editor.editPosition := Scanner.tokenPos;
+    Editor.Edit;
+    savedEditPos := Editor.editPosition;
+    IF local5 > local6 THEN
+      Scanner.CheckStackMark(local3);
+      IF Files.ReadBytes(local11, local3-local8, local8)<>local8 THEN
+        Editor.editOkFlag := FALSE
+      END;
+      Files.Delete(local11);
+    END; (* 01B2 *)
+    MOVE(local3-local9, local2-local9, local9);
+    heap := local2 + 60;
+    local2^ := FREEMARKER;
+    mainModule := local14;
+    IF Editor.editOkFlag THEN
+      IF doSymtabRollback THEN SymTab.RestoreAfterEdit END;
+      RAISE usererror
+    END;
+  END; (* 01DD *)
+  FatalError("");
+
+EXCEPTION Files.DiskFull:
+    heap := local2 + 60;
+    local2^ := FREEMARKER;
+    Files.Delete(local11);
+    FatalError("ERROR: DISK FULL")
+END DoError;
+
+(* $[+ retain procedure names *)
+
+PROCEDURE TruncateScopeStack(param1: ADDRESS);
+VAR local2: SymTab.T1;
+    local3: CARDINAL;
+    local4: CARDINAL;
+BEGIN
+  local3 := 0;
+  local4 := 16;
+  WHILE local3 <= local4 DO
+    local2 := SymTab.rollbackStack^[local3];
+    WHILE (local2 <> NIL) AND (param1 <= ADDRESS(local2)) DO
+      local2 := local2^.link0
+    END;
+    SymTab.rollbackStack^[local3] := local2;
+    INC(local3);
+  END;
+END TruncateScopeStack;
+
+PROCEDURE RollbackToMark;
+VAR local2: SymTab.T1;
+    local3: ADDRESS;
+BEGIN
+  Scanner.GetStackMark(local3);
+  local2 := SymTab.currentScope^.link1;
+  WHILE local2 <> NIL DO
+    IF (local2^.word4 = 3) AND (local2^.link2^.word4 = 6)
+    AND (local2^.link2^.link2 <> NIL)
+    AND (local3 <= ADDRESS(local2^.link2^.link2))
+    THEN
+      local2^.link2^.link2 := NIL;
+    END;
+    local2 := local2^.link0;
+  END;
+  TruncateScopeStack(local3);
+  Scanner.OpenSourceFile;
+END RollbackToMark;
+
+CONST BUT = "but ";
+CONST EVALUATED = " evaluated";
+CONST DIFFERENT = "Different ";
+CONST EDITQUIT  = "E(dit, Q(uit ";
+
+PROCEDURE NextSetBit(VAR param2: INTEGER;  param1: BITSET): BOOLEAN;
+BEGIN
+  REPEAT
+    INC(param2);
+    IF CARDINAL(param2) IN param1 THEN RETURN TRUE END;
+  UNTIL param2 >= 16;
+  RETURN FALSE
+END NextSetBit;
+
+PROCEDURE DerefAliasType(ptr: SymTab.T1): SymTab.T1;
+BEGIN
+  IF (ptr^.link0 = NIL) AND (ptr^.word4 = 1) THEN
+    ptr := ptr^.link2
+  END;
+  RETURN ptr
+END DerefAliasType;
+
+PROCEDURE WriteSlashField(n: CARDINAL; str: ARRAY OF CHAR);
+VAR i,j: CARDINAL;
+BEGIN
+  i := 0;
+  j := 0;
+  WHILE i < n DO
+    WHILE (j <= HIGH(str)) AND (str[j] # '/') DO INC(j) END;
+    INC(j); INC(i);
+  END;
+  WHILE (j <= HIGH(str)) AND (str[j] # '/') DO
+    Terminal.WriteChar(str[j]);
+    INC(j)
+  END;
+END WriteSlashField;
+
+PROCEDURE WriteKindName(param1: CARDINAL);
+BEGIN
+  WriteSlashField(param1, "Undefined/Constant/Exception/Type/Variable/Procedure/Module/")
+END WriteKindName;
+
+PROCEDURE WriteTypeName(n: CARDINAL);
+BEGIN
+  WriteSlashField(n,"Enumeration/Subrange///Set/Pointer/Opaque Type///Procedure/Record/Array/")
+END WriteTypeName;
+
+PROCEDURE WriteTypeOfSymbol(ptr: SymTab.T1);
+BEGIN
+  IF ptr^.link0 # NIL THEN
+    WriteString(ptr^.link0^.link1^)
+  ELSE
+    WriteTypeName(ptr^.word4)
+  END;
+END WriteTypeOfSymbol;
+
+PROCEDURE WriteSymbolToken(param1: CHAR);
+VAR ch: CHAR;
+VAR local3: CARDINAL;
+VAR local4: Scanner.Identifier;
+BEGIN
+  local3 := Scanner.procPtr27(local4, param1);
+  IF local3 # 0 THEN WriteString(local4)
+  ELSE
+    Terminal.WriteChar('"');
+    local3 := 0;
+    WHILE (local3 <= 24) AND (opCharTable^[local3+2] # param1) DO INC(local3, 3) END;
+    IF (local3 <= 24) AND ((local3 DIV 3) IN compileOptions) THEN
+      Terminal.WriteChar(opCharTable^[local3]);
+      Terminal.WriteChar(opCharTable^[local3+1]);
+    ELSE
+      IF ORD(param1) = 8 THEN
+        Terminal.WriteChar(listPipeChar);
+      ELSE
+        ch := '!';
+        WHILE (ch < DEL) AND (Scanner.charClassTable^[ORD(ch)] # param1) DO INC(ch) END;
+        IF ch < DEL THEN Terminal.WriteChar(ch) END;
+      END; (* 0433 *)
+    END; (* 0433 *)
+    Terminal.WriteChar('"');
+  END; (* 0437 *)
+END WriteSymbolToken;
+
+PROCEDURE WriteFoundToken;
+BEGIN
+  IF Scanner.curSymbol = 0C THEN
+    IF Scanner.isLiteral THEN WriteString('literal "') ELSE WriteString('identifier "') END;
+    WriteString(Scanner.tokenBuffer);
+    Terminal.WriteChar('"');
+  ELSE
+    WriteSymbolToken(Scanner.curSymbol)
+  END; (* 046E *)
+  WriteString(" found")
+END WriteFoundToken;
+
+PROCEDURE ShowErrorPosition(param1: CHAR);
+VAR local2: CARDINAL;
+VAR local3: CARDINAL;
+VAR local5: LONGINT;
+VAR local6: CHAR;
+BEGIN
+  IF NOT( 0 IN Scanner.scanOptions) THEN
+    Terminal.WriteLn;
+    local5 := Files.NextPos(Scanner.sourceFile);
+    local3 := errorMarkPtr;
+    REPEAT
+      DEC(local3);
+      Files.SetPos(Scanner.sourceFile, LONG(local3));
+      Files.ReadByte(Scanner.sourceFile, local6);
+    UNTIL (local3 = 0) OR (local6 = LF);
+    INC(local3, ORD(Scanner.curChar = LF));
+    Scanner.tokenColumn := Scanner.tokenPos - local3 - 6;
+    Files.SetPos(Scanner.sourceFile, LONG(local3));
+    WHILE local3 # errorMarkPtr DO
+      Files.ReadByte(Scanner.sourceFile, local6);
+      IF local6 # EOF THEN Terminal.WriteChar(local6) END;
+      INC(local3);
+    END;
+    Files.SetPos(Scanner.sourceFile, local5);
+    INCL(Scanner.scanOptions, 0);
+  END; (* 04f6 *)
+  IF param1 # 'X' THEN
+    WHILE (Scanner.curChar # CR) AND (Scanner.curChar # CHR(255)) DO
+      Scanner.NextChar
+    END;
+  END; (*050D*)
+  Terminal.WriteLn;
+  IF Scanner.tokenColumn + 5 < Terminal.numCols THEN
+    local2 := 0;
+    WHILE local2 < Scanner.tokenColumn + 5 DO Terminal.WriteChar(' '); INC(local2) END;
+    WriteString("^   ");
+  END; (* 0535 *)
+  IF param1 = 'X' THEN WriteString("Warning") 
+  ELSE
+    WriteString("Error");
+    WriteSlashField(ORD(param1)-ORD('A'),
+        " in Syntax/ in Identifier/ in Type/, Compiler Limit Exceeded//,illegal Constant/, Implementation Restriction/");
+  END;
+  Terminal.WriteChar(':'); Terminal.WriteLn;
+END ShowErrorPosition;
+
+PROCEDURE AskEditOrQuit;
+VAR ch: CHAR;
+BEGIN
+  Terminal.WriteLn;
+  REPEAT
+    WriteString(EDITQUIT);
+    Terminal.WriteChar('>');
+    Terminal.ReadChar(ch);  ch := CAP(ch);
+    Terminal.WriteChar(ch);
+    Terminal.WriteLn;
+  UNTIL (ch = 'E') OR (ch = 'Q');
+ DoError(ch);
+END AskEditOrQuit; 
+
+PROCEDURE AskContinue(param1: ADDRESS);
+VAR ch: CHAR;
+VAR local3: POINTER TO RECORD w0: WORD; w1: BufPtr END;
+VAR local4: POINTER TO ARRAY [0..1] OF CHAR;
+VAR local5: BITSET;
+BEGIN
+  local5 := Scanner.scanOptions;
+  IF CARDINAL(param1) <= 1 THEN
+    PrintOverlayMessage(param1+50, local4^)
+  ELSIF CARDINAL(param1) = 2 THEN
+    PrintOverlayMessage(52, Scanner.tokenBuffer)
+  ELSE
+    local3 := param1;
+    PrintOverlayMessage(52, local3^.w1^);
+  END; (* 0628 *)
+  Scanner.scanOptions := local5;
+  Terminal.WriteLn;
+  REPEAT
+    WriteString(EDITQUIT);
+    WriteString("C(ontinue >");
+    Terminal.ReadChar(ch);
+    ch := CAP(ch);
+    Terminal.WriteChar(ch); Terminal.WriteLn;
+  UNTIL ORD(ch) - ORD('C') IN {0,2,14};
+  IF ch # 'C' THEN DoError(ch) END;
+END AskContinue;
+
+PROCEDURE PrintOverlayMessage(param3: CARDINAL; VAR msg: ARRAY OF CHAR);
+VAR errFile: Files.FILE;
+VAR local3: CARDINAL;
+VAR local4: CHAR;
+VAR local5: ARRAY [0..7] OF CHAR;
+VAR local6: ADDRESS;
+BEGIN
+  MARK(local6);
+  IF param3 < 100 THEN
+    errorNumber := param3;
+    local5  := "ERRMSGS "; (* NB: one extra char *)
+    FindFileOnDrives(errFile, local5, "OVR");
+    Files.SetPos(errFile, LONG(param3 DIV 20 * 650));
+    local3  := 0;
+    WHILE local3 < param3 MOD 20 DO
+      REPEAT
+        Files.ReadByte(errFile, local4);
+      UNTIL local4 = LF;
+      INC(local3);
+    END; (* 06b7 *)
+    Files.ReadByte(errFile, local4);
+    ShowErrorPosition(local4);
+    REPEAT
+      Files.ReadByte(errFile, local4);
+      IF local4 = '&' THEN WriteString(msg)
+      ELSE Terminal.WriteChar(local4)
+      END; (* 06d1*)
+    UNTIL (local4 = CR) OR (local4 = EOF);
+  END; (* 06dc *)
+  RELEASE(local6);
+END PrintOverlayMessage;
+
+PROCEDURE ReportError(param1: CARDINAL);
+VAR dummy: POINTER TO ARRAY [0..1] OF CHAR;
+BEGIN
+  ReportErrorWithText(param1, dummy^);
+END ReportError;
+
+PROCEDURE ReportErrorWithText(param3: CARDINAL; VAR msg: ARRAY OF CHAR);
+BEGIN
+  PrintOverlayMessage(param3,msg);
+  AskEditOrQuit;
+END ReportErrorWithText;
+
+PROCEDURE ReportExpectedSet(param2: BITSET; param1: INTEGER);
+VAR local2, local3: INTEGER;
+VAR local4: CARDINAL;
+BEGIN
+  ShowErrorPosition('A');
+  local2 := -1;
+  local4 := 0;
+  IF NextSetBit(local2, param2) THEN
+    WriteSymbolToken(CHR(local2 + param1));
+    INC(local4);
+  END;
+  IF NextSetBit(local2, param2) THEN
+    local3 := local2;
+    INC(local4);
+    WHILE NextSetBit(local3, param2) DO
+      Terminal.WriteChar(',');
+      WriteSymbolToken(CHR(local2 + param1));
+      local2 := local3;
+      INC(local4);
+    END; (* 0730 *)
+    WriteString(" or ");
+    WriteSymbolToken(CHR(local2 + param1));
+  END; (* 073d *)
+  WriteString(" expected, ");
+  IF local4 > 5 THEN Terminal.WriteLn END;
+  WriteString(BUT);
+  WriteFoundToken;
+  AskEditOrQuit;
+END ReportExpectedSet;
+
+PROCEDURE WriteRequiredNote(param1: ADDRESS);
+VAR local2: POINTER TO RECORD w0: WORD; w1: BufPtr END;
+BEGIN
+  WriteString(" required");
+  IF param1 > 255 THEN
+    local2 := param1;
+    IF local2^.w1 # NIL THEN
+      WriteString(' for "');
+      WriteString(local2^.w1^);
+      Terminal.WriteChar('"');
+    END; (* 078A *)
+    RETURN
+  END; (* 078B *)
+  IF param1 # NIL THEN
+    WriteString(" for ");
+    WriteSymbolToken(CHR(param1));
+  END; (* 079b *)
+END WriteRequiredNote;
+
+PROCEDURE ReportTypeMismatch(param3: BITSET; param2: ADDRESS; param1: CARDINAL);
+VAR local2: INTEGER;
+VAR local3: SymTab.T1;
+BEGIN
+  ShowErrorPosition('C');
+  local3 := param2;
+  IF    param3 = {0,1}  THEN WriteString("Bounded Type")
+  ELSIF param3 = {4,8}  THEN WriteString("REAL or Set Type")
+  ELSE
+    IF 2 IN param3 THEN
+      IF 0 IN param3    THEN WriteString("Scalar")
+                        ELSE WriteString("Numeric") END;
+      IF 5 IN param3    THEN WriteString(",Pointer") END;
+      IF 4 IN param3    THEN WriteString(" or Set") END;
+      WriteString(" Type");
+      IF NOT( {7,8} - param3 = {} ) THEN 
+        WriteString(",exluding");
+        IF NOT( 7 IN param3 ) THEN WriteString(" LONGINT and") END;
+        WriteString(" REAL,");
+      END; (* 085b *)
+    ELSE
+      local2 := -1;
+      IF NextSetBit(local2, param3) THEN WriteTypeName(local2) END; (* 086b *)
+    END; (* 086b *)
+  END; (* 086B *)
+  WriteRequiredNote(param1);
+  Terminal.WriteChar(',');
+  Terminal.WriteLn;
+  WriteString(BUT);
+  IF (local3^.link0 # NIL) AND (6 IN local3^.link0^.word3) THEN
+    WriteTypeOfSymbol(local3)
+  ELSE
+    WriteTypeName(local3^.word4)
+  END;
+  WriteString(EVALUATED);
+  AskEditOrQuit;
+END ReportTypeMismatch;
+
+PROCEDURE ReportIncompatibleTypes(param4, param3: T1; param2: ARRAY OF CHAR);
+VAR local2, local3: T1;
+BEGIN
+  ShowErrorPosition('C');
+  local2 := DerefAliasType(param4);
+  local3 := DerefAliasType(param3);
+  IF  (local2^.link0 = NIL)
+  AND (local3^.link0 = NIL)
+  AND (local2^.word4 = local3^.word4) 
+  THEN
+    WriteString(DIFFERENT);
+    WriteTypeName(local2^.word4);
+    WriteString(" types");
+  ELSE
+    WriteTypeOfSymbol(local2);
+    WriteString(" and ");
+    WriteTypeOfSymbol(local3);
+  END; (* 08db *)
+  WriteString(" are");
+  WriteString(param2);
+  WriteString(" incompatible");
+  AskEditOrQuit;
+END ReportIncompatibleTypes;
+
+PROCEDURE ReportAssignMismatch(param3, param2: T1; param1: ADDRESS);
+VAR local2, local3: T1;
+BEGIN
+  ShowErrorPosition('C');
+  local2 := param3;
+  local3 := param2;
+  IF local2^.link0 = NIL THEN local2 := DerefAliasType(local2) END;
+  IF local3^.link0 = NIL THEN local3 := DerefAliasType(local3) END;
+  IF (local2^.link0 # NIL) OR (local2^.word4 # local3^.word4) THEN
+    WriteTypeOfSymbol(local2);
+    WriteRequiredNote(param1);
+    WriteString(", but ");
+    IF (local3^.link0 = NIL) AND (local2^.word4 = local3^.word4) THEN
+      WriteString("other ")
+    END;
+    WriteTypeOfSymbol(local3);
+    WriteString(EVALUATED);
+  ELSE
+    WriteString(DIFFERENT);
+    WriteTypeName(local2^.word4);
+    WriteRequiredNote(param1);    
+  END; (*095c *)
+  AskEditOrQuit;
+END ReportAssignMismatch;
+
+END Errors.

+ 30 - 0
src/compiler/README.md

@@ -0,0 +1,30 @@
+# Compiler sources, renamed for readability
+
+Derived from `Reversing-Turbo-Modula2-main/decompiled/COMPILER/`
+(upstream, git-ignored). Semantics unchanged — only identifiers renamed.
+No `0x1A`/`0x00` padding, LF line endings.
+
+## Files
+
+| file | status |
+|---|---|
+| `COMPILER.DEF/MOD` | renamed: `InsertIdent/DeclConst/DeclStdProc/DeclProc/DeclFunc/NewTypeDesc/DeclType/InitOperatorTable/.../InitCompiler/OpenSourceAndOutput`, globals `curScopeHead/savedScopeHead/baseScopeHead/lastParamList/systemModule/...` |
+| `SCANNER.DEF/MOD` | renamed: `GetSym/NextChar/AcceptSymbol/ExpectSymbol/TestSymbolInSet/.../ParseNumber/SkipComment/ScanNextToken`, state `scanOptions/curChar/curSymbol/tokenBuffer/charClassTable/...` |
+| `ERRORS.DEF/MOD` | renamed: `ReportError/FindFileOnDrives/AskEditOrQuit/DoError/RollbackToMark/ReportExpectedSet/ReportTypeMismatch/...` |
+| `CODEGEN.DEF/MOD` | renamed: `SetPendingOp/FlushPendingOp/QueueConst/EmitTypedOp/OpenFixup/CloseFixup/PeekCodeByte/...`, state `nextEmitPos/windowBase/codeWindow/fixupQueue/...` |
+| `SYMTAB.DEF` | renamed globals/procs; `SYMTAB.MOD` does not exist upstream yet (only `symtab.txt` disassembly) |
+
+Full old->new tables: `RENAME-MAP.md`.
+
+## Deliberately NOT renamed (next steps)
+
+* `param1..param4` / `local2..localN` inside `.MOD` bodies (DEF signatures are renamed; bodies keep originals so diffs stay reviewable).
+* `Pass1/Statemen/Express` — no `.MOD` upstream, only `pass1.txt/statement.txt/express.txt`; see `docs/compiler/60-frontend.md` for the proc map.
+* `GENZ80` native backend — untouched until MCode output is stable.
+
+## Verification (required before calling these authoritative)
+
+Per `docs/compiler/70-rename-plan.md`: recompile with Turbo Modula-2
+and `unassemble.c`-diff the `.MCD` against
+`MCode_disassembly/{compiler,scanner,errors,codegen}.txt`.
+Not done yet — these files are a reading aid, not a verified rebuild.

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

@@ -0,0 +1,205 @@
+# Rename map (old -> new)
+
+Mechanical, semantics-preserving. Qualified cross-module uses renamed too
+(e.g. `SCANNER.proc15` -> `SCANNER.NewNode`). `paramN/localN` in `.MOD` bodies kept as-is for now.
+
+## COMPILER
+* `PROC1` -> `InitCompiler`
+* `PROC3` -> `InsertIdent`
+* `PROC4` -> `DeclConst`
+* `PROC5` -> `DeclStdProc`
+* `PROC6` -> `DeclProc`
+* `PROC7` -> `DeclFunc`
+* `PROC8` -> `NewTypeDesc`
+* `PROC9` -> `DeclType`
+* `PROC10` -> `InitOperatorTable`
+* `PROC11` -> `AddOperator`
+* `PROC12` -> `InitCharClasses`
+* `PROC13` -> `InitKeywordTable`
+* `PROC14` -> `DeclarePredefinedTypes`
+* `PROC15` -> `MakeParamList`
+* `PROC16` -> `DeclareStandardNames`
+* `PROC17` -> `DeclareSystemModule`
+* `PROC18` -> `DeclareStorageModule`
+* `GETINP` -> `OpenSourceAndOutput`
+* `global2` -> `rangeCheckEnabled`
+* `global3` -> `scopeSpare3`
+* `global4` -> `stdScopeAnchor`
+* `global5` -> `keywordTable`
+* `global12` -> `charArrayDesc`
+* `global17` -> `addressAlias`
+* `global20` -> `systemModule`
+* `global21` -> `storageModule`
+* `global22` -> `allocateSignature`
+* `global23` -> `systemScopeDesc`
+* `global24` -> `storageScopeDesc`
+* `global25` -> `nativeCodeRequested`
+* `global26` -> `compilationActive`
+* `global27` -> `reserved27`
+* `global28` -> `reserved28`
+* `global29` -> `reserved29`
+* `global30` -> `reserved30`
+* `global31` -> `curScopeHead`
+* `global32` -> `savedScopeHead`
+* `global33` -> `baseScopeHead`
+* `global34` -> `bitsetRangeDesc`
+* `global35` -> `lastParamList`
+* `proc1..28 (DEF only)` -> `overlayProc01..28 (link-stub imports, not Compiler-owned)`
+
+## SCANNER
+* `proc1` -> `GetSym`
+* `proc2` -> `NextChar`
+* `proc3` -> `PushWithScope`
+* `proc4` -> `AcceptSymbol`
+* `proc5` -> `GetStackMark`
+* `proc6` -> `CheckStackMark`
+* `proc8` -> `ExpectSymbol`
+* `proc9` -> `TestSymbolInSet`
+* `proc10` -> `TestSymbolRange40`
+* `proc11` -> `TestSymbolRange13`
+* `proc12` -> `NeedIdentifier`
+* `proc13` -> `ExpectIdentKind`
+* `proc14` -> `NewSizedNode`
+* `proc15` -> `NewNode`
+* `proc16` -> `CopyStringToHeap`
+* `proc17` -> `OpenScope`
+* `proc18` -> `MakeIdentifierNode`
+* `proc19` -> `DeclareIdentifier`
+* `proc20` -> `InsertSymbol`
+* `proc21` -> `ExpectStringLiteral`
+* `proc25` -> `ChangeFileExtension`
+* `proc26` -> `ScannerError`
+* `proc27` -> `EnterModuleSymbol`
+* `proc28` -> `InitScanner`
+* `proc29` -> `OpenSourceFile`
+* `proc32` -> `WriteListingPrefix`
+* `proc33` -> `EchoSourceChar`
+* `proc34` -> `RefillBuffer`
+* `proc37` -> `AccumulateDigit`
+* `proc38` -> `SkipComment`
+* `Number` -> `ParseNumber`
+* `Ten` -> `Power10`
+* `scanNextToken` -> `ScanNextToken`
+* `global2` -> `scanOptions`
+* `global3` -> `curChar`
+* `global4` -> `codeFile`
+* `global5` -> `curSymbol`
+* `global6` -> `isLiteral`
+* `global7` -> `cardValue`
+* `global8` -> `curNode`
+* `global9` -> `identKind`
+* `global10` -> `followSet`
+* `global11` -> `tokenBuffer`
+* `global12` -> `literalType`
+* `global13` -> `scopeCursor`
+* `global14` -> `realValue`
+* `global16` -> `sourceFile`
+* `global17` -> `longrealValue`
+* `global21` -> `codeBuffer`
+* `global22` -> `codeSize`
+* `global23` -> `listingEnabled`
+* `global24` -> `charClassTable`
+* `global25` -> `tokenPos`
+* `global26` -> `tokenColumn`
+* `procPtr27` -> `keywordHashFunc`
+* `global29` -> `withScopeSave`
+* `global30` -> `tokenLength`
+
+## ERRORS
+* `proc1` -> `ReportError`
+* `proc2` -> `ReportErrorWithText`
+* `proc3` -> `AskContinue`
+* `proc4` -> `WriteString`
+* `proc5` -> `FindFileOnDrives`
+* `proc6` -> `WriteKindName`
+* `proc7` -> `WriteFoundToken`
+* `proc8` -> `ShowErrorPosition`
+* `proc9` -> `AskEditOrQuit`
+* `proc10` -> `PrintOverlayMessage`
+* `proc11` -> `RollbackToMark`
+* `proc12` -> `FatalError`
+* `proc13` -> `ReportAssignMismatch`
+* `proc14` -> `ReportTypeMismatch`
+* `proc15` -> `ReportIncompatibleTypes`
+* `proc16` -> `ReportExpectedSet`
+* `proc19` -> `TruncateScopeStack`
+* `proc24` -> `NextSetBit`
+* `proc25` -> `DerefAliasType`
+* `proc26` -> `WriteSlashField`
+* `proc27` -> `WriteTypeName`
+* `proc28` -> `WriteTypeOfSymbol`
+* `proc29` -> `WriteSymbolToken`
+* `proc30` -> `WriteRequiredNote`
+* `DoError` -> `DoError (kept)`
+* `global1 (field)` -> `modExtra`
+* `global2` -> `savedEditPos`
+* `global3` -> `opCharTable`
+* `global4` -> `doSymtabRollback`
+* `global5` -> `errorNumber`
+* `global6` -> `currentDrive`
+* `global7` -> `driveInitDone`
+* `globalPtr` -> `errorMarkPtr`
+* `options` -> `compileOptions`
+* `pipe` -> `listPipeChar`
+
+## CODEGEN
+* `proc4` -> `DiscardPending`
+* `proc5` -> `SetPendingOp`
+* `proc6` -> `EmitTypedOp`
+* `proc7` -> `EmitExtendedOp`
+* `proc8` -> `QueueConst`
+* `proc9` -> `QueueLongConst`
+* `proc10` -> `EmitStandardOp`
+* `proc11` -> `EmitMiscOp`
+* `proc12` -> `OpenFixup`
+* `proc13` -> `CloseFixup`
+* `proc14` -> `InsertFixup`
+* `proc15` -> `AdjustFixup`
+* `proc16` -> `MarkCodePos`
+* `proc17` -> `EmitSystemCall`
+* `proc18` -> `EmitExtCall3`
+* `proc19` -> `PeekCodeByte`
+* `proc20` -> `PokeCodeByte`
+* `proc22` -> `OpenEmitter`
+* `proc23` -> `FlushCodeWindow`
+* `proc24` -> `InitCodeGenerator`
+* `proc26` -> `CodeAssert`
+* `proc28` -> `SeekCodeWindow`
+* `proc29` -> `CheckCodeOverflow`
+* `proc30` -> `FlushPendingOp`
+* `proc31` -> `BaseOpCode`
+* `proc32` -> `FlushConstQueue`
+* `proc33/34/36` -> `Reserved33/34/36 (no-op stubs)`
+* `proc35` -> `IsPowerOfTwo`
+* `global2` -> `nextEmitPos`
+* `global3` -> `windowBase`
+* `global4` -> `windowLimit`
+* `global5` -> `emitEnabled`
+* `global6` -> `codeWindow`
+* `global7` -> `pendMode`
+* `global8` -> `pendSize`
+* `global9` -> `pendDisp`
+* `global10` -> `pendOffset`
+* `global11` -> `fixupQueue`
+* `global12` -> `fixupCount`
+* `global13` -> `spare13`
+* `global14` -> `pendActive`
+* `global15` -> `checkOverflow`
+
+## SYMTAB (DEF only)
+* `global2` -> `symOptions`
+* `global3` -> `moduleCount`
+* `global4` -> `symFile`
+* `global5` -> `stringPoolPtr`
+* `global6` -> `moduleTable`
+* `global7` -> `scopeStack`
+* `global8` -> `rollbackStack`
+* `global9` -> `currentScope`
+* `proc1` -> `CompileDispatch`
+* `proc2` -> `DefineDefinitionModule`
+* `proc3` -> `DefineProgramModule`
+* `proc4` -> `InitSymbolTables`
+* `proc5` -> `BuildBitsetConst`
+* `proc6` -> `SaveSymbolFile`
+* `proc7` -> `RestoreAfterEdit`
+* `proc8` -> `SymbolAssert`

+ 78 - 0
src/compiler/SCANNER.DEF

@@ -0,0 +1,78 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+DEFINITION MODULE SCANNER;
+FROM SYSTEM   IMPORT ADDRESS, WORD;
+FROM Compiler IMPORT RecordPtr; (* to be sure Compiler is first dependency *)
+FROM Files    IMPORT FILE;
+FROM Texts    IMPORT TEXT;
+FROM ComLine  IMPORT PromptFor;
+FROM Loader   IMPORT Call;
+FROM Doubles  IMPORT StrToDouble;
+FROM Errors   IMPORT DoError;
+FROM SymTab   IMPORT T1, StringPtr;
+
+TYPE Ext = ARRAY [0..2] OF CHAR;
+     T = POINTER TO ARRAY [0..6] OF WORD;
+     Buf = ARRAY [0..128] OF CHAR;
+(*
+     Identifier = ARRAY [0..14] OF CHAR;
+     StringPtr  = POINTER TO Identifier;
+*)
+     List = POINTER TO ListDesc;
+     ListDesc = RECORD first: T1; ident: StringPtr END;
+     TypeOfChar = [0..255];
+VAR
+    scanOptions: BITSET;
+    curChar: CHAR;
+    codeFile: FILE;
+    curSymbol: CARDINAL;
+    isLiteral: BOOLEAN;
+    cardValue: CARDINAL;
+    curNode: RecordPtr;
+    identKind: CARDINAL;
+    followSet: BITSET;
+    tokenBuffer: Buf;
+    literalType: RecordPtr;
+    scopeCursor: T1;
+    realValue: REAL;
+    sourceFile: FILE;
+    longrealValue: LONGREAL;
+    codeBuffer: ADDRESS;
+    codeSize: CARDINAL;
+    listingEnabled: BOOLEAN;
+    charClassTable: POINTER TO ARRAY [0..255] OF TypeOfChar;
+    tokenPos: CARDINAL;
+    tokenColumn: CARDINAL;
+    keywordHashFunc: PROCEDURE(ADDRESS, BOOLEAN):CARDINAL;
+
+PROCEDURE GetSym;
+PROCEDURE NextChar;
+PROCEDURE PushWithScope;
+PROCEDURE AcceptSymbol(sym: CARDINAL): BOOLEAN;
+PROCEDURE GetStackMark(VAR mark: ADDRESS);
+PROCEDURE CheckStackMark(mark: ADDRESS);
+PROCEDURE Allocate(VAR a:ADDRESS; n:CARDINAL);
+PROCEDURE ExpectSymbol(sym: CARDINAL);
+PROCEDURE TestSymbolInSet(symSet: BITSET);
+PROCEDURE TestSymbolRange40(symSet: BITSET);
+PROCEDURE TestSymbolRange13(symSet: BITSET);
+PROCEDURE NeedIdentifier;
+PROCEDURE ExpectIdentKind(kind: CARDINAL);
+PROCEDURE NewSizedNode(nodeKind:CARDINAL):ADDRESS;
+PROCEDURE NewNode(nodeKind:CARDINAL):ADDRESS;
+PROCEDURE CopyStringToHeap(VAR dest:ADDRESS; VAR src: ARRAY OF CHAR);
+PROCEDURE OpenScope(scopeChain:ADDRESS; next: ADDRESS);
+PROCEDURE MakeIdentifierNode(nodeKind: CARDINAL):ADDRESS;
+PROCEDURE DeclareIdentifier(nodeKind: CARDINAL):ADDRESS;
+PROCEDURE InsertSymbol(node: T1);
+PROCEDURE ExpectStringLiteral(VAR pattern: ARRAY OF CHAR);
+PROCEDURE FindIdent(list: List; identifier: ADDRESS; caseInsensitive: BOOLEAN):RecordPtr;
+PROCEDURE StrCmp(ptr1,ptr2: StringPtr; caseInsensitive: BOOLEAN): BOOLEAN;
+PROCEDURE StrLen(VAR s: ARRAY OF CHAR): CARDINAL;
+PROCEDURE ChangeFileExtension(VAR fname: ARRAY OF CHAR; ext: Ext; force: BOOLEAN);
+PROCEDURE ScannerError(code: CARDINAL);
+PROCEDURE EnterModuleSymbol(modName: ARRAY OF CHAR; kind: CARDINAL): CARDINAL;
+PROCEDURE InitScanner;
+PROCEDURE OpenSourceFile;
+PROCEDURE Compile;
+
+END SCANNER.

+ 710 - 0
src/compiler/SCANNER.MOD

@@ -0,0 +1,710 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+IMPLEMENTATION MODULE Scanner;
+IMPORT Compiler, Files, Texts, ComLine, Loader, Doubles, Errors, SymTab, CodeGen;
+IMPORT VarAddr;
+FROM SYSTEM IMPORT ADR, MOVE, CODE, TSIZE, BYTE, OVERFLOW, REALOVERFLOW;
+FROM STORAGE IMPORT ALLOCATE,MARK,RELEASE;
+
+VAR withScopeSave: RecordPtr;
+    tokenLength: CARDINAL;
+
+CONST FREEMARKER = 3AE3H;
+CONST EOT = 032C; DEL = 177C;
+CONST LINEFEED = 012C; TAB = 011C; CR = 015C;
+
+VAR stackLimit [0316H]: ADDRESS;
+VAR buffer  [0080H]: ARRAY [0..127] OF CHAR;
+VAR bufIndex[006CH]: CARDINAL;
+VAR filePos [006EH]: CARDINAL;
+VAR column  [0070H]: CARDINAL;
+VAR flag    [0072H]: CARDINAL;
+
+CONST EXPECTED = " expected, but ";
+
+(* $[+ remove procedure names *)
+
+PROCEDURE ScannerError(param1: CARDINAL);
+BEGIN
+  Errors.ReportError(param1);
+END ScannerError;
+
+PROCEDURE ChangeFileExtension(VAR s: ARRAY OF CHAR; ext: Ext; b: BOOLEAN);
+VAR i: CARDINAL;
+BEGIN
+  i := 0;
+  WHILE (i < HIGH(s) - 3) AND (s[i] <> 0C) AND (s[i] <> '.') DO
+    INC(i)
+  END;
+  IF b OR (s[i] <> '.') THEN
+    s[i] := '.';
+    MOVE(ADR(ext), ADR(s[i+1]), 3);
+  END;
+END ChangeFileExtension;
+
+PROCEDURE EnterModuleSymbol(param3: ARRAY OF CHAR; param1: CARDINAL): CARDINAL;
+VAR i: CARDINAL;
+    ptr : POINTER TO SymTab.Symbol;
+BEGIN
+  i := 0;
+  WHILE (i < SymTab.moduleCount) AND (SymTab.moduleTable^[i].name <> param3) DO
+    INC(i)
+  END; 
+  ptr := ADR(SymTab.moduleTable^[i]);
+  IF i < SymTab.moduleCount THEN
+    IF ptr^.word <> param1 THEN Errors.ReportErrorWithText(12, ptr^.name) END;
+  ELSE
+    IF i > 15 THEN ScannerError(84) END;
+    ptr^.name := param3;
+    ptr^.word := param1;
+    INC(SymTab.moduleCount);
+  END;
+  RETURN i
+END EnterModuleSymbol;
+
+PROCEDURE Allocate(VAR a:ADDRESS; n:CARDINAL); (* was in Z80 code *)
+BEGIN
+  ALLOCATE(a, n);
+  RETURN;
+
+(* padding to compensate for length difference *)
+  n := n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n+n;
+  RETURN; RETURN;
+
+END Allocate;
+
+PROCEDURE GetStackMark(VAR param1: ADDRESS);
+BEGIN
+  param1 := stackLimit - 60;
+END GetStackMark;
+
+PROCEDURE CheckStackMark(param1: ADDRESS);
+BEGIN 
+  stackLimit := param1 + 60;
+  param1^ := FREEMARKER;
+END CheckStackMark;
+
+(* proc 22: find identifier in keyword list *)
+PROCEDURE FindIdent(list: List; identifier: ADDRESS; caseInsensitive: BOOLEAN): RecordPtr;
+(* original was in Z80 code *)
+VAR current: T1;
+BEGIN
+  current := list^.first;
+  WHILE current <> NIL DO
+    IF StrCmp(identifier, current^.link1, caseInsensitive) THEN 
+      RETURN ADDRESS(current)
+    END;
+    current := current^.link0;
+  END;
+  RETURN NIL;
+
+(* padding to compensate for different length *)
+  caseInsensitive := NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT
+    NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT NOT caseInsensitive
+END FindIdent;
+
+(* proc 23: original was in Z80 code, removed caseInsensitive comparison *)
+PROCEDURE StrCmp(ptr1, ptr2: StringPtr; caseInsensitive: BOOLEAN):BOOLEAN;
+BEGIN
+  WHILE ptr1^[0] = ptr2^[0] DO
+    IF ptr1^[0] = 0C THEN RETURN TRUE END;
+    ptr1 := ADDRESS(ptr1)+1;
+    ptr2 := ADDRESS(ptr2)+1;
+  END;
+  RETURN FALSE;
+END StrCmp;
+
+(* StrLen: original was Z80 code *)
+PROCEDURE StrLen(VAR s: ARRAY OF CHAR): CARDINAL;
+VAR i: CARDINAL;
+BEGIN
+  i := 0;
+  WHILE (i < HIGH(s)) AND (s[i] # 0C) DO INC(i) END;
+  IF i # 0 THEN RETURN i END;
+  RETURN 1;  (* never return a zero length *)
+END StrLen;
+
+PROCEDURE CopyStringToHeap(VAR a: ADDRESS; VAR s: ARRAY OF CHAR);
+VAR length: CARDINAL;
+BEGIN
+  length := StrLen(s);
+  Allocate(a, length+1);
+  MOVE(ADR(s), a, length);
+END CopyStringToHeap;
+
+PROCEDURE NewNode(n:CARDINAL):ADDRESS;
+VAR ptr: Compiler.RecordPtr;
+BEGIN
+  Allocate(ptr, 14);
+  ptr^.word4 := n;
+  RETURN ptr
+END NewNode;
+
+PROCEDURE NewSizedNode(n:CARDINAL):ADDRESS;
+VAR ptr: Compiler.RecordPtr;
+BEGIN
+  IF    n <=  1 THEN Allocate(ptr, 14)
+  ELSIF n <=  6 THEN Allocate(ptr, 10)
+  ELSIF n <= 11 THEN Allocate(ptr, 12)
+  ELSE               Allocate(ptr, 16)
+  END;
+  ptr^.word4 := n;
+  RETURN ptr
+END NewSizedNode;
+
+PROCEDURE MakeIdentifierNode(param1: CARDINAL): ADDRESS;
+VAR ptr : Compiler.RecordPtr;
+BEGIN
+  NeedIdentifier;
+  IF ADDRESS(scopeCursor) = SymTab.currentScope THEN Errors.ReportErrorWithText(1, tokenBuffer) END;
+  ptr := NewNode(param1);
+  CopyStringToHeap(ptr^.word1, tokenBuffer);
+  GetSym;
+  RETURN ptr
+END MakeIdentifierNode;
+
+PROCEDURE InsertSymbol(param1: SymTab.T1);
+BEGIN
+  IF FindIdent(ADR(SymTab.currentScope^.link1), param1^.link1, 9 IN scanOptions) # NIL THEN
+    Errors.ReportErrorWithText(1, param1^.link1^)
+  END;
+  param1^.link0 := SymTab.currentScope^.link1;
+  SymTab.currentScope^.link1 := param1;
+END InsertSymbol;
+
+PROCEDURE DeclareIdentifier(param1: CARDINAL): ADDRESS;
+VAR ptr: SymTab.T1;
+BEGIN
+  ptr := MakeIdentifierNode(param1);
+  ptr^.link0 := SymTab.currentScope^.link1;
+  SymTab.currentScope^.link1 := ptr;
+  RETURN ptr;
+END DeclareIdentifier;
+
+PROCEDURE OpenScope(a:ADDRESS; next: ADDRESS);
+BEGIN
+  Allocate(SymTab.currentScope, 14);
+  SymTab.currentScope^.w0 := a;
+  SymTab.currentScope^.link1 := next;
+END OpenScope;
+
+PROCEDURE WriteListingPrefix;
+VAR PipeChar[034EH]: CHAR;
+BEGIN
+  Texts.WriteLn(2);
+  IF listingEnabled 
+  THEN Texts.WriteCard(2, CodeGen.nextEmitPos - codeSize, 4)
+  ELSE Texts.SetCol(2, 4)
+  END;
+  Texts.WriteChar(2, PipeChar);
+  Texts.WriteChar(2, ' ')
+END WriteListingPrefix;
+
+PROCEDURE EchoSourceChar;
+BEGIN
+  IF (ORD(curChar) + 1) MOD 256 > 32 THEN Texts.WriteChar(2, curChar); RETURN END;
+  IF curChar = LINEFEED THEN column := 0; WriteListingPrefix; RETURN END;
+  IF curChar = TAB      THEN 
+    REPEAT Texts.WriteChar(2," "); INC(column) UNTIL column MOD 8 = 0;
+  ELSIF (curChar < " ") AND (curChar # CR) THEN
+    Texts.WriteChar(2, "^");
+    Texts.WriteChar(2, CHR(ORD(curChar)+40H))
+  END;
+END EchoSourceChar;
+
+PROCEDURE RefillBuffer;
+VAR nbRead: CARDINAL;
+BEGIN
+  nbRead := Files.ReadBytes(sourceFile, ADR(buffer), 128);
+  IF nbRead < 128 THEN buffer[nbRead] := EOT END;
+  bufIndex := 0;
+  NextChar
+END RefillBuffer;
+
+PROCEDURE InitScanner;
+BEGIN
+  curChar  := ' ';
+  column   := 0;
+  bufIndex := 128;
+  IF 0 IN scanOptions THEN WriteListingPrefix END;
+  Files.NoTrailer(sourceFile)
+END InitScanner;
+
+PROCEDURE OpenSourceFile;
+VAR nbRead: CARDINAL;
+BEGIN
+  IF NOT Files.Open(sourceFile, ComLine.inName) THEN HALT END;
+  InitScanner;
+  Files.SetPos(sourceFile, LONG((tokenPos DIV 128) * 128));
+  nbRead   := Files.ReadBytes(sourceFile, ADR(buffer), 128);
+  bufIndex := tokenPos MOD 128;
+  filePos  := tokenPos;
+  GetSym
+END OpenSourceFile;
+
+PROCEDURE NextChar; (* original was in Z80 code *)
+VAR ch: CHAR;
+BEGIN
+  IF bufIndex = 128 THEN RefillBuffer
+  ELSE
+    ch := buffer[bufIndex];
+    INC(bufIndex);
+    IF ch = EOT THEN curChar := 377C ELSE curChar := ch END;
+    INC(filePos);
+    IF 0 IN scanOptions THEN
+      IF ch >= ' ' THEN
+        INC(column);
+        IF flag # 0 THEN
+          Texts.WriteChar(3, ch);
+          RETURN; RETURN; (* padding *)
+        END;
+      END;
+      EchoSourceChar
+    END;
+  END;
+  RETURN;
+(* padding to compensate for smaller length *)
+  INC(ch); INC(ch); INC(ch); INC(ch); INC(ch); INC(ch);
+  INC(ch); INC(ch); INC(ch); INC(ch); RETURN; RETURN; RETURN; RETURN;
+END NextChar;
+
+PROCEDURE GetSym;
+(* $[- keep procedure names *)
+
+  (* proc 35 *)
+  PROCEDURE ParseNumber;
+  VAR local2: CARDINAL;
+  VAR local3: CARDINAL;
+  VAR local4: CARDINAL;
+  VAR local5: CARDINAL;
+  VAR local6: CARDINAL;
+  VAR local7: INTEGER;
+  VAR local8: BOOLEAN;
+  VAR local9 : BOOLEAN;
+  VAR local10: BOOLEAN;
+  VAR local12: LONGINT;
+  VAR local14: LONGINT;
+  VAR local16: LONGINT;
+  VAR local18: LONGINT;
+  
+  (* $[+ remove procedure names *)
+  
+    (* proc 36 *)
+    PROCEDURE Power10(exp: CARDINAL): REAL;
+    VAR n: CARDINAL;
+    VAR r: REAL;
+    BEGIN
+      n := 0;
+      r := 1.0;
+      REPEAT
+        IF ODD(exp) THEN
+          CASE n OF
+          | 0 : r := r * 1.0E01
+          | 1 : r := r * 1.0E02
+          | 2 : r := r * 1.0E04
+          | 3 : r := r * 1.0E08
+          | 4 : r := r * 1.0E16
+          | 5 : r := r * 1.0E32
+          ELSE RAISE REALOVERFLOW
+          END;
+        END;
+        exp := exp DIV 2;
+        INC(n);
+      UNTIL exp = 0;
+      RETURN r
+    END Power10;
+  
+    PROCEDURE AccumulateDigit;
+    VAR digit: CARDINAL;
+    BEGIN
+      IF tokenLength < 128 THEN tokenBuffer[tokenLength] := curChar; INC(tokenLength) END;
+      NextChar;
+      digit := ORD(curChar) - ORD('0');
+      IF digit > 9 THEN
+        IF digit >= 17 THEN digit := digit - 7 ELSE digit := 16 END;
+      END; (* 043C *)
+      local4 := digit
+    END AccumulateDigit;
+  
+  (* $[- keep procedure names *)
+  BEGIN (* ParseNumber *)
+    local2  := 0;
+    local12 := LONG(0);
+    local7  := 0;
+    local18 := local12;
+    local16 := local12;
+    local14 := local12;
+    tokenLength:= 0;
+    local4  := ORD(curChar) - ORD('0');
+    local5  := local4;
+    local10 := TRUE;
+    local9  := TRUE;
+    REPEAT
+      IF local5 > 7 THEN
+        local10 := FALSE;
+        IF local5 > 9 THEN local9 := FALSE END;
+      END;
+      IF local4 <= 9 THEN
+        local14 := local14 * LONG(10) + LONG(local4);
+        IF local14 < 3355443L THEN local12 := local14 ELSE INC(local7) END;
+        IF local4 <= 7 THEN local16 := local16 * LONG(8) + LONG(local4) END;
+      END; (* 04b1 *)
+      IF local18 <= LONG(65535) THEN local18 := local18 * LONG(16) + LONG(local4) END;
+      local5 := local4;
+      AccumulateDigit;
+    UNTIL local4 > 15;
+    literalType := Compiler.CardType;
+    IF curChar = '.' THEN
+      AccumulateDigit;
+      IF    curChar = '.' THEN curChar := DEL; DEC(tokenLength)
+      ELSIF curChar = ')' THEN curChar := ']'; DEC(tokenLength)
+      ELSE
+        IF NOT local9 THEN ScannerError(30) END;
+        literalType := Compiler.RealType;
+        WHILE local4 <= 9 DO
+          IF local12 < 3355443L THEN
+            local12 := local12 * LONG(10) + LONG(local4);
+            DEC(local7)
+          END; (* 0527 *)
+          AccumulateDigit;
+        END; (* 052B *)
+        IF local4 IN {13, 14} THEN
+          IF local4 = 13 THEN literalType := Compiler.LongrealType END;
+          local3 := 0;
+          AccumulateDigit;
+          local8 := (curChar = '-');
+          IF local8 OR (curChar = '+') THEN AccumulateDigit END;
+          IF local4 > 9 THEN ScannerError(30) END;
+          REPEAT
+            IF local3 < 255 THEN local3 := local3 * 10 + local4 END;
+            AccumulateDigit;
+          UNTIL local4 > 9;
+          IF local8
+          THEN DEC(local7, local3)
+          ELSE INC(local7, local3)
+          END;
+        END; (* 0577 *)
+        IF literalType = Compiler.RealType THEN
+          IF local12 >= 16777216L
+          THEN realValue := FLOAT((local12 + LONG(1)) DIV LONG(2)) * 2.0
+          ELSE realValue := FLOAT(local12)
+          END;
+          IF    local7 < 0 THEN realValue := realValue / Power10(-local7)
+          ELSIF local7 # 0 THEN realValue := realValue * Power10(local7)
+          END;
+        ELSE
+          tokenBuffer[tokenLength] := 0C;
+          IF NOT Doubles.StrToDouble(tokenBuffer, longrealValue) THEN ScannerError(74) END;
+        END;
+      END;
+    END; (* 05D4 *)
+    IF literalType^.word4 # 8 THEN
+      IF curChar = 'L' THEN
+        AccumulateDigit;
+        realValue := REAL(local14);
+        literalType := Compiler.LongintType;
+      ELSE
+        local6 := 65535;
+        IF curChar = 'H' THEN AccumulateDigit; local12 := local18
+        ELSE
+          IF local5 IN {11, 12} THEN
+            IF NOT local10 THEN ScannerError(30) END;
+            local12 := local16;
+            IF local5 = 12 THEN literalType := Compiler.CharType; local6 := 255 END;
+          ELSE
+            IF NOT local9 OR (local5 > 9) THEN ScannerError(30) END;
+            local12 := local14;
+          END;
+        END; (* 062e *)
+        IF local12 > LONG(local6) THEN ScannerError(73) END;
+        cardValue := CARD(local12);
+      END;
+    END; (* 063f *)
+    IF charClassTable^[ORD(curChar)] = 10 THEN ScannerError(30) END;
+  EXCEPTION
+  | OVERFLOW: ScannerError(73)
+  | REALOVERFLOW: 
+      IF local7 >= 0 THEN ScannerError(74) END;
+      realValue := REAL(0L);
+  END ParseNumber;
+  
+(* $[+ remove procedure names *)
+  PROCEDURE SkipComment;
+  VAR local2: CARDINAL;
+  BEGIN
+    REPEAT
+      REPEAT
+        IF curChar = CHR(255) THEN ScannerError(29) END;
+        NextChar;
+        IF curChar = '(' THEN
+          REPEAT NextChar UNTIL curChar # '(';
+          IF curChar = '*' THEN SkipComment END; (* recursive call *)
+        END;
+        IF curChar = '$' THEN
+          NextChar;
+          local2 := CARDINAL(BITSET(curChar) * {0,1,2,3,4,6}) - ORD('L');
+          IF local2 <= 15 THEN
+            NextChar;
+            IF    curChar = '-' THEN EXCL(scanOptions, local2)
+            ELSIF curChar = '+' THEN INCL(scanOptions, local2)
+            END;
+            IF local2 = 0 THEN Texts.WriteLn(2) END;
+          END;
+        END; (* 06c6 *)
+      UNTIL curChar = '*';
+      REPEAT NextChar UNTIL curChar # '*';
+    UNTIL curChar = ')';
+  END SkipComment;
+
+  PROCEDURE ScanNextToken():BOOLEAN; (* original was in Z80 code *)
+  VAR i: CARDINAL;
+  VAR local3: CARDINAL;
+  BEGIN
+    isLiteral := FALSE;
+    identKind := 7;
+    WHILE curChar <= ' ' DO NextChar END;
+    tokenPos := filePos - 1;
+    tokenColumn := column;
+    IF curChar < CHR(128)
+    THEN curSymbol := charClassTable^[ORD(curChar)]
+    ELSE curSymbol := 0
+    END;
+    IF curSymbol = 10 THEN
+      i := 0;
+      REPEAT
+        IF i # 128 THEN tokenBuffer[i] := curChar; INC(i) END;
+        NextChar;
+        local3 := charClassTable^[ORD(curChar)];
+      UNTIL (local3 # 10) AND (local3 # 11);
+      tokenBuffer[i] := 0C;
+      RETURN TRUE
+    END; (* 0730 *)
+    RETURN FALSE;
+    (* padding to compensate for smaller code *)
+    i := i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i+i
+  END ScanNextToken;
+
+VAR local2: BOOLEAN;
+    local3: CARDINAL;
+    local4: BOOLEAN;
+    local5: CHAR;
+BEGIN
+  REPEAT
+    IF ScanNextToken() THEN
+      local4 := 9 IN scanOptions;
+      curSymbol := keywordHashFunc(ADR(tokenBuffer), local4);
+      IF curSymbol # 0 THEN
+        identKind := 7;
+        IF ((curSymbol = 14) OR (curSymbol = 39)) AND NOT (12 IN scanOptions) THEN
+          Errors.AskContinue(2)
+        END;
+        RETURN
+      END; (* 0797 *)
+      scopeCursor := ADDRESS(SymTab.currentScope);
+      REPEAT
+        curNode  := FindIdent(ADR(scopeCursor^.link1), ADR(tokenBuffer), local4);
+        IF curNode # NIL THEN
+          literalType := ADDRESS(curNode^.word2);
+          identKind  := curNode^.word4;
+          followSet := curNode^.word3;
+          RETURN
+        END;
+        scopeCursor := scopeCursor^.link0;
+      UNTIL scopeCursor = NIL;
+      identKind := 0;
+      RETURN
+    END; (* 07BA *)
+    local2 := TRUE;
+    CASE curSymbol OF
+    | 0 : ScannerError(31 - ORD(curChar = CHR(255)) * 2)
+    | 2 : NextChar;
+          IF    curChar = '=' THEN curSymbol := 27; NextChar
+          ELSIF curChar = ')' THEN curSymbol := 7; NextChar
+          END;
+    | 3 : NextChar;
+          IF    curChar = '.' THEN curSymbol := 4; NextChar
+          ELSIF curChar = ')' THEN curSymbol := 6; NextChar
+          END;
+    |11 : isLiteral := TRUE;
+          identKind := 1;
+          curSymbol := 0;
+          ParseNumber;
+          tokenBuffer[tokenLength] := 0C
+    |12 : local3  := 0;
+          isLiteral := TRUE;
+          identKind := 1;
+          curSymbol := 0;
+          local5  := curChar;
+          NextChar;
+          WHILE curChar # local5 DO
+            IF curChar = LINEFEED THEN ScannerError(28) END;
+            IF curChar = CHR(255) THEN ScannerError(29) END;
+            IF local3 < 128 THEN
+              tokenBuffer[local3] := curChar;
+              INC(local3);
+            END;
+            NextChar;
+          END; (* 0839 *)
+          tokenBuffer[local3] := 0C;
+          NextChar;
+          literalType := Compiler.charArrayDesc;
+          IF local3 = 1 THEN
+            literalType := Compiler.CharType;
+            cardValue  := ORD(tokenBuffer[0]);
+          END;
+    ELSE
+          NextChar;
+          CASE curSymbol OF
+          | 43:  IF curChar = '*' THEN SkipComment; NextChar; local2 := FALSE
+              ELSIF curChar = '.' THEN curSymbol := 44; NextChar
+              ELSIF curChar = ':' THEN curSymbol := 45; NextChar
+              END;
+          | 54:  IF curChar = '=' THEN curSymbol := 56; NextChar 
+              ELSIF curChar = '>' THEN curSymbol := 53; NextChar
+              END;
+          | 55:  IF curChar = '=' THEN curSymbol := 57; NextChar END;
+          END;
+    END;
+  UNTIL local2;
+END GetSym;
+
+PROCEDURE AcceptSymbol(param1: CARDINAL): BOOLEAN;
+BEGIN
+  IF curSymbol = param1 THEN GetSym; RETURN TRUE END;
+  RETURN FALSE
+END AcceptSymbol;
+
+PROCEDURE PushWithScope;
+BEGIN
+  IF identKind = 6 THEN
+    withScopeSave^.word1 := ADDRESS(curNode^.high);
+    withScopeSave^.word0 := ADDRESS(SymTab.currentScope);
+    SymTab.currentScope  := ADDRESS(withScopeSave);
+    GetSym;
+    ExpectSymbol(3);
+    NeedIdentifier;
+    IF scopeCursor # ADDRESS(withScopeSave) THEN Errors.ReportErrorWithText(3, tokenBuffer) END;
+    SymTab.currentScope  := SymTab.currentScope^.w0;
+  END;
+END PushWithScope;
+
+PROCEDURE ExpectSymbol(param1:CARDINAL);
+VAR local2: BITSET;
+VAR local3: CARDINAL;
+BEGIN
+  IF curSymbol = param1 THEN GetSym; RETURN END;
+  local3 := 0;
+  local2 := {};
+  IF    param1 >= 51 THEN local3 := 51
+  ELSIF param1 >= 40 THEN local3 := 40
+  ELSIF param1 >= 27 THEN local3 := 27
+  ELSIF param1 >= 13 THEN local3 := 13
+  END;
+  local2 := local2 + {param1 - local3};
+  Errors.ReportExpectedSet(local2, local3)
+END ExpectSymbol;
+
+PROCEDURE TestSymbolInSet(param1: BITSET);
+BEGIN
+  IF NOT (curSymbol IN param1) THEN Errors.ReportExpectedSet(param1, 0) END;
+END TestSymbolInSet;
+
+PROCEDURE TestSymbolRange40(param1: BITSET);
+BEGIN
+  IF NOT ((curSymbol-40) IN param1) THEN Errors.ReportExpectedSet(param1, 40) END;
+END TestSymbolRange40;
+
+PROCEDURE TestSymbolRange13(param1: BITSET);
+BEGIN
+  IF NOT ((curSymbol-13) IN param1) THEN Errors.ReportExpectedSet(param1, 13) END;
+END TestSymbolRange13;
+
+PROCEDURE NeedIdentifier;
+BEGIN
+  IF (identKind = 7) OR isLiteral THEN
+    Errors.ShowErrorPosition('A');
+    Texts.WriteString(3, "Identifier");
+    Texts.WriteString(3, EXPECTED);
+    Errors.Allocate;
+    Errors.AskEditOrQuit;
+  END;
+END NeedIdentifier;
+
+PROCEDURE ExpectStringLiteral(VAR param2: ARRAY OF CHAR);
+BEGIN
+  NeedIdentifier;
+  IF StrCmp(ADR(tokenBuffer), ADR(param2), 9 IN scanOptions) THEN GetSym; RETURN END;
+  Errors.ReportErrorWithText(9, param2);
+END ExpectStringLiteral;
+
+PROCEDURE ExpectIdentKind(param1: CARDINAL);
+BEGIN
+  IF identKind # param1 THEN
+    NeedIdentifier;
+    IF identKind = 0 THEN Errors.ReportErrorWithText(0, tokenBuffer) END;
+    Errors.ShowErrorPosition('B');
+    Errors.WriteKindName(param1);
+    Texts.WriteString(3, EXPECTED);
+    Errors.WriteKindName(identKind);
+    Texts.WriteString(3, " found");
+    Errors.AskEditOrQuit;
+  END;
+END ExpectIdentKind;
+
+PROCEDURE Compile;
+
+  (* Z80 proc 40 removed, replaced by a MCode one in module VarAddr *)
+  (*
+  PROCEDURE VarAddress(VAR v: WORD):ADDRESS;
+  CODE("Z80RET")
+  END VarAddress;
+  *)
+
+VAR addr: ADDRESS;
+    ptr       [006EH]: CARDINAL;
+    console   [0072H]: BOOLEAN;
+    jmpOpcode [0074H]: CARDINAL;
+    jmpAddress[0075H]: CARDINAL;
+
+(* $[- keep procedure names *)
+
+BEGIN
+  jmpOpcode := 0C3H;
+  addr      := VarAddr.ADR(scanOptions)-6;
+  addr      := ADDRESS(addr^) - 4;
+  jmpAddress:= addr + CARDINAL(addr^) + 3;
+  MARK(addr);
+  ptr       := 0;
+  console   := (ComLine.outName = "CON:");
+  listingEnabled  := FALSE;
+  tokenPos  := 0;
+  Allocate(withScopeSave, 14);
+  Compiler.InitCompiler;
+  IF sourceFile <> NIL THEN
+    Allocate(codeBuffer, 4096);
+    Loader.Call("COMPILE");
+    IF Compiler.nativeCodeRequested THEN
+      CheckStackMark(codeBuffer + CodeGen.nextEmitPos);
+      IF CodeGen.windowBase <> 0 THEN
+        MOVE(codeBuffer, codeBuffer + CodeGen.windowBase, CodeGen.nextEmitPos - CodeGen.windowBase);
+        Files.SetPos(codeFile, LONG(0));
+        IF Files.ReadBytes(codeFile, codeBuffer, CodeGen.windowBase) <> CodeGen.windowBase THEN
+          RAISE Files.EndError
+        END;
+      END;
+      Files.SetPos(codeFile, LONG(0));
+      Loader.Call("GENZ80");
+    END;
+  END;
+  Texts.CloseText(Texts.output);
+  RELEASE(addr);
+
+EXCEPTION Loader.LoadError:
+  Texts.WriteLn(3); (* console *)
+  Texts.WriteString(3,"ERROR: CANNOT LOAD OVERLAY");
+  Texts.WriteLn(3);
+  Files.Delete(codeFile);
+  RELEASE(addr)
+END Compile;
+
+(* $[+ remove procedure names *)
+END Scanner.

+ 42 - 0
src/compiler/SYMTAB.DEF

@@ -0,0 +1,42 @@
+(* Renamed for readability. Semantics unchanged. Original identifiers: see docs/compiler/ + src/compiler/RENAME-MAP.md. *)
+DEFINITION MODULE SYMTAB;
+FROM SYSTEM IMPORT ADDRESS, WORD;
+IMPORT Files;
+
+TYPE Ext = ARRAY [0..2] OF CHAR;
+TYPE Symbol = RECORD
+                name: ARRAY [0..7] OF CHAR;
+                word: CARDINAL;
+              END;
+     RecordPtr = POINTER TO Record;
+     Record = RECORD link: RecordPtr END;
+TYPE T1 = POINTER TO R1;
+     R1 = RECORD
+            link0 : T1;
+            link1 : POINTER TO ARRAY [0..128] OF CHAR;
+            link2 : T1;
+            word3 : BITSET;
+            word4 : CARDINAL;
+          END;
+
+VAR
+
+    symOptions: BITSET;
+    moduleCount: CARDINAL;
+    symFile: Files.FILE;
+    stringPoolPtr: CARDINAL;
+    moduleTable: POINTER TO ARRAY [0..99] OF Symbol;
+    scopeStack,
+    rollbackStack: POINTER TO ARRAY [0..16] OF T1;
+    currentScope: POINTER TO RECORD w0:WORD; link1: T1 END;
+
+PROCEDURE CompileDispatch;
+PROCEDURE DefineDefinitionModule;
+PROCEDURE DefineProgramModule;
+PROCEDURE InitSymbolTables;
+PROCEDURE BuildBitsetConst;
+PROCEDURE SaveSymbolFile;
+PROCEDURE RestoreAfterEdit;
+PROCEDURE SymbolAssert;
+
+END SYMTAB.