	.file "exp.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: exp.s 1.5 90/06/28 $
 *
 *		EXPONENTIAL FUNCTION
 *
 * Functional Description:
 *
 *	This routine implements the exponenetial function for the C language.
 *	It uses the implicit f2xm1 instruction of the 287 NPX. No error checking
 *	is performed since it is assumed that the 287 will correctly handle
 *	an illegal argument.
 *
 * Input:	Argument is located at 4(%esp)
 *
 * Output:	Double Precision value is returned in ST(0)
 *
 * Method:
 *		k = NINT(arg/ln2)
 *		arg = |arg|
 *		IF (arg > 0) 
 *		   exp(arg) = 2 ** (log2(e) * arg - (k * ln2))
 *		ELSE
 *		   exp(arg) = 1/(2 ** (log2(e) * arg - (k * ln2)))
 *		ENDIF
 *		   
 *
 * Created:
 *	05/23/86	MF - 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.
 *
 *
 */

#include "sys.s"

ONED0:	.long	0, 0x3ff00000		/ 1.0d0

STR1:
	.byte	0x65,0x78,0x70,0x00	/ "exp\0"
	.align 2

ENTRY(exp)
	fldl	4(%esp)			/load argument
	fld	%st(0)			/duplicate x
	fabs
	fldl	libm_maxexp		/load in maximum range
	fcompp				/Compare max with |arg|
	fstsw	%ax			/Save the result of the test
	sahf				/Set the flags
	jc	exp_rng_err		/if arg > maximum, error
	fldl2e				/ Load log2(e)
	fmulp   %st,%st(1)		/ Multiply by value
	fstl	4(%esp)			/ Store modified argument
	movl	8(%esp), %eax		/ Load modified argument
	andl	$ 0x7fffffff, %eax	/ Mask off sign
	cmpl	$ 0x3fe00000, %eax	/ Is |X'| > 0.5?
	jg	exp_big			/ Yes.  The hard way
	f2xm1				/ No. Compute EXP(X)-1.0
	faddl	ONED0			/ Now EXP(X)
	ret
exp_big:
	fld	%st(0)			/ Duplicate X'
	frndint				/ Get nearest integer
	fxch	%st(1)			/ Swap st(1) with st(0)
	fsub	%st(1), %st		/ Compute -0.5 <= X'' <= 0.5
	f2xm1				/ Compute EXP(X'')-1.0
	faddl	ONED0			/ Now EXP(X'')
	fscale				/ Now EXP(X)
err_return:
	fstp	%st(1)			/ pop stack
	ret

exp_rng_err:
	ftst				/ is x > 0.0
	fstsw	%ax			/Save the result of the test
	sahf				/Set the flags
	jc	exp_sml_err		/ x is < 0.0

	subl	$0x20,%esp
	fstpl	0x8(%esp)		/ exc.arg1 = x
	movl	$0x3,(%esp)		/ exc.type = OVERFLOW
	movl	$STR1,0x4(%esp)		/ exc.name = "exp"
	fldl	_maxdouble		/ exc.retval = HUGE_VAL
	fstpl	0x18(%esp)
	leal	(%esp),%eax		/ call matherr(&exc)
	pushl	%eax
	call	matherr
	popl	%ecx
	testl	%eax,%eax		/ if (!matherr(&exc))
	jne	big_exit
	movl	$0x22,errno		/ errno = ERANGE

big_exit:
	fldl	0x18(%esp)
	addl	$0x20,%esp
	ret

exp_sml_err:
	subl	$0x20,%esp
	fstpl	0x8(%esp)		/ exc.arg1 = x
	movl	$0x4,(%esp)		/ exc.type = UNDERFLOW
	movl	$STR1,0x4(%esp)		/ exc.name = "exp"
	fldz				/ exc.retval = 0.0
	fstpl	0x18(%esp)
	leal	(%esp),%eax		/ call matherr(&exc)
	pushl	%eax
	call	matherr
	popl	%ecx
	testl	%eax,%eax		/ if (!matherr(&exc))
	jne	sml_exit
	movl	$0x22,errno		/ errno = ERANGE

sml_exit:
	fldl	0x18(%esp)
	addl	$0x20,%esp
	ret
