/* $NetBSD: fpu_add.c,v 1.4 2005/12/11 12:18:42 christos Exp $ */12/*-3* SPDX-License-Identifier: BSD-3-Clause4*5* Copyright (c) 1992, 19936* The Regents of the University of California. All rights reserved.7*8* This software was developed by the Computer Systems Engineering group9* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and10* contributed to Berkeley.11*12* All advertising materials mentioning features or use of this software13* must display the following acknowledgement:14* This product includes software developed by the University of15* California, Lawrence Berkeley Laboratory.16*17* Redistribution and use in source and binary forms, with or without18* modification, are permitted provided that the following conditions19* are met:20* 1. Redistributions of source code must retain the above copyright21* notice, this list of conditions and the following disclaimer.22* 2. Redistributions in binary form must reproduce the above copyright23* notice, this list of conditions and the following disclaimer in the24* documentation and/or other materials provided with the distribution.25* 3. Neither the name of the University nor the names of its contributors26* may be used to endorse or promote products derived from this software27* without specific prior written permission.28*29* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND30* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE31* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE32* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE33* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL34* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS35* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)36* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT37* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY38* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF39* SUCH DAMAGE.40*/4142/*43* Perform an FPU add (return x + y).44*45* To subtract, negate y and call add.46*/4748#include <sys/types.h>49#include <sys/systm.h>5051#include <machine/fpu.h>52#include <machine/ieeefp.h>5354#include <powerpc/fpu/fpu_arith.h>55#include <powerpc/fpu/fpu_emu.h>5657struct fpn *58fpu_add(struct fpemu *fe)59{60struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2, *r;61u_int r0, r1, r2, r3;62int rd;6364/*65* Put the `heavier' operand on the right (see fpu_emu.h).66* Then we will have one of the following cases, taken in the67* following order:68*69* - y = NaN. Implied: if only one is a signalling NaN, y is.70* The result is y.71* - y = Inf. Implied: x != NaN (is 0, number, or Inf: the NaN72* case was taken care of earlier).73* If x = -y, the result is NaN. Otherwise the result74* is y (an Inf of whichever sign).75* - y is 0. Implied: x = 0.76* If x and y differ in sign (one positive, one negative),77* the result is +0 except when rounding to -Inf. If same:78* +0 + +0 = +0; -0 + -0 = -0.79* - x is 0. Implied: y != 0.80* Result is y.81* - other. Implied: both x and y are numbers.82* Do addition a la Hennessey & Patterson.83*/84DPRINTF(FPE_REG, ("fpu_add:\n"));85DUMPFPN(FPE_REG, x);86DUMPFPN(FPE_REG, y);87DPRINTF(FPE_REG, ("=>\n"));88ORDER(x, y);89if (ISNAN(y)) {90fe->fe_cx |= FPSCR_VXSNAN;91DUMPFPN(FPE_REG, y);92return (y);93}94if (ISINF(y)) {95if (ISINF(x) && x->fp_sign != y->fp_sign) {96fe->fe_cx |= FPSCR_VXISI;97return (fpu_newnan(fe));98}99DUMPFPN(FPE_REG, y);100return (y);101}102rd = ((fe->fe_fpscr) & FPSCR_RN);103if (ISZERO(y)) {104if (rd != FP_RM) /* only -0 + -0 gives -0 */105y->fp_sign &= x->fp_sign;106else /* any -0 operand gives -0 */107y->fp_sign |= x->fp_sign;108DUMPFPN(FPE_REG, y);109return (y);110}111if (ISZERO(x)) {112DUMPFPN(FPE_REG, y);113return (y);114}115/*116* We really have two numbers to add, although their signs may117* differ. Make the exponents match, by shifting the smaller118* number right (e.g., 1.011 => 0.1011) and increasing its119* exponent (2^3 => 2^4). Note that we do not alter the exponents120* of x and y here.121*/122r = &fe->fe_f3;123r->fp_class = FPC_NUM;124if (x->fp_exp == y->fp_exp) {125r->fp_exp = x->fp_exp;126r->fp_sticky = 0;127} else {128if (x->fp_exp < y->fp_exp) {129/*130* Try to avoid subtract case iii (see below).131* This also guarantees that x->fp_sticky = 0.132*/133SWAP(x, y);134}135/* now x->fp_exp > y->fp_exp */136r->fp_exp = x->fp_exp;137r->fp_sticky = fpu_shr(y, x->fp_exp - y->fp_exp);138}139r->fp_sign = x->fp_sign;140if (x->fp_sign == y->fp_sign) {141FPU_DECL_CARRY142143/*144* The signs match, so we simply add the numbers. The result145* may be `supernormal' (as big as 1.111...1 + 1.111...1, or146* 11.111...0). If so, a single bit shift-right will fix it147* (but remember to adjust the exponent).148*/149/* r->fp_mant = x->fp_mant + y->fp_mant */150FPU_ADDS(r->fp_mant[3], x->fp_mant[3], y->fp_mant[3]);151FPU_ADDCS(r->fp_mant[2], x->fp_mant[2], y->fp_mant[2]);152FPU_ADDCS(r->fp_mant[1], x->fp_mant[1], y->fp_mant[1]);153FPU_ADDC(r0, x->fp_mant[0], y->fp_mant[0]);154if ((r->fp_mant[0] = r0) >= FP_2) {155(void) fpu_shr(r, 1);156r->fp_exp++;157}158} else {159FPU_DECL_CARRY160161/*162* The signs differ, so things are rather more difficult.163* H&P would have us negate the negative operand and add;164* this is the same as subtracting the negative operand.165* This is quite a headache. Instead, we will subtract166* y from x, regardless of whether y itself is the negative167* operand. When this is done one of three conditions will168* hold, depending on the magnitudes of x and y:169* case i) |x| > |y|. The result is just x - y,170* with x's sign, but it may need to be normalized.171* case ii) |x| = |y|. The result is 0 (maybe -0)172* so must be fixed up.173* case iii) |x| < |y|. We goofed; the result should174* be (y - x), with the same sign as y.175* We could compare |x| and |y| here and avoid case iii,176* but that would take just as much work as the subtract.177* We can tell case iii has occurred by an overflow.178*179* N.B.: since x->fp_exp >= y->fp_exp, x->fp_sticky = 0.180*/181/* r->fp_mant = x->fp_mant - y->fp_mant */182FPU_SET_CARRY(y->fp_sticky);183FPU_SUBCS(r3, x->fp_mant[3], y->fp_mant[3]);184FPU_SUBCS(r2, x->fp_mant[2], y->fp_mant[2]);185FPU_SUBCS(r1, x->fp_mant[1], y->fp_mant[1]);186FPU_SUBC(r0, x->fp_mant[0], y->fp_mant[0]);187if (r0 < FP_2) {188/* cases i and ii */189if ((r0 | r1 | r2 | r3) == 0) {190/* case ii */191r->fp_class = FPC_ZERO;192r->fp_sign = rd == FP_RM;193return (r);194}195} else {196/*197* Oops, case iii. This can only occur when the198* exponents were equal, in which case neither199* x nor y have sticky bits set. Flip the sign200* (to y's sign) and negate the result to get y - x.201*/202#ifdef DIAGNOSTIC203if (x->fp_exp != y->fp_exp || r->fp_sticky)204panic("fpu_add");205#endif206r->fp_sign = y->fp_sign;207FPU_SUBS(r3, 0, r3);208FPU_SUBCS(r2, 0, r2);209FPU_SUBCS(r1, 0, r1);210FPU_SUBC(r0, 0, r0);211}212r->fp_mant[3] = r3;213r->fp_mant[2] = r2;214r->fp_mant[1] = r1;215r->fp_mant[0] = r0;216if (r0 < FP_1)217fpu_norm(r);218}219DUMPFPN(FPE_REG, r);220return (r);221}222223224