155111Storek /*
2*63319Sbostic * Copyright (c) 1992, 1993
3*63319Sbostic * The Regents of the University of California. All rights reserved.
455111Storek *
555111Storek * This software was developed by the Computer Systems Engineering group
655111Storek * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
755111Storek * contributed to Berkeley.
855111Storek *
955500Sbostic * All advertising materials mentioning features or use of this software
1055500Sbostic * must display the following acknowledgement:
1155500Sbostic * This product includes software developed by the University of
1259195Storek * California, Lawrence Berkeley Laboratory.
1355500Sbostic *
1455111Storek * %sccs.include.redist.c%
1555111Storek *
16*63319Sbostic * @(#)fpu_add.c 8.1 (Berkeley) 06/11/93
1755111Storek *
1859195Storek * from: $Header: fpu_add.c,v 1.4 92/11/26 01:39:46 torek Exp $
1955111Storek */
2055111Storek
2155111Storek /*
2255111Storek * Perform an FPU add (return x + y).
2355111Storek *
2455111Storek * To subtract, negate y and call add.
2555111Storek */
2655111Storek
2756537Sbostic #include <sys/types.h>
2855111Storek
2956537Sbostic #include <machine/reg.h>
3055111Storek
3156537Sbostic #include <sparc/fpu/fpu_arith.h>
3256537Sbostic #include <sparc/fpu/fpu_emu.h>
3355111Storek
3455111Storek struct fpn *
fpu_add(fe)3555111Storek fpu_add(fe)
3655111Storek register struct fpemu *fe;
3755111Storek {
3855111Storek register struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2, *r;
3955111Storek register u_int r0, r1, r2, r3;
4055111Storek register int rd;
4155111Storek
4255111Storek /*
4355111Storek * Put the `heavier' operand on the right (see fpu_emu.h).
4455111Storek * Then we will have one of the following cases, taken in the
4555111Storek * following order:
4655111Storek *
4755111Storek * - y = NaN. Implied: if only one is a signalling NaN, y is.
4855111Storek * The result is y.
4955111Storek * - y = Inf. Implied: x != NaN (is 0, number, or Inf: the NaN
5055111Storek * case was taken care of earlier).
5155111Storek * If x = -y, the result is NaN. Otherwise the result
5255111Storek * is y (an Inf of whichever sign).
5355111Storek * - y is 0. Implied: x = 0.
5455111Storek * If x and y differ in sign (one positive, one negative),
5555111Storek * the result is +0 except when rounding to -Inf. If same:
5655111Storek * +0 + +0 = +0; -0 + -0 = -0.
5755111Storek * - x is 0. Implied: y != 0.
5855111Storek * Result is y.
5955111Storek * - other. Implied: both x and y are numbers.
6055111Storek * Do addition a la Hennessey & Patterson.
6155111Storek */
6255111Storek ORDER(x, y);
6355111Storek if (ISNAN(y))
6455111Storek return (y);
6555111Storek if (ISINF(y)) {
6655111Storek if (ISINF(x) && x->fp_sign != y->fp_sign)
6755111Storek return (fpu_newnan(fe));
6855111Storek return (y);
6955111Storek }
7055111Storek rd = ((fe->fe_fsr >> FSR_RD_SHIFT) & FSR_RD_MASK);
7155111Storek if (ISZERO(y)) {
7255111Storek if (rd != FSR_RD_RM) /* only -0 + -0 gives -0 */
7355111Storek y->fp_sign &= x->fp_sign;
7455111Storek else /* any -0 operand gives -0 */
7555111Storek y->fp_sign |= x->fp_sign;
7655111Storek return (y);
7755111Storek }
7855111Storek if (ISZERO(x))
7955111Storek return (y);
8055111Storek /*
8155111Storek * We really have two numbers to add, although their signs may
8255111Storek * differ. Make the exponents match, by shifting the smaller
8355111Storek * number right (e.g., 1.011 => 0.1011) and increasing its
8455111Storek * exponent (2^3 => 2^4). Note that we do not alter the exponents
8555111Storek * of x and y here.
8655111Storek */
8755111Storek r = &fe->fe_f3;
8855111Storek r->fp_class = FPC_NUM;
8955111Storek if (x->fp_exp == y->fp_exp) {
9055111Storek r->fp_exp = x->fp_exp;
9155111Storek r->fp_sticky = 0;
9255111Storek } else {
9355111Storek if (x->fp_exp < y->fp_exp) {
9455111Storek /*
9555111Storek * Try to avoid subtract case iii (see below).
9655111Storek * This also guarantees that x->fp_sticky = 0.
9755111Storek */
9855111Storek SWAP(x, y);
9955111Storek }
10055111Storek /* now x->fp_exp > y->fp_exp */
10155111Storek r->fp_exp = x->fp_exp;
10255111Storek r->fp_sticky = fpu_shr(y, x->fp_exp - y->fp_exp);
10355111Storek }
10455111Storek r->fp_sign = x->fp_sign;
10555111Storek if (x->fp_sign == y->fp_sign) {
10655111Storek FPU_DECL_CARRY
10755111Storek
10855111Storek /*
10955111Storek * The signs match, so we simply add the numbers. The result
11055111Storek * may be `supernormal' (as big as 1.111...1 + 1.111...1, or
11155111Storek * 11.111...0). If so, a single bit shift-right will fix it
11255111Storek * (but remember to adjust the exponent).
11355111Storek */
11455111Storek /* r->fp_mant = x->fp_mant + y->fp_mant */
11555111Storek FPU_ADDS(r->fp_mant[3], x->fp_mant[3], y->fp_mant[3]);
11655111Storek FPU_ADDCS(r->fp_mant[2], x->fp_mant[2], y->fp_mant[2]);
11755111Storek FPU_ADDCS(r->fp_mant[1], x->fp_mant[1], y->fp_mant[1]);
11855111Storek FPU_ADDC(r0, x->fp_mant[0], y->fp_mant[0]);
11955111Storek if ((r->fp_mant[0] = r0) >= FP_2) {
12055111Storek (void) fpu_shr(r, 1);
12155111Storek r->fp_exp++;
12255111Storek }
12355111Storek } else {
12455111Storek FPU_DECL_CARRY
12555111Storek
12655111Storek /*
12755111Storek * The signs differ, so things are rather more difficult.
12855111Storek * H&P would have us negate the negative operand and add;
12955111Storek * this is the same as subtracting the negative operand.
13055111Storek * This is quite a headache. Instead, we will subtract
13155111Storek * y from x, regardless of whether y itself is the negative
13255111Storek * operand. When this is done one of three conditions will
13355111Storek * hold, depending on the magnitudes of x and y:
13455111Storek * case i) |x| > |y|. The result is just x - y,
13555111Storek * with x's sign, but it may need to be normalized.
13655111Storek * case ii) |x| = |y|. The result is 0 (maybe -0)
13755111Storek * so must be fixed up.
13855111Storek * case iii) |x| < |y|. We goofed; the result should
13955111Storek * be (y - x), with the same sign as y.
14055111Storek * We could compare |x| and |y| here and avoid case iii,
14155111Storek * but that would take just as much work as the subtract.
14255111Storek * We can tell case iii has occurred by an overflow.
14355111Storek *
14455111Storek * N.B.: since x->fp_exp >= y->fp_exp, x->fp_sticky = 0.
14555111Storek */
14655111Storek /* r->fp_mant = x->fp_mant - y->fp_mant */
14755111Storek FPU_SET_CARRY(y->fp_sticky);
14855111Storek FPU_SUBCS(r3, x->fp_mant[3], y->fp_mant[3]);
14955111Storek FPU_SUBCS(r2, x->fp_mant[2], y->fp_mant[2]);
15055111Storek FPU_SUBCS(r1, x->fp_mant[1], y->fp_mant[1]);
15155111Storek FPU_SUBC(r0, x->fp_mant[0], y->fp_mant[0]);
15255111Storek if (r0 < FP_2) {
15355111Storek /* cases i and ii */
15455111Storek if ((r0 | r1 | r2 | r3) == 0) {
15555111Storek /* case ii */
15655111Storek r->fp_class = FPC_ZERO;
15755111Storek r->fp_sign = rd == FSR_RD_RM;
15855111Storek return (r);
15955111Storek }
16055111Storek } else {
16155111Storek /*
16255111Storek * Oops, case iii. This can only occur when the
16355111Storek * exponents were equal, in which case neither
16455111Storek * x nor y have sticky bits set. Flip the sign
16555111Storek * (to y's sign) and negate the result to get y - x.
16655111Storek */
16755111Storek #ifdef DIAGNOSTIC
16855111Storek if (x->fp_exp != y->fp_exp || r->fp_sticky)
16955111Storek panic("fpu_add");
17055111Storek #endif
17155111Storek r->fp_sign = y->fp_sign;
17255111Storek FPU_SUBS(r3, 0, r3);
17355111Storek FPU_SUBCS(r2, 0, r2);
17455111Storek FPU_SUBCS(r1, 0, r1);
17555111Storek FPU_SUBC(r0, 0, r0);
17655111Storek }
17755111Storek r->fp_mant[3] = r3;
17855111Storek r->fp_mant[2] = r2;
17955111Storek r->fp_mant[1] = r1;
18055111Storek r->fp_mant[0] = r0;
18155111Storek if (r0 < FP_1)
18255111Storek fpu_norm(r);
18355111Storek }
18455111Storek return (r);
18555111Storek }
186