Prechádzať zdrojové kódy

runtime: MBF comparison and Int (recmp, reint)

Two new entries, both direct TPSRC3 transcriptions:

  * real_cmp (entry 26, recmp).  [BP+4] is the arithmetic block (a then
    b); nothing is written; AX := -1/0/+1, the same convention cmpstr
    returns, so the compiler keeps one comparison arm for reals and
    strings.  The reference's flag-setting core is intact: different signs
    are decided by RCL on a's high word, and two negatives invert the
    magnitude result.  Thirty rt_exec cases.

  * real_int (entry 27, reint).  [BP+4] is the real, transformed in place.
    Int(real) is a REAL, not an integer: the result stays six bytes.  The
    mask-and-AND works on the packed bytes directly (fraction cleared,
    sign bit and implicit leading 1 kept); exponents >= 0A8h are already
    integral, <= 80h give zero.  Eighteen cases, incl. 12345.75 -> 12345.

The harness can now observe a return value: rtdrv.s stores every call's
AX at D_AX (0x0AA6), and a case can dump that word.  Until now cmpstr's
sign was only pinned by whole programs.

Runtime 1833 -> 2059 bytes, 28 entries.  rt_exec 191/191.  The golden is
re-blessed and the audit covers the new emitters.  Non-vacuity 113/113:
XorDxDi -> XorDxSi and the 0A8h cutoff -> 088h must each turn a real case
red.  run_all ALL PASS.

Headroom: RT_BASE 0x190 and HDR 0x0A00 cap the runtime at 2160 bytes; the
next real rung will need another scaffold shift.
Eric Streit 1 deň pred
rodič
commit
3409f8aac5

+ 31 - 8
CALC-COMPILE.md

@@ -552,11 +552,11 @@ Exec86):
   **OVERALL: ALL PASS**, compile matrix **83/83**, CALC ladder **1/1** at
   **OVERALL: ALL PASS**, compile matrix **83/83**, CALC ladder **1/1** at
   `ERR 102 18267`, terminal config **4/4**, 64 `.COM` linked and **72 executed
   `ERR 102 18267`, terminal config **4/4**, 64 `.COM` linked and **72 executed
   under both qemu and Exec86** (`t64_arrays` is compile-only: its image is past
   under both qemu and Exec86** (`t64_arrays` is compile-only: its image is past
-  the 8 KB boot-sector read).  `tests/nonvacuity.sh`: **111 ok, 0 failed**.
-  The runtime is 1833 bytes (wrintw, real_from_int, real_add, real_sub,
-  real_mul, real_div; the golden was re-blessed) and `tests/rt_exec.py`
-  passes **143/143**, including 16 add/sub, ten multiply and fourteen
-  division exact-byte cases.
+  the 8 KB boot-sector read).  `tests/nonvacuity.sh`: **113 ok, 0 failed**.
+  The runtime is 2059 bytes (wrintw, real_from_int, real_add, real_sub,
+  real_mul, real_div, real_cmp, real_int; the golden was re-blessed) and
+  `tests/rt_exec.py` passes **191/191**, including 16 add/sub, ten multiply,
+  fourteen division, thirty comparison and eighteen Int exact-byte cases.
   `check_realconst.py` pins the measured 6-byte Real operand bytes.
   `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
@@ -701,9 +701,32 @@ Then the two that are *outside* the ladder and gate the goal as stated:
             normal compare path has CF=0, which hid it in 13 of 14 cases.
             normal compare path has CF=0, which hid it in 13 of 14 cases.
           - the runtime outgrew the probe scaffold: rtdrv.s/rt_exec.py moved
           - the runtime outgrew the probe scaffold: rtdrv.s/rt_exec.py moved
             their records and blocks up 0x200 (HDR=0x0A00, blocks 0x0B30+).
             their records and blocks up 0x200 (HDR=0x0A00, blocks 0x0B30+).
-      * **NEXT:** `recmp` (the six real comparisons), `reint`, then
-        `fmtreal`/`ascreal` for `Write(Value:FW:DEC)`, `Str` and `Val`.
-        Each is a direct transcription with rt_exec cases.
+      * **DONE: `real_cmp`** (`v-TP3-MBF-CMPINT`, entry 26).  TPSRC3 `recmp`.
+        ABI: [BP+4] is the same 12-byte block as the arithmetic entries;
+        nothing is written; the result is AX = -1/0/+1, cmpstr's convention,
+        so the compiler has ONE comparison arm for reals and strings.  The
+        reference's core keeps the flags; the last step is the untested
+        flag-to-AX conversion.  Different signs are decided by RCL on a's
+        high word (the sign lands in CF); two negatives invert the magnitude
+        result with CMC.  Thirty rt_exec cases.
+        The harness gained an observable AX: rtdrv.s stores every call's AX
+        at D_AX (0x0AA6), so a case can dump it.  Until now cmpstr's return
+        sign was only checked by whole programs.
+      * **DONE: `real_int`** (`v-TP3-MBF-CMPINT`, entry 27).  TPSRC3 `reint`,
+        in place at [BP+4].  It is NOT integer-valued: `Int(real)` returns a
+        REAL, so the result stays six bytes.  The reference works directly on
+        the packed bytes (no hidden-bit setup): it builds a mask by shifting
+        1s right by `exponent - 129` and ANDs the mantissa, which clears the
+        fraction and leaves the sign bit and the implicit leading 1 alone.
+        Exponents >= 0A8h (2^39) are already integral and return unchanged;
+        <= 80h give zero.  Eighteen rt_exec cases, including 12345.75 -> 12345
+        and 2^40 unchanged.
+      * **NEXT:** `fmtreal`/`ascreal` for `Write(Value:FW:DEC)`, `Str` and
+        `Val`.  Each is a direct transcription with rt_exec cases.
+      * **HEADROOM:** the runtime is 2059 bytes and the probe's scaffold now
+        starts at HDR=0x0A00 with RT_BASE=0x0190, so the ceiling is 2160
+        bytes - about 100 bytes left.  The next real rung will need another
+        shift of rtdrv.s/rt_exec.py, or a larger probe image.
       * **THEN** the compiler side: real expressions need a value model
       * **THEN** the compiler side: real expressions need a value model
         (pointers to 6-byte temporaries), real comparisons, and real function
         (pointers to 6-byte temporaries), real comparisons, and real function
         results (currently refused because the result is wider than a word).
         results (currently refused because the result is wider than a word).

+ 2 - 0
shell/Runtime.def

@@ -58,6 +58,8 @@ CONST
    E_RealSub = 23 ;         (* ...and subtraction: a - b *)
    E_RealSub = 23 ;         (* ...and subtraction: a - b *)
    E_RealMul = 24 ;         (* ...and multiplication: a * b *)
    E_RealMul = 24 ;         (* ...and multiplication: a * b *)
    E_RealDiv = 25 ;         (* ...and division: a / b *)
    E_RealDiv = 25 ;         (* ...and division: a / b *)
+   E_RealCmp = 26 ;         (* real comparison: AX = -1/0/+1 for a<b, a=b, a>b *)
+   E_RealInt = 27 ;         (* Int(real): truncate the mantissa in place *)
 
 
    (* 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

+ 132 - 2
shell/Runtime.mod

@@ -139,7 +139,7 @@ VAR
    dataAt : CARDINAL ;
    dataAt : CARDINAL ;
    rtBase : CARDINAL ;
    rtBase : CARDINAL ;
    built  : BOOLEAN ;
    built  : BOOLEAN ;
-   entNm  : ARRAY [0..25] OF ARRAY [0..MaxNm] OF CHAR ;
+   entNm  : ARRAY [0..27] OF ARRAY [0..MaxNm] OF CHAR ;
 
 
 (* ---------------------------------------------------------------- *)
 (* ---------------------------------------------------------------- *)
 (*  name helpers                                                     *)
 (*  name helpers                                                     *)
@@ -648,7 +648,16 @@ PROCEDURE RclCh1 ; BEGIN B (0D0H) ; B (0D5H) END RclCh1 ;   (* RCL CH,1 *)
 PROCEDURE Lahf ; BEGIN B (9FH) END Lahf ;
 PROCEDURE Lahf ; BEGIN B (9FH) END Lahf ;
 PROCEDURE Sahf ; BEGIN B (9EH) END Sahf ;
 PROCEDURE Sahf ; BEGIN B (9EH) END Sahf ;
 PROCEDURE XorCxDi ; BEGIN B (31H) ; B (0F9H) END XorCxDi ;   (* CX:=CX XOR DI *)
 PROCEDURE XorCxDi ; BEGIN B (31H) ; B (0F9H) END XorCxDi ;   (* CX:=CX XOR DI *)
+PROCEDURE XorDxDi ; BEGIN B (31H) ; B (0FAH) END XorDxDi ;   (* DX:=DX XOR DI *)
 PROCEDURE NotCh ; BEGIN B (0F6H) ; B (0D5H) END NotCh ;     (* NOT CH *)
 PROCEDURE NotCh ; BEGIN B (0F6H) ; B (0D5H) END NotCh ;     (* NOT CH *)
+PROCEDURE AndDxDi ; BEGIN B (21H) ; B (0FAH) END AndDxDi ;   (* AND DX,DI *)
+PROCEDURE AndBxSi ; BEGIN B (21H) ; B (0F3H) END AndBxSi ;   (* AND BX,SI *)
+PROCEDURE AndAhCh ; BEGIN B (20H) ; B (0ECH) END AndAhCh ;   (* AND AH,CH *)
+PROCEDURE CmpDxDi ; BEGIN B (39H) ; B (0FAH) END CmpDxDi ;   (* CMP DX,DI *)
+PROCEDURE CmpBxSi ; BEGIN B (39H) ; B (0F3H) END CmpBxSi ;   (* CMP BX,SI *)
+PROCEDURE CmpAhCh ; BEGIN B (38H) ; B (0ECH) END CmpAhCh ;   (* CMP AH,CH *)
+PROCEDURE MovDhImm (v : CARDINAL) ;
+BEGIN B (0B6H) ; B (v MOD 256) END MovDhImm ;                (* MOV DH,imm8 *)
 PROCEDURE AndChImm (v : CARDINAL) ;
 PROCEDURE AndChImm (v : CARDINAL) ;
 BEGIN B (80H) ; B (0E5H) ; B (v MOD 256) END AndChImm ;
 BEGIN B (80H) ; B (0E5H) ; B (v MOD 256) END AndChImm ;
 PROCEDURE MovDiData (delta : CARDINAL) ;
 PROCEDURE MovDiData (delta : CARDINAL) ;
@@ -1985,6 +1994,124 @@ BEGIN
    JmpNear ("rm_noadj")
    JmpNear ("rm_noadj")
 END EmitRealMul ;
 END EmitRealMul ;
 
 
+PROCEDURE EmitRealCmp ;
+(* recmp: compare two packed 6-byte MBF reals.
+
+   ABI: [BP+4] points at a 12-byte block holding a then b (the same block the
+   four arithmetic entries take).  Nothing is written; the result is AX =
+   -1/0/+1 for a<b, a=b, a>b.
+
+   TP3's recmp leaves the flags set and its six wrappers branch on them.  Here
+   the caller is our compiler, which already has one comparison arm for reals
+   and strings: EmSetcc over AX.  cmpstr returns the same -1/0/+1, so the two
+   cannot drift.  The reference's core is kept intact; only the final
+   flag-to-AX step is new, and it is cmpstr's: MOV AX,0 leaves the flags alone,
+   then JB/JE/MOV decide.
+
+   The comparison is on the packed representation: the sign is bit 7 of each
+   high byte, the magnitude is exponent then DX/BX/AH (TP3's 40-bit order), and
+   a negative pair inverts the magnitude result.  Different signs are decided
+   by RCL on a's high word, which shifts the sign into CF - exactly TPSRC's
+   trick, and it leaves ZF clear because the XOR found the signs different. *)
+BEGIN
+   M ("real_cmp") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   MovSiBp4 ;
+   LdAxSi ;
+   LdCxSiOff (2) ; LdDxSiOff (4) ;
+   MovBxCx ;
+   MovDiSi ;
+   LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
+   (* recmp core *)
+   PushDx ; XorDxDi ; PopDx ;
+   Jns8 ("rc_sign") ;
+   PushDx ; RclDx1 ; PopDx ;
+   Jmp8 ("rc_flags") ;
+   M ("rc_sign") ;
+   TestDhImm (80H) ;
+   Je8 ("rc_pos") ;
+   Call ("rc_num") ;
+   Je8 ("rc_flags") ;
+   Cmc ;
+   Jmp8 ("rc_flags") ;
+   M ("rc_pos") ;
+   Call ("rc_num") ;
+   M ("rc_flags") ;
+   MovAlD (0) ; MovAh (0) ;          (* AX := 0; MOV leaves the flags alone *)
+   Jb8 ("rc_less") ;
+   Je8 ("rc_done") ;
+   MovAlD (1) ;
+   Jmp8 ("rc_done") ;
+   M ("rc_less") ;
+   MovAlD (0FFH) ; MovAh (0FFH) ;
+   M ("rc_done") ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR ;
+   (* the shared magnitude compare; CALL/RET do not touch the flags *)
+   M ("rc_num") ;
+   CmpAlCl ; Jne8 ("rc_num_ret") ;
+   CmpAl0 ; Je8 ("rc_num_ret") ;
+   CmpDxDi ; Jne8 ("rc_num_ret") ;
+   CmpBxSi ; Jne8 ("rc_num_ret") ;
+   CmpAhCh ;
+   M ("rc_num_ret") ;
+   RetR
+END EmitRealCmp ;
+
+PROCEDURE EmitRealInt ;
+(* reint: Int(real), the real-valued integer part, in place.
+
+   ABI: [BP+4] is the address of a 6-byte packed MBF real; the result
+   overwrites it.  TPSRC3's reint works directly on the packed bytes: it builds
+   a mask by shifting 1s right by (exponent - 129) bits and ANDs it with the
+   mantissa, which clears the fraction while keeping the sign bit and the
+   implicit leading 1.  Exponents >= 0A8h are already integral and returned
+   unchanged; exponents <= 80h give zero.
+
+   The 16/8-bit fast paths are the reference's, including the byte moves that
+   shift the mask instead of running the bit loop.  Only the wrapper is ours
+   (the caller passes an address rather than the value in registers). *)
+BEGIN
+   M ("real_int") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   PushBp4 ;
+   MovSiBp4 ;
+   LdAxSi ;
+   LdCxSiOff (2) ; LdDxSiOff (4) ;
+   MovBxCx ;
+   (* reint core *)
+   CmpAl (0A8H) ;
+   Jae8 ("ri_store") ;               (* already integral: return as-is *)
+   MovCxAx ; MovSiBx ; MovDiDx ;     (* save the packed mantissa for the mask *)
+   XorAhAh ; XorBxBx ; XorDxDx ;     (* build the mask in AH/BX/DX *)
+   SubClImm (80H) ;                  (* CL := exponent - 129 + 1 *)
+   Jbe8 ("ri_zero") ;
+   M ("ri_16") ;
+   CmpClImm (10H) ; Jb8 ("ri_8") ;
+   MovAhBh ; MovBxDx ; MovDxImm (0FFFFH) ;
+   SubClImm (10H) ; Jmp8 ("ri_16") ;
+   M ("ri_8") ;
+   CmpClImm (08H) ; Jb8 ("ri_1") ;
+   MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; MovDhImm (0FFH) ;
+   SubClImm (08H) ; Jmp8 ("ri_8") ;
+   M ("ri_1") ;
+   CmpCl0 ; Je8 ("ri_and") ;
+   M ("ri_1l") ;
+   Stc ; RcrDx1 ; RcrBx1 ; RcrAh1 ; DecCl ; Jne8 ("ri_1l") ;
+   M ("ri_and") ;
+   AndDxDi ; AndBxSi ; AndAhCh ;
+   M ("ri_store") ;
+   PopDi ;
+   StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR ;
+   M ("ri_zero") ;
+   MovAlD (0) ;
+   Jmp8 ("ri_store")
+END EmitRealInt ;
+
 PROCEDURE EmitData ;
 PROCEDURE EmitData ;
 (* The runtime's data block.  The D_ constants name the offsets in it and are
 (* The runtime's data block.  The D_ constants name the offsets in it and are
    ASSERTED against what is emitted here, three ways: the starting offset of
    ASSERTED against what is emitted here, three ways: the starting offset of
@@ -2126,6 +2253,7 @@ BEGIN
    EmitGetCh ; EmitUnGetCh ;
    EmitGetCh ; EmitUnGetCh ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
    EmitRealFromInt ; EmitRealAddSub ; EmitRealMul ;
    EmitRealFromInt ; EmitRealAddSub ; EmitRealMul ;
+   EmitRealCmp ; EmitRealInt ;
    EmitData ;
    EmitData ;
    FixUp ;
    FixUp ;
    SetStr (entNm [0], "initmem") ;
    SetStr (entNm [0], "initmem") ;
@@ -2154,6 +2282,8 @@ BEGIN
    SetStr (entNm [23], "real_sub") ;
    SetStr (entNm [23], "real_sub") ;
    SetStr (entNm [24], "real_mul") ;
    SetStr (entNm [24], "real_mul") ;
    SetStr (entNm [25], "real_div") ;
    SetStr (entNm [25], "real_div") ;
+   SetStr (entNm [26], "real_cmp") ;
+   SetStr (entNm [27], "real_int") ;
    built := TRUE
    built := TRUE
 END RT_Build ;
 END RT_Build ;
 
 
@@ -2181,7 +2311,7 @@ BEGIN
    IF NOT built THEN
    IF NOT built THEN
       RT_Build (0)
       RT_Build (0)
    END ;
    END ;
-   IF i > 25 THEN
+   IF i > 27 THEN
       RETURN 0
       RETURN 0
    END ;
    END ;
    RETURN rtBase + LblOff (entNm [i])
    RETURN rtBase + LblOff (entNm [i])

+ 5 - 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..25] OF ARRAY [0..15] OF CHAR ;
+    names : ARRAY [0..27] 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 <= 25 DO
+   WHILE i <= 27 DO
       names [i] := "" ;
       names [i] := "" ;
       INC (i)
       INC (i)
    END ;
    END ;
@@ -138,8 +138,10 @@ BEGIN
    names [23] := "real_sub" ;
    names [23] := "real_sub" ;
    names [24] := "real_mul" ;
    names [24] := "real_mul" ;
    names [25] := "real_div" ;
    names [25] := "real_div" ;
+   names [26] := "real_cmp" ;
+   names [27] := "real_int" ;
    i := 0 ;
    i := 0 ;
-   WHILE i <= 25 DO
+   WHILE i <= 27 DO
       PENT (i, names [i]) ;
       PENT (i, names [i]) ;
       INC (i)
       INC (i)
    END ;
    END ;

+ 1 - 0
shell/tests/audit_helpers.py

@@ -134,6 +134,7 @@ NON_ONE_LINE = {
         "SubAhData": COMP, "StDataAl": COMP,
         "SubAhData": COMP, "StDataAl": COMP,
         "MovBxImm": PARAM, "MovCxImm": PARAM, "MovDxImm": PARAM,
         "MovBxImm": PARAM, "MovCxImm": PARAM, "MovDxImm": PARAM,
         "MovDl": PARAM, "MovAlD": PARAM, "MovAh": PARAM,
         "MovDl": PARAM, "MovAlD": PARAM, "MovAh": PARAM,
+        "MovDhImm": PARAM,
         "SubAl": PARAM, "CmpAl": PARAM, "AddDl": PARAM, "J8": COMP,
         "SubAl": PARAM, "CmpAl": PARAM, "AddDl": PARAM, "J8": COMP,
         "Jcc": COMP, "CmpCxV": PARAM,
         "Jcc": COMP, "CmpCxV": PARAM,
         "B": DATA, "C8": DATA,
         "B": DATA, "C8": DATA,

+ 2 - 0
shell/tests/check_runtime.py

@@ -137,6 +137,8 @@ GOLDEN = {
     "real_sub": "55 8B EC 53 52 56 57",
     "real_sub": "55 8B EC 53 52 56 57",
     "real_mul": "55 8B EC 53 52 56 57",
     "real_mul": "55 8B EC 53 52 56 57",
     "real_div": "55 8B EC 53 52 56 57",
     "real_div": "55 8B EC 53 52 56 57",
+    "real_cmp": "55 8B EC 53 52 56 57",
+    "real_int": "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

+ 39 - 0
shell/tests/exec/rtdrv.s

@@ -102,6 +102,7 @@
 	.set	D_DUMPLEN, 0x0AA0	# the driver's own three words
 	.set	D_DUMPLEN, 0x0AA0	# the driver's own three words
 	.set	D_DUMPADR, 0x0AA2
 	.set	D_DUMPADR, 0x0AA2
 	.set	D_CNT, 0x0AA4
 	.set	D_CNT, 0x0AA4
+	.set	D_AX, 0x0AA6		# last call's AX, for the comparison cases
 	.set	RDSTR_DST, 0x0AB0	# destination for the rdstr cases
 	.set	RDSTR_DST, 0x0AB0	# destination for the rdstr cases
 	.set	SST_HELLO, 0x0AC0	# a short source string for strstore
 	.set	SST_HELLO, 0x0AC0	# a short source string for strstore
 	.set	SST_LONG, 0x0AD0	# a source longer than a small max
 	.set	SST_LONG, 0x0AD0	# a source longer than a small max
@@ -132,6 +133,17 @@
 	.set	R_BLK18, 0x0C40
 	.set	R_BLK18, 0x0C40
 	.set	R_BLK19, 0x0C50
 	.set	R_BLK19, 0x0C50
 	.set	R_BLK20, 0x0C60
 	.set	R_BLK20, 0x0C60
+	.set	R_BLK21, 0x0C70
+	.set	R_BLK22, 0x0C80
+	.set	R_BLK23, 0x0C90
+	.set	R_BLK24, 0x0CA0
+	.set	R_BLK25, 0x0CB0
+	.set	R_BLK26, 0x0CC0
+	.set	R_BLK27, 0x0CD0
+	.set	R_BLK28, 0x0CE0
+	.set	R_BLK29, 0x0CF0
+	.set	R_BLK30, 0x0D00
+	.set	R_BLK31, 0x0D10
 
 
 	.code16
 	.code16
 	.text
 	.text
@@ -273,6 +285,7 @@ doslot:
 	testb	$4, %al
 	testb	$4, %al
 	jnz	.Linl
 	jnz	.Linl
 	call	*%si
 	call	*%si
+	movw	%ax, D_AX + LOAD_BIAS	# observable return value
 	movb	4(%bp), %al
 	movb	4(%bp), %al
 	testb	$8, %al
 	testb	$8, %al
 	jnz	.Lcleantwo
 	jnz	.Lcleantwo
@@ -475,3 +488,29 @@ HDR:
 	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
 	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
 	.org	R_BLK20
 	.org	R_BLK20
 	.byte	0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x83, 0x00, 0x00, 0x00, 0x00, 0x40
 	.byte	0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x83, 0x00, 0x00, 0x00, 0x00, 0x40
+
+# Fractional blocks: real_int truncates `a'; real_cmp compares the pair.
+	.org	R_BLK21
+	.byte	0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0x6C, 0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0x6C
+	.org	R_BLK22
+	.byte	0x82, 0x9A, 0x99, 0x99, 0x99, 0x79, 0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0x6C
+	.org	R_BLK23
+	.byte	0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0xEC, 0x82, 0x9A, 0x99, 0x99, 0x99, 0xF9
+	.org	R_BLK24
+	.byte	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK25
+	.byte	0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK26
+	.byte	0x8E, 0x00, 0x00, 0x00, 0xE7, 0x40, 0x8E, 0x00, 0x00, 0x00, 0xE6, 0x40
+	.org	R_BLK27
+	.byte	0x82, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK28
+	.byte	0x82, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK29
+	.byte	0x80, 0x0A, 0xD7, 0xA3, 0x70, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK30
+	.byte	0x81, 0x33, 0x33, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+
+# 2^40: above reint's 0A8h early return, so it must come back unchanged.
+	.org	R_BLK31
+	.byte	0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

+ 33 - 0
shell/tests/nonvacuity.sh

@@ -1441,6 +1441,39 @@ fi
 cp "$SAVED" Runtime.mod
 cp "$SAVED" Runtime.mod
 "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
 "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
 
 
+# --- recmp compares b's SIGN, not its middle word -----------------------
+# The different-sign path is decided by RCL on a's high word, but WHICH pair is
+# different is decided by XOR DX,DI.  XORing with SI (b's middle mantissa word)
+# still sees a's sign, so two negatives are taken as different signs: -3.7 and
+# -3.9 then compare backwards.  The fractional real_cmp cases catch it.
+if mutate Runtime.mod 's|BEGIN B (31H) ; B (0FAH) END XorDxDi|BEGIN B (31H) ; B (0F2H) END XorDxDi|'; then
+    if "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1; then
+        expect_red "real_cmp red when the sign XOR uses SI instead of DI" \
+            "real_cmp" python3 tests/rt_exec.py
+    else
+        echo "  FAIL: the runtime would not rebuild with the XorDxDi mutation"
+        fail=$((fail + 1))
+    fi
+fi
+cp "$SAVED" Runtime.mod
+"$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
+
+# --- reint must not skip the mask for ordinary large reals ---------------
+# 0A8h is 2^39, above which every representable value is already integral.
+# Lowering it to 088h (256) makes 12345.75 come back unchanged instead of
+# 12345: the fraction survives, and the exact-byte real_int case says so.
+if mutate Runtime.mod 's|CmpAl (0A8H)|CmpAl (088H)|'; then
+    if "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1; then
+        expect_red "real_int red when the integral short-circuit starts too low" \
+            "real_int" python3 tests/rt_exec.py
+    else
+        echo "  FAIL: the runtime would not rebuild with the CmpAl mutation"
+        fail=$((fail + 1))
+    fi
+fi
+cp "$SAVED" Runtime.mod
+"$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
+
 # --- the compiler's arms --------------------------------------------------
 # --- the compiler's arms --------------------------------------------------
 # The limit both entries read is MOV CX,imm16.  MOV AX,imm16 is one opcode away,
 # The limit both entries read is MOV CX,imm16.  MOV AX,imm16 is one opcode away,
 # the same length, and the audit's MovCxi row is COMP - "no single sequence to
 # the same length, and the audit's MovCxi row is COMP - "no single sequence to

+ 56 - 1
shell/tests/rt_exec.py

@@ -42,6 +42,7 @@ readln that printed anything would change the same compared string, so the
 assertion is unchanged - one qemu boot per check instead of two.
 assertion is unchanged - one qemu boot per check instead of two.
 """
 """
 
 
+import math
 import os
 import os
 import re
 import re
 import struct
 import struct
@@ -78,6 +79,7 @@ SST_DST = 0x0AF0
 SST_CAT = 0x0B00
 SST_CAT = 0x0B00
 SST_CAT0 = 0x0B10
 SST_CAT0 = 0x0B10
 SST_WORLD = 0x0B20
 SST_WORLD = 0x0B20
+AXOUT = 0x0AA6          # the driver stores the last call's AX here
 
 
 # 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
 # offset.  The load bias is therefore the difference between the two, and
 # offset.  The load bias is therefore the difference between the two, and
@@ -104,7 +106,7 @@ 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_div", "real_cmp", "real_int"]
 
 
 # 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
@@ -527,6 +529,59 @@ def build_cases():
                 dump=6, dumpbytes=q, dumpadr=blk,
                 dump=6, dumpbytes=q, dumpadr=blk,
                 note="the measured MBF bytes, the reference's rediv")
                 note="the measured MBF bytes, the reference's rediv")
 
 
+    # --- recmp: AX := -1/0/+1 for a<b, a=b, a>b ------------------------
+    # The driver stores every call's AX at AXOUT, so these cases can read the
+    # result.  The operand block is the same one the arithmetic cases use.
+    seen = set()
+    for bi, (a, b) in enumerate(BLOCKS):
+        if (a, b) in seen:
+            continue
+        seen.add((a, b))
+        blk = 0x0B30 + 16 * bi
+        want = (a > b) - (a < b)
+        add("real_cmp(%d,%d)" % (a, b),
+            [s("real_cmp", blk + LOAD_BIAS)],
+            dump=2, dumpbytes=struct.pack("<h", want), dumpadr=AXOUT,
+            note="the reference's recmp, returned as AX = -1/0/+1")
+
+    # Fractional blocks, compared through the exponent/mantissa path.
+    FRAC = [(3.7, 3.7), (3.9, 3.7), (-3.7, -3.9), (0.5, 0.25),
+            (-0.5, 0.5), (12345.75, 12345.5), (2.5, 0.0), (-2.5, 0.0),
+            (0.99, 0.0), (1.9, 0.0)]
+    for fi, (a, b) in enumerate(FRAC):
+        blk = 0x0C70 + 16 * fi
+        want = (a > b) - (a < b)
+        add("real_cmp(%g,%g)" % (a, b),
+            [s("real_cmp", blk + LOAD_BIAS)],
+            dump=2, dumpbytes=struct.pack("<h", want), dumpadr=AXOUT,
+            note="fractional magnitudes: the mantissa decides")
+    add("real_cmp(2^40,0)",
+        [s("real_cmp", 0x0D10 + LOAD_BIAS)],
+        dump=2, dumpbytes=struct.pack("<h", 1), dumpadr=AXOUT,
+        note="the exponent alone decides")
+
+    # --- reint: Int(real), in place --------------------------------------
+    # a is the real; the block's b is unused.  The expected bytes are the
+    # measured MBF of trunc(a), because reint returns a REAL, not an integer
+    # (TPSRC's xint pushes the six-byte result back).
+    for bi, (a, b) in enumerate(BLOCKS):
+        if a in (0, 1, -3, 32767):
+            blk = 0x0B30 + 16 * bi
+            add("real_int(%d)" % a,
+                [s("real_int", blk + LOAD_BIAS)],
+                dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
+                note="the integer part as a real, the reference's reint")
+    for fi, (a, b) in enumerate(FRAC):
+        blk = 0x0C70 + 16 * fi
+        add("real_int(%g)" % a,
+            [s("real_int", blk + LOAD_BIAS)],
+            dump=6, dumpbytes=mbf(math.trunc(a)), dumpadr=blk,
+            note="the fraction is masked away; the sign and exponent stay")
+    add("real_int(2^40)",
+        [s("real_int", 0x0D10 + LOAD_BIAS)],
+        dump=6, dumpbytes=bytes.fromhex("A90000000000"), dumpadr=0x0D10,
+        note="exponent >= 0A8h is already integral and returns unchanged")
+
     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"!")
 
 

+ 172 - 55
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 02 08    mov bx,802h
+003C  BB E4 08    mov bx,8e4h
 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 02 08    mov bx,802h
+00A8  BB E4 08    mov bx,8e4h
 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 07 08    mov dx,807h
+00E6  BA E9 08    mov dx,8e9h
 00E9  EB 03       jmp 0eeh
 00E9  EB 03       jmp 0eeh
-00EB  BA 02 08    mov dx,802h
+00EB  BA E4 08    mov dx,8e4h
 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 10 08    mov dx,810h
+00F4  BA F2 08    mov dx,8f2h
 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 0D 08    mov dx,80dh
+00FC  BA EF 08    mov dx,8efh
 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 16 08 mov bx,word ptr [816h]
+021B  8B 1E F8 08 mov bx,word ptr [8f8h]
 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 16 08 mov word ptr [816h],bx
+0228  89 1E F8 08 mov word ptr [8f8h],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 16 08 mov word ptr [816h],bx
+0239  89 1E F8 08 mov word ptr [8f8h],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
@@ -541,7 +541,7 @@
 03BE  8B 4D 06    mov cx,word ptr [di+6h]
 03BE  8B 4D 06    mov cx,word ptr [di+6h]
 03C1  8B 75 08    mov si,word ptr [di+8h]
 03C1  8B 75 08    mov si,word ptr [di+8h]
 03C4  8B 7D 0A    mov di,word ptr [di+0ah]
 03C4  8B 7D 0A    mov di,word ptr [di+0ah]
-03C7  C7 06 18 08 00 00 mov word ptr [818h],0h
+03C7  C7 06 FA 08 00 00 mov word ptr [8fah],0h
 03CD  E8 4D 00    call 41dh
 03CD  E8 4D 00    call 41dh
 03D0  5F          pop di
 03D0  5F          pop di
 03D1  89 05       mov word ptr [di],ax
 03D1  89 05       mov word ptr [di],ax
@@ -570,7 +570,7 @@
 03FA  8B 4D 06    mov cx,word ptr [di+6h]
 03FA  8B 4D 06    mov cx,word ptr [di+6h]
 03FD  8B 75 08    mov si,word ptr [di+8h]
 03FD  8B 75 08    mov si,word ptr [di+8h]
 0400  8B 7D 0A    mov di,word ptr [di+0ah]
 0400  8B 7D 0A    mov di,word ptr [di+0ah]
-0403  C7 06 18 08 00 80 mov word ptr [818h],8000h
+0403  C7 06 FA 08 00 80 mov word ptr [8fah],8000h
 0409  E8 11 00    call 41dh
 0409  E8 11 00    call 41dh
 040C  5F          pop di
 040C  5F          pop di
 040D  89 05       mov word ptr [di],ax
 040D  89 05       mov word ptr [di],ax
@@ -585,7 +585,7 @@
 041C  C3          ret
 041C  C3          ret
 041D  80 F9 00    cmp cl,0h
 041D  80 F9 00    cmp cl,0h
 0420  74 0E       je 430h
 0420  74 0E       je 430h
-0422  33 3E 18 08 xor di,word ptr [818h]
+0422  33 3E FA 08 xor di,word ptr [8fah]
 0426  3C 00       cmp al,0h
 0426  3C 00       cmp al,0h
 0428  75 07       jne 431h
 0428  75 07       jne 431h
 042A  89 C8       mov ax,cx
 042A  89 C8       mov ax,cx
@@ -597,16 +597,16 @@
 0435  91          xchg ax,cx
 0435  91          xchg ax,cx
 0436  87 F3       xchg bx,si
 0436  87 F3       xchg bx,si
 0438  87 FA       xchg dx,di
 0438  87 FA       xchg dx,di
-043A  88 0E 1C 08 mov byte ptr [81ch],cl
+043A  88 0E FE 08 mov byte ptr [8feh],cl
 043E  28 C1       sub cl,al
 043E  28 C1       sub cl,al
 0440  80 F9 28    cmp cl,28h
 0440  80 F9 28    cmp cl,28h
 0443  72 06       jb 44bh
 0443  72 06       jb 44bh
-0445  8A 0E 1C 08 mov cl,byte ptr [81ch]
+0445  8A 0E FE 08 mov cl,byte ptr [8feh]
 0449  EB DF       jmp 42ah
 0449  EB DF       jmp 42ah
-044B  89 3E 18 08 mov word ptr [818h],di
-044F  80 26 19 08 80 and byte ptr [819h],80h
-0454  89 3E 1A 08 mov word ptr [81ah],di
-0458  30 36 1B 08 xor byte ptr [81bh],dh
+044B  89 3E FA 08 mov word ptr [8fah],di
+044F  80 26 FB 08 80 and byte ptr [8fbh],80h
+0454  89 3E FC 08 mov word ptr [8fch],di
+0458  30 36 FD 08 xor byte ptr [8fdh],dh
 045C  81 CF 00 80 or di,8000h
 045C  81 CF 00 80 or di,8000h
 0460  80 CE 80    or dh,80h
 0460  80 CE 80    or dh,80h
 0463  80 F9 10    cmp cl,10h
 0463  80 F9 10    cmp cl,10h
@@ -631,8 +631,8 @@
 048E  D0 DC       rcr ah,1h
 048E  D0 DC       rcr ah,1h
 0490  FE C9       dec cl
 0490  FE C9       dec cl
 0492  75 F6       jne 48ah
 0492  75 F6       jne 48ah
-0494  A0 1C 08    mov al,byte ptr [81ch]
-0497  F6 06 1B 08 80 test byte ptr [81bh],80h
+0494  A0 FE 08    mov al,byte ptr [8feh]
+0497  F6 06 FD 08 80 test byte ptr [8fdh],80h
 049C  75 14       jne 4b2h
 049C  75 14       jne 4b2h
 049E  00 EC       add ah,ch
 049E  00 EC       add ah,ch
 04A0  11 F3       adc bx,si
 04A0  11 F3       adc bx,si
@@ -652,7 +652,7 @@
 04BA  19 F3       sbb bx,si
 04BA  19 F3       sbb bx,si
 04BC  19 FA       sbb dx,di
 04BC  19 FA       sbb dx,di
 04BE  73 14       jnb 4d4h
 04BE  73 14       jnb 4d4h
-04C0  80 36 19 08 80 xor byte ptr [819h],80h
+04C0  80 36 FB 08 80 xor byte ptr [8fbh],80h
 04C5  F6 D4       not ah
 04C5  F6 D4       not ah
 04C7  F7 D3       not bx
 04C7  F7 D3       not bx
 04C9  F7 D2       not dx
 04C9  F7 D2       not dx
@@ -684,7 +684,7 @@
 0502  31 D2       xor dx,dx
 0502  31 D2       xor dx,dx
 0504  C3          ret
 0504  C3          ret
 0505  80 E6 7F    and dh,7fh
 0505  80 E6 7F    and dh,7fh
-0508  32 36 19 08 xor dh,byte ptr [819h]
+0508  32 36 FB 08 xor dh,byte ptr [8fbh]
 050C  C3          ret
 050C  C3          ret
 050D  55          push bp
 050D  55          push bp
 050E  8B EC       mov bp,sp
 050E  8B EC       mov bp,sp
@@ -730,23 +730,23 @@
 0561  B0 01       mov al,1h
 0561  B0 01       mov al,1h
 0563  B4 4C       mov ah,4ch
 0563  B4 4C       mov ah,4ch
 0565  CD 21       int 21h
 0565  CD 21       int 21h
-0567  89 0E 23 08 mov word ptr [823h],cx
+0567  89 0E 05 09 mov word ptr [905h],cx
 056B  8B CA       mov cx,dx
 056B  8B CA       mov cx,dx
 056D  31 F9       xor cx,di
 056D  31 F9       xor cx,di
 056F  F6 D5       not ch
 056F  F6 D5       not ch
 0571  80 E5 80    and ch,80h
 0571  80 E5 80    and ch,80h
-0574  88 2E 19 08 mov byte ptr [819h],ch
+0574  88 2E FB 08 mov byte ptr [8fbh],ch
 0578  80 CE 80    or dh,80h
 0578  80 CE 80    or dh,80h
 057B  81 CF 00 80 or di,8000h
 057B  81 CF 00 80 or di,8000h
-057F  89 36 25 08 mov word ptr [825h],si
-0583  89 3E 27 08 mov word ptr [827h],di
-0587  A3 1D 08    mov word ptr [81dh],ax
-058A  89 1E 1F 08 mov word ptr [81fh],bx
-058E  89 16 21 08 mov word ptr [821h],dx
+057F  89 36 07 09 mov word ptr [907h],si
+0583  89 3E 09 09 mov word ptr [909h],di
+0587  A3 FF 08    mov word ptr [8ffh],ax
+058A  89 1E 01 09 mov word ptr [901h],bx
+058E  89 16 03 09 mov word ptr [903h],dx
 0592  30 E4       xor ah,ah
 0592  30 E4       xor ah,ah
 0594  31 DB       xor bx,bx
 0594  31 DB       xor bx,bx
 0596  31 D2       xor dx,dx
 0596  31 D2       xor dx,dx
-0598  BF 23 08    mov di,823h
+0598  BF 05 09    mov di,905h
 059B  B1 05       mov cl,5h
 059B  B1 05       mov cl,5h
 059D  47          inc di
 059D  47          inc di
 059E  8A 2D       mov ch,byte ptr [di]
 059E  8A 2D       mov ch,byte ptr [di]
@@ -761,9 +761,9 @@
 05B1  BE 08 00    mov si,8h
 05B1  BE 08 00    mov si,8h
 05B4  D0 DD       rcr ch,1h
 05B4  D0 DD       rcr ch,1h
 05B6  73 0C       jnb 5c4h
 05B6  73 0C       jnb 5c4h
-05B8  02 26 1E 08 add ah,byte ptr [81eh]
-05BC  13 1E 1F 08 adc bx,word ptr [81fh]
-05C0  13 16 21 08 adc dx,word ptr [821h]
+05B8  02 26 00 09 add ah,byte ptr [900h]
+05BC  13 1E 01 09 adc bx,word ptr [901h]
+05C0  13 16 03 09 adc dx,word ptr [903h]
 05C4  D1 DA       rcr dx,1h
 05C4  D1 DA       rcr dx,1h
 05C6  D1 DB       rcr bx,1h
 05C6  D1 DB       rcr bx,1h
 05C8  D0 DC       rcr ah,1h
 05C8  D0 DC       rcr ah,1h
@@ -783,7 +783,7 @@
 05E2  74 02       je 5e6h
 05E2  74 02       je 5e6h
 05E4  FE C9       dec cl
 05E4  FE C9       dec cl
 05E6  91          xchg ax,cx
 05E6  91          xchg ax,cx
-05E7  32 36 19 08 xor dh,byte ptr [819h]
+05E7  32 36 FB 08 xor dh,byte ptr [8fbh]
 05EB  3C 00       cmp al,0h
 05EB  3C 00       cmp al,0h
 05ED  75 06       jne 5f5h
 05ED  75 06       jne 5f5h
 05EF  31 C0       xor ax,ax
 05EF  31 C0       xor ax,ax
@@ -832,29 +832,29 @@
 0643  B0 01       mov al,1h
 0643  B0 01       mov al,1h
 0645  B4 4C       mov ah,4ch
 0645  B4 4C       mov ah,4ch
 0647  CD 21       int 21h
 0647  CD 21       int 21h
-0649  89 0E 23 08 mov word ptr [823h],cx
+0649  89 0E 05 09 mov word ptr [905h],cx
 064D  8B CA       mov cx,dx
 064D  8B CA       mov cx,dx
 064F  31 F9       xor cx,di
 064F  31 F9       xor cx,di
 0651  F6 D5       not ch
 0651  F6 D5       not ch
 0653  80 E5 80    and ch,80h
 0653  80 E5 80    and ch,80h
-0656  88 2E 19 08 mov byte ptr [819h],ch
+0656  88 2E FB 08 mov byte ptr [8fbh],ch
 065A  80 CE 80    or dh,80h
 065A  80 CE 80    or dh,80h
 065D  81 CF 00 80 or di,8000h
 065D  81 CF 00 80 or di,8000h
-0661  89 36 25 08 mov word ptr [825h],si
-0665  89 3E 27 08 mov word ptr [827h],di
-0669  A2 1D 08    mov byte ptr [81dh],al
-066C  BF 22 08    mov di,822h
+0661  89 36 07 09 mov word ptr [907h],si
+0665  89 3E 09 09 mov word ptr [909h],di
+0669  A2 FF 08    mov byte ptr [8ffh],al
+066C  BF 04 09    mov di,904h
 066F  B1 05       mov cl,5h
 066F  B1 05       mov cl,5h
 0671  BE 08 00    mov si,8h
 0671  BE 08 00    mov si,8h
-0674  3B 16 27 08 cmp dx,word ptr [827h]
+0674  3B 16 09 09 cmp dx,word ptr [909h]
 0678  75 0A       jne 684h
 0678  75 0A       jne 684h
-067A  3B 1E 25 08 cmp bx,word ptr [825h]
+067A  3B 1E 07 09 cmp bx,word ptr [907h]
 067E  75 04       jne 684h
 067E  75 04       jne 684h
-0680  3A 26 24 08 cmp ah,byte ptr [824h]
+0680  3A 26 06 09 cmp ah,byte ptr [906h]
 0684  72 0C       jb 692h
 0684  72 0C       jb 692h
-0686  2A 26 24 08 sub ah,byte ptr [824h]
-068A  1B 1E 25 08 sbb bx,word ptr [825h]
-068E  1B 16 27 08 sbb dx,word ptr [827h]
+0686  2A 26 06 09 sub ah,byte ptr [906h]
+068A  1B 1E 07 09 sbb bx,word ptr [907h]
+068E  1B 16 09 09 sbb dx,word ptr [909h]
 0692  F5          cmc
 0692  F5          cmc
 0693  D0 D5       rcl ch,1h
 0693  D0 D5       rcl ch,1h
 0695  4E          dec si
 0695  4E          dec si
@@ -868,24 +868,24 @@
 06A4  D1 D3       rcl bx,1h
 06A4  D1 D3       rcl bx,1h
 06A6  D1 D2       rcl dx,1h
 06A6  D1 D2       rcl dx,1h
 06A8  73 CA       jnb 674h
 06A8  73 CA       jnb 674h
-06AA  2A 26 24 08 sub ah,byte ptr [824h]
-06AE  1B 1E 25 08 sbb bx,word ptr [825h]
-06B2  1B 16 27 08 sbb dx,word ptr [827h]
+06AA  2A 26 06 09 sub ah,byte ptr [906h]
+06AE  1B 1E 07 09 sbb bx,word ptr [907h]
+06B2  1B 16 09 09 sbb dx,word ptr [909h]
 06B6  F8          clc
 06B6  F8          clc
 06B7  EB D9       jmp 692h
 06B7  EB D9       jmp 692h
 06B9  D0 E4       shl ah,1h
 06B9  D0 E4       shl ah,1h
 06BB  D1 D3       rcl bx,1h
 06BB  D1 D3       rcl bx,1h
 06BD  D1 D2       rcl dx,1h
 06BD  D1 D2       rcl dx,1h
 06BF  72 11       jb 6d2h
 06BF  72 11       jb 6d2h
-06C1  3B 16 27 08 cmp dx,word ptr [827h]
+06C1  3B 16 09 09 cmp dx,word ptr [909h]
 06C5  75 0A       jne 6d1h
 06C5  75 0A       jne 6d1h
-06C7  3B 1E 25 08 cmp bx,word ptr [825h]
+06C7  3B 1E 07 09 cmp bx,word ptr [907h]
 06CB  75 04       jne 6d1h
 06CB  75 04       jne 6d1h
-06CD  3A 26 24 08 cmp ah,byte ptr [824h]
+06CD  3A 26 06 09 cmp ah,byte ptr [906h]
 06D1  F5          cmc
 06D1  F5          cmc
-06D2  8B 0E 1D 08 mov cx,word ptr [81dh]
-06D6  8B 1E 1F 08 mov bx,word ptr [81fh]
-06DA  8B 16 21 08 mov dx,word ptr [821h]
+06D2  8B 0E FF 08 mov cx,word ptr [8ffh]
+06D6  8B 1E 01 09 mov bx,word ptr [901h]
+06DA  8B 16 03 09 mov dx,word ptr [903h]
 06DE  9F          lahf
 06DE  9F          lahf
 06DF  F6 C6 80    test dh,80h
 06DF  F6 C6 80    test dh,80h
 06E2  75 09       jne 6edh
 06E2  75 09       jne 6edh
@@ -900,3 +900,120 @@
 06F3  B4 4C       mov ah,4ch
 06F3  B4 4C       mov ah,4ch
 06F5  CD 21       int 21h
 06F5  CD 21       int 21h
 06F7  E9 EC FE    jmp 5e6h
 06F7  E9 EC FE    jmp 5e6h
+06FA  55          push bp
+06FB  8B EC       mov bp,sp
+06FD  53          push bx
+06FE  52          push dx
+06FF  56          push si
+0700  57          push di
+0701  8B 76 04    mov si,word ptr [bp+4h]
+0704  8B 04       mov ax,word ptr [si]
+0706  8B 4C 02    mov cx,word ptr [si+2h]
+0709  8B 54 04    mov dx,word ptr [si+4h]
+070C  89 CB       mov bx,cx
+070E  8B FE       mov di,si
+0710  8B 4D 06    mov cx,word ptr [di+6h]
+0713  8B 75 08    mov si,word ptr [di+8h]
+0716  8B 7D 0A    mov di,word ptr [di+0ah]
+0719  52          push dx
+071A  31 FA       xor dx,di
+071C  5A          pop dx
+071D  79 06       jns 725h
+071F  52          push dx
+0720  D1 D2       rcl dx,1h
+0722  5A          pop dx
+0723  EB 10       jmp 735h
+0725  F6 C6 80    test dh,80h
+0728  74 08       je 732h
+072A  E8 20 00    call 74dh
+072D  74 06       je 735h
+072F  F5          cmc
+0730  EB 03       jmp 735h
+0732  E8 18 00    call 74dh
+0735  B0 00       mov al,0h
+0737  B4 00       mov ah,0h
+0739  72 06       jb 741h
+073B  74 08       je 745h
+073D  B0 01       mov al,1h
+073F  EB 04       jmp 745h
+0741  B0 FF       mov al,0ffh
+0743  B4 FF       mov ah,0ffh
+0745  5F          pop di
+0746  5E          pop si
+0747  5A          pop dx
+0748  5B          pop bx
+0749  89 EC       mov sp,bp
+074B  5D          pop bp
+074C  C3          ret
+074D  38 C8       cmp al,cl
+074F  75 0E       jne 75fh
+0751  3C 00       cmp al,0h
+0753  74 0A       je 75fh
+0755  39 FA       cmp dx,di
+0757  75 06       jne 75fh
+0759  39 F3       cmp bx,si
+075B  75 02       jne 75fh
+075D  38 EC       cmp ah,ch
+075F  C3          ret
+0760  55          push bp
+0761  8B EC       mov bp,sp
+0763  53          push bx
+0764  52          push dx
+0765  56          push si
+0766  57          push di
+0767  FF 76 04    push word ptr [bp+4h]
+076A  8B 76 04    mov si,word ptr [bp+4h]
+076D  8B 04       mov ax,word ptr [si]
+076F  8B 4C 02    mov cx,word ptr [si+2h]
+0772  8B 54 04    mov dx,word ptr [si+4h]
+0775  89 CB       mov bx,cx
+0777  3C A8       cmp al,0a8h
+0779  73 4C       jnb 7c7h
+077B  89 C1       mov cx,ax
+077D  89 DE       mov si,bx
+077F  89 D7       mov di,dx
+0781  30 E4       xor ah,ah
+0783  31 DB       xor bx,bx
+0785  31 D2       xor dx,dx
+0787  80 E9 80    sub cl,80h
+078A  76 4C       jbe 7d8h
+078C  80 F9 10    cmp cl,10h
+078F  72 0C       jb 79dh
+0791  88 FC       mov ah,bh
+0793  89 D3       mov bx,dx
+0795  BA FF FF    mov dx,0ffffh
+0798  80 E9 10    sub cl,10h
+079B  EB EF       jmp 78ch
+079D  80 F9 08    cmp cl,8h
+07A0  72 0F       jb 7b1h
+07A2  88 DC       mov ah,bl
+07A4  88 FB       mov bl,bh
+07A6  88 D7       mov bh,dl
+07A8  88 F2       mov dl,dh
+07AA  B6 FF       mov dh,0ffh
+07AC  80 E9 08    sub cl,8h
+07AF  EB EC       jmp 79dh
+07B1  80 F9 00    cmp cl,0h
+07B4  74 0B       je 7c1h
+07B6  F9          stc
+07B7  D1 DA       rcr dx,1h
+07B9  D1 DB       rcr bx,1h
+07BB  D0 DC       rcr ah,1h
+07BD  FE C9       dec cl
+07BF  75 F5       jne 7b6h
+07C1  21 FA       and dx,di
+07C3  21 F3       and bx,si
+07C5  20 EC       and ah,ch
+07C7  5F          pop di
+07C8  89 05       mov word ptr [di],ax
+07CA  89 5D 02    mov word ptr [di+2h],bx
+07CD  89 55 04    mov word ptr [di+4h],dx
+07D0  5F          pop di
+07D1  5E          pop si
+07D2  5A          pop dx
+07D3  5B          pop bx
+07D4  89 EC       mov sp,bp
+07D6  5D          pop bp
+07D7  C3          ret
+07D8  B0 00       mov al,0h
+07DA  EB EB       jmp 7c7h