OpenSolaris_b135/uts/sparc/fpu/addsub.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1988 by Sun Microsystems, Inc.
 */

#ident	"%Z%%M%	%I%	%E% SMI"	/* SunOS-4.1 1.8 88/12/06 */

#include <sys/fpu/fpu_simulator.h>
#include <sys/fpu/globals.h>

static void
true_add(px, py, pz)
	unpacked	*px, *py, *pz;
{
	unsigned 	c;
	unpacked	*pt;

	if ((int) px->fpclass <= (int) py->fpclass) {	/* Reverse. */
		pt = py;
		py = px;
		px = pt;
	}
	/* Now class(x) >= class(y). */
	switch (px->fpclass) {
	case fp_quiet:		/* NaN + x -> NaN */
	case fp_signaling:	/* NaN + x -> NaN */
	case fp_infinity:	/* Inf + x -> Inf */
	case fp_zero:		/* 0 + 0 -> 0 */
		*pz = *px;
		return;
	default:
		if (py->fpclass == fp_zero) {
			*pz = *px;
			return;
		}
	}
	/* Now z is normal or subnormal. */
	/* Now y is normal or subnormal. */
	if (px->exponent < py->exponent) {	/* Reverse. */
		pt = py;
		py = px;
		px = pt;
	}
	/* Now class(x) >= class(y). */
	pz->fpclass = px->fpclass;
	pz->sign = px->sign;
	pz->exponent = px->exponent;
	pz->rounded = pz->sticky  = 0;

	if (px->exponent != py->exponent) {	/* pre-alignment required */
		fpu_rightshift(py, pz->exponent - py->exponent);
		pz->rounded = py->rounded;
		pz->sticky  = py->sticky;
	}
	c = 0;
	c = fpu_add3wc(&(pz->significand[3]), px->significand[3],
						py->significand[3], c);
	c = fpu_add3wc(&(pz->significand[2]), px->significand[2],
						py->significand[2], c);
	c = fpu_add3wc(&(pz->significand[1]), px->significand[1],
						py->significand[1], c);
	c = fpu_add3wc(&(pz->significand[0]), px->significand[0],
						py->significand[0], c);

	/* Handle carry out of msb. */
	if (pz->significand[0] >= 0x20000) {
		fpu_rightshift(pz, 1);	/* Carried out bit. */
		pz->exponent++;		/* Renormalize. */
	}
}

static void
true_sub(pfpsd, px, py, pz)
	fp_simd_type	*pfpsd;		/* Pointer to simulator data */
	unpacked	*px, *py, *pz;
{
	unsigned	*z, g, s, r, c;
	unpacked	*pt;

	if ((int) px->fpclass <= (int) py->fpclass) {	/* Reverse. */
		pt = py;
		py = px;
		px = pt;
	}
	/* Now class(x) >= class(y). */
	*pz = *px;		/* Tentative difference: x. */
	switch (pz->fpclass) {
	case fp_quiet:		/* NaN - x -> NaN */
	case fp_signaling:	/* NaN - x -> NaN */
		return;
	case fp_infinity:	/* Inf - x -> Inf */
		if (py->fpclass == fp_infinity) {
			fpu_error_nan(pfpsd, pz);	/* Inf - Inf -> NaN */
			pz->fpclass = fp_quiet;
		}
		return;
	case fp_zero:		/* 0 - 0 -> 0 */
		pz->sign = (pfpsd->fp_direction == fp_negative);
		return;
	default:
		if (py->fpclass == fp_zero)
			return;
	}

	/* x and y are both normal or subnormal. */

	if (px->exponent < py->exponent) { /* Reverse. */
		pt = py;
		py = px;
		px = pt;
	}
	/* Now exp(x) >= exp(y). */
	pz->fpclass = px->fpclass;
	pz->sign = px->sign;
	pz->exponent = px->exponent;
	pz->rounded = 0;
	pz->sticky = 0;
	z = pz->significand;

	if (px->exponent == py->exponent) {	/* no pre-alignment required */
		c = 0;
		c = fpu_sub3wc(&z[3], px->significand[3],
				py->significand[3], c);
		c = fpu_sub3wc(&z[2], px->significand[2],
				py->significand[2], c);
		c = fpu_sub3wc(&z[1], px->significand[1],
				py->significand[1], c);
		c = fpu_sub3wc(&z[0], px->significand[0],
				py->significand[0], c);
		if ((z[0]|z[1]|z[2]|z[3]) == 0) {	/* exact zero result */
			pz->sign = (pfpsd->fp_direction == fp_negative);
			pz->fpclass = fp_zero;
			return;
		}
		if (z[0] >= 0x20000) {	/* sign reversal occurred */
			pz->sign = py->sign;
			c = 0;
			c = fpu_neg2wc(&z[3], z[3], c);
			c = fpu_neg2wc(&z[2], z[2], c);
			c = fpu_neg2wc(&z[1], z[1], c);
			c = fpu_neg2wc(&z[0], z[0], c);
		}
		fpu_normalize(pz);
		return;
	} else {		/* pre-alignment required */
		fpu_rightshift(py, pz->exponent - py->exponent - 1);
		r = py->rounded; 	/* rounded bit */
		s = py->sticky;		/* sticky bit */
		fpu_rightshift(py, 1);
		g = py->rounded;	/* guard bit */
		if (s != 0) r = (r == 0);
		if ((r|s) != 0) g = (g == 0); /* guard and rounded bits of z */
		c = ((g|r|s) != 0);
		c = fpu_sub3wc(&z[3], px->significand[3],
				py->significand[3], c);
		c = fpu_sub3wc(&z[2], px->significand[2],
				py->significand[2], c);
		c = fpu_sub3wc(&z[1], px->significand[1],
				py->significand[1], c);
		c = fpu_sub3wc(&z[0], px->significand[0],
				py->significand[0], c);

		if (z[0] >= 0x10000) { 	/* don't need post-shifted */
			pz->sticky = s|r;
			pz->rounded = g;
		} else {		/* post-shifted left 1 bit */
			pz->sticky = s;
			pz->rounded = r;
			pz->significand[0] = (z[0]<<1)|((z[1]&0x80000000)>>31);
			pz->significand[1] = (z[1]<<1)|((z[2]&0x80000000)>>31);
			pz->significand[2] = (z[2]<<1)|((z[3]&0x80000000)>>31);
			pz->significand[3] = (z[3]<<1)|g;
			pz->exponent -= 1;
			if (z[0] < 0x10000) fpu_normalize(pz);
		}
		return;
	}
}

void
_fp_add(pfpsd, px, py, pz)
	fp_simd_type	*pfpsd;
	unpacked	*px, *py, *pz;
{
	if (px->sign == py->sign)
		true_add(px, py, pz);
	else
		true_sub(pfpsd, px, py, pz);
}

void
_fp_sub(pfpsd, px, py, pz)
	fp_simd_type	*pfpsd;
	unpacked	*px, *py, *pz;
{
	if (py->fpclass < fp_quiet) py->sign = 1 - py->sign;
	if (px->sign == py->sign)
		true_add(px, py, pz);
	else
		true_sub(pfpsd, px, py, pz);
}