home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / Samples / CASM.ARJ / MATH87.ASM < prev    next >
Encoding:
Assembly Source File  |  1988-09-06  |  15.0 KB  |  694 lines

  1. ;_ math87.asm   Tue Feb  2 1988   Modified by: Walter Bright */
  2. ; Copyright (C) 1987-1988 by Northwest Software
  3. ; All Rights Reserved
  4. ; Written by Walter Bright
  5.  
  6.     include    macros.asm
  7.  
  8.     .8087
  9.  
  10. fncompp    macro
  11.     db    0D8h+6,0D9h        ;MASM doesn't recognize FNCOMPP
  12.     endm
  13.  
  14. fntst    macro
  15.     db    0D8h+1,0E4h        ;MASM doesn't recognize FNTST
  16.     endm
  17.  
  18. fnabs    macro
  19.     db    0D8h+1,0E1h        ;MASM doesn't recognize FNABS
  20.     endm
  21.  
  22. .fsin    macro                ;bugs in MASM 5.0
  23.     db    0D8h+1,0FEh
  24.     endm
  25.  
  26. .fcos    macro                ;bugs in MASM 5.0
  27.     db    0D8h+1,0FFh
  28.     endm
  29.  
  30. .fprem1    macro                ;bugs in MASM 5.0
  31.     db    0D8h+1,0F5h
  32.     endm
  33.  
  34. ;;;;;;;;;;;;;;;;;;;;;;;;;;;
  35. ; Values for exception.type (MUST match values in math.h)
  36.  
  37. DOMAIN        equ    1
  38. SING        equ    2
  39. OVERFLOW    equ    3
  40. UNDERFLOW    equ    4
  41. TLOSS        equ    5
  42. PLOSS        equ    6
  43.  
  44. ;Bits for condition code flags in 8087 status word
  45. C0    equ    00000001B
  46. C1    equ    00000010B
  47. C2    equ    00000100B
  48. C3    equ    01000000B
  49.  
  50.     begdata
  51.  
  52.     c_extrn    _8087,word
  53.  
  54. PIOVER4    dt    3FFEC90FDAA22168C235R
  55. SQRT2    dt    3FFFB504F333F9DE6484R
  56. ONEHALF    dd    0.5
  57.     enddata
  58.  
  59.     if LCODE
  60.     c_extrn    _arcerr,far, __trigerr,far
  61.     else
  62.     c_extrn    _arcerr,near, __trigerr,near
  63.     endif
  64.  
  65.     begcode    math87
  66.  
  67. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  68. ; Get and return the 8087 status word.
  69. ; This routine will hang if there is no 8087, therefore
  70. ; _8087 should be consulted before calling it.
  71.  
  72.     c_public    _status87
  73. func    _status87
  74.     push    BP
  75.     mov    BP,SP
  76.     push    AX        ;make room on stack
  77.     fstsw    word ptr -2[BP]    ;store 8087 status word
  78. S1:    fwait
  79.     pop    AX
  80.     pop    BP
  81.     ret
  82. c_endp    _status87
  83.  
  84. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  85. ; Get and clear the 8087 status word.
  86. ; This routine will hang if there is no 8087, therefore
  87. ; _8087 should be consulted before calling it.
  88. ; Returns:
  89. ;    8087 status word prior to its being cleared
  90.  
  91.     c_public    _clear87
  92. func    _clear87
  93.     push    BP
  94.     mov    BP,SP
  95.     push    AX        ;make room on stack
  96.     fstsw    word ptr -2[BP]    ;store 8087 clear word
  97.     fclex            ;clear exceptions
  98.     jmp    S1
  99. c_endp    _clear87
  100.  
  101. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  102. ; Get and set the 8087 control word.
  103. ; This routine will hang if there is no 8087, therefore
  104. ; _8087 should be consulted before calling it.
  105. ; Use:
  106. ;    unsigned _control87(newvalue,mask)
  107. ;    unsigned newvalue;    /* new value for control word        */
  108. ;    unsigned mask;        /* mask for which bits to modify    */
  109. ; The control word will be set to:
  110. ;    (oldcontrolword & ~mask) | (newvalue & mask)
  111. ; Returns:
  112. ;    8087 control word prior to its being set
  113.  
  114.     c_public    _control87
  115. func    _control87
  116.     push    BP
  117.     mov    BP,SP
  118.     push    AX        ;make room on stack
  119.     fstcw    word ptr -2[BP]    ;store 8087 control word
  120.     mov    CX,P[BP]
  121.     mov    BX,CX
  122.     not    BX        ;BX = ~mask
  123.     and    CX,P+2[BP]    ;CX = newvalue & mask
  124.     fwait            ;make sure the control word is there
  125.     pop    AX        ;AX = control word
  126.     and    BX,AX        ;BX = control & ~mask
  127.     or    BX,CX        ;BX = (control & ~mask) | (newvalue & mask)
  128.     push    BX
  129.     ;fnldcw    -2[BP]        ;load new control word
  130.     db    0D8h+1,06Eh,-2
  131.     fwait
  132.     pop    BX
  133.     pop    BP
  134.     ret
  135. c_endp    _control87
  136.  
  137. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  138. ; Reset 8087
  139. ; This routine will hang if there is no 8087, therefore
  140. ; _8087 should be consulted before calling it.
  141.  
  142.     c_public    _fpreset
  143. func    _fpreset
  144.     finit
  145.     ret
  146. c_endp    _fpreset
  147.  
  148. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  149. ; Square root
  150. ; Errors aren't checked for, should be checked by caller.
  151. ;    double _sqrt87(double);
  152.  
  153.     c_public    _sqrt87
  154. func    _sqrt87
  155.     push    BP
  156.     mov    BP,SP
  157.     fld    qword ptr P[BP]
  158.     fsqrt
  159.     sub    SP,8
  160. return_ST:
  161.     fstp    qword ptr -8[BP]
  162.     fwait
  163.     .pop    <DX,CX,BX,AX>
  164.     pop    BP
  165.     ret
  166. c_endp    _sqrt87
  167.  
  168. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  169. ; 8087 equivalent to poly().
  170. ;    double _poly87(double x, int deg, double coeff[]);
  171.  
  172.     c_public    _poly87
  173. func    _poly87
  174.     push    BP
  175.     mov    BP,SP
  176.     mov    CX,P+8[BP]    ;CX = deg
  177.     mov    BX,CX
  178.     shl    BX,1
  179.     shl    BX,1
  180.     shl    BX,1        ;* 8 to get offset into doubles
  181.     if SPTR
  182.     add    BX,P+8+2[BP]
  183.     fld    qword ptr [BX]        ;ST0 = coeff[deg]
  184.     else ;LPTR
  185.     add    BX,P+8+2[BP]
  186.     mov    ES,P+8+2+2[BP]
  187.     fld    qword ptr ES:[BX]
  188.     endif
  189.     sub    SP,8            ;make room for result at -8[BP]
  190.     jcxz    return_ST
  191.     fld    qword ptr P[BP]        ;ST0 = x
  192.     fxch    ST(1)            ;ST1 = x, ST0 = r
  193. L2:    fmul    ST(0),ST(1)        ;r *= x
  194.     sub    BX,8            ;deg--
  195.     if SPTR
  196.     fadd    qword ptr [BX]
  197.     else
  198.     fadd    qword ptr ES:[BX]
  199.     endif
  200.     loop    L2
  201.     fxch    ST(1)            ;ST1 = r, ST0 = x
  202.     fstp    ST(0)            ;dump x
  203.     jmp    return_ST
  204. c_endp    _poly87
  205.  
  206. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  207. ; Return absolute value of double.
  208. ;    double fabs(double);
  209.  
  210.     c_public    fabs
  211. func    fabs
  212.     push    BP
  213.     mov    BP,SP
  214.     mov    AX,P+6[BP]
  215.     mov    BX,P+4[BP]
  216.     mov    CX,P+2[BP]
  217.     mov    DX,P[BP]
  218.     and    AH,07Fh        ;clear sign bit
  219.     pop    BP
  220.     ret
  221. c_endp    fabs
  222.  
  223. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  224. ; 8087 versions of floor() and ceil().
  225. ; Also we have _chop87(), which rounds towards 0.
  226.  
  227.     c_public _chop87,_floor87,_ceil87
  228.  
  229. func    _chop87
  230.     mov    AH,3 SHL 2    ;round towards 0 (chop)
  231.     jmps    FC1
  232. c_endp    _chop87
  233.  
  234. func    _floor87
  235.     mov    AH,1 SHL 2    ;round towards - infinity
  236.     jmps    FC1
  237. c_endp    _floor87
  238.  
  239. func    _ceil87
  240.     mov    AH,2 SHL 2    ;round towards + infinity
  241. FC1:    push    BP
  242.     mov    BP,SP
  243.     fld    qword ptr P[BP]    ;get parameter
  244.     sub    SP,8        ;allocate space for return value and temps
  245.     fstcw    -2[BP]        ;need to manipulate control word
  246.     fwait
  247.     mov    AL,-1[BP]    ;get high byte of control word
  248.     mov    BL,AL        ;and save original
  249.     and    AL,11110011b    ;clear old rounding control bits
  250.     or    AL,AH        ;set new rounding control
  251.     mov    -1[BP],AL
  252.     ;fnldcw    -2[BP]        ;load new rounding control
  253.     db    0D8h+1,06Eh,-2
  254.     frndint            ;and round
  255.     mov    -1[BP],BL
  256.     fldcw    -2[BP]        ;restore original control word
  257.     jmp    return_ST    ;return
  258. c_endp    _ceil87
  259.  
  260. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  261. ; 8087 version of fmod(x,y).
  262.  
  263.     c_public    _fmod87
  264. func    _fmod87
  265.     push    BP
  266.     mov    BP,SP
  267.     sub    SP,8            ;scratch and return value
  268.     fld    qword ptr P+8[BP]
  269.     ftst
  270.     fstsw    -2[BP]
  271.     fwait
  272.     mov    AH,-1[BP]        ;get msb of status word in AH
  273.     sahf                ;transfer to flags
  274.     je    FM2            ;y is 0, return 0
  275.     fld    qword ptr P[BP]        ;ST = x, ST1 = y
  276. FM1:    fprem                ;ST = ST - ST1
  277.     fstsw    -2[BP]
  278.     fwait
  279.     mov    AH,-1[BP]        ;get msb of status word in AH
  280.     sahf                ;transfer to flags
  281.     jp    FM1            ;continue till ST < ST1
  282.     fstp    ST(1)            ;leave remainder on stack
  283. FM2:    jmp    return_ST
  284. c_endp    _fmod87
  285.  
  286. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  287. ; Compute arc tan of y/x.
  288. ; Cases of y/0 and 0/0 are already handled.
  289.  
  290.     c_public    _atan287
  291. func    _atan287
  292.     push    BP
  293.     mov    BP,SP
  294.  
  295.     ;Must adjust things so that (0 < y < x < infinity).
  296.     fld    qword ptr P[BP]        ;get y
  297.     sub    SP,8            ;room for return value
  298.     ftst
  299.     fstsw    -2[BP]            ;save sign of y
  300.     fld    qword ptr P+8[BP]    ;get x
  301. AT4:    ftst
  302.     fstsw    -4[BP]            ;save sign of x
  303.     fdivp    ST(1),ST        ;compute z=y/x
  304.     fabs
  305.     fld1
  306.     fcom                ;see if 0th octant or 1st octant
  307.     fstsw    -6[BP]
  308.     fwait
  309.     mov    AH,-5[BP]
  310.     sahf
  311.     ja    oct0            ;0 <= angle < 45
  312.     jb    oct1            ;45 < angle < 90
  313.  
  314.     ;Angle is 45 degrees (because z == 1)
  315.     fncompp                ;clear stack
  316.     fld    PIOVER4            ;return PI/4
  317.     jmps    AT1
  318.  
  319. oct1:    ;Must use identity atan(z) == PI/2 - atan(1/z)
  320.     fxch    ST(1)
  321.     fpatan                ;ST(0) == atan(1/z)
  322.     fld    PIOVER4
  323.     fadd    ST,ST(0)        ;compute PI/2
  324.     fsubrp    ST(1),ST        ;compute atan(z)
  325.     jmps    AT1
  326.  
  327. oct0:    fpatan                ;tangent of z/1
  328.  
  329. AT1:    ;Determine, based on the original signs of x and y, which
  330.     ;quadrant the angle should be in.
  331.     ;The 4 cases are:
  332.     ; 1:    x > 0 && y >= 0:    return ST
  333.     ; 2:    x > 0 && y <  0:    return -ST
  334.     ; 3:    x < 0 && y >= 0:    return PI - ST
  335.     ; 4:    x < 0 && y <  0:    return -(PI - ST)
  336.  
  337.     mov    AH,-3[BP]
  338.     sahf
  339.     ja    AT2            ;x > 0, so cases 1 and 2
  340.     fldpi
  341.     fsubrp    ST(1),ST
  342. AT2:    mov    AH,-1[BP]
  343.     sahf
  344.     jae    AT3            ;y >= 0
  345.     fchs
  346. AT3:    jmp    return_ST
  347. c_endp    _atan287
  348.  
  349. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  350. ; Do arcsin's and arccos's.
  351. ; Input:
  352. ;    double _asincos87(int flag,double x);
  353. ;
  354. ;    flag = 0: asin
  355. ;           1: acos
  356.  
  357.     c_public    _asincos87
  358. func    _asincos87
  359.     push    BP
  360.     mov    BP,SP
  361.  
  362.     ;Use the identities:
  363.     ;    asin(x) = atan2(x,sqrt(1-x*x))
  364.     ;    acos(x) = atan2(sqrt(1-x*x),x)
  365.  
  366.     fld    qword ptr P+2[BP]    ;ST = x
  367.     sub    SP,8
  368.     ftst
  369.     fstsw    -2[BP]            ;remember flags for atan2()
  370.     ;Compute (1-x*x)
  371.     fld    ST            ;copy x
  372.     fmul    ST,ST            ;ST = x * x
  373.     fld1
  374.     fsubrp    ST(1),ST        ;ST = 1 - x * x
  375.     ;If (1-x*x) < 0, we have a domain error
  376.     ftst
  377.     fstsw    -6[BP]
  378.     fwait
  379.     mov    AH,-5[BP]
  380.     sahf
  381.     jae    ASC1            ;no domain error
  382.     fncompp                ;clean 8087 stack
  383.     mov    SP,BP
  384.     pop    BP
  385.     jmp    _arcerr            ;_arcerr(flag,x) handles the exception
  386.  
  387. ASC1:    fsqrt                ;sqrt(1 - x * x)
  388.     ;If acos, we need to reverse the arguments to atan2()
  389.     .if    <word ptr P[BP]> e 0, ASC2    ;if asin
  390.     ftst
  391.     fstsw    -2[BP]            ;flags of y
  392.     fxch    ST(1)
  393. ASC2:    jmp    AT4            ;let atan2(y,x) do the rest
  394. c_endp    _asincos87
  395.  
  396. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  397. ; Do sine, cosine and tangent of x.
  398. ; Use:
  399. ;    double _sincostan87(double x,int flag);
  400. ; Input:
  401. ;    flag:    0 = sine
  402. ;        1 = cosine
  403. ;        2 = tan
  404.  
  405. func    trigerr
  406.     ;Total loss of precision
  407. TR2:    fstp    ST
  408. TR1:    fstp    ST            ;clean stack
  409.     sub    SP,2+8+8+2-8        ;already 8 on stack
  410.     fld    qword ptr P[BP]
  411.     mov    -2[BP],BX        ;flag
  412.     fstp    qword ptr -2-8[BP]    ;x
  413.     fldz
  414.     fstp    qword ptr -2-8-8[BP]        ;return value of 0.0
  415.     mov    word ptr -2-8-8-2[BP],TLOSS    ;total loss of significance
  416.     fwait
  417.     callm    __trigerr
  418.     mov    SP,BP
  419.     pop    BP
  420.     ret
  421. c_endp    trigerr
  422.  
  423.     c_public    _sincostan87
  424. func    _sincostan87
  425.     push    BP
  426.     mov    BP,SP
  427.     sub    SP,8
  428.     mov    BX,P+8[BP]        ;BL = flag
  429. if 1
  430.     .if    _8087 b 3, SC11        ;if 8087 or 80287
  431.  
  432.     ;Take advantage of special 80387 instructions
  433.     .286C
  434.     fld    qword ptr P[BP]        ;load theta
  435.     fxam                ;test for oddball values
  436.     fstsw    AX
  437.     sahf
  438.     jc    trigerr            ;x is NAN, infinity, or empty
  439.                     ;387's can handle denormals
  440. SC18:    tst    BL            ;sine?
  441.     jnz    SC12            ;no
  442.     .fsin
  443.     jmps    SC13
  444. SC12:    .if BL ne 2, SC15
  445.     fptan
  446.     fstp    ST            ;dump X, which is always 1
  447.     jmps    SC13
  448. SC15:    .fcos
  449. SC13:    fstsw    AX
  450.     sahf
  451.     jp    SC16            ;C2 = 1 (x is out of range)
  452.     jmp    return_ST
  453.  
  454.     ;Need to do argument reduction
  455. SC16:    fldpi
  456.     fxch
  457. SC17:    .fprem1
  458.     fstsw    AX
  459.     sahf
  460.     jp    SC17
  461.     fstp    ST(1)            ;remove pi from stack
  462.     jmp    SC18
  463.     .8087
  464. endif
  465.  
  466. SC11:    fld    PIOVER4            ;pi/4
  467.     fld    qword ptr P[BP]        ;load theta
  468.     fxam                ;test for oddball values
  469.     fstsw    -6[BP]
  470.     fwait
  471.     mov    AL,-5[BP]
  472.     mov    AH,AL            ;C1 gives sign of x, save in AL
  473.     sahf                ;C3 == Z, C2 == P, C0 == C
  474.     jc    SC14            ;x is NAN, infinity, or empty
  475.     jp    SC3            ;x is normal or denormal
  476.     jnz    SC3            ;x is unnormal
  477.     ;x is 0
  478.     ;fnstp    ST(1)            ;get rid of pi/4
  479.     db    0D8h+5,0D9h        ;I thought assemblers were supposed
  480.                     ;to eliminate machine code
  481.     .if    BL ne 1, SC2        ;if sin or tan, return 0
  482.     fstp    ST
  483.     fld1
  484.     jmps    SC2            ;cos(0) is 1
  485.  
  486. SC14:    jmp    TR2
  487.  
  488. SC3:    ;Attempt to reduce x so that |r| < PI/4
  489.     fnabs
  490.     fprem                ;also normalize ST if unnormal
  491.     fstsw    -4[BP]
  492.     fwait
  493.     mov    AH,-3[BP]
  494.     sahf
  495.     jp    SC14 ;(trigerr)        ;|x| > 2**62, so total loss of precision
  496.                     ;BUG: what about partial loss?
  497.     .if    BL e 2, TAN        ;do tangent
  498.     ;skip section if sin
  499.     tst    BL
  500.     jz    SC4            ;if sin
  501.  
  502.     ;Since cos(-x) == cox(x), ignore sign of angle
  503.     and    AL,not C1
  504.  
  505.     ;Since cos(x) == sin(x + pi/2), add 2 octants to the
  506.     ;octant formed by C0,C3,C1
  507.     xor    AH,C3
  508.     test    AH,C3
  509.     jnz    SC4
  510.     xor    AH,C0            ;carry
  511.  
  512. SC4:    ;If argument was in octants 1,3,5,7 then replace x with pi/4-x
  513.     test    AH,C1
  514.     jz    SC5            ;in an even octant
  515.     ;fnsubp    ST(1),ST        ;pi/4 is also removed
  516.     db    0D8h+6,0E9h        ;maybe someday MASM will support the 8087
  517.     jmps    SC6
  518.  
  519. SC5:    ;fptan will not work if x == 0, so if x == 0 then result is 1
  520.     fntst
  521.     fstsw    -2[BP]
  522.     fstp    ST(1)            ;remove pi/4 from stack
  523.     test    byte ptr -1[BP],C3    ;C3 is Z bit
  524.     je    SC6
  525.     fld1                ;y/x is 0/1
  526.     jmps    SC7
  527.  
  528. SC6:    fptan
  529.  
  530. SC7:    ;If in octants 1,2,5,6
  531.     ;then use relation cos(angle) = x/sqrt(x*x + y*y)
  532.     ;else use relation sin(angle) = y/sqrt(x*x + y*y)
  533.  
  534.     test    AH,C3 + C1
  535.     jp    SC8            ;in octants 0,3,4,8
  536.     fxch    ST(1)
  537. SC8:
  538.     ;Compute ST1/sqrt(ST1*ST1 + ST0*ST0)
  539.     fmul    ST,ST
  540.     fld    ST(1)
  541.     fmul    ST,ST
  542.     faddp    ST(1),ST
  543.     fsqrt
  544.     fdivp    ST(1),ST
  545.  
  546.     ;Figure out sign of result while fdiv is running
  547.     and    AX,C0*256 + C1        ;isolate sign of fprem and fxam
  548. SC10:    or    AL,AH            ;set parity = !(C0 ^ C1)
  549.     jp    SC2
  550.     fchs
  551.  
  552. SC2:    jmp    return_ST
  553.  
  554. TAN:    test    AH,C1            ;test C1, if set then octants 1,3,5,7
  555.     jz    TAN3            ;octants 0,2,4,6
  556.     fsubp    ST(1),ST        ;ST == PI/4 - |r|
  557.     jmps    TAN4
  558.  
  559. TAN3:    fstp    ST(1)            ;get rid of pi/4
  560.     ;See if ST is 0
  561.     ftst
  562.     fstsw    -2[BP]
  563.     fwait
  564.     test    byte ptr -1[BP],C3    ;check C3 (Z bit)
  565.     jz    TAN4            ;ST is not 0
  566.  
  567.     ;fptan of 0 won't work, we fake it with 1/0
  568.     fld1
  569.     fxch    ST(1)
  570.     jmps    TAN5
  571.  
  572. TAN4:    fptan
  573. TAN5:    ;Determine if we should take reciprocal (octants 1,2,5,6)
  574.     and    AH,C3 or C1        ;isolate bits C3 and C1
  575.     jp    TAN7            ;jmp if octants 0,3,4,7
  576.     fdivrp    ST(1),ST
  577.     jmps    TAN6
  578.  
  579. TAN7:    fdivp    ST(1),ST
  580. TAN6:    ;Determine if the sign should be switched
  581.     and    AX,0100000000000010B    ;isolate C3 in AH: octants 2,3,6,7
  582.                     ;and C1 in AL: the sign bit of x
  583.     jmp    SC10
  584. c_endp    _sincostan87
  585.  
  586. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  587. ; Do natural log of x, and log base 10 of x.
  588. ; The case of x <= 0 is assumed to be handled already.
  589. ; Use relations:
  590. ;    log(x) = loge(2) * log2(x)
  591. ;    log10(x) = log10(2) * log2(x)
  592.  
  593.     c_public    _log87,_log1087
  594. func    _log87
  595.     fldln2
  596.     jmps    LOG1
  597. c_endp    _log87
  598.  
  599. func    _log1087
  600.     fldlg2
  601. LOG1:    push    BP
  602.     mov    BP,SP
  603.     fld    qword ptr P[BP]        ;load x
  604.     fyl2x
  605.     sub    SP,8
  606.     jmp    return_ST
  607. c_endp    _log1087
  608.  
  609. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  610. ; Compute:
  611. ;    _exp87(y):        e ** y
  612. ;    _pow87(x,y,sign):    (sign) ? -(x ** y) : x ** y
  613. ; Note:
  614. ;    for _exp87(), values of y that will cause underflow or overflow
  615. ;    are handled previously.
  616. ;    for _pow87(), cases where x<=0 are already handled. Values of
  617. ;    y that will cause overflow or underflow will work correctly, but
  618. ;    will not trigger a call to matherr() as they should (BUG).
  619.  
  620.     c_public    _exp87,_pow87
  621.  
  622. func    _exp87
  623.     ;Use the formula exp(y) = 2 ** (y * log2(e))
  624.     fldl2e
  625.     push    BP
  626.     mov    BP,SP
  627.     fld    qword ptr P[BP]
  628.     clr    BH            ;want positive result
  629.     fmulp    ST(1),ST        ;ST = y * log2(e)
  630.     jmps    EXP2
  631. c_endp    _exp87
  632.  
  633. func    _pow87
  634.     ;Use the formula x ** y = 2 ** (y * log2(x))
  635.     push    BP
  636.     mov    BP,SP
  637.     fld    qword ptr P+8[BP]    ;push y
  638.     fld    qword ptr P[BP]        ;push x    (0 < x < infinity)
  639.     mov    BH,P+8+8[BP]        ;if BH != 0, then want negative result
  640.     fyl2x                ;ST = y * log2(x)
  641.  
  642. EXP2:    ;The common part computes (2 ** ST)
  643.     ;Use the formula:
  644.     ;    (2 ** ST) = (2 ** fraction(ST)) * (2 ** floor(ST))
  645.     sub    SP,8            ;make room for result
  646.     fld    ST            ;duplicate ST
  647.  
  648.     ;Compute ST = floor(ST)
  649.     fstcw    -2[BP]            ;need to manipulate control word
  650.     fwait
  651.     mov    AL,-1[BP]        ;get high byte of control word
  652.     mov    BL,AL            ;and save original
  653.     and    AL,11110011b        ;clear old rounding control bits
  654.     or    AL,00000100b        ;round towards -infinity
  655.     mov    -1[BP],AL
  656.     ;fnldcw    -2[BP]
  657.     db    0D8h+1,06Eh,-2
  658.     frndint
  659.     mov    -1[BP],BL
  660.     fldcw    -2[BP]            ;restore original control word
  661.  
  662.     fsub    ST(1),ST        ;ST(1) = fraction (0 <= ST < 1)
  663.     fxch    ST(1)            ;ST(1) = floor, ST = fraction
  664.  
  665.     ;Compute ST = 2 ** ST
  666.     ;Note that 0 <= ST < 1
  667.     ;Use the relation:
  668.     ;    (2 ** ST) = (ST < .5) ? (2 ** ST) : (2 ** (ST-.5) * SQRT2)
  669.     fld    ONEHALF
  670.     fxch    ST(1)
  671.     fprem                ;0 <= ST < .5
  672.     fstsw    -2[BP]            ;remember if (.5 <= ST < 1)
  673.     fstp    ST(1)            ;dump ONEHALF
  674.     f2xm1                ;ST = (2 ** ST) - 1
  675.     test    byte ptr -1[BP],C1    ;was (.5 <= ST < 1)?
  676.     fld1
  677.     faddp    ST(1),ST        ;add in that 1
  678.     jz    EXP1            ;no
  679.     fld    SQRT2            ;yes
  680.     fmulp    ST(1),ST
  681.  
  682. EXP1:    fscale                ;ST *= 2 ** floor
  683.     tst    BH            ;do we want a negative result?
  684.     fstp    ST(1)            ;get rid of floor
  685.     jz    EXP3            ;result is positive
  686.     fchs                ;form negative result
  687. EXP3:    jmp    return_ST
  688.  
  689. c_endp    _pow87
  690.  
  691.     endcode    math87
  692.  
  693.     end
  694.