Ver código fonte

runtime: MBF multiplication (remult)

TPSRC2 1532-1587 with testexp (TPSRC3 50-78) inlined.  Same block ABI as
real_add/real_sub: [BP+4] holds a then b; the result overwrites a; a is
AX/BX/DX and b CX/SI/DI.

testexp's reference version POPS ITS RETURN ADDRESS on underflow/overflow
to leave remult entirely.  That cannot survive being called from our
wrapper, so the two fault paths jump to this core's exits instead:
underflow returns zero, overflow leaves through TP3's error 1 (exit 1).

The multiply is the reference's shift-and-add over the five mantissa
bytes, with LAHF/SAHF carrying the guard bit across the normalisation
TEST.  Data slots for a's saved AX/BX/DX and b's CX/SI/DI (the low byte
of the CX slot is the exponent and is skipped, exactly as the original's
`INC DI' does).

Two jump bugs, both silent before the tests:
  * remult's early zero/return tests are more than 127 bytes from their
    targets, so Jcc's rel8 wrapped.  JccNear (inverse Jcc_s over an E9)
    and JmpNear are now used, and FixUp REFUSES a rel8 whose target is
    out of range instead of wrapping.
  * the first JccNear emitted the condition raw, so it jumped when the
    condition was FALSE: every product became zero.  The inverse is what
    makes the E9 run when the condition HOLDS.

rt_exec: ten real_mul cases against the measured table - 1*2, 3*5, 5*3,
1*1, 1000*1, -3*2, 42*1000, 32767*1, 0*5, -3*-2.  99/99 entries pass.
The driver gains two blocks and RT_BASE moves to 0x190 because the
runtime now runs into the test scaffold at 0x800.

Non-vacuity puts the raw condition back into JccNear and requires the
real_mul cases to go red.

The ladder is unchanged at 18267; rediv is the remaining half of this
step, then recmp/reint and the formatter.
Eric Streit 1 dia atrás
pai
commit
3fdf28e4b1

+ 1 - 0
shell/Runtime.def

@@ -56,6 +56,7 @@ CONST
    E_RealAdd = 22 ;         (* MBF add: [BP+4] is a 12-byte block holding a
    E_RealAdd = 22 ;         (* MBF add: [BP+4] is a 12-byte block holding a
                                then b, and the result overwrites a *)
                                then b, and the result overwrites a *)
    E_RealSub = 23 ;         (* ...and subtraction: a - b *)
    E_RealSub = 23 ;         (* ...and subtraction: a - b *)
+   E_RealMul = 24 ;         (* ...and multiplication: a * b *)
 
 
    (* 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

+ 190 - 5
shell/Runtime.mod

@@ -76,7 +76,13 @@ CONST
    D_RMANT = 32 ;     (* 2 bytes: the second operand's saved MSB *)
    D_RMANT = 32 ;     (* 2 bytes: the second operand's saved MSB *)
    D_RMANT1 = 33 ;    (* its high byte, mixed with operand 1's sign *)
    D_RMANT1 = 33 ;    (* its high byte, mixed with operand 1's sign *)
    D_CVDEC = 34 ;     (* 1 byte: the second exponent during alignment *)
    D_CVDEC = 34 ;     (* 1 byte: the second exponent during alignment *)
-   D_END   = 35 ;     (* the block is padded to here *)
+   D_RMUL11 = 35 ;    (* a's saved AX; D_RMUL11+1 is its high byte (AH) *)
+   D_RMUL12 = 37 ;    (* a's saved BX *)
+   D_RMUL13 = 39 ;    (* a's saved DX *)
+   D_RMUL21 = 41 ;    (* b's saved CX (low byte is the exponent, skipped) *)
+   D_RMUL22 = 43 ;    (* b's saved SI *)
+   D_RMUL23 = 45 ;    (* b's saved DI *)
+   D_END   = 47 ;     (* the block is padded to here *)
 
 
    (* THE LOAD BIAS.  Everything above is an offset into the image as this
    (* THE LOAD BIAS.  Everything above is an offset into the image as this
       compiler numbers it: the entry JMP is at image 0.  But a DOS .COM is
       compiler numbers it: the entry JMP is at image 0.  But a DOS .COM is
@@ -133,7 +139,7 @@ VAR
    dataAt : CARDINAL ;
    dataAt : CARDINAL ;
    rtBase : CARDINAL ;
    rtBase : CARDINAL ;
    built  : BOOLEAN ;
    built  : BOOLEAN ;
-   entNm  : ARRAY [0..23] OF ARRAY [0..MaxNm] OF CHAR ;
+   entNm  : ARRAY [0..24] OF ARRAY [0..MaxNm] OF CHAR ;
 
 
 (* ---------------------------------------------------------------- *)
 (* ---------------------------------------------------------------- *)
 (*  name helpers                                                     *)
 (*  name helpers                                                     *)
@@ -620,6 +626,41 @@ PROCEDURE Stc ; BEGIN B (0F9H) END Stc ;                    (* STC *)
 PROCEDURE CmpAlCl ; BEGIN B (38H) ; B (0C8H) END CmpAlCl ;  (* CMP AL,CL *)
 PROCEDURE CmpAlCl ; BEGIN B (38H) ; B (0C8H) END CmpAlCl ;  (* CMP AL,CL *)
 PROCEDURE CmpCl0 ; BEGIN B (80H) ; B (0F9H) ; B (0) END CmpCl0 ; (* 80 F9 00 *)
 PROCEDURE CmpCl0 ; BEGIN B (80H) ; B (0F9H) ; B (0) END CmpCl0 ; (* 80 F9 00 *)
 PROCEDURE MovBhBl ; BEGIN B (88H) ; B (0DFH) END MovBhBl ;  (* BH:=BL *)
 PROCEDURE MovBhBl ; BEGIN B (88H) ; B (0DFH) END MovBhBl ;  (* BH:=BL *)
+PROCEDURE CmpCh0 ; BEGIN B (80H) ; B (0FDH) ; B (0) END CmpCh0 ;
+PROCEDURE AddAlCl ; BEGIN B (00H) ; B (0C8H) END AddAlCl ;  (* AL:=AL+CL *)
+PROCEDURE AddAlImm (v : CARDINAL) ;
+BEGIN B (04H) ; B (v MOD 256) END AddAlImm ;
+PROCEDURE MovSiImm (v : CARDINAL) ;
+BEGIN B (0BEH) ; W (v) END MovSiImm ;
+PROCEDURE RcrCh1 ; BEGIN B (0D0H) ; B (0DDH) END RcrCh1 ;   (* RCR CH,1 *)
+PROCEDURE RclCh1 ; BEGIN B (0D0H) ; B (0D5H) END RclCh1 ;   (* RCL CH,1 *)
+PROCEDURE Lahf ; BEGIN B (9FH) END Lahf ;
+PROCEDURE Sahf ; BEGIN B (9EH) END Sahf ;
+PROCEDURE XorCxDi ; BEGIN B (31H) ; B (0F9H) END XorCxDi ;   (* CX:=CX XOR DI *)
+PROCEDURE NotCh ; BEGIN B (0F6H) ; B (0D5H) END NotCh ;     (* NOT CH *)
+PROCEDURE AndChImm (v : CARDINAL) ;
+BEGIN B (80H) ; B (0E5H) ; B (v MOD 256) END AndChImm ;
+PROCEDURE MovDiData (delta : CARDINAL) ;
+BEGIN B (0BFH) ; Dd (delta) END MovDiData ;
+PROCEDURE LdChDiOff ; BEGIN B (8AH) ; B (2DH) END LdChDiOff ; (* CH:=[DI] *)
+PROCEDURE AddAhData (delta : CARDINAL) ;
+BEGIN B (02H) ; B (26H) ; Dd (delta) END AddAhData ;        (* ADD AH,[Vx] *)
+PROCEDURE AdcBxData (delta : CARDINAL) ;
+BEGIN B (13H) ; B (1EH) ; Dd (delta) END AdcBxData ;        (* ADC BX,[Vx] *)
+PROCEDURE AdcDxData (delta : CARDINAL) ;
+BEGIN B (13H) ; B (16H) ; Dd (delta) END AdcDxData ;        (* ADC DX,[Vx] *)
+PROCEDURE StDataCh (delta : CARDINAL) ;
+BEGIN B (88H) ; B (2EH) ; Dd (delta) END StDataCh ;
+PROCEDURE StDataCx (delta : CARDINAL) ;
+BEGIN B (89H) ; B (0EH) ; Dd (delta) END StDataCx ;
+PROCEDURE StDataSi (delta : CARDINAL) ;
+BEGIN B (89H) ; B (36H) ; Dd (delta) END StDataSi ;
+PROCEDURE StDataAx (delta : CARDINAL) ;
+BEGIN B (0A3H) ; Dd (delta) END StDataAx ;
+PROCEDURE StDataBx (delta : CARDINAL) ;
+BEGIN B (89H) ; B (1EH) ; Dd (delta) END StDataBx ;
+PROCEDURE StDataDx (delta : CARDINAL) ;
+BEGIN B (89H) ; B (16H) ; Dd (delta) END StDataDx ;
 PROCEDURE SubAhCh ; BEGIN B (28H) ; B (0ECH) END SubAhCh ;  (* AH:=AH-CH *)
 PROCEDURE SubAhCh ; BEGIN B (28H) ; B (0ECH) END SubAhCh ;  (* AH:=AH-CH *)
 PROCEDURE MovClImm (v : CARDINAL) ;
 PROCEDURE MovClImm (v : CARDINAL) ;
 BEGIN B (0B1H) ; B (v MOD 256) END MovClImm ;
 BEGIN B (0B1H) ; B (v MOD 256) END MovClImm ;
@@ -678,6 +719,23 @@ BEGIN B (8BH) ; B (75H) ; B (off MOD 256) END LdSiDiOff ;
 PROCEDURE LdDiDiOff (off : CARDINAL) ;
 PROCEDURE LdDiDiOff (off : CARDINAL) ;
 BEGIN B (8BH) ; B (7DH) ; B (off MOD 256) END LdDiDiOff ;
 BEGIN B (8BH) ; B (7DH) ; B (off MOD 256) END LdDiDiOff ;
 PROCEDURE PushBp4 ; BEGIN B (0FFH) ; B (76H) ; B (4) END PushBp4 ; (* PUSH [BP+4] *)
 PROCEDURE PushBp4 ; BEGIN B (0FFH) ; B (76H) ; B (4) END PushBp4 ; (* PUSH [BP+4] *)
+PROCEDURE JccNear (code : CARDINAL ; nm : ARRAY OF CHAR) ;
+(* A conditional near jump: the 8086 has no Jcc rel16, so an INVERSE Jcc_s
+   +3 over an E9 rel16.  The inverse is what makes the E9 run when `code'
+   HOLDS; a raw Jcc here would jump when it does NOT (remult's zero test did
+   exactly that and multiplied everything to zero).  Use this when the target
+   is more than 127 bytes away; Jcc's rel8 wraps silently otherwise (the
+   FixUp guard below now refuses that). *)
+BEGIN
+   IF (code MOD 2) = 0 THEN
+      B (code + 1)
+   ELSE
+      B (code - 1)
+   END ;
+   B (3) ; B (0E9H) ; AddFix (1, rpos, nm, 0) ; W (0)
+END JccNear ;
+PROCEDURE JmpNear (nm : ARRAY OF CHAR) ;
+BEGIN B (0E9H) ; AddFix (1, rpos, nm, 0) ; W (0) END JmpNear ;
 PROCEDURE Call (nm : ARRAY OF CHAR) ;
 PROCEDURE Call (nm : ARRAY OF CHAR) ;
 BEGIN B (0E8H) ; AddFix (1, rpos, nm, 0) ; W (0) END Call ;
 BEGIN B (0E8H) ; AddFix (1, rpos, nm, 0) ; W (0) END Call ;
 PROCEDURE StVxBx   (delta : CARDINAL ) ;
 PROCEDURE StVxBx   (delta : CARDINAL ) ;
@@ -1714,6 +1772,100 @@ BEGIN
    RetR
    RetR
 END EmitRealAddSub ;
 END EmitRealAddSub ;
 
 
+PROCEDURE EmitRealMul ;
+(* remult, TPSRC2 1532-1587, with testexp (TPSRC3 50-78) inlined.
+
+   ABI and register convention are real_add's: [BP+4] is a 12-byte block
+   holding a then b; the result overwrites a; a is AX/BX/DX and b CX/SI/DI.
+
+   testexp is inlined because the reference's version is called and, on
+   underflow or overflow, POPS ITS RETURN ADDRESS to leave remult entirely.
+   That trick cannot survive being called from our wrapper, so the two
+   exponent fault paths jump to this core's zero/error exits instead.
+
+   Multiplication is the reference's shift-and-add over the five mantissa
+   bytes: for each byte, if it is zero the 40-bit result is shifted right
+   eight; otherwise each of its eight bits conditionally adds a's mantissa
+   and shifts the result one.  LAHF/SAHF carry the guard bit across the
+   normalisation TEST.  Overflow leaves through TP3's error 1 (exit code 1);
+   underflow returns zero. *)
+BEGIN
+   M ("real_mul") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   PushBp4 ;
+   MovSiBp4 ;
+   LdAxSi ;
+   LdCxSiOff (2) ; LdDxSiOff (4) ;
+   MovBxCx ;
+   MovDiSi ;
+   LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
+   Call ("rm_core") ;
+   PopDi ;
+   StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR ;
+
+   M ("rm_core") ;
+   CmpCl0 ; JccNear (74H, "rm_zero") ;
+   CmpAl0 ; JccNear (74H, "rm_ret") ;
+   AddAlCl ;
+   Jb8 ("rm_ovr1") ;
+   AddAlImm (80H) ;
+   Jb8 ("rm_ok") ;
+   JmpNear ("rm_zero") ;                 (* underflow *)
+   M ("rm_ovr1") ;
+   AddAlImm (80H) ;
+   Jae8 ("rm_ok") ;
+   (* overflow: TP3 error 1 *)
+   MovAlD (1) ; MovAh (4CH) ; Int21 ;
+   M ("rm_ok") ;
+   (* testexp's tail *)
+   StDataCx (D_RMUL21) ;
+   MovCxDx ; XorCxDi ; NotCh ; AndChImm (80H) ; StDataCh (D_RESAVE) ;
+   OrDhImm (80H) ; OrDiImm (8000H) ;
+   StDataSi (D_RMUL22) ; StDataDi (D_RMUL23) ;
+   (* remult *)
+   StDataAx (D_RMUL11) ; StDataBx (D_RMUL12) ; StDataDx (D_RMUL13) ;
+   XorAhAh ; XorBxBx ; XorDxDx ;
+   MovDiData (D_RMUL21) ;
+   MovClImm (5) ;
+   M ("rm_byt") ;
+   IncDi ;
+   LdChDiOff ;
+   CmpCh0 ; Jne8 ("rm_domul") ;
+   MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; XorDhDh ;
+   Jmp8 ("rm_nxt") ;
+   M ("rm_domul") ;
+   MovSiImm (8) ;
+   M ("rm_mul") ;
+   RcrCh1 ;
+   Jae8 ("rm_bit0") ;
+   AddAhData (D_RMUL11 + 1) ;
+   AdcBxData (D_RMUL12) ;
+   AdcDxData (D_RMUL13) ;
+   M ("rm_bit0") ;
+   RcrDx1 ; RcrBx1 ; RcrAh1 ;
+   DecSi ; Jne8 ("rm_mul") ;
+   M ("rm_nxt") ;
+   DecCl ; Jne8 ("rm_byt") ;
+   XchgAxCx ;
+   Lahf ;
+   TestDhImm (80H) ; Jne8 ("rm_noadj") ;
+   Sahf ;
+   RclCh1 ; RclBx1 ; RclDx1 ;
+   CmpCl0 ; Je8 ("rm_noadj") ;
+   DecCl ;
+   M ("rm_noadj") ;
+   XchgAxCx ;
+   XorDhData (D_RESAVE) ;
+   CmpAl0 ; Jne8 ("rm_ret") ;
+   M ("rm_zero") ;
+   XorAxAx ; XorBxBx ; XorDxDx ;
+   M ("rm_ret") ;
+   RetR
+END EmitRealMul ;
+
 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
@@ -1762,6 +1914,30 @@ BEGIN
       HALT
       HALT
    END ;
    END ;
    B (0) ;
    B (0) ;
+   IF dataAt + D_RMUL11 # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;
+   IF dataAt + D_RMUL12 # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;
+   IF dataAt + D_RMUL13 # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;
+   IF dataAt + D_RMUL21 # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;
+   IF dataAt + D_RMUL22 # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;
+   IF dataAt + D_RMUL23 # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;
    WHILE rpos < dataAt + D_END DO
    WHILE rpos < dataAt + D_END DO
       B (0)
       B (0)
    END ;
    END ;
@@ -1789,7 +1965,15 @@ BEGIN
          t := LblOff (fix [i].nm) ;
          t := LblOff (fix [i].nm) ;
          IF fix [i].kind = 0 THEN
          IF fix [i].kind = 0 THEN
             (* rel8 is measured from the end of the instruction, i.e. one
             (* rel8 is measured from the end of the instruction, i.e. one
-               byte past the displacement field *)
+               byte past the displacement field.  A target beyond +127/-128
+               would wrap silently and send the machine into the middle of
+               an instruction; that happened in remult's early zero/return
+               jumps.  Refuse it. *)
+            IF (VAL (LONGINT, t) - VAL (LONGINT, fix [i].place + 1) < -128)
+               OR (VAL (LONGINT, t) - VAL (LONGINT, fix [i].place + 1) > 127)
+            THEN
+               HALT
+            END ;
             rel := (t + 100H - (fix [i].place + 1)) MOD 100H ;
             rel := (t + 100H - (fix [i].place + 1)) MOD 100H ;
             rt [fix [i].place] := VAL (BYTE, rel)
             rt [fix [i].place] := VAL (BYTE, rel)
          ELSE
          ELSE
@@ -1822,7 +2006,7 @@ BEGIN
    EmitRdChar ; EmitRdBool ; EmitRdLn ;
    EmitRdChar ; EmitRdBool ; EmitRdLn ;
    EmitGetCh ; EmitUnGetCh ;
    EmitGetCh ; EmitUnGetCh ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
-   EmitRealFromInt ; EmitRealAddSub ;
+   EmitRealFromInt ; EmitRealAddSub ; EmitRealMul ;
    EmitData ;
    EmitData ;
    FixUp ;
    FixUp ;
    SetStr (entNm [0], "initmem") ;
    SetStr (entNm [0], "initmem") ;
@@ -1849,6 +2033,7 @@ BEGIN
    SetStr (entNm [21], "real_from_int") ;
    SetStr (entNm [21], "real_from_int") ;
    SetStr (entNm [22], "real_add") ;
    SetStr (entNm [22], "real_add") ;
    SetStr (entNm [23], "real_sub") ;
    SetStr (entNm [23], "real_sub") ;
+   SetStr (entNm [24], "real_mul") ;
    built := TRUE
    built := TRUE
 END RT_Build ;
 END RT_Build ;
 
 
@@ -1876,7 +2061,7 @@ BEGIN
    IF NOT built THEN
    IF NOT built THEN
       RT_Build (0)
       RT_Build (0)
    END ;
    END ;
-   IF i > 23 THEN
+   IF i > 24 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..23] OF ARRAY [0..15] OF CHAR ;
+    names : ARRAY [0..24] 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 <= 23 DO
+   WHILE i <= 24 DO
       names [i] := "" ;
       names [i] := "" ;
       INC (i)
       INC (i)
    END ;
    END ;
@@ -136,8 +136,9 @@ BEGIN
    names [21] := "real_from_int" ;
    names [21] := "real_from_int" ;
    names [22] := "real_add" ;
    names [22] := "real_add" ;
    names [23] := "real_sub" ;
    names [23] := "real_sub" ;
+   names [24] := "real_mul" ;
    i := 0 ;
    i := 0 ;
-   WHILE i <= 23 DO
+   WHILE i <= 24 DO
       PENT (i, names [i]) ;
       PENT (i, names [i]) ;
       INC (i)
       INC (i)
    END ;
    END ;

+ 10 - 0
shell/tests/audit_helpers.py

@@ -118,6 +118,13 @@ NON_ONE_LINE = {
         "SubClImm": COMP, "TestDhImm": COMP, "TestDataImm": COMP,
         "SubClImm": COMP, "TestDhImm": COMP, "TestDataImm": COMP,
         "XorDhData": COMP, "XorDiData": COMP, "XorDataDh": COMP,
         "XorDhData": COMP, "XorDiData": COMP, "XorDataDh": COMP,
         "XorDataImm": COMP, "PushBp4": COMP,
         "XorDataImm": COMP, "PushBp4": COMP,
+        # remult/testexp helpers.  The multiplication is pinned by rt_exec's
+        # ten real_mul cases, including zero, cancellation and signs.
+        "AdcBxData": COMP, "AdcDxData": COMP, "AddAhData": COMP,
+        "AddAlImm": COMP, "AndChImm": COMP, "JccNear": COMP,
+        "JmpNear": COMP, "MovDiData": COMP, "MovSiImm": COMP,
+        "StDataAx": COMP, "StDataBx": COMP, "StDataCh": COMP,
+        "StDataCx": COMP, "StDataDx": COMP, "StDataSi": 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,
         "SubAl": PARAM, "CmpAl": PARAM, "AddDl": PARAM, "J8": COMP,
         "SubAl": PARAM, "CmpAl": PARAM, "AddDl": PARAM, "J8": COMP,
@@ -397,6 +404,9 @@ PATTERNS = [
     (re.compile(r"^(Shl|Shr|Rcl|Rcr|Rol|Ror|Sar)(%s)1$" % _ALT),
     (re.compile(r"^(Shl|Shr|Rcl|Rcr|Rol|Ror|Sar)(%s)1$" % _ALT),
      None, ["r:%(2)s", "i:1"], {0xD0, 0xD1}),
      None, ["r:%(2)s", "i:1"], {0xD0, 0xD1}),
     (re.compile(r"^Stc$"), "stc", [], {0xF9}),
     (re.compile(r"^Stc$"), "stc", [], {0xF9}),
+    (re.compile(r"^Lahf$"), "lahf", [], {0x9F}),
+    (re.compile(r"^Sahf$"), "sahf", [], {0x9E}),
+    (re.compile(r"^LdChDiOff$"), "mov", ["r:Ch", "m:Di"], {0x8A}),
     (re.compile(r"^PushBp4$"), "push", ["m:Bp+4"], {0xFF}),
     (re.compile(r"^PushBp4$"), "push", ["m:Bp+4"], {0xFF}),
     (re.compile(r"^Adc(%s)(%s)$" % (_ALT16, _ALT16)),
     (re.compile(r"^Adc(%s)(%s)$" % (_ALT16, _ALT16)),
      "adc", ["r:%(1)s", "r:%(2)s"], {0x11, 0x13}),
      "adc", ["r:%(1)s", "r:%(2)s"], {0x11, 0x13}),

+ 1 - 0
shell/tests/check_runtime.py

@@ -135,6 +135,7 @@ GOLDEN = {
     # real_add/real_sub share a frame and the readd core.
     # real_add/real_sub share a frame and the readd core.
     "real_add": "55 8B EC 53 52 56 57",
     "real_add": "55 8B EC 53 52 56 57",
     "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",
     # 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

+ 8 - 1
shell/tests/exec/rtdrv.s

@@ -91,7 +91,7 @@
 # above 0x0800 so that a runtime which grew by a few hundred bytes would not
 # above 0x0800 so that a runtime which grew by a few hundred bytes would not
 # 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, 0x0200
+	.set	RT_BASE, 0x0190
 	.set	HDR, 0x0800
 	.set	HDR, 0x0800
 	.set	DATA, 0x0810
 	.set	DATA, 0x0810
 	.set	DLEN, 32			# bytes of data area for initmem to clear
 	.set	DLEN, 32			# bytes of data area for initmem to clear
@@ -120,6 +120,8 @@
 	.set	R_BLK6, 0x0980
 	.set	R_BLK6, 0x0980
 	.set	R_BLK7, 0x0990
 	.set	R_BLK7, 0x0990
 	.set	R_BLK8, 0x09A0
 	.set	R_BLK8, 0x09A0
+	.set	R_BLK9, 0x09B0
+	.set	R_BLK10, 0x09C0
 
 
 	.code16
 	.code16
 	.text
 	.text
@@ -436,3 +438,8 @@ HDR:
 	.byte	0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A
 	.byte	0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A
 	.org	R_BLK8
 	.org	R_BLK8
 	.byte	0x8F, 0x00, 0x00, 0x00, 0xFE, 0x7F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
 	.byte	0x8F, 0x00, 0x00, 0x00, 0xFE, 0x7F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
+
+	.org	R_BLK9
+	.byte	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x20
+	.org	R_BLK10
+	.byte	0x82, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x80

+ 31 - 0
shell/tests/nonvacuity.sh

@@ -1391,6 +1391,37 @@ 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
 
 
+# --- JccNear jumps when the condition HOLDS ------------------------------
+# The near form is an INVERSE Jcc over an E9.  Putting the condition back
+# unchanged makes remult's `b = 0 ?' test jump when b is NON-zero, so every
+# product becomes zero.  The rt_exec real_mul cases compare exact bytes.
+cp "$SAVED" Runtime.mod
+if python3 - <<'PYX'
+p = 'Runtime.mod'
+s = open(p).read()
+old = """   IF (code MOD 2) = 0 THEN
+      B (code + 1)
+   ELSE
+      B (code - 1)
+   END ;"""
+assert s.count(old) == 1, 'the JccNear inverse found %d times' % s.count(old)
+open(p, 'w').write(s.replace(old, '   B (code) ;'))
+PYX
+then
+    if "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1; then
+        expect_red "real_mul red when JccNear uses the raw condition" \
+            "real_mul" python3 tests/rt_exec.py
+    else
+        echo "  FAIL: the runtime would not rebuild with the raw JccNear"
+        fail=$((fail + 1))
+    fi
+else
+    echo "  BROKEN CASE: the JccNear-inverse mutation did not apply"
+    fail=$((fail + 1))
+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

+ 7 - 3
shell/tests/rt_exec.py

@@ -61,7 +61,7 @@ SHELL = os.path.dirname(HERE)
 # a known offset and Python needs to know where the driver thinks it is, so the
 # a known offset and Python needs to know where the driver thinks it is, so the
 # 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 = 0x0200
+RT_BASE = 0x0190
 HDR = 0x0800
 HDR = 0x0800
 DLEN = 32
 DLEN = 32
 DATA = 0x0810
 DATA = 0x0810
@@ -103,7 +103,7 @@ SOH, STX, ETX, EOT = 0x01, 0x02, 0x03, 0x04
 ENTS = ["initmem", "progend", "stackchk", "wrint", "wrchar", "wrbool",
 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"]
+        "wrintw", "real_from_int", "real_add", "real_sub", "real_mul"]
 
 
 # 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
@@ -497,7 +497,7 @@ def build_cases():
     # The driver preloads 12-byte blocks (a then b); real_add/real_sub take
     # The driver preloads 12-byte blocks (a then b); real_add/real_sub take
     # the block's address, read both operands, and overwrite a with the result.
     # the block's address, read both operands, and overwrite a with the result.
     BLOCKS = [(1, 2), (3, 5), (5, 3), (1, 1), (1000, 1), (-3, 2),
     BLOCKS = [(1, 2), (3, 5), (5, 3), (1, 1), (1000, 1), (-3, 2),
-              (42, 1000), (32767, 1)]
+              (42, 1000), (32767, 1), (0, 5), (-3, -2)]
     for bi, (a, b) in enumerate(BLOCKS):
     for bi, (a, b) in enumerate(BLOCKS):
         blk = 0x0930 + 16 * bi
         blk = 0x0930 + 16 * bi
         add("real_add(%d,%d)" % (a, b),
         add("real_add(%d,%d)" % (a, b),
@@ -508,6 +508,10 @@ def build_cases():
             [s("real_sub", blk + LOAD_BIAS)],
             [s("real_sub", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(a - b), dumpadr=blk,
             dump=6, dumpbytes=mbf(a - b), dumpadr=blk,
             note="the measured MBF bytes, the reference's resub")
             note="the measured MBF bytes, the reference's resub")
+        add("real_mul(%d,%d)" % (a, b),
+            [s("real_mul", blk + LOAD_BIAS)],
+            dump=6, dumpbytes=mbf(a * b), dumpadr=blk,
+            note="the measured MBF bytes, the reference's remult")
 
 
     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"!")

+ 126 - 22
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 15 06    mov bx,615h
+003C  BB FE 06    mov bx,6feh
 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 15 06    mov bx,615h
+00A8  BB FE 06    mov bx,6feh
 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 1A 06    mov dx,61ah
+00E6  BA 03 07    mov dx,703h
 00E9  EB 03       jmp 0eeh
 00E9  EB 03       jmp 0eeh
-00EB  BA 15 06    mov dx,615h
+00EB  BA FE 06    mov dx,6feh
 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 23 06    mov dx,623h
+00F4  BA 0C 07    mov dx,70ch
 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 20 06    mov dx,620h
+00FC  BA 09 07    mov dx,709h
 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 29 06 mov bx,word ptr [629h]
+021B  8B 1E 12 07 mov bx,word ptr [712h]
 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 29 06 mov word ptr [629h],bx
+0228  89 1E 12 07 mov word ptr [712h],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 29 06 mov word ptr [629h],bx
+0239  89 1E 12 07 mov word ptr [712h],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 2B 06 00 00 mov word ptr [62bh],0h
+03C7  C7 06 14 07 00 00 mov word ptr [714h],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 2B 06 00 80 mov word ptr [62bh],8000h
+0403  C7 06 14 07 00 80 mov word ptr [714h],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 2B 06 xor di,word ptr [62bh]
+0422  33 3E 14 07 xor di,word ptr [714h]
 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 2F 06 mov byte ptr [62fh],cl
+043A  88 0E 18 07 mov byte ptr [718h],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 2F 06 mov cl,byte ptr [62fh]
+0445  8A 0E 18 07 mov cl,byte ptr [718h]
 0449  EB DF       jmp 42ah
 0449  EB DF       jmp 42ah
-044B  89 3E 2B 06 mov word ptr [62bh],di
-044F  80 26 2C 06 80 and byte ptr [62ch],80h
-0454  89 3E 2D 06 mov word ptr [62dh],di
-0458  30 36 2E 06 xor byte ptr [62eh],dh
+044B  89 3E 14 07 mov word ptr [714h],di
+044F  80 26 15 07 80 and byte ptr [715h],80h
+0454  89 3E 16 07 mov word ptr [716h],di
+0458  30 36 17 07 xor byte ptr [717h],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 2F 06    mov al,byte ptr [62fh]
-0497  F6 06 2E 06 80 test byte ptr [62eh],80h
+0494  A0 18 07    mov al,byte ptr [718h]
+0497  F6 06 17 07 80 test byte ptr [717h],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 2C 06 80 xor byte ptr [62ch],80h
+04C0  80 36 15 07 80 xor byte ptr [715h],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,5 +684,109 @@
 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 2C 06 xor dh,byte ptr [62ch]
+0508  32 36 15 07 xor dh,byte ptr [715h]
 050C  C3          ret
 050C  C3          ret
+050D  55          push bp
+050E  8B EC       mov bp,sp
+0510  53          push bx
+0511  52          push dx
+0512  56          push si
+0513  57          push di
+0514  FF 76 04    push word ptr [bp+4h]
+0517  8B 76 04    mov si,word ptr [bp+4h]
+051A  8B 04       mov ax,word ptr [si]
+051C  8B 4C 02    mov cx,word ptr [si+2h]
+051F  8B 54 04    mov dx,word ptr [si+4h]
+0522  89 CB       mov bx,cx
+0524  8B FE       mov di,si
+0526  8B 4D 06    mov cx,word ptr [di+6h]
+0529  8B 75 08    mov si,word ptr [di+8h]
+052C  8B 7D 0A    mov di,word ptr [di+0ah]
+052F  E8 11 00    call 543h
+0532  5F          pop di
+0533  89 05       mov word ptr [di],ax
+0535  89 5D 02    mov word ptr [di+2h],bx
+0538  89 55 04    mov word ptr [di+4h],dx
+053B  5F          pop di
+053C  5E          pop si
+053D  5A          pop dx
+053E  5B          pop bx
+053F  89 EC       mov sp,bp
+0541  5D          pop bp
+0542  C3          ret
+0543  80 F9 00    cmp cl,0h
+0546  75 03       jne 54bh
+0548  E9 A4 00    jmp 5efh
+054B  3C 00       cmp al,0h
+054D  75 03       jne 552h
+054F  E9 A3 00    jmp 5f5h
+0552  00 C8       add al,cl
+0554  72 07       jb 55dh
+0556  04 80       add al,80h
+0558  72 0D       jb 567h
+055A  E9 92 00    jmp 5efh
+055D  04 80       add al,80h
+055F  73 06       jnb 567h
+0561  B0 01       mov al,1h
+0563  B4 4C       mov ah,4ch
+0565  CD 21       int 21h
+0567  89 0E 1F 07 mov word ptr [71fh],cx
+056B  8B CA       mov cx,dx
+056D  31 F9       xor cx,di
+056F  F6 D5       not ch
+0571  80 E5 80    and ch,80h
+0574  88 2E 15 07 mov byte ptr [715h],ch
+0578  80 CE 80    or dh,80h
+057B  81 CF 00 80 or di,8000h
+057F  89 36 21 07 mov word ptr [721h],si
+0583  89 3E 23 07 mov word ptr [723h],di
+0587  A3 19 07    mov word ptr [719h],ax
+058A  89 1E 1B 07 mov word ptr [71bh],bx
+058E  89 16 1D 07 mov word ptr [71dh],dx
+0592  30 E4       xor ah,ah
+0594  31 DB       xor bx,bx
+0596  31 D2       xor dx,dx
+0598  BF 1F 07    mov di,71fh
+059B  B1 05       mov cl,5h
+059D  47          inc di
+059E  8A 2D       mov ch,byte ptr [di]
+05A0  80 FD 00    cmp ch,0h
+05A3  75 0C       jne 5b1h
+05A5  88 DC       mov ah,bl
+05A7  88 FB       mov bl,bh
+05A9  88 D7       mov bh,dl
+05AB  88 F2       mov dl,dh
+05AD  30 F6       xor dh,dh
+05AF  EB 1C       jmp 5cdh
+05B1  BE 08 00    mov si,8h
+05B4  D0 DD       rcr ch,1h
+05B6  73 0C       jnb 5c4h
+05B8  02 26 1A 07 add ah,byte ptr [71ah]
+05BC  13 1E 1B 07 adc bx,word ptr [71bh]
+05C0  13 16 1D 07 adc dx,word ptr [71dh]
+05C4  D1 DA       rcr dx,1h
+05C6  D1 DB       rcr bx,1h
+05C8  D0 DC       rcr ah,1h
+05CA  4E          dec si
+05CB  75 E7       jne 5b4h
+05CD  FE C9       dec cl
+05CF  75 CC       jne 59dh
+05D1  91          xchg ax,cx
+05D2  9F          lahf
+05D3  F6 C6 80    test dh,80h
+05D6  75 0E       jne 5e6h
+05D8  9E          sahf
+05D9  D0 D5       rcl ch,1h
+05DB  D1 D3       rcl bx,1h
+05DD  D1 D2       rcl dx,1h
+05DF  80 F9 00    cmp cl,0h
+05E2  74 02       je 5e6h
+05E4  FE C9       dec cl
+05E6  91          xchg ax,cx
+05E7  32 36 15 07 xor dh,byte ptr [715h]
+05EB  3C 00       cmp al,0h
+05ED  75 06       jne 5f5h
+05EF  31 C0       xor ax,ax
+05F1  31 DB       xor bx,bx
+05F3  31 D2       xor dx,dx
+05F5  C3          ret