	.file "sinhcosh.s"
/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*
 *
 *	$Header: sinhcosh.s 1.6 90/06/28 $
 *
 *		SINH & COSH FUNCTIONS
 *
 * Functional Description:
 *
 *	This routine implements the hyperbolic sine and cosine function for
 *	the C language. Error checking is performed for overflow errors.
 *
 *
 * Input:	Argument is located at 4(%esp)
 *	        Assumed that the argument is normal.
 *		A check is done to be sure that it will not overflow.
 *
 * Output:	Double Precision value is returned in ST(0)
 *
 * Assumptions:  ST(0) & ST(1) are scratch stack registers
 *		Input argument is normal.
 *		Registers ax,cx, and dx are scratch registers.
 *
 * Created:
 *	05/29/86	GS - Quantitative Technology Corporation for
 *				Sequent Computer Systems
 *	09/04/86	Phil Hochstetler - converted to C library conventions
 *
 *	05/22/87	Gary Tracy - converted to stack model C interface
 *			  under this model, the caller is responsible for
 *			  saving and restoring any required 387 state.
 *			  this need not be done if:
 *				1.  the callee does no calls.
 *				2.  the sum of 387 registers used by the caller
 *				    and callee does not exceed 8.
 *
 *	08/08/88	Dorsey Drane - check for small values of sinh(x) and
 *			use a polynomial for computing sinh(x) for x < 0.5.
 *			(from 02/23/88 Gary Graunke's fix to plib/dsinhcosh.s)
 *
 * Method:
 *	Cosh:
 *
 *	1. Replace x by |x|
 *
 *	2. For 2^-27 <= x <= ln2/2 : cosh(x) =      [exp(x) - 1]^2
 *					   1 + -------------------
 *						2 * exp(x)
 *
 *          For ln2/2 < x <  ovfl : cosh(x) = exp(x) + 1/exp(x)
 *					     -----------------
 *						     2
 *
 *	3. for x < 2^-27 : cosh(x) = 1
 *
 *	Sinh:
 *
 *	1. Replace x = |x| since sinh(-x) = - sinh(x)
 *
 *	2. 0.75 <=  x <= lnovfl	: sinh(-x) = (exp(x)-1) + (exp(x)-1)/exp(x)
 *					     ------------------------------
 *							   2
 *	3. x < 2^-10 : sinh(x) = polynomial(x)
 */

#include "sys.s"

cmpval:	.double 0Dx3fe8000000000000	/ 0.75
sinh_a:	.double	0Dx3de6473c809eda4b	/ 0.000000000162095726
sinh_b:	.double	0Dx3e5ae5f3caf0d8a6	/ 0.000000025050948590
sinh_c:	.double	0Dx3ec71de3e3996da7	/ 0.000002755732364785
sinh_d:	.double	0Dx3f2a01a019d0eea7	/ 0.000198412698326202
sinh_e:	.double	0Dx3f8111111111232b	/ 0.008333333333341372
sinh_f:	.double	0Dx3fc555555555554b	/ 0.166666666666666387
cosh_a:	.double	0Dx3e22203b8e37b8d4	/ 0.000000002110133500
cosh_b:	.double 0Dx3e927e04236f2b8d	/ 0.000000275556007731
cosh_c:	.double	0Dx3efa01a08c597eb1	/ 0.000024801593801248
cosh_d:	.double	0Dx3f56c16c1668ecc9	/ 0.001388888887631267
cosh_e:	.double 0Dx3fa5555555559647	/ 0.041666666666782032
cosh_f:	.double	0Dx3fdfffffffffffb8	/ 0.499999999999996056
	.set	maskC0,0x0100

SINHERR:
	.byte	0x73,0x69,0x6e,0x68,0x00

COSHERR:
	.byte 0x63,0x6f,0x73,0x68,0x00
	.data
	.align 2

ENTRY(cosh)
	movb	$1,%cl			/ Set the operation flag to cosh
	fldl	4(%esp)			/ Put argument onto stack
	fld	%st(0)			/ replicat x
	fabs				/ take |x|
	fldl	cmpval
	fcompp				/ see if |x| < 0.75
	fstsw	%ax			/ load ax with results of compare
	sahf				/ set flags
	jc	sinhcosh
	fmul	%st,%st(0)		/ square to get x^2
	fldl	cosh_a			/ evaluate polynomial in x^2
	fmul	%st(1),%st
	faddl	cosh_b
	fmul	%st(1),%st
	faddl	cosh_c
	fmul	%st(1),%st
	faddl	cosh_d
	fmul	%st(1),%st
	faddl	cosh_e
	fmul	%st(1),%st
	faddl	cosh_f
	fmulp	%st,%st(1)		/ last mulitply by x^2
	fld1				/ add 1.0
	faddp	%st,%st(1)
	ret

ENTRY(sinh)
	movb	$0,%cl			/ Set the operation flag to sinh
	fldl	4(%esp)			/ Put argument onto stack
	fld	%st(0)			/ replicat x
	fabs				/ take |x|
	fldl	cmpval
	fcompp				/ see if |x| < 0.75
	fstsw	%ax			/ load ax with results of compare
	sahf				/ set flags
	jc	sinhcosh
	fld	%st(0)			/ replicate x
	fmul	%st,%st(0)		/ square to get x^2
	fldl	sinh_a			/ evaluate polynomial in x^2
	fmul	%st(1),%st
	faddl	sinh_b
	fmul	%st(1),%st
	faddl	sinh_c
	fmul	%st(1),%st
	faddl	sinh_d
	fmul	%st(1),%st
	faddl	sinh_e
	fmul	%st(1),%st
	faddl	sinh_f
	fmulp	%st,%st(1)		/ last mulitply by x^2
	fmul	%st(1),%st		/ poly * x
	faddp	%st,%st(1)		/ add in 1.0 * x
	ret				/ done--st(0) has answer--rest clean
sinhcosh:
	fldz				/ Put zero on stack for space
	fld	%st(1)			/ copy argument on the stack
	fldl	libm_maxsinh		/ Load maximum range for argument

	subl	$4, %esp		/set up temperary space

	fcomp	%st(1)			/ Check the argument's range
	fstsw	%ax			/ Store the status word
	sahf
	jnc	cont_sinhcosh		/ If argument <= libm_maxsinh continue
/*
 *    Error code for argument too big /
 */
got_err:
	fstp	%st(0)			/ clear stack
	fstp	%st(0)
	subl	$0x1c,%esp		/ allocate struct exception exc
	fstpl	0x8(%esp)		/ exc.arg1 = x
	fldl	_maxdouble		/exc.retval = HUGE_VAL
	fstpl	0x18(%esp)
	movl	$0x3,(%esp)		/exc.type = OVERFLOW
	/testw	$0x1,%cl		/ if sinh jmp
	.byte	0x66,0xf7,0xc1,0x01,00
	jz	sinh_err
	movl	$COSHERR,0x4(%esp)	/ exc.name = "cosh"
	jmp	cont_err
sinh_err:
	movl	$SINHERR,0x4(%esp)	/ exc.name = "sinh"
	fldl	0x8(%esp)		/ put x on stack
	ftst				/ check for x < 0
	fstsw	%ax
	sahf
	jnc	pos_err			/ x is positive
	fldl	0x18(%esp)		/ load HUGE_VAL
	fchs				/ make -HUGE_VAL
	fstpl	0x18(%esp)		/ exc.retval = -HUGE_VAL
pos_err:
	fstp	%st(0)			/ clear stack
cont_err:
	leal	(%esp),%eax		/ call matherr
	pushl	%eax
	call	matherr
	popl	%ecx
	testl	%eax,%eax
	jne	err_exit
	movl	$0x22,errno		/ errno = ERANGE
err_exit:
	fldl	0x18(%esp)		/ return exc.retval
	addl	$0x20,%esp		/ deallocat exc
	ret

/*
 *    This would be the place to add a funny parameter test
 */

cont_sinhcosh:
	fabs				/ x = |x|
	fldln2
	fxch	%st(1)
/*
 *    Beginning of exp for hypers
 *	Registers
 *		st(0) = arg
 *		st(1) = ln2
 *		st(2) = open for use.
 *	For restoration st = original st(1)
 */
	jmp	exph			/call exp - assume exp(x) is in st(0)

/*
 *    End of exp for hypers
 *	Registers
 *	st(0) = exp(arg)
 *		For restoration st = st(3)
 */
expdone:
	fld	%st(0)
	fld	%st(0)
	fld	%st(3)
	/testw	$1,%cl			/ If the operation is sinh then
	.byte	0x66,0xf7,0xc1,0x01,0x00
	jz	calc_sinh		/    calculate sinh
					/ Else
					/    calculate cosh
cont_cosh:
	fabs				/ x = |x|

	fxch	%st(1)
	fstp	%st(0)			/ Pop to balance the load of the stack
	fldl	libm_log_tot
	fxch	%st(1)

	/ Stack Registers have the following values
	/ st(0) = x
	/ st(1) = ln2/2
	/ st(2) = exp(x)

	fcom				/compare |x| and ln2/2
	fstsw	%ax			/store flags from compare
	fxch	%st(2)
	fxch	%st(1)
	fstp	%st(0)			/ Pop to balance the stack load
	fld1
	fxch	%st(1)

	sahf				/ If (x < ln2/2) then
					/     use function 0
	jc	function0		/ Else
					/     use function 1

	/ Stack Registers have the following values
	/ st(0) = exp(operand)
	/ st(1) = 1
	/ st(2) = x



function1:	/ for x >= ln2/2
		/	where cosh(x) = exp(x) + 1/exp(x)
		/	                ------------------
		/		           2 * exp(x)

	fdivr	%st,%st(1)		/ st(1) = 1/exp(x)
	fadd	%st,%st(1)		/ st(1) = exp(x) + 1/exp(x)
	fstp	%st(0)			/ Pop to balance the stack load
	fld1				/ st(0)	= 1
	fst	%st(2)			/ st(2) = 1
	fadd	%st(2),%st		/ st(0) = 1 + 1 = 2
	fdivr	%st(1),%st		/ st(0) = (exp(x) + 1/exp(x))/2

        jmp sinhcosh_return

function0:	/ for 0 <= x < ln2/2
		/	where cosh(x) =       (exp(x) - 1)^2
		/			1 + ------------------
		/			      2 * exp(x)

	fst	%st(2)			/ st(2) = exp(x)
	fsub	%st(1),%st		/ st(0) = exp(x) - 1
	fmul	%st,%st(0)		/ st(0) = (exp(x) - 1)^2
	fxch	%st(2)			/ st(0) = exp(x), st(2)=(exp(x) - 1)^2
	fadd	%st,%st(0)		/ st(0) = 2 * exp(x)
	fdivr	%st(2),%st		/ st(0) = ((exp(x) - 1)^2)/ (2 *exp(x))
	fadd	%st(1),%st		/ Add a 1 so that st(0) = cosh(x)

	jmp sinhcosh_return

calc_sinh:
	/ Registers have the following values
	/ st(0) = x
	/ st(1) = ?
	/ st(2) = exp(x)

	ftst				/ Compare the opperand to zero
	fstsw	%ax
	sahf
	jc	neg_opperand		/   If x > 0 then
	movb	$0,%cl			/       Set the sign flag = positive
	jmp	cont_sinh

neg_opperand:				/   Else
	movb	$1,%cl			/       Set the sign flag = negative

cont_sinh:

	/ Registers have the following values
	/ st(0) = x
	/ st(1) = ?
	/ st(2) = exp(x)
	/ cl    = sign flag (1 = neg, 0 = pos)

	fstp	%st(0)			/ Pop to balance the stack load
	fld1				/ ST(0) = 1
	fst	%st(1)			/ ST(1) = 1
	fsubr	%st(2),%st		/ st(0) = exp(x) - 1
	fdiv	%st,%st(2)		/ ST(2) = (exp(x) - 1)/exp(x)
	fadd	%st,%st(2)		/ ST(2) = (exp(x)-1)/exp(x) + (exp(x)-1)
	fstp	%st(0)			/ Pop to balance the stack load
	fld1				/ Form 2 by adding 1's
	fadd	%st(1),%st		/ ST(0) = 1 + 1
	fdivr	%st(2),%st		/ ST =(((exp(x)-1)/exp(x))+(exp(x)-1))/2

		/ Correct the sign of the outcome

	/btw	$0,%cl			/ If opperand sign = pos then
	.byte	0x66,0x0f,0xba,0xe1,0x00
	jnc	sinhcosh_return		/       Return
	fchs				/ Else   change the sign

	        / clear the stack, and return the answer
sinhcosh_return:
	fstp	%st(1)			/Pop and move answer down
	fstp	%st(1)			/Pop and move answer down
	fstp	%st(1)			/Pop and move answer down
	fstp	%st(1)			/Pop and move answer down
	addl	$4, %esp
	ret

/*
 *    Beginning of exp for hypers
 *	Registers
 *		st(0) = arg
 *		st(1) = ln2
 *		st(2) = open for use.
 */

exph:	fldl	libm_maxsinh		/load in maximum range
	fcomp	%st(1)			/Compare maximum with argument
	fstsw	%ax			/Save the result of the test
	sahf				/Set the flags
	jc	big_err			/if argument > maximum, issue error

	fldl	libm_minexp		/load in minimum range
	fcomp	%st(1)			/Compare minimum with argument
	fstsw	%ax			/Save the result of the test
	sahf				/Set the flags
	jc	cont_calc		/if argument > minimum, continue
	jz	cont_calc		/if argument = minimum, continue
	fstp	%st(0)			/point to st(2)
	fstp	%st(0)			/point to st(3)
	fstp	%st(0)			/point to st(4)
	fldz				/return a 0.0 in st(3)
	jmp	sinhcosh_return		/go return
/*
 *    Error code for overflow argument
 */
big_err:
	fstp	%st(0)
	jmp	got_err

cont_calc:
	fst	%st(2)			/save arg in st2
	fdiv	%st(1),%st		/form arg/ln2
	fstcw	(%esp)			/get control word
	fwait				/wait until it is memory
	movw	(%esp),%ax		/put it into eax and
	andw	$0xf3ff,%ax		/ set rounding mode to nearest
	orw	$0x0300,%ax		/ set precision to 64 bits
	movw	%ax,2(%esp)		/move new control word to memory and
	fldcw	2(%esp)			/ store it back
	fwait				/wait until control word is stored
	frndint				/form k = NINT(arg/ln2)
	fwait				/wait until store is complete
	fxch	%st(1)			/switch k and ln2
	fmul	%st(1),%st		/form k * ln2
	fsubr	%st(2),%st		/form x - (k * ln2)
	fldl2e				/load LOG2e
	fmul				/form LOG2e * (x - (k*ln2))
	ftst				/check if value < 0
	fstsw	%eax			/get status word from test
	fabs				/form absolute value
	f2xm1				/form (2 ** (LOG2e * x - (k*ln2))) - 1.0
	fld1				/put 1.0 onto stack
	fxch	%st(1)			/switch 1.0 and result of exp
	fadd	%st(1),%st		/add back in the 1.0 to form Y
	testw	$maskC0,%eax		/was value < 0?
	je	no_invert		/if not, don't invert result
	/fdivr	%st(1),%st(0)		/if so, invert result (1.0/result)
	.byte	0xd8,0xf9
no_invert:
	fxch	%st(3)			/switch arg and Y
	fstp	%st(0)			/pop arg
	fscale				/scale 1.0 by k
	fxch	%st(1)			/switch k and 2 ** k
	fstp	%st(0)			/pop k
	fmul				/form result = Y * 2 ** k
	fldcw	(%esp)			/restore the control word
/*
 *    End of exp for hypers
 *	Registers
 *	    st(0) = exp(arg)
 *	    For restoration st = st(3)
 */
	jmp	expdone
