home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / PASCAL / TPL60N19 / ARISOURC / FP48KER.ASM < prev    next >
Assembly Source File  |  1993-01-25  |  41KB  |  899 lines

  1.  
  2. ; *******************************************************
  3. ; *                                                     *
  4. ; *     Turbo Pascal Runtime Library Version 6.0        *
  5. ; *     Real Kernel Routines (Add,Sub,Mul,Div,Sqr)      *
  6. ; *                                                     *
  7. ; *     Copyright (C) 1989-1992 Norbert Juffa           *
  8. ; *                                                     *
  9. ; *******************************************************
  10.  
  11.              TITLE   FP48KER
  12.  
  13.              INCLUDE SE.ASM
  14.  
  15. ;-------------------------------------------------------------------------------
  16. ;
  17. ;  Turbo Pascal REAL floating-point format
  18. ;
  19. ;  47 46                            8 7       0
  20. ;  +--+------------------------------+--------+
  21. ;  |S |           Mantissa           |Exponent|
  22. ;  +--+------------------------------+--------+
  23. ;
  24. ;  47             31          15      7       0
  25. ;  +-------------+------------+------+--------+
  26. ;  |     DX      |     BX     |  AH  |   AL   |
  27. ;  +-------------+------------+------+--------+
  28. ;
  29. ;  47             31          15      7       0
  30. ;  +-------------+------------+------+--------+
  31. ;  |     DI      |     SI     |  CH  |   CL   |
  32. ;  +-------------+------------+------+--------+
  33. ;
  34. ;  value = 1^(-S) * Mantissa/2^40 * 2^(Exponent - 129)
  35. ;
  36. ;-------------------------------------------------------------------------------
  37.  
  38.  
  39. CODE         SEGMENT BYTE PUBLIC
  40.  
  41.              ASSUME  CS:CODE
  42.  
  43. ; Externals
  44.              EXTRN   HaltError:NEAR
  45.  
  46. ; Publics
  47.  
  48.              PUBLIC  RealAdd,RealSub,RealMul,RealDiv
  49.              PUBLIC  RealSqr,RealSqrNoChk,RealDivRev
  50.              PUBLIC  RealMulNoChk,RealMulNChk2
  51.              PUBLIC  RAdd,RSub,RMul,RDiv,RSqr,ROverflow
  52.  
  53. ;-------------------------------------------------------------------------------
  54. ; RealAdd and RealSub are the routines for adding and subtracting two numbers
  55. ; in the Turbo Pascal 6 byte floating point format. They are practically ident-
  56. ; ical, since subtraction is implemented as an addition with a negated second
  57. ; addend. If underflow occurs, zero is returned. On overflow the carry flag
  58. ; will be set. The rounding of these routines complies with the IEEE "round to
  59. ; nearest or even" mode. Guard and sticky flags are therefore fully implemented.
  60. ;
  61. ; INPUT:     DX:BX:AX  first addend
  62. ;            DI:SI:CX  second addend
  63. ;
  64. ; OUTPUT:    DX:BX:AX  sum
  65. ;            CF        set if overflow occured, else cleared
  66. ;
  67. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  68. ;-------------------------------------------------------------------------------
  69.  
  70. AddExt       PROC    NEAR
  71. $ret_second: XCHG    AX, CX            ; load second addend
  72.              MOV     BX, SI            ;  into DX:BX:AX (DX currently loaded)
  73.              RET                       ; done
  74. AddExt       ENDP
  75.  
  76.  
  77. RealSub      PROC    NEAR
  78.              XOR     DI, 8000h         ; negate second argument
  79. RealSub      ENDP
  80.  
  81. RealAdd      PROC    NEAR
  82.              CMP     CL, AL            ; second addend bigger ?
  83.              JAE     $bigger           ; yes
  84.              XCHG    AX, CX            ; no,
  85.              XCHG    BX, SI            ;  exchange
  86.              XCHG    DX, DI            ;   addends
  87. $bigger:     XCHG    DX, DI            ; DX = msb of second addend
  88.              NEG     AL                ; smaller addend zero ?
  89.              JZ      $ret_second       ; yes, return other addend
  90.              ADD     AL, CL            ; compute difference of exponents
  91.              CMP     AL, 41            ; difference too big ?
  92.              JA      $ret_second       ; yes, add/sub will not change bigger arg
  93.              PUSH    BP                ; save TURBO-Pascal frame pointer
  94.              MOV     BP, 0FF00h        ; load mask for msb
  95.              AND     BP, CX            ; save msb of second addend
  96.              MOV     CH, 80h           ; mask for sign bit
  97.              AND     CH, DH            ; sign bit of second addend
  98.              PUSH    CX                ; save sign and exponent
  99.              XOR     CX, DI            ; test if operands have different sign
  100.              PUSHF                     ; save sign indicator
  101.              OR      DH, 80h           ; set implicit bit in second addend
  102.              XCHG    DX, DI            ; DX = msb of first addend
  103.              OR      DH, 80h           ; set implicit bit in first addend
  104.              XOR     CX, CX            ; set guard and sticky bytes to 0
  105.  
  106.              IFDEF   USE386
  107. .386
  108.              XCHG    CL, AL            ; shift counter in CX
  109.              SHL     EDX, 16
  110.              MOV     DX, BX
  111.              XOR     EBX, EBX
  112.              SHRD    EBX, EAX, 16
  113.              XOR     EAX, EAX          ; mantissa in EDX:EBX:EAX
  114.              CMP     CX, 32
  115.              JB      $do_32
  116.              XCHG    EAX, EDX
  117.              XCHG    EAX, EBX
  118. $do_32:      SHRD    EAX, EBX, CL
  119.              SHRD    EBX, EDX, CL
  120.              SHR     EDX, CL
  121.              NEG     EAX
  122.              SBB     CX, CX
  123.              OR      CX, BX
  124.              MOV     BX, DX
  125.              SHR     EDX, 16
  126.              SHLD    EAX, EBX, 16
  127. .8086
  128.              ELSE
  129.  
  130. comment #
  131.              XCHG    CL, AL
  132. $try_16:     CMP     CL, 16
  133.              JB      $try_1
  134.              OR      CH, AL
  135.              OR      CH, AH
  136.              MOV     AX, BX
  137.              MOV     BX, DX
  138.              XOR     DX, DX
  139.              SUB     CL, 16
  140.              JMP     $try_16
  141. $try_1:      PUSH    SI
  142.              NEG     CH
  143.              SBB     SI, SI
  144.              SHRD    SI, AX, CL
  145.              SHRD    AX, BX, CL
  146.              SHRD    BX, DX, CL
  147.              SHR     DX, CL
  148.              NEG     SI
  149.              SBB     CH, CH
  150.              POP     SI
  151.              MOV     CL, CH
  152.  
  153.              ELSE
  154. #
  155.              XCHG    AL, CH            ; DX:BX:AX = mantissa, CH = shift counter
  156. $test_shift: CMP     CH, 4             ; less than 4 bit shifts necessary ?
  157.              JB      $bit_shift        ; yes, do it one bit at a time
  158.              CMP     CH, 8             ; between 4 and 7 bit shifts necessary ?
  159.              JB      $4bit_shift       ; yes, do 4 bit shift first
  160.              CMP     CH, 16            ; between 8 and 15 bit shifts necessary ?
  161.              JB      $byte_shift       ; yes, do byte shift first
  162.              OR      CL, AL            ; accumulate
  163.              OR      CL, AH            ;  sticky byte
  164.              XCHG    AX, DX            ; shift
  165.              XCHG    AX, BX            ;  mantissa 16 bits
  166.              XOR     DX, DX            ;   to the right
  167.              SUB     CH, 16            ; decrement shift counter by 16
  168.              JMP     $test_shift       ; test remaining shifts
  169. $byte_shift: OR      CL, AL            ; accumulate sticky byte
  170.              MOV     AL, AH            ; shift
  171.              MOV     AH, BL            ;  mantissa
  172.              MOV     BL, BH            ;   eight
  173.              MOV     BH, DL            ;    bits
  174.              MOV     DL, DH            ;     to the
  175.              XOR     DH, DH            ;      right
  176.              TEST    CH, 4             ; 4 bit shift possible ?
  177.              JZ      $bit_shift        ; no, try single bit shifts
  178. $4bit_shift: NEG     CL                ; set sticky flag = FFh
  179.              SBB     CL, CL            ;  if <> 0 before
  180.              OR      CL, AL            ; accumulate
  181.              AND     CL, 0Fh           ;  sticky flag
  182.              SHR     DX, 1             ; shift
  183.              RCR     BX, 1             ;  mantissa
  184.              RCR     AX, 1             ;   1 bit to the right
  185.              SHR     DX, 1             ; shift
  186.              RCR     BX, 1             ;  mantissa
  187.              RCR     AX, 1             ;   1 bit to the right
  188.              SHR     DX, 1             ; shift
  189.              RCR     BX, 1             ;  mantissa
  190.              RCR     AX, 1             ;   1 bit to the right
  191.              SHR     DX, 1             ; shift
  192.              RCR     BX, 1             ;  mantissa
  193.              RCR     AX, 1             ;   1 bit to the right
  194. $bit_shift:  AND     CH, 3             ; compute number of single bit shifts
  195.              JZ      $shift_done       ; no shifts necessary, mantissas aligned
  196.              NEG     CL                ; set sticky flag to FFh
  197.              SBB     CL, CL            ;  if <> 0 before
  198.  
  199.              ALIGN   4
  200.  
  201. $bit_loop:   SHR     DX, 1             ; shift
  202.              RCR     BX, 1             ;  mantissa
  203.              RCR     AX, 1             ;   1 bit to the right
  204.              ADC     CL, CL            ; accumulate sticky byte
  205.              DEC     CH                ; decrement shift counter
  206.              JNZ     $bit_loop         ; until shift counter zero
  207.  
  208.              ENDIF
  209.  
  210. $shift_done: POPF                      ; signs of addends different ?
  211.              JS      $subtract         ; sign of addends differ
  212.              ADD     AX, BP            ; add
  213.              ADC     BX, SI            ;  mantissas
  214.              ADC     DX, DI            ;   of two addends
  215.              MOV     BP, CX            ; get sticky byte
  216.              POP     CX                ; get exponent and sign
  217.              JNC     $no_overf         ; no mantissa overflow
  218.              SHR     DX, 1             ; divide
  219.              RCR     BX, 1             ;  mantissa
  220.              RCR     AX, 1             ;   by two
  221.              INC     CX                ; adjust exponent
  222. $no_overf:   DEC     CX                ; exponent-1
  223.              JMP     $add_sub_end      ; do rounding
  224. $ret_first:  POP     BP                ; restore TURBO-Pascal frame pointer
  225.              RET                       ; done
  226. $subtract:   XCHG    AX, BP            ; exchange
  227.              XCHG    BX, SI            ;  addends
  228.              XCHG    DX, DI            ;   for correct order
  229.              NEG     CX                ; set carry if sticky byte <> 0
  230.              SBB     AX, BP            ; subtract
  231.              SBB     BX, SI            ;  the two
  232.              SBB     DX, DI            ;   mantissas
  233.              MOV     BP, CX            ; get sticky byte
  234.              POP     CX                ; get exponent and sign of result
  235.              JNC     $no_negate        ; no negative result
  236.              XOR     CH, 80h           ; result has other sign than 2. addend
  237.              NOT     DX                ; negate
  238.              NOT     BX                ;  number
  239.              NEG     AX                ;   in
  240.              SBB     BX, -1            ;    DX:BX:AX
  241.              SBB     DX, -1            ;     "
  242. $no_negate:  JS      $no_overf         ; mantissa normalized
  243.              JZ      $test_z1          ; first mantissa word is zero
  244.  
  245.              ALIGN   4
  246.  
  247. $shift_l:    DEC     CX                ; adjust exponent
  248.              ADD     AX, AX            ; multiply
  249.              ADC     BX, BX            ;  mantissa
  250.              ADC     DX, DX            ;   by two
  251.              JNS     $shift_l          ; normalized? no
  252.              DEC     CX                ; exponent-1
  253.              JMP     $add_sub_end      ; do rounding
  254. $test_z1:    XCHG    BX, AX            ; do a 16-bit
  255.              XCHG    DX, AX            ;  left shift of the mantissa
  256.              SUB     CX, 16            ; adjust exponent
  257.              OR      DX, DX            ; first mantissa word zero?
  258.              JG      $shift_l          ; not zero, no sign
  259.              JS      $no_overf         ; mantissa normalized
  260.              XCHG    DX, BX            ; shift mantissa 16 bits left (AX=0)
  261.              SUB     CX, 16            ; adjust exponent
  262.              OR      DX, DX            ; first mantissa word zero ?
  263.              JG      $shift_l          ; not zero, no sign
  264.              JS      $no_overf         ; mantissa normalized
  265.              POP     BP                ; mantissa zero, return DX:BX:AX=0
  266.              RET                       ; done
  267.  
  268. RealAdd      ENDP
  269.  
  270.  
  271.  
  272. ;-------------------------------------------------------------------------------
  273. ; RealMul multiplies two numbers in the Turbo Pascal 6 byte floating point
  274. ; format. If underflow occurs, zero is returned. On overflow the carry flag
  275. ; will be set. The routine multiplies the mantissas by computing nine partial
  276. ; products using the 80x86 MUL instruction. RealMulNoChk is the same routine
  277. ; as RealMul but does not check the operand in DI:SI:CX for zero. The fastest
  278. ; multiplication routine, RealMulNChk2, does not check either operand for zero.
  279. ; The rounding of this routine complies with IEEE "round to nearest or even"
  280. ; mode. For this purpose, guard and sticky flags are implemented.
  281. ;
  282. ; INPUT:     DX:BX:AX  multiplicand
  283. ;            DI:SI:CX  multiplicator
  284. ;
  285. ; OUTPUT:    DX:BX:AX  product
  286. ;            CF        set if overflow occured, else cleared
  287. ;
  288. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  289. ;-------------------------------------------------------------------------------
  290.  
  291.              ALIGN   4
  292.  
  293. RealMul      PROC    NEAR
  294.              OR      CL, CL            ; multiplicator = 0 ?
  295.              JZ      $zero_res         ; result will be 0
  296.  
  297. RealMulNoChk PROC    NEAR
  298.              OR      AL, AL            ; multiplicand = 0 ?
  299.              JZ      $zero_res         ; result is zero
  300.  
  301. RealMulNChk2 PROC    NEAR
  302.              PUSH    BP                ; save TURBO-framepointer
  303.              XCHG    BX, DI            ; BX = b1, DI = a2
  304.              MOV     BP, DX            ; get sign of multiplicant
  305.              XOR     BP, BX            ; compute sign of result
  306.              AND     BP, 8000h         ; mask out sign bit
  307.              XCHG    AL, CH            ; save b3
  308.              ADD     CL, CH            ; sum of biased exponents
  309.              SBB     CH, CH            ; clear msb
  310.              NEG     CH                ;  and put possible overflow in CH
  311.              OR      CX, BP            ; zap in sign bit
  312.              PUSH    CX                ; save new exponent and sign bit
  313.              XOR     CX, CX            ; clear lo-bytes of a3 and b3
  314.              OR      DH, 80h           ; set implicit bit of multipicand
  315.              OR      BH, 80h           ; set implicit bit of multiplicator
  316.              OR      SI, SI            ; b2 = 0 ?
  317.              JZ      $test_short       ; yes, test if b3 = 0
  318.              OR      DI, DI            ; a2 = 0 ?
  319.              JNZ     $full_mult        ; no, use full multiplication
  320.              OR      AH, AH            ; a3 = 0 ?
  321.              JNZ     $full_mult        ; no, use full multiplication
  322.              XCHG    AH, AL            ; swap a3 and b3
  323.              XCHG    DI, SI            ; swap a2 and b2
  324.              XCHG    DX, BX            ; swap a1 and b1
  325. $test_short: OR      AL, AL            ; b3 = 0 ?
  326.              JNZ     $full_mult        ; no, use full multiplication
  327.              MOV     SI, DX            ; save a1
  328.              MUL     BX                ; b1 * a3
  329.              MOV     BP, AX            ; generate sticky byte = 0
  330.              XCHG    AX, DX            ; AX = msw of product
  331.              XCHG    AX, DI            ; save msw of product, get a2
  332.              MUL     BX                ; b1 * a2
  333.              XCHG    AX, BX            ; save lsw of product, get b1
  334.              XCHG    DX, SI            ; save msw of product, get a1
  335.              ADD     BX, DI            ; add product
  336.              ADC     SI, CX            ;  to FPA
  337.              MUL     DX                ; b1 * a1
  338.              ADD     AX, SI            ; add product
  339.              ADC     DX, CX            ;  result in DX:AX:BX
  340.              JMP     $end_mantiss      ; handle exponent
  341. $zero_res:   JMP     $zero_prod2       ; result is 0
  342.  
  343.              ALIGN   4
  344.  
  345.  
  346.              IFDEF   USE386
  347.  
  348.              .386
  349.  
  350. $full_mult:  XCHG    AL, CH            ; CH = b3, AL = 0
  351.  
  352. ; b1 = BX
  353. ; b2 = SI
  354. ; b3 = CX
  355. ; a1 = DX
  356. ; a2 = DI
  357. ; a3 = AX
  358.              SHL     ESI, 16
  359.              SHRD    ESI, EBX, 16      ; b1:b2 in ESI
  360.              SHL     EDI, 16
  361.              SHRD    EDI, EDX, 16      ; a1:a2 in EDI
  362.              MOV     BX, AX            ;
  363.              MUL     CX                ; a3*b3
  364.              SHL     EBX, 16           ; a3
  365.              SHL     ECX, 16           ; b3
  366.              SHL     EAX, 16
  367.              SHRD    EAX, EDX, 16      ; result in EAX
  368.              XCHG    EAX, EBX          ; save a3*b3
  369.              MUL     ESI               ; a3*(b1:b2)
  370.              ADD     EBX, EAX
  371.              XCHG    EAX, ECX          ; get b3
  372.              MOV     ECX, 0
  373.              ADC     ECX, EDX          ; ECX:EBX
  374.              MUL     EDI               ; b3*(a1:a2)
  375.              ADD     EBX, EAX
  376.              MOV     EAX, 0
  377.              ADC     ECX, EDX
  378.              XCHG    EAX, EDI
  379.              ADC     EDI, EDI
  380.              MUL     ESI
  381.              ADD     EAX, ECX
  382.              ADC     EDX, EDI           ; EDX:EAX:EBX
  383.              NEG     EBX
  384.              SBB     BP, BP
  385.              OR      BP, AX
  386.              SHLD    EBX, EAX, 16
  387.              XCHG    AX, DX
  388.              SHR     EDX, 16
  389.              JMPS    $end_mantiss
  390.  
  391. $sqr_end:    ADC     DI, DX            ;  to   SI:DI:BX
  392.              ADC     SI, 0             ;   FPA
  393.              MUL     CX                ; a1 * b1
  394.              ADD     AX, DI
  395.              ADC     DX, SI            ; result in DX:AX:BX
  396.  
  397.             .8086
  398.  
  399.              ELSEIFDEF   FASTOPS
  400.  
  401.              ALIGN   4
  402.  
  403. $full_mult:  XCHG    AL, CH            ; CH = b3, AL = 0
  404.              PUSH    BX                ; save b1
  405.              PUSH    DX                ; save a1
  406.              MOV     BP, DX            ; save a1
  407. ; b1 = BX
  408. ; b2 = SI
  409. ; b3 = CX
  410. ; a1 = DX
  411. ; a2 = DI
  412. ; a3 = AX
  413.              MUL     BX                ; b1 * a3
  414.              XOR     BX, BX            ; clear FPA
  415.              XCHG    AX, CX            ; get b3, save LSW (b1*a3)
  416.              XCHG    DX, BP            ; get a1, save MSW (b1*a3)
  417.              MUL     DX                ; a1 * b3
  418.              ADD     CX, AX            ; add
  419.              ADC     BP, DX            ;  result
  420.              ADC     BX, BX            ;   to FPA
  421.              MOV     AX, SI            ; b2
  422.              MUL     DI                ; a2 * b2
  423.              ADC     CX, AX
  424.              ADC     BP, DX
  425.              ADC     BX, 0
  426.              XOR     CX, CX            ; FPA = CX:BX:BP
  427.              XCHG    AX, SI            ; get b2
  428.              POP     SI                ; get a1
  429.              MUL     SI                ; a1 * b2
  430.              ADD     BP, AX            ; add
  431.              ADC     BX, DX            ;  result
  432.              ADC     CX, CX            ;   to FPA
  433.              XCHG    AX, DI            ; get a2
  434.              POP     DI                ; get b1
  435.              MUL     DI                ; a2 * b1
  436.              ADD     BP, AX            ; add result
  437.              XCHG    AX, DI            ; get a1
  438.              XCHG    CX, SI            ; CX = b1
  439.              MOV     DI, BX            ; FPA = SI:DI:BX
  440.              MOV     BX, BP            ;
  441. $sqr_end:    ADC     DI, DX            ;  to   SI:DI:BX
  442.              ADC     SI, 0             ;   FPA
  443.              MUL     CX                ; a1 * b1
  444.              ADD     AX, DI
  445.              ADC     DX, SI            ; result in DX:AX:BX
  446.  
  447.              ELSE
  448.  
  449. $full_mult:  PUSH    BX                ; save b1
  450.              PUSH    DI                ; save a2
  451.              PUSH    SI                ; save b2
  452.              PUSH    DX                ; save a1
  453.              PUSH    BX                ; save b1
  454.              MOV     BX, CX            ; clear most significant word of FPA
  455.              XCHG    AL, CH            ; CH = b3, AL = 0
  456.              MOV     BP, AX            ; a3
  457.              MOV     AL, CH            ; b3
  458.              MUL     AH                ; a3 * b3
  459.              XCHG    AX, DI            ; store to FPA, get a2
  460.              MUL     CX                ; a2 * b3
  461.              ADD     DI, AX            ; add result
  462.              ADC     DX, BX            ;  to FPA
  463.              XCHG    AX, DX            ;   and
  464.              XCHG    AX, SI            ;    get b2
  465.              MUL     BP                ; a3 * b2
  466.              ADD     DI, AX            ; add result
  467.              ADC     SI, DX            ;  to
  468.              ADC     BX, BX            ;   FPA
  469.              XCHG    AX, BP            ; get a3
  470.              MOV     BP, DI            ; generate sticky flag
  471.              XOR     DI, DI            ; FPA = DI:BX:SI
  472.              POP     DX                ; get b1
  473.              MUL     DX                ; a3 * b1
  474.              ADD     SI, AX            ; add result to
  475.              ADC     BX, DX            ;  FPA, no overflow possible
  476.              XCHG    AX, CX            ; b3
  477.              POP     CX                ; a1
  478.              MUL     CX                ; a1 * b3
  479.              ADD     SI, AX            ; add
  480.              ADC     BX, DX            ;  result to
  481.              ADC     DI, DI            ;   FPA
  482.              POP     AX                ; b2
  483.              POP     DX                ; a2
  484.              PUSH    DX                ; save a2
  485.              PUSH    AX                ; save b2
  486.              MUL     DX                ; a2 * b2
  487.              ADD     SI, AX            ; add
  488.              ADC     BX, DX            ;  result
  489.              ADC     DI, 0             ;   to FPA
  490.              OR      BP, SI            ; accumulate sticky flag
  491.              XOR     SI, SI            ; FPA = SI:DI:BX
  492.              POP     AX                ; b2
  493.              MUL     CX                ; a1 * b2
  494.              ADD     BX, AX            ; add
  495.              ADC     DI, DX            ;  result
  496.              ADC     SI, SI            ;   to FPA
  497.              POP     AX                ; a2
  498.              POP     DX                ; get b1
  499.              PUSH    DX                ; save b1
  500.              MUL     DX                ; a2 * b1
  501.              ADD     BX, AX            ; add -------+
  502.              POP     AX                ; get b1     !
  503. $sqr_end:    ADC     DI, DX            ;  result  <-+
  504.              ADC     SI, 0             ;   to FPA
  505.              MUL     CX                ; a1 * b1
  506.              ADD     AX, DI            ; add result to FPA
  507.              ADC     DX, SI            ;  DX:AX:BX = result
  508.  
  509.              ENDIF
  510.  
  511. $end_mantiss:POP     CX                ; CH = exponent  CL = sign
  512.              XCHG    AX, BX            ; DX:BX:AX = result
  513. ;              SUB     CX, 81h           ; compute new exponent-1
  514. $div_end:  ;   OR      DX, DX            ; is mantissa normalized ?
  515. ;              JS      $add_sub_end      ; yes
  516.              js      $$1
  517.              ADD     AX, AX            ; no, shift
  518.              ADC     BX, BX            ;  FPA 1 bit
  519.              ADC     DX, DX            ;   to the left
  520.               DEC     CX                ; adjust exponent
  521. $$1:         sub     cx, 81h
  522. $add_sub_end:ADD     AX, 80h           ; round
  523.              ADC     BX, 0             ;  up
  524.              ADC     DX, 0             ;   mantissa
  525.              ADC     CX, 0             ; increment exponent if mantissa overfl.
  526.  
  527.              IFNDEF  FASTOPS
  528.              OR      AL, AL            ; tie case ?
  529.              JZ      $tie_case         ; tie case possible if sticky = 0, too
  530.              ENDIF
  531.  
  532. $round_done: POP     BP                ; restore caller's frame pointer
  533.              TEST    CH, 40H           ; test if (exponent-1) negative
  534.              JNZ     $zero_prod2       ; yes, underflow, return zero
  535.              INC     CX                ; new exponent
  536.              MOV     AL, CL            ; store exponent
  537.              AND     DH, 7Fh           ; force MSB of mantissa to 0
  538.              OR      DH, CH            ; fill in sign bit
  539.  
  540.              IFDEF   NOOVERFLOW
  541.              ROR     CH, 1             ; test if exponent overflow
  542.              ROL     CH, 1             ; restore sign flag
  543.              ELSE
  544.              SHR     CH, 1             ; test if exponent overflow (> FFh)
  545.              ENDIF
  546.  
  547.              RET                       ; done
  548. $zero_prod2: XOR     AX, AX            ; load
  549.              MOV     BX, AX            ;  a
  550.              CWD                       ;   zero
  551.              RET                       ; done
  552. $tie_case:   OR      BP, BP            ; sticky flag = 0 (tie case) ?
  553.              JNZ     $round_done       ; no, round up was correct
  554.              AND     AH, 0FEh          ; tie case, make mantissa even
  555.              JMP     $round_done       ; IEEE rounding done
  556.  
  557. RealMulNChk2 ENDP
  558. RealMulNoChk ENDP
  559. RealMul      ENDP
  560.  
  561.  
  562.  
  563. ;-------------------------------------------------------------------------------
  564. ; RealSqr computes the square of a number in the Turbo Pascal 6-byte floating
  565. ; point format. If underflow occurs, zero is returned. On overflow the carry
  566. ; flag will be set. Since squaring allows for some optimizations in code when
  567. ; compared with normal multiplication, RealSqr is implemented as a self con-
  568. ; tained routine and not as a call to RealMul. The routine exits thru RealMul.
  569. ; RealSqrNoChk does not check the argument for zero before squaring. Rounding
  570. ; complies with the IEEE "round to nearest or even" mode, so guard and sticky
  571. ; flags are provided.
  572. ;
  573. ; INPUT:     DX:BX:AX  argument
  574. ;
  575. ; OUTPUT:    DX:BX:AX  square of argument
  576. ;            CF        set if overflow occured, else cleared
  577. ;
  578. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  579. ;-------------------------------------------------------------------------------
  580.  
  581. RealSqr      PROC    NEAR
  582.              OR      AL, AL            ; argument = 0 ?
  583.              JZ      $zero_prod2       ; result is zero
  584.  
  585. RealSqrNoChk PROC    NEAR
  586.              XOR     CX, CX            ; clear register
  587.              XCHG    CL, AL            ; exponent in CL, AL = 0
  588.              ADD     CX, CX            ; new exponent, sign always positive (0)
  589.              PUSH    BP                ; save TURBO-Pascal frame pointer
  590.              PUSH    CX                ; save sign and exponent
  591.              OR      DH, 80h           ; set implicit bit of argument
  592.              MOV     SI, AX            ; a2 and
  593.              OR      SI, BX            ;  a3 = 0 ?
  594.              JNZ     $full_sqr         ; no, do full multiplication
  595.              MOV     AX, DX            ; load a1
  596.              MUL     DX                ; a1 * a1
  597.              or      dx, dx
  598.              JMPS    $end_mantiss      ; result in DX:AX:BX
  599.  
  600.              ALIGN   2
  601.  
  602. $full_sqr:   PUSH    BX                ; save a2
  603.              XOR     DI, DI            ; load zero
  604.              MOV     CX, DX            ; save a1
  605.              MOV     BP, AX            ; save a3
  606.              MOV     AL, AH            ; load a3
  607.              MUL     AL                ; a3 * a3
  608.              XCHG    AX, BX            ; save product, get a2
  609.              MUL     BP                ; a2 * a3
  610.              XCHG    AX, BP            ; get a3, BP = save lo-word a2*a3
  611.              MOV     SI, DX            ; save hi-word a2*a3
  612.              ADD     BX, BP            ; add a3*a3 to
  613.              ADC     SI, DI            ;  a2*a3 (result in SI:BX, no overflow)
  614.              ADD     BP, BX            ; add a2*a3 lo-word to result
  615.              MOV     BX, DI            ; BX = 0
  616.              ADC     SI, DX            ; add a2*a3 hi-word
  617.              ADC     DI, DI            ;  to result (DI:SI:BP)
  618.              XCHG    DI, BX            ; FPA = DI:BX:SI, BP = sticky byte
  619.              MUL     CX                ; a1 * a3
  620.              ADD     SI, AX            ; add product
  621.              ADC     BX, DX            ;  to FPA (no overflow possible)
  622.              ADD     SI, AX            ; add
  623.              ADC     BX, DX            ;  product to
  624.              ADC     DI, DI            ;   FPA another time
  625.              POP     AX                ; get a2
  626.              PUSH    AX                ; save a2
  627.              MUL     AX                ; a2 * a2
  628.              ADD     SI, AX            ; add
  629.              ADC     BX, DX            ;  product to
  630.              ADC     DI, 0             ;   FPA
  631.              OR      BP, SI            ; accumulate sticky byte
  632.              XOR     SI, SI            ; FPA = SI:DI:BX
  633.              POP     AX                ; get a2
  634.              MUL     CX                ; a1 * a2
  635.              ADD     BX, AX            ; add
  636.              ADC     DI, DX            ;  resulting
  637.              ADC     SI, SI            ;   product
  638.              ADD     BX, AX            ;    to FPA twice
  639.              MOV     AX, CX            ; AX = CX = a1
  640.              JMP     $sqr_end          ; exit thru REAL_MUL
  641. RealSqrNoChk ENDP
  642. RealSqr      ENDP
  643.  
  644.  
  645.  
  646. ;-------------------------------------------------------------------------------
  647. ; RealDiv divides two numbers in the Turbo Pascal 6 byte floating point
  648. ; format. If underflow occurs, zero is returned. On overflow the carry flag
  649. ; will be set. The routine exits through the REAL_MUL routine. It makes use
  650. ; of the 80x86 DIV instruction in an estimate and correct algorithm. In each
  651. ; of the three steps, an estimation of a part of the quotient is produced by
  652. ; dividing the first 32 bits of the current remainder by the first 16 bits of
  653. ; the divisor using a machine instruction. Then the divisor is multiplied by
  654. ; the result and this product subtracted from the current remainder. If the sum
  655. ; is negative, the partial quotient must be decremented until the new remainder
  656. ; is positive. RealDivRev is an additional routine which exchanges the operands
  657. ; before performing the division. The rounding provided complies with IEEE
  658. ; "round to nearest or even" mode. For this purpose, guard and sticky flags
  659. ; are implemented.
  660. ;
  661. ; INPUT:     DX:BX:AX  dividend
  662. ;            DI:SI:CX  divisor
  663. ;
  664. ; OUTPUT:    DX:BX:AX   quotient
  665. ;            CF         set if overflow occured, else cleared
  666. ;
  667. ; DESTROYS:  AX,BX,CX,DX,SI,DI,Flags
  668. ;-------------------------------------------------------------------------------
  669.  
  670. RealDivRev   PROC    NEAR
  671.              XCHG    AX, CX            ; exchange
  672.              XCHG    BX, SI            ;  divisor and
  673.              XCHG    DX, DI            ;   dividend
  674. RealDivRev   ENDP
  675.  
  676. RealDiv      PROC    NEAR
  677.              OR      AL, AL            ; dividend = 0 ?
  678.              JZ      $zero_prod2       ; result is zero
  679.              PUSH    BP                ; save TURBO-Pascal framepointer
  680.              MOV     BP, DX            ; get msw of dividend
  681.              XOR     BP, DI            ; xor with msw of divisor to make sign
  682.              AND     BP, 8000h         ; isolate sign bit of result
  683.              OR      DH, 80h           ; set implicit bit in dividend
  684.              XCHG    DX, DI            ; DX = divisor msw, DI = dividend msw
  685.              OR      DH, 80h           ; set implicit bit in divisor
  686.              SUB     AL, CL            ; subtract exponents ----------+
  687.              MOV     CL, 0             ; clear lsb of divisor lsw     |
  688.              PUSH    SI                ;  save divisor middle word    |
  689.              PUSH    CX                ;   and lsw on stack           |
  690.              MOV     CX, BP            ; get sign                     |
  691.              XCHG    AL, CL            ; AL = 0, CL = new exponent    |
  692.              SBB     CH, AL            ; put carry here <-------------+
  693.  
  694.              add     cx,101h
  695.              MOV     BP, SP            ; access divisor on stack via BP
  696.              SUB     BP, 6             ; leave room for three pushes
  697.              SHR     DI, 1             ; divide dividend
  698.              RCR     BX, 1             ;  by 2 to prevent
  699.              RCR     AX, 1             ;   an overflow condition
  700.  
  701.              IFDEF   FASTOPS           ; di:bx:ax = dividend
  702.                                        ; dx:[bp+8]:[bp+6] = divisor
  703.              PUSH    CX                ; save sign and exponent
  704.              XCHG    AX, BX            ; di:ax:bx = dividend
  705.              XCHG    DX, DI            ; dx:ax:bx = dividend, di:[bp+8]:[bp+6]
  706.              DIV     DI                ; AX = quotient1, DX = remainder msw
  707.              MOV     CX, AX            ; save quotient 1
  708.              MOV     SI, DX            ; save remainder = SI:BX
  709.              MUL     WORD PTR [BP+8]   ; quotient1 * divisor middle word
  710.              SUB     BX, AX            ;
  711.              SBB     SI, DX            ;
  712.              SBB     DX, DX            ; DX:SI:BX = remainder
  713.              PUSH    DX                ; save msw of divisor
  714.              MOV     AX, [BP+6]        ; divisor msw
  715.              MUL     CX                ;
  716.              NEG     AX                ;
  717.              SBB     BX, DX            ;
  718.              SBB     SI, 0             ;
  719.              POP     DX                ;
  720.              SBB     DX, 0             ; DX:SI:BX:AX = remainder, CX = quot 1
  721.              MOV     DX, [BP+6]
  722.              MOV     BP, [BP+8]
  723.              JZ      $sub_ok           ;
  724. $add_twice:  DEC     CX                ;
  725.              ADD     AX, DX            ;
  726.              ADC     BX, BP            ;
  727.              ADC     SI, DI            ;
  728.              JNC     $add_twice        ; until remainder positive
  729. $sub_ok:     MOV     DX, SI            ; SI:BX:AX = remainder
  730.              XCHG    AX, BX            ; DX:AX:BX = remainder
  731.              PUSH    CX                ; save quotient 1
  732.              XOR     SI, SI
  733.              CMP     DI, DX            ; division overflow ?
  734.              JE      $div_overfl
  735.              DIV     DI                ;
  736. $cont:       PUSH    AX                ; save quot2
  737.              MOV     CX, DX            ; CX:BX = remainder
  738.              MUL     BP                ;
  739.              SUB     BX, AX
  740.              SBB     CX, DX
  741.              SBB     SI, 0
  742.              POP     SI                ; quot2
  743.              JNC     $sub_ok2          ;
  744. $add_twice2: DEC     SI
  745.              ADD     BX, BP
  746.              ADC     CX, DI
  747.              JNC     $add_twice2
  748. $sub_ok2:    MOV     DX, CX
  749.              MOV     AX, BX
  750.              CMP     DX, DI
  751.              JE      $div_overfl2
  752.              DIV     DI
  753. $cont2:      POP     DX                ; get quotient 1
  754.              MOV     BX, SI            ; quotient = DX:BX:AX
  755.  
  756.              ELSE
  757.  
  758.              ALIGN   4
  759.  
  760. $divide_loop:PUSH    CX                ; save sign & exponent resp. part. quot.
  761.              MOV     CX, DX            ; get msw of divisor
  762.              XCHG    AX, BX            ; create new dividend
  763.              XCHG    AX, DI            ;  by shifting remainder
  764.              XCHG    AX, SI            ;   16 bits to the left
  765.              CMP     CX, SI            ; overflow possible on division ?
  766.              JE      $div_overfl       ; yes
  767.              MOV     DX, SI            ; get msw of dividend
  768.              XCHG    AX, DI            ; second word of dividend
  769.              DIV     CX                ; compute partial quotient
  770.              XOR     SI, SI            ; subtract product of divisor high word
  771.              MOV     DI, DX            ;  and partial quotient from dividend
  772. $comp_rem:   XCHG    AX, CX            ; AX = divisor high word, CX = quotient
  773.              PUSH    AX                ; save divisor high word
  774.              MOV     AX, [BP+8]        ; get middle word of divisor
  775.              MUL     CX                ; multiply by partial quotient
  776.              SUB     BX, AX            ; subtract the product of
  777.              SBB     DI, DX            ;  divisor middle word and partial
  778.              SBB     SI, 0             ;   quotient from dividend
  779.              MOV     AX, [BP+6]        ; get lsw of divisor
  780.              MUL     CX                ; multiply by partial quotient
  781.              NEG     AX                ; subtract the product
  782.              SBB     BX, DX            ;   of divisor LSW
  783.              SBB     DI, 0             ;     and partial
  784.              SBB     SI, 0             ;      quotient from dividend
  785.              POP     DX                ; get back msw of divisor
  786.              JZ      $sub_ok           ; remainder must be positive
  787. $add_twice:  DEC     CX                ; quotient to high, decrement it
  788.              ADD     AX, [BP+6]        ; adjust
  789.              ADC     BX, [BP+8]        ;  quotient and
  790.              ADC     DI, DX            ;   remainder
  791.              JNC     $add_twice        ;     until remainder positive
  792. $sub_ok:     CMP     BP, SP            ; two partial quotients saved already ?
  793.              JNE     $divide_loop      ; no, continue (carry set !!!)
  794.              MOV     BP, AX            ; accumulate
  795.              OR      BP, BX            ;  sticky
  796.              OR      BP, DI            ;   byte
  797.              XCHG    AX, CX            ; get last partial quotient
  798.              POP     BX                ; get other
  799.              POP     DX                ;  partial quotients
  800.  
  801.              ENDIF
  802.  
  803.              POP     CX                ; get sign and exponent
  804.              ADD     SP, 4             ; remove saved divisor from stack
  805. ;             ADD     CX, 80h           ; adjust new exponent for bias
  806. ;             add     cx, 101h
  807.              or      dx, dx
  808.              JMP     $div_end          ; normalize mantissa and round
  809.  
  810.              IFDEF   FASTOPS
  811.  
  812. $div_overfl: MOV     DX, AX
  813.              ADD     DX, DI
  814.              ADC     SI, SI
  815.              MOV     AX, -1
  816.              JMP     $cont
  817. $div_overfl2:
  818.              MOV     AX, -1
  819.              JMP     $cont2
  820.  
  821.              ELSE
  822.  
  823. $div_overfl: XOR     SI, SI            ; remainder - 10000h * divisor
  824.              ADD     DI, CX            ; remainder -
  825.              ADC     SI, SI            ;  FFFFh * divisor
  826.              MOV     AX, -1            ; quotient = FFFFh
  827.              JMP     $comp_rem         ; continue computation of remainder
  828.              ENDIF
  829.  
  830. RealDiv      ENDP
  831.  
  832.              ALIGN   4
  833.  
  834. RAdd         PROC    FAR
  835.              CALL    RealAdd           ; perform addition
  836.              JC      ROverflow         ; overflow error
  837.              RET                       ; done
  838. RAdd         ENDP
  839.  
  840.              ALIGN   4
  841.  
  842. RSub         PROC    FAR
  843.              CALL    RealSub           ; perform subtraction
  844.              JC      ROverflow         ; overflow error
  845.              RET                       ; done
  846. RSub         ENDP
  847.  
  848.              ALIGN   4
  849.  
  850. RSqr         PROC    FAR
  851.              CALL    RealSqr           ; perform squaring
  852.              JC      ROverflow         ; overflow error
  853.              RET                       ; done
  854. RSqr         ENDP
  855.  
  856.              ALIGN   4
  857.  
  858. RMul         PROC    FAR
  859.              CALL    RealMul           ; perform multiplication
  860.              JC      ROverflow         ; overflow error
  861.              RET                       ; done
  862. RMul         ENDP
  863.  
  864.              ALIGN   4
  865.  
  866. RDiv         PROC    FAR
  867.              OR      CL, CL            ; divisor zero ?
  868.              JZ      RDivZero          ; yes, error
  869.              CALL    RealDiv           ; perform division
  870.              JC      ROverflow         ; overflow error
  871.              RET                       ; done
  872. RDiv         ENDP
  873.  
  874.              IFDEF   NOOVERFLOW
  875.  
  876. ROverflow:   MOV     AX, 0FFFFh        ; load
  877.              MOV     BX, 0FFFFh        ;  largest
  878.              MOV     DX, 07FFFh        ;   REAL number
  879.              OR      DH, CH            ; stuff in sign bit
  880.              RETF                      ; done
  881. RDivZero:    MOV     CH, DH            ; get dividend's sign
  882.              XOR     CX, DI            ; make sign of result
  883.              JMP     ROverflow         ; return largest REAL number
  884.  
  885.              ELSE
  886.  
  887. ROverflow:   MOV     AX, 0CDh          ; error code 205 (fp overflow)
  888.              JMP     HaltError         ; execute error handler
  889. RDivZero:    MOV     AX, 0C8h          ; error code 200 (division by zero)
  890.              JMP     HaltError         ; execute error handler
  891.  
  892.              ENDIF
  893.  
  894.              ALIGN   4
  895.  
  896. CODE         ENDS
  897.  
  898.              END
  899.