[Chapter Fourteen][Previous]
[Art of Assembly][Randall
Hyde]
Art of Assembly: Chapter Fourteen
- 14.5 - Sample Program: Additional Trigonometric
Functions
14.5 Sample Program: Additional Trigonometric Functions
This section provides various examples of 80x87 FPU programming. This
group of routines provides several trigonometric, inverse trigonometric,
logarithmic, and exponential 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 trigonometric 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.
; Transcnd.asm
;
; Some transcendental functions for the 80x87 FPU
.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
; COT(x) - computes the cotangent of st(0) and leaves result in st(0).
; 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
fdivr ;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
fld1 ;Compute x*x-1
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.
fld cotVar
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
- 14.5 - Sample Program: Additional
Trigonometric Functions
Art of Assembly: Chapter Fourteen - 28 SEP 1996
[Chapter Fourteen][Previous]
[Art of Assembly][Randall
Hyde]