Ver código fonte

compiler: Read(Kbd, Ch) and ClrEol (CALC file args + CRT)

The ladder moves: CALC.PAS went from ERR 41 at 10927 to ERR 102 at 12083.
`Read(Kbd, Ch)' compiles, `ClrEol' is real, and the next blocker is
`NormVideo' - one of the CRT identifiers still refused.

  - KBD is a predefined KVar of class TText, flagged SymEntry.isKbd.
  - IoCall accepts a LEADING FILE argument.  When it is KBD and the call is a
    READ, the file is dropped: this runtime's read entries already read the
    console, so `Read(Kbd, Ch)' is `Read(Ch)'.  A real file variable there is a
    loud ENoLib, not a silent read into the file variable's own bytes.
  - ClrEol is a real INT 10h call: AH=03 reads the cursor, CX := it, DL := 79,
    AH=06 blanks that window.  Added to EmCrtSeq (CrtEol), so it shares the
    documented COMP exclusion.

Fixtures: t70_kbd (`Read(Kbd, Ch)', .in `Z', .out `Z'+CRLF), executed under
qemu and Exec86; t69_crt extended with ClrEol (OK 119 4, rewritten row).
60 fixtures execute under both oracles.

nonvacuity.sh: a KBD-registration case, and the CLRSCR case's expected size
updated (adding ClrEol moved t69 from OK 102 4 to OK 119 4).  90 ok, 0 failed.

run_all.sh: ALL PASS - matrix 71/71, ladder 1/1 at ERR 102 12083, 62 .COM
linked, 60 executed under both oracles; helper audit PASS.
Eric Streit 3 dias atrás
pai
commit
4c165e1bcf

+ 13 - 6
CALC-COMPILE.md

@@ -97,7 +97,7 @@ Current rung (measured today, normalised position):
 
 
 | position | blocker |
 | position | blocker |
 |---|---|
 |---|---|
-| **10927** | `Read(Kbd, Ch)` in `CALC.INC` — the CRT keyboard file as a READ argument (`ERR 41`) |
+| **12083** | `NormVideo` in `CALC.INC` — a CRT identifier still refused (`ERR 102`) |
 
 
 Rungs behind it (now passed):
 Rungs behind it (now passed):
 
 
@@ -113,12 +113,13 @@ Rungs behind it (now passed):
 | 10350 | `procedure Center(S : AnyString)` — a **string value parameter** | calling convention, steps 1-3 |
 | 10350 | `procedure Center(S : AnyString)` — a **string value parameter** | calling convention, steps 1-3 |
 | 10405 | `Length(S)` — the first string builtin | `LENGTH`, inline |
 | 10405 | `Length(S)` — the first string builtin | `LENGTH`, inline |
 | 10753 | `ClrScr` — CRT identifiers begin | `ClrScr` + `GotoXY` (real INT 10h) |
 | 10753 | `ClrScr` — CRT identifiers begin | `ClrScr` + `GotoXY` (real INT 10h) |
+| 10927 | `Read(Kbd, Ch)` — a READ with a leading file | `Kbd` + `ClrEol` |
 
 
 Rungs ahead:
 Rungs ahead:
 
 
 | position | blocker |
 | position | blocker |
 |---|---|
 |---|---|
-| 10927 | `Kbd` / `Read` with a file argument, then `KeyPressed`, `ClrEol`, `LowVideo`/`NormVideo`, `Delay`, the remaining string builtins and the rest of `CALC.INC` |
+| 12083 | `NormVideo`/`LowVideo`/`Delay`, `KeyPressed`, then the rest of `CALC.INC` (file operations, indexing, `with`, pointers, `var` params, reals, string builtins, recursion) |
 
 
 Corrections the measurements force, worth stating plainly because the stage list
 Corrections the measurements force, worth stating plainly because the stage list
 below was written before them:
 below was written before them:
@@ -475,8 +476,14 @@ Exec86):
   `INT 10h`** (cursor tracked, screen calls no-ops, everything else faults), so
   `INT 10h`** (cursor tracked, screen calls no-ops, everything else faults), so
   `t69_crt` runs under BOTH oracles; removing that facility would have left the
   `t69_crt` runs under BOTH oracles; removing that facility would have left the
   CRT group with only one execution oracle.  The rung moved 10753 → **10927**.
   CRT group with only one execution oracle.  The rung moved 10753 → **10927**.
-- **The ladder now stops at `ERR 41` at `10927`** — `Read(Kbd, Ch)`, the CRT
-  keyboard file as a READ argument.  That is the file-operation + `Kbd` work.
+- **`Read(Kbd, Ch)` and `ClrEol` are landed.**  `KBD` is a predefined console
+  TEXT file; a READ's leading file argument is dropped when it is `Kbd` (the
+  runtime's read entries already read the console) and is a loud `ENoLib` for a
+  real file variable.  `ClrEol` is a real `INT 10h` call (AH=03 reads the
+  cursor, AH=06 blanks to column 79).  Fixtures `t70_kbd` (executed with a
+  `.in`), `t69_crt` extended.  The rung moved 10927 → **12083**.
+- **The ladder now stops at `ERR 102` at `12083`** — `NormVideo` (the
+  `LowVideo`/`NormVideo`/`Delay` group), then `KeyPressed`.
 - **One surprise on resuming:** the working tree was found holding the
 - **One surprise on resuming:** the working tree was found holding the
   nonvacuity script's *frame-rule* mutation of `shell/Compiler.mod` (the
   nonvacuity script's *frame-rule* mutation of `shell/Compiler.mod` (the
   `locFree := 0FFFEH` / pre-decrement-base revert), i.e. a mutation from a
   `locFree := 0FFFEH` / pre-decrement-base revert), i.e. a mutation from a
@@ -486,8 +493,8 @@ Exec86):
   byte-for-byte the same for INTEGER locals, so the compile matrix would have
   byte-for-byte the same for INTEGER locals, so the compile matrix would have
   been green and only `t40_readln` (and later CALC) would have noticed.
   been green and only `t40_readln` (and later CALC) would have noticed.
 - **Verified baseline, run on this commit:** `./tests/run_all.sh` →
 - **Verified baseline, run on this commit:** `./tests/run_all.sh` →
-  **OVERALL: ALL PASS**, compile matrix **70/70**, CALC ladder **1/1** at
-  `ERR 41 10927`, 61 `.COM` linked and 59 executed under both qemu and Exec86
+  **OVERALL: ALL PASS**, compile matrix **71/71**, CALC ladder **1/1** at
+  `ERR 102 12083`, 62 `.COM` linked and 60 executed under both qemu and Exec86
   (`t64_arrays` is compile-only: its image is past the 8 KB boot-sector read).
   (`t64_arrays` is compile-only: its image is past the 8 KB boot-sector read).
 - **`tests/nonvacuity.sh`:** re-run after this commit's cases (the set
 - **`tests/nonvacuity.sh`:** re-run after this commit's cases (the set
   membership bit among them); see the commit message for the count. It is not in
   membership bit among them); see the commit message for the count. It is not in

+ 9 - 9
SUMMARY.md

@@ -2037,16 +2037,16 @@ census of what `CALC.PAS` actually needs and the reference measurements from
 the original `TURBO.COM`. Two tracks run in parallel: the ladder (front-end,
 the original `TURBO.COM`. Two tracks run in parallel: the ladder (front-end,
 verifiable on small fixtures) and the layout (§3, the only thing that stops
 verifiable on small fixtures) and the layout (§3, the only thing that stops
 the compiler *emitting* a 16,560-byte program). The ratchet and front-end
 the compiler *emitting* a 16,560-byte program). The ratchet and front-end
-features through **the first CRT identifiers** are landed: the ladder
+features through **`Read(Kbd, Ch)` and `ClrEol`** are landed: the ladder
 (`tests/calc_ladder.sh` + `tests/fixtures/calc_ladder.tsv`, run by `run_all.sh`;
 (`tests/calc_ladder.sh` + `tests/fixtures/calc_ladder.tsv`, run by `run_all.sh`;
-CALC.PAS now stops at `ERR 41` at pos 10927, `Read(Kbd, Ch)` inside `CALC.INC`),
-the `{$I}` include, `^X`/`#nn`, scalar typed constants, sets, enums, records
-(CellRec = 80), subranges, arrays, element/field access, file types, string
-value parameters, `Length`, and `ClrScr`/`GotoXY` (fixtures through `t69_crt`).
-The next rungs are `Kbd`/file operations, the remaining CRT identifiers, the
-remaining string builtins, variable-index subscripts + `with`, pointers, `var`
-params, reals, recursion, and the layout change. Items 1-4 below are
-independent of both.
+CALC.PAS now stops at `ERR 102` at pos 12083, `NormVideo`), the `{$I}` include,
+`^X`/`#nn`, scalar typed constants, sets, enums, records (CellRec = 80),
+subranges, arrays, element/field access, file types, string value parameters,
+`Length`, `ClrScr`/`GotoXY`/`ClrEol`, `Kbd`, and `INT 10h` in Exec86 (fixtures
+through `t70_kbd`). The next rungs are `NormVideo`/`LowVideo`/`Delay`,
+`KeyPressed`, the remaining string builtins, file operations, variable-index
+subscripts + `with`, pointers, `var` params, reals, recursion, and the layout
+change. Items 1-4 below are independent of both.
 
 
 1. The rest of the string language. `v-TP3-STRVAR`/`v-TP3-STRREAD-ASSIGN` cover
 1. The rest of the string language. `v-TP3-STRVAR`/`v-TP3-STRREAD-ASSIGN` cover
    string variables, `s := 'literal'`, `writeln (s)`, `readln (s)`/`read (s)`
    string variables, `s := 'literal'`, `writeln (s)`, `readln (s)`/`read (s)`

+ 53 - 6
shell/Compiler.mod

@@ -121,7 +121,7 @@ CONST
    BI_ClrScr  = 6 ;  BI_GotoXY = 7 ;  BI_LowVideo = 8 ;
    BI_ClrScr  = 6 ;  BI_GotoXY = 7 ;  BI_LowVideo = 8 ;
    BI_NormVideo = 9 ; BI_ClrEol = 10 ; BI_Delay = 11 ;
    BI_NormVideo = 9 ; BI_ClrEol = 10 ; BI_Delay = 11 ;
    (* the sub-codes EmCrtSeq takes *)
    (* the sub-codes EmCrtSeq takes *)
-   CrtClr = 0 ; CrtDl = 1 ; CrtDh = 2 ; CrtCaret = 3 ;
+   CrtClr = 0 ; CrtDl = 1 ; CrtDh = 2 ; CrtCaret = 3 ; CrtEol = 4 ;
 
 
    (* keyword tokens *)
    (* keyword tokens *)
    TkNone = 0 ;   TkProgram = 1 ;  TkBegin  = 2 ;  TkEnd = 3 ;
    TkNone = 0 ;   TkProgram = 1 ;  TkBegin  = 2 ;  TkEnd = 3 ;
@@ -179,6 +179,7 @@ TYPE
          arrIdx : CARDINAL ;        (* an ARRAY type's shape in ArrRec (0 = no) *)
          arrIdx : CARDINAL ;        (* an ARRAY type's shape in ArrRec (0 = no) *)
          parmFirst, parmCount : CARDINAL ;  (* a PROCEDURE/FUNCTION's parameters:
          parmFirst, parmCount : CARDINAL ;  (* a PROCEDURE/FUNCTION's parameters:
                                        symtab indices, in declaration order *)
                                        symtab indices, in declaration order *)
+         isKbd  : BOOLEAN ;          (* the predefined KBD file: reads are console *)
          level  : CARDINAL ;
          level  : CARDINAL ;
          local  : BOOLEAN ;
          local  : BOOLEAN ;
          resvar : CARDINAL ;
          resvar : CARDINAL ;
@@ -1495,6 +1496,7 @@ BEGIN
    e.arrIdx := 0 ;
    e.arrIdx := 0 ;
    e.parmFirst := 0 ;
    e.parmFirst := 0 ;
    e.parmCount := 0 ;
    e.parmCount := 0 ;
+   e.isKbd := FALSE ;
    e.level := lexnest ;
    e.level := lexnest ;
    e.local := local ;
    e.local := local ;
    e.resvar := 0 ;
    e.resvar := 0 ;
@@ -3652,6 +3654,15 @@ BEGIN
    ELSIF which = CrtDh THEN
    ELSIF which = CrtDh THEN
       Ebyte (48H) ;                                (* DEC AX    *)
       Ebyte (48H) ;                                (* DEC AX    *)
       Ebyte (88H) ; Ebyte (0C6H)                   (* MOV DH,AL *)
       Ebyte (88H) ; Ebyte (0C6H)                   (* MOV DH,AL *)
+   ELSIF which = CrtEol THEN
+      Ebyte (0B4H) ; Ebyte (03H) ;                 (* MOV AH,03h  get cursor *)
+      Ebyte (0B7H) ; Ebyte (00H) ;                 (* MOV BH,00h             *)
+      Ebyte (0CDH) ; Ebyte (10H) ;                 (* INT 10h  -> DH,DL      *)
+      Ebyte (89H) ; Ebyte (0D1H) ;                 (* MOV CX,DX  upper-left   *)
+      Ebyte (0B2H) ; Ebyte (4FH) ;                 (* MOV DL,4Fh  col 79      *)
+      Ebyte (0B8H) ; Ebyte (00H) ; Ebyte (06H) ;   (* MOV AX,0600h blank      *)
+      Ebyte (0B7H) ; Ebyte (07H) ;                 (* MOV BH,07h  attribute   *)
+      Ebyte (0CDH) ; Ebyte (10H)                  (* INT 10h                 *)
    ELSE
    ELSE
       Ebyte (0B4H) ; Ebyte (02H) ;                 (* MOV AH,02h *)
       Ebyte (0B4H) ; Ebyte (02H) ;                 (* MOV AH,02h *)
       Ebyte (0B7H) ; Ebyte (00H) ;                 (* MOV BH,00h *)
       Ebyte (0B7H) ; Ebyte (00H) ;                 (* MOV BH,00h *)
@@ -3674,6 +3685,7 @@ VAR arg : ERes ;
     nArgs, ent, which, acls : CARDINAL ;
     nArgs, ent, which, acls : CARDINAL ;
     reading : BOOLEAN ;
     reading : BOOLEAN ;
     pushed : BOOLEAN ;
     pushed : BOOLEAN ;
+    skipArg : BOOLEAN ;
     n : CARDINAL ;
     n : CARDINAL ;
     dummy : ERes ;
     dummy : ERes ;
 BEGIN
 BEGIN
@@ -3690,6 +3702,13 @@ BEGIN
       EmCrtSeq (CrtClr) ;
       EmCrtSeq (CrtClr) ;
       RETURN
       RETURN
    END ;
    END ;
+   IF which = BI_ClrEol THEN
+      (* clear from the cursor to the end of the line - a real INT 10h call,
+         not a no-op: AH=03 reads the cursor, CX := it, DL := 79, AH=06 blanks
+         that window. *)
+      EmCrtSeq (CrtEol) ;
+      RETURN
+   END ;
    IF which = BI_GotoXY THEN
    IF which = BI_GotoXY THEN
       (* GotoXY (x, y): 1-based; evaluate x first and park it, then y, so the
       (* GotoXY (x, y): 1-based; evaluate x first and park it, then y, so the
          code for y cannot clobber DL. *)
          code for y cannot clobber DL. *)
@@ -3715,7 +3734,7 @@ BEGIN
       RETURN
       RETURN
    END ;
    END ;
    IF (which = BI_LowVideo) OR (which = BI_NormVideo) OR
    IF (which = BI_LowVideo) OR (which = BI_NormVideo) OR
-      (which = BI_ClrEol) OR (which = BI_Delay) THEN
+      (which = BI_Delay) THEN
       (* Catalogued, and LOUD.  Their screen behaviour is a later question
       (* Catalogued, and LOUD.  Their screen behaviour is a later question
          (CALC-COMPILE.md Stage 14: a compiling CALC needs them to resolve, a
          (CALC-COMPILE.md Stage 14: a compiling CALC needs them to resolve, a
          RUNNING one needs them right), but a silent no-op would be exactly the
          RUNNING one needs them right), but a silent no-op would be exactly the
@@ -3757,6 +3776,26 @@ BEGIN
                the whole list first and emitting afterwards printed the first
                the whole list first and emitting afterwards printed the first
                argument as the last argument's value, and pushed an argument
                argument as the last argument's value, and pushed an argument
                that an earlier inline string's call had already overwritten. *)
                that an earlier inline string's call had already overwritten. *)
+            skipArg := FALSE ;
+            IF (nArgs = 0) AND ((arg.cls = TText) OR (arg.cls = TFile)) THEN
+               (* A leading FILE argument: `Read(Kbd, Ch)'.  KBD is the console
+                  keyboard and this runtime's read entries already read the
+                  console, so the file is dropped and the rest of the argument
+                  list is read exactly as without it.  A real file is not
+                  implemented yet - loud, rather than silently reading the file
+                  variable as a data argument. *)
+               IF reading AND (arg.kind = 1) AND symtab [arg.idx].isKbd THEN
+                  IF NOT MatchDelim (',') THEN
+                     Err (ENoSemi) ;
+                     RETURN
+                  END ;
+                  skipArg := TRUE
+               ELSE
+                  Err (ENoLib) ;
+                  RETURN
+               END
+            END ;
+            IF NOT skipArg THEN
             pushed := TRUE ;      (* default: value is on the stack -> call + pop *)
             pushed := TRUE ;      (* default: value is on the stack -> call + pop *)
             IF reading THEN
             IF reading THEN
                IF (arg.kind # 1) AND (arg.kind # 5) THEN
                IF (arg.kind # 1) AND (arg.kind # 5) THEN
@@ -3888,9 +3927,12 @@ BEGIN
                DropC (EmCall (ent)) ;
                DropC (EmCall (ent)) ;
                EmAddSp (2)                         (* one 16-bit argument *)
                EmAddSp (2)                         (* one 16-bit argument *)
             END ;
             END ;
-            INC (nArgs) ;
-            IF NOT MatchDelim (',') THEN
-               EXIT
+               INC (nArgs)
+            END ;
+            IF NOT skipArg THEN
+               IF NOT MatchDelim (',') THEN
+                  EXIT
+               END
             END
             END
          END ;
          END ;
          IF NOT MatchDelim (')') THEN
          IF NOT MatchDelim (')') THEN
@@ -5213,7 +5255,12 @@ BEGIN
    i := NewSym ("CLREOL" , KBuiltin, BI_ClrEol  , 0, 0, 0, 0, FALSE) ;
    i := NewSym ("CLREOL" , KBuiltin, BI_ClrEol  , 0, 0, 0, 0, FALSE) ;
    symtab [i].defnd := TRUE ;
    symtab [i].defnd := TRUE ;
    i := NewSym ("DELAY"  , KBuiltin, BI_Delay   , 0, 0, 0, 0, FALSE) ;
    i := NewSym ("DELAY"  , KBuiltin, BI_Delay   , 0, 0, 0, 0, FALSE) ;
-   symtab [i].defnd := TRUE
+   symtab [i].defnd := TRUE ;
+   (* KBD: the console keyboard as a TEXT file.  This runtime's read entries
+      already read the console, so `Read(Kbd, Ch)' drops the file argument and
+      reads a character exactly as `Read(Ch)' does. *)
+   i := NewSym ("KBD", KVar, TText, FileVarSize, 1, 0, 0, FALSE) ;
+   symtab [i].isKbd := TRUE
 END DefBuiltins ;
 END DefBuiltins ;
 
 
 PROCEDURE Inittur () ;
 PROCEDURE Inittur () ;

+ 5 - 6
shell/tests/fixtures/calc_ladder.tsv

@@ -25,9 +25,8 @@
 # positions shift — which is exactly why this must be a measured number, not a
 # positions shift — which is exactly why this must be a measured number, not a
 # remembered one.
 # remembered one.
 #
 #
-# Current rung (2026-10-20): `Read(Kbd, Ch)` in CALC.INC - the CRT keyboard
-# file `Kbd` as a READ argument (`ERR 41').  The rung moved from 10753 (ClrScr)
-# when the CRT group's ClrScr and GotoXY landed as real INT 10h builtins; the
-# other four CRT identifiers are registered but refused loudly until their
-# screen behaviour is implemented.
-CALC.PAS	ERR	41	10927
+# Current rung (2026-10-20): `NormVideo' in CALC.INC - one of the CRT
+# identifiers still refused with ENoLib.  The rung moved from 10927
+# (`Read(Kbd,Ch)') when the leading-file READ argument and ClrEol landed
+# (`Kbd' is the console, and ClrEol is a real INT 10h call).
+CALC.PAS	ERR	102	12083

+ 8 - 1
shell/tests/fixtures/expected.tsv

@@ -543,4 +543,11 @@ t68_length	OK	122	25
 #                   calls no-ops), so the same image must print `crt' there and
 #                   calls no-ops), so the same image must print `crt' there and
 #                   under qemu.  code=102 pins the sequences, data=4 the @@T
 #                   under qemu.  code=102 pins the sequences, data=4 the @@T
 #                   temporaries.
 #                   temporaries.
-t69_crt	OK	102	4
+t69_crt	OK	119	4
+#   t70_kbd                `Read(Kbd, Ch)' - a READ with a leading FILE
+#                   argument.  Kbd is the console keyboard; the runtime's read
+#                   entries already read the console, so the file is dropped and
+#                   the rest is `Read(Ch)'.  A real file there is a loud
+#                   ENoLib.  .in `Z', .out `Z' + CRLF.  data=5 = ch (1) + the
+#                   @@T temporaries.
+t70_kbd	OK	55	5

+ 1 - 0
shell/tests/fixtures/t69_crt.pas

@@ -25,5 +25,6 @@ begin
    ClrScr;
    ClrScr;
    GotoXY (1, 1);
    GotoXY (1, 1);
    GotoXY (40, 12);
    GotoXY (40, 12);
+   ClrEol;
    writeln ('crt')
    writeln ('crt')
 end.
 end.

+ 1 - 0
shell/tests/fixtures/t70_kbd.in

@@ -0,0 +1 @@
+Z

+ 1 - 0
shell/tests/fixtures/t70_kbd.out

@@ -0,0 +1 @@
+Z

+ 20 - 0
shell/tests/fixtures/t70_kbd.pas

@@ -0,0 +1,20 @@
+program t70_kbd;
+
+{ t70 -- Read(Kbd, Ch): a READ with a leading FILE argument.  CALC.INC is full
+  of `Read(Kbd, Ch)' (8 times) and `KeyPressed'.
+
+  `Kbd' is the predefined console keyboard, modelled as a TEXT file.  This
+  runtime's read entries already read the console, so the leading file is
+  DROPPED and the remaining arguments are read exactly as `Read(Ch)' would be.
+  A real file variable in that position is a loud ENoLib, not a silent read
+  into the file variable's own bytes.
+
+  The .in is `Z'; the .out is `Z' + CRLF.  Read (not ReadLn) leaves the newline
+  pending, and the writeln after it adds the CRLF. }
+
+var
+   ch : char;
+begin
+   Read (Kbd, ch);
+   writeln (ch)
+end.

+ 27 - 1
shell/tests/nonvacuity.sh

@@ -1712,7 +1712,7 @@ PYX
 then
 then
     if rebuild_compiler; then
     if rebuild_compiler; then
         expect_red "t69 red when CLRSCR is not registered" \
         expect_red "t69 red when CLRSCR is not registered" \
-            "expected: OK 102 4" tests/run_compile_tests.sh
+            "expected: OK 119 4" tests/run_compile_tests.sh
     else
     else
         echo "  FAIL: the compiler would not rebuild without the CLRSCR builtin"
         echo "  FAIL: the compiler would not rebuild without the CLRSCR builtin"
         fail=$((fail + 1))
         fail=$((fail + 1))
@@ -1723,6 +1723,32 @@ else
 fi
 fi
 cp "$SAVED_C" Compiler.mod
 cp "$SAVED_C" Compiler.mod
 
 
+# --- KBD is registered ----------------------------------------------------
+# Without the predefined KBD file, `Read(Kbd, Ch)' is an unknown identifier and
+# t70 stops compiling against its expected OK 55 5.
+cp "$SAVED_C" Compiler.mod
+if python3 - <<'PYX'
+p = 'Compiler.mod'
+s = open(p).read()
+old = ('   i := NewSym ("KBD", KVar, TText, FileVarSize, 1, 0, 0, FALSE) ;\n'
+       '   symtab [i].isKbd := TRUE\n')
+assert s.count(old) == 1, 'the KBD registration found %d times -- update this mutation' % s.count(old)
+open(p, 'w').write(s.replace(old, ''))
+PYX
+then
+    if rebuild_compiler; then
+        expect_red "t70 red when KBD is not registered" \
+            "expected: OK 55 5" tests/run_compile_tests.sh
+    else
+        echo "  FAIL: the compiler would not rebuild without the KBD file"
+        fail=$((fail + 1))
+    fi
+else
+    echo "  BROKEN CASE: the KBD-registration mutation did not apply"
+    fail=$((fail + 1))
+fi
+cp "$SAVED_C" Compiler.mod
+
 if rebuild_compiler; then
 if rebuild_compiler; then
     if python3 tests/run_com_exec.py >/dev/null 2>&1; then
     if python3 tests/run_com_exec.py >/dev/null 2>&1; then
         echo "  ok: every executed fixture passes on the restored sources"
         echo "  ok: every executed fixture passes on the restored sources"