	.file "pow.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: pow.s 1.5 90/06/28 $
 *	
 *	POWER FUNCTION
 *
 * Functional Description:
 *
 *	This routine implements the power function for the C language.
 *	It uses borrowed code from the exp routine to calculate the
 *	power and to avoid the rounding error of writing the parameter to memory
 *	to implement a call instruction.  A call is made to log to calculate
 *	the natural log.  Little error checking for NAN's or unnormals
 *	is performed since it is assumed that the 287 will correctly handle
 *	these illegal argument.
 *
 * Method:
 *	IF base == 0
 *	   IF power <= 0
 *	      error(0^-x)
 *	   ELSE
 *	      return(0)
 *	   ENDIF
 *	ELSE
 *	   IF power is an integer
 *	      bits = int(power)
 *	      IF (bits == 0)
 *		    return(1)
 *	      ELSE
 *	         IF (bits is odd)
 *		    result = base
 *		 ELSE
 *		    result = 1
 *		 ENDIF
 *	         WHILE (bits >>= 1) DO
 *		    base = base * base
 *		    IF (bits is odd)
 *		       result = result * base
 *		    ENDIF
 *		 ENDDO
 *	         IF (power < 0)
 *		    result = 1/result
 *		    return(result)
 *	      ENDIF
 *          ELSE
 *              IF base > 0 then
 *                 result = exp(ln(base)*power)
 *                 return(result)
 *              ELSE
 *                 Error(-x^real)
 *	       ENDIF
 *	   ENDIF
 *
 *
 * Input:	Base is located at 4(%esp)
 *		Power is located at 12(%esp)
 *
 * Output:	Double Precision value is returned in ST(0)
 *
 * Created:
 *	06/04/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.
 *
 */

#include "sys.s"

	.set	maskC0,0x0100		/mask for C0
	.set	maskC0C3,0x4100		/mask for C0 and C3 in status word
	.set	mask_last_bit,0x0001	/mask off all but last bit
	.set	maskC1,0x0200		/mask off C1

POWERR1:
	.byte	0x70,0x6f,0x77,0x00

POWERR2:
	.byte	0x70,0x6f,0x77,0x3a,0x20,0x44,0x4f,0x4d,0x41,0x49
	.byte	0x4e,0x20,0x65,0x72,0x72,0x6f,0x72,0x0a,0x00

HALF:	.double	0Dx3fe0000000000000
	.data
	.align 2

ENTRY(pow)
	fldl	12(%esp)		/put power argument onto stack
	fldl	4(%esp)			/put base argument onto stack

	subl	$8,%esp			/get some temp space

	ftst				/Compare base to 0
	fstsw	%ax			/Save the results of the test
	movw	%ax,%cx			/Save away for later
	sahf				/Set flags for the test
	jnz	pow_cont		/If base != 0 then continue calc

	fxch	%st(1)			/Move power to top of stack
	ftst				/Compare power to 0
	fstsw	%ax			/Save the results of the test
	sahf				/Set flags for the test
	jbe	pow_domain_err		/If power <= 0 then issue error
	fstp	%st(0)			/Remove power
	jmp	pow_restore_stack	/go return a 0
/*
 *    Error code for 0^0 and 0^-x input
 */
pow_domain_err:
	subl	$0x18,%esp
	fstpl	0x10(%esp)		/exc.arg2 = y
	fstpl	0x8(%esp)		/exc.arg1 = x
	movl	$0x1,(%esp)		/exc.type = DOMAIN
	movl	$POWERR1,0x4(%esp)	/exc.name = "pow"
	fldz				/exc.retval = 0.0
	fstpl	0x18(%esp)
	leal	(%esp),%eax
	pushl	%eax
	call	matherr
	popl	%ecx
	testl	%eax,%eax
	jne	pow_domain_exit

	pushl	$0x12
	pushl	$POWERR2
	pushl	$0x2
	call	write
	addl	$0xc,%esp
	movl	$0x21,errno
pow_domain_exit:
	fldl	0x18(%esp)
	addl	$0x20,%esp
	ret

pow_cont:
	fxch	%st(1)			/move power to the top of stack
	fld	%st(0)			/make backup copy of power
	frndint				/st(0) = rnd(power)
	fcomp				/Compare rnd(power) and power
	fstsw	%ax			/load ax with result of compare
	sahf				/set flags
	jz	pow_for_int		/If power=rnd(power) then use spec. rout
pow_for_real:
	movw	%cx,%ax			/Get the results from ftst(base)
	sahf				/Set the flags
	jnc	pow_setup_for_log	/If base>0 then use exp(ln(base)*power)

/*
 *    Error code for -x^real  = negative root error /
 */
	jmp	pow_domain_err

/*
 *    Use this algo    x^y = exp(ln(x)*y)
 */
pow_setup_for_log:
	fstp	%st(0)			/pop and put base in tos
	fstpl	(%esp)			/save argument in memory
	call	log
	fldl	12+8(%esp)		/put power argument onto stack
	fldl	4+8(%esp)		/put base argument onto stack
	fxch	%st(1)			/st(1) = ln(base)
	fxch	%st(2)			/st(0) = power
	fmul	%st(2),%st		/st(0) = ln(base) * power

	fldln2				/st(3) = ln2
	fxch	%st(1)			/Place ln(base)*power on top of stack
/*
 *    beginning of exp (ln(base)*power)
 */
	fldl	libm_maxexp		/get maximum value of exp
	fcomp	%st(1)			/compare maximum with ln(base)*power
	fstsw	%ax			/save the result of the test
	sahf				/set the flags
	jc	pow_big_err0		/if GT libm_maxexp, error
	fldl	libm_minexp		/get minimum value of exp
	fcomp	%st(1)			/compare minexp with ln(base)*power
	fstsw	%ax			/save the result of the test
	sahf				/set the flags
	jc	pow_cont_calc		/if GT libm_minexp, continue
	jz	pow_cont_calc		/if EQ libm_minexp, continue
pow_return_zero:
	fstp	%st(0)			/if LT libm_minexp, UNDERFLOW
	fstp	%st(0)			/point to st(4)
pow_return_zero1:
	fldz				/return a 0.0
	subl	$0x18,%esp		/allocate struct exception exc
	movl	$0x4,(%esp)		/exc.type = UNDERFLOW
	fstpl	0x18(%esp)		/exc.retval = 0.0
	jmp	pow_range_err		/go fix report error and return

pow_big_err0:
	fstp	%st(0)
	fstp	%st(0)
	fstp	%st(0)
pow_big_err:
	fstp	%st(0)
	fclex
	fldl	12+8(%esp)		/put power on stack
	fldl	4+8(%esp)		/put base on stack
	subl	$0x18,%esp		/allocate struct exception exc
	fldl	_maxdouble		/exc.retval = HUGE_VAL
	fstpl	0x18(%esp)
	movl	$0x3,(%esp)		/exc.type = OVERFLOW
	fld	%st(0)			/dup base and check for > 0
	fabs
	fcomp	%st(1)			/compare x = |x| to set +- HUGE_VAL
	fstsw	%ax			/load ax with results of compare
	sahf
	je	pow_range_err		/retval stays at HUGE_VAL

	leal	0x10(%esp),%eax		/load address for dum
	pushl	%eax
	fld	%st(1)			/put y*0.5 on stack
	fmull	HALF
	subl	$0x8,%esp		/make room on stack for y*0.5
	fstpl	(%esp)
	call	modf
	addl	$0xc,%esp
	fldz
	fcomp	%st(1)
	fstsw	%ax
	sahf
	je	pow_big_err1
	fcompl	HALF			/ if (modf(y*0.5,&dum) != 0.5)
	fstsw	%ax
	sahf
	jne	pow_big_err2		/ this is a domain error
	fldl	0x18(%esp)		/ yes y is odd so
	fchs				/exc.retval = -HUGE_VAL
	fstpl	0x18(%esp)
	jmp	pow_range_err
pow_big_err1:				/ no y is even exc.retval is ok
	fstp	%st(0)			/ remove results of modf()
	jmp	pow_range_err
pow_big_err2:
	addl	$0x18,%esp		/ setup stack for jmp
	jmp	pow_domain_err

pow_range_err:
	fstpl	0x8(%esp)		/exc.arg1 = x
	fstpl	0x10(%esp)		/exc.arg2 = y
	movl	$POWERR1,0x4(%esp)	/exc.name = "pow"
	leal	(%esp),%eax
	pushl	%eax
	call	matherr
	popl	%ecx
	testl	%eax,%eax
	jne	pow_range_exit
	movl	$0x22,errno		/errno = ERANGE
pow_range_exit:
	fldl	0x18(%esp)
	addl	$0x20,%esp
	ret

pow_cont_calc:
	fst	%st(2)			/save ln(base)*power in st(2)
	fdiv	%st(1),%st		/form (ln(base)*power)/ln2
	fstcw	(%esp)			/get control word
	fwait				/wait until it is memory
	movw	(%esp),%ax		/put it into ax and
	andw	$0xf3ff,%ax		/ set rounding mode to nearest
	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((ln(base)*power)/ln2)
	fldcw	(%esp)			/restore control to original value
	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	%ax			/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,%ax		/was value < 0?
	jz	pow_no_invert		/if not, don't invert result
	/fdivr	%st(1),%st(0)		/if so, invert result (1.0/result)
	.byte	0xd8,0xf9
pow_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
/*
 *    end of exp
 */
	fstp	%st(1)			/copy result into st(1)
	jmp	pow_return

/*
 *    Register Status
 *      st(0) = power
 *      st(1) = base
 *      st = st(4) for restoration
 *      Assert base != 0 and power is an integer
*/
pow_for_int:
	fld	%st(0)			/make backup copy of power
	fabs				/take abs(power)
	fcompl	pow_max_int_power	/compare and pop abs(power)
	fstsw	%ax			/load ax with result of compare
	sahf				/set flags
	ja	pow_for_real		/jmp if power too large to use as int
	fistpl	(%esp)			/store power as integer
	fwait				/wait until store is complete
	movl	(%esp),%eax		/move int(power) into eax
	xorl	%ecx,%ecx		/0 to ecx
	cmpl	%ecx,%eax		/compare power to 0
	je	pow_result_1		/If power = 0, Result = base
	jg	pow_dont_invert		/If power > 0, don't invert
	incl	%ecx			/Else invert
	negl	%eax

pow_dont_invert:
	testw	$1,%ax
	jne	pow_odd
	fld1
	jmp	pow_calc_int_pow
pow_odd:
	fld	%st(0)

pow_calc_int_pow:
	movl	%eax,%edx		/edx holds "bits"
	fxch	%st(1)			/put base into st(0), pow into st(1)
pow_while_do:
	shrl	$1,%edx			/bits >>= 1
	jz	pow_end_of_while
	fmul	%st,%st(0)		/base = base * base
	testw	$1,%dx
	je	pow_while_do
	fmul	%st,%st(1)		/result = result * base
	jmp	pow_while_do
pow_end_of_while:
	fxch	%st(1)			/put result back into st(0)
	testw	$1,%ecx
	je	pow_int_return
	fld1
	fdiv

pow_int_return:
	fstp	%st(1)
	jmp	pow_return

pow_result_1:
	fstp	%st(0)			/st = st(2)
	fld1				/st = st(1), st = st(1)
	addl	$8,%esp			/flush local storage
	ret

pow_return:
					/check for overflow
	fld	%st(0)			/make backup copy of result
	fabs				/take abs(result)
	fcompl	_maxdouble		/compare abs(result)
	fstsw	%ax			/load ax with result of compare
	sahf				/set flags
	ja	pow_big_err		/jmp if abs(result) is above maxdouble
	jp	pow_big_err		/jmp if abs(result) is unordered

					/check for underflow
	fld	%st(0)			/make backup copy of result
	fabs				/take abs(result)
	fcompl	_mindouble		/compare and pop abs(result)
	fstsw	%ax			/load ax with result of compare
	sahf				/set flags
	ja	pow_restore_stack

	fstp	%st(0)
	fclex
	fldl	12+8(%esp)		/put power on stack
	fldl	4+8(%esp)		/put base on stack
	jmp	pow_return_zero1

pow_restore_stack:
	addl	$8,%esp		/flush local storage
	ret

	.align	2
pow_max_int_power: .double 0DX41DFFFFFFFC00000	/ 2147483647.0
