Quellcode durchsuchen

runtime: MBF division (rediv)

Transcribe TPSRC2/3's rediv as entry 25 (real_div), sharing testexp's
tail and remult's rm_noadj/rm_zero/rm_ret exits.  ABI unchanged: [BP+4]
points at a then b, the result overwrites a.

Three bugs this rung exposed, each now covered:

  * the div core's first label was rd_core, which add/sub already used.
    LblOff returns the FIRST spelling, so real_div called readd and every
    quotient was a sum (1/1 = 2, 6/2 = 8).  M now HALTs on a duplicate
    label, and a non-vacuity case re-adds one.
  * TPSRC's first mantissa subtraction is SUB.B AH,remul21a, not SBB:
    in the carry-out path CF is 1 and SBB made 42/6 two ulps low.  The
    ordinary compare path has CF=0, which hid it in 13 of 14 cases.  A
    non-vacuity case re-encodes it as SBB and requires real_div red.
  * the runtime grew to 1833 bytes, past the probe's scaffold: rtdrv.s
    and rt_exec.py moved their records and blocks up 0x200 (HDR=0x0A00,
    blocks at 0x0B30+).

rt_exec is 143/143, including fourteen exact-byte division cases
(5/2 = 2.5, 42/6, signs, zero numerator, exact and fractional).
check_runtime re-blessed; audit_helpers covers the new emitters.
non-vacuity 111/111; run_all ALL PASS.
Eric Streit vor 1 Tag
Ursprung
Commit
1addb53272

+ 21 - 6
CALC-COMPILE.md

@@ -552,10 +552,11 @@ Exec86):
   **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
   under both qemu and Exec86** (`t64_arrays` is compile-only: its image is past
-  the 8 KB boot-sector read).  `tests/nonvacuity.sh`: **108 ok, 0 failed**.
-  The runtime is 1573 bytes (wrintw, real_from_int, real_add, real_sub,
-  real_mul; the golden was re-blessed) and `tests/rt_exec.py` passes
-  **99/99**, including 16 add/sub and ten multiply exact-byte cases.
+  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.
   `check_realconst.py` pins the measured 6-byte Real operand bytes.
 - **`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
@@ -686,8 +687,22 @@ Then the two that are *outside* the ladder and gate the goal as stated:
         with `testexp` inlined; ten rt_exec cases.  Two jump bugs found:
         rel8 overflow to the far zero/return targets (JccNear/JmpNear plus a
         FixUp range guard now), and JccNear's condition polarity.
-      * **NEXT:** `rediv` (the five-byte long division), `recmp`, `reint`,
-        then `fmtreal`/`ascreal` for `Write(Value:FW:DEC)`, `Str` and `Val`.
+      * **DONE: `real_div`** (`v-TP3-MBF-DIV`, entry 25).  TPSRC2/3 `rediv`,
+        sharing `testexp`'s tail and `remult`'s `rm_noadj`/`rm_zero`/`rm_ret`
+        exits.  Fourteen rt_exec cases, including 5/2 = 2.5 and 42/6, the one
+        quotient below 1 (where the five-byte loop normalises by a left
+        shift).  Three traps found:
+          - the div core's first label was `rd_core`, which add/sub already
+            used; `LblOff` returned the FIRST spelling, so `real_div` called
+            `readd` and every "quotient" was a sum.  `M` now refuses a
+            duplicate label.
+          - TPSRC's `SUB.B AH,remul21a` must be SUB, not SBB: in the
+            carry-out path CF is 1, and SBB made 42/6 two ulps low.  The
+            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
+            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.
       * **THEN** the compiler side: real expressions need a value model
         (pointers to 6-byte temporaries), real comparisons, and real function

+ 1 - 0
shell/Runtime.def

@@ -57,6 +57,7 @@ CONST
                                then b, and the result overwrites a *)
    E_RealSub = 23 ;         (* ...and subtraction: a - b *)
    E_RealMul = 24 ;         (* ...and multiplication: a * b *)
+   E_RealDiv = 25 ;         (* ...and division: a / b *)
 
    (* 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

+ 123 - 3
shell/Runtime.mod

@@ -139,7 +139,7 @@ VAR
    dataAt : CARDINAL ;
    rtBase : CARDINAL ;
    built  : BOOLEAN ;
-   entNm  : ARRAY [0..24] OF ARRAY [0..MaxNm] OF CHAR ;
+   entNm  : ARRAY [0..25] OF ARRAY [0..MaxNm] OF CHAR ;
 
 (* ---------------------------------------------------------------- *)
 (*  name helpers                                                     *)
@@ -194,6 +194,7 @@ END W ;
 
 PROCEDURE M (nm : ARRAY OF CHAR ) ;
 (* mark: nm is the current offset *)
+VAR i : CARDINAL ;
 BEGIN
    (* A dropped label is not a smaller runtime, it is a WRONG one: its bytes
       are still emitted, so only a branch that names it goes off target.  That
@@ -202,6 +203,16 @@ BEGIN
    IF ltop >= MaxLbl THEN
       HALT
    END ;
+   (* A DUPLICATE label is worse than a missing one: LblOff returns the FIRST
+      spelling, so the second body (and every branch meant for it) silently
+      runs the first.  That is exactly how real_div came to call readd.  *)
+   i := 0 ;
+   WHILE i < ltop DO
+      IF StrEq (lbl [i].nm, nm) THEN
+         HALT
+      END ;
+      INC (i)
+   END ;
    SetStr (lbl [ltop].nm, nm) ;
    lbl [ltop].off := rpos ;
    INC (ltop)
@@ -661,12 +672,38 @@ 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 StDataAl (delta : CARDINAL) ;
+BEGIN B (0A2H) ; Dd (delta) END StDataAl ;
+PROCEDURE MovCxData (delta : CARDINAL) ;
+BEGIN B (8BH) ; B (0EH) ; Dd (delta) END MovCxData ;
+PROCEDURE MovBxData (delta : CARDINAL) ;
+BEGIN B (8BH) ; B (1EH) ; Dd (delta) END MovBxData ;
+PROCEDURE MovDxData (delta : CARDINAL) ;
+BEGIN B (8BH) ; B (16H) ; Dd (delta) END MovDxData ;
+PROCEDURE CmpDxData (delta : CARDINAL) ;
+BEGIN B (3BH) ; B (16H) ; Dd (delta) END CmpDxData ;
+PROCEDURE CmpBxData (delta : CARDINAL) ;
+BEGIN B (3BH) ; B (1EH) ; Dd (delta) END CmpBxData ;
+PROCEDURE CmpAhData (delta : CARDINAL) ;
+BEGIN B (3AH) ; B (26H) ; Dd (delta) END CmpAhData ;
+PROCEDURE SubAhData (delta : CARDINAL) ;
+BEGIN B (2AH) ; B (26H) ; Dd (delta) END SubAhData ;  (* SUB.B AH,remul21a: ignores the carry *)
+PROCEDURE SbbBxData (delta : CARDINAL) ;
+BEGIN B (1BH) ; B (1EH) ; Dd (delta) END SbbBxData ;
+PROCEDURE SbbDxData (delta : CARDINAL) ;
+BEGIN B (1BH) ; B (16H) ; Dd (delta) END SbbDxData ;
+PROCEDURE StDiCh ; BEGIN B (88H) ; B (2DH) END StDiCh ;     (* [DI]:=CH *)
+PROCEDURE Cmc ; BEGIN B (0F5H) END Cmc ;
+PROCEDURE Clc ; BEGIN B (0F8H) END Clc ;
+PROCEDURE IncCl ; BEGIN B (0FEH) ; B (0C1H) END IncCl ;
+PROCEDURE DecDi ; BEGIN B (4FH) END DecDi ;
 PROCEDURE SubAhCh ; BEGIN B (28H) ; B (0ECH) END SubAhCh ;  (* AH:=AH-CH *)
 PROCEDURE MovClImm (v : CARDINAL) ;
 BEGIN B (0B1H) ; B (v MOD 256) END MovClImm ;
 PROCEDURE CmpClImm (v : CARDINAL) ;
 BEGIN B (80H) ; B (0F9H) ; B (v MOD 256) END CmpClImm ;
 PROCEDURE SubClAl ; BEGIN B (28H) ; B (0C1H) END SubClAl ;  (* CL:=CL-AL *)
+PROCEDURE SubAlCl ; BEGIN B (28H) ; B (0C8H) END SubAlCl ;  (* AL:=AL-CL *)
 PROCEDURE SubClImm (v : CARDINAL) ;
 BEGIN B (80H) ; B (0E9H) ; B (v MOD 256) END SubClImm ;
 PROCEDURE AddAhCh ; BEGIN B (00H) ; B (0ECH) END AddAhCh ;  (* AH:=AH+CH *)
@@ -1863,7 +1900,89 @@ BEGIN
    M ("rm_zero") ;
    XorAxAx ; XorBxBx ; XorDxDx ;
    M ("rm_ret") ;
-   RetR
+   RetR ;
+
+   M ("real_div") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   PushBp4 ;
+   MovSiBp4 ;
+   LdAxSi ;
+   LdCxSiOff (2) ; LdDxSiOff (4) ;
+   MovBxCx ;
+   MovDiSi ;
+   LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
+   Call ("rv_core") ;
+   PopDi ;
+   StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR ;
+
+   M ("rv_core") ;
+   CmpAl0 ; JccNear (74H, "rm_ret") ;
+   SubAlCl ;
+   Cmc ;
+   Jb8 ("rd_ovr1") ;
+   AddAlImm (80H) ;
+   Jb8 ("rd_okx") ;
+   JmpNear ("rm_zero") ;                 (* underflow *)
+   M ("rd_ovr1") ;
+   AddAlImm (80H) ;
+   Jae8 ("rd_okx") ;
+   MovAlD (1) ; MovAh (4CH) ; Int21 ;
+   M ("rd_okx") ;
+   (* testexp's tail *)
+   StDataCx (D_RMUL21) ;
+   MovCxDx ; XorCxDi ; NotCh ; AndChImm (80H) ; StDataCh (D_RESAVE) ;
+   OrDhImm (80H) ; OrDiImm (8000H) ;
+   StDataSi (D_RMUL22) ; StDataDi (D_RMUL23) ;
+   (* rediv *)
+   StDataAl (D_RMUL11) ;
+   MovDiData (D_RMUL21 - 1) ;            (* remul21b: one before the mantissa *)
+   MovClImm (5) ;
+   MovSiImm (8) ;
+   M ("rd_loop") ;
+   CmpDxData (D_RMUL23) ; Jne8 ("rd_cmp") ;
+   CmpBxData (D_RMUL22) ; Jne8 ("rd_cmp") ;
+   CmpAhData (D_RMUL21 + 1) ;
+   M ("rd_cmp") ;
+   Jb8 ("rd_shft") ;
+   SubAhData (D_RMUL21 + 1) ; SbbBxData (D_RMUL22) ; SbbDxData (D_RMUL23) ;
+   M ("rd_shft") ;
+   Cmc ;
+   RclCh1 ;
+   DecSi ; Jne8 ("rd_bit") ;
+   StDiCh ;
+   DecCl ; Je8 ("rd_last") ;
+   DecDi ;
+   MovSiImm (8) ;
+   M ("rd_bit") ;
+   ShlAh1 ; RclBx1 ; RclDx1 ;
+   Jae8 ("rd_loop") ;
+   SubAhData (D_RMUL21 + 1) ; SbbBxData (D_RMUL22) ; SbbDxData (D_RMUL23) ;
+   Clc ;
+   Jmp8 ("rd_shft") ;
+   M ("rd_last") ;
+   ShlAh1 ; RclBx1 ; RclDx1 ;
+   Jb8 ("rd_shft2") ;
+   CmpDxData (D_RMUL23) ; Jne8 ("rd_cmp2") ;
+   CmpBxData (D_RMUL22) ; Jne8 ("rd_cmp2") ;
+   CmpAhData (D_RMUL21 + 1) ;
+   M ("rd_cmp2") ;
+   Cmc ;
+   M ("rd_shft2") ;
+   MovCxData (D_RMUL11) ; MovBxData (D_RMUL12) ; MovDxData (D_RMUL13) ;
+   Lahf ;
+   TestDhImm (80H) ; Jne8 ("rd_nrm") ;
+   Sahf ;
+   RclCh1 ; RclBx1 ; RclDx1 ;
+   Jmp8 ("rd_ok") ;
+   M ("rd_nrm") ;
+   IncCl ;
+   Jne8 ("rd_ok") ;
+   MovAlD (1) ; MovAh (4CH) ; Int21 ;
+   M ("rd_ok") ;
+   JmpNear ("rm_noadj")
 END EmitRealMul ;
 
 PROCEDURE EmitData ;
@@ -2034,6 +2153,7 @@ BEGIN
    SetStr (entNm [22], "real_add") ;
    SetStr (entNm [23], "real_sub") ;
    SetStr (entNm [24], "real_mul") ;
+   SetStr (entNm [25], "real_div") ;
    built := TRUE
 END RT_Build ;
 
@@ -2061,7 +2181,7 @@ BEGIN
    IF NOT built THEN
       RT_Build (0)
    END ;
-   IF i > 24 THEN
+   IF i > 25 THEN
       RETURN 0
    END ;
    RETURN rtBase + LblOff (entNm [i])

+ 4 - 3
shell/tests/RtProbe.mod

@@ -108,7 +108,7 @@ END PENT ;
 
 PROCEDURE Main ;
 VAR i, n, base : CARDINAL ;
-    names : ARRAY [0..24] OF ARRAY [0..15] OF CHAR ;
+    names : ARRAY [0..25] OF ARRAY [0..15] OF CHAR ;
 BEGIN
    base := GetBase () ;
    RT_Build (base) ;
@@ -121,7 +121,7 @@ BEGIN
    PCARD (RT_Size ()) ; PS (" bytes") ; NL ;
    PS ("code ends at ") ; PCARD (RT_CodeEnd ()) ; NL ;
    i := 0 ;
-   WHILE i <= 24 DO
+   WHILE i <= 25 DO
       names [i] := "" ;
       INC (i)
    END ;
@@ -137,8 +137,9 @@ BEGIN
    names [22] := "real_add" ;
    names [23] := "real_sub" ;
    names [24] := "real_mul" ;
+   names [25] := "real_div" ;
    i := 0 ;
-   WHILE i <= 24 DO
+   WHILE i <= 25 DO
       PENT (i, names [i]) ;
       INC (i)
    END ;

+ 9 - 0
shell/tests/audit_helpers.py

@@ -125,6 +125,13 @@ NON_ONE_LINE = {
         "JmpNear": COMP, "MovDiData": COMP, "MovSiImm": COMP,
         "StDataAx": COMP, "StDataBx": COMP, "StDataCh": COMP,
         "StDataCx": COMP, "StDataDx": COMP, "StDataSi": COMP,
+        # rediv's memory-operand compares/subtracts and result loads.  The
+        # displacement is a parameter, so the bytes cannot be disassembled as
+        # a fixed sequence; rt_exec's fourteen real_div cases pin them.
+        "CmpDxData": COMP, "CmpBxData": COMP, "CmpAhData": COMP,
+        "MovCxData": COMP, "MovBxData": COMP, "MovDxData": COMP,
+        "SbbBxData": COMP, "SbbDxData": COMP,
+        "SubAhData": COMP, "StDataAl": COMP,
         "MovBxImm": PARAM, "MovCxImm": PARAM, "MovDxImm": PARAM,
         "MovDl": PARAM, "MovAlD": PARAM, "MovAh": PARAM,
         "SubAl": PARAM, "CmpAl": PARAM, "AddDl": PARAM, "J8": COMP,
@@ -404,6 +411,8 @@ PATTERNS = [
     (re.compile(r"^(Shl|Shr|Rcl|Rcr|Rol|Ror|Sar)(%s)1$" % _ALT),
      None, ["r:%(2)s", "i:1"], {0xD0, 0xD1}),
     (re.compile(r"^Stc$"), "stc", [], {0xF9}),
+    (re.compile(r"^Cmc$"), "cmc", [], {0xF5}),
+    (re.compile(r"^Clc$"), "clc", [], {0xF8}),
     (re.compile(r"^Lahf$"), "lahf", [], {0x9F}),
     (re.compile(r"^Sahf$"), "sahf", [], {0x9E}),
     (re.compile(r"^LdChDiOff$"), "mov", ["r:Ch", "m:Di"], {0x8A}),

+ 1 - 0
shell/tests/check_runtime.py

@@ -136,6 +136,7 @@ GOLDEN = {
     "real_add": "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_div": "55 8B EC 53 52 56 57",
     # 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
     # every write of a character printed whatever the *last* character read

+ 59 - 27
shell/tests/exec/rtdrv.s

@@ -92,36 +92,46 @@
 # silently run into the test scaffolding.  rt_exec.py asserts
 # RT_BASE + blob size <= HDR, so the bound is a checked one.
 	.set	RT_BASE, 0x0190
-	.set	HDR, 0x0800
-	.set	DATA, 0x0810
+	.set	HDR, 0x0A00
+	.set	DATA, 0x0A10
 	.set	DLEN, 32			# bytes of data area for initmem to clear
-	.set	STORE, 0x0840		# where a read entry's result is stored
-	.set	WSTR, 0x0850		# a string for the wrstr case: length + chars
-	.set	WSTR_EMPTY, 0x0858	# the same, with a length of 0
-	.set	CASE, 0x0860		# the case record
-	.set	D_DUMPLEN, 0x08A0	# the driver's own three words
-	.set	D_DUMPADR, 0x08A2
-	.set	D_CNT, 0x08A4
-	.set	RDSTR_DST, 0x08B0	# destination for the rdstr cases
-	.set	SST_HELLO, 0x08C0	# a short source string for strstore
-	.set	SST_LONG, 0x08D0	# a source longer than a small max
-	.set	SST_EMPTY, 0x08E0	# an empty source string
-	.set	SST_DST, 0x08F0		# destination for the strstore cases
-	.set	SST_CAT, 0x0900		# destination for the strcat cases, with a
+	.set	STORE, 0x0A40		# where a read entry's result is stored
+	.set	WSTR, 0x0A50		# a string for the wrstr case: length + chars
+	.set	WSTR_EMPTY, 0x0A58	# the same, with a length of 0
+	.set	CASE, 0x0A60		# the case record
+	.set	D_DUMPLEN, 0x0AA0	# the driver's own three words
+	.set	D_DUMPADR, 0x0AA2
+	.set	D_CNT, 0x0AA4
+	.set	RDSTR_DST, 0x0AB0	# destination for the rdstr cases
+	.set	SST_HELLO, 0x0AC0	# a short source string for strstore
+	.set	SST_LONG, 0x0AD0	# a source longer than a small max
+	.set	SST_EMPTY, 0x0AE0	# an empty source string
+	.set	SST_DST, 0x0AF0		# destination for the strstore cases
+	.set	SST_CAT, 0x0B00		# destination for the strcat cases, with a
 					# length to append to -- unlike SST_DST,
 					# which strstore overwrites
-	.set	SST_CAT0, 0x0910	# ...and one that starts empty
-	.set	SST_WORLD, 0x0920	# a source distinct from SST_HELLO
-	.set	R_BLK1, 0x0930
-	.set	R_BLK2, 0x0940
-	.set	R_BLK3, 0x0950
-	.set	R_BLK4, 0x0960
-	.set	R_BLK5, 0x0970
-	.set	R_BLK6, 0x0980
-	.set	R_BLK7, 0x0990
-	.set	R_BLK8, 0x09A0
-	.set	R_BLK9, 0x09B0
-	.set	R_BLK10, 0x09C0
+	.set	SST_CAT0, 0x0B10	# ...and one that starts empty
+	.set	SST_WORLD, 0x0B20	# a source distinct from SST_HELLO
+	.set	R_BLK1, 0x0B30
+	.set	R_BLK2, 0x0B40
+	.set	R_BLK3, 0x0B50
+	.set	R_BLK4, 0x0B60
+	.set	R_BLK5, 0x0B70
+	.set	R_BLK6, 0x0B80
+	.set	R_BLK7, 0x0B90
+	.set	R_BLK8, 0x0BA0
+	.set	R_BLK9, 0x0BB0
+	.set	R_BLK10, 0x0BC0
+	.set	R_BLK11, 0x0BD0
+	.set	R_BLK12, 0x0BE0
+	.set	R_BLK13, 0x0BF0
+	.set	R_BLK14, 0x0C00
+	.set	R_BLK15, 0x0C10
+	.set	R_BLK16, 0x0C20
+	.set	R_BLK17, 0x0C30
+	.set	R_BLK18, 0x0C40
+	.set	R_BLK19, 0x0C50
+	.set	R_BLK20, 0x0C60
 
 	.code16
 	.text
@@ -443,3 +453,25 @@ HDR:
 	.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
+
+# Division pairs for real_div.
+	.org	R_BLK11
+	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0x40, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK12
+	.byte	0x84, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK13
+	.byte	0x84, 0x00, 0x00, 0x00, 0x00, 0x70, 0x82, 0x00, 0x00, 0x00, 0x00, 0x40
+	.org	R_BLK14
+	.byte	0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK15
+	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK16
+	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0x40, 0x82, 0x00, 0x00, 0x00, 0x00, 0x80
+	.org	R_BLK17
+	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x80
+	.org	R_BLK18
+	.byte	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x20
+	.org	R_BLK19
+	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK20
+	.byte	0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x83, 0x00, 0x00, 0x00, 0x00, 0x40

+ 19 - 0
shell/tests/nonvacuity.sh

@@ -1422,6 +1422,25 @@ fi
 cp "$SAVED" Runtime.mod
 "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
 
+# --- rediv's first subtraction is SUB, not SBB ---------------------------
+# TPSRC's `SUB.B AH,remul21a' ignores the carry; `SBB' would subtract the
+# carry-out of the shifted numerator as well.  In the ordinary compare path
+# CF is 0, so the two agree and a casual transcription passes most divisions;
+# only 42/6 - the one quotient below 1, where the loop subtracts on equality -
+# comes out two ulps low.  Encoding the AH subtraction as SBB must turn the
+# exact-byte real_div cases red.
+if mutate Runtime.mod 's|BEGIN B (2AH) ; B (26H) ; Dd (delta) END SubAhData|BEGIN B (1AH) ; B (26H) ; Dd (delta) END SubAhData|'; then
+    if "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1; then
+        expect_red "real_div red when the mantissa subtraction uses SBB instead of SUB" \
+            "real_div" python3 tests/rt_exec.py
+    else
+        echo "  FAIL: the runtime would not rebuild with SubAhData as SBB"
+        fail=$((fail + 1))
+    fi
+fi
+cp "$SAVED" Runtime.mod
+"$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
+
 # --- the compiler's arms --------------------------------------------------
 # 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

+ 31 - 17
shell/tests/rt_exec.py

@@ -62,22 +62,22 @@ SHELL = os.path.dirname(HERE)
 # 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.
 RT_BASE = 0x0190
-HDR = 0x0800
+HDR = 0x0A00
 DLEN = 32
-DATA = 0x0810
-STORE = 0x0840
-WSTR = 0x0850
-WSTR_EMPTY = 0x0858
-CASE = 0x0860
+DATA = 0x0A10
+STORE = 0x0A40
+WSTR = 0x0A50
+WSTR_EMPTY = 0x0A58
+CASE = 0x0A60
 CASE_SZ = 64
-RDSTR_DST = 0x08B0
-SST_HELLO = 0x08C0
-SST_LONG = 0x08D0
-SST_EMPTY = 0x08E0
-SST_DST = 0x08F0
-SST_CAT = 0x0900
-SST_CAT0 = 0x0910
-SST_WORLD = 0x0920
+RDSTR_DST = 0x0AB0
+SST_HELLO = 0x0AC0
+SST_LONG = 0x0AD0
+SST_EMPTY = 0x0AE0
+SST_DST = 0x0AF0
+SST_CAT = 0x0B00
+SST_CAT0 = 0x0B10
+SST_WORLD = 0x0B20
 
 # 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
@@ -103,7 +103,8 @@ SOH, STX, ETX, EOT = 0x01, 0x02, 0x03, 0x04
 ENTS = ["initmem", "progend", "stackchk", "wrint", "wrchar", "wrbool",
         "wrreal", "wrln", "rdint", "rdchar", "rdbool", "rdln", "halt",
         "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"]
 
 # A call slot's flags, as rtdrv.s reads them.
 F_STACK = 1     # the argument goes on the stack, popped by the caller
@@ -497,9 +498,11 @@ def build_cases():
     # 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.
     BLOCKS = [(1, 2), (3, 5), (5, 3), (1, 1), (1000, 1), (-3, 2),
-              (42, 1000), (32767, 1), (0, 5), (-3, -2)]
+              (42, 1000), (32767, 1), (0, 5), (-3, -2),
+              (6, 2), (10, 2), (15, 3), (1000, 2), (-6, 2), (6, -2),
+              (-6, -2), (0, 5), (5, 2), (42, 6)]
     for bi, (a, b) in enumerate(BLOCKS):
-        blk = 0x0930 + 16 * bi
+        blk = 0x0B30 + 16 * bi
         add("real_add(%d,%d)" % (a, b),
             [s("real_add", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
@@ -512,6 +515,17 @@ def build_cases():
             [s("real_mul", blk + LOAD_BIAS)],
             dump=6, dumpbytes=mbf(a * b), dumpadr=blk,
             note="the measured MBF bytes, the reference's remult")
+        if b != 0 and a % b == 0:
+            q = mbf(a // b)
+        elif a == 5 and b == 2:
+            q = bytes.fromhex("820000000020")   # 2.5
+        else:
+            q = None
+        if q is not None:
+            add("real_div(%d,%d)" % (a, b),
+                [s("real_div", blk + LOAD_BIAS)],
+                dump=6, dumpbytes=q, dumpadr=blk,
+                note="the measured MBF bytes, the reference's rediv")
 
     add("wrchar('A')", [s("wrchar", ord("A"))], out=b"A")
     add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")

+ 144 - 34
shell/tests/runtime.golden

@@ -41,7 +41,7 @@
 0036  58          pop ax
 0037  F7 D8       neg ax
 0039  B9 0A 00    mov cx,0ah
-003C  BB FE 06    mov bx,6feh
+003C  BB 02 08    mov bx,802h
 003F  89 DE       mov si,bx
 0041  31 D2       xor dx,dx
 0043  F7 F1       div ax,cx
@@ -93,7 +93,7 @@
 00A2  58          pop ax
 00A3  F7 D8       neg ax
 00A5  B9 0A 00    mov cx,0ah
-00A8  BB FE 06    mov bx,6feh
+00A8  BB 02 08    mov bx,802h
 00AB  89 DE       mov si,bx
 00AD  31 D2       xor dx,dx
 00AF  F7 F1       div ax,cx
@@ -125,18 +125,18 @@
 00DE  83 7E 04 00 cmp word ptr [bp+4h],0h
 00E2  89 EC       mov sp,bp
 00E4  75 05       jne 0ebh
-00E6  BA 03 07    mov dx,703h
+00E6  BA 07 08    mov dx,807h
 00E9  EB 03       jmp 0eeh
-00EB  BA FE 06    mov dx,6feh
+00EB  BA 02 08    mov dx,802h
 00EE  B4 09       mov ah,9h
 00F0  CD 21       int 21h
 00F2  5D          pop bp
 00F3  C3          ret
-00F4  BA 0C 07    mov dx,70ch
+00F4  BA 10 08    mov dx,810h
 00F7  B4 09       mov ah,9h
 00F9  CD 21       int 21h
 00FB  C3          ret
-00FC  BA 09 07    mov dx,709h
+00FC  BA 0D 08    mov dx,80dh
 00FF  B4 09       mov ah,9h
 0101  CD 21       int 21h
 0103  C3          ret
@@ -303,12 +303,12 @@
 0217  EB EF       jmp 208h
 0219  58          pop ax
 021A  C3          ret
-021B  8B 1E 12 07 mov bx,word ptr [712h]
+021B  8B 1E 16 08 mov bx,word ptr [816h]
 021F  83 FB 00    cmp bx,0h
 0222  74 09       je 22dh
 0224  8A C3       mov al,bl
 0226  31 DB       xor bx,bx
-0228  89 1E 12 07 mov word ptr [712h],bx
+0228  89 1E 16 08 mov word ptr [816h],bx
 022C  C3          ret
 022D  B4 08       mov ah,8h
 022F  CD 21       int 21h
@@ -316,7 +316,7 @@
 0232  88 C2       mov dl,al
 0234  BB 01 00    mov bx,1h
 0237  8A DA       mov bl,dl
-0239  89 1E 12 07 mov word ptr [712h],bx
+0239  89 1E 16 08 mov word ptr [816h],bx
 023D  C3          ret
 023E  55          push bp
 023F  8B EC       mov bp,sp
@@ -541,7 +541,7 @@
 03BE  8B 4D 06    mov cx,word ptr [di+6h]
 03C1  8B 75 08    mov si,word ptr [di+8h]
 03C4  8B 7D 0A    mov di,word ptr [di+0ah]
-03C7  C7 06 14 07 00 00 mov word ptr [714h],0h
+03C7  C7 06 18 08 00 00 mov word ptr [818h],0h
 03CD  E8 4D 00    call 41dh
 03D0  5F          pop di
 03D1  89 05       mov word ptr [di],ax
@@ -570,7 +570,7 @@
 03FA  8B 4D 06    mov cx,word ptr [di+6h]
 03FD  8B 75 08    mov si,word ptr [di+8h]
 0400  8B 7D 0A    mov di,word ptr [di+0ah]
-0403  C7 06 14 07 00 80 mov word ptr [714h],8000h
+0403  C7 06 18 08 00 80 mov word ptr [818h],8000h
 0409  E8 11 00    call 41dh
 040C  5F          pop di
 040D  89 05       mov word ptr [di],ax
@@ -585,7 +585,7 @@
 041C  C3          ret
 041D  80 F9 00    cmp cl,0h
 0420  74 0E       je 430h
-0422  33 3E 14 07 xor di,word ptr [714h]
+0422  33 3E 18 08 xor di,word ptr [818h]
 0426  3C 00       cmp al,0h
 0428  75 07       jne 431h
 042A  89 C8       mov ax,cx
@@ -597,16 +597,16 @@
 0435  91          xchg ax,cx
 0436  87 F3       xchg bx,si
 0438  87 FA       xchg dx,di
-043A  88 0E 18 07 mov byte ptr [718h],cl
+043A  88 0E 1C 08 mov byte ptr [81ch],cl
 043E  28 C1       sub cl,al
 0440  80 F9 28    cmp cl,28h
 0443  72 06       jb 44bh
-0445  8A 0E 18 07 mov cl,byte ptr [718h]
+0445  8A 0E 1C 08 mov cl,byte ptr [81ch]
 0449  EB DF       jmp 42ah
-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
+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
 045C  81 CF 00 80 or di,8000h
 0460  80 CE 80    or dh,80h
 0463  80 F9 10    cmp cl,10h
@@ -631,8 +631,8 @@
 048E  D0 DC       rcr ah,1h
 0490  FE C9       dec cl
 0492  75 F6       jne 48ah
-0494  A0 18 07    mov al,byte ptr [718h]
-0497  F6 06 17 07 80 test byte ptr [717h],80h
+0494  A0 1C 08    mov al,byte ptr [81ch]
+0497  F6 06 1B 08 80 test byte ptr [81bh],80h
 049C  75 14       jne 4b2h
 049E  00 EC       add ah,ch
 04A0  11 F3       adc bx,si
@@ -652,7 +652,7 @@
 04BA  19 F3       sbb bx,si
 04BC  19 FA       sbb dx,di
 04BE  73 14       jnb 4d4h
-04C0  80 36 15 07 80 xor byte ptr [715h],80h
+04C0  80 36 19 08 80 xor byte ptr [819h],80h
 04C5  F6 D4       not ah
 04C7  F7 D3       not bx
 04C9  F7 D2       not dx
@@ -684,7 +684,7 @@
 0502  31 D2       xor dx,dx
 0504  C3          ret
 0505  80 E6 7F    and dh,7fh
-0508  32 36 15 07 xor dh,byte ptr [715h]
+0508  32 36 19 08 xor dh,byte ptr [819h]
 050C  C3          ret
 050D  55          push bp
 050E  8B EC       mov bp,sp
@@ -730,23 +730,23 @@
 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
+0567  89 0E 23 08 mov word ptr [823h],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
+0574  88 2E 19 08 mov byte ptr [819h],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
+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
 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
+0598  BF 23 08    mov di,823h
 059B  B1 05       mov cl,5h
 059D  47          inc di
 059E  8A 2D       mov ch,byte ptr [di]
@@ -761,9 +761,9 @@
 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]
+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]
 05C4  D1 DA       rcr dx,1h
 05C6  D1 DB       rcr bx,1h
 05C8  D0 DC       rcr ah,1h
@@ -783,10 +783,120 @@
 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]
+05E7  32 36 19 08 xor dh,byte ptr [819h]
 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
+05F6  55          push bp
+05F7  8B EC       mov bp,sp
+05F9  53          push bx
+05FA  52          push dx
+05FB  56          push si
+05FC  57          push di
+05FD  FF 76 04    push word ptr [bp+4h]
+0600  8B 76 04    mov si,word ptr [bp+4h]
+0603  8B 04       mov ax,word ptr [si]
+0605  8B 4C 02    mov cx,word ptr [si+2h]
+0608  8B 54 04    mov dx,word ptr [si+4h]
+060B  89 CB       mov bx,cx
+060D  8B FE       mov di,si
+060F  8B 4D 06    mov cx,word ptr [di+6h]
+0612  8B 75 08    mov si,word ptr [di+8h]
+0615  8B 7D 0A    mov di,word ptr [di+0ah]
+0618  E8 11 00    call 62ch
+061B  5F          pop di
+061C  89 05       mov word ptr [di],ax
+061E  89 5D 02    mov word ptr [di+2h],bx
+0621  89 55 04    mov word ptr [di+4h],dx
+0624  5F          pop di
+0625  5E          pop si
+0626  5A          pop dx
+0627  5B          pop bx
+0628  89 EC       mov sp,bp
+062A  5D          pop bp
+062B  C3          ret
+062C  3C 00       cmp al,0h
+062E  75 03       jne 633h
+0630  E9 C2 FF    jmp 5f5h
+0633  28 C8       sub al,cl
+0635  F5          cmc
+0636  72 07       jb 63fh
+0638  04 80       add al,80h
+063A  72 0D       jb 649h
+063C  E9 B0 FF    jmp 5efh
+063F  04 80       add al,80h
+0641  73 06       jnb 649h
+0643  B0 01       mov al,1h
+0645  B4 4C       mov ah,4ch
+0647  CD 21       int 21h
+0649  89 0E 23 08 mov word ptr [823h],cx
+064D  8B CA       mov cx,dx
+064F  31 F9       xor cx,di
+0651  F6 D5       not ch
+0653  80 E5 80    and ch,80h
+0656  88 2E 19 08 mov byte ptr [819h],ch
+065A  80 CE 80    or dh,80h
+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
+066F  B1 05       mov cl,5h
+0671  BE 08 00    mov si,8h
+0674  3B 16 27 08 cmp dx,word ptr [827h]
+0678  75 0A       jne 684h
+067A  3B 1E 25 08 cmp bx,word ptr [825h]
+067E  75 04       jne 684h
+0680  3A 26 24 08 cmp ah,byte ptr [824h]
+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]
+0692  F5          cmc
+0693  D0 D5       rcl ch,1h
+0695  4E          dec si
+0696  75 0A       jne 6a2h
+0698  88 2D       mov byte ptr [di],ch
+069A  FE C9       dec cl
+069C  74 1B       je 6b9h
+069E  4F          dec di
+069F  BE 08 00    mov si,8h
+06A2  D0 E4       shl ah,1h
+06A4  D1 D3       rcl bx,1h
+06A6  D1 D2       rcl dx,1h
+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]
+06B6  F8          clc
+06B7  EB D9       jmp 692h
+06B9  D0 E4       shl ah,1h
+06BB  D1 D3       rcl bx,1h
+06BD  D1 D2       rcl dx,1h
+06BF  72 11       jb 6d2h
+06C1  3B 16 27 08 cmp dx,word ptr [827h]
+06C5  75 0A       jne 6d1h
+06C7  3B 1E 25 08 cmp bx,word ptr [825h]
+06CB  75 04       jne 6d1h
+06CD  3A 26 24 08 cmp ah,byte ptr [824h]
+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]
+06DE  9F          lahf
+06DF  F6 C6 80    test dh,80h
+06E2  75 09       jne 6edh
+06E4  9E          sahf
+06E5  D0 D5       rcl ch,1h
+06E7  D1 D3       rcl bx,1h
+06E9  D1 D2       rcl dx,1h
+06EB  EB 0A       jmp 6f7h
+06ED  FE C1       inc cl
+06EF  75 06       jne 6f7h
+06F1  B0 01       mov al,1h
+06F3  B4 4C       mov ah,4ch
+06F5  CD 21       int 21h
+06F7  E9 EC FE    jmp 5e6h