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 2 dny
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
    E_RealFromInt = 21 ;     (* integer -> 6-byte MBF real: the source word's
                                address is at [BP+6] and the destination's at
                                address is at [BP+6] and the destination's at
                                [BP+4] *)
                                [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
    (* 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

+ 252 - 6
shell/Runtime.mod

@@ -47,8 +47,8 @@ CONST
       that case; the fill checks below make any future overflow a HALT instead
       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
       of wrong code.  tests/check_runtime.py's branch-boundary check is what
       caught this one. *)
       caught this one. *)
-   MaxLbl = 96 ;
-   MaxFix = 512 ;
+   MaxLbl = 160 ;       (* the MBF core's labels took the count past 96 *)
+   MaxFix = 768 ;
    MaxNm  = 15 ;
    MaxNm  = 15 ;
 
 
    (* Offsets inside the runtime's own data block.  These must agree with the
    (* 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
                           word is 0 exactly when the slot is empty - a NUL in
                           the input would otherwise be indistinguishable from
                           the input would otherwise be indistinguishable from
                           "nothing pushed back".  Byte 30 is unused. *)
                           "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
    (* 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
@@ -127,7 +133,7 @@ VAR
    dataAt : CARDINAL ;
    dataAt : CARDINAL ;
    rtBase : CARDINAL ;
    rtBase : CARDINAL ;
    built  : BOOLEAN ;
    built  : BOOLEAN ;
-   entNm  : ARRAY [0..21] OF ARRAY [0..MaxNm] OF CHAR ;
+   entNm  : ARRAY [0..23] OF ARRAY [0..MaxNm] OF CHAR ;
 
 
 (* ---------------------------------------------------------------- *)
 (* ---------------------------------------------------------------- *)
 (*  name helpers                                                     *)
 (*  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 CmpAl0   ; BEGIN B (3CH) ; B (0) END CmpAl0 ;         (* 3C 00 *)
 PROCEDURE AndDhImm (v : CARDINAL) ;
 PROCEDURE AndDhImm (v : CARDINAL) ;
 BEGIN B (80H) ; B (0E6H) ; B (v MOD 256) END AndDhImm ;   (* 80 E6 imm *)
 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 ) ;
 PROCEDURE StVxBx   (delta : CARDINAL ) ;
 (* 89 1E lo hi: MOV [Vx],BX - the mirror of LdBxVx above, and it must use the
 (* 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
    same mod=00 / rm=110 and the same Dd fixup kind, or one of the two
@@ -1482,6 +1585,135 @@ BEGIN
    MovSpBp ; PopBp ; RetR
    MovSpBp ; PopBp ; RetR
 END EmitRealFromInt ;
 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 ;
 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
@@ -1518,6 +1750,18 @@ BEGIN
       HALT
       HALT
    END ;
    END ;
    B (0) ; B (0) ;                 (* the pushback slot, empty to begin with *)
    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
    WHILE rpos < dataAt + D_END DO
       B (0)
       B (0)
    END ;
    END ;
@@ -1578,7 +1822,7 @@ BEGIN
    EmitRdChar ; EmitRdBool ; EmitRdLn ;
    EmitRdChar ; EmitRdBool ; EmitRdLn ;
    EmitGetCh ; EmitUnGetCh ;
    EmitGetCh ; EmitUnGetCh ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
    EmitRdStr ; EmitStrStore ; EmitCmpStr ; EmitStrCat ;
-   EmitRealFromInt ;
+   EmitRealFromInt ; EmitRealAddSub ;
    EmitData ;
    EmitData ;
    FixUp ;
    FixUp ;
    SetStr (entNm [0], "initmem") ;
    SetStr (entNm [0], "initmem") ;
@@ -1603,6 +1847,8 @@ BEGIN
    SetStr (entNm [19], "strcat") ;
    SetStr (entNm [19], "strcat") ;
    SetStr (entNm [20], "wrintw") ;
    SetStr (entNm [20], "wrintw") ;
    SetStr (entNm [21], "real_from_int") ;
    SetStr (entNm [21], "real_from_int") ;
+   SetStr (entNm [22], "real_add") ;
+   SetStr (entNm [23], "real_sub") ;
    built := TRUE
    built := TRUE
 END RT_Build ;
 END RT_Build ;
 
 
@@ -1630,7 +1876,7 @@ BEGIN
    IF NOT built THEN
    IF NOT built THEN
       RT_Build (0)
       RT_Build (0)
    END ;
    END ;
-   IF i > 21 THEN
+   IF i > 23 THEN
       RETURN 0
       RETURN 0
    END ;
    END ;
    RETURN rtBase + LblOff (entNm [i])
    RETURN rtBase + LblOff (entNm [i])

+ 5 - 3
shell/tests/RtProbe.mod

@@ -108,7 +108,7 @@ END PENT ;
 
 
 PROCEDURE Main ;
 PROCEDURE Main ;
 VAR i, n, base : CARDINAL ;
 VAR i, n, base : CARDINAL ;
-    names : ARRAY [0..21] OF ARRAY [0..15] OF CHAR ;
+    names : ARRAY [0..23] 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 <= 21 DO
+   WHILE i <= 23 DO
       names [i] := "" ;
       names [i] := "" ;
       INC (i)
       INC (i)
    END ;
    END ;
@@ -134,8 +134,10 @@ BEGIN
    names [18] := "cmpstr" ; names [19] := "strcat" ;
    names [18] := "cmpstr" ; names [19] := "strcat" ;
    names [20] := "wrintw" ;
    names [20] := "wrintw" ;
    names [21] := "real_from_int" ;
    names [21] := "real_from_int" ;
+   names [22] := "real_add" ;
+   names [23] := "real_sub" ;
    i := 0 ;
    i := 0 ;
-   WHILE i <= 21 DO
+   WHILE i <= 23 DO
       PENT (i, names [i]) ;
       PENT (i, names [i]) ;
       INC (i)
       INC (i)
    END ;
    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.
         # immediates; each is a fixed sequence, pinned by rt_exec's MBF cases.
         "MovAxImm": COMP, "Jmp8": COMP, "AndDhImm": COMP,
         "MovAxImm": COMP, "Jmp8": COMP, "AndDhImm": COMP,
         "StDiOffBx": COMP, "StDiOffDx": 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,
         "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,
@@ -314,6 +326,7 @@ JCCS = ["Je", "Jne", "Jz", "Jnz", "Jge", "Jnl", "Jle", "Jl", "Ja", "Jae",
         "Jecxz", "Jrcxz", "Loop", "Loope", "Loopne"]
         "Jecxz", "Jrcxz", "Loop", "Loope", "Loopne"]
 
 
 _ALT = "|".join(REGS)
 _ALT = "|".join(REGS)
+_ALT16 = "Ax|Bx|Cx|Dx|Si|Di|Bp|Sp"
 _BASE = "Si|Di|Bx|Bp|Sp|Arg|Data"
 _BASE = "Si|Di|Bx|Bp|Sp|Arg|Data"
 _STORE = (0x88, 0x89)      # reg is the source
 _STORE = (0x88, 0x89)      # reg is the source
 _LOAD = (0x8A, 0x8B)       # reg is the destination
 _LOAD = (0x8A, 0x8B)       # reg is the destination
@@ -379,8 +392,16 @@ PATTERNS = [
     # would accept B4 under the name MovAl0.
     # would accept B4 under the name MovAl0.
     # --- shifts: by one, and by CL.  The set membership test uses both:
     # --- 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.
     # 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}),
     (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
     # --- 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) ---
     #     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
     # It keeps the destination in DI and reads the source through SI, so both
     # are saved and restored.
     # are saved and restored.
     "real_from_int": "55 8B EC 53 52 56 57",
     "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
     # 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

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

@@ -112,6 +112,14 @@
 					# which strstore overwrites
 					# which strstore overwrites
 	.set	SST_CAT0, 0x0910	# ...and one that starts empty
 	.set	SST_CAT0, 0x0910	# ...and one that starts empty
 	.set	SST_WORLD, 0x0920	# a source distinct from SST_HELLO
 	.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
 	.code16
 	.text
 	.text
@@ -410,3 +418,21 @@ HDR:
 
 
 	.org	SST_WORLD
 	.org	SST_WORLD
 	.byte	5, 'w', 'o', 'r', 'l', 'd'
 	.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
 cp "$SAVED" Runtime.mod
 "$GM2" -fiso -c Runtime.mod >/dev/null 2>&1
 "$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 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

+ 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",
 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"]
+        "wrintw", "real_from_int", "real_add", "real_sub"]
 
 
 # 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
@@ -493,6 +493,22 @@ def build_cases():
             dump=6, dumpbytes=mbf(v), dumpadr=STORE,
             dump=6, dumpbytes=mbf(v), dumpadr=STORE,
             note="the measured exponent bias is 129")
             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('A')", [s("wrchar", ord("A"))], out=b"A")
     add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")
     add("wrchar('!')", [s("wrchar", ord("!"))], out=b"!")
 
 

+ 170 - 9
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 AD 04    mov bx,4adh
+003C  BB 15 06    mov bx,615h
 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 AD 04    mov bx,4adh
+00A8  BB 15 06    mov bx,615h
 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 B2 04    mov dx,4b2h
+00E6  BA 1A 06    mov dx,61ah
 00E9  EB 03       jmp 0eeh
 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
 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 BB 04    mov dx,4bbh
+00F4  BA 23 06    mov dx,623h
 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 B8 04    mov dx,4b8h
+00FC  BA 20 06    mov dx,620h
 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 C1 04 mov bx,word ptr [4c1h]
+021B  8B 1E 29 06 mov bx,word ptr [629h]
 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 C1 04 mov word ptr [4c1h],bx
+0228  89 1E 29 06 mov word ptr [629h],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 C1 04 mov word ptr [4c1h],bx
+0239  89 1E 29 06 mov word ptr [629h],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
@@ -525,3 +525,164 @@
 03A1  89 EC       mov sp,bp
 03A1  89 EC       mov sp,bp
 03A3  5D          pop bp
 03A3  5D          pop bp
 03A4  C3          ret
 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