Explorar o código

calc: real output and string-field comparison (ladder 19847)

Write(Value:FW:DEC) now works, and the CALC ladder moved 18267 -> 19847.

Runtime (entry 30, real_write): [BP+4]=decimals, [BP+6]=width,
[BP+8]=real.  Formats into the D_FMTBUF buffer through the already-transcribed
fmtreal core, pads left to the width, and prints the field.  Eight exact
output cases, including the scientific default.

Compiler:
  * a REAL argument pushes its ADDRESS (DI for a field), then the width and
    decimals.  TP3's defaults are width 18 and decimals FFFFh (scientific);
    a width with no second colon keeps FFFFh.  The entry is TU_RealWrite.
  * a string comparison pushes the LEFT address BEFORE parsing the right
    operand, so a kind-5 field's DI survives; SaveLeft is no longer used for
    strings.  EmPushStrAddr now accepts a DI-addressed string field instead
    of ENoLib.  This is CALC's `if Sheet[UFX,UFY].Contents <> '' then'.
  * the new fixtures: t83_strfield_cmp (field comparison) and t84_realwrite
    (variable width/decimals, default scientific), both executed.

The next CALC blocker is run-time SET ARITHMETIC at 19847:
`Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []`.

Harness: the runtime is 3479 bytes, so the probe scaffold moved from HDR
0x0F00 to 0x1100; the case record's index is now a WORD (four hex digits)
because rt_exec passed 255 cases, and the driver understands three stack
words for real_write.  Exec86 gained the FE group (INC/DEC r/m8) and
CMC/CLC/STC/LAHF/SAHF, which the formatter uses; t84 cross-checks qemu.

rt_exec 264/264; run_com_exec 74/74; run_exec86 74/74; compile matrix
85/85; ladder 1/1 at ERR 102 19847; non-vacuity 118/118; run_all ALL PASS.
Eric Streit hai 1 día
pai
achega
4902c5dfed

+ 34 - 19
CALC-COMPILE.md

@@ -549,16 +549,17 @@ 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 the code HEAD above:** `./tests/run_all.sh` →
 - **Verified baseline, run on the code HEAD above:** `./tests/run_all.sh` →
-  **OVERALL: ALL PASS**, compile matrix **83/83**, CALC ladder **1/1** at
-  `ERR 102 18267`, terminal config **4/4**, 74 `.COM` linked and **72 executed
+  **OVERALL: ALL PASS**, compile matrix **85/85**, CALC ladder **1/1** at
+  `ERR 102 19847`, terminal config **4/4**, 76 `.COM` linked and **74 executed
   under both qemu and Exec86** (`t64_arrays` remains compile-only because it
   under both qemu and Exec86** (`t64_arrays` remains compile-only because it
-  has no `.out`; its ~16 KB image now fits the raised read).  `tests/nonvacuity.sh`:
-  **115 ok, 0 failed**.  The runtime is 3381 bytes (wrintw, real_from_int,
+  has no `.out`; its ~16 KB image fits the raised read).  `tests/nonvacuity.sh`:
+  **118 ok, 0 failed**.  The runtime is 3479 bytes (wrintw, real_from_int,
   real_add, real_sub, real_mul, real_div, real_cmp, real_int, real_fmt,
   real_add, real_sub, real_mul, real_div, real_cmp, real_int, real_fmt,
-  real_val; the golden was re-blessed) and `tests/rt_exec.py` passes
-  **256/256**, including 16 add/sub, ten multiply, fourteen division, thirty
-  comparison, eighteen Int, ten format and thirty parse cases.
-  `check_realconst.py` pins the measured 6-byte Real operand bytes.
+  real_val, real_write; the golden was re-blessed) and `tests/rt_exec.py`
+  passes **264/264**, including 16 add/sub, ten multiply, fourteen division,
+  thirty comparison, eighteen Int, the MBF format/parse cases and eight
+  real-output cases.  `check_realconst.py` pins the measured 6-byte Real
+  operand bytes.
 - **`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
   the default `run_all.sh` and must be run **manually** after touching any check.
   the default `run_all.sh` and must be run **manually** after touching any check.
@@ -743,17 +744,31 @@ Then the two that are *outside* the ladder and gate the goal as stated:
             there, corrupting every digit after the first.
             there, corrupting every digit after the first.
           - `rc_fmt`'s DEC>=25 path used `SUB DL,AL` (28 C2) instead of
           - `rc_fmt`'s DEC>=25 path used `SUB DL,AL` (28 C2) instead of
             `SUB AL,DL` (2A C2).
             `SUB AL,DL` (2A C2).
-      * **NEXT:** wire `Write(Value:FW:DEC)` and `Str`/`Val` in the compiler.
-        The runtime entries are ready; the compiler still refuses a real
-        width/decimals and has no real-expression model.
-      * **HEADROOM:** the runtime is 3381 bytes and the probe's scaffold starts
-        at HDR=0x0F00 with RT_BASE=0x0190, so the ceiling is 3440 bytes - about
-        59 bytes left.  The boot read now allows a .COM up to 0x6F00, so the
-        next runtime addition should move HDR up (e.g. 0x2000) and shift the
-        blocks with it; there is room below the input at 0x7000.
-      * **THEN** the compiler side: real expressions need a value model
-        (pointers to 6-byte temporaries), real comparisons, and real function
-        results (currently refused because the result is wider than a word).
+      * **DONE: real output wired** (`v-TP3-CALC-REALWRITE`, entry 30
+        `real_write`).  `Write(Value:FW:DEC)`, `Write(Value:FW)` and the
+        default `Write(real)` push the real's ADDRESS (or DI for a field),
+        the width and the decimals, then call real_write; TP3's defaults are
+        width 18 and decimals FFFFh, which selects the scientific mode.  The
+        entry formats into D_FMTBUF, pads and prints.  `t84_realwrite` is the
+        end-to-end fixture.  The same rung makes a string FIELD addressable
+        in a comparison: `if Sheet[UFX,UFY].Contents <> '' then' pushes the
+        left address before parsing the right, so DI survives; pinned by
+        `t83_strfield_cmp`.  The ladder moved **18267 -> 19847**.
+      * **NEXT BLOCKER:** run-time SET ARITHMETIC.  CALC 19847 is
+        `until (Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []) or ...;`
+        - the set field intersected with a literal.  Constant sets, set
+        fields, assignment and membership exist; `+`, `-`, `*`, `=`, `<>`,
+        `<=`, `>=` over run-time sets are the next rung.
+      * **STILL TO COME:** `Str`/`Val` wiring, then real expressions (a 6-byte
+        temporary/value model), real comparisons and real function results.
+      * **HEADROOM:** the runtime is 3479 bytes and the probe's scaffold starts
+        at HDR=0x1100 with RT_BASE=0x0190, so the ceiling is 3952 bytes - about
+        473 bytes left.  The boot read allows a .COM up to 0x6F00, so the next
+        additions can move HDR again (e.g. 0x2000) with plenty of room below
+        the input at 0x7000.
+      * **Exec86** needed the FE group (INC/DEC r/m8) and CMC/CLC/STC/LAHF/
+        SAHF for the formatter; t84 is the first fixture that executes the
+        real conversion under the interpreter, and it cross-checks qemu.
 
 
 14. **Raise the execution harness's read — DONE (`v-TP3-MBF-CONV`).**
 14. **Raise the execution harness's read — DONE (`v-TP3-MBF-CONV`).**
     `tests/exec/bootcom.s` now reads **72 sectors (36,864 bytes)** into
     `tests/exec/bootcom.s` now reads **72 sectors (36,864 bytes)** into

+ 108 - 51
shell/Compiler.mod

@@ -366,7 +366,7 @@ VAR
    TU_StackChk : CARDINAL ;
    TU_StackChk : CARDINAL ;
    TU_WrInt    : CARDINAL ;  TU_WrIntW : CARDINAL ;
    TU_WrInt    : CARDINAL ;  TU_WrIntW : CARDINAL ;
    TU_WrChar   : CARDINAL ;  TU_WrBool : CARDINAL ;
    TU_WrChar   : CARDINAL ;  TU_WrBool : CARDINAL ;
-   TU_WrReal   : CARDINAL ;  TU_WrLn   : CARDINAL ;
+   TU_WrReal   : CARDINAL ;  TU_RealWrite : CARDINAL ;  TU_WrLn   : CARDINAL ;
    TU_RdInt    : CARDINAL ;  TU_RdChar : CARDINAL ;  TU_RdBool : CARDINAL ;
    TU_RdInt    : CARDINAL ;  TU_RdChar : CARDINAL ;  TU_RdBool : CARDINAL ;
    TU_RdLn     : CARDINAL ;  TU_Halt   : CARDINAL ;
    TU_RdLn     : CARDINAL ;  TU_Halt   : CARDINAL ;
    TU_RdByte   : CARDINAL ;  (* one-byte variable reads; see IoCall *)
    TU_RdByte   : CARDINAL ;  (* one-byte variable reads; see IoCall *)
@@ -2886,9 +2886,12 @@ PROCEDURE EmPushStrAddr (r : ERes) ;
    - it is the same rule `c := 'a'` relies on - and the caller rejects it. *)
    - it is the same rule `c := 'a'` relies on - and the caller rejects it. *)
 BEGIN
 BEGIN
    IF r.kind = 5 THEN
    IF r.kind = 5 THEN
-      (* a string element/field addressed through DI: the counted-string
-         consumers take a symbol+offset, not an address in DI *)
-      Err (ENoLib)
+      (* a string element/field computed through DI - `Sheet[X,Y].Contents'
+         and the `with' form.  DI holds the field's address, which is exactly
+         what cmpstr/strcat/strstore take on the stack, so push it.  This used
+         to be ENoLib, and it was the CALC rung after Write(Value:FW:DEC):
+         `if Sheet[UFX,UFY].Contents <> '' then'. *)
+      EmPushDi ()
    ELSIF r.kind = 1 THEN
    ELSIF r.kind = 1 THEN
       EmPushVarAddr (symtab [r.idx].local,
       EmPushVarAddr (symtab [r.idx].local,
                      (symtab [r.idx].off + r.boff) MOD 10000H)
                      (symtab [r.idx].off + r.boff) MOD 10000H)
@@ -3391,15 +3394,18 @@ BEGIN
          RETURN
          RETURN
       END ;
       END ;
       left := r ;
       left := r ;
-      SaveLeft (left) ;
-      ParseAdd (right) ;
-      IF (left.cls = TString) OR (right.cls = TString) THEN
+      IF left.cls = TString THEN
          (* A string comparison: cmpstr, the entry TPSRC9's vcompare (133)
          (* A string comparison: cmpstr, the entry TPSRC9's vcompare (133)
             reaches through TPSRC2's csteq/cstne/cstge/cstle/cstg/cstl
             reaches through TPSRC2's csteq/cstne/cstge/cstle/cstg/cstl
-            wrappers (890-925).  It is intercepted BEFORE the constant-fold
-            arm, because a string literal is never a kind-0 constant and,
-            without this branch, both operands would fall through to LoadAtom,
-            which refuses a TString (ENoLib) - the symptom this arm cures.
+            wrappers (890-925).
+
+            The operands are pushed as ADDRESSES, before the right-hand parse
+            can clobber DI.  This is why the left is pushed here rather than
+            through SaveLeft: a kind-5 left is a string FIELD whose address is
+            in DI, and SaveLeft's job is to materialise a scalar in AX, which
+            would read the length byte as a number (or, for a whole string,
+            refuse it).  `if Sheet[UFX,UFY].Contents <> '' then' was exactly
+            that: the CALC rung after Write(Value:FW:DEC).
 
 
             cmpstr returns AX = -1/0/+1 (see Runtime.mod for why a value and
             cmpstr returns AX = -1/0/+1 (see Runtime.mod for why a value and
             not flags), so the second half is exactly the numeric arm's: put
             not flags), so the second half is exactly the numeric arm's: put
@@ -3408,11 +3414,12 @@ BEGIN
             check_8086.py reads every `CMP AX,0000' in the program region as
             check_8086.py reads every `CMP AX,0000' in the program region as
             an IF/WHILE/REPEAT test and requires a branch right after it, and
             an IF/WHILE/REPEAT test and requires a branch right after it, and
             a comparison used as a VALUE (writeln(a = b)) is not one. *)
             a comparison used as a VALUE (writeln(a = b)) is not one. *)
-         IF (left.cls # TString) OR (right.cls # TString) THEN
+         EmPushStrAddr (left) ;
+         ParseAdd (right) ;
+         IF right.cls # TString THEN
             Err (ETypeErr) ;         (* a string compared with a number/char *)
             Err (ETypeErr) ;         (* a string compared with a number/char *)
             RETURN
             RETURN
          END ;
          END ;
-         EmPushStrAddr (left) ;
          EmPushStrAddr (right) ;
          EmPushStrAddr (right) ;
          DropC (EmCall (TU_CmpStr)) ;
          DropC (EmCall (TU_CmpStr)) ;
          EmAddSp (4) ;
          EmAddSp (4) ;
@@ -3420,7 +3427,14 @@ BEGIN
          EmRelSetcc (op) ;
          EmRelSetcc (op) ;
          r.kind := 2 ;
          r.kind := 2 ;
          r.cls := TBool
          r.cls := TBool
-      ELSIF (left.kind = 0) AND (right.kind = 0) THEN
+      ELSE
+         SaveLeft (left) ;
+         ParseAdd (right) ;
+         IF right.cls = TString THEN
+            Err (ETypeErr) ;         (* a number compared with a string *)
+            RETURN
+         END ;
+         IF (left.kind = 0) AND (right.kind = 0) THEN
          IF ConstCmp (op, left.imm, right.imm, f) THEN
          IF ConstCmp (op, left.imm, right.imm, f) THEN
             r.kind := 0 ;
             r.kind := 0 ;
             r.imm := f ;
             r.imm := f ;
@@ -3430,12 +3444,13 @@ BEGIN
             r.imm := 0 ;
             r.imm := 0 ;
             r.cls := TBool
             r.cls := TBool
          END
          END
-      ELSE
-         LoadPair (left, right) ;
-         EmCmpAxCx () ;
+         ELSE
+            LoadPair (left, right) ;
+            EmCmpAxCx () ;
          EmRelSetcc (op) ;
          EmRelSetcc (op) ;
          r.kind := 2 ;
          r.kind := 2 ;
          r.cls := TBool
          r.cls := TBool
+         END
       END
       END
    END
    END
 END ParseCmp ;
 END ParseCmp ;
@@ -4708,45 +4723,86 @@ BEGIN
                      END ;
                      END ;
                      ent := TU_WrStr
                      ent := TU_WrStr
                   ELSE
                   ELSE
-                     LoadAtom (arg) ;
-                     EmPushAx () ;
-                     IF arg.chr THEN
-                        ent := TU_WrChar             (* 'a' - one char, not 97 *)
-                     ELSIF acls = TReal THEN
-                        ent := TU_WrReal
-                     ELSIF acls = TBool THEN
-                        ent := TU_WrBool
-                     ELSIF acls = TChar THEN
-                        ent := TU_WrChar             (* c : char - one char *)
-                     ELSIF acls = TScalar THEN
-                        ent := TU_WrInt
-                     ELSE
-                        Err (ETypeErr) ;
-                        RETURN
-                     END ;
-                     IF MatchDelim (':') THEN
-                        (* Field width: `writeln(I:2)'.  TP3 evaluates the
-                           width (CALC passes `FW', a variable), so it is
-                           parsed and pushed AFTER the value: width at BP+4,
-                           value at BP+6.  Only an integer has a width entry
-                           yet - a CHAR/BOOLEAN/STRING width needs its own
-                           writer, and `:decimals' is the REAL form. *)
-                        IF (acls # TScalar) OR arg.chr THEN
-                           Err (ENoLib) ;
-                           RETURN
+                     IF acls = TReal THEN
+                        (* A REAL is six bytes, so the writer takes its
+                           ADDRESS, not its value: push the address first,
+                           then the width, then the decimals.  The entry is
+                           real_write, with [BP+4]=decimals, [BP+6]=width and
+                           [BP+8]=real.  TP3's defaults are width 18 and
+                           decimals FFFFh (its scientific mode); a width with
+                           no second colon keeps that default, which is
+                           exactly what TPSRC8 pwrdef/pwrdef3 push. *)
+                        IF arg.kind = 5 THEN
+                           EmPushDi ()
+                        ELSE
+                           EmPushVarAddr (symtab [arg.idx].local,
+                                          (symtab [arg.idx].off + arg.boff) MOD 10000H)
                         END ;
                         END ;
-                        ParseExpr (dummy) ;
-                        IF NOT OK () THEN
-                           RETURN
+                        IF MatchDelim (':') THEN
+                           ParseExpr (dummy) ;
+                           IF NOT OK () THEN
+                              RETURN
+                           END ;
+                           LoadAtom (dummy) ;
+                           EmPushAx () ;
+                           IF MatchDelim (':') THEN
+                              ParseExpr (dummy) ;
+                              IF NOT OK () THEN
+                                 RETURN
+                              END ;
+                              LoadAtom (dummy) ;
+                              EmPushAx ()
+                           ELSE
+                              EmMovAxi (0FFFFH) ;
+                              EmPushAx ()
+                           END
+                        ELSE
+                           EmMovAxi (18) ;
+                           EmPushAx () ;
+                           EmMovAxi (0FFFFH) ;
+                           EmPushAx ()
                         END ;
                         END ;
-                        LoadAtom (dummy) ;
+                        ent := TU_RealWrite ;
+                        argBytes := 6
+                     ELSE
+                        LoadAtom (arg) ;
                         EmPushAx () ;
                         EmPushAx () ;
-                        IF MatchDelim (':') THEN
-                           Err (ENoLib) ;
+                        IF arg.chr THEN
+                           ent := TU_WrChar             (* 'a' - one char, not 97 *)
+                        ELSIF acls = TBool THEN
+                           ent := TU_WrBool
+                        ELSIF acls = TChar THEN
+                           ent := TU_WrChar             (* c : char - one char *)
+                        ELSIF acls = TScalar THEN
+                           ent := TU_WrInt
+                        ELSE
+                           Err (ETypeErr) ;
                            RETURN
                            RETURN
                         END ;
                         END ;
-                        ent := TU_WrIntW ;
-                        argBytes := 4
+                        IF MatchDelim (':') THEN
+                           (* Field width: `writeln(I:2)'.  TP3 evaluates the
+                              width (CALC passes `FW', a variable), so it is
+                              parsed and pushed AFTER the value: width at BP+4,
+                              value at BP+6.  Only an integer has a width entry
+                              yet - a CHAR/BOOLEAN/STRING width needs its own
+                              writer, and `:decimals' is the REAL form. *)
+                           IF (acls # TScalar) OR arg.chr THEN
+                              Err (ENoLib) ;
+                              RETURN
+                           END ;
+                           ParseExpr (dummy) ;
+                           IF NOT OK () THEN
+                              RETURN
+                           END ;
+                           LoadAtom (dummy) ;
+                           EmPushAx () ;
+                           IF MatchDelim (':') THEN
+                              Err (ENoLib) ;
+                              RETURN
+                           END ;
+                           ent := TU_WrIntW ;
+                           argBytes := 4
+                        END
                      END
                      END
                   END
                   END
                END
                END
@@ -6445,6 +6501,7 @@ BEGIN
    TU_WrChar   := RT_Entry (4) ;
    TU_WrChar   := RT_Entry (4) ;
    TU_WrBool   := RT_Entry (5) ;
    TU_WrBool   := RT_Entry (5) ;
    TU_WrReal   := RT_Entry (6) ;
    TU_WrReal   := RT_Entry (6) ;
+   TU_RealWrite := RT_Entry (30) ;     (* fmtreal + padding + output *)
    TU_WrLn     := RT_Entry (7) ;
    TU_WrLn     := RT_Entry (7) ;
    TU_RdInt    := RT_Entry (8) ;
    TU_RdInt    := RT_Entry (8) ;
    TU_RdChar   := RT_Entry (9) ;
    TU_RdChar   := RT_Entry (9) ;

+ 45 - 0
shell/Exec86.mod

@@ -1124,6 +1124,30 @@ BEGIN
    ELSIF op = 99H THEN                       (* CWD: sign-extend AX into DX *)
    ELSIF op = 99H THEN                       (* CWD: sign-extend AX into DX *)
       IF AX >= 8000H THEN DX := 0FFFFH ELSE DX := 0 END
       IF AX >= 8000H THEN DX := 0FFFFH ELSE DX := 0 END
 
 
+   ELSIF (op = 0F5H) OR (op = 0F8H) OR (op = 0F9H) THEN
+      (* CMC / CLC / STC.  The real arithmetic and the decimal conversion
+         toggle the carry across multi-word shifts; bootcom's handler also
+         uses CLC/STC, though those are outside the guest's view. *)
+      IF op = 0F5H THEN CF := NOT CF
+      ELSIF op = 0F8H THEN CF := FALSE
+      ELSE CF := TRUE
+      END
+
+   ELSIF op = 09FH THEN                      (* LAHF: flags -> AH *)
+      (* PF and AF are deliberately not maintained; the runtime's LAHF/SAHF
+         pair only needs CF/ZF/SF, which are the bits it round-trips. *)
+      v := 0 ;
+      IF CF THEN v := v + 1 END ;
+      IF ZF THEN v := v + 64 END ;
+      IF SF THEN v := v + 128 END ;
+      AX := (AX DIV 256) * 256 + v
+
+   ELSIF op = 09EH THEN                      (* SAHF: AH -> flags *)
+      v := AX DIV 256 ;
+      CF := (v MOD 2) = 1 ;
+      ZF := ((v DIV 64) MOD 2) = 1 ;
+      SF := v >= 128
+
    ELSIF (op >= 0A0H) AND (op <= 0A3H) THEN
    ELSIF (op >= 0A0H) AND (op <= 0A3H) THEN
       d := Fetch16 () ;                      (* moffs: DS is 0, checked in Run86 *)
       d := Fetch16 () ;                      (* moffs: DS is 0, checked in Run86 *)
       CASE op OF
       CASE op OF
@@ -1194,6 +1218,27 @@ BEGIN
       DoModRM (w) ;
       DoModRM (w) ;
       DoShiftGroup (w, cnt)
       DoShiftGroup (w, cnt)
 
 
+   ELSIF op = 0FEH THEN
+      (* INC/DEC r/m8.  The 8-bit form of the 40-4F group; realdec uses
+         INC AL / DEC AL and the set-index path uses INC/DEC [BX].  CF is
+         preserved, as on the 8086, and the other flags come from the result. *)
+      DoModRM (0) ;
+      k := rmReg ;
+      res := RmRd (0) ;
+      cfSave := CF ;
+      IF k = 0 THEN
+         OFl := res = 7FH ;
+         res := (res + 1) MOD 256
+      ELSIF k = 1 THEN
+         OFl := res = 80H ;
+         res := (res + 255) MOD 256
+      ELSE
+         Fault ("FE group: only INC (/0) and DEC (/1) exist")
+      END ;
+      RmWr (res, 0) ;
+      SetZSF (res, 0) ;
+      CF := cfSave
+
    ELSIF (op = 0F6H) OR (op = 0F7H) THEN
    ELSIF (op = 0F6H) OR (op = 0F7H) THEN
       DoModRM (op - 0F6H) ;
       DoModRM (op - 0F6H) ;
       DoUnaryGroup (op - 0F6H)
       DoUnaryGroup (op - 0F6H)

+ 2 - 0
shell/Runtime.def

@@ -62,6 +62,8 @@ CONST
    E_RealInt = 27 ;         (* Int(real): truncate the mantissa in place *)
    E_RealInt = 27 ;         (* Int(real): truncate the mantissa in place *)
    E_RealFmt = 28 ;         (* fmtreal: a padded length-prefixed field *)
    E_RealFmt = 28 ;         (* fmtreal: a padded length-prefixed field *)
    E_RealVal = 29 ;         (* ascreal: parse an ASCIIZ string into a real *)
    E_RealVal = 29 ;         (* ascreal: parse an ASCIIZ string into a real *)
+   E_RealWrite = 30 ;       (* fmtreal + padding + console output:
+                               [BP+4]=decimals, [BP+6]=width, [BP+8]=real *)
 
 
    (* Where a .COM's first byte ends up.  DOS loads a .COM at CS:0100 (the
    (* Where a .COM's first byte ends up.  DOS loads a .COM at CS:0100 (the
       100 bytes below are the PSP), and CS = DS, so an image offset K is at
       100 bytes below are the PSP), and CS = DS, so an image offset K is at

+ 48 - 3
shell/Runtime.mod

@@ -149,7 +149,7 @@ VAR
    dataAt : CARDINAL ;
    dataAt : CARDINAL ;
    rtBase : CARDINAL ;
    rtBase : CARDINAL ;
    built  : BOOLEAN ;
    built  : BOOLEAN ;
-   entNm  : ARRAY [0..29] OF ARRAY [0..MaxNm] OF CHAR ;
+   entNm  : ARRAY [0..30] OF ARRAY [0..MaxNm] OF CHAR ;
 
 
 (* ---------------------------------------------------------------- *)
 (* ---------------------------------------------------------------- *)
 (*  name helpers                                                     *)
 (*  name helpers                                                     *)
@@ -805,6 +805,9 @@ PROCEDURE IncBxByte ; BEGIN B (0FEH) ; B (07H) END IncBxByte ;    (* INC [BX] *)
 PROCEDURE MovDiBp6 ; BEGIN B (8BH) ; B (7EH) ; B (6) END MovDiBp6 ;
 PROCEDURE MovDiBp6 ; BEGIN B (8BH) ; B (7EH) ; B (6) END MovDiBp6 ;
 PROCEDURE MovDxBp6 ; BEGIN B (8BH) ; B (56H) ; B (6) END MovDxBp6 ;
 PROCEDURE MovDxBp6 ; BEGIN B (8BH) ; B (56H) ; B (6) END MovDxBp6 ;
 PROCEDURE PushBp6 ; BEGIN B (0FFH) ; B (76H) ; B (6) END PushBp6 ;
 PROCEDURE PushBp6 ; BEGIN B (0FFH) ; B (76H) ; B (6) END PushBp6 ;
+PROCEDURE MovCxBp6 ; BEGIN B (8BH) ; B (4EH) ; B (6) END MovCxBp6 ;
+PROCEDURE MovDxBp4 ; BEGIN B (8BH) ; B (56H) ; B (4) END MovDxBp4 ;
+PROCEDURE MovDiBp8 ; BEGIN B (8BH) ; B (7EH) ; B (8) END MovDiBp8 ;
 PROCEDURE SbbBxData (delta : CARDINAL) ;
 PROCEDURE SbbBxData (delta : CARDINAL) ;
 BEGIN B (1BH) ; B (1EH) ; Dd (delta) END SbbBxData ;
 BEGIN B (1BH) ; B (1EH) ; Dd (delta) END SbbBxData ;
 PROCEDURE SbbDxData (delta : CARDINAL) ;
 PROCEDURE SbbDxData (delta : CARDINAL) ;
@@ -2733,7 +2736,48 @@ BEGIN
    JccNear (72H, "rc_adret") ;
    JccNear (72H, "rc_adret") ;
    SubClImm (30H) ;
    SubClImm (30H) ;
    M ("rc_adret") ;
    M ("rc_adret") ;
-   RetR
+   RetR ;
+
+   (* --------------------------------------------------------- real_write *)
+   (* [BP+4] = decimals, [BP+6] = width, [BP+8] = real.  Formats into the
+      internal buffer, then pads and prints: the same field real_fmt builds
+      as a string, sent straight to the console.  `Write(Value:FW:DEC)' uses
+      this; the default widths are the compiler's (18 columns, decimals
+      FFFFh, which fmtreal turns into its scientific mode). *)
+   M ("real_write") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   MovAxBp6 ; StDataAx (D_FMTW) ;     (* width *)
+   MovDxBp4 ;                         (* DX := decimals *)
+   MovCxBp6 ;                         (* CX := width *)
+   MovDiBp8 ;                         (* DI := the real *)
+   MovBxDataAddr (D_FMTBUF) ;
+   Call ("rc_fmt") ;                  (* BX := end of the formatted text *)
+   MovAxBx ;
+   MovBxDataAddr (D_FMTBUF) ;
+   SubAxBx ;                          (* AX := length *)
+   StDataAx (D_FMTLEN) ;
+   MovAxData (D_FMTW) ;
+   MovDxData (D_FMTLEN) ;
+   SubAxDx ;
+   CmpAx0 ;
+   Jge8 ("rc_wpadok") ;
+   XorAxAx ;
+   M ("rc_wpadok") ;
+   StDataAx (D_FMTPAD) ;
+   MovCxData (D_FMTPAD) ;
+   Jcxz8 ("rc_wbody") ;
+   M ("rc_wpad") ;
+   MovAlD (20H) ; MovAh (02H) ; Int21 ; Loop8 ("rc_wpad") ;
+   M ("rc_wbody") ;
+   MovSiData (D_FMTBUF) ;
+   MovCxData (D_FMTLEN) ;
+   Jcxz8 ("rc_wdone") ;
+   M ("rc_wout") ;
+   LdAlSi ; MovAh (02H) ; Int21 ; IncSi ; Loop8 ("rc_wout") ;
+   M ("rc_wdone") ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR
 END EmitRealConv ;
 END EmitRealConv ;
 
 
 
 
@@ -2972,6 +3016,7 @@ BEGIN
    SetStr (entNm [27], "real_int") ;
    SetStr (entNm [27], "real_int") ;
    SetStr (entNm [28], "real_fmt") ;
    SetStr (entNm [28], "real_fmt") ;
    SetStr (entNm [29], "real_val") ;
    SetStr (entNm [29], "real_val") ;
+   SetStr (entNm [30], "real_write") ;
    built := TRUE
    built := TRUE
 END RT_Build ;
 END RT_Build ;
 
 
@@ -2999,7 +3044,7 @@ BEGIN
    IF NOT built THEN
    IF NOT built THEN
       RT_Build (0)
       RT_Build (0)
    END ;
    END ;
-   IF i > 29 THEN
+   IF i > 30 THEN
       RETURN 0
       RETURN 0
    END ;
    END ;
    RETURN rtBase + LblOff (entNm [i])
    RETURN rtBase + LblOff (entNm [i])

+ 4 - 3
shell/tests/RtProbe.mod

@@ -108,7 +108,7 @@ END PENT ;
 
 
 PROCEDURE Main ;
 PROCEDURE Main ;
 VAR i, n, base : CARDINAL ;
 VAR i, n, base : CARDINAL ;
-    names : ARRAY [0..29] OF ARRAY [0..15] OF CHAR ;
+    names : ARRAY [0..30] OF ARRAY [0..15] OF CHAR ;
 BEGIN
 BEGIN
    base := GetBase () ;
    base := GetBase () ;
    RT_Build (base) ;
    RT_Build (base) ;
@@ -121,7 +121,7 @@ BEGIN
    PCARD (RT_Size ()) ; PS (" bytes") ; NL ;
    PCARD (RT_Size ()) ; PS (" bytes") ; NL ;
    PS ("code ends at ") ; PCARD (RT_CodeEnd ()) ; NL ;
    PS ("code ends at ") ; PCARD (RT_CodeEnd ()) ; NL ;
    i := 0 ;
    i := 0 ;
-   WHILE i <= 29 DO
+   WHILE i <= 30 DO
       names [i] := "" ;
       names [i] := "" ;
       INC (i)
       INC (i)
    END ;
    END ;
@@ -142,8 +142,9 @@ BEGIN
    names [27] := "real_int" ;
    names [27] := "real_int" ;
    names [28] := "real_fmt" ;
    names [28] := "real_fmt" ;
    names [29] := "real_val" ;
    names [29] := "real_val" ;
+   names [30] := "real_write" ;
    i := 0 ;
    i := 0 ;
-   WHILE i <= 29 DO
+   WHILE i <= 30 DO
       PENT (i, names [i]) ;
       PENT (i, names [i]) ;
       INC (i)
       INC (i)
    END ;
    END ;

+ 7 - 1
shell/tests/check_8086.py

@@ -181,7 +181,13 @@ def probe_runtime():
 # sweeps clean, so the whole thing is code") is simply false for them.  They are
 # sweeps clean, so the whole thing is code") is simply false for them.  They are
 # listed rather than detected, and the runtime sweep (the backbone) plus the
 # listed rather than detected, and the runtime sweep (the backbone) plus the
 # execution oracles still cover the emitted instructions.
 # execution oracles still cover the emitted instructions.
-CODE_RESIDENT_CONST = {"t61_setconst", "t64_arrays"}
+CODE_RESIDENT_CONST = {"t61_setconst", "t64_arrays",
+                       "t49_strcmp_lit", "t83_strfield_cmp"}
+# The last two emit string literals INLINE in the code stream
+# (`JMP over <len><chars>`): the bytes after the jump are data, and a linear
+# sweep that does not follow the jump decodes them as instructions.  The
+# execution tests still run them and check their output; clause F's instruction
+# sweep cannot meaningfully cover a region that contains arbitrary characters.
 
 
 
 
 def sweep(code, base=0, wrtinl=None):
 def sweep(code, base=0, wrtinl=None):

+ 1 - 0
shell/tests/check_runtime.py

@@ -141,6 +141,7 @@ GOLDEN = {
     "real_int": "55 8B EC 53 52 56 57",
     "real_int": "55 8B EC 53 52 56 57",
     "real_fmt": "55 8B EC 53 52 56 57",
     "real_fmt": "55 8B EC 53 52 56 57",
     "real_val": "55 8B EC 53 52 56 57",
     "real_val": "55 8B EC 53 52 56 57",
+    "real_write": "55 8B EC 53 52 56 57",
     # wrchar: AL, not DL.  DOS INT 21h AH=02h takes the character in AL, and
     # wrchar: AL, not DL.  DOS INT 21h AH=02h takes the character in AL, and
     # DL is the *other* 8086 convention (BIOS teletype).  This read DL, so
     # DL is the *other* 8086 convention (BIOS teletype).  This read DL, so
     # every write of a character printed whatever the *last* character read
     # every write of a character printed whatever the *last* character read

+ 77 - 60
shell/tests/exec/rtdrv.s

@@ -77,8 +77,8 @@
 	.set	OFF_DUMPADR, 2	# word  image offset to report them from
 	.set	OFF_DUMPADR, 2	# word  image offset to report them from
 	.set	OFF_INLEN, 4	# byte  bytes of input to hand the shim
 	.set	OFF_INLEN, 4	# byte  bytes of input to hand the shim
 	.set	OFF_INBUF, 5	# byte  inbuf[8]
 	.set	OFF_INBUF, 5	# byte  inbuf[8]
-	.set	OFF_CASE, 13	# byte  the index printed in the header
-	.set	OFF_NCALLS, 14	# word  how many call slots follow
+	.set	OFF_CASE, 13	# word  the index printed in the header (4 hex digits)
+	.set	OFF_NCALLS, 15	# byte  how many call slots follow
 	.set	SLOT0, 16		#      call slots, 12 bytes each:
 	.set	SLOT0, 16		#      call slots, 12 bytes each:
 					#      +0 word entry, +2 word arg A,
 					#      +0 word entry, +2 word arg A,
 					#      +4 byte flags, +5 pad,
 					#      +4 byte flags, +5 pad,
@@ -92,65 +92,65 @@
 # silently run into the test scaffolding.  rt_exec.py asserts
 # silently run into the test scaffolding.  rt_exec.py asserts
 # RT_BASE + blob size <= HDR, so the bound is a checked one.
 # RT_BASE + blob size <= HDR, so the bound is a checked one.
 	.set	RT_BASE, 0x0190
 	.set	RT_BASE, 0x0190
-	.set	HDR, 0x0F00
-	.set	FMTDEST, 0x1230	# 64-byte destination for real_fmt
-	.set	ASCSRC, 0x1270	# ASCIIZ sources for real_val
-	.set	DATA, 0x0F10
+	.set	HDR, 0x1100
+	.set	FMTDEST, 0x1430	# 64-byte destination for real_fmt
+	.set	ASCSRC, 0x1470	# ASCIIZ sources for real_val
+	.set	DATA, 0x1110
 	.set	DLEN, 32			# bytes of data area for initmem to clear
 	.set	DLEN, 32			# bytes of data area for initmem to clear
-	.set	STORE, 0x0F40		# where a read entry's result is stored
-	.set	WSTR, 0x0F50		# a string for the wrstr case: length + chars
-	.set	WSTR_EMPTY, 0x0F58	# the same, with a length of 0
-	.set	CASE, 0x0F60		# the case record
-	.set	D_DUMPLEN, 0x0FA0	# the driver's own three words
-	.set	D_DUMPADR, 0x0FA2
-	.set	D_CNT, 0x0FA4
-	.set	D_AX, 0x0FA6		# last call's AX, for the comparison cases
-	.set	RDSTR_DST, 0x0FB0	# destination for the rdstr cases
-	.set	SST_HELLO, 0x0FC0	# a short source string for strstore
-	.set	SST_LONG, 0x0FD0	# a source longer than a small max
-	.set	SST_EMPTY, 0x0FE0	# an empty source string
-	.set	SST_DST, 0x0FF0		# destination for the strstore cases
-	.set	SST_CAT, 0x1000		# destination for the strcat cases, with a
+	.set	STORE, 0x1140		# where a read entry's result is stored
+	.set	WSTR, 0x1150		# a string for the wrstr case: length + chars
+	.set	WSTR_EMPTY, 0x1158	# the same, with a length of 0
+	.set	CASE, 0x1160		# the case record
+	.set	D_DUMPLEN, 0x11A0	# the driver's own three words
+	.set	D_DUMPADR, 0x11A2
+	.set	D_CNT, 0x11A4
+	.set	D_AX, 0x11A6		# last call's AX, for the comparison cases
+	.set	RDSTR_DST, 0x11B0	# destination for the rdstr cases
+	.set	SST_HELLO, 0x11C0	# a short source string for strstore
+	.set	SST_LONG, 0x11D0	# a source longer than a small max
+	.set	SST_EMPTY, 0x11E0	# an empty source string
+	.set	SST_DST, 0x11F0		# destination for the strstore cases
+	.set	SST_CAT, 0x1200		# destination for the strcat cases, with a
 					# length to append to -- unlike SST_DST,
 					# length to append to -- unlike SST_DST,
 					# which strstore overwrites
 					# which strstore overwrites
-	.set	SST_CAT0, 0x1010	# ...and one that starts empty
-	.set	SST_WORLD, 0x1020	# a source distinct from SST_HELLO
-	.set	R_BLK1, 0x1030
-	.set	R_BLK2, 0x1040
-	.set	R_BLK3, 0x1050
-	.set	R_BLK4, 0x1060
-	.set	R_BLK5, 0x1070
-	.set	R_BLK6, 0x1080
-	.set	R_BLK7, 0x1090
-	.set	R_BLK8, 0x10A0
-	.set	R_BLK9, 0x10B0
-	.set	R_BLK10, 0x10C0
-	.set	R_BLK11, 0x10D0
-	.set	R_BLK12, 0x10E0
-	.set	R_BLK13, 0x10F0
-	.set	R_BLK14, 0x1100
-	.set	R_BLK15, 0x1110
-	.set	R_BLK16, 0x1120
-	.set	R_BLK17, 0x1130
-	.set	R_BLK18, 0x1140
-	.set	R_BLK19, 0x1150
-	.set	R_BLK20, 0x1160
-	.set	R_BLK21, 0x1170
-	.set	R_BLK22, 0x1180
-	.set	R_BLK23, 0x1190
-	.set	R_BLK24, 0x11A0
-	.set	R_BLK25, 0x11B0
-	.set	R_BLK26, 0x11C0
-	.set	R_BLK27, 0x11D0
-	.set	R_BLK28, 0x11E0
-	.set	R_BLK29, 0x11F0
-	.set	R_BLK30, 0x1200
-	.set	R_BLK31, 0x1210
-	.set	R_BLK32, 0x1300
-	.set	R_BLK33, 0x1310
-	.set	R_BLK34, 0x1320
-	.set	R_BLK35, 0x1330
-	.set	R_BLK36, 0x1340
+	.set	SST_CAT0, 0x1210	# ...and one that starts empty
+	.set	SST_WORLD, 0x1220	# a source distinct from SST_HELLO
+	.set	R_BLK1, 0x1230
+	.set	R_BLK2, 0x1240
+	.set	R_BLK3, 0x1250
+	.set	R_BLK4, 0x1260
+	.set	R_BLK5, 0x1270
+	.set	R_BLK6, 0x1280
+	.set	R_BLK7, 0x1290
+	.set	R_BLK8, 0x12A0
+	.set	R_BLK9, 0x12B0
+	.set	R_BLK10, 0x12C0
+	.set	R_BLK11, 0x12D0
+	.set	R_BLK12, 0x12E0
+	.set	R_BLK13, 0x12F0
+	.set	R_BLK14, 0x1300
+	.set	R_BLK15, 0x1310
+	.set	R_BLK16, 0x1320
+	.set	R_BLK17, 0x1330
+	.set	R_BLK18, 0x1340
+	.set	R_BLK19, 0x1350
+	.set	R_BLK20, 0x1360
+	.set	R_BLK21, 0x1370
+	.set	R_BLK22, 0x1380
+	.set	R_BLK23, 0x1390
+	.set	R_BLK24, 0x13A0
+	.set	R_BLK25, 0x13B0
+	.set	R_BLK26, 0x13C0
+	.set	R_BLK27, 0x13D0
+	.set	R_BLK28, 0x13E0
+	.set	R_BLK29, 0x13F0
+	.set	R_BLK30, 0x1400
+	.set	R_BLK31, 0x1410
+	.set	R_BLK32, 0x1500
+	.set	R_BLK33, 0x1510
+	.set	R_BLK34, 0x1520
+	.set	R_BLK35, 0x1530
+	.set	R_BLK36, 0x1540
 
 
 	.code16
 	.code16
 	.text
 	.text
@@ -192,7 +192,11 @@ drive:
 	# 2. Open the record: SOH, the case index as two hex digits, STX.
 	# 2. Open the record: SOH, the case index as two hex digits, STX.
 	movb	$SOH, %al
 	movb	$SOH, %al
 	call	putc
 	call	putc
-	movb	OFF_CASE(%bp), %al
+	movw	OFF_CASE(%bp), %ax
+	movw	%ax, %bx
+	movb	%bh, %al
+	call	hexb
+	movb	%bl, %al
 	call	hexb
 	call	hexb
 	movb	$STX, %al
 	movb	$STX, %al
 	call	putc
 	call	putc
@@ -216,7 +220,8 @@ drive:
 	movw	%ax, D_DUMPLEN + LOAD_BIAS
 	movw	%ax, D_DUMPLEN + LOAD_BIAS
 	movw	OFF_DUMPADR(%bp), %ax
 	movw	OFF_DUMPADR(%bp), %ax
 	movw	%ax, D_DUMPADR + LOAD_BIAS
 	movw	%ax, D_DUMPADR + LOAD_BIAS
-	movw	OFF_NCALLS(%bp), %ax
+	xorw	%ax, %ax
+	movb	OFF_NCALLS(%bp), %al
 	movw	%ax, D_CNT + LOAD_BIAS
 	movw	%ax, D_CNT + LOAD_BIAS
 	addw	$SLOT0, %bp		# BP now points AT the first call slot
 	addw	$SLOT0, %bp		# BP now points AT the first call slot
 .Lcall:
 .Lcall:
@@ -269,6 +274,8 @@ drive:
 # The flags are re-read after the call because the entry has just used AX.
 # The flags are re-read after the call because the entry has just used AX.
 doslot:
 doslot:
 	movb	4(%bp), %al
 	movb	4(%bp), %al
+	testb	$32, %al
+	jnz	.Lthreestack
 	testb	$8, %al
 	testb	$8, %al
 	jnz	.Ltwostack
 	jnz	.Ltwostack
 	testb	$1, %al
 	testb	$1, %al
@@ -278,6 +285,11 @@ doslot:
 .Ltwostack:
 .Ltwostack:
 	pushw	6(%bp)			# arg B: the source, ends at [BP+6]
 	pushw	6(%bp)			# arg B: the source, ends at [BP+6]
 	pushw	2(%bp)			# arg A: the destination, ends at [BP+4]
 	pushw	2(%bp)			# arg A: the destination, ends at [BP+4]
+	jmp	.Lnostack
+.Lthreestack:
+	pushw	8(%bp)			# [BP+8]
+	pushw	6(%bp)			# [BP+6]
+	pushw	2(%bp)			# [BP+4]
 .Lnostack:
 .Lnostack:
 	testb	$2, %al
 	testb	$2, %al
 	jz	.Lnoax
 	jz	.Lnoax
@@ -294,6 +306,8 @@ doslot:
 	call	*%si
 	call	*%si
 	movw	%ax, D_AX + LOAD_BIAS	# observable return value
 	movw	%ax, D_AX + LOAD_BIAS	# observable return value
 	movb	4(%bp), %al
 	movb	4(%bp), %al
+	testb	$32, %al
+	jnz	.Lcleanthree
 	testb	$8, %al
 	testb	$8, %al
 	jnz	.Lcleantwo
 	jnz	.Lcleantwo
 	testb	$1, %al
 	testb	$1, %al
@@ -304,6 +318,9 @@ doslot:
 .Lcleantwo:
 .Lcleantwo:
 	addw	$4, %sp
 	addw	$4, %sp
 	ret
 	ret
+.Lcleanthree:
+	addw	$6, %sp
+	ret
 
 
 # wrtinl's argument is NOT a stack word: the entry reads a length byte and that
 # wrtinl's argument is NOT a stack word: the entry reads a length byte and that
 # many characters from its own return address.  That is the caller's half of
 # many characters from its own return address.  That is the caller's half of

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

@@ -28,6 +28,9 @@
 # Current rung (2026-10-21): `Write(Value:FW:DEC)' in CALC.INC's GotoCell - a
 # Current rung (2026-10-21): `Write(Value:FW:DEC)' in CALC.INC's GotoCell - a
 # REAL write with a field width and decimals.  UpCase (t81_upcase), run-time
 # REAL write with a field width and decimals.  UpCase (t81_upcase), run-time
 # set membership (t82_setin) and a string field written through DI (t79
 # set membership (t82_setin) and a string field written through DI (t79
-# extended) moved the ladder 15911 -> 18267.  The blocker is now REAL
-# OUTPUT/ARITHMETIC: formatting a 6-byte MBF value with FW:DEC.
-CALC.PAS	ERR	102	18267
+# extended) moved the ladder 15911 -> 18267 -> 19847.  REAL output now works
+# (`Write(Value:FW:DEC)`, `Write(Value:FW)` and the default width), and a
+# string field may be compared before the right operand is parsed.  The blocker
+# is now run-time SET ARITHMETIC:
+#   until (Sheet[FX,FY].CellStatus * [OverWritten,Locked] = []) or ...;
+CALC.PAS	ERR	102	19847

+ 5 - 0
shell/tests/fixtures/expected.tsv

@@ -611,3 +611,8 @@ t81_upcase	OK	273	6
 #                   literal, a range literal, a typed constant and a data
 #                   literal, a range literal, a typed constant and a data
 #                   set (a record field); the byte/bit index is computed.
 #                   set (a record field); the byte/bit index is computed.
 t82_setin	OK	1052	26
 t82_setin	OK	1052	26
+
+#   t83/t84 are the real-output rung: a string field compared through DI,
+#   and Write(real:width:decimals) with both formats driven by variables.
+t83_strfield_cmp	OK	159	29
+t84_realwrite	OK	160	15

+ 2 - 0
shell/tests/fixtures/t83_strfield_cmp.out

@@ -0,0 +1,2 @@
+no
+empty

+ 17 - 0
shell/tests/fixtures/t83_strfield_cmp.pas

@@ -0,0 +1,17 @@
+PROGRAM T83;
+TYPE
+   Rec = RECORD
+      Contents : STRING [10] ;
+   END ;
+VAR
+   A : ARRAY [1..2] OF Rec ;
+   I : INTEGER ;
+BEGIN
+   I := 1 ;
+   IF A [I].Contents <> '' THEN
+      WRITELN ('yes')
+   ELSE
+      WRITELN ('no') ;
+   IF A [2].Contents = '' THEN
+      WRITELN ('empty')
+END.

+ 3 - 0
shell/tests/fixtures/t84_realwrite.out

@@ -0,0 +1,3 @@
+     1234.00
+ 1234.00
+1.23E+03

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

@@ -0,0 +1,20 @@
+PROGRAM T84;
+TYPE
+   RRec = RECORD
+      R : Real ;
+   END ;
+VAR
+   X : RRec ;
+   W, D : Integer ;
+BEGIN
+   X.R := 1234 ;
+   W := 12 ; D := 2 ;
+   Write (X.R:W:D) ;
+   WriteLn ;
+   W := 8 ;
+   Write (X.R:W:D) ;
+   WriteLn ;
+   W := 8 ;
+   Write (X.R:W) ;
+   WriteLn
+END.

+ 58 - 0
shell/tests/nonvacuity.sh

@@ -2246,6 +2246,64 @@ else
 fi
 fi
 cp "$SAVED_C" Compiler.mod
 cp "$SAVED_C" Compiler.mod
 
 
+# --- real output goes through real_write, not the placeholder ----------
+# TU_WrReal is still the "?REAL?" marker; the compiler's new real arm must
+# select TU_RealWrite.  t84 prints a real field with variable width/decimals.
+cp "$SAVED_C" Compiler.mod
+if mutate_compiler 's|                        ent := TU_RealWrite ;|                        ent := TU_WrReal ;|'; then
+    if rebuild_compiler; then
+        expect_red "t84 red when real output falls back to the placeholder" \
+            "t84_realwrite" python3 tests/run_com_exec.py t84_realwrite
+    else
+        echo "  FAIL: the compiler would not rebuild with the placeholder"
+        fail=$((fail + 1))
+    fi
+fi
+cp "$SAVED_C" Compiler.mod
+
+# --- a DI-addressed string field is an address, not a scalar ------------
+# `if A[I].Contents <> '' then' must push DI; the old EmPushStrAddr refused
+# kind 5 (ENoLib) and SaveLeft would try to load the whole string.
+cp "$SAVED_C" Compiler.mod
+if python3 - <<'PYX'
+p = 'Compiler.mod'
+s = open(p).read()
+old = """      EmPushDi ()
+   ELSIF r.kind = 1 THEN"""
+assert s.count(old) == 1, 'the kind-5 string push found %d times' % s.count(old)
+open(p, 'w').write(s.replace(old, """      Err (ENoLib)
+   ELSIF r.kind = 1 THEN"""))
+PYX
+then
+    if rebuild_compiler; then
+        expect_red "t83 red when a string field address is refused" \
+            "t83_strfield_cmp" python3 tests/run_com_exec.py t83_strfield_cmp
+    else
+        echo "  FAIL: the compiler would not rebuild without the kind-5 push"
+        fail=$((fail + 1))
+    fi
+else
+    echo "  BROKEN CASE: the kind-5 string-field mutation did not apply"
+    fail=$((fail + 1))
+fi
+cp "$SAVED_C" Compiler.mod
+
+# --- real_write pads from the WIDTH, not from the length ----------------
+# The pad is width - length; using the length for both makes it zero, so every
+# right-justified field loses its leading spaces.  rt_exec checks exact output.
+cp "$SAVED" Runtime.mod
+if mutate Runtime.mod 's|   MovAxData (D_FMTW) ;|   MovAxData (D_FMTLEN) ;|'; then
+    if "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1; then
+        expect_red "real_write red when the pad uses the length" \
+            "real_write" python3 tests/rt_exec.py
+    else
+        echo "  FAIL: the runtime would not rebuild with the pad mutation"
+        fail=$((fail + 1))
+    fi
+fi
+cp "$SAVED" Runtime.mod
+"$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
+
 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"

+ 66 - 44
shell/tests/rt_exec.py

@@ -63,25 +63,25 @@ SHELL = os.path.dirname(HERE)
 # two are written down and then CHECKED against each other (see build_image).
 # two are written down and then CHECKED against each other (see build_image).
 # A restated constant that is never compared is a constant waiting to lie.
 # A restated constant that is never compared is a constant waiting to lie.
 RT_BASE = 0x0190
 RT_BASE = 0x0190
-HDR = 0x0F00
+HDR = 0x1100
 DLEN = 32
 DLEN = 32
-DATA = 0x0F10
-STORE = 0x0F40
-WSTR = 0x0F50
-WSTR_EMPTY = 0x0F58
-CASE = 0x0F60
+DATA = 0x1110
+STORE = 0x1140
+WSTR = 0x1150
+WSTR_EMPTY = 0x1158
+CASE = 0x1160
 CASE_SZ = 64
 CASE_SZ = 64
-RDSTR_DST = 0x0FB0
-SST_HELLO = 0x0FC0
-SST_LONG = 0x0FD0
-SST_EMPTY = 0x0FE0
-SST_DST = 0x0FF0
-SST_CAT = 0x1000
-SST_CAT0 = 0x1010
-SST_WORLD = 0x1020
-AXOUT = 0x0FA6          # the driver stores the last call's AX here
-FMTDEST = 0x1230        # a 64-byte string for the real_fmt cases
-ASCSRC = 0x1270          # the ASCIIZ strings for real_val
+RDSTR_DST = 0x11B0
+SST_HELLO = 0x11C0
+SST_LONG = 0x11D0
+SST_EMPTY = 0x11E0
+SST_DST = 0x11F0
+SST_CAT = 0x1200
+SST_CAT0 = 0x1210
+SST_WORLD = 0x1220
+AXOUT = 0x11A6          # the driver stores the last call's AX here
+FMTDEST = 0x1430        # a 64-byte string for the real_fmt cases
+ASCSRC = 0x1470          # the ASCIIZ strings for real_val
 REALDST = 0x0FA8
 REALDST = 0x0FA8
 
 
 # A .COM is loaded at CS:0100, and every address in the image is an image
 # A .COM is loaded at CS:0100, and every address in the image is an image
@@ -109,7 +109,8 @@ ENTS = ["initmem", "progend", "stackchk", "wrint", "wrchar", "wrbool",
         "wrreal", "wrln", "rdint", "rdchar", "rdbool", "rdln", "halt",
         "wrreal", "wrln", "rdint", "rdchar", "rdbool", "rdln", "halt",
         "wrtinl", "rdbyte", "wrstr", "rdstr", "strstore", "cmpstr", "strcat",
         "wrtinl", "rdbyte", "wrstr", "rdstr", "strstore", "cmpstr", "strcat",
         "wrintw", "real_from_int", "real_add", "real_sub", "real_mul",
         "wrintw", "real_from_int", "real_add", "real_sub", "real_mul",
-        "real_div", "real_cmp", "real_int", "real_fmt", "real_val"]
+        "real_div", "real_cmp", "real_int", "real_fmt", "real_val",
+        "real_write"]
 
 
 # A call slot's flags, as rtdrv.s reads them.
 # A call slot's flags, as rtdrv.s reads them.
 F_STACK = 1     # the argument goes on the stack, popped by the caller
 F_STACK = 1     # the argument goes on the stack, popped by the caller
@@ -119,6 +120,8 @@ F_STACK2 = 8    # two stack words: +6 pushed first (lands at [BP+6]), +2 second
                 # (at [BP+4]) -- strstore's dest/src order
                 # (at [BP+4]) -- strstore's dest/src order
 F_CX = 16       # CX := the word at +8, the maximum length rdstr and strstore
 F_CX = 16       # CX := the word at +8, the maximum length rdstr and strstore
                 # both read from the register
                 # both read from the register
+F_STACK3 = 32   # three stack words: +8 first, then +6, then +2, so +2 is
+                # [BP+4], +6 is [BP+6] and +8 is [BP+8]
 
 
 # The byte patterns check_bp_contract looks for, and the rule they express:
 # The byte patterns check_bp_contract looks for, and the rule they express:
 # "an entry that borrows BP opens with PUSH BP; MOV BP,SP and pops it again
 # "an entry that borrows BP opens with PUSH BP; MOV BP,SP and pops it again
@@ -315,12 +318,12 @@ def case_bytes(c, entries):
     struct.pack_into("<H", b, 2, c["dumpadr"])          # OFF_DUMPADR
     struct.pack_into("<H", b, 2, c["dumpadr"])          # OFF_DUMPADR
     b[4] = len(c["in"])                                 # OFF_INLEN
     b[4] = len(c["in"])                                 # OFF_INLEN
     b[5:5 + len(c["in"])] = c["in"]                     # OFF_INBUF
     b[5:5 + len(c["in"])] = c["in"]                     # OFF_INBUF
-    b[13] = c["index"]                                  # OFF_CASE
+    struct.pack_into("<H", b, 13, c["index"])           # OFF_CASE (word)
     calls = c["calls"]
     calls = c["calls"]
     if len(calls) > 4:
     if len(calls) > 4:
         sys.exit("case %s has %d calls; the record holds 4"
         sys.exit("case %s has %d calls; the record holds 4"
                  % (c["name"], len(calls)))
                  % (c["name"], len(calls)))
-    struct.pack_into("<H", b, 14, len(calls))           # OFF_NCALLS
+    b[15] = len(calls)                                  # OFF_NCALLS (byte)
     for i, (ent, arg, mode, arg2, cx) in enumerate(calls):
     for i, (ent, arg, mode, arg2, cx) in enumerate(calls):
         o = 16 + 12 * i                                 # SLOT0, SLOT_SZ = 12
         o = 16 + 12 * i                                 # SLOT0, SLOT_SZ = 12
         struct.pack_into("<H", b, o, entries[ent])
         struct.pack_into("<H", b, o, entries[ent])
@@ -387,20 +390,20 @@ def parse_record(raw, want_index):
     """
     """
     if not raw or raw[0] != SOH:
     if not raw or raw[0] != SOH:
         raise AssertionError("no record header: the driver printed %r" % raw[:64])
         raise AssertionError("no record header: the driver printed %r" % raw[:64])
-    if len(raw) < 5 or raw[3] != STX:
+    if len(raw) < 7 or raw[5] != STX:
         raise AssertionError("malformed record header %r" % raw[:16])
         raise AssertionError("malformed record header %r" % raw[:16])
-    idx = int(raw[1:3], 16)
+    idx = int(raw[1:5], 16)
     if idx != want_index:
     if idx != want_index:
-        raise AssertionError("record is for case %02X, expected %02X"
+        raise AssertionError("record is for case %04X, expected %04X"
                              % (idx, want_index))
                              % (idx, want_index))
     if raw[-1] != EOT:
     if raw[-1] != EOT:
         raise AssertionError("record was never closed (EOT): the last call did "
         raise AssertionError("record was never closed (EOT): the last call did "
                              "not return, or the machine halted early")
                              "not return, or the machine halted early")
-    end = raw.index(ETX, 4)
+    end = raw.index(ETX, 6)
     if end < 0:
     if end < 0:
         raise AssertionError("record has no ETX: the calls printed nothing and "
         raise AssertionError("record has no ETX: the calls printed nothing and "
                              "the driver never got past them")
                              "the driver never got past them")
-    printed, dump = raw[4:end], raw[end + 1:-1]
+    printed, dump = raw[6:end], raw[end + 1:-1]
     if len(dump) % 2 or not re.fullmatch(b"[0-9A-F]*", dump):
     if len(dump) % 2 or not re.fullmatch(b"[0-9A-F]*", dump):
         raise AssertionError("the memory report %r is not hex" % dump)
         raise AssertionError("the memory report %r is not hex" % dump)
     return idx, printed, bytes.fromhex(dump.decode())
     return idx, printed, bytes.fromhex(dump.decode())
@@ -448,6 +451,11 @@ def build_cases():
         second (at [BP+4]).  real_from_int(src, dst) takes its source first."""
         second (at [BP+4]).  real_from_int(src, dst) takes its source first."""
         return (ent, arg, F_STACK | F_STACK2, arg2, 0)
         return (ent, arg, F_STACK | F_STACK2, arg2, 0)
 
 
+    def s3(ent, a, b, c):
+        """Three stack words: c is pushed first (landing at [BP+8]), then b
+        ([BP+6]), then a ([BP+4])."""
+        return (ent, a, F_STACK | F_STACK3, b, c)
+
     def s2cx(ent, arg, arg2, cx):
     def s2cx(ent, arg, arg2, cx):
         """Two stack words AND CX: arg2 is pushed first (it lands at [BP+6]),
         """Two stack words AND CX: arg2 is pushed first (it lands at [BP+6]),
         arg second (at [BP+4]), and CX carries the maximum length.  strstore is
         arg second (at [BP+4]), and CX carries the maximum length.  strstore is
@@ -524,7 +532,7 @@ def build_cases():
               (6, 2), (10, 2), (15, 3), (1000, 2), (-6, 2), (6, -2),
               (6, 2), (10, 2), (15, 3), (1000, 2), (-6, 2), (6, -2),
               (-6, -2), (0, 5), (5, 2), (42, 6)]
               (-6, -2), (0, 5), (5, 2), (42, 6)]
     for bi, (a, b) in enumerate(BLOCKS):
     for bi, (a, b) in enumerate(BLOCKS):
-        blk = 0x1030 + 16 * bi
+        blk = 0x1230 + 16 * bi
         add("real_add(%d,%d)" % (a, b),
         add("real_add(%d,%d)" % (a, b),
             [s("real_add", blk + LOAD_BIAS)],
             [s("real_add", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
             dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
@@ -557,7 +565,7 @@ def build_cases():
         if (a, b) in seen:
         if (a, b) in seen:
             continue
             continue
         seen.add((a, b))
         seen.add((a, b))
-        blk = 0x1030 + 16 * bi
+        blk = 0x1230 + 16 * bi
         want = (a > b) - (a < b)
         want = (a > b) - (a < b)
         add("real_cmp(%d,%d)" % (a, b),
         add("real_cmp(%d,%d)" % (a, b),
             [s("real_cmp", blk + LOAD_BIAS)],
             [s("real_cmp", blk + LOAD_BIAS)],
@@ -569,14 +577,14 @@ def build_cases():
             (-0.5, 0.5), (12345.75, 12345.5), (2.5, 0.0), (-2.5, 0.0),
             (-0.5, 0.5), (12345.75, 12345.5), (2.5, 0.0), (-2.5, 0.0),
             (0.99, 0.0), (1.9, 0.0)]
             (0.99, 0.0), (1.9, 0.0)]
     for fi, (a, b) in enumerate(FRAC):
     for fi, (a, b) in enumerate(FRAC):
-        blk = 0x1170 + 16 * fi
+        blk = 0x1370 + 16 * fi
         want = (a > b) - (a < b)
         want = (a > b) - (a < b)
         add("real_cmp(%g,%g)" % (a, b),
         add("real_cmp(%g,%g)" % (a, b),
             [s("real_cmp", blk + LOAD_BIAS)],
             [s("real_cmp", blk + LOAD_BIAS)],
             dump=2, dumpbytes=struct.pack("<H", want & 0xFFFF), dumpadr=AXOUT,
             dump=2, dumpbytes=struct.pack("<H", want & 0xFFFF), dumpadr=AXOUT,
             note="fractional magnitudes: the mantissa decides")
             note="fractional magnitudes: the mantissa decides")
     add("real_cmp(2^40,0)",
     add("real_cmp(2^40,0)",
-        [s("real_cmp", 0x1210 + LOAD_BIAS)],
+        [s("real_cmp", 0x1410 + LOAD_BIAS)],
         dump=2, dumpbytes=struct.pack("<h", 1), dumpadr=AXOUT,
         dump=2, dumpbytes=struct.pack("<h", 1), dumpadr=AXOUT,
         note="the exponent alone decides")
         note="the exponent alone decides")
 
 
@@ -586,20 +594,20 @@ def build_cases():
     # (TPSRC's xint pushes the six-byte result back).
     # (TPSRC's xint pushes the six-byte result back).
     for bi, (a, b) in enumerate(BLOCKS):
     for bi, (a, b) in enumerate(BLOCKS):
         if a in (0, 1, -3, 32767):
         if a in (0, 1, -3, 32767):
-            blk = 0x1030 + 16 * bi
+            blk = 0x1230 + 16 * bi
             add("real_int(%d)" % a,
             add("real_int(%d)" % a,
                 [s("real_int", blk + LOAD_BIAS)],
                 [s("real_int", blk + LOAD_BIAS)],
                 dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
                 dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
                 note="the integer part as a real, the reference's reint")
                 note="the integer part as a real, the reference's reint")
     for fi, (a, b) in enumerate(FRAC):
     for fi, (a, b) in enumerate(FRAC):
-        blk = 0x1170 + 16 * fi
+        blk = 0x1370 + 16 * fi
         add("real_int(%g)" % a,
         add("real_int(%g)" % a,
             [s("real_int", blk + LOAD_BIAS)],
             [s("real_int", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
             dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
             note="the fraction is masked away; the sign and exponent stay")
             note="the fraction is masked away; the sign and exponent stay")
     add("real_int(2^40)",
     add("real_int(2^40)",
-        [s("real_int", 0x1210 + LOAD_BIAS)],
-        dump=6, dumpbytes=bytes.fromhex("A90000000000"), dumpadr=0x1210,
+        [s("real_int", 0x1410 + LOAD_BIAS)],
+        dump=6, dumpbytes=bytes.fromhex("A90000000000"), dumpadr=0x1410,
         note="exponent >= 0A8h is already integral and returns unchanged")
         note="exponent >= 0A8h is already integral and returns unchanged")
 
 
     # --- fmtreal: real_fmt writes a length-prefixed, left-padded field -----
     # --- fmtreal: real_fmt writes a length-prefixed, left-padded field -----
@@ -616,15 +624,15 @@ def build_cases():
          s2cx("real_fmt", STORE + LOAD_BIAS, FMTDEST + LOAD_BIAS, 0)],
          s2cx("real_fmt", STORE + LOAD_BIAS, FMTDEST + LOAD_BIAS, 0)],
         stdin=struct.pack("<h", 1), dump=2, dumpbytes=bytes([1]) + b"1",
         stdin=struct.pack("<h", 1), dump=2, dumpbytes=bytes([1]) + b"1",
         dumpadr=FMTDEST, note="a known-good 1.0 from real_from_int")
         dumpadr=FMTDEST, note="a known-good 1.0 from real_from_int")
-    fmt("real_fmt(1:0:0)", 0x1030, 0, 0, b"1")
-    fmt("real_fmt(0:0:0)", 0x10B0, 0, 0, b"0")
-    fmt("real_fmt(3.7:0:2)", 0x1170, 0, 2, b"3.70")
-    fmt("real_fmt(12345.75:12:2)", 0x11C0, 12, 2, b"    12345.75")
-    fmt("real_fmt(0.5:6:3)", 0x11A0, 6, 3, b" 0.500")
-    fmt("real_fmt(-3.7:0:1)", 0x1190, 0, 1, b"-3.7")
-    fmt("real_fmt(0:5:2)", 0x10B0, 5, 2, b" 0.00")
-    fmt("real_fmt(2.5:0:0)", 0x11D0, 0, 0, b"3")
-    fmt("real_fmt(3.7:10:25)", 0x1170, 10, 25, b"3.7000E+00")
+    fmt("real_fmt(1:0:0)", 0x1230, 0, 0, b"1")
+    fmt("real_fmt(0:0:0)", 0x12B0, 0, 0, b"0")
+    fmt("real_fmt(3.7:0:2)", 0x1370, 0, 2, b"3.70")
+    fmt("real_fmt(12345.75:12:2)", 0x13C0, 12, 2, b"    12345.75")
+    fmt("real_fmt(0.5:6:3)", 0x13A0, 6, 3, b" 0.500")
+    fmt("real_fmt(-3.7:0:1)", 0x1390, 0, 1, b"-3.7")
+    fmt("real_fmt(0:5:2)", 0x12B0, 5, 2, b" 0.00")
+    fmt("real_fmt(2.5:0:0)", 0x13D0, 0, 0, b"3")
+    fmt("real_fmt(3.7:10:25)", 0x1370, 10, 25, b"3.7000E+00")
 
 
     # --- ascreal: real_val parses an ASCIIZ string ------------------------
     # --- ascreal: real_val parses an ASCIIZ string ------------------------
     # A dump of AXOUT shows the returned offset, or FFFFh on a hard error.
     # A dump of AXOUT shows the returned offset, or FFFFh on a hard error.
@@ -655,17 +663,31 @@ def build_cases():
         off += len(txt)
         off += len(txt)
 
 
     for bi, (a, b) in [(0, (12340, 5)), (1, (12340, 1))]:
     for bi, (a, b) in [(0, (12340, 5)), (1, (12340, 1))]:
-        blk = 0x1300 + 16 * bi
+        blk = 0x1500 + 16 * bi
         add("real_add(%d,%d)" % (a, b), [s("real_add", blk + LOAD_BIAS)],
         add("real_add(%d,%d)" % (a, b), [s("real_add", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
             dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
             note="a large value plus a small digit: the add core's alignment")
             note="a large value plus a small digit: the add core's alignment")
 
 
     for bi, a in [(2, 12341), (3, 12345), (4, 12340)]:
     for bi, a in [(2, 12341), (3, 12345), (4, 12340)]:
-        blk = 0x1300 + 16 * bi
+        blk = 0x1500 + 16 * bi
         add("real_mul(%d,1)" % a, [s("real_mul", blk + LOAD_BIAS)],
         add("real_mul(%d,1)" % a, [s("real_mul", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(a), dumpadr=blk,
             dump=6, dumpbytes=mbf(a), dumpadr=blk,
             note="multiplying by 1.0 must be exact")
             note="multiplying by 1.0 must be exact")
 
 
+    # --- real_write: [BP+4]=decimals, [BP+6]=width, [BP+8]=real ----------
+    def wr(name, blk, width, dec, text):
+        add(name, [s3("real_write", dec, width, blk + LOAD_BIAS)], out=text,
+            note="fmtreal's field, padded and printed by the entry itself")
+    wr("real_write(3.7:0:2)", 0x1370, 0, 2, b"3.70")
+    wr("real_write(3.7:10:2)", 0x1370, 10, 2, b"      3.70")
+    wr("real_write(12345.75:12:2)", 0x13C0, 12, 2, b"    12345.75")
+    wr("real_write(0.5:0:3)", 0x13A0, 0, 3, b"0.500")
+    wr("real_write(-3.7:0:1)", 0x1390, 0, 1, b"-3.7")
+    wr("real_write(2.5:0:0)", 0x13D0, 0, 0, b"3")
+    wr("real_write(42:0:0)", 0x1290, 0, 0, b"42")
+    wr("real_write(3.7:18:FFFF)", 0x1370, 18, 0xFFFF,
+       b"  3.7000000000E+00")
+
     add("wrchar('A')", [s("wrchar", ord("A"))], out=b"A")
     add("wrchar('A')", [s("wrchar", ord("A"))], out=b"A")
     add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")
     add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")
 
 

+ 129 - 84
shell/tests/runtime.golden

@@ -41,7 +41,7 @@
 0036  58          pop ax
 0036  58          pop ax
 0037  F7 D8       neg ax
 0037  F7 D8       neg ax
 0039  B9 0A 00    mov cx,0ah
 0039  B9 0A 00    mov cx,0ah
-003C  BB 7A 0D    mov bx,0d7ah
+003C  BB DC 0D    mov bx,0ddch
 003F  89 DE       mov si,bx
 003F  89 DE       mov si,bx
 0041  31 D2       xor dx,dx
 0041  31 D2       xor dx,dx
 0043  F7 F1       div ax,cx
 0043  F7 F1       div ax,cx
@@ -93,7 +93,7 @@
 00A2  58          pop ax
 00A2  58          pop ax
 00A3  F7 D8       neg ax
 00A3  F7 D8       neg ax
 00A5  B9 0A 00    mov cx,0ah
 00A5  B9 0A 00    mov cx,0ah
-00A8  BB 7A 0D    mov bx,0d7ah
+00A8  BB DC 0D    mov bx,0ddch
 00AB  89 DE       mov si,bx
 00AB  89 DE       mov si,bx
 00AD  31 D2       xor dx,dx
 00AD  31 D2       xor dx,dx
 00AF  F7 F1       div ax,cx
 00AF  F7 F1       div ax,cx
@@ -125,18 +125,18 @@
 00DE  83 7E 04 00 cmp word ptr [bp+4h],0h
 00DE  83 7E 04 00 cmp word ptr [bp+4h],0h
 00E2  89 EC       mov sp,bp
 00E2  89 EC       mov sp,bp
 00E4  75 05       jne 0ebh
 00E4  75 05       jne 0ebh
-00E6  BA 7F 0D    mov dx,0d7fh
+00E6  BA E1 0D    mov dx,0de1h
 00E9  EB 03       jmp 0eeh
 00E9  EB 03       jmp 0eeh
-00EB  BA 7A 0D    mov dx,0d7ah
+00EB  BA DC 0D    mov dx,0ddch
 00EE  B4 09       mov ah,9h
 00EE  B4 09       mov ah,9h
 00F0  CD 21       int 21h
 00F0  CD 21       int 21h
 00F2  5D          pop bp
 00F2  5D          pop bp
 00F3  C3          ret
 00F3  C3          ret
-00F4  BA 88 0D    mov dx,0d88h
+00F4  BA EA 0D    mov dx,0deah
 00F7  B4 09       mov ah,9h
 00F7  B4 09       mov ah,9h
 00F9  CD 21       int 21h
 00F9  CD 21       int 21h
 00FB  C3          ret
 00FB  C3          ret
-00FC  BA 85 0D    mov dx,0d85h
+00FC  BA E7 0D    mov dx,0de7h
 00FF  B4 09       mov ah,9h
 00FF  B4 09       mov ah,9h
 0101  CD 21       int 21h
 0101  CD 21       int 21h
 0103  C3          ret
 0103  C3          ret
@@ -303,12 +303,12 @@
 0217  EB EF       jmp 208h
 0217  EB EF       jmp 208h
 0219  58          pop ax
 0219  58          pop ax
 021A  C3          ret
 021A  C3          ret
-021B  8B 1E 8E 0D mov bx,word ptr [0d8eh]
+021B  8B 1E F0 0D mov bx,word ptr [0df0h]
 021F  83 FB 00    cmp bx,0h
 021F  83 FB 00    cmp bx,0h
 0222  74 09       je 22dh
 0222  74 09       je 22dh
 0224  8A C3       mov al,bl
 0224  8A C3       mov al,bl
 0226  31 DB       xor bx,bx
 0226  31 DB       xor bx,bx
-0228  89 1E 8E 0D mov word ptr [0d8eh],bx
+0228  89 1E F0 0D mov word ptr [0df0h],bx
 022C  C3          ret
 022C  C3          ret
 022D  B4 08       mov ah,8h
 022D  B4 08       mov ah,8h
 022F  CD 21       int 21h
 022F  CD 21       int 21h
@@ -316,7 +316,7 @@
 0232  88 C2       mov dl,al
 0232  88 C2       mov dl,al
 0234  BB 01 00    mov bx,1h
 0234  BB 01 00    mov bx,1h
 0237  8A DA       mov bl,dl
 0237  8A DA       mov bl,dl
-0239  89 1E 8E 0D mov word ptr [0d8eh],bx
+0239  89 1E F0 0D mov word ptr [0df0h],bx
 023D  C3          ret
 023D  C3          ret
 023E  55          push bp
 023E  55          push bp
 023F  8B EC       mov bp,sp
 023F  8B EC       mov bp,sp
@@ -543,7 +543,7 @@
 03C2  8B 4D 06    mov cx,word ptr [di+6h]
 03C2  8B 4D 06    mov cx,word ptr [di+6h]
 03C5  8B 75 08    mov si,word ptr [di+8h]
 03C5  8B 75 08    mov si,word ptr [di+8h]
 03C8  8B 7D 0A    mov di,word ptr [di+0ah]
 03C8  8B 7D 0A    mov di,word ptr [di+0ah]
-03CB  C7 06 90 0D 00 00 mov word ptr [0d90h],0h
+03CB  C7 06 F2 0D 00 00 mov word ptr [0df2h],0h
 03D1  E8 4D 00    call 421h
 03D1  E8 4D 00    call 421h
 03D4  5F          pop di
 03D4  5F          pop di
 03D5  89 05       mov word ptr [di],ax
 03D5  89 05       mov word ptr [di],ax
@@ -572,7 +572,7 @@
 03FE  8B 4D 06    mov cx,word ptr [di+6h]
 03FE  8B 4D 06    mov cx,word ptr [di+6h]
 0401  8B 75 08    mov si,word ptr [di+8h]
 0401  8B 75 08    mov si,word ptr [di+8h]
 0404  8B 7D 0A    mov di,word ptr [di+0ah]
 0404  8B 7D 0A    mov di,word ptr [di+0ah]
-0407  C7 06 90 0D 00 80 mov word ptr [0d90h],8000h
+0407  C7 06 F2 0D 00 80 mov word ptr [0df2h],8000h
 040D  E8 11 00    call 421h
 040D  E8 11 00    call 421h
 0410  5F          pop di
 0410  5F          pop di
 0411  89 05       mov word ptr [di],ax
 0411  89 05       mov word ptr [di],ax
@@ -587,7 +587,7 @@
 0420  C3          ret
 0420  C3          ret
 0421  80 F9 00    cmp cl,0h
 0421  80 F9 00    cmp cl,0h
 0424  74 0E       je 434h
 0424  74 0E       je 434h
-0426  33 3E 90 0D xor di,word ptr [0d90h]
+0426  33 3E F2 0D xor di,word ptr [0df2h]
 042A  3C 00       cmp al,0h
 042A  3C 00       cmp al,0h
 042C  75 07       jne 435h
 042C  75 07       jne 435h
 042E  89 C8       mov ax,cx
 042E  89 C8       mov ax,cx
@@ -599,16 +599,16 @@
 0439  91          xchg ax,cx
 0439  91          xchg ax,cx
 043A  87 F3       xchg bx,si
 043A  87 F3       xchg bx,si
 043C  87 FA       xchg dx,di
 043C  87 FA       xchg dx,di
-043E  88 0E 94 0D mov byte ptr [0d94h],cl
+043E  88 0E F6 0D mov byte ptr [0df6h],cl
 0442  28 C1       sub cl,al
 0442  28 C1       sub cl,al
 0444  80 F9 28    cmp cl,28h
 0444  80 F9 28    cmp cl,28h
 0447  72 06       jb 44fh
 0447  72 06       jb 44fh
-0449  8A 0E 94 0D mov cl,byte ptr [0d94h]
+0449  8A 0E F6 0D mov cl,byte ptr [0df6h]
 044D  EB DF       jmp 42eh
 044D  EB DF       jmp 42eh
-044F  89 3E 90 0D mov word ptr [0d90h],di
-0453  80 26 91 0D 80 and byte ptr [0d91h],80h
-0458  89 3E 92 0D mov word ptr [0d92h],di
-045C  30 36 93 0D xor byte ptr [0d93h],dh
+044F  89 3E F2 0D mov word ptr [0df2h],di
+0453  80 26 F3 0D 80 and byte ptr [0df3h],80h
+0458  89 3E F4 0D mov word ptr [0df4h],di
+045C  30 36 F5 0D xor byte ptr [0df5h],dh
 0460  81 CF 00 80 or di,8000h
 0460  81 CF 00 80 or di,8000h
 0464  80 CE 80    or dh,80h
 0464  80 CE 80    or dh,80h
 0467  80 F9 10    cmp cl,10h
 0467  80 F9 10    cmp cl,10h
@@ -633,8 +633,8 @@
 0492  D0 DC       rcr ah,1h
 0492  D0 DC       rcr ah,1h
 0494  FE C9       dec cl
 0494  FE C9       dec cl
 0496  75 F6       jne 48eh
 0496  75 F6       jne 48eh
-0498  A0 94 0D    mov al,byte ptr [0d94h]
-049B  F6 06 93 0D 80 test byte ptr [0d93h],80h
+0498  A0 F6 0D    mov al,byte ptr [0df6h]
+049B  F6 06 F5 0D 80 test byte ptr [0df5h],80h
 04A0  75 14       jne 4b6h
 04A0  75 14       jne 4b6h
 04A2  00 EC       add ah,ch
 04A2  00 EC       add ah,ch
 04A4  11 F3       adc bx,si
 04A4  11 F3       adc bx,si
@@ -654,7 +654,7 @@
 04BE  19 F3       sbb bx,si
 04BE  19 F3       sbb bx,si
 04C0  19 FA       sbb dx,di
 04C0  19 FA       sbb dx,di
 04C2  73 14       jnb 4d8h
 04C2  73 14       jnb 4d8h
-04C4  80 36 91 0D 80 xor byte ptr [0d91h],80h
+04C4  80 36 F3 0D 80 xor byte ptr [0df3h],80h
 04C9  F6 D4       not ah
 04C9  F6 D4       not ah
 04CB  F7 D3       not bx
 04CB  F7 D3       not bx
 04CD  F7 D2       not dx
 04CD  F7 D2       not dx
@@ -686,7 +686,7 @@
 0506  31 D2       xor dx,dx
 0506  31 D2       xor dx,dx
 0508  C3          ret
 0508  C3          ret
 0509  80 E6 7F    and dh,7fh
 0509  80 E6 7F    and dh,7fh
-050C  32 36 91 0D xor dh,byte ptr [0d91h]
+050C  32 36 F3 0D xor dh,byte ptr [0df3h]
 0510  C3          ret
 0510  C3          ret
 0511  55          push bp
 0511  55          push bp
 0512  8B EC       mov bp,sp
 0512  8B EC       mov bp,sp
@@ -732,23 +732,23 @@
 0565  B0 01       mov al,1h
 0565  B0 01       mov al,1h
 0567  B4 4C       mov ah,4ch
 0567  B4 4C       mov ah,4ch
 0569  CD 21       int 21h
 0569  CD 21       int 21h
-056B  89 0E 9B 0D mov word ptr [0d9bh],cx
+056B  89 0E FD 0D mov word ptr [0dfdh],cx
 056F  8B CA       mov cx,dx
 056F  8B CA       mov cx,dx
 0571  31 F9       xor cx,di
 0571  31 F9       xor cx,di
 0573  F6 D5       not ch
 0573  F6 D5       not ch
 0575  80 E5 80    and ch,80h
 0575  80 E5 80    and ch,80h
-0578  88 2E 91 0D mov byte ptr [0d91h],ch
+0578  88 2E F3 0D mov byte ptr [0df3h],ch
 057C  80 CE 80    or dh,80h
 057C  80 CE 80    or dh,80h
 057F  81 CF 00 80 or di,8000h
 057F  81 CF 00 80 or di,8000h
-0583  89 36 9D 0D mov word ptr [0d9dh],si
-0587  89 3E 9F 0D mov word ptr [0d9fh],di
-058B  A3 95 0D    mov word ptr [0d95h],ax
-058E  89 1E 97 0D mov word ptr [0d97h],bx
-0592  89 16 99 0D mov word ptr [0d99h],dx
+0583  89 36 FF 0D mov word ptr [0dffh],si
+0587  89 3E 01 0E mov word ptr [0e01h],di
+058B  A3 F7 0D    mov word ptr [0df7h],ax
+058E  89 1E F9 0D mov word ptr [0df9h],bx
+0592  89 16 FB 0D mov word ptr [0dfbh],dx
 0596  30 E4       xor ah,ah
 0596  30 E4       xor ah,ah
 0598  31 DB       xor bx,bx
 0598  31 DB       xor bx,bx
 059A  31 D2       xor dx,dx
 059A  31 D2       xor dx,dx
-059C  BF 9B 0D    mov di,0d9bh
+059C  BF FD 0D    mov di,0dfdh
 059F  B1 05       mov cl,5h
 059F  B1 05       mov cl,5h
 05A1  47          inc di
 05A1  47          inc di
 05A2  8A 2D       mov ch,byte ptr [di]
 05A2  8A 2D       mov ch,byte ptr [di]
@@ -763,9 +763,9 @@
 05B5  BE 08 00    mov si,8h
 05B5  BE 08 00    mov si,8h
 05B8  D0 DD       rcr ch,1h
 05B8  D0 DD       rcr ch,1h
 05BA  73 0C       jnb 5c8h
 05BA  73 0C       jnb 5c8h
-05BC  02 26 96 0D add ah,byte ptr [0d96h]
-05C0  13 1E 97 0D adc bx,word ptr [0d97h]
-05C4  13 16 99 0D adc dx,word ptr [0d99h]
+05BC  02 26 F8 0D add ah,byte ptr [0df8h]
+05C0  13 1E F9 0D adc bx,word ptr [0df9h]
+05C4  13 16 FB 0D adc dx,word ptr [0dfbh]
 05C8  D1 DA       rcr dx,1h
 05C8  D1 DA       rcr dx,1h
 05CA  D1 DB       rcr bx,1h
 05CA  D1 DB       rcr bx,1h
 05CC  D0 DC       rcr ah,1h
 05CC  D0 DC       rcr ah,1h
@@ -785,7 +785,7 @@
 05E6  74 02       je 5eah
 05E6  74 02       je 5eah
 05E8  FE C9       dec cl
 05E8  FE C9       dec cl
 05EA  91          xchg ax,cx
 05EA  91          xchg ax,cx
-05EB  32 36 91 0D xor dh,byte ptr [0d91h]
+05EB  32 36 F3 0D xor dh,byte ptr [0df3h]
 05EF  3C 00       cmp al,0h
 05EF  3C 00       cmp al,0h
 05F1  75 06       jne 5f9h
 05F1  75 06       jne 5f9h
 05F3  31 C0       xor ax,ax
 05F3  31 C0       xor ax,ax
@@ -834,29 +834,29 @@
 0647  B0 01       mov al,1h
 0647  B0 01       mov al,1h
 0649  B4 4C       mov ah,4ch
 0649  B4 4C       mov ah,4ch
 064B  CD 21       int 21h
 064B  CD 21       int 21h
-064D  89 0E 9B 0D mov word ptr [0d9bh],cx
+064D  89 0E FD 0D mov word ptr [0dfdh],cx
 0651  8B CA       mov cx,dx
 0651  8B CA       mov cx,dx
 0653  31 F9       xor cx,di
 0653  31 F9       xor cx,di
 0655  F6 D5       not ch
 0655  F6 D5       not ch
 0657  80 E5 80    and ch,80h
 0657  80 E5 80    and ch,80h
-065A  88 2E 91 0D mov byte ptr [0d91h],ch
+065A  88 2E F3 0D mov byte ptr [0df3h],ch
 065E  80 CE 80    or dh,80h
 065E  80 CE 80    or dh,80h
 0661  81 CF 00 80 or di,8000h
 0661  81 CF 00 80 or di,8000h
-0665  89 36 9D 0D mov word ptr [0d9dh],si
-0669  89 3E 9F 0D mov word ptr [0d9fh],di
-066D  A2 95 0D    mov byte ptr [0d95h],al
-0670  BF 9A 0D    mov di,0d9ah
+0665  89 36 FF 0D mov word ptr [0dffh],si
+0669  89 3E 01 0E mov word ptr [0e01h],di
+066D  A2 F7 0D    mov byte ptr [0df7h],al
+0670  BF FC 0D    mov di,0dfch
 0673  B1 05       mov cl,5h
 0673  B1 05       mov cl,5h
 0675  BE 08 00    mov si,8h
 0675  BE 08 00    mov si,8h
-0678  3B 16 9F 0D cmp dx,word ptr [0d9fh]
+0678  3B 16 01 0E cmp dx,word ptr [0e01h]
 067C  75 0A       jne 688h
 067C  75 0A       jne 688h
-067E  3B 1E 9D 0D cmp bx,word ptr [0d9dh]
+067E  3B 1E FF 0D cmp bx,word ptr [0dffh]
 0682  75 04       jne 688h
 0682  75 04       jne 688h
-0684  3A 26 9C 0D cmp ah,byte ptr [0d9ch]
+0684  3A 26 FE 0D cmp ah,byte ptr [0dfeh]
 0688  72 0C       jb 696h
 0688  72 0C       jb 696h
-068A  2A 26 9C 0D sub ah,byte ptr [0d9ch]
-068E  1B 1E 9D 0D sbb bx,word ptr [0d9dh]
-0692  1B 16 9F 0D sbb dx,word ptr [0d9fh]
+068A  2A 26 FE 0D sub ah,byte ptr [0dfeh]
+068E  1B 1E FF 0D sbb bx,word ptr [0dffh]
+0692  1B 16 01 0E sbb dx,word ptr [0e01h]
 0696  F5          cmc
 0696  F5          cmc
 0697  D0 D5       rcl ch,1h
 0697  D0 D5       rcl ch,1h
 0699  4E          dec si
 0699  4E          dec si
@@ -870,24 +870,24 @@
 06A8  D1 D3       rcl bx,1h
 06A8  D1 D3       rcl bx,1h
 06AA  D1 D2       rcl dx,1h
 06AA  D1 D2       rcl dx,1h
 06AC  73 CA       jnb 678h
 06AC  73 CA       jnb 678h
-06AE  2A 26 9C 0D sub ah,byte ptr [0d9ch]
-06B2  1B 1E 9D 0D sbb bx,word ptr [0d9dh]
-06B6  1B 16 9F 0D sbb dx,word ptr [0d9fh]
+06AE  2A 26 FE 0D sub ah,byte ptr [0dfeh]
+06B2  1B 1E FF 0D sbb bx,word ptr [0dffh]
+06B6  1B 16 01 0E sbb dx,word ptr [0e01h]
 06BA  F8          clc
 06BA  F8          clc
 06BB  EB D9       jmp 696h
 06BB  EB D9       jmp 696h
 06BD  D0 E4       shl ah,1h
 06BD  D0 E4       shl ah,1h
 06BF  D1 D3       rcl bx,1h
 06BF  D1 D3       rcl bx,1h
 06C1  D1 D2       rcl dx,1h
 06C1  D1 D2       rcl dx,1h
 06C3  72 11       jb 6d6h
 06C3  72 11       jb 6d6h
-06C5  3B 16 9F 0D cmp dx,word ptr [0d9fh]
+06C5  3B 16 01 0E cmp dx,word ptr [0e01h]
 06C9  75 0A       jne 6d5h
 06C9  75 0A       jne 6d5h
-06CB  3B 1E 9D 0D cmp bx,word ptr [0d9dh]
+06CB  3B 1E FF 0D cmp bx,word ptr [0dffh]
 06CF  75 04       jne 6d5h
 06CF  75 04       jne 6d5h
-06D1  3A 26 9C 0D cmp ah,byte ptr [0d9ch]
+06D1  3A 26 FE 0D cmp ah,byte ptr [0dfeh]
 06D5  F5          cmc
 06D5  F5          cmc
-06D6  8B 0E 95 0D mov cx,word ptr [0d95h]
-06DA  8B 1E 97 0D mov bx,word ptr [0d97h]
-06DE  8B 16 99 0D mov dx,word ptr [0d99h]
+06D6  8B 0E F7 0D mov cx,word ptr [0df7h]
+06DA  8B 1E F9 0D mov bx,word ptr [0df9h]
+06DE  8B 16 FB 0D mov dx,word ptr [0dfbh]
 06E2  9F          lahf
 06E2  9F          lahf
 06E3  F6 C6 80    test dh,80h
 06E3  F6 C6 80    test dh,80h
 06E6  75 09       jne 6f1h
 06E6  75 09       jne 6f1h
@@ -1030,47 +1030,47 @@
 07EC  B5 00       mov ch,0h
 07EC  B5 00       mov ch,0h
 07EE  B6 00       mov dh,0h
 07EE  B6 00       mov dh,0h
 07F0  51          push cx
 07F0  51          push cx
-07F1  BB EB 0D    mov bx,0debh
+07F1  BB 4D 0E    mov bx,0e4dh
 07F4  E8 41 02    call 0a38h
 07F4  E8 41 02    call 0a38h
 07F7  59          pop cx
 07F7  59          pop cx
 07F8  89 D8       mov ax,bx
 07F8  89 D8       mov ax,bx
-07FA  BB EB 0D    mov bx,0debh
+07FA  BB 4D 0E    mov bx,0e4dh
 07FD  2B C3       sub ax,bx
 07FD  2B C3       sub ax,bx
-07FF  A3 2B 0E    mov word ptr [0e2bh],ax
-0802  89 0E 2D 0E mov word ptr [0e2dh],cx
-0806  8B 06 2D 0E mov ax,word ptr [0e2dh]
-080A  8B 16 2B 0E mov dx,word ptr [0e2bh]
+07FF  A3 8D 0E    mov word ptr [0e8dh],ax
+0802  89 0E 8F 0E mov word ptr [0e8fh],cx
+0806  8B 06 8F 0E mov ax,word ptr [0e8fh]
+080A  8B 16 8D 0E mov dx,word ptr [0e8dh]
 080E  2B C2       sub ax,dx
 080E  2B C2       sub ax,dx
 0810  83 F8 00    cmp ax,0h
 0810  83 F8 00    cmp ax,0h
 0813  7D 02       jnl 817h
 0813  7D 02       jnl 817h
 0815  31 C0       xor ax,ax
 0815  31 C0       xor ax,ax
-0817  A3 2F 0E    mov word ptr [0e2fh],ax
-081A  8B 16 2B 0E mov dx,word ptr [0e2bh]
+0817  A3 91 0E    mov word ptr [0e91h],ax
+081A  8B 16 8D 0E mov dx,word ptr [0e8dh]
 081E  03 C2       add ax,dx
 081E  03 C2       add ax,dx
 0820  3D FF 00    cmp ax,0ffh
 0820  3D FF 00    cmp ax,0ffh
 0823  77 03       jnbe 828h
 0823  77 03       jnbe 828h
 0825  E9 03 00    jmp 82bh
 0825  E9 03 00    jmp 82bh
 0828  B8 FF 00    mov ax,0ffh
 0828  B8 FF 00    mov ax,0ffh
-082B  A3 31 0E    mov word ptr [0e31h],ax
+082B  A3 93 0E    mov word ptr [0e93h],ax
 082E  8B 56 06    mov dx,word ptr [bp+6h]
 082E  8B 56 06    mov dx,word ptr [bp+6h]
 0831  89 D7       mov di,dx
 0831  89 D7       mov di,dx
 0833  88 05       mov byte ptr [di],al
 0833  88 05       mov byte ptr [di],al
 0835  47          inc di
 0835  47          inc di
-0836  8B 0E 2F 0E mov cx,word ptr [0e2fh]
+0836  8B 0E 91 0E mov cx,word ptr [0e91h]
 083A  E3 07       jcxz 843h
 083A  E3 07       jcxz 843h
 083C  B0 20       mov al,20h
 083C  B0 20       mov al,20h
 083E  88 05       mov byte ptr [di],al
 083E  88 05       mov byte ptr [di],al
 0840  47          inc di
 0840  47          inc di
 0841  E2 F9       loop 83ch
 0841  E2 F9       loop 83ch
-0843  BE EB 0D    mov si,0debh
-0846  8B 0E 2B 0E mov cx,word ptr [0e2bh]
+0843  BE 4D 0E    mov si,0e4dh
+0846  8B 0E 8D 0E mov cx,word ptr [0e8dh]
 084A  E3 08       jcxz 854h
 084A  E3 08       jcxz 854h
 084C  8A 04       mov al,byte ptr [si]
 084C  8A 04       mov al,byte ptr [si]
 084E  88 05       mov byte ptr [di],al
 084E  88 05       mov byte ptr [di],al
 0850  46          inc si
 0850  46          inc si
 0851  47          inc di
 0851  47          inc di
 0852  E2 F8       loop 84ch
 0852  E2 F8       loop 84ch
-0854  8B 06 31 0E mov ax,word ptr [0e31h]
+0854  8B 06 93 0E mov ax,word ptr [0e93h]
 0858  5F          pop di
 0858  5F          pop di
 0859  5E          pop si
 0859  5E          pop si
 085A  5A          pop dx
 085A  5A          pop dx
@@ -1086,12 +1086,12 @@
 0866  57          push di
 0866  57          push di
 0867  8B 5E 04    mov bx,word ptr [bp+4h]
 0867  8B 5E 04    mov bx,word ptr [bp+4h]
 086A  8B 7E 06    mov di,word ptr [bp+6h]
 086A  8B 7E 06    mov di,word ptr [bp+6h]
-086D  89 1E 33 0E mov word ptr [0e33h],bx
+086D  89 1E 95 0E mov word ptr [0e95h],bx
 0871  E8 CF 02    call 0b43h
 0871  E8 CF 02    call 0b43h
 0874  73 03       jnb 879h
 0874  73 03       jnb 879h
 0876  E9 0B 00    jmp 884h
 0876  E9 0B 00    jmp 884h
 0879  89 D8       mov ax,bx
 0879  89 D8       mov ax,bx
-087B  8B 16 33 0E mov dx,word ptr [0e33h]
+087B  8B 16 95 0E mov dx,word ptr [0e95h]
 087F  2B C2       sub ax,dx
 087F  2B C2       sub ax,dx
 0881  E9 03 00    jmp 887h
 0881  E9 03 00    jmp 887h
 0884  B8 FF FF    mov ax,0ffffh
 0884  B8 FF FF    mov ax,0ffffh
@@ -1140,7 +1140,7 @@
 08CE  52          push dx
 08CE  52          push dx
 08CF  53          push bx
 08CF  53          push bx
 08D0  50          push ax
 08D0  50          push ax
-08D1  88 0E AD 0D mov byte ptr [0dadh],cl
+08D1  88 0E 0F 0E mov byte ptr [0e0fh],cl
 08D5  80 F9 00    cmp cl,0h
 08D5  80 F9 00    cmp cl,0h
 08D8  78 03       js 8ddh
 08D8  78 03       js 8ddh
 08DA  E9 02 00    jmp 8dfh
 08DA  E9 02 00    jmp 8dfh
@@ -1151,7 +1151,7 @@
 08E6  D0 EB       shr bl,1h
 08E6  D0 EB       shr bl,1h
 08E8  00 FB       add bl,bh
 08E8  00 FB       add bl,bh
 08EA  30 FF       xor bh,bh
 08EA  30 FF       xor bh,bh
-08EC  8D BF AF 0D lea di,[bx+0dafh]
+08EC  8D BF 11 0E lea di,[bx+0e11h]
 08F0  8B 05       mov ax,word ptr [di]
 08F0  8B 05       mov ax,word ptr [di]
 08F2  8B 5D 02    mov bx,word ptr [di+2h]
 08F2  8B 5D 02    mov bx,word ptr [di+2h]
 08F5  8B 55 04    mov dx,word ptr [di+4h]
 08F5  8B 55 04    mov dx,word ptr [di+4h]
@@ -1168,7 +1168,7 @@
 0910  58          pop ax
 0910  58          pop ax
 0911  5B          pop bx
 0911  5B          pop bx
 0912  5A          pop dx
 0912  5A          pop dx
-0913  F6 06 AD 0D 80 test byte ptr [0dadh],80h
+0913  F6 06 0F 0E 80 test byte ptr [0e0fh],80h
 0918  74 03       je 91dh
 0918  74 03       je 91dh
 091A  E9 03 00    jmp 920h
 091A  E9 03 00    jmp 920h
 091D  E9 27 FC    jmp 547h
 091D  E9 27 FC    jmp 547h
@@ -1195,11 +1195,11 @@
 0947  3C 00       cmp al,0h
 0947  3C 00       cmp al,0h
 0949  74 03       je 94eh
 0949  74 03       je 94eh
 094B  E9 16 00    jmp 964h
 094B  E9 16 00    jmp 964h
-094E  BE A1 0D    mov si,0da1h
+094E  BE 03 0E    mov si,0e03h
 0951  C7 04 30 30 mov word ptr [si],3030h
 0951  C7 04 30 30 mov word ptr [si],3030h
 0955  46          inc si
 0955  46          inc si
 0956  46          inc si
 0956  46          inc si
-0957  81 FE AD 0D cmp si,0dadh
+0957  81 FE 0F 0E cmp si,0e0fh
 095B  74 03       je 960h
 095B  74 03       je 960h
 095D  E9 F1 FF    jmp 951h
 095D  E9 F1 FF    jmp 951h
 0960  B9 00 00    mov cx,0h
 0960  B9 00 00    mov cx,0h
@@ -1243,7 +1243,7 @@
 09AF  FE C9       dec cl
 09AF  FE C9       dec cl
 09B1  74 03       je 9b6h
 09B1  74 03       je 9b6h
 09B3  E9 F3 FF    jmp 9a9h
 09B3  E9 F3 FF    jmp 9a9h
-09B6  BE A1 0D    mov si,0da1h
+09B6  BE 03 0E    mov si,0e03h
 09B9  88 F5       mov ch,dh
 09B9  88 F5       mov ch,dh
 09BB  B1 04       mov cl,4h
 09BB  B1 04       mov cl,4h
 09BD  D2 ED       shr ch,cl
 09BD  D2 ED       shr ch,cl
@@ -1269,13 +1269,13 @@
 09E1  D1 D3       rcl bx,1h
 09E1  D1 D3       rcl bx,1h
 09E3  D1 D2       rcl dx,1h
 09E3  D1 D2       rcl dx,1h
 09E5  46          inc si
 09E5  46          inc si
-09E6  81 FE AD 0D cmp si,0dadh
+09E6  81 FE 0F 0E cmp si,0e0fh
 09EA  74 03       je 9efh
 09EA  74 03       je 9efh
 09EC  E9 CA FF    jmp 9b9h
 09EC  E9 CA FF    jmp 9b9h
 09EF  59          pop cx
 09EF  59          pop cx
 09F0  C3          ret
 09F0  C3          ret
 09F1  30 E4       xor ah,ah
 09F1  30 E4       xor ah,ah
-09F3  BB A1 0D    mov bx,0da1h
+09F3  BB 03 0E    mov bx,0e03h
 09F6  03 D8       add bx,ax
 09F6  03 D8       add bx,ax
 09F8  80 3F 35    cmp byte ptr [bx],35h
 09F8  80 3F 35    cmp byte ptr [bx],35h
 09FB  C6 07 00    mov byte ptr [bx],0h
 09FB  C6 07 00    mov byte ptr [bx],0h
@@ -1346,7 +1346,7 @@
 0A92  00 C8       add al,cl
 0A92  00 C8       add al,cl
 0A94  78 03       js 0a99h
 0A94  78 03       js 0a99h
 0A96  E9 08 00    jmp 0aa1h
 0A96  E9 08 00    jmp 0aa1h
-0A99  C6 06 A1 0D 00 mov byte ptr [0da1h],0h
+0A99  C6 06 03 0E 00 mov byte ptr [0e03h],0h
 0A9E  E9 0C 00    jmp 0aadh
 0A9E  E9 0C 00    jmp 0aadh
 0AA1  3C 0C       cmp al,0ch
 0AA1  3C 0C       cmp al,0ch
 0AA3  73 03       jnb 0aa8h
 0AA3  73 03       jnb 0aa8h
@@ -1354,7 +1354,7 @@
 0AA8  B0 0B       mov al,0bh
 0AA8  B0 0B       mov al,0bh
 0AAA  E8 44 FF    call 9f1h
 0AAA  E8 44 FF    call 9f1h
 0AAD  5B          pop bx
 0AAD  5B          pop bx
-0AAE  BE A1 0D    mov si,0da1h
+0AAE  BE 03 0E    mov si,0e03h
 0AB1  F6 C5 80    test ch,80h
 0AB1  F6 C5 80    test ch,80h
 0AB4  75 03       jne 0ab9h
 0AB4  75 03       jne 0ab9h
 0AB6  E9 05 00    jmp 0abeh
 0AB6  E9 05 00    jmp 0abeh
@@ -1440,7 +1440,7 @@
 0B75  31 DB       xor bx,bx
 0B75  31 DB       xor bx,bx
 0B77  31 D2       xor dx,dx
 0B77  31 D2       xor dx,dx
 0B79  31 C9       xor cx,cx
 0B79  31 C9       xor cx,cx
-0B7B  C6 06 AE 0D 00 mov byte ptr [0daeh],0h
+0B7B  C6 06 10 0E 00 mov byte ptr [0e10h],0h
 0B80  8A 0C       mov cl,byte ptr [si]
 0B80  8A 0C       mov cl,byte ptr [si]
 0B82  80 F9 61    cmp cl,61h
 0B82  80 F9 61    cmp cl,61h
 0B85  73 03       jnb 0b8ah
 0B85  73 03       jnb 0b8ah
@@ -1469,7 +1469,7 @@
 0BB5  58          pop ax
 0BB5  58          pop ax
 0BB6  5B          pop bx
 0BB6  5B          pop bx
 0BB7  5A          pop dx
 0BB7  5A          pop dx
-0BB8  C7 06 90 0D 00 00 mov word ptr [0d90h],0h
+0BB8  C7 06 F2 0D 00 00 mov word ptr [0df2h],0h
 0BBE  E8 60 F8    call 421h
 0BBE  E8 60 F8    call 421h
 0BC1  59          pop cx
 0BC1  59          pop cx
 0BC2  5E          pop si
 0BC2  5E          pop si
@@ -1477,7 +1477,7 @@
 0BC4  F6 C5 40    test ch,40h
 0BC4  F6 C5 40    test ch,40h
 0BC7  75 03       jne 0bcch
 0BC7  75 03       jne 0bcch
 0BC9  E9 04 00    jmp 0bd0h
 0BC9  E9 04 00    jmp 0bd0h
-0BCC  FE 0E AE 0D dec byte ptr [0daeh]
+0BCC  FE 0E 10 0E dec byte ptr [0e10h]
 0BD0  46          inc si
 0BD0  46          inc si
 0BD1  E9 AC FF    jmp 0b80h
 0BD1  E9 AC FF    jmp 0b80h
 0BD4  80 F9 2E    cmp cl,2eh
 0BD4  80 F9 2E    cmp cl,2eh
@@ -1489,7 +1489,7 @@
 0BE4  80 CD 40    or ch,40h
 0BE4  80 CD 40    or ch,40h
 0BE7  E9 E6 FF    jmp 0bd0h
 0BE7  E9 E6 FF    jmp 0bd0h
 0BEA  80 F9 45    cmp cl,45h
 0BEA  80 F9 45    cmp cl,45h
-0BED  8A 0E AE 0D mov cl,byte ptr [0daeh]
+0BED  8A 0E 10 0E mov cl,byte ptr [0e10h]
 0BF1  74 03       je 0bf6h
 0BF1  74 03       je 0bf6h
 0BF3  E9 4D 00    jmp 0c43h
 0BF3  E9 4D 00    jmp 0c43h
 0BF6  E8 2A FD    call 923h
 0BF6  E8 2A FD    call 923h
@@ -1548,3 +1548,48 @@
 0C6B  E9 03 00    jmp 0c71h
 0C6B  E9 03 00    jmp 0c71h
 0C6E  80 E9 30    sub cl,30h
 0C6E  80 E9 30    sub cl,30h
 0C71  C3          ret
 0C71  C3          ret
+0C72  55          push bp
+0C73  8B EC       mov bp,sp
+0C75  53          push bx
+0C76  52          push dx
+0C77  56          push si
+0C78  57          push di
+0C79  8B 46 06    mov ax,word ptr [bp+6h]
+0C7C  A3 8F 0E    mov word ptr [0e8fh],ax
+0C7F  8B 56 04    mov dx,word ptr [bp+4h]
+0C82  8B 4E 06    mov cx,word ptr [bp+6h]
+0C85  8B 7E 08    mov di,word ptr [bp+8h]
+0C88  BB 4D 0E    mov bx,0e4dh
+0C8B  E8 AA FD    call 0a38h
+0C8E  89 D8       mov ax,bx
+0C90  BB 4D 0E    mov bx,0e4dh
+0C93  2B C3       sub ax,bx
+0C95  A3 8D 0E    mov word ptr [0e8dh],ax
+0C98  8B 06 8F 0E mov ax,word ptr [0e8fh]
+0C9C  8B 16 8D 0E mov dx,word ptr [0e8dh]
+0CA0  2B C2       sub ax,dx
+0CA2  83 F8 00    cmp ax,0h
+0CA5  7D 02       jnl 0ca9h
+0CA7  31 C0       xor ax,ax
+0CA9  A3 91 0E    mov word ptr [0e91h],ax
+0CAC  8B 0E 91 0E mov cx,word ptr [0e91h]
+0CB0  E3 08       jcxz 0cbah
+0CB2  B0 20       mov al,20h
+0CB4  B4 02       mov ah,2h
+0CB6  CD 21       int 21h
+0CB8  E2 F8       loop 0cb2h
+0CBA  BE 4D 0E    mov si,0e4dh
+0CBD  8B 0E 8D 0E mov cx,word ptr [0e8dh]
+0CC1  E3 09       jcxz 0ccch
+0CC3  8A 04       mov al,byte ptr [si]
+0CC5  B4 02       mov ah,2h
+0CC7  CD 21       int 21h
+0CC9  46          inc si
+0CCA  E2 F7       loop 0cc3h
+0CCC  5F          pop di
+0CCD  5E          pop si
+0CCE  5A          pop dx
+0CCF  5B          pop bx
+0CD0  89 EC       mov sp,bp
+0CD2  5D          pop bp
+0CD3  C3          ret