/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
/* $Header: ldexp.s,v 1.11.1.1 92/06/24 13:59:26 marker Exp $ */

/*
 * double ldexp (value, exp)
 * double value;
 * int exp;
 *
 * Ldexp returns value*2**exp, if that result is in range.
 * If underflow occurs, it returns zero.  If overflow occurs,
 * it returns a value of appropriate sign and largest
 * possible magnitude.  In case of either overflow or underflow,
 * errno is set to ERANGE.  Note that errno is not modified if
 * no error occurs.
 */

#include <regdef.h>

.text
.globl ldexp
.ent ldexp
ldexp:
	.frame	sp, 0, ra
.set noreorder
	mov.d	$f0, $f12
	mfc1	t0, $f13
	beq	a2, 0, 1f	# exponent zero, return value
	mfc1	t2, $f12
	bne	t0, 0, 7f	# value not zero, continue normally
	sll	t1, t0, 1
	beq	t2, 0, 1f	# original value zero, return zero
	nop
7:
	srl	t1, 21		# t1 now has exp of val
	beq	t1, 0, 10f	# if val is denormalized, calculate true exp.
	slt	t2, a2, 2047	# check if exp will cause return value
	beq	t2, 0, 2f	#       to exceed representable range.
	addu	t1, a2
	ble	t1, 0, 3f
	 slt	t2, t1, 2047
	beq	t2, 0, 2f
	 sll	a2, 20
	addu	t0, a2
	mtc1	t0, $f1
1:	j	ra
	nop

15:
	slt	t2, a2, 2099	# check if exp will cause return value
	beq	t2, 0, 2f	#       to exceed representable range (2047 + 52)
	addu	t1, a2		
	ble	t1, 0, 3f	# denormalized return
	slt	t2, t1, 2047	
	beq	t2, 0, 2f	# overflow
	mfc1	t0, $f13
	mfc1	t2, $f12
	sll	t1, 20		# exp is handled differenly if val was denormalized
	or	t0, t1		# the resulting exp is actually in t1
	mtc1	t0, $f1
	j	ra
	mtc1	t2, $f0

10:
	/*
	 * Handle case where the value passed in is denormalized.
	 * I normalize the number and store it back in $f12 and $f13.
	 * The exponent field in $f13 remains zero, and the "true"
	 * exponent of the value will be passed back in t1.
	 * The rest of the code can then assume $f12 and $f13 is
	 * a normalized number.
	 */
	mfc1 	t6, $f13
	li	t2, 0x80000000  # mask for looking for the first 1 bit
	li 	t3, 1
	sll	t6, 12		# get rid of sign bit and exp
12:	sub	t1, t3		# decrement exp
	nop
	ble	t1, -21, 13f	# No more bits.  Look at most significant word.
	and 	t7, t6, t2	# is there a 1 here?
	beq	t7, 0, 12b	# if not, shift again.
	sll	t6, 1		
	li	t3, 0xffffffff	# FOUND FIRST 1
	xor	t7, t1, t3
	mfc1	t3, $f12
	addu	t7, 1		# t7 contains how many bits we have shifted
	sll	t3, t7		# shift $f12 by the amount we shifted and keep it in t3
	mfc1	t5, $f12
	subu	t7, 32
	srl	t5, t7		# t5 contains the bits that will be shifted into $f13
	sll	t6, 12		# shift most sig. bits into place
	or 	t6, t5		# 'or' in the bits shifted in from $f12
	mfc1	t5, $f13
	nop
	and 	t5, t2		# get the sign bit
	and 	t6, t5
	mtc1	t6, $f13	# store the normalized $f13 value (without exponent)
	addu	t1, 1		# (weird fence post in moving from denorm. to norm)
	j	15b		# done, t1 has true exp
	mtc1	t3, $f12	# store the shifted $f12 value
	
13:	addu	t1, 1		# add one back because I decremented twice
	mfc1	t6, $f12	# now look through most significant word
14:	subu	t1, 1		# decrement exp
	ble	t1, -53, 1b	# No more bits.  The value passed in was 0.
	and 	t7, t6, t2	# is there a 1 here?
	beq	t7, 0, 14b	# if not, shift again.
	sll	t6, 1
	mfc1	t3, $f13	# FOUND FIRST 1
	srl	t5, t6, 12	# t5 contains the bits that will be shifted into $f13
	and 	t3, t2		# get only the sign bit
	or 	t3, t5
	sll	t6, 20
	mtc1	t3, $f13	# store the normalized $f13 value
	addu	t1, 1		# (weird fence post in moving from denorm. to norm)
	j	15b		# done, t1 has true exp
	mtc1	t6, $f12	# store the normalized $f12 value
	
2:	/* Return HUGE/HUGE_VAL with appropriate sign. */
.extern errno 4
#define ERANGE  34
	li      t7, ERANGE
	sw      t7, errno
#if	(defined(__STDC__) || defined(__SVR4__STDC))
	lw	t6, _lib_version
	li	t5, 0		# c_issue_4
	beq	t5, t6, 4f
	 li.d    $f4, 0.0
	li.d    $f0, 1.0
	bge	t0, 0, 1b
	 div.d   $f0, $f4	# return HUGE_VAL
	j	ra
	 neg.d	$f0
4:
#endif
	li.d 	$f0, 3.4028234663852886e+38 	# return HUGE 
	bge	t0, 0, 1b
         nop
	j	ra
	 neg.d	$f0
3:	/* Return denorm. */
	ble	t1, -52, 5f
	 mfc1	t5, $f13	# t4,t5: copy of unscaled number
	li	t2, 0x80000000
	sll	t5, 11		# clear exponent
	ble	t1, -31, 4f
	 or	t5, t2		# turn on hidden bit
	srl	t5, 11
	mfc1	t4, $f12
	subu	t1, 1		# now shift by amount of underflow
	sll	t3, t5, t1
	srl	t2, t4, t1
	negu	t1
	srl	t4, t1
	or	t4, t3
	bge	t2, 0, 9f
	 srl	t5, t1
	/* Round up */
	addu	t4, 1
	sltu	t6, t4, 1
	sll	t2, 1
	bne	t2, 0, 9f
	 addu	t5, t6
	and	t4, -2
9:	mtc1	t4, $f0
	bge	t0, 0, 1b
	 mtc1	t5, $f1
	j	ra
	 neg.d	$f0
4:	/* Return denorm with most significant word all zero. */
	mtc1	$0, $f1
	addu	t1, 20		# now shift by amount of underflow
	sll	t2, t5, t1
	negu	t1
	bge	t2, 0, 8f
	 srl	t4, t5, t1
	/* Round up */
	addu	t4, 1
	sltu	t6, t4, 1
	sll	t2, 1
	bne	t2, 0, 8f
	 mtc1	t6, $f1
	and	t4, -2
8:	bge	t0, 0, 1b
	 mtc1	t4, $f0
	j	ra
	 neg.d	$f0
5:	/* Return zero. */
	li      t7, ERANGE
	sw      t7, errno
	mtc1	$0, $f0
	bge	t0, 0, 1b
	 mtc1	$0, $f1
	j	ra
	 neg.d	$f0
.set reorder
.end ldexp
