windows-nt/Source/XPSP1/NT/base/ntdll/i386/emftran.asm

1207 lines
36 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
subttl emftran.asm - Transcendental instructions
page
;*******************************************************************************
; Copyright (c) Microsoft Corporation 1991
; All Rights Reserved
;
;emftran.asm - Transcendental instructions
; by Tim Paterson
;
;Purpose:
; F2XM1, FPATAN, FYL2X, FYL2XP1 instructions
;Inputs:
; edi = [CURstk]
;
;Revision History:
;
; [] 09/05/91 TP Initial 32-bit version.
;
;*******************************************************************************
;********************* Polynomial Coefficients *********************
;These polynomial coefficients were all taken from "Computer Approximations"
;by J.F. Hart (reprinted 1978 w/corrections). All calculations and
;conversions to hexadecimal were done with a character-string calculator
;written in Visual Basic with precision set to 30 digits. Once the constants
;were typed into this file, all transfers were done with cut-and-paste
;operations to and from the calculator to help eliminate any typographical
;errors.
tAtanPoly label word
;These constants are from Hart #5056: atan(x) = x * P(x^2) / Q(x^2),
;accurate to 20.78 digits over interval [0, tan(pi/12)].
dd 4 ;P() is degree four
; Hart constant
;
;+.16241 70218 72227 96595 08 E0
;Hex value: 0.A650A5D5050DE43A2C25A8C00 HFFFE
dq 0A650A5D5050DE43AH
dw bTAG_VALID,0FFFEH-1
;+.65293 76545 29069 63960 675 E1
;Hex value: 0.D0F0A714A9604993AC4AC49A0 H3
dq 0D0F0A714A9604994H
dw bTAG_VALID,03H-1
;+.39072 57269 45281 71734 92684 E2
;Hex value: 0.9C4A507F16530AC3CDDEFA3DE H6
dq 09C4A507F16530AC4H
dw bTAG_VALID,06H-1
;+.72468 55912 17450 17145 90416 9 E2
;Hex value: 0.90EFE6FB30465042CF089D1310 H7
dq 090EFE6FB30465043H
dw bTAG_VALID,07H-1
;+.41066 29181 34876 24224 77349 62 E2
;Hex value: 0.A443E2004BB000B84A5154D44 H6
dq 0A443E2004BB000B8H
dw bTAG_VALID,06H-1
dd 4 ;Q() is degree four
; Hart constant
;
;+.15023 99905 56978 85827 4928 E2
;Hex value: 0.F0624CD575B782643AFB912D0 H4
dq 0F0624CD575B78264H
dw bTAG_VALID,04H-1
;+.59578 42201 83554 49303 22456 E2
;Hex value: 0.EE504DDC907DEAEB7D7473B82 H6
dq 0EE504DDC907DEAEBH
dw bTAG_VALID,06H-1
;+.86157 32305 95742 25062 42472 E2
;Hex value: 0.AC508CA5E78E504AB2032E864 H7
dq 0AC508CA5E78E504BH
dw bTAG_VALID,07H-1
;+.41066 29181 34876 24224 84140 84 E2
;Hex value: 0.A443E2004BB000B84F542813C H6
dq 0A443E2004BB000B8H
dw bTAG_VALID,06H-1
;tan(pi/12) = tan(15 deg.) = 2 - sqrt(3)
;= 0.26794 91924 31122 70647 25536 58494 12763 ;From Hart appendix
;Hex value: 0.8930A2F4F66AB189B517A51F2 HFFFF
Tan15Hi equ 08930A2F4H
Tan15Lo equ 0F66AB18AH
Tan15exp equ 0FFFFH-1
;1/tan(pi/6) = sqrt(3) = 1.73205 08075 68877 29352 74463 41505 87236 ;From Hart appendix
;Hex value: 0.DDB3D742C265539D92BA16B8 H1
Sqrt3Hi equ 0DDB3D742H
Sqrt3Lo equ 0C265539EH
Sqrt3exp equ 01H-1
;pi = +3.14159265358979323846264338328
;Hex value: 0.C90FDAA22168C234C4C6628B8 H2
PiHi equ 0C90FDAA2H
PiLo equ 02168C235H
PiExp equ 02H-1
;3*pi = +9.42477796076937971538793014984
;Hex value: 0.96CBE3F9990E91A79394C9E890 H4
XThreePiHi equ 096CBE3F9H
XThreePiMid equ 0990E91A7H
XThreePiLo equ 090000000H
ThreePiExp equ 04H-1
;This is a table of multiples of pi/6. It is used to adjust the
;final result angle after atan(). Derived from Hart appendix
;pi/180 = 0.01745 32925 19943 29576 92369 07684 88612
;
;When the reduced argument for atan() is very small, these correction
;constants simply become the result. These constants have all been
;rounded to nearest, but the user may have selected a different rounding
;mode. The tag byte is not needed for these constants, so its space
;is used to indicate if it was rounded. To determine if a constant
;was rounded, 7FH is subtracted from this flag; CY set means it was
;rounded up.
RoundedUp equ 040H
RoundedDown equ 0C0H
tAtanPiFrac label dword
;pi/2 = +1.57079632679489661923132169163
;Hex value: 0.C90FDAA22168C234C4C6628B0 H1
dq 0C90FDAA22168C235H
dw RoundedUp,01H-1
;2*pi/3 = +2.09439510239319549230842892218
;Hex value: 0.860A91C16B9B2C232DD997078 H2
dq 0860A91C16B9B2C23H
dw RoundedDown,02H-1
;none
dd 0,0,0
;pi/6 = +0.523598775598298873077107230544E0
;Hex value: 0.860A91C16B9B2C232DD99707A H0
dq 0860A91C16B9B2C23H
dw RoundedDown,00H-1
;pi/2 = +1.57079632679489661923132169163
;Hex value: 0.C90FDAA22168C234C4C6628B0 H1
dq 0C90FDAA22168C235H
dw RoundedUp,01H-1
;pi/3 = +1.04719755119659774615421446109
;Hex value: 0.860A91C16B9B2C232DD997078 H1
dq 0860A91C16B9B2C23H
dw RoundedDown,01H-1
;pi = +3.14159265358979323846264338328
;Hex value: 0.C90FDAA22168C234C4C6628B8 H2
dq 0C90FDAA22168C235H
dw RoundedUp,02H-1
;5*pi/6 = +2.61799387799149436538553615272
;Hex value: 0.A78D3631C681F72BF94FFCC96 H2
dq 0A78D3631C681F72CH
dw RoundedUp,02H-1
;*********************
tExpPoly label word
;These constants are from Hart #1324: 2^x - 1 =
; 2 * x * P(x^2) / ( Q(x^2) - x * P(x^2) )
;accurate to 21.54 digits over interval [0, 0.5].
dd 2 ;P() is degree two
; Hart constant
;
;+.60613 30790 74800 42574 84896 07 E2
;Hex value: 0.F27406FCF405189818F68BB78 H6
dq 0F27406FCF4051898H
dw bTAG_VALID,06H-1
;+.30285 61978 21164 59206 24269 927 E5
;Hex value: 0.EC9B3D5414E1AD0852E432A18 HF
dq 0EC9B3D5414E1AD08H
dw bTAG_VALID,0FH-1
;+.20802 83036 50596 27128 55955 242 E7
;Hex value: 0.FDF0D84AC3A35FAF89A690CC4 H15
dq 0FDF0D84AC3A35FB0H
dw bTAG_VALID,015H-1
dd 3 ;Q() is degree three. First
;coefficient is 1.0 and is not listed.
; Hart constant
;
;+.17492 20769 51057 14558 99141 717 E4
;Hex value: 0.DAA7108B387B776F212ECFBEC HB
dq 0DAA7108B387B776FH
dw bTAG_VALID,0BH-1
;+.32770 95471 93281 18053 40200 719 E6
;Hex value: 0.A003B1829B7BE85CC81BD5309 H13
dq 0A003B1829B7BE85DH
dw bTAG_VALID,013H-1
;+.60024 28040 82517 36653 36946 908 E7
;Hex value: 0.B72DF814E709837E066855BDD H17
dq 0B72DF814E709837EH
dw bTAG_VALID,017H-1
;sqrt(2) = 1.41421 35623 73095 04880 16887 24209 69808 ;From Hart appendix
;Hex value: 0.B504F333F9DE6484597D89B30 H1
Sqrt2Hi equ 0B504F333H
Sqrt2Lo equ 0F9DE6484H
Sqrt2Exp equ 01H-1
;sqrt(2) - 1 = +0.4142135623730950488016887242E0
;Hex value: 0.D413CCCFE779921165F626CC4 HFFFF
Sqrt2m1Hi equ 0D413CCCFH
Sqrt2m1Lo equ 0E7799211H
XSqrt2m1Lo equ 060000000H
Sqrt2m1Exp equ 0FFFFH-1
;2 - sqrt(2) = +0.5857864376269049511983112758E0
;Hex value: 0.95F619980C4336F74D04EC9A0 H0
TwoMinusSqrt2Hi equ 095F61998H
TwoMinusSqrt2Lo equ 00C4336F7H
TwoMinusSqrt2Exp equ 00H-1
;*********************
tLogPoly label dword
;These constants are derived from Hart #2355: log2(x) = z * P(z^2) / Q(z^2),
; z = (x+1) / (x-1) accurate to 19.74 digits over interval
;[1/sqrt(2), sqrt(2)]. The original Hart coefficients were for log10();
;the P() coefficients have been scaled by log2(10) to compute log2().
;
;log2(10) = 3.32192 80948 87362 34787 03194 29489 39017 ;From Hart appendix
dd 3 ;P() is degree three
; Original Hart constant Scaled value
;
;+.18287 59212 09199 9337 E0 +0.607500660543248917834110566373E0
;Hex value: 0.9B8529CD54E72022A12BAEC53 H0
dq 09B8529CD54E72023H
dw bTAG_VALID,00H-1
;-.41855 96001 31266 20633 E1 -13.9042489506087332809657007634
;Hex value: 0.DE77CDBF64E8C53F0DCD458D0 H4
dq 0DE77CDBF64E8C53FH
dw bSign shl 8 + bTAG_VALID,04H-1
;+.13444 58152 27503 62236 E2 +44.6619330844279438866067340334
;Hex value: 0.B2A5D1C95708A0C9FE50F6F97 H6
dq 0B2A5D1C95708A0CAH
dw bTAG_VALID,06H-1
;-.10429 11213 72526 69497 44122 E2 -34.6447606134704282123622236943
;Hex value: 0.8A943C20526AE439A98B30F6A H6
dq 08A943C20526AE43AH
dw bSign shl 8 + bTAG_VALID,06H-1
dd 3 ;Q() is degree three. First
;coefficient is 1.0 and is not listed.
; Hart constant
;
;-.89111 09060 90270 85654 E1
;Hex value: 0.8E93E7183AA998D74F45CDFF0 H4
dq 08E93E7183AA998D7H
dw bSign shl 8 + bTAG_VALID,04H-1
;+.19480 96618 79809 36524 155 E2
;Hex value: 0.9BD904CCFEE118D4BEF319716 H5
dq 09BD904CCFEE118D5H
dw bTAG_VALID,05H-1
;-.12006 95907 02006 34243 4218 E2
;Hex value: 0.C01C811D2EC1B5806304B1858 H4
dq 0C01C811D2EC1B580H
dw bSign shl 8 + bTAG_VALID,04H-1
;Log2(e) = 1.44269 50408 88963 40735 99246 81001 89213 ;From Hart appendix
;Hex value: 0.B8AA3B295C17F0BBBE87FED04 H1
Log2OfEHi equ 0B8AA3B29H
Log2OfELo equ 05C17F0BCH
Log2OfEexp equ 01H-1
;********************* Generic polynomial evaluation *********************
;
;EvalPoly, EvalPolyAdd, EvalPolySetup, Eval2Poly
;
;Inputs:
; ebx:esi,ecx = floating point number, internal format
; edi = pointer to polynomial degree and coefficients
;Outputs:
; result in ebx:esi,ecx
; edi incremented to start of last coefficient in list
;
;EvalPoly is the basic polynomial evaluator, using Horner's rule. The
;polynomial pointer in edi points to a list: the first dword in the list
;is the degree of the polynomial (n); it is followed by the n+1
;coefficients in internal (12-byte) format. The argment for EvalPoly
;must be stored in the static FloatTemp in addition to being in
;registers.
;
;EvalPolyAdd is an alternate entry point into the middle of EvalPoly.
;It is used when the first coefficient is 1.0, so it skips the first
;multiplication. It requires that the degree of the polynomial be
;already loaded into ebp.
;
;EvalPolySetup store a copy of the argument in the static ArgTemp,
;and stores the square of the argument in the static FloatTemp.
;Then it falls into EvalPoly to evaluate the polynomial on the square.
;
;Eval2Poly evaluate two polynomials on its argument. The first
;polynomial is x * P(x^2), and its result is left at [[CURstk]].
;The second polynomial is Q(x^2), and its result is left in registers.
;The most significant coefficient of Q() is 1.
;
;Polynomial evaluation uses a slight variation on the standard add
;and multiply routines. PolyAddDouble and PolyMulDouble both check
;to see if the argument in registers (the current accumulation) is
;zero. The argument pointed to by edi is a coefficient and is never
;zero.
;
;In addition, the [RoundMode] and [ZeroVector] vectors are "trapped",
;i.e., redirected to special handlers for polynomial evaluation.
;[RoundMode] ordinarily points to the routine that handles the
;the current rounding mode and precision control; however, during
;polynomial evaluation, we always want full precision and round
;nearest. The normal rounding routines also store their result
;at [[Result]], but we want the result left in registers.
;[ZeroVector] exists solely so polynomial evaluation can trap
;when AddDouble results of zero. The normal response is to store
;a zero at [[Result]], but we need the zero left in registers.
;PolyRound and PolyZero handle these traps.
EvalPolySetup:
;Save x in ArgTemp
mov EMSEG:[ArgTemp].ExpSgn,ecx
mov EMSEG:[ArgTemp].lManHi,ebx
mov EMSEG:[ArgTemp].lManLo,esi
mov EMSEG:[RoundMode],offset PolyRound
mov EMSEG:[ZeroVector],offset PolyZero
push edi ;Save pointer to polynomials
;op1 mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
mov edx,ebx
mov edi,esi
mov eax,ecx
;op2 mantissa in edx:edi, exponent in high eax, sign in ah bit 7
call MulDoubleReg ;Compute x^2
;Save x^2 in FloatTemp
mov EMSEG:[FloatTemp].ExpSgn,ecx
mov EMSEG:[FloatTemp].lManHi,ebx
mov EMSEG:[FloatTemp].lManLo,esi
pop edi
EvalPoly:
;ebx:esi,ecx = arg to evaluate, also in FloatTemp
;edi = pointer to degree and list of coefficients.
push edi
mov eax,cs:[edi+4].ExpSgn
mov edx,cs:[edi+4].lManHi
mov edi,cs:[edi+4].lManLo
call MulDoubleReg ;Multiply arg by first coef.
pop edi
mov ebp,cs:[edi] ;Get polynomial degree
add edi,4+Reg87Len ;Point to second coefficient
jmp EvalPolyAdd
PolyLoop:
push ebp ;Save loop count
ifdef NT386
mov edi,YFloatTemp
else
mov edi,offset edata:FloatTemp
endif
call PolyMulDouble
pop ebp
pop edi
add di,Reg87Len
EvalPolyAdd:
push edi
mov eax,cs:[edi].ExpSgn
mov edx,cs:[edi].lManHi
mov edi,cs:[edi].lManLo
cmp cl,bTAG_ZERO ;Adding to zero?
jz AddToZero
call AddDoubleReg ;ebp preserved
ContPolyLoop:
dec ebp
jnz PolyLoop
pop edi
ret
AddToZero:
;Number in registers is zero, so just return value from memory.
mov ecx,eax
mov ebx,edx
mov esi,edi
jmp ContPolyLoop
Eval2Poly:
call EvalPolySetup
push edi
ifdef NT386
mov edi,YArgTemp
else
mov edi,offset edata:ArgTemp
endif
call PolyMulDouble ;Multiply first result by argument
pop edi
;Save result of first polynomial at [[CURstk]]
mov edx,EMSEG:[CURstk]
mov EMSEG:[edx].ExpSgn,ecx
mov EMSEG:[edx].lManHi,ebx
mov EMSEG:[edx].lManLo,esi
;Load x^2 back into registers
mov ecx,EMSEG:[FloatTemp].ExpSgn
mov ebx,EMSEG:[FloatTemp].lManHi
mov esi,EMSEG:[FloatTemp].lManLo
;Start second polynomial evaluation
add edi,4+Reg87Len ;Point to coefficient
mov ebp,cs:[edi-4] ;Get polynomial degree
jmp EvalPolyAdd
PolyRound:
;This routine handles all rounding during polynomial evaluation.
;It performs 64-but round nearest, with result left in registers.
;
;Inputs:
; mantissa in ebx:esi:eax, exponent in high ecx, sign in ch bit 7
;Outputs:
; same, plus tag in cl.
;
;To perform "round even" when the round bit is set and the sticky bits
;are zero, we treat the LSB as if it were a sticky bit. Thus if the LSB
;is set, that will always force a round up (to even) if the round bit is
;set. If the LSB is zero, then the sticky bits remain zero and we always
;round down. This rounding rule is implemented by adding RoundBit-1
;(7F..FFH), setting CY if round up.
;
;This routine needs to be reversible in case we're at the last step
;in the polynomial and final rounding uses a different rounding mode.
;We do this by copying the LSB of esi into al. While the rounding is
;reversible, you can't tell if the answer was exact.
mov edx,esi
and dl,1 ;Look at LSB
or al,dl ;Set LSB as sticky bit
add eax,(1 shl 31)-1 ;Sum LSB & sticky bits--CY if round up
adc esi,0
adc ebx,0
jc PolyBumpExponent ;Overflowed, increment exponent
or esi,esi ;Any bits in low half?
.erre bTAG_VALID eq 1
.erre bTAG_SNGL eq 0
setnz cl ;if low half==0 then cl=0 else cl=1
ret
PolyBumpExponent:
add ecx,1 shl 16 ;Mantissa overflowed, bump exponent
or ebx,1 shl 31 ;Set MSB
mov cl,bTAG_SNGL
PolyZero:
;Enter here when result is zero
ret
;*******************************************************************************
;FPATAN instruction
;Actual instruction entry point is in emarith.asm
tFpatanDisp label dword ;Source (ST(0)) Dest (*[di] = ST(1))
dd AtanDouble ;single single
dd AtanDouble ;single double
dd AtanZeroDest ;single zero
dd AtanSpclDest ;single special
dd AtanDouble ;double single
dd AtanDouble ;double double
dd AtanZeroDest ;double zero
dd AtanSpclDest ;double special
dd AtanZeroSource ;zero single
dd AtanZeroSource ;zero double
dd AtanZeroDest ;zero zero
dd AtanSpclDest ;zero special
dd AtanSpclSource ;special single
dd AtanSpclSource ;special double
dd AtanSpclSource ;special zero
dd TwoOpBothSpcl ;special special
dd AtanTwoInf ;Two infinites
;Compute atan( st(1)/st(0) ). Neither st(0) or st(1) are zero or
;infinity at this point.
;
;Argument reduction starts by dividing the smaller by the larger,
;ensuring that the result x is <= 1. The absolute value of the quotient
;is used and the quadrant is fixed up later. If x = st(0)/st(1), then
;the final atan result is subtracted from pi/2 (and normalized for the
;correct range of -pi to +pi).
;
;The range of x is further reduced using the formulas:
; t = (x - k) / (1 + kx)
; atan(x) = atan(k) + atan(t)
;
;Given that x <= 1, if we choose k = tan(pi/6) = 1/sqrt(3), then we
;are assured that t <= tan(pi/12) = 2 - sqrt(3), and
;for x >= tan(pi/12) = 2 - sqrt(3), t >= -tan(pi/12).
;Thus we can always reduce the argument to abs(t) <= tan(pi/12).
;
;Since k = 1/sqrt(3), it is convenient to multiply the numerator
;and denominator of t by 1/k, which gives
;t = (x/k - 1) / (1/k + x) = ( x*sqrt(3) - 1 ) / ( sqrt(3) + x ).
;This is the form found in Cody and Waite and in previous versions
;of the emulator. It requires one each add, subtract, multiply, and
;divide.
;
;Hart has derived a simpler version of this formula:
;t = 1/k - (1/k^2 + 1) / (1/k + x) = sqrt(3) - 4 / ( sqrt(3) + x ).
;Note that this computation requires one each add, subtract, and
;divide, but no multiply.
;st(0) mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;[edi] points to st(1), where result is returned
AtanDouble:
mov EMSEG:[Result],edi
mov EMSEG:[RoundMode],offset PolyRound
mov EMSEG:[ZeroVector],offset PolyZero
mov ah,EMSEG:[edi].bSgn ;Sign of result
mov al,ch ;Affects quadrant of result
and al,bSign ;Zero other bits, used as flags
push eax ;Save flag
;First figure out which is larger
push offset AtanQuo ;Return address for DivDouble
shld edx,ecx,16 ;Get exponent to ax
cmp dx,EMSEG:[edi].wExp ;Compare exponents
jl DivrDoubleSetFlag ;ST(0) is smaller, make it dividend
jg DivDouble ; ...is bigger, make it divisor
;Exponents are equal, compare mantissas
cmp ebx,EMSEG:[edi].lManHi
jb DivrDoubleSetFlag ;ST(0) is smaller, make it dividend
ja DivDouble ; ...is bigger, make it divisor
cmp esi,EMSEG:[edi].lManLo
jbe DivrDoubleSetFlag ;ST(0) is smaller, make it dividend
jmp DivDouble
TinyAtan:
;Come here if the angle was reduced to zero, or the divide resulted in
;unmasked underflow so that the quotient exponent was biased.
;Note that an angle of zero means reduction was performed, and the
;result will be corrected to a non-zero value.
mov dl,[esp] ;Get flag byte
or dl,dl ;No correction needed?
jz AtanSetSign ;Just return result of divide
and EMSEG:[CURerr],not Underflow
;Angle in registers is too small to affect correction amount. Just
;load up correction angle instead of adding it in.
add dl,40H ;Change flags for correction lookup
shr dl,5-2 ;Now in bits 2,3,4
and edx,7 shl 2
mov ebx,[edx+2*edx+tAtanPiFrac].lManHi
mov esi,[edx+2*edx+tAtanPiFrac].lManLo
mov ecx,[edx+2*edx+tAtanPiFrac].ExpSgn
shrd eax,ecx,8 ;Copy rounding flag to high eax
jmp AtanSetSign
AtanQuo:
;Return here after divide. Underflow flag is set only for "big underflow",
;meaning the (15-bit) exponent couldn't even be kept in 16 bits. This can
;only happen dividing a denormal by one of the largest numbers.
;
;Rounded mantissa in ebx:esi:eax, exp/sign in high ecx
test EMSEG:[CURerr],Underflow;Did we underflow?
jnz TinyAtan
;Now compare quotient in ebx:esi,ecx with tan(pi/12) = 2 - sqrt(3)
xor cx,cx ;Use absolute value
cmp ecx,Tan15exp shl 16
jg AtnNeedReduce
jl AtnReduced
cmp ebx,Tan15Hi
ja AtnNeedReduce
jb AtnReduced
cmp esi,Tan15Lo
jbe AtnReduced
AtnNeedReduce:
or byte ptr [esp],20H ;Note reduction in flags on stack
;Compute t = sqrt(3) - 4 / ( sqrt(3) + x ).
mov eax,Sqrt3exp shl 16
mov edx,Sqrt3Hi
mov edi,Sqrt3Lo
call AddDoubleReg ;x + sqrt(3)
mov edi,esi
mov esi,ebx ;Mantissa in esi:edi
mov ebx,ecx ;ExpSgn to ebx
mov ecx,(2+TexpBias) shl 16
mov edx,1 shl 31
xor eax,eax ;edx:edi,eax = 4.0
;dividend mantissa in edx:eax, exponent in high ecx, sign in ch bit 7
;divisor mantissa in esi:edi, exponent in high ebx, sign in bh bit 7
call DivDoubleReg ;4 / ( x + sqrt(3) )
not ch ;Flip sign
mov eax,Sqrt3exp shl 16
mov edx,Sqrt3Hi
mov edi,Sqrt3Lo
call AddDoubleReg ;sqrt(3) - 4 / ( x + sqrt(3) )
;Result in ebx:esi,ecx could be very small (or zero) if arg was near tan(pi/6).
cmp cl,bTAG_ZERO
jz TinyAtan
AtnReduced:
;If angle is small, skip the polynomial. atan(x) = x when x - x^3/3 = x
;[or 1 - x^2/3 = 1], which happens when x < 2^-32. This prevents underflow
;in computing x^2.
TinyAtanArg equ -32
cmp ecx,TinyAtanArg shl 16
jl AtanCorrection
mov edi,offset tAtanPoly
call Eval2Poly
mov edi,EMSEG:[CURstk] ;Point to first result
call DivDouble ;x * P(x^2) / Q(x^2)
AtanCorrection:
;Rounded mantissa in ebx:esi:eax, exp/sign in high ecx
;
;Correct sign and add fraction of pi to account for various angle reductions:
;
; flag bit indicates correction
;----------------------------------------------------
; 5 arg > tan(pi/12) add pi/6
; 6 st(1) > st(0) sub from pi/2
; 7 st(0) < 0 sub from pi
;
;This results in the following correction for the result R:
;
;bit 7 6 5 correction
;---------------------------
; 0 0 0 none
; 0 0 1 pi/6 + R
; 0 1 0 pi/2 - R
; 0 1 1 pi/3 - R
; 1 0 0 pi - R
; 1 0 1 5*pi/6 - R
; 1 1 0 pi/2 + R
; 1 1 1 2*pi/3 + R
mov dl,[esp] ;Get flag byte
or dl,dl ;No correction needed?
jz AtanSetSign
add dl,40H ;Set bit 7 for all -R cases
;This changes the meaning of the flag bits to the following:
;
;bit 7 6 5 correction
;---------------------------
; 0 0 0 pi/2 + R
; 0 0 1 2*pi/3 + R
; 0 1 0 none
; 0 1 1 pi/6 + R
; 1 0 0 pi/2 - R
; 1 0 1 pi/3 - R
; 1 1 0 pi - R
; 1 1 1 5*pi/6 - R
xor ch,dl ;Flip sign bit in cases 4 - 7
shr dl,5-2 ;Now in bits 2,3,4
and edx,7 shl 2
mov eax,[edx+2*edx+tAtanPiFrac].ExpSgn
mov edi,[edx+2*edx+tAtanPiFrac].lManLo
mov edx,[edx+2*edx+tAtanPiFrac].lManHi
call AddDoubleReg ;Add in correction angle
AtanSetSign:
pop edx ;Get flags again
mov ch,dh ;Set sign to original ST(1)
;Rounded mantissa in ebx:esi:eax, exp/sign in ecx
jmp TransUnround
;***
AtanSpclDest:
mov al,EMSEG:[edi].bTag ;Pick up tag
; cmp cl,bTAG_INF ;Is argument infinity?
cmp al,bTAG_INF ;Is argument infinity?
jnz SpclDest ;In emarith.asm
AtanZeroSource:
;Dividend is infinity or divisor is zero. Return pi/2 with
;same sign as dividend.
mov ecx,(PiExp-1) shl 16 + bTAG_VALID ;Exponent for pi/2
PiMant:
;For storing multiples of pi. Exponent/tag is in ecx.
mov ch,EMSEG:[edi].bSgn ;Get dividend's sign
mov ebx,XPiHi
mov esi,XPiMid
mov eax,XPiLo
;A jump through [TransRound] is only valid if the number is known not to
;underflow. Unmasked underflow requires [RoundMode] be set.
jmp EMSEG:[TransRound]
;***
AtanSpclSource:
cmp cl,bTAG_INF ;Scaling by infinity?
jnz SpclSource ;in emarith.asm
AtanZeroDest:
;Divisor is infinity or dividend is zero. Return zero for +divisor,
;pi for -divisor. Result sign is same is dividend.
or ch,ch ;Check divisor's sign
mov ecx,PiExp shl 16 + bTAG_VALID ;Exponent for pi
js PiMant ;Store pi
;Result is zero
mov EMSEG:[edi].lManHi,0
mov EMSEG:[edi].lManLo,0
mov EMSEG:[edi].wExp,0
mov EMSEG:[edi].bTAG,bTAG_ZERO
ret
;***
AtanTwoInf:
;Return pi/4 for +infinity divisor, 3*pi/4 for -infinity divisor.
;Result sign is same is dividend infinity.
or ch,ch ;Check divisor's sign
mov ecx,(PiExp-2) shl 16 + bTAG_VALID ;Exponent for pi/4
jns PiMant ;Store pi/4
mov ecx,(ThreePiExp-2) shl 16 + bTAG_VALID ;Exponent for 3*pi/4
mov ch,EMSEG:[edi].bSgn ;Get dividend's sign
mov ebx,XThreePiHi
mov esi,XThreePiMid
mov eax,XThreePiLo
;A jump through [TransRound] is only valid if the number is known not to
;underflow. Unmasked underflow requires [RoundMode] be set.
jmp EMSEG:[TransRound]
;*******************************************************************************
ExpSpcl:
;Tagged special
cmp cl,bTAG_DEN
jz ExpDenorm
cmp cl,bTAG_INF
mov al, cl
jnz SpclDestNotDen ;Check for Empty or NAN
;Have infinity, check its sign.
;Return -1 for -infinity, no change if +infinity
or ch,ch ;Check sign
jns ExpRet ;Just return the +inifinity
mov EMSEG:[edi].lManLo,0
mov EMSEG:[edi].lManHi,1 shl 31
mov EMSEG:[edi].ExpSgn,bSign shl 8 + bTAG_SNGL ;-1.0 (exponent is zero)
ret
ExpDenorm:
mov EMSEG:[CURerr],Denormal
test EMSEG:[CWmask],Denormal ;Is denormal exception masked?
jnz ExpCont ;Yes, continue
ExpRet:
ret
EM_ENTRY eF2XM1
eF2XM1:
;edi = [CURstk]
mov ecx,EMSEG:[edi].ExpSgn
cmp cl,bTAG_ZERO
jz ExpRet ;Return same zero
ja ExpSpcl
ExpCont:
;The input range specified for the function is (-1, +1). The polynomial
;used for this function is valid only over the range [0, +0.5], so range
;reduction is needed. Range reduction is based on the identity:
;
; 2^(a+b) = 2^a * 2^b
;
;1.0 or 0.5 can be added/subtracted from the argument to bring it into
;range. We calculate 2^x - 1 with a polynomial, and then adjust the
;result according to the amount added or subtracted, as shown in the table:
;
;Arg range Adj Polynomial result Required result, 2^x - 1
;
; (-1, -0.5] +1 P = 2^(x+1) - 1 (P - 1)/2
;
; (-0.5, 0) +0.5 P = 2^(x+0.5) - 1 P * sqrt(2)/2 + (sqrt(2)/2 - 1)
;
; (0, 0.5) 0 P = 2^x - 1 P
;
; [0.5, 1) -0.5 P = 2^(x-0.5) - 1 P * sqrt(2) + (sqrt(2)-1)
;
;Since the valid input range does not include +1.0 or -1.0, and zero is
;handled separately, the precision exception will always be set.
mov EMSEG:[Result],edi
mov EMSEG:[RoundMode],offset PolyRound
mov EMSEG:[ZeroVector],offset PolyZero
push offset TransUnround ;Always exit through here
mov ebx,EMSEG:[edi].lManHi
mov esi,EMSEG:[edi].lManLo
;Check for small argument, so that x^2 does not underflow. Note that
;e^x = 1+x for small x, where small x means x + x^2/2 = x [or 1 + x/2 = 1],
;which happens when x < 2^-64, so 2^x - 1 = x * ln(2) for small x.
TinyExpArg equ -64
cmp ecx,TinyExpArg shl 16
jl TinyExp
cmp ecx,-1 shl 16 + bSign shl 8 ;See if positive, < 0.5
jl ExpReduced
;Argument was not in range (0, 0.5), so we need some kind of reduction
or ecx,ecx ;Exp >= 0 means arg >= 1.0 --> too big
;CONSIDER: this returns through TransUnround which restores the rounding
;vectors, but it also randomly rounds the result becase eax is not set.
jge ExpRet ;Give up if arg out of range
;We're going to need to add/subtract 1.0 or 0.5, so load up the constant
mov edx,1 shl 31
xor edi,edi
mov eax,-1 shl 16 + bSign shl 8 ;edx:edi,eax = -0.5
mov ebp,offset ExpReducedMinusHalf
or ch,ch ;If it's positive, must be [0.5, 1)
jns ExpReduction
xor ah,ah ;edx:edi,eax = +0.5
mov ebp,offset ExpReducedPlusHalf
cmp ecx,eax ;See if abs(arg) >= 0.5
jl ExpReduction ;No, adjust by .5
xor eax,eax ;edx:edi,eax = 1.0
mov ebp,offset ExpReducedPlusOne
ExpReduction:
call AddDoubleReg ;Argument now in range [0, 0.5]
cmp cl,bTAG_ZERO ;Did reduction result in zero?
jz ExpHalf ;If so, must have been exactly 0.5
push ebp ;Address of reduction cleanup
ExpReduced:
mov edi,offset tExpPoly
call Eval2Poly
;2^x - 1 is approximated with 2 * x*P(x^2) / ( Q(x^2) - x*P(x^2) )
;Q(x^2) is in registers, P(x^2) is at [[CURstk]]
mov edi,EMSEG:[CURstk]
mov dx,bSign shl 8 ;Subtract memory operand
;Note that Q() and P() have no roots over the input range
;(they will never be zero).
call AddDouble ;Q(x^2) - x*P(x^2)
sub ecx,1 shl 16 ;Divide by two
mov edi,EMSEG:[CURstk]
jmp DivDouble ;2 * x*P(x^2) / ( Q(x^2) - x*P(x^2) )
;Returns to correct argument reduction correction routine or TransUnround
TinyExp:
;Exponent is very small (and was not reduced)
mov edx,cFLDLN2hi
mov edi,cFLDLN2lo
mov eax,cFLDLN2exp shl 16
;This could underflow (but not big time)
jmp MulDoubleReg ;Returns to TransUnround
ExpHalf:
;Argument of exactly 0.5 was reduced to zero. Just return result.
mov ebx,Sqrt2m1Hi
mov esi,Sqrt2m1Lo
mov eax,XSqrt2m1Lo + 1 shl 31 - 1
mov ecx,Sqrt2m1Exp shl 16
ret ;Exit through TransUnround
ExpReducedPlusOne:
;Correct result is (P - 1)/2
sub ecx,1 shl 16 ;Divide by two
mov edx,1 shl 31
xor edi,edi
mov eax,-1 shl 16 + bSign shl 8 ;edx:edi,eax = -0.5
jmp AddDoubleReg
ExpReducedPlusHalf:
;Correct result is P * sqrt(2)/2 - (1 - sqrt(2)/2)
mov edx,Sqrt2Hi
mov edi,Sqrt2Lo
mov eax,Sqrt2exp-1 shl 16 ;sqrt(2)/2
call MulDoubleReg
mov edx,TwoMinusSqrt2Hi
mov edi,TwoMinusSqrt2Lo
mov eax,(TwoMinusSqrt2Exp-1) shl 16 + bSign shl 8 ;(2-sqrt(2))/2
jmp AddDoubleReg
ExpReducedMinusHalf:
;Correct result is P * sqrt(2) + (sqrt(2)-1)
mov edx,Sqrt2Hi
mov edi,Sqrt2Lo
mov eax,Sqrt2exp shl 16
call MulDoubleReg
mov edx,Sqrt2m1Hi
mov edi,Sqrt2m1Lo
mov eax,Sqrt2m1Exp shl 16
jmp AddDoubleReg
;*******************************************************************************
;Dispatch table for log(x+1)
;
;One operand has been loaded into ecx:ebx:esi ("source"), the other is
;pointed to by edi ("dest").
;
;Tag of source is shifted. Tag values are as follows:
.erre TAG_SNGL eq 0 ;SINGLE: low 32 bits are zero
.erre TAG_VALID eq 1
.erre TAG_ZERO eq 2
.erre TAG_SPCL eq 3 ;NAN, Infinity, Denormal, Empty
;Any special case routines not found in this file are in emarith.asm
tFyl2xp1Disp label dword ;Source (ST(0)) Dest (*[di] = ST(1))
dd LogP1Double ;single single
dd LogP1Double ;single double
dd LogP1ZeroDest ;single zero
dd LogP1SpclDest ;single special
dd LogP1Double ;double single
dd LogP1Double ;double double
dd LogP1ZeroDest ;double zero
dd LogP1SpclDest ;double special
dd XorSourceSign ;zero single
dd XorSourceSign ;zero double
dd XorDestSign ;zero zero
dd LogP1SpclDest ;zero special
dd LogSpclSource ;special single
dd LogSpclSource ;special double
dd LogSpclSource ;special zero
dd TwoOpBothSpcl ;special special
dd LogTwoInf ;Two infinites
LogP1Double:
;st(0) mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;[edi] points to st(1), where result is returned
;
;This instruction is defined only for x+1 in the range [1/sqrt(2), sqrt(2)]
;The approximation used (valid over exactly this range) is
; log2(x) = z * P(z^2) / Q(z^2), z = (x-1) / (x+1), which is
; log2(x+1) = r * P(r^2) / Q(r^2), r = x / (x+2)
;
;We're not too picky about this range check because the function is simply
;"undefined" if out of range--EXCEPT, we're supposed to check for -1 and
;signal Invalid if less, -infinity if equal.
or ecx,ecx ;abs(x) >= 1.0?
jge LogP1OutOfRange ;Valid range is approx [-0.3, +0.4]
mov EMSEG:[Result],edi
mov EMSEG:[RoundMode],offset PolyRound
mov EMSEG:[ZeroVector],offset PolyZero
mov eax,1 shl 16 ;Exponent of 1 for adding 2.0
push offset TotalLog ;Return address for BasicLog
; jmp BasicLog ;Fall into BasicLog
;.erre BasicLog eq $
;BasicLog is used by eFYL2X and eFYL2XP1.
;eax has exponent and sign to add 1.0 or 2.0 to argument
;ebx:esi,ecx has argument, non-zero, tag not set
;ST has argument to take log2 of, minus 1. (This is the actual argument
;of eFYL2XP1, or argument minus 1 of eFYL2X.)
BasicLog:
mov edx,1 shl 31
xor edi,edi ;edx:edi,eax = +1.0 or +2.0
call AddDoubleReg
mov edi,EMSEG:[CURstk] ;Point to x-1
call DivDouble ;Compute (x-1) / (x+1)
;Result in registers is z = (x-1)/(x+1). For tiny z, ln(x) = 2*z, so
; log2(x) = 2 * log2(e) * z. Tiny z is such that z + z^3/3 = z.
cmp ecx,-32 shl 16 ;Smallest exponent to bother with
jl LogSkipPoly
mov edi,offset tLogPoly
call Eval2Poly
mov edi,EMSEG:[CURstk] ;Point to first result, r * P(r^2)
jmp DivDouble ;Compute r * P(r^2) / Q(r^2)
LogSkipPoly:
;Multiply r by 2 * log2(e)
mov edx,Log2OfEHi
mov edi,Log2OfELo
mov eax,(Log2OfEexp+1) shl 16
jmp MulDoubleReg
LogP1OutOfRange:
;Input range isn't valid, so we can return anything we want--EXCEPT, for
;numbers < -1 we must signal Invalid Operation, and Divide By Zero for
;-1. Otherwise, we return an effective log of one by just leaving the
;second operand as the return value.
;
;Exponent in ecx >= 0 ( abs(x) >= 1 )
or ch,ch ;Is it positive?
jns LogP1Ret ;If so, skip it
and ecx,0FFFFH shl 16 ;Look at exponent only: 0 for -1.0
sub ebx,1 shl 31 ;Kill MSB
or ebx,esi
or ebx,ecx
jnz ReturnIndefinite ;Must be < -1.0
jmp DivideByMinusZero
LogP1Ret:
ret
;***
LogP1ZeroDest:
or ch,ch ;Is it negative?
jns LogP1Ret ;If not, just leave it zero
or ecx,ecx ;abs(x) >= 1.0?
jl XorDestSign ;Flip sign of zero
;Argument is <= -1
jmp ReturnIndefinite ;Have 0 * log( <=0 )
;***
LogP1SpclDest:
mov al,EMSEG:[edi].bTag ;Pick up tag
cmp al,bTAG_INF ;Is argument infinity?
jnz SpclDest ;In emarith.asm
;Multiplying log(x+1) * infinity.
;If x > 0, return original infinity.
;If -1 <= x < 0, return infinity with sign flipped.
;If x < -1 or x == 0, invalid operation.
cmp cl,bTAG_ZERO
jz ReturnIndefinite
or ch,ch ;Is it positive?
jns LogP1Ret
test ecx,0FFFFH shl 16 ;Is exponent zero?
jl XorDestSign
jg ReturnIndefinite
sub ebx,1 shl 31 ;Kill MSB
or ebx,esi
jnz ReturnIndefinite ;Must be < -1.0
jmp XorDestSign
;***
LogSpclSource:
cmp cl,bTAG_INF ;Is argument infinity?
jnz SpclSource ;in emarith.asm
or ch,ch ;Is it negative infinity?
js ReturnIndefinite
jmp MulByInf
;***
LogTwoInf:
or ch,ch ;Is it negative infinity?
js ReturnIndefinite
jmp XorDestSign
;*******************************************************************************
;Dispatch table for log(x)
;
;One operand has been loaded into ecx:ebx:esi ("source"), the other is
;pointed to by edi ("dest").
;
;Tag of source is shifted. Tag values are as follows:
.erre TAG_SNGL eq 0 ;SINGLE: low 32 bits are zero
.erre TAG_VALID eq 1
.erre TAG_ZERO eq 2
.erre TAG_SPCL eq 3 ;NAN, Infinity, Denormal, Empty
;Any special case routines not found in this file are in emarith.asm
tFyl2xDisp label dword ;Source (ST(0)) Dest (*[di] = ST(1))
dd LogDouble ;single single
dd LogDouble ;single double
dd LogZeroDest ;single zero
dd LogSpclDest ;single special
dd LogDouble ;double single
dd LogDouble ;double double
dd LogZeroDest ;double zero
dd LogSpclDest ;double special
dd DivideByMinusZero ;zero single
dd DivideByMinusZero ;zero double
dd ReturnIndefinite ;zero zero
dd LogSpclDest ;zero special
dd LogSpclSource ;special single
dd LogSpclSource ;special double
dd LogSpclSource ;special zero
dd TwoOpBothSpcl ;special special
dd LogTwoInf ;Two infinites
LogDouble:
;st(0) mantissa in ebx:esi, exponent in high ecx, sign in ch bit 7
;[edi] points to st(1), where result is returned
;
;Must reduce the argument to the range [1/sqrt(2), sqrt(2)]
or ch,ch ;Is it positive?
js ReturnIndefinite ;Can't take log of negative number
mov EMSEG:[Result],edi
mov EMSEG:[RoundMode],offset PolyRound
mov EMSEG:[ZeroVector],offset PolyZero
shld eax,ecx,16 ;Save exponent in ax as int part of log2
xor ecx,ecx ;Zero exponent: 1 <= x < 2
cmp ebx,Sqrt2Hi ;x > sqrt(2)?
jb LogReduced
ja LogReduceOne
cmp esi,Sqrt2Lo
jb LogReduced
LogReduceOne:
sub ecx,1 shl 16 ;1/sqrt(2) < x < 1
inc eax
LogReduced:
push eax ;Save integer part of log2
mov ebp,ecx ;Save reduced exponent (tag is wrong!)
mov edx,1 shl 31
mov eax,bSign shl 8 ;Exponent of 0, negaitve
xor edi,edi ;edx:edi,eax = -1.0
call AddDoubleReg
cmp cl,bTAG_ZERO ;Was it exact power of two?
jz LogDone ;Skip log if power of two
;Save (x - 1), reload x with reduced exponent
mov edi,EMSEG:[CURstk] ;Point to original x again
xchg EMSEG:[edi].lManHi,ebx
xchg EMSEG:[edi].lManLo,esi
mov EMSEG:[edi].ExpSgn,ecx
mov ecx,ebp ;Get reduced exponent
xor eax,eax ;Exponent of 0, positive
call BasicLog
LogDone:
pop eax ;Get integer part back
cwde
or eax,eax ;Is it zero?
jz TotalLog
;Next 3 instructions take abs() of integer
cdq ;Extend sign through edx
xor eax,edx ;Complement...
sub eax,edx ; and increment if negative
bsr dx,ax ;Look for MSB to normalize integer
;Bit number in dx ranges from 0 to 15
mov cl,dl
not cl ;Convert to shift count
shl eax,cl ;Normalize
.erre TexpBias eq 0
rol edx,16 ;Move exponent high, sign low
or ebx,ebx ;Was log zero?
jz ExactPower
xchg edx,eax ;Exp/sign to eax, mantissa to edx
xor edi,edi ;Extend with zero
call AddDoubleReg
TotalLog:
;Registers could be zero if input was exactly 1.0
cmp cl,bTAG_ZERO
jz ZeroLog
TotalLogNotZero:
mov edi,EMSEG:[Result] ;Point to second arg
push offset TransUnround
jmp MulDouble
ExactPower:
;Arg was a power of two, so log is exact (but not zero).
mov ebx,eax ;Mantissa to ebx
mov ecx,edx ;Exponent to ecx
xor esi,esi ;Extend with zero
;Exponent of arg [= log2(arg)] is now normalized in ebx:esi,ecx
;
;The result log is exact, so we don't want TransUnround, which is designed
;to ensure the result is never exact. Instead we set the [RoundMode]
;vector to [TransRound] before the final multiply.
mov eax,EMSEG:[TransRound]
mov EMSEG:[RoundMode],eax
mov edi,EMSEG:[Result] ;Point to second arg
push offset RestoreRound ;Return addr. for MulDouble in emtrig.asm
jmp MulDouble
ZeroLog:
mov eax,EMSEG:[SavedRoundMode]
mov EMSEG:[RoundMode],eax
mov EMSEG:[ZeroVector],offset SaveResult
jmp SaveResult
;***
LogZeroDest:
or ch,ch ;Is it negative?
js ReturnIndefinite ;Can't take log of negative numbers
;See if log is + or - so we can get correct sign of zero
or ecx,ecx ;Is exponent >= 0?
jge LogRet ;If so, keep present zero sign
FlipDestSign:
not EMSEG:[edi].bSgn
ret
;***
LogSpclDest:
mov al,EMSEG:[edi].bTag ;Pick up tag
cmp al,bTAG_INF ;Is argument infinity?
jnz SpclDest ;In emarith.asm
;Multiplying log(x) * infinity.
;If x > 1, return original infinity.
;If 0 <= x < 1, return infinity with sign flipped.
;If x < 0 or x == 1, invalid operation.
cmp cl,bTAG_ZERO
jz FlipDestSign
or ch,ch ;Is it positive?
js ReturnIndefinite
test ecx,0FFFFH shl 16 ;Is exponent zero?
jg LogRet ;x > 1, just return infinity
jl FlipDestSign
sub ebx,1 shl 31 ;Kill MSB
or ebx,esi
jz ReturnIndefinite ;x == 1.0
LogRet:
ret