This section provides various examples of 80x87 FPU programming. This group of routines provides several trigonometric, inverse trigonometric, logarithmic, and exponen-tial functions using various algebraic identities. All these functions assume that the input values are on the stack are are within valid ranges for the given functions. The trigono-metric routines expect angles expressed in radians and the inverse trig routines produce angles measured in radians.
This program (transcnd.asm) appears on the companion CD-ROM.
.xlist
include stdlib.a includelib stdlib.lib .list
.386 .387
option segment:use16 dseg segment para public ‘data’
result real8 ?
; Some variables we use to test the routines in this package:
cotvar real8 3.0
cotRes real8 ?
acotRes real8 ?
cscvar real8 1.5
cscRes real8 ?
acscRes real8 ?
secvar real8 0.5
secRes real8 ?
asecRes real8 ?
sinvar real8 0.75
sinRes real8 ?
asinRes real8 ?
cosvar real8 0.25
cosRes real8 ?
acosRes real8 ?
Two2xvar real8 -2.5
Two2xRes real8 ?
lgxRes real8 ?
Ten2xVar real8 3.75
Ten2xRes real8 ?
logRes real8 ?
expVar real8 3.25
expRes real8 ?
lnRes real8 ?
Y2Xx real8 3.0
Y2Xy real8 3.0
Y2XRes real8 ?
dseg ends
cseg segment para public ‘code’
assume cs:cseg, ds:dseg
; st(0) contains x (in radians) and must be between
; -2**63 and +2**63
;
; There must be at least one free register on the stack for
; this routine to operate properly.
;
; cot(x) = 1/tan(x)
cot proc near
fsincos fdivr ret
cot endp
; CSC(x) - computes the cosecant of st(0) and leaves result in st(0).
; st(0) contains x (in radians) and must be between
; -2**63 and +2**63.
; The cosecant of x is undefined for any value of sin(x) that
; produces zero (e.g., zero or pi radians).
;
; There must be at least one free register on the stack for
; this routine to operate properly.
;
; csc(x) = 1/sin(x)
csc proc near
fsin fld1 fdivr ret
csc endp
; SEC(x) - computes the secant of st(0) and leaves result in st(0).
; st(0) contains x (in radians) and must be between
; -2**63 and +2**63.
;
; The secant of x is undefined for any value of cos(x) that
; produces zero (e.g., pi/2 radians).
;
; There must be at least one free register on the stack for
; this routine to operate properly.
;
; sec(x) = 1/cos(x)
sec proc near
fcos fld1 fdivr ret
sec endp
; ASIN(x)- Computes the arcsine of st(0) and leaves the result in st(0).
; Allowable range: -1<=x<=+1
; There must be at least two free registers for this
; function to operate properly.
;
; asin(x) = atan(sqrt(x*x/(1-x*x)))
asin proc near
fld st(0) ;Duplicate X on tos.
fmul ;Compute X**2.
fld st(0) ;Duplicate X**2 on tos.
fld1 ;Compute 1-X**2.
fsubr
fdiv ;Compute X**2/(1-X**2).
fsqrt ;Compute sqrt(x**2/(1-X**2)).
fld1 ;To compute full arctangent.
fpatan ;Compute atan of the above.
ret
asin endp
; ACOS(x)- Computes the arccosine of st(0) and leaves the
; result in st(0).
; Allowable range: -1<=x<=+1
; There must be at least two free registers for
; this function to operate properly.
;
; acos(x) = atan(sqrt((1-x*x)/(x*x)))
acos proc near
fld st(0) ;Duplicate X on tos.
fmul ;Compute X**2.
fld st(0) ;Duplicate X**2 on tos.
fld1 ;Compute 1-X**2.
fsubr
fdiv ;Compute (1-x**2)/X**2.
fsqrt ;Compute sqrt((1-X**2)/X**2).
fld1 ;To compute full arctangent.
fpatan ;Compute atan of the above.
ret
acos endp
; ACOT(x)- Computes the arccotangent of st(0) and leaves the
; result in st(0).
; X cannot equal zero.
; There must be at least one free register for
; this function to operate properly.
;
; acot(x) = atan(1/x)
acot proc near
fld1 ;fpatan computes
fxch ; atan(st(1)/st(0)).
fpatan ; we want atan(st(0)/st(1)).
ret
acot endp
; ACSC(x)- Computes the arccosecant of st(0) and leaves the
; result in st(0).
; abs(X) must be greater than one.
; There must be at least two free registers for
; this function to operate properly.
;
; acsc(x) = atan(sqrt(1/(x*x-1)))
acsc proc near
fld st(0) ;Compute x*x
fmul
fld1 ;Compute x*x-1
fsub
fld1 ;Compute 1/(x*x-1)
fdivr
fsqrt ;Compute sqrt(1/(x*x-1)) fld1
fpatan ;Compute atan of above.
ret
acsc endp
; ASEC(x)- Computes the arcsecant of st(0) and leaves the
; result in st(0).
; abs(X) must be greater than one.
; There must be at least two free registers for
; this function to operate properly.
;
; asec(x) = atan(sqrt(x*x-1))
asec proc near
fld st(0) ;Compute x*x
fmul
fsub
fsqrt ;Compute sqrt(x*x-1)
fld1
fpatan ;Compute atan of above.
ret
asec endp
; TwoToX(x)- Computes 2**x.
; It does this by using the algebraic identity:
;
; 2**x = 2**int(x) * 2**frac(x).
; We can easily compute 2**int(x) with fscale and
; 2**frac(x) using f2xm1.
;
; This routine requires three free registers.
SaveCW word ?
MaskedCW word ?
TwoToX proc near
fstcw cseg:SaveCW
; Modify the control word to truncate when rounding.
fstcw cseg:MaskedCW
or byte ptr cseg:MaskedCW+1, 1100b fldcw cseg:MaskedCW
fld st(0) ;Duplicate tos.
fld st(0)
frndint ;Compute integer portion.
fxch ;Swap whole and int values.
fsub st(0), st(1) ;Compute fractional part.
f2xm1 ;Compute 2**frac(x)-1.
fld1
fadd ;Compute 2**frac(x).
fxch ;Get integer portion.
fld1 ;Compute 1*2**int(x).
fscale
fstp st(1) ;Remove st(1) (which is 1).
fmul ;Compute 2**int(x) * 2**frac(x).
fldcw cseg:SaveCW ;Restore rounding mode.
ret
TwoToX endp
; TenToX(x)- Computes 10**x.
;
; This routine requires three free registers.
;
; TenToX(x) = 2**(x * lg(10))
TenToX proc near
fldl2t ;Put lg(10) onto the stack
fmul ;Compute x*lg(10)
call TwoToX ;Compute 2**(x * lg(10)).
ret
TenToX endp
; exp(x)- Computes e**x.
;
; This routine requires three free registers.
;
; exp(x) = 2**(x * lg(e))
exp proc near
fldl2e ;Put lg(e) onto the stack.
fmul ;Compute x*lg(e).
call TwoToX ;Compute 2**(x * lg(e)) ret
exp endp
; YtoX(y,x)- Computes y**x (y=st(1), x=st(0)).
;
; This routine requires three free registers.
;
; Y must be greater than zero.
;
; YtoX(y,x) = 2 ** (x * lg(y))
YtoX proc near
fxch ;Compute lg(y).
fld1 fxch fyl2x
fmul ;Compute x*lg(y).
call TwoToX ;Compute 2**(x*lg(y)).
ret
YtoX endp
; LOG(x)- Computes the base 10 logarithm of x.
;
; Usual range for x (>0).
;
; LOG(x) = lg(x)/lg(10).
log proc near
fld1 fxch
fyl2x ;Compute 1*lg(x).
fldl2t ;Load lg(10).
fdiv ;Compute lg(x)/lg(10).
ret
log endp
; LN(x)- Computes the base e logarithm of x.
;
; X must be greater than zero.
;
; ln(x) = lg(x)/lg(e).
ln proc near
fld1 fxch
fyl2x ;Compute 1*lg(x).
fldl2e ;Load lg(e).
fdiv ;Compute lg(x)/lg(10).
ret
ln endp
; This main program tests the various functions in this package.
Main proc
mov ax, dseg mov ds, ax mov es, ax meminit
finit
; Check to see if cot and acot are working properly.
call cot fst cotRes call acot fstp acotRes printff
byte “x=%8.5gf, cot(x)=%8.5gf, acot(cot(x)) = %8.5gf\n”,0 dword cotVar, cotRes, acotRes
; Check to see if csc and acsc are working properly.
fld cscVar call csc fst cscRes call acsc fstp acscRes printff
byte “x=%8.5gf, csc(x)=%8.5gf, acsc(csc(x)) = %8.5gf\n”,0 dword cscVar, cscRes, acscRes
; Check to see if sec and asec are working properly.
fld secVar call sec fst secRes call asec fstp asecRes printff
byte “x=%8.5gf, sec(x)=%8.5gf, asec(sec(x)) = %8.5gf\n”,0 dword secVar, secRes, asecRes
; Check to see if sin and asin are working properly.
fld sinVar fsin
fst sinRes call asin fstp asinRes printff
byte “x=%8.5gf, sin(x)=%8.5gf, asin(sin(x)) = %8.5gf\n”,0 dword sinVar, sinRes, asinRes
; Check to see if cos and acos are working properly.
fld cosVar fcos
fst cosRes call acos fstp acosRes printff
byte “x=%8.5gf, cos(x)=%8.5gf, acos(cos(x)) = %8.5gf\n”,0 dword cosVar, cosRes, acosRes
; Check to see if 2**x and lg(x) are working properly.
fld Two2xVar call TwoToX fst Two2xRes fld1
fxch fyl2x
fstp lgxRes printff
byte “x=%8.5gf, 2**x =%8.5gf, lg(2**x) = %8.5gf\n”,0
dword Two2xVar, Two2xRes, lgxRes
; Check to see if 10**x and l0g(x) are working properly.
fld Ten2xVar call TenToX fst Ten2xRes call LOG fstp logRes printff
byte “x=%8.5gf, 10**x =%8.2gf, log(10**x) = %8.5gf\n”,0 dword Ten2xVar, Ten2xRes, logRes
; Check to see if exp(x) and ln(x) are working properly.
fld expVar call exp fst expRes
call ln
fstp lnRes printff
byte “x=%8.5gf, e**x =%8.2gf, ln(e**x) = %8.5gf\n”,0 dword expVar, expRes, lnRes
; Check to see if y**x is working properly.
fld Y2Xy
fld Y2Xx
call YtoX fstp Y2XRes printff
byte “x=%8.5gf, y =%8.5gf, y**x = %8.4gf\n”,0 dword Y2Xx, Y2Xy, Y2XRes
Quit: ExitPgm
Main endp
cseg ends
sseg segment para stack ‘stack’
stk byte 1024 dup (“stack “)
sseg ends
zzzzzzseg segment para public ‘zzzzzz’
LastBytes byte 16 dup (?) zzzzzzseg ends
end Main
Sample program output:
x= 3.00000, cot(x)=-7.01525, acot(cot(x)) = 3.00000 x= 1.50000, csc(x)= 1.00251, acsc(csc(x)) = 1.50000 x= 0.50000, sec(x)= 1.13949, asec(sec(x)) = 0.50000 x= 0.75000, sin(x)= 0.68163, asin(sin(x)) = 0.75000 x= 0.25000, cos(x)= 0.96891, acos(cos(x)) = 0.25000 x=-2.50000, 2**x = 0.17677, lg(2**x) = -2.50000 x= 3.75000, 10**x = 5623.41, log(10**x) = 3.75000 x= 3.25000, e**x = 25.79, ln(e**x) = 3.25000 x= 3.00000, y = 3.00000, y**x = 27.0000