17e76048aSMarcel Moolenaar /* $NetBSD: fpu_add.c,v 1.4 2005/12/11 12:18:42 christos Exp $ */ 27e76048aSMarcel Moolenaar 37e76048aSMarcel Moolenaar /* 47e76048aSMarcel Moolenaar * Copyright (c) 1992, 1993 57e76048aSMarcel Moolenaar * The Regents of the University of California. All rights reserved. 67e76048aSMarcel Moolenaar * 77e76048aSMarcel Moolenaar * This software was developed by the Computer Systems Engineering group 87e76048aSMarcel Moolenaar * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 97e76048aSMarcel Moolenaar * contributed to Berkeley. 107e76048aSMarcel Moolenaar * 117e76048aSMarcel Moolenaar * All advertising materials mentioning features or use of this software 127e76048aSMarcel Moolenaar * must display the following acknowledgement: 137e76048aSMarcel Moolenaar * This product includes software developed by the University of 147e76048aSMarcel Moolenaar * California, Lawrence Berkeley Laboratory. 157e76048aSMarcel Moolenaar * 167e76048aSMarcel Moolenaar * Redistribution and use in source and binary forms, with or without 177e76048aSMarcel Moolenaar * modification, are permitted provided that the following conditions 187e76048aSMarcel Moolenaar * are met: 197e76048aSMarcel Moolenaar * 1. Redistributions of source code must retain the above copyright 207e76048aSMarcel Moolenaar * notice, this list of conditions and the following disclaimer. 217e76048aSMarcel Moolenaar * 2. Redistributions in binary form must reproduce the above copyright 227e76048aSMarcel Moolenaar * notice, this list of conditions and the following disclaimer in the 237e76048aSMarcel Moolenaar * documentation and/or other materials provided with the distribution. 247e76048aSMarcel Moolenaar * 3. Neither the name of the University nor the names of its contributors 257e76048aSMarcel Moolenaar * may be used to endorse or promote products derived from this software 267e76048aSMarcel Moolenaar * without specific prior written permission. 277e76048aSMarcel Moolenaar * 287e76048aSMarcel Moolenaar * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 297e76048aSMarcel Moolenaar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 307e76048aSMarcel Moolenaar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 317e76048aSMarcel Moolenaar * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 327e76048aSMarcel Moolenaar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 337e76048aSMarcel Moolenaar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 347e76048aSMarcel Moolenaar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 357e76048aSMarcel Moolenaar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 367e76048aSMarcel Moolenaar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 377e76048aSMarcel Moolenaar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 387e76048aSMarcel Moolenaar * SUCH DAMAGE. 397e76048aSMarcel Moolenaar * 407e76048aSMarcel Moolenaar * @(#)fpu_add.c 8.1 (Berkeley) 6/11/93 417e76048aSMarcel Moolenaar */ 427e76048aSMarcel Moolenaar 437e76048aSMarcel Moolenaar /* 447e76048aSMarcel Moolenaar * Perform an FPU add (return x + y). 457e76048aSMarcel Moolenaar * 467e76048aSMarcel Moolenaar * To subtract, negate y and call add. 477e76048aSMarcel Moolenaar */ 487e76048aSMarcel Moolenaar 497e76048aSMarcel Moolenaar #include <sys/cdefs.h> 507e76048aSMarcel Moolenaar __FBSDID("$FreeBSD$"); 517e76048aSMarcel Moolenaar 527e76048aSMarcel Moolenaar #include <sys/systm.h> 537e76048aSMarcel Moolenaar #include <sys/types.h> 547e76048aSMarcel Moolenaar 557e76048aSMarcel Moolenaar #include <machine/fpu.h> 567e76048aSMarcel Moolenaar #include <machine/ieeefp.h> 577e76048aSMarcel Moolenaar #include <machine/reg.h> 587e76048aSMarcel Moolenaar 597e76048aSMarcel Moolenaar #include <powerpc/fpu/fpu_arith.h> 607e76048aSMarcel Moolenaar #include <powerpc/fpu/fpu_emu.h> 617e76048aSMarcel Moolenaar #include <powerpc/fpu/fpu_extern.h> 627e76048aSMarcel Moolenaar 637e76048aSMarcel Moolenaar struct fpn * 647e76048aSMarcel Moolenaar fpu_add(struct fpemu *fe) 657e76048aSMarcel Moolenaar { 667e76048aSMarcel Moolenaar struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2, *r; 677e76048aSMarcel Moolenaar u_int r0, r1, r2, r3; 687e76048aSMarcel Moolenaar int rd; 697e76048aSMarcel Moolenaar 707e76048aSMarcel Moolenaar /* 717e76048aSMarcel Moolenaar * Put the `heavier' operand on the right (see fpu_emu.h). 727e76048aSMarcel Moolenaar * Then we will have one of the following cases, taken in the 737e76048aSMarcel Moolenaar * following order: 747e76048aSMarcel Moolenaar * 757e76048aSMarcel Moolenaar * - y = NaN. Implied: if only one is a signalling NaN, y is. 767e76048aSMarcel Moolenaar * The result is y. 777e76048aSMarcel Moolenaar * - y = Inf. Implied: x != NaN (is 0, number, or Inf: the NaN 787e76048aSMarcel Moolenaar * case was taken care of earlier). 797e76048aSMarcel Moolenaar * If x = -y, the result is NaN. Otherwise the result 807e76048aSMarcel Moolenaar * is y (an Inf of whichever sign). 817e76048aSMarcel Moolenaar * - y is 0. Implied: x = 0. 827e76048aSMarcel Moolenaar * If x and y differ in sign (one positive, one negative), 837e76048aSMarcel Moolenaar * the result is +0 except when rounding to -Inf. If same: 847e76048aSMarcel Moolenaar * +0 + +0 = +0; -0 + -0 = -0. 857e76048aSMarcel Moolenaar * - x is 0. Implied: y != 0. 867e76048aSMarcel Moolenaar * Result is y. 877e76048aSMarcel Moolenaar * - other. Implied: both x and y are numbers. 887e76048aSMarcel Moolenaar * Do addition a la Hennessey & Patterson. 897e76048aSMarcel Moolenaar */ 907e76048aSMarcel Moolenaar DPRINTF(FPE_REG, ("fpu_add:\n")); 917e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, x); 927e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, y); 937e76048aSMarcel Moolenaar DPRINTF(FPE_REG, ("=>\n")); 947e76048aSMarcel Moolenaar ORDER(x, y); 957e76048aSMarcel Moolenaar if (ISNAN(y)) { 967e76048aSMarcel Moolenaar fe->fe_cx |= FPSCR_VXSNAN; 977e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, y); 987e76048aSMarcel Moolenaar return (y); 997e76048aSMarcel Moolenaar } 1007e76048aSMarcel Moolenaar if (ISINF(y)) { 1017e76048aSMarcel Moolenaar if (ISINF(x) && x->fp_sign != y->fp_sign) { 1027e76048aSMarcel Moolenaar fe->fe_cx |= FPSCR_VXISI; 1037e76048aSMarcel Moolenaar return (fpu_newnan(fe)); 1047e76048aSMarcel Moolenaar } 1057e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, y); 1067e76048aSMarcel Moolenaar return (y); 1077e76048aSMarcel Moolenaar } 1087e76048aSMarcel Moolenaar rd = ((fe->fe_fpscr) & FPSCR_RN); 1097e76048aSMarcel Moolenaar if (ISZERO(y)) { 1107e76048aSMarcel Moolenaar if (rd != FP_RM) /* only -0 + -0 gives -0 */ 1117e76048aSMarcel Moolenaar y->fp_sign &= x->fp_sign; 1127e76048aSMarcel Moolenaar else /* any -0 operand gives -0 */ 1137e76048aSMarcel Moolenaar y->fp_sign |= x->fp_sign; 1147e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, y); 1157e76048aSMarcel Moolenaar return (y); 1167e76048aSMarcel Moolenaar } 1177e76048aSMarcel Moolenaar if (ISZERO(x)) { 1187e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, y); 1197e76048aSMarcel Moolenaar return (y); 1207e76048aSMarcel Moolenaar } 1217e76048aSMarcel Moolenaar /* 1227e76048aSMarcel Moolenaar * We really have two numbers to add, although their signs may 1237e76048aSMarcel Moolenaar * differ. Make the exponents match, by shifting the smaller 1247e76048aSMarcel Moolenaar * number right (e.g., 1.011 => 0.1011) and increasing its 1257e76048aSMarcel Moolenaar * exponent (2^3 => 2^4). Note that we do not alter the exponents 1267e76048aSMarcel Moolenaar * of x and y here. 1277e76048aSMarcel Moolenaar */ 1287e76048aSMarcel Moolenaar r = &fe->fe_f3; 1297e76048aSMarcel Moolenaar r->fp_class = FPC_NUM; 1307e76048aSMarcel Moolenaar if (x->fp_exp == y->fp_exp) { 1317e76048aSMarcel Moolenaar r->fp_exp = x->fp_exp; 1327e76048aSMarcel Moolenaar r->fp_sticky = 0; 1337e76048aSMarcel Moolenaar } else { 1347e76048aSMarcel Moolenaar if (x->fp_exp < y->fp_exp) { 1357e76048aSMarcel Moolenaar /* 1367e76048aSMarcel Moolenaar * Try to avoid subtract case iii (see below). 1377e76048aSMarcel Moolenaar * This also guarantees that x->fp_sticky = 0. 1387e76048aSMarcel Moolenaar */ 1397e76048aSMarcel Moolenaar SWAP(x, y); 1407e76048aSMarcel Moolenaar } 1417e76048aSMarcel Moolenaar /* now x->fp_exp > y->fp_exp */ 1427e76048aSMarcel Moolenaar r->fp_exp = x->fp_exp; 1437e76048aSMarcel Moolenaar r->fp_sticky = fpu_shr(y, x->fp_exp - y->fp_exp); 1447e76048aSMarcel Moolenaar } 1457e76048aSMarcel Moolenaar r->fp_sign = x->fp_sign; 1467e76048aSMarcel Moolenaar if (x->fp_sign == y->fp_sign) { 1477e76048aSMarcel Moolenaar FPU_DECL_CARRY 1487e76048aSMarcel Moolenaar 1497e76048aSMarcel Moolenaar /* 1507e76048aSMarcel Moolenaar * The signs match, so we simply add the numbers. The result 1517e76048aSMarcel Moolenaar * may be `supernormal' (as big as 1.111...1 + 1.111...1, or 1527e76048aSMarcel Moolenaar * 11.111...0). If so, a single bit shift-right will fix it 1537e76048aSMarcel Moolenaar * (but remember to adjust the exponent). 1547e76048aSMarcel Moolenaar */ 1557e76048aSMarcel Moolenaar /* r->fp_mant = x->fp_mant + y->fp_mant */ 1567e76048aSMarcel Moolenaar FPU_ADDS(r->fp_mant[3], x->fp_mant[3], y->fp_mant[3]); 1577e76048aSMarcel Moolenaar FPU_ADDCS(r->fp_mant[2], x->fp_mant[2], y->fp_mant[2]); 1587e76048aSMarcel Moolenaar FPU_ADDCS(r->fp_mant[1], x->fp_mant[1], y->fp_mant[1]); 1597e76048aSMarcel Moolenaar FPU_ADDC(r0, x->fp_mant[0], y->fp_mant[0]); 1607e76048aSMarcel Moolenaar if ((r->fp_mant[0] = r0) >= FP_2) { 1617e76048aSMarcel Moolenaar (void) fpu_shr(r, 1); 1627e76048aSMarcel Moolenaar r->fp_exp++; 1637e76048aSMarcel Moolenaar } 1647e76048aSMarcel Moolenaar } else { 1657e76048aSMarcel Moolenaar FPU_DECL_CARRY 1667e76048aSMarcel Moolenaar 1677e76048aSMarcel Moolenaar /* 1687e76048aSMarcel Moolenaar * The signs differ, so things are rather more difficult. 1697e76048aSMarcel Moolenaar * H&P would have us negate the negative operand and add; 1707e76048aSMarcel Moolenaar * this is the same as subtracting the negative operand. 1717e76048aSMarcel Moolenaar * This is quite a headache. Instead, we will subtract 1727e76048aSMarcel Moolenaar * y from x, regardless of whether y itself is the negative 1737e76048aSMarcel Moolenaar * operand. When this is done one of three conditions will 1747e76048aSMarcel Moolenaar * hold, depending on the magnitudes of x and y: 1757e76048aSMarcel Moolenaar * case i) |x| > |y|. The result is just x - y, 1767e76048aSMarcel Moolenaar * with x's sign, but it may need to be normalized. 1777e76048aSMarcel Moolenaar * case ii) |x| = |y|. The result is 0 (maybe -0) 1787e76048aSMarcel Moolenaar * so must be fixed up. 1797e76048aSMarcel Moolenaar * case iii) |x| < |y|. We goofed; the result should 1807e76048aSMarcel Moolenaar * be (y - x), with the same sign as y. 1817e76048aSMarcel Moolenaar * We could compare |x| and |y| here and avoid case iii, 1827e76048aSMarcel Moolenaar * but that would take just as much work as the subtract. 1837e76048aSMarcel Moolenaar * We can tell case iii has occurred by an overflow. 1847e76048aSMarcel Moolenaar * 1857e76048aSMarcel Moolenaar * N.B.: since x->fp_exp >= y->fp_exp, x->fp_sticky = 0. 1867e76048aSMarcel Moolenaar */ 1877e76048aSMarcel Moolenaar /* r->fp_mant = x->fp_mant - y->fp_mant */ 1887e76048aSMarcel Moolenaar FPU_SET_CARRY(y->fp_sticky); 1897e76048aSMarcel Moolenaar FPU_SUBCS(r3, x->fp_mant[3], y->fp_mant[3]); 1907e76048aSMarcel Moolenaar FPU_SUBCS(r2, x->fp_mant[2], y->fp_mant[2]); 1917e76048aSMarcel Moolenaar FPU_SUBCS(r1, x->fp_mant[1], y->fp_mant[1]); 1927e76048aSMarcel Moolenaar FPU_SUBC(r0, x->fp_mant[0], y->fp_mant[0]); 1937e76048aSMarcel Moolenaar if (r0 < FP_2) { 1947e76048aSMarcel Moolenaar /* cases i and ii */ 1957e76048aSMarcel Moolenaar if ((r0 | r1 | r2 | r3) == 0) { 1967e76048aSMarcel Moolenaar /* case ii */ 1977e76048aSMarcel Moolenaar r->fp_class = FPC_ZERO; 1987e76048aSMarcel Moolenaar r->fp_sign = rd == FP_RM; 1997e76048aSMarcel Moolenaar return (r); 2007e76048aSMarcel Moolenaar } 2017e76048aSMarcel Moolenaar } else { 2027e76048aSMarcel Moolenaar /* 2037e76048aSMarcel Moolenaar * Oops, case iii. This can only occur when the 2047e76048aSMarcel Moolenaar * exponents were equal, in which case neither 2057e76048aSMarcel Moolenaar * x nor y have sticky bits set. Flip the sign 2067e76048aSMarcel Moolenaar * (to y's sign) and negate the result to get y - x. 2077e76048aSMarcel Moolenaar */ 2087e76048aSMarcel Moolenaar #ifdef DIAGNOSTIC 2097e76048aSMarcel Moolenaar if (x->fp_exp != y->fp_exp || r->fp_sticky) 2107e76048aSMarcel Moolenaar panic("fpu_add"); 2117e76048aSMarcel Moolenaar #endif 2127e76048aSMarcel Moolenaar r->fp_sign = y->fp_sign; 2137e76048aSMarcel Moolenaar FPU_SUBS(r3, 0, r3); 2147e76048aSMarcel Moolenaar FPU_SUBCS(r2, 0, r2); 2157e76048aSMarcel Moolenaar FPU_SUBCS(r1, 0, r1); 2167e76048aSMarcel Moolenaar FPU_SUBC(r0, 0, r0); 2177e76048aSMarcel Moolenaar } 2187e76048aSMarcel Moolenaar r->fp_mant[3] = r3; 2197e76048aSMarcel Moolenaar r->fp_mant[2] = r2; 2207e76048aSMarcel Moolenaar r->fp_mant[1] = r1; 2217e76048aSMarcel Moolenaar r->fp_mant[0] = r0; 2227e76048aSMarcel Moolenaar if (r0 < FP_1) 2237e76048aSMarcel Moolenaar fpu_norm(r); 2247e76048aSMarcel Moolenaar } 2257e76048aSMarcel Moolenaar DUMPFPN(FPE_REG, r); 2267e76048aSMarcel Moolenaar return (r); 2277e76048aSMarcel Moolenaar } 228