Procházet zdrojové kódy

runtime: MBF addition and subtraction (readd/resub)

Transcribed from TPSRC2 1421-1530, instruction for instruction.

ABI: [BP+4] is a 12-byte block holding a (6 bytes) then b; the result
overwrites a.  Both operands are read before the result is written, so the
in-place destination is safe.  The reference's register convention is
kept: a in AX/BX/DX and b in CX/SI/DI, AL the exponent, AH+BX the low
mantissa, DX the high 16 with the sign in DH bit 7.  The scratch bytes are
the reference's own: resign/resave, remant/remant1, cvdecexp.

readd and resub share one core; resub sets resign=8000h and the core
toggles b's sign with XOR DI,resign.  The alignment loops (16 bytes, 8
bytes, single bits), the add/sub paths, two's-complement fixup,
normalisation by 8 and by 1, and the STC overflow exit are all there.

Two encoder traps came out of it:
  * XOR [mem],reg is 30h, XOR reg,[mem] is 32h.  XorDataDh was written
    with 32h, so it XORed DH with the saved sign instead of storing it;
    3+5 came out 8.5 and 32767+1 came out 32768.5.  The rt_exec cases
    caught it byte for byte.
  * FCML rejects 08 C0 (OR AL,AL) as it does 09 /r, so the zero tests use
    CMP reg,0 (the same ZF/SF/CF for a non-negative register); the audit
    gains patterns for shifts/rotates by one, STC, ADC/SBB, PUSH [BP+4],
    and 8-bit XCHG, and the parameterised MBF helpers are documented.

rt_exec: 16 new cases (8 blocks, add and sub), each comparing the six
result bytes against CALC-PORT.md's measured MBF table, including
alignment (1000+1), cancellation (1-1), sign flips (-3+2), and a carry
into a new exponent (32767+1).  85/85 entries pass.

The driver gains eight preloaded 12-byte MBF blocks.  Non-vacuity flips
XorDataDh back to 32h and requires the real_add cases to go red.

The ladder is unchanged at 18267; remult/rediv, recmp, reint and the
formatter are next.
Eric Streit před 1 dnem
rodič
revize
89d6a344b6

+ 3 - 0
shell/Runtime.def

@@ -53,6 +53,9 @@ CONST
    E_RealFromInt = 21 ;     (* integer -> 6-byte MBF real: the source word's
                                address is at [BP+6] and the destination's at
                                [BP+4] *)
+   E_RealAdd = 22 ;         (* MBF add: [BP+4] is a 12-byte block holding a
+                               then b, and the result overwrites a *)
+   E_RealSub = 23 ;         (* ...and subtraction: 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

+ 252 - 6
shell/Runtime.mod

@@ -47,8 +47,8 @@ CONST
       that case; the fill checks below make any future overflow a HALT instead
       of wrong code.  tests/check_runtime.py's branch-boundary check is what
       caught this one. *)
-   MaxLbl = 96 ;
-   MaxFix = 512 ;
+   MaxLbl = 160 ;       (* the MBF core's labels took the count past 96 *)
+   MaxFix = 768 ;
    MaxNm  = 15 ;
 
    (* Offsets inside the runtime's own data block.  These must agree with the
@@ -70,7 +70,13 @@ CONST
                           word is 0 exactly when the slot is empty - a NUL in
                           the input would otherwise be indistinguishable from
                           "nothing pushed back".  Byte 30 is unused. *)
-   D_END   = 31 ;     (* the block is padded to here *)
+   D_RSIGN = 30 ;     (* 2 bytes: the result sign word; D_RESAVE (31) is its
+                          high byte, which is the sign the reference keeps *)
+   D_RESAVE = 31 ;    (* the high byte of D_RSIGN: the saved sign *)
+   D_RMANT = 32 ;     (* 2 bytes: the second operand's saved MSB *)
+   D_RMANT1 = 33 ;    (* its high byte, mixed with operand 1's sign *)
+   D_CVDEC = 34 ;     (* 1 byte: the second exponent during alignment *)
+   D_END   = 35 ;     (* the block is padded to here *)
 
    (* 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
@@ -127,7 +133,7 @@ VAR
    dataAt : CARDINAL ;
    rtBase : CARDINAL ;
    built  : BOOLEAN ;
-   entNm  : ARRAY [0..21] OF ARRAY [0..MaxNm] OF CHAR ;
+   entNm  : ARRAY [0..23] OF ARRAY [0..MaxNm] OF CHAR ;
 
 (* ---------------------------------------------------------------- *)
 (*  name helpers                                                     *)
@@ -577,6 +583,103 @@ PROCEDURE CmpDx0   ; BEGIN B (83H) ; B (0FAH) ; B (0) END CmpDx0 ; (* 83 FA 00 *
 PROCEDURE CmpAl0   ; BEGIN B (3CH) ; B (0) END CmpAl0 ;         (* 3C 00 *)
 PROCEDURE AndDhImm (v : CARDINAL) ;
 BEGIN B (80H) ; B (0E6H) ; B (v MOD 256) END AndDhImm ;   (* 80 E6 imm *)
+
+(* --- the MBF core's register moves and flag helpers --------------------- *)
+PROCEDURE MovAxCx ; BEGIN B (89H) ; B (0C8H) END MovAxCx ;  (* AX:=CX *)
+PROCEDURE MovBxSi ; BEGIN B (89H) ; B (0F3H) END MovBxSi ;  (* BX:=SI *)
+PROCEDURE MovDxDi ; BEGIN B (89H) ; B (0FAH) END MovDxDi ;  (* DX:=DI *)
+PROCEDURE MovBxCx ; BEGIN B (89H) ; B (0CBH) END MovBxCx ;  (* BX:=CX *)
+PROCEDURE MovBxDx ; BEGIN B (89H) ; B (0D3H) END MovBxDx ;  (* BX:=DX *)
+PROCEDURE MovAhBh ; BEGIN B (88H) ; B (0FCH) END MovAhBh ;  (* AH:=BH *)
+PROCEDURE MovAhBl ; BEGIN B (88H) ; B (0DCH) END MovAhBl ;  (* AH:=BL *)
+PROCEDURE MovBlBh ; BEGIN B (88H) ; B (0FBH) END MovBlBh ;  (* BL:=BH *)
+PROCEDURE MovBhDl ; BEGIN B (88H) ; B (0D7H) END MovBhDl ;  (* BH:=DL *)
+PROCEDURE MovDlDh ; BEGIN B (88H) ; B (0F2H) END MovDlDh ;  (* DL:=DH *)
+PROCEDURE MovDhDl ; BEGIN B (88H) ; B (0D6H) END MovDhDl ;  (* DH:=DL *)
+PROCEDURE MovDlBh ; BEGIN B (88H) ; B (0FAH) END MovDlBh ;  (* DL:=BH *)
+PROCEDURE MovBlAh ; BEGIN B (88H) ; B (0E3H) END MovBlAh ;  (* BL:=AH *)
+PROCEDURE XchgAxCx ; BEGIN B (91H) END XchgAxCx ;           (* XCHG AX,CX *)
+PROCEDURE XchgBxSi ; BEGIN B (87H) ; B (0F3H) END XchgBxSi ;
+PROCEDURE XchgDxDi ; BEGIN B (87H) ; B (0FAH) END XchgDxDi ;
+PROCEDURE XchgAhCh ; BEGIN B (86H) ; B (0ECH) END XchgAhCh ;
+PROCEDURE XorDhDh ; BEGIN B (30H) ; B (0F6H) END XorDhDh ;  (* XOR DH,DH *)
+PROCEDURE XorAhAh ; BEGIN B (30H) ; B (0E4H) END XorAhAh ;  (* XOR AH,AH *)
+PROCEDURE NotAh ; BEGIN B (0F6H) ; B (0D4H) END NotAh ;     (* NOT AH *)
+PROCEDURE NotBx ; BEGIN B (0F7H) ; B (0D3H) END NotBx ;     (* NOT BX *)
+PROCEDURE NotDx ; BEGIN B (0F7H) ; B (0D2H) END NotDx ;     (* NOT DX *)
+PROCEDURE ShrDx1 ; BEGIN B (0D1H) ; B (0EAH) END ShrDx1 ;   (* SHR DX,1 *)
+PROCEDURE RcrBx1 ; BEGIN B (0D1H) ; B (0DBH) END RcrBx1 ;   (* RCR BX,1 *)
+PROCEDURE RcrDx1 ; BEGIN B (0D1H) ; B (0DAH) END RcrDx1 ;   (* RCR DX,1 *)
+PROCEDURE RcrAh1 ; BEGIN B (0D0H) ; B (0DCH) END RcrAh1 ;   (* RCR AH,1 *)
+PROCEDURE RclBx1 ; BEGIN B (0D1H) ; B (0D3H) END RclBx1 ;   (* RCL BX,1 *)
+PROCEDURE RclDx1 ; BEGIN B (0D1H) ; B (0D2H) END RclDx1 ;   (* RCL DX,1 *)
+PROCEDURE ShlAh1 ; BEGIN B (0D0H) ; B (0E4H) END ShlAh1 ;   (* SHL AH,1 *)
+PROCEDURE IncAl ; BEGIN B (0FEH) ; B (0C0H) END IncAl ;     (* INC AL *)
+PROCEDURE DecCl ; BEGIN B (0FEH) ; B (0C9H) END DecCl ;     (* DEC CL *)
+PROCEDURE Stc ; BEGIN B (0F9H) END Stc ;                    (* STC *)
+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 MovBhBl ; BEGIN B (88H) ; B (0DFH) END MovBhBl ;  (* BH:=BL *)
+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 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 *)
+PROCEDURE AddAhImm (v : CARDINAL) ;
+BEGIN B (80H) ; B (0C4H) ; B (v MOD 256) END AddAhImm ;
+PROCEDURE AdcBxSi ; BEGIN B (11H) ; B (0F3H) END AdcBxSi ;
+PROCEDURE AdcDxDi ; BEGIN B (11H) ; B (0FAH) END AdcDxDi ;
+PROCEDURE AdcBxImm (v : CARDINAL) ;
+BEGIN B (83H) ; B (0D3H) ; B (v MOD 256) END AdcBxImm ;
+PROCEDURE AdcDxImm (v : CARDINAL) ;
+BEGIN B (83H) ; B (0D2H) ; B (v MOD 256) END AdcDxImm ;
+PROCEDURE SbbBxSi ; BEGIN B (19H) ; B (0F3H) END SbbBxSi ;
+PROCEDURE SbbDxDi ; BEGIN B (19H) ; B (0FAH) END SbbDxDi ;
+PROCEDURE OrDiImm (v : CARDINAL) ;
+BEGIN B (81H) ; B (0CFH) ; W (v) END OrDiImm ;              (* DI:=DI OR imm16 *)
+PROCEDURE OrDhImm (v : CARDINAL) ;
+BEGIN B (80H) ; B (0CEH) ; B (v MOD 256) END OrDhImm ;
+PROCEDURE TestDhImm (v : CARDINAL) ;
+BEGIN B (0F6H) ; B (0C6H) ; B (v MOD 256) END TestDhImm ;
+PROCEDURE StDataImm (delta, v : CARDINAL) ;
+BEGIN B (0C7H) ; B (06H) ; Dd (delta) ; W (v) END StDataImm ;
+PROCEDURE StDataCl (delta : CARDINAL) ;
+BEGIN B (88H) ; B (0EH) ; Dd (delta) END StDataCl ;
+PROCEDURE StDataDi (delta : CARDINAL) ;
+BEGIN B (89H) ; B (3EH) ; Dd (delta) END StDataDi ;
+PROCEDURE LdAlData (delta : CARDINAL) ;
+BEGIN B (0A0H) ; Dd (delta) END LdAlData ;
+PROCEDURE LdClData (delta : CARDINAL) ;
+BEGIN B (8AH) ; B (0EH) ; Dd (delta) END LdClData ;
+PROCEDURE XorDiData (delta : CARDINAL) ;
+BEGIN B (33H) ; B (3EH) ; Dd (delta) END XorDiData ;          (* XOR DI,[Vx] *)
+PROCEDURE XorDataDh (delta : CARDINAL) ;
+BEGIN B (30H) ; B (36H) ; Dd (delta) END XorDataDh ;          (* XOR [Vx],DH *)
+PROCEDURE XorDhData (delta : CARDINAL) ;
+BEGIN B (32H) ; B (36H) ; Dd (delta) END XorDhData ;          (* XOR DH,[Vx] *)
+PROCEDURE XorDataImm (delta, v : CARDINAL) ;
+BEGIN B (80H) ; B (36H) ; Dd (delta) ; B (v MOD 256) END XorDataImm ;
+PROCEDURE AndDataImm (delta, v : CARDINAL) ;
+BEGIN B (80H) ; B (26H) ; Dd (delta) ; B (v MOD 256) END AndDataImm ;
+PROCEDURE TestDataImm (delta, v : CARDINAL) ;
+BEGIN B (0F6H) ; B (06H) ; Dd (delta) ; B (v MOD 256) END TestDataImm ;
+PROCEDURE LdCxSiOff (off : CARDINAL) ;
+BEGIN B (8BH) ; B (4CH) ; B (off MOD 256) END LdCxSiOff ;
+PROCEDURE LdDxSiOff (off : CARDINAL) ;
+BEGIN B (8BH) ; B (54H) ; B (off MOD 256) END LdDxSiOff ;
+PROCEDURE LdCxDiOff (off : CARDINAL) ;
+BEGIN B (8BH) ; B (4DH) ; B (off MOD 256) END LdCxDiOff ;
+PROCEDURE LdSiDiOff (off : CARDINAL) ;
+BEGIN B (8BH) ; B (75H) ; B (off MOD 256) END LdSiDiOff ;
+PROCEDURE LdDiDiOff (off : CARDINAL) ;
+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 Call (nm : ARRAY OF CHAR) ;
+BEGIN B (0E8H) ; AddFix (1, rpos, nm, 0) ; W (0) END Call ;
 PROCEDURE StVxBx   (delta : CARDINAL ) ;
 (* 89 1E lo hi: MOV [Vx],BX - the mirror of LdBxVx above, and it must use the
    same mod=00 / rm=110 and the same Dd fixup kind, or one of the two
@@ -1482,6 +1585,135 @@ BEGIN
    MovSpBp ; PopBp ; RetR
 END EmitRealFromInt ;
 
+PROCEDURE EmitRealAddSub ;
+(* readd/resub, TPSRC2 1421-1530, transcribed.
+
+   ABI: [BP+4] is the address of a 12-byte block holding a (6 bytes) then b
+   (6 bytes); the result overwrites a.  Both operands are read before the
+   result is written, so the in-place destination is safe.  The reference
+   keeps a in AX/BX/DX and b in CX/SI/DI, with AL the exponent, AH+BX the low
+   mantissa and DX the high 16 carrying the sign in DH bit 7.  The scratch
+   bytes are the reference's own (resign/resave, remant/remant1, cvdecexp).
+
+   The sign of b is toggled for subtraction with XOR DI,resign, exactly as
+   resub does, and the rest is one shared core.  An exponent overflow leaves
+   through the reference's STC + RET; no caller consumes that yet. *)
+BEGIN
+   M ("real_add") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   PushBp4 ;                          (* keep the block address for the store *)
+   MovSiBp4 ;
+   LdAxSi ;
+   LdCxSiOff (2) ; LdDxSiOff (4) ;
+   MovBxCx ;
+   MovDiSi ;
+   LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
+   StDataImm (D_RSIGN, 0) ;
+   Call ("rd_core") ;
+   PopDi ;
+   StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR ;
+
+   M ("real_sub") ;
+   PushBp ; MovBpSp ;
+   PushBx ; PushDx ; PushSi ; PushDi ;
+   PushBp4 ;
+   MovSiBp4 ;
+   LdAxSi ;
+   LdCxSiOff (2) ; LdDxSiOff (4) ;
+   MovBxCx ;
+   MovDiSi ;
+   LdCxDiOff (6) ; LdSiDiOff (8) ; LdDiDiOff (10) ;
+   StDataImm (D_RSIGN, 8000H) ;
+   Call ("rd_core") ;
+   PopDi ;
+   StDiAx ; StDiOffBx (2) ; StDiOffDx (4) ;
+   PopDi ; PopSi ; PopDx ; PopBx ;
+   MovSpBp ; PopBp ; RetR ;
+
+   M ("rd_core") ;
+   CmpCl0 ;
+   Je8 ("raret") ;
+   XorDiData (D_RSIGN) ;
+   CmpAl0 ;
+   Jne8 ("ranotriv") ;
+   M ("raretn2") ;
+   MovAxCx ; MovBxSi ; MovDxDi ;
+   M ("raret") ;
+   RetR ;
+   M ("ranotriv") ;
+   CmpAlCl ;
+   Jbe8 ("ranoswap") ;
+   XchgAxCx ; XchgBxSi ; XchgDxDi ;
+   M ("ranoswap") ;
+   StDataCl (D_CVDEC) ;
+   SubClAl ;
+   CmpClImm (28H) ;
+   Jb8 ("ranoundr") ;
+   LdClData (D_CVDEC) ;
+   Jmp8 ("raretn2") ;
+   M ("ranoundr") ;
+   StDataDi (D_RSIGN) ;                 (* resign; D_RESAVE is its high byte *)
+   AndDataImm (D_RESAVE, 80H) ;
+   StDataDi (D_RMANT) ;
+   XorDataDh (D_RMANT1) ;               (* sign1 XOR sign2 in the high byte *)
+   OrDiImm (8000H) ;                  (* b's hidden 1 *)
+   OrDhImm (80H) ;                    (* a's hidden 1 *)
+   M ("raadj16l") ;
+   CmpClImm (10H) ;
+   Jb8 ("raadj8") ;
+   MovAhBh ; MovBxDx ; XorDxDx ; SubClImm (10H) ;
+   Jmp8 ("raadj16l") ;
+   M ("raadj8") ;
+   CmpClImm (08H) ;
+   Jb8 ("raadj8l") ;
+   MovAhBl ; MovBlBh ; MovBhDl ; MovDlDh ; XorDhDh ; SubClImm (08H) ;
+   M ("raadj8l") ;
+   CmpCl0 ;
+   Je8 ("raadjend") ;
+   M ("raadj1l") ;
+   ShrDx1 ; RcrBx1 ; RcrAh1 ; DecCl ; Jne8 ("raadj1l") ;
+   M ("raadjend") ;
+   LdAlData (D_CVDEC) ;
+   TestDataImm (D_RMANT1, 80H) ;
+   Jne8 ("radosub") ;
+   AddAhCh ; AdcBxSi ; AdcDxDi ;
+   Jae8 ("rasign") ;
+   RcrDx1 ; RcrBx1 ; RcrAh1 ; IncAl ; Jne8 ("rasign") ;
+   Stc ; RetR ;
+   M ("radosub") ;
+   XchgAhCh ; XchgBxSi ; XchgDxDi ;
+   SubAhCh ; SbbBxSi ; SbbDxDi ;
+   Jae8 ("ranoneg") ;
+   XorDataImm (D_RESAVE, 80H) ;
+   NotAh ; NotBx ; NotDx ;
+   AddAhImm (1) ; AdcBxImm (0) ; AdcDxImm (0) ;
+   M ("ranoneg") ;
+   MovClImm (5) ;
+   M ("ranrm8l") ;
+   CmpDh0 ;
+   Jne8 ("ranrm1l") ;
+   MovDhDl ; MovDlBh ; MovBhBl ; MovBlAh ; XorAhAh ;
+   SubAl (08H) ;
+   Jb8 ("razero") ;
+   DecCl ;
+   Jne8 ("ranrm8l") ;
+   Jmp8 ("razero") ;
+   M ("ranrm1l") ;
+   TestDhImm (80H) ;
+   Jne8 ("rasign") ;
+   ShlAh1 ; RclBx1 ; RclDx1 ; DecAl ; Jne8 ("ranrm1l") ;
+   M ("razero") ;
+   XorAxAx ; XorBxBx ; XorDxDx ;
+   RetR ;
+   M ("rasign") ;
+   AndDhImm (7FH) ;
+   XorDhData (D_RESAVE) ;
+   RetR
+END EmitRealAddSub ;
+
 PROCEDURE EmitData ;
 (* 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
@@ -1518,6 +1750,18 @@ BEGIN
       HALT
    END ;
    B (0) ; B (0) ;                 (* the pushback slot, empty to begin with *)
+   IF dataAt + D_RSIGN # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;                 (* resign/resave *)
+   IF dataAt + D_RMANT # rpos THEN
+      HALT
+   END ;
+   B (0) ; B (0) ;                 (* remant/remant1 *)
+   IF dataAt + D_CVDEC # rpos THEN
+      HALT
+   END ;
+   B (0) ;
    WHILE rpos < dataAt + D_END DO
       B (0)
    END ;
@@ -1578,7 +1822,7 @@ BEGIN
    EmitRdChar ; EmitRdBool ; EmitRdLn ;
    EmitGetCh ; EmitUnGetCh ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
-   EmitRealFromInt ;
+   EmitRealFromInt ; EmitRealAddSub ;
    EmitData ;
    FixUp ;
    SetStr (entNm [0], "initmem") ;
@@ -1603,6 +1847,8 @@ BEGIN
    SetStr (entNm [19], "strcat") ;
    SetStr (entNm [20], "wrintw") ;
    SetStr (entNm [21], "real_from_int") ;
+   SetStr (entNm [22], "real_add") ;
+   SetStr (entNm [23], "real_sub") ;
    built := TRUE
 END RT_Build ;
 
@@ -1630,7 +1876,7 @@ BEGIN
    IF NOT built THEN
       RT_Build (0)
    END ;
-   IF i > 21 THEN
+   IF i > 23 THEN
       RETURN 0
    END ;
    RETURN rtBase + LblOff (entNm [i])

+ 5 - 3
shell/tests/RtProbe.mod

@@ -108,7 +108,7 @@ END PENT ;
 
 PROCEDURE Main ;
 VAR i, n, base : CARDINAL ;
-    names : ARRAY [0..21] OF ARRAY [0..15] OF CHAR ;
+    names : ARRAY [0..23] 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 <= 21 DO
+   WHILE i <= 23 DO
       names [i] := "" ;
       INC (i)
    END ;
@@ -134,8 +134,10 @@ BEGIN
    names [18] := "cmpstr" ; names [19] := "strcat" ;
    names [20] := "wrintw" ;
    names [21] := "real_from_int" ;
+   names [22] := "real_add" ;
+   names [23] := "real_sub" ;
    i := 0 ;
-   WHILE i <= 21 DO
+   WHILE i <= 23 DO
       PENT (i, names [i]) ;
       INC (i)
    END ;

+ 23 - 2
shell/tests/audit_helpers.py

@@ -106,6 +106,18 @@ NON_ONE_LINE = {
         # immediates; each is a fixed sequence, pinned by rt_exec's MBF cases.
         "MovAxImm": COMP, "Jmp8": COMP, "AndDhImm": COMP,
         "StDiOffBx": COMP, "StDiOffDx": COMP,
+        # The MBF core's parameterised moves and data-byte helpers.  Each is
+        # a fixed instruction/sequence; real_add/real_sub's 16 rt_exec cases
+        # pin the behaviour (alignment, add/sub, normalisation, sign).
+        "AdcBxImm": COMP, "AdcDxImm": COMP, "AddAhImm": COMP,
+        "AndDataImm": COMP, "Call": COMP, "CmpClImm": COMP,
+        "LdAlData": COMP, "LdClData": COMP, "LdCxDiOff": COMP,
+        "LdCxSiOff": COMP, "LdDiDiOff": COMP, "LdDxSiOff": COMP,
+        "LdSiDiOff": COMP, "MovClImm": COMP, "OrDhImm": COMP,
+        "OrDiImm": COMP, "StDataCl": COMP, "StDataDi": COMP, "StDataImm": COMP,
+        "SubClImm": COMP, "TestDhImm": COMP, "TestDataImm": COMP,
+        "XorDhData": COMP, "XorDiData": COMP, "XorDataDh": COMP,
+        "XorDataImm": COMP, "PushBp4": COMP,
         "MovBxImm": PARAM, "MovCxImm": PARAM, "MovDxImm": PARAM,
         "MovDl": PARAM, "MovAlD": PARAM, "MovAh": PARAM,
         "SubAl": PARAM, "CmpAl": PARAM, "AddDl": PARAM, "J8": COMP,
@@ -314,6 +326,7 @@ JCCS = ["Je", "Jne", "Jz", "Jnz", "Jge", "Jnl", "Jle", "Jl", "Ja", "Jae",
         "Jecxz", "Jrcxz", "Loop", "Loope", "Loopne"]
 
 _ALT = "|".join(REGS)
+_ALT16 = "Ax|Bx|Cx|Dx|Si|Di|Bp|Sp"
 _BASE = "Si|Di|Bx|Bp|Sp|Arg|Data"
 _STORE = (0x88, 0x89)      # reg is the source
 _LOAD = (0x8A, 0x8B)       # reg is the destination
@@ -379,8 +392,16 @@ PATTERNS = [
     # would accept B4 under the name MovAl0.
     # --- shifts: by one, and by CL.  The set membership test uses both:
     # D1 E8 (SHR AX,1) three times, and D2 E2 (SHL DL,CL) for the mask.
-    (re.compile(r"^ShrAx(1)$"), "shr", ["r:Ax", "i:%(1)s"], {0xD1}),
-    (re.compile(r"^ShlDx(1)$"), "shl", ["r:Dx", "i:%(1)s"], {0xD1}),
+    # Shifts and rotates by one, byte and word.  The MBF core shifts a 40-bit
+    # mantissa with SHR/RCR and normalises with SHL/RCL.
+    (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"^PushBp4$"), "push", ["m:Bp+4"], {0xFF}),
+    (re.compile(r"^Adc(%s)(%s)$" % (_ALT16, _ALT16)),
+     "adc", ["r:%(1)s", "r:%(2)s"], {0x11, 0x13}),
+    (re.compile(r"^Sbb(%s)(%s)$" % (_ALT16, _ALT16)),
+     "sbb", ["r:%(1)s", "r:%(2)s"], {0x18, 0x19, 0x1A, 0x1B}),
     (re.compile(r"^ShlDlCl$"),  "shl", ["r:Dl", "r:Cl"],     {0xD2}),
     # --- CMP reg,0 as a flag test (the 8086 has no OR reg,reg FCML can
     #     decode here; CMP sets the same ZF/SF/CF for a non-negative reg) ---

+ 3 - 0
shell/tests/check_runtime.py

@@ -132,6 +132,9 @@ GOLDEN = {
     # It keeps the destination in DI and reads the source through SI, so both
     # are saved and restored.
     "real_from_int": "55 8B EC 53 52 56 57",
+    # real_add/real_sub share a frame and the readd core.
+    "real_add": "55 8B EC 53 52 56 57",
+    "real_sub": "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

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

@@ -112,6 +112,14 @@
 					# 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
 
 	.code16
 	.text
@@ -410,3 +418,21 @@ HDR:
 
 	.org	SST_WORLD
 	.byte	5, 'w', 'o', 'r', 'l', 'd'
+
+# 12-byte MBF blocks for real_add/real_sub: a at +0, b at +6.
+	.org	R_BLK1
+	.byte	0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK2
+	.byte	0x82, 0x00, 0x00, 0x00, 0x00, 0x40, 0x83, 0x00, 0x00, 0x00, 0x00, 0x20
+	.org	R_BLK3
+	.byte	0x83, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x40
+	.org	R_BLK4
+	.byte	0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK5
+	.byte	0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK6
+	.byte	0x82, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
+	.org	R_BLK7
+	.byte	0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A
+	.org	R_BLK8
+	.byte	0x8F, 0x00, 0x00, 0x00, 0xFE, 0x7F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00

+ 21 - 0
shell/tests/nonvacuity.sh

@@ -1370,6 +1370,27 @@ fi
 cp "$SAVED" Runtime.mod
 "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
 
+# --- the MBF sign is stored, not XORed into DH --------------------------
+# XorDataDh is `30 36' (memory destination).  Encoding it as `32 36' makes it
+# XOR DH with the saved sign instead, which leaves the result with the
+# operand's fraction bits (the bug found while transcribing readd).  The
+# real_add/real_sub cases compare exact bytes.
+cp "$SAVED" Runtime.mod
+if mutate Runtime.mod 's|BEGIN B (30H) ; B (36H) ; Dd (delta) END XorDataDh|BEGIN B (32H) ; B (36H) ; Dd (delta) END XorDataDh|'; then
+    if "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1; then
+        expect_red "real_add red when the sign is XORed into DH, not memory" \
+            "real_add" python3 tests/rt_exec.py
+    else
+        echo "  FAIL: the runtime would not rebuild with the XOR direction wrong"
+        fail=$((fail + 1))
+    fi
+else
+    echo "  BROKEN CASE: the MBF XOR-direction 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 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

+ 17 - 1
shell/tests/rt_exec.py

@@ -103,7 +103,7 @@ 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"]
+        "wrintw", "real_from_int", "real_add", "real_sub"]
 
 # A call slot's flags, as rtdrv.s reads them.
 F_STACK = 1     # the argument goes on the stack, popped by the caller
@@ -493,6 +493,22 @@ def build_cases():
             dump=6, dumpbytes=mbf(v), dumpadr=STORE,
             note="the measured exponent bias is 129")
 
+    # --- MBF addition and subtraction -----------------------------------
+    # 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)]
+    for bi, (a, b) in enumerate(BLOCKS):
+        blk = 0x0930 + 16 * bi
+        add("real_add(%d,%d)" % (a, b),
+            [s("real_add", blk + LOAD_BIAS)],
+            dump=6, dumpbytes=mbf(a + b), dumpadr=blk,
+            note="the measured MBF bytes, the reference's readd")
+        add("real_sub(%d,%d)" % (a, b),
+            [s("real_sub", blk + LOAD_BIAS)],
+            dump=6, dumpbytes=mbf(a - b), dumpadr=blk,
+            note="the measured MBF bytes, the reference's resub")
+
     add("wrchar('A')", [s("wrchar", ord("A"))], out=b"A")
     add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")
 

+ 170 - 9
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 AD 04    mov bx,4adh
+003C  BB 15 06    mov bx,615h
 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 AD 04    mov bx,4adh
+00A8  BB 15 06    mov bx,615h
 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 B2 04    mov dx,4b2h
+00E6  BA 1A 06    mov dx,61ah
 00E9  EB 03       jmp 0eeh
-00EB  BA AD 04    mov dx,4adh
+00EB  BA 15 06    mov dx,615h
 00EE  B4 09       mov ah,9h
 00F0  CD 21       int 21h
 00F2  5D          pop bp
 00F3  C3          ret
-00F4  BA BB 04    mov dx,4bbh
+00F4  BA 23 06    mov dx,623h
 00F7  B4 09       mov ah,9h
 00F9  CD 21       int 21h
 00FB  C3          ret
-00FC  BA B8 04    mov dx,4b8h
+00FC  BA 20 06    mov dx,620h
 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 C1 04 mov bx,word ptr [4c1h]
+021B  8B 1E 29 06 mov bx,word ptr [629h]
 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 C1 04 mov word ptr [4c1h],bx
+0228  89 1E 29 06 mov word ptr [629h],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 C1 04 mov word ptr [4c1h],bx
+0239  89 1E 29 06 mov word ptr [629h],bx
 023D  C3          ret
 023E  55          push bp
 023F  8B EC       mov bp,sp
@@ -525,3 +525,164 @@
 03A1  89 EC       mov sp,bp
 03A3  5D          pop bp
 03A4  C3          ret
+03A5  55          push bp
+03A6  8B EC       mov bp,sp
+03A8  53          push bx
+03A9  52          push dx
+03AA  56          push si
+03AB  57          push di
+03AC  FF 76 04    push word ptr [bp+4h]
+03AF  8B 76 04    mov si,word ptr [bp+4h]
+03B2  8B 04       mov ax,word ptr [si]
+03B4  8B 4C 02    mov cx,word ptr [si+2h]
+03B7  8B 54 04    mov dx,word ptr [si+4h]
+03BA  89 CB       mov bx,cx
+03BC  8B FE       mov di,si
+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 2B 06 00 00 mov word ptr [62bh],0h
+03CD  E8 4D 00    call 41dh
+03D0  5F          pop di
+03D1  89 05       mov word ptr [di],ax
+03D3  89 5D 02    mov word ptr [di+2h],bx
+03D6  89 55 04    mov word ptr [di+4h],dx
+03D9  5F          pop di
+03DA  5E          pop si
+03DB  5A          pop dx
+03DC  5B          pop bx
+03DD  89 EC       mov sp,bp
+03DF  5D          pop bp
+03E0  C3          ret
+03E1  55          push bp
+03E2  8B EC       mov bp,sp
+03E4  53          push bx
+03E5  52          push dx
+03E6  56          push si
+03E7  57          push di
+03E8  FF 76 04    push word ptr [bp+4h]
+03EB  8B 76 04    mov si,word ptr [bp+4h]
+03EE  8B 04       mov ax,word ptr [si]
+03F0  8B 4C 02    mov cx,word ptr [si+2h]
+03F3  8B 54 04    mov dx,word ptr [si+4h]
+03F6  89 CB       mov bx,cx
+03F8  8B FE       mov di,si
+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 2B 06 00 80 mov word ptr [62bh],8000h
+0409  E8 11 00    call 41dh
+040C  5F          pop di
+040D  89 05       mov word ptr [di],ax
+040F  89 5D 02    mov word ptr [di+2h],bx
+0412  89 55 04    mov word ptr [di+4h],dx
+0415  5F          pop di
+0416  5E          pop si
+0417  5A          pop dx
+0418  5B          pop bx
+0419  89 EC       mov sp,bp
+041B  5D          pop bp
+041C  C3          ret
+041D  80 F9 00    cmp cl,0h
+0420  74 0E       je 430h
+0422  33 3E 2B 06 xor di,word ptr [62bh]
+0426  3C 00       cmp al,0h
+0428  75 07       jne 431h
+042A  89 C8       mov ax,cx
+042C  89 F3       mov bx,si
+042E  89 FA       mov dx,di
+0430  C3          ret
+0431  38 C8       cmp al,cl
+0433  76 05       jbe 43ah
+0435  91          xchg ax,cx
+0436  87 F3       xchg bx,si
+0438  87 FA       xchg dx,di
+043A  88 0E 2F 06 mov byte ptr [62fh],cl
+043E  28 C1       sub cl,al
+0440  80 F9 28    cmp cl,28h
+0443  72 06       jb 44bh
+0445  8A 0E 2F 06 mov cl,byte ptr [62fh]
+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
+045C  81 CF 00 80 or di,8000h
+0460  80 CE 80    or dh,80h
+0463  80 F9 10    cmp cl,10h
+0466  72 0B       jb 473h
+0468  88 FC       mov ah,bh
+046A  89 D3       mov bx,dx
+046C  31 D2       xor dx,dx
+046E  80 E9 10    sub cl,10h
+0471  EB F0       jmp 463h
+0473  80 F9 08    cmp cl,8h
+0476  72 0D       jb 485h
+0478  88 DC       mov ah,bl
+047A  88 FB       mov bl,bh
+047C  88 D7       mov bh,dl
+047E  88 F2       mov dl,dh
+0480  30 F6       xor dh,dh
+0482  80 E9 08    sub cl,8h
+0485  80 F9 00    cmp cl,0h
+0488  74 0A       je 494h
+048A  D1 EA       shr dx,1h
+048C  D1 DB       rcr bx,1h
+048E  D0 DC       rcr ah,1h
+0490  FE C9       dec cl
+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
+049C  75 14       jne 4b2h
+049E  00 EC       add ah,ch
+04A0  11 F3       adc bx,si
+04A2  11 FA       adc dx,di
+04A4  73 5F       jnb 505h
+04A6  D1 DA       rcr dx,1h
+04A8  D1 DB       rcr bx,1h
+04AA  D0 DC       rcr ah,1h
+04AC  FE C0       inc al
+04AE  75 55       jne 505h
+04B0  F9          stc
+04B1  C3          ret
+04B2  86 EC       xchg ah,ch
+04B4  87 F3       xchg bx,si
+04B6  87 FA       xchg dx,di
+04B8  28 EC       sub ah,ch
+04BA  19 F3       sbb bx,si
+04BC  19 FA       sbb dx,di
+04BE  73 14       jnb 4d4h
+04C0  80 36 2C 06 80 xor byte ptr [62ch],80h
+04C5  F6 D4       not ah
+04C7  F7 D3       not bx
+04C9  F7 D2       not dx
+04CB  80 C4 01    add ah,1h
+04CE  83 D3 00    adc bx,0h
+04D1  83 D2 00    adc dx,0h
+04D4  B1 05       mov cl,5h
+04D6  80 FE 00    cmp dh,0h
+04D9  75 14       jne 4efh
+04DB  88 D6       mov dh,dl
+04DD  88 FA       mov dl,bh
+04DF  88 DF       mov bh,bl
+04E1  88 E3       mov bl,ah
+04E3  30 E4       xor ah,ah
+04E5  2C 08       sub al,8h
+04E7  72 15       jb 4feh
+04E9  FE C9       dec cl
+04EB  75 E9       jne 4d6h
+04ED  EB 0F       jmp 4feh
+04EF  F6 C6 80    test dh,80h
+04F2  75 11       jne 505h
+04F4  D0 E4       shl ah,1h
+04F6  D1 D3       rcl bx,1h
+04F8  D1 D2       rcl dx,1h
+04FA  FE C8       dec al
+04FC  75 F1       jne 4efh
+04FE  31 C0       xor ax,ax
+0500  31 DB       xor bx,bx
+0502  31 D2       xor dx,dx
+0504  C3          ret
+0505  80 E6 7F    and dh,7fh
+0508  32 36 2C 06 xor dh,byte ptr [62ch]
+050C  C3          ret