subttl	emtrig.asm - Trig functions sine, cosine, tangent
	page
;*******************************************************************************
;	 Copyright (c) Microsoft Corporation 1991
;	 All Rights Reserved
;
;emtrig.asm - Trig functions sine, cosine, tangent
;	by Tim Paterson
;
;Purpose:
;	FCOS, FPTAN, FSIN, FSINCOS instructions
;Inputs:
;	edi = [CURstk]
;
;Revision History:
;
; []	09/05/91  TP	Initial 32-bit version.
;
;*******************************************************************************


;XPi is the 66-bit value of Pi from the Intel manual
XPiHi		equ	0C90FDAA2H
XPiMid		equ	02168C234H
XPiLo		equ	0C0000000H	;Extension of pi
PiOver4exp	equ	-1		;Pi/4 ~= 3/4, so exponent is -1

TinyAngleExp	equ	-32		;Smallest angle we bother with
MaxAngleExp	equ	63		;Angle that's too big

Trig1Result:
;Trig function reduction routine used by functions returning 1 value
;(FSIN and FCOS)
;edi = [CURstk] = argument pointer
;Argument has already been checked for zero.
;ZF = (tag == bTAG_ZERO)
	jb	TrigPrem
;Tagged special
	mov	al,EMSEG:[edi].bTAG
	cmp	al,bTAG_DEN
	jz	TrigDenorm
	add	sp,4			;Don't return to caller
	cmp	al,bTAG_INF
	jnz	SpclDestNotDen		;Check for Empty or NAN
	mov	EMSEG:[SWcc],C2		;Can't reduce infinity
	jmp	ReturnIndefinite

TrigDenorm:
	mov	EMSEG:[CURerr],Denormal
	test	EMSEG:[CWmask],Denormal	;Is denormal exception masked?
	jnz	TrigPrem		;Yes, continue
	add	sp,4			;Don't return to caller
TrigRet:
	ret


Trig2Inf:
	mov	EMSEG:[SWcc],C2		;Can't reduce infinity
	jmp	Trig2Indefinite

Trig2StackOver:
	mov	EMSEG:[SWcc],C1		;Signal overflow
Trig2StackUnder:
	mov	EMSEG:[CURerr],Invalid+StackFlag
Trig2Indefinite:
	add	sp,4			;Don't return to caller
	call	ReturnIndefinite
	jz	TrigRet			;Unmasked, don't change registers
;Produce masked response
	mov	EMSEG:[CURstk],esi		;Push stack
	mov	edi,esi
	jmp	ReturnIndefinite

Trig2Special:
	cmp	al,bTAG_DEN
	jz	TrigDenorm
	cmp	al,bTAG_INF
	jz	Trig2Inf
;Must be a NAN
	add	sp,4			;Don't return to caller
	call	DestNAN
	jz	TrigRet			;Unmasked, don't change registers
;Produce masked response
	mov	EMSEG:[CURstk],esi		;Push stack
        mov     eax,EMSEG:[edi].ExpSgn
        mov     EMSEG:[esi].ExpSgn,eax
        mov     eax,EMSEG:[edi].lManHi
        mov     EMSEG:[esi].lManHi,eax
        mov     eax,EMSEG:[edi].lManLo
        mov     EMSEG:[esi].lManLo,eax
	ret

Trig2Zero:
	add	sp,4			;Don't return to caller
	mov	EMSEG:[CURstk],esi
	mov	edi,esi
;Amazing coincidence: both FSINCOS and FPTAN return the same result for
;a zero argument:
;	FSINCOS returns ST(0) = cos(0) = 1, ST(1) = sin(0) = 0.
;	FPTAN returns ST(0) = 1 always, ST(1) = tan(0) = 0.
;Return zero has same sign as argument zero, so we don't need to touch
;it -- just push +1.0.
	jmp	ReturnOne

TrigOutOfRange:
	mov	EMSEG:[SWcc],C2		;Signal argument not reduced
	add	sp,4
	ret

PrevStackWrap	esi,Trig2		;Tied to PrevStackElem below

Trig2Result:
;Trig function reduction routine used by functions returning 2 values
;(FSINCOS and FPTAN)
;edi = [CURstk] = argument pointer
	mov	esi,edi
	PrevStackElem	esi,Trig2	;esi points to second result location
	mov	al,EMSEG:[edi].bTAG	;Get tag
	cmp	al,bTAG_EMPTY		;Stack underflow if empty
	jz	Trig2StackUnder
	cmp	EMSEG:[esi].bTAG,bTAG_EMPTY	;Stack overflow if not empty
	jnz	Trig2StackOver
	cmp	al,bTAG_ZERO		;Is it Special?
	ja	Trig2Special
	jz	Trig2Zero
;Fall into TrigPrem

;****
;TrigPrem
;
;This routine reduces an angle in radians to the range [0, pi/4].
;Angles in odd-numbered octants have been subtracted from pi/4.
;It uses a 66-bit value for pi, as required by the 387.
;TrigPrem uses the same two-stage algorithm as FPREM (see 
;emfprem.asm).	However, it is limited to an argument < 2^63.
;
;Inputs:
;	edi = [CURstk]
;Outputs:
;	ebx:esi = remainder, normalized
;	high ecx = exponent, cl = tag
;	al = octant
;	edi = [CURstk]

TrigPrem:
	mov	EMSEG:[Result],edi
	mov	eax,EMSEG:[edi].lManLo
	mov	edx,EMSEG:[edi].lManHi
	movsx	ebx,EMSEG:[edi].wExp
	cmp	ebx,MaxAngleExp
	jge	TrigOutOfRange
	xor	edi,edi			;Extend dividend
	xor	esi,esi			;Quotient, in case we skip stage 1
.erre	PiOver4exp eq -1
	inc	ebx			;Subtract exponent of pi/4
	jl	ExitTrigPrem		;If dividend is smaller, return it.
;We now know that 0 <= ExpDif < 64, so it fits in bl.
	cmp	bl,31			;Do we need to do stage 1?
	jl	FitPi			;No, start stage 2

;FPREM stage 1
;
;Exponent difference is at least 31.  Use 32-bit division to compute
;quotient and exact remainder, reducing exponent difference by 31.
;
;edx:eax = dividend
;ebx = exponent difference

;Shift dividend right one bit to be sure DIV instruction won't overflow
;This means we'll be reducing the exponent difference by 31, not 32
	xor	ebp,ebp			;Dividend extension
	shrd	ebp,eax,1
	shrd	eax,edx,1
	shr	edx,1

	sub	bl,31			;Exponent reduced
	mov	ecx,XPiHi
	div	ecx			;Guess a quotient "digit"

;Check out our guess.  
;Currently, remainder in edx = (high dividend) - (quotient * high pi).
;(High dividend is the upper 64 bits--ebp has 1 bit.)  The definition 
;of remainder is (all dividend) - (quotient * all pi).  So if we
;subtract (quotient * low pi) from edx:ebp, we'll get the true 
;remainder.  If it's negative, our guess was too big.

	mov	esi,eax			;Save quotient
	mov	ecx,edx			;Save remainder

;The pi/4 we use has two bits set below the first 64 bits.  This means
;we must add another 3/4 of the quotient into the amount to subtract,
;which we'll compute by rounding the low 32 bits up 1, then subtracting 
;1/4 of quotient.  But since we're computing the amount to subtract from
;the remainder, we'll add the 1/4 of the quotient to the remainder instead
;of subtracting it from the amount to subtract.

.erre	XPiLo eq (3 shl 30)
	mov	eax,XPiMid+1
	mul	esi			;Quotient * low pi
;Note that ebp is either 0 or 800...00H
	shr	ebp,30			;Move down to low end
	shld	ebp,esi,30		;Move back up, adding 1/4 of quotient
	mov	edi,esi			;Another copy of quotient
	shl	edi,30			;Keep last two bits
;edx:eax has amount to subtract to get correct remainder from ecx:ebp:edi
	sub	ebp,eax
	sbb	ecx,edx			;Subtract from remainder
	mov	eax,ebp
	mov	edx,ecx			;Remainder back to edx:eax:edi
	jnc	TrigPremNorm		;Was quotient OK?
TrigCorrect:
	dec	esi			;Quotient was too big
	add	edi,XPiLo
	adc	eax,XPiMid		;Add divisor back into remainder
	adc	edx,XPiHi
	jnc	TrigCorrect		;Repeat if quotient is still too big
	jmp	TrigPremNorm

;FPREM stage 2
;
;Exponent difference is less than 32.  Use restoring long division to
;compute quotient bits until exponent difference is zero.  Note that we
;often get more than one bit/loop:  BSR is used to scan off leading
;zeros each time around.  Since the divisor is normalized, we can
;instantly compute a zero quotient bit for each leading zero bit.

TrigPremLoop:
;edx:eax:edi = dividend (remainder) minus pi/4
;esi = quotient
;ebx = exponent difference
;
;If D is current dividend and p is pi/4, then we have edx:eax:edi = D - p, 
;which is negative.  We want 2*D - p, which is positive.  
;2*D - p = 2*(D - p) + p.
	add	edi,edi			;2*(D - p)
	adc	eax,eax
	adc	edx,edx

	add	edi,XPiLo		;2*(D-p) + p = 2*D - p
	adc	eax,XPiMid
	adc	edx,XPiHi

	add	esi,esi			;Double quotient too
	dec	ebx			;Decrement exponent difference
PiFit:
	inc	esi
TrigPremNorm:
	bsr	ecx,edx			;Find first 1 bit
	jz	TrigPremZero
	not	cl
	and	cl,1FH			;Convert bit no. to shift count
	sub	ebx,ecx			;Reduce exponent difference
	jl	TrigTooFar
	shld	edx,eax,cl
	shld	eax,edi,cl
	shl	edi,cl			;Finish normalize shift
	shl	esi,cl			;Shift quotient
FitPi:
;Dividend could be larger or smaller than divisor
	sub	edi,XPiLo
	sbb	eax,XPiMid
	sbb	edx,XPiHi
	jnc	PiFit
;Couldn't subtract pi/2 from dividend.	
;edx:eax:edi = dividend - pi/4, which is negative
	or	ebx,ebx			;Is exponent difference zero?
	jg	TrigPremLoop
;If quotient (octant number) is odd, we have subtracted an odd number of
;pi/4's.  However, simple angle reductions work in multiples of pi/2.
;We will keep the extra pi/4 we just subtracted if the octant was odd.
;This will give a result range of [-pi/4, pi/4].  
	test	esi,1			;Is octant odd?
	jz	EvenOctant
NegPremResult:
;-pi/4 < dividend < 0.  Negate this since we use sign-magnitude representation.
	not	edx			;96-bit negation
	not	eax
	neg	edi
	sbb	eax,-1
	sbb	edx,-1
;May need to normalize
	bsr	ecx,edx
	jz	TrigNorm32
	lea	ebx,[ebx+ecx-31]	;Fix up exponent for normalization
	not	cl			;Convert bit no. to shift count
TrigShortNorm:
	shld	edx,eax,cl
	shld	eax,edi,cl
	shl	edi,cl			;Finish normalize shift
RoundPrem:
;Must round 66-bit result to 64 bits.
;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.  
	bt	eax,0			;Is mantissa even or odd? (set CY)
	adc	edi,(1 shl 31)-1	;Sum LSB & sticky bits--CY if round up
	adc	eax,0
	adc	edx,0
ExitTrigPrem:
;edx:eax = remainder, normalized
;esi = quotient
;ebx = exponent difference, zero or less
.erre	PiOver4exp eq -1
	dec	ebx			;True exponent
.erre	bTAG_SNGL eq 0
	shrd	ecx,ebx,16		;Exponent to high ecx
	mov	ebx,edx			;High mant. to ebx
	xchg	esi,eax			;Low mant. to esi, octant to eax
	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
	mov	edi,EMSEG:[CURstk]
	test	EMSEG:[edi].bSgn,bSign	;Was angle negative?
	jnz	FlipOct			;Yes, flip octant over
	ret

FlipOct:
;Angle was negative.  Subtract octant from 7.
	neg	al
	add	al,7
	ret

EvenOctant:
;Restore dividend
	add	edi,XPiLo
	adc	eax,XPiMid
	adc	edx,XPiHi
	jmp	RoundPrem

TrigTooFar:
;Exponent difference in ebx went negative when reduced by shift count in ecx.
;We need a quotient corresponding to exponent difference of zero.
	add	ecx,ebx			;Compute previous exponent difference
	shl	esi,cl			;Fix up quotient
	sub	ecx,ebx			;Restore shift count
	test	esi,1			;Is octant odd?
	jz	TrigShortNorm		;No, go normalize
	xor	ebx,ebx			;Restore old exponent difference (zero)
SubPiOver4:
;We are here if exponent difference was zero and octant is odd.
;As noted above, we need to reduce the angle by a multiple of pi/2,
;not pi/4.  We will subtract one more pi/4, which will make the
;result range [-pi/4, pi/4].
	sub	edi,XPiLo
	sbb	eax,XPiMid
	sbb	edx,XPiHi
	jmp	NegPremResult

TrigPremZero:
;High dword of remainder is all zero, so we've reduced exponent difference
;by 32 bits and overshot.  We need a quotient corresponding to exponent 
;difference of zero, so we just shift it by the original difference.  Then
;we need to normalize the rest of the remainder.
	mov	ecx,ebx			;Get exponent difference
	shl	esi,cl			;Fix up quotient
	test	esi,1			;Is octant odd?
	jnz	SubPiOver4		;Yes, go subtract another pi/4
TrigNorm32:
	bsr	ecx,eax
	jz	TinyTrig
	lea	ebx,[ebx+ecx-31-32]	;Fix up exponent for normalization
	mov	edx,eax
	mov	eax,edi			;Shift left by 32 bits
	not	cl			;Convert bit no. to shift count
	shld	edx,eax,cl		;Normalize remainder
	shl	eax,cl
	jmp	ExitTrigPrem

TinyTrig:
;Upper 64 bits of remainder are all zero.  We are assured that the extended
;remainder is never zero, though.
	mov	edx,edi			;Shift left 64 bits
	bsr	ecx,edi
	lea	ebx,[ebx+ecx-31-64]	;Fix up exponent for normalization
	not	cl			;Convert bit no. to shift count
	shl	edx,cl			;Normalize
	jmp	ExitTrigPrem

;*******************************************************************************

EM_ENTRY eFCOS
eFCOS:
    and		[esp].[OldLongStatus+4],NOT(C2 SHL 16)	;clear C2
	cmp	EMSEG:[edi].bTAG,bTAG_ZERO
	jz	ReturnOne
	call	Trig1Result
;ebx:esi,ecx = reduced argument
;eax = octant
	mov	ch,80H			;Assume negative
	test	al,110B			;Negative in octants 2 - 5
	jpo	@F			;Occurs when 1 of these bits are set
	xor	ch,ch			;Actually positve
@@:
	test	al,011B			;Look for octants 0,3,4,7
	jpo	TakeSine		;Use sine if not
TakeCosine:
	cmp	ecx,TinyAngleExp shl 16	;Is angle really small?
	jl	CosReturnOne		;cos(x) = 1 for tiny x
CosNotTiny:
	mov	edi,offset tCosPoly
;Note that argument needs to be saved in ArgTemp (by EvalPolySetup) in case 
;we were called from eFSINCOS and we'll need the arg for the sine.  Argument
;is not needed for cosine, however (just its square).
	call	EvalPolySetup		;In emftran.asm
	mov	ch,EMSEG:[ArgTemp].bSgn	;Get sign we already figured out
TransUnround:
;The last operation performed a simple round nearest, without setting the 
;C1 status bit if round up occured.  We reverse this last rounding now
;so we can do the user's selected rounding mode.  We also ensure that
;the answer is never exact.
	sub	eax,(1 shl 31)-1	;Sum LSB & sticky bits--CY if round up
	jz	UnroundExact		;Answer looks exact, but it's not
	sbb	esi,0
	sbb	ebx,0
	jns	PolyDropExponent	;We had rounded up exponent too
FinalTransRound:
;A jump through [TransRound] is only valid if the number is known not to
;underflow.  Unmasked underflow requires [RoundMode] be set.
	mov	edx,EMSEG:[TransRound]
	mov	EMSEG:[RoundMode],edx
	call	edx			;Perform user's rounding
RestoreRound:
;Restore rounding vectors
	mov	EMSEG:[ZeroVector],offset SaveResult
	mov	eax,EMSEG:[SavedRoundMode]
	mov	EMSEG:[RoundMode],eax
	ret

UnroundExact:
	inc	eax			;Let's say our answer is a bit small
	jmp	FinalTransRound

PolyDropExponent:
	sub	ecx,1 shl 16		;Decrement exponent
	or	ebx,1 shl 31		;Set MSB
	jmp	FinalTransRound


SinRet:
	ret

SaveTinySin:
;Argument in ebx:esi,ecx is small enough so that sin(x) = x, which happens
;when x - x^3/6 = x [or 1 - x^2/6 = 1].  Note that the infinitely precise
;result is slightly less than the argument.  To get the correct answer for
;any rounding mode, we decrement the argument and set up for rounding.
	mov	eax,-1			;Set up rounding bits
	sub	esi,1
	sbb	ebx,0			;Drop mantissa by one
	js	FinalTransRound		;Still normalized?
;mantissa must have been 800..000H, set it to 0FFF...FFFH and drop exponent
	mov	ebx,eax			;ebx = -1
	sub	ecx,1 shl 16		;Drop exponent by one
	jmp	FinalTransRound


EM_ENTRY eFSIN
eFSIN:
    and		[esp].[OldLongStatus+4],NOT(C2 SHL 16)	;clear C2
	cmp	EMSEG:[edi].bTAG,bTAG_ZERO
	jz	SinRet			;Return zero for zero argument
	call	Trig1Result
	mov	ch,al
	shl	ch,7-2			;Move bit 2 to bit 7 as sign bit
ReducedSine:
;ebx:esi,ecx = reduced argument
;ch = correct sign
;eax = octant
	test	al,011B			;Look for octants 0,3,4,7
	jpo	TakeCosine		;Use cosine if not
TakeSine:
	cmp	ecx,TinyAngleExp shl 16	;Is angle really small?
	jl	SaveTinySin		;sin(x) = x for tiny x

;The polynomial for sine is  sin(x) = x * P(x^2).  However, the degree zero
;coefficient of P() is 1, so  P() = R() + 1, where R() has no degree zero
;term.	Thus  sin(x) = x * [R(x^2) + 1] = x * R(x^2) + x.
;
;What's important here is that adding 1 to R(x^2) can blow away a lot of
;precision just before we do that last multiply by x.  Note that x < pi/4 < 1,
;so that x^2 is often << 1.  The precision is lost when R(x^2) is shifted
;right to align its binary point with 1.0.  This can cause a loss of at
;least 1 bit of precision after the final multiply by x in addition to 
;rounding errors.
;
;To avoid this precision loss, we use the alternate form given above,
;sin(x) = x * R(x^2) + x.  Instead of adding 1.0 and multiplying by x,
;we multiply by x and add x--exactly the same level of difficulty.  But
;the mulitply has all of R(x^2)'s precision available.
;
;Because the polynomial R() has no zero-degree term, we give EvalPoly
;one degree less (so we don't have to add zero as the last term).
;Then we have to multiply once more by x^2 since we left the loop early.

SineNotTiny:
	mov	edi,offset tSinPoly
	call	EvalPolySetup		;In emftran.asm
SineFinish:

ifdef NT386
        mov	edi,YFloatTemp
else
	mov	edi,offset edata:FloatTemp
endif
	call	PolyMulDouble		;Last coefficient in R(x^2)

ifdef NT386
	mov	edi,YArgTemp		;Point to original x
else
	mov	edi,offset edata:ArgTemp ;Point to original x
endif

	call	PolyMulDouble		;Compute x * R(x^2)

ifdef NT386
	mov	edi,YArgTemp		;Point to original x
else
	mov	edi,offset edata:ArgTemp ;Point to original x
endif

	push	offset TransUnround
	jmp	PolyAddDouble		;Compute x * R(x^2) + x


EM_ENTRY eFPTAN
eFPTAN:
    and		[esp].[OldLongStatus+4],NOT(C2 SHL 16)	;clear C2
	call	Trig2Result
	push	offset TanPushOne	; Push 1.0 when we're all done
;ebx:esi,ecx = reduced argument
;eax = octant
	mov	ch,al
	shl	ch,7-1			;Move bit 1 to bit 7 as sign bit
;Note that ch bit 6 now has even/odd octant, which we'll need when we're
;done to see if we should take reciprocal.
	cmp	ecx,TinyAngleExp shl 16	;Is angle really small?
	jl	TinyTan
	mov	edi,offset tTanPoly
	call	Eval2Poly		;In emftran.asm
	mov	edi,EMSEG:[CURstk]	;Point to first result
	push	offset TransUnround	;Return address of divide
	test	EMSEG:[ArgTemp].bSgn,0C0H	;Check low 2 bits of octant
;Given the reduced input range, the result can never overflow or underflow.
;It is must then be safe to assume neither operand is zero.
	jpe	DivDouble		;Tan() octants 0,3,4,7
	jmp	DivrDouble		;CoTan()

TinyTan:
	test	ch,0C0H			;Check low 2 bits of octant
	jpe	SaveTinySin		;Octants 0,3,4,7: tan(x) = x for tiny x
;Need reciprocal of reduced argument
	mov	edi,esi
	mov	esi,ebx			;Mantissa in esi:edi
	mov	ebx,ecx			;ExpSgn to ebx
	mov	edx,1 shl 31		;Load 1.0
	xor	eax,eax
.erre	TexpBias eq 0
	xor	ecx,ecx			;Sign and exponent are zero
;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
	push	offset TransUnround	;Return address of divide
;Note that this can never overflow, because the reduced argument is never
;smaller than about 2^-65.
	jmp	DivDoubleReg


PrevStackWrap	edi,Tan			;Tied to PrevStackElem below

TanPushOne:
	PrevStackElem	edi,Tan		;edi points to second result location
	mov	EMSEG:[CURstk],edi
ReturnOne:
	mov	EMSEG:[edi].lManLo,0
	mov	EMSEG:[edi].lManHi,1 shl 31
	mov	EMSEG:[edi].ExpSgn,(0-TexpBias) shl 16 + bTAG_SNGL
	ret


PrevStackWrap	edi,SinCos		;Tied to PrevStackElem below

eFSINCOS:
    and		[esp].[OldLongStatus+4],NOT(C2 SHL 16)	;clear C2
	call	Trig2Result
;Figure out signs
	mov	ch,al			;Start with sign of sine
	shl	ch,7-2			;Move bit 2 to bit 7 as sign bit
	mov	ah,80H			;Assume sign of cosine is negative
	test	al,110B			;Negative in octants 2 - 5
	jpo	@F			;Occurs when 1 of these bits are set
	xor	ah,ah			;Actually positve
@@:
;ch = sign of sine
;ah = sign of cosine
	cmp	ecx,TinyAngleExp shl 16	;Is angle really small?
	jl	TinySinCos
	push	eax			;Save octant and sign of cosine
	call	ReducedSine		;On exit, edi = [CURstk]
	pop	eax
;The Sin() funcion restored the rounding vectors to normal.  Set them back.
	mov	EMSEG:[RoundMode],offset PolyRound
	mov	EMSEG:[ZeroVector],offset PolyZero
	PrevStackElem	edi,SinCos	;edi points to second result location
	mov	EMSEG:[CURstk],edi
	mov	EMSEG:[Result],edi
;Load x^2 back into registers
	mov	ecx,EMSEG:[FloatTemp].ExpSgn
	mov	ebx,EMSEG:[FloatTemp].lManHi
	mov	esi,EMSEG:[FloatTemp].lManLo
	mov	EMSEG:[ArgTemp].bSgn,ah	;Save sign
	test	al,011B			;Look for octants 0,3,4,7
	jpo	FastSine		;Use sine if not
	mov	edi,offset tCosPoly
	call	EvalPoly		;In emftran.asm
	mov	ch,EMSEG:[ArgTemp].bSgn	;Get sign we already figured out
	jmp	TransUnround

FastSine:
	mov	edi,offset tSinPoly
	push	offset SineFinish
	jmp	EvalPoly		;In emftran.asm

TinySinCos:
;ch = sign of sine
;ah = sign of cosine
;ebx:esi,high ecx = reduced argument
;edi = [CURstk]
	test	al,011B			;Look for octants 0,3,4,7
	jpo	TinyCosSin		;Take cosine first if not
	push	eax
	call	SaveTinySin		;For sine, arg is result
	pop	ecx
;edi = [CURstk]
;ch = sign of cosine
;Set cosine to 1.0
	PrevStackElem	edi,TinySinCos	;edi points to second result location
	mov	EMSEG:[CURstk],edi
	mov	EMSEG:[Result],edi
CosReturnOne:
;Cosine is nearly equal to 1.0.  Put in next smaller value and round it.
	mov	ebx,-1
	mov	esi,ebx			;Set mantissa to -1
	mov	eax,ebx			;Set up rounding bits
.erre	TexpBias eq 0
	and	ecx,bSign shl 8		;Keep only sign
	sub	ecx,1 shl 16		;Exponent of -1
;A jump through [TransRound] is only valid if the number is known not to
;underflow.  Unmasked underflow requires [RoundMode] be set.
	jmp	EMSEG:[TransRound]

	PrevStackWrap	edi,TinySinCos

	PrevStackWrap	edi,TinyCosSin

TinyCosSin:
;Sine is nearly 1.0, cosine is argument
;
;ch = sign of sine
;ah = sign of cosine
;ebx:esi,high ecx = reduced argument
;edi = [CURstk]
	xchg	ah,ch			;Cosine sign to ch, sine sign to ah
	push	edi			;Save place for sine
	PrevStackElem	edi,TinyCosSin	;edi points to second result location
	mov	EMSEG:[CURstk],edi
	mov	EMSEG:[Result],edi
	push	eax
	call	SaveTinySin		;For sine, arg is result
	pop	ecx
;ch = sign of sine
	pop	EMSEG:[Result]		;Set up location for sine
	jmp	CosReturnOne

;*******************************************************************************

;********************* 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.


tCosPoly	label	word

;These constants are derived from Hart #3824: cos(x) = P(x^2),
;accurate to 19.45 digits over interval [0, pi/4].  The original 
;constants in Hart required that the argument x be divided by pi/4.  
;These constants have been scaled so this is no longer required.
;Scaling is done by multiplying the constant by a power of 4/pi.
;The power is given in the table.

	dd	7			;Degree seven

;  Original Hart constant	      power	Scaled constant
;
;-0.38577 62037 2		 E-12  14  -0.113521232057839395845871741043E-10
;Hex value:    0.C7B56AF786699CF1BD13FD290 HFFDC
	dq	0C7B56AF786699CF2H
	dw	(bSign shl 8)+bTAG_VALID,0FFDCH-1

;+0.11500 49702 4263		  E-9  12  +0.208755551456778828747793797596E-8
;Hex value:    0.8F74AA3CCE49E68D6F5444A18 HFFE4
	dq	08F74AA3CCE49E68DH
	dw	bTAG_VALID,0FFE4H-1

;-0.24611 36382 63700 5		  E-7  10  -0.275573128656960822243472872247E-6
;Hex value:    0.93F27B7F10CC8A1703EFC8A04 HFFEB
	dq	093F27B7F10CC8A17H
	dw	(bSign shl 8)+bTAG_VALID,0FFEBH-1

;+0.35908 60445 88581 953	  E-5	8  +0.248015872828994630247806807317E-4
;Hex value:    0.D00D00CD6BB3ECD17E10D5830 HFFF1
	dq	0D00D00CD6BB3ECD1H
	dw	bTAG_VALID,0FFF1H-1

;-0.32599 18869 26687 55044	  E-3	6  -0.138888888888589604343951947246E-2
;Hex value:    0.B60B60B609B165894CFE522AC HFFF7
	dq	0B60B60B609B16589H
	dw	(bSign shl 8)+bTAG_VALID,0FFF7H-1

;+0.15854 34424 38154 10897 54	  E-1	4  +0.416666666666664302573692446873E-1
;Hex value:    0.AAAAAAAAAAA99A1AF53042B08 HFFFC
	dq	0AAAAAAAAAAA99A1BH
	dw	bTAG_VALID,0FFFCH-1

;-0.30842 51375 34042 45242 414	  E0	2  -0.499999999999999992843582920899E0
;Hex value:    0.FFFFFFFFFFFFFEF7F98D3BFA8 HFFFF
	dq	0FFFFFFFFFFFFFEF8H
	dw	(bSign shl 8)+bTAG_VALID,0FFFFH-1

;+0.99999 99999 99999 99996 415	  E0	0  (no change)
;Hex value     0.FFFFFFFFFFFFFFFF56B402618 H0
	dq	0FFFFFFFFFFFFFFFFH
	dw	bTAG_VALID,00H-1


tSinPoly	label	word

;These constants are derived from Hart #3044: sin(x) = x * P(x^2),
;accurate to 20.73 digits over interval [0, pi/4].  The original 
;constants in Hart required that the argument x be divided by pi/4.  
;These constants have been scaled so this is no longer required.
;Scaling is done by multiplying the constant by a power of 4/pi.
;The power is given in the table.

	dd	7-1			;Degree seven, but the last coefficient
					;is 1.0 and is not listed here.

;  Original Hart constant	      power	Scaled constant
;
;-0.20225 31292 93		 E-13  15  -0.757786788401271156262125540409E-12
;Hex value:    0.D54C4AF2B524F0F2D6411C90A HFFD8
	dq	0D54C4AF2B524F0F3H
	dw	(bSign shl 8)+bTAG_VALID,0FFD8H-1

;+0.69481 52035 0522		 E-11  13  +0.160583476232246065559545749398E-9
;Hex value:    0.B0903AF085DA66030F16E43BC HFFE0
	dq	0B0903AF085DA6603H
	dw	bTAG_VALID,0FFE0H-1

;-0.17572 47417 61708 06	  E-8  11  -0.250521047382673309542092418731E-7
;Hex value:    0.D73229320D2AF05971AC96FF4 HFFE7
	dq	0D73229320D2AF059H
	dw	(bSign shl 8)+bTAG_VALID,0FFE7H-1

;+0.31336 16889 17325 348	  E-6	9  +0.275573192133901687156480447942E-5
;Hex value:    0.B8EF1D2984D2FBA28A9CC9DEE HFFEE
	dq	0B8EF1D2984D2FBA3H
	dw	bTAG_VALID,0FFEEH-1

;-0.36576 20418 21464 00052 9	  E-4	7  -0.198412698412531058609618529749E-3
;Hex value:    0.D00D00D00C3FDDD7916E5CB28 HFFF4
	dq	0D00D00D00C3FDDD8H
	dw	(bSign shl 8)+bTAG_VALID,0FFF4H-1

;+0.24903 94570 19271 62752 519	  E-2	5  +0.83333333333333203341753387264E-2
;Hex value:    0.8888888888884C95D619A0343 HFFFA
	dq	08888888888884C96H
	dw	bTAG_VALID,0FFFAH-1

;-0.80745 51218 82807 81520 2582  E-1	3  -0.166666666666666666281276062229E0
;Hex value:    0.AAAAAAAAAAAAAA8E3AD80EAB8 HFFFE
	dq	0AAAAAAAAAAAAAA8EH
	dw	(bSign shl 8)+bTAG_VALID,0FFFEH-1

;+0.78539 81633 97448 30961 41845 E0	1  +0.99999999999999999999812025812E0
;Hex value:    0.FFFFFFFFFFFFFFFFF71F88110 H0
;	dq	8000000000000000H	;This constant of 1.0 omitted here.
;	dw	bTAG_VALID,0		;   It is handled in code.


tTanPoly	label	word

;These constants are derived from Hart #4286: tan(x) = x * P(x^2) / Q(x^2),
;accurate to 19.94 digits over interval [0, pi/4].  The original 
;constants in Hart required that the argument x be divided by pi/4.  
;These constants have been scaled so this is no longer required.
;Scaling is done by multiplying the constant by the same power of 4/pi
;as the power of x the constant is used on.  However, the highest
;degree coefficient of Q() is 1, and after scaling this way it would
;become (4/pi)^8.  In order to keep this coefficient equal to one,
;we scale everything again by (pi/4)^8.  This scaling is partially
;canceled by the original scaling by powers of 4/pi, and the net
;resulting power of pi/4 is given in the table.


	dd	3			;First poly is degree 3

;  Original Hart constant	        power	Scaled constant
;
;-.45649 31943 86656 31873 96113 7    E2  1  -35.8528916474714232910463077546
;Hex value:    0.8F695C6D93AF6F97B6E022AB3 H6
        dq      08F695C6D93AF6F98H
        dw      (bSign shl 8)+bTAG_VALID,06H-1

;+.14189 85425 27617 78388 00394 831  E5  3  +6874.60229709782436592720603503
;Hex value:    0.D6D4D181240D0D08C88DF4AA6 HD
        dq      0D6D4D181240D0D09H
        dw      bTAG_VALID,0DH-1

;-.89588 84400 67680 41087 29639 541  E6  5  -267733.884797157298951145495276
;Hex value:    0.82BABC504220C62B1D0722684 H13
        dq      082BABC504220C62BH
        dw      (bSign shl 8)+bTAG_VALID,013H-1

;+.10888 60043 72816 87521 38857 983  E8  7  +2007248.9111748838841548144685
;Hex value:    0.F506874A160EB9C0994AADD6A H15
        dq      0F506874A160EB9C1H
        dw      bTAG_VALID,015H-1



	dd	4			;Second poly is degree 4
;NOTE: Eval2Poly assumes the first coefficient is 1.0, so it is omitted

;  Original Hart constant	        power	Scaled constant
;
;-.10146 56190 25288 53387 54401 947  E4  2  -625.890950057027419879480354834
;Hex value:    0.9C790553635355A95241A5324 HA
        dq      09C790553635355A9H
        dw      (bSign shl 8)+bTAG_VALID,0AH-1

;+.13538 27128 05119 09382 89294 872  E6  4  +51513.6992033752080924797647367
;Hex value:    0.C939B2FEFE0DC585E649870FE H10
        dq      0C939B2FEFE0DC586H
        dw      bTAG_VALID,010H-1

;-.39913 09518 03516 51504 43427 94   E7  6  -936816.855188785264866481436899
;Hex value:    0.E4B70DAEDA6F89E5A7CE626FA H14
        dq      0E4B70DAEDA6F89E6H
        dw      (bSign shl 8)+bTAG_VALID,014H-1

;+.13863 79666 35676 29165 33913 361  E8  8  +2007248.91117488388417770850458
;Hex value:    0.F506874A160EB9C0CCD8313BC H15
        dq      0F506874A160EB9C1H
        dw      bTAG_VALID,015H-1