1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds 3*1da177e4SLinus Torvalds fp_arith.c: floating-point math routines for the Linux-m68k 4*1da177e4SLinus Torvalds floating point emulator. 5*1da177e4SLinus Torvalds 6*1da177e4SLinus Torvalds Copyright (c) 1998-1999 David Huggins-Daines. 7*1da177e4SLinus Torvalds 8*1da177e4SLinus Torvalds Somewhat based on the AlphaLinux floating point emulator, by David 9*1da177e4SLinus Torvalds Mosberger-Tang. 10*1da177e4SLinus Torvalds 11*1da177e4SLinus Torvalds You may copy, modify, and redistribute this file under the terms of 12*1da177e4SLinus Torvalds the GNU General Public License, version 2, or any later version, at 13*1da177e4SLinus Torvalds your convenience. 14*1da177e4SLinus Torvalds */ 15*1da177e4SLinus Torvalds 16*1da177e4SLinus Torvalds #include "fp_emu.h" 17*1da177e4SLinus Torvalds #include "multi_arith.h" 18*1da177e4SLinus Torvalds #include "fp_arith.h" 19*1da177e4SLinus Torvalds 20*1da177e4SLinus Torvalds const struct fp_ext fp_QNaN = 21*1da177e4SLinus Torvalds { 22*1da177e4SLinus Torvalds .exp = 0x7fff, 23*1da177e4SLinus Torvalds .mant = { .m64 = ~0 } 24*1da177e4SLinus Torvalds }; 25*1da177e4SLinus Torvalds 26*1da177e4SLinus Torvalds const struct fp_ext fp_Inf = 27*1da177e4SLinus Torvalds { 28*1da177e4SLinus Torvalds .exp = 0x7fff, 29*1da177e4SLinus Torvalds }; 30*1da177e4SLinus Torvalds 31*1da177e4SLinus Torvalds /* let's start with the easy ones */ 32*1da177e4SLinus Torvalds 33*1da177e4SLinus Torvalds struct fp_ext * 34*1da177e4SLinus Torvalds fp_fabs(struct fp_ext *dest, struct fp_ext *src) 35*1da177e4SLinus Torvalds { 36*1da177e4SLinus Torvalds dprint(PINSTR, "fabs\n"); 37*1da177e4SLinus Torvalds 38*1da177e4SLinus Torvalds fp_monadic_check(dest, src); 39*1da177e4SLinus Torvalds 40*1da177e4SLinus Torvalds dest->sign = 0; 41*1da177e4SLinus Torvalds 42*1da177e4SLinus Torvalds return dest; 43*1da177e4SLinus Torvalds } 44*1da177e4SLinus Torvalds 45*1da177e4SLinus Torvalds struct fp_ext * 46*1da177e4SLinus Torvalds fp_fneg(struct fp_ext *dest, struct fp_ext *src) 47*1da177e4SLinus Torvalds { 48*1da177e4SLinus Torvalds dprint(PINSTR, "fneg\n"); 49*1da177e4SLinus Torvalds 50*1da177e4SLinus Torvalds fp_monadic_check(dest, src); 51*1da177e4SLinus Torvalds 52*1da177e4SLinus Torvalds dest->sign = !dest->sign; 53*1da177e4SLinus Torvalds 54*1da177e4SLinus Torvalds return dest; 55*1da177e4SLinus Torvalds } 56*1da177e4SLinus Torvalds 57*1da177e4SLinus Torvalds /* Now, the slightly harder ones */ 58*1da177e4SLinus Torvalds 59*1da177e4SLinus Torvalds /* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB, 60*1da177e4SLinus Torvalds FDSUB, and FCMP instructions. */ 61*1da177e4SLinus Torvalds 62*1da177e4SLinus Torvalds struct fp_ext * 63*1da177e4SLinus Torvalds fp_fadd(struct fp_ext *dest, struct fp_ext *src) 64*1da177e4SLinus Torvalds { 65*1da177e4SLinus Torvalds int diff; 66*1da177e4SLinus Torvalds 67*1da177e4SLinus Torvalds dprint(PINSTR, "fadd\n"); 68*1da177e4SLinus Torvalds 69*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 70*1da177e4SLinus Torvalds 71*1da177e4SLinus Torvalds if (IS_INF(dest)) { 72*1da177e4SLinus Torvalds /* infinity - infinity == NaN */ 73*1da177e4SLinus Torvalds if (IS_INF(src) && (src->sign != dest->sign)) 74*1da177e4SLinus Torvalds fp_set_nan(dest); 75*1da177e4SLinus Torvalds return dest; 76*1da177e4SLinus Torvalds } 77*1da177e4SLinus Torvalds if (IS_INF(src)) { 78*1da177e4SLinus Torvalds fp_copy_ext(dest, src); 79*1da177e4SLinus Torvalds return dest; 80*1da177e4SLinus Torvalds } 81*1da177e4SLinus Torvalds 82*1da177e4SLinus Torvalds if (IS_ZERO(dest)) { 83*1da177e4SLinus Torvalds if (IS_ZERO(src)) { 84*1da177e4SLinus Torvalds if (src->sign != dest->sign) { 85*1da177e4SLinus Torvalds if (FPDATA->rnd == FPCR_ROUND_RM) 86*1da177e4SLinus Torvalds dest->sign = 1; 87*1da177e4SLinus Torvalds else 88*1da177e4SLinus Torvalds dest->sign = 0; 89*1da177e4SLinus Torvalds } 90*1da177e4SLinus Torvalds } else 91*1da177e4SLinus Torvalds fp_copy_ext(dest, src); 92*1da177e4SLinus Torvalds return dest; 93*1da177e4SLinus Torvalds } 94*1da177e4SLinus Torvalds 95*1da177e4SLinus Torvalds dest->lowmant = src->lowmant = 0; 96*1da177e4SLinus Torvalds 97*1da177e4SLinus Torvalds if ((diff = dest->exp - src->exp) > 0) 98*1da177e4SLinus Torvalds fp_denormalize(src, diff); 99*1da177e4SLinus Torvalds else if ((diff = -diff) > 0) 100*1da177e4SLinus Torvalds fp_denormalize(dest, diff); 101*1da177e4SLinus Torvalds 102*1da177e4SLinus Torvalds if (dest->sign == src->sign) { 103*1da177e4SLinus Torvalds if (fp_addmant(dest, src)) 104*1da177e4SLinus Torvalds if (!fp_addcarry(dest)) 105*1da177e4SLinus Torvalds return dest; 106*1da177e4SLinus Torvalds } else { 107*1da177e4SLinus Torvalds if (dest->mant.m64 < src->mant.m64) { 108*1da177e4SLinus Torvalds fp_submant(dest, src, dest); 109*1da177e4SLinus Torvalds dest->sign = !dest->sign; 110*1da177e4SLinus Torvalds } else 111*1da177e4SLinus Torvalds fp_submant(dest, dest, src); 112*1da177e4SLinus Torvalds } 113*1da177e4SLinus Torvalds 114*1da177e4SLinus Torvalds return dest; 115*1da177e4SLinus Torvalds } 116*1da177e4SLinus Torvalds 117*1da177e4SLinus Torvalds /* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB 118*1da177e4SLinus Torvalds instructions. 119*1da177e4SLinus Torvalds 120*1da177e4SLinus Torvalds Remember that the arguments are in assembler-syntax order! */ 121*1da177e4SLinus Torvalds 122*1da177e4SLinus Torvalds struct fp_ext * 123*1da177e4SLinus Torvalds fp_fsub(struct fp_ext *dest, struct fp_ext *src) 124*1da177e4SLinus Torvalds { 125*1da177e4SLinus Torvalds dprint(PINSTR, "fsub "); 126*1da177e4SLinus Torvalds 127*1da177e4SLinus Torvalds src->sign = !src->sign; 128*1da177e4SLinus Torvalds return fp_fadd(dest, src); 129*1da177e4SLinus Torvalds } 130*1da177e4SLinus Torvalds 131*1da177e4SLinus Torvalds 132*1da177e4SLinus Torvalds struct fp_ext * 133*1da177e4SLinus Torvalds fp_fcmp(struct fp_ext *dest, struct fp_ext *src) 134*1da177e4SLinus Torvalds { 135*1da177e4SLinus Torvalds dprint(PINSTR, "fcmp "); 136*1da177e4SLinus Torvalds 137*1da177e4SLinus Torvalds FPDATA->temp[1] = *dest; 138*1da177e4SLinus Torvalds src->sign = !src->sign; 139*1da177e4SLinus Torvalds return fp_fadd(&FPDATA->temp[1], src); 140*1da177e4SLinus Torvalds } 141*1da177e4SLinus Torvalds 142*1da177e4SLinus Torvalds struct fp_ext * 143*1da177e4SLinus Torvalds fp_ftst(struct fp_ext *dest, struct fp_ext *src) 144*1da177e4SLinus Torvalds { 145*1da177e4SLinus Torvalds dprint(PINSTR, "ftst\n"); 146*1da177e4SLinus Torvalds 147*1da177e4SLinus Torvalds (void)dest; 148*1da177e4SLinus Torvalds 149*1da177e4SLinus Torvalds return src; 150*1da177e4SLinus Torvalds } 151*1da177e4SLinus Torvalds 152*1da177e4SLinus Torvalds struct fp_ext * 153*1da177e4SLinus Torvalds fp_fmul(struct fp_ext *dest, struct fp_ext *src) 154*1da177e4SLinus Torvalds { 155*1da177e4SLinus Torvalds union fp_mant128 temp; 156*1da177e4SLinus Torvalds int exp; 157*1da177e4SLinus Torvalds 158*1da177e4SLinus Torvalds dprint(PINSTR, "fmul\n"); 159*1da177e4SLinus Torvalds 160*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 161*1da177e4SLinus Torvalds 162*1da177e4SLinus Torvalds /* calculate the correct sign now, as it's necessary for infinities */ 163*1da177e4SLinus Torvalds dest->sign = src->sign ^ dest->sign; 164*1da177e4SLinus Torvalds 165*1da177e4SLinus Torvalds /* Handle infinities */ 166*1da177e4SLinus Torvalds if (IS_INF(dest)) { 167*1da177e4SLinus Torvalds if (IS_ZERO(src)) 168*1da177e4SLinus Torvalds fp_set_nan(dest); 169*1da177e4SLinus Torvalds return dest; 170*1da177e4SLinus Torvalds } 171*1da177e4SLinus Torvalds if (IS_INF(src)) { 172*1da177e4SLinus Torvalds if (IS_ZERO(dest)) 173*1da177e4SLinus Torvalds fp_set_nan(dest); 174*1da177e4SLinus Torvalds else 175*1da177e4SLinus Torvalds fp_copy_ext(dest, src); 176*1da177e4SLinus Torvalds return dest; 177*1da177e4SLinus Torvalds } 178*1da177e4SLinus Torvalds 179*1da177e4SLinus Torvalds /* Of course, as we all know, zero * anything = zero. You may 180*1da177e4SLinus Torvalds not have known that it might be a positive or negative 181*1da177e4SLinus Torvalds zero... */ 182*1da177e4SLinus Torvalds if (IS_ZERO(dest) || IS_ZERO(src)) { 183*1da177e4SLinus Torvalds dest->exp = 0; 184*1da177e4SLinus Torvalds dest->mant.m64 = 0; 185*1da177e4SLinus Torvalds dest->lowmant = 0; 186*1da177e4SLinus Torvalds 187*1da177e4SLinus Torvalds return dest; 188*1da177e4SLinus Torvalds } 189*1da177e4SLinus Torvalds 190*1da177e4SLinus Torvalds exp = dest->exp + src->exp - 0x3ffe; 191*1da177e4SLinus Torvalds 192*1da177e4SLinus Torvalds /* shift up the mantissa for denormalized numbers, 193*1da177e4SLinus Torvalds so that the highest bit is set, this makes the 194*1da177e4SLinus Torvalds shift of the result below easier */ 195*1da177e4SLinus Torvalds if ((long)dest->mant.m32[0] >= 0) 196*1da177e4SLinus Torvalds exp -= fp_overnormalize(dest); 197*1da177e4SLinus Torvalds if ((long)src->mant.m32[0] >= 0) 198*1da177e4SLinus Torvalds exp -= fp_overnormalize(src); 199*1da177e4SLinus Torvalds 200*1da177e4SLinus Torvalds /* now, do a 64-bit multiply with expansion */ 201*1da177e4SLinus Torvalds fp_multiplymant(&temp, dest, src); 202*1da177e4SLinus Torvalds 203*1da177e4SLinus Torvalds /* normalize it back to 64 bits and stuff it back into the 204*1da177e4SLinus Torvalds destination struct */ 205*1da177e4SLinus Torvalds if ((long)temp.m32[0] > 0) { 206*1da177e4SLinus Torvalds exp--; 207*1da177e4SLinus Torvalds fp_putmant128(dest, &temp, 1); 208*1da177e4SLinus Torvalds } else 209*1da177e4SLinus Torvalds fp_putmant128(dest, &temp, 0); 210*1da177e4SLinus Torvalds 211*1da177e4SLinus Torvalds if (exp >= 0x7fff) { 212*1da177e4SLinus Torvalds fp_set_ovrflw(dest); 213*1da177e4SLinus Torvalds return dest; 214*1da177e4SLinus Torvalds } 215*1da177e4SLinus Torvalds dest->exp = exp; 216*1da177e4SLinus Torvalds if (exp < 0) { 217*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_UNFL); 218*1da177e4SLinus Torvalds fp_denormalize(dest, -exp); 219*1da177e4SLinus Torvalds } 220*1da177e4SLinus Torvalds 221*1da177e4SLinus Torvalds return dest; 222*1da177e4SLinus Torvalds } 223*1da177e4SLinus Torvalds 224*1da177e4SLinus Torvalds /* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and 225*1da177e4SLinus Torvalds FSGLDIV instructions. 226*1da177e4SLinus Torvalds 227*1da177e4SLinus Torvalds Note that the order of the operands is counter-intuitive: instead 228*1da177e4SLinus Torvalds of src / dest, the result is actually dest / src. */ 229*1da177e4SLinus Torvalds 230*1da177e4SLinus Torvalds struct fp_ext * 231*1da177e4SLinus Torvalds fp_fdiv(struct fp_ext *dest, struct fp_ext *src) 232*1da177e4SLinus Torvalds { 233*1da177e4SLinus Torvalds union fp_mant128 temp; 234*1da177e4SLinus Torvalds int exp; 235*1da177e4SLinus Torvalds 236*1da177e4SLinus Torvalds dprint(PINSTR, "fdiv\n"); 237*1da177e4SLinus Torvalds 238*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 239*1da177e4SLinus Torvalds 240*1da177e4SLinus Torvalds /* calculate the correct sign now, as it's necessary for infinities */ 241*1da177e4SLinus Torvalds dest->sign = src->sign ^ dest->sign; 242*1da177e4SLinus Torvalds 243*1da177e4SLinus Torvalds /* Handle infinities */ 244*1da177e4SLinus Torvalds if (IS_INF(dest)) { 245*1da177e4SLinus Torvalds /* infinity / infinity = NaN (quiet, as always) */ 246*1da177e4SLinus Torvalds if (IS_INF(src)) 247*1da177e4SLinus Torvalds fp_set_nan(dest); 248*1da177e4SLinus Torvalds /* infinity / anything else = infinity (with approprate sign) */ 249*1da177e4SLinus Torvalds return dest; 250*1da177e4SLinus Torvalds } 251*1da177e4SLinus Torvalds if (IS_INF(src)) { 252*1da177e4SLinus Torvalds /* anything / infinity = zero (with appropriate sign) */ 253*1da177e4SLinus Torvalds dest->exp = 0; 254*1da177e4SLinus Torvalds dest->mant.m64 = 0; 255*1da177e4SLinus Torvalds dest->lowmant = 0; 256*1da177e4SLinus Torvalds 257*1da177e4SLinus Torvalds return dest; 258*1da177e4SLinus Torvalds } 259*1da177e4SLinus Torvalds 260*1da177e4SLinus Torvalds /* zeroes */ 261*1da177e4SLinus Torvalds if (IS_ZERO(dest)) { 262*1da177e4SLinus Torvalds /* zero / zero = NaN */ 263*1da177e4SLinus Torvalds if (IS_ZERO(src)) 264*1da177e4SLinus Torvalds fp_set_nan(dest); 265*1da177e4SLinus Torvalds /* zero / anything else = zero */ 266*1da177e4SLinus Torvalds return dest; 267*1da177e4SLinus Torvalds } 268*1da177e4SLinus Torvalds if (IS_ZERO(src)) { 269*1da177e4SLinus Torvalds /* anything / zero = infinity (with appropriate sign) */ 270*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_DZ); 271*1da177e4SLinus Torvalds dest->exp = 0x7fff; 272*1da177e4SLinus Torvalds dest->mant.m64 = 0; 273*1da177e4SLinus Torvalds 274*1da177e4SLinus Torvalds return dest; 275*1da177e4SLinus Torvalds } 276*1da177e4SLinus Torvalds 277*1da177e4SLinus Torvalds exp = dest->exp - src->exp + 0x3fff; 278*1da177e4SLinus Torvalds 279*1da177e4SLinus Torvalds /* shift up the mantissa for denormalized numbers, 280*1da177e4SLinus Torvalds so that the highest bit is set, this makes lots 281*1da177e4SLinus Torvalds of things below easier */ 282*1da177e4SLinus Torvalds if ((long)dest->mant.m32[0] >= 0) 283*1da177e4SLinus Torvalds exp -= fp_overnormalize(dest); 284*1da177e4SLinus Torvalds if ((long)src->mant.m32[0] >= 0) 285*1da177e4SLinus Torvalds exp -= fp_overnormalize(src); 286*1da177e4SLinus Torvalds 287*1da177e4SLinus Torvalds /* now, do the 64-bit divide */ 288*1da177e4SLinus Torvalds fp_dividemant(&temp, dest, src); 289*1da177e4SLinus Torvalds 290*1da177e4SLinus Torvalds /* normalize it back to 64 bits and stuff it back into the 291*1da177e4SLinus Torvalds destination struct */ 292*1da177e4SLinus Torvalds if (!temp.m32[0]) { 293*1da177e4SLinus Torvalds exp--; 294*1da177e4SLinus Torvalds fp_putmant128(dest, &temp, 32); 295*1da177e4SLinus Torvalds } else 296*1da177e4SLinus Torvalds fp_putmant128(dest, &temp, 31); 297*1da177e4SLinus Torvalds 298*1da177e4SLinus Torvalds if (exp >= 0x7fff) { 299*1da177e4SLinus Torvalds fp_set_ovrflw(dest); 300*1da177e4SLinus Torvalds return dest; 301*1da177e4SLinus Torvalds } 302*1da177e4SLinus Torvalds dest->exp = exp; 303*1da177e4SLinus Torvalds if (exp < 0) { 304*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_UNFL); 305*1da177e4SLinus Torvalds fp_denormalize(dest, -exp); 306*1da177e4SLinus Torvalds } 307*1da177e4SLinus Torvalds 308*1da177e4SLinus Torvalds return dest; 309*1da177e4SLinus Torvalds } 310*1da177e4SLinus Torvalds 311*1da177e4SLinus Torvalds struct fp_ext * 312*1da177e4SLinus Torvalds fp_fsglmul(struct fp_ext *dest, struct fp_ext *src) 313*1da177e4SLinus Torvalds { 314*1da177e4SLinus Torvalds int exp; 315*1da177e4SLinus Torvalds 316*1da177e4SLinus Torvalds dprint(PINSTR, "fsglmul\n"); 317*1da177e4SLinus Torvalds 318*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 319*1da177e4SLinus Torvalds 320*1da177e4SLinus Torvalds /* calculate the correct sign now, as it's necessary for infinities */ 321*1da177e4SLinus Torvalds dest->sign = src->sign ^ dest->sign; 322*1da177e4SLinus Torvalds 323*1da177e4SLinus Torvalds /* Handle infinities */ 324*1da177e4SLinus Torvalds if (IS_INF(dest)) { 325*1da177e4SLinus Torvalds if (IS_ZERO(src)) 326*1da177e4SLinus Torvalds fp_set_nan(dest); 327*1da177e4SLinus Torvalds return dest; 328*1da177e4SLinus Torvalds } 329*1da177e4SLinus Torvalds if (IS_INF(src)) { 330*1da177e4SLinus Torvalds if (IS_ZERO(dest)) 331*1da177e4SLinus Torvalds fp_set_nan(dest); 332*1da177e4SLinus Torvalds else 333*1da177e4SLinus Torvalds fp_copy_ext(dest, src); 334*1da177e4SLinus Torvalds return dest; 335*1da177e4SLinus Torvalds } 336*1da177e4SLinus Torvalds 337*1da177e4SLinus Torvalds /* Of course, as we all know, zero * anything = zero. You may 338*1da177e4SLinus Torvalds not have known that it might be a positive or negative 339*1da177e4SLinus Torvalds zero... */ 340*1da177e4SLinus Torvalds if (IS_ZERO(dest) || IS_ZERO(src)) { 341*1da177e4SLinus Torvalds dest->exp = 0; 342*1da177e4SLinus Torvalds dest->mant.m64 = 0; 343*1da177e4SLinus Torvalds dest->lowmant = 0; 344*1da177e4SLinus Torvalds 345*1da177e4SLinus Torvalds return dest; 346*1da177e4SLinus Torvalds } 347*1da177e4SLinus Torvalds 348*1da177e4SLinus Torvalds exp = dest->exp + src->exp - 0x3ffe; 349*1da177e4SLinus Torvalds 350*1da177e4SLinus Torvalds /* do a 32-bit multiply */ 351*1da177e4SLinus Torvalds fp_mul64(dest->mant.m32[0], dest->mant.m32[1], 352*1da177e4SLinus Torvalds dest->mant.m32[0] & 0xffffff00, 353*1da177e4SLinus Torvalds src->mant.m32[0] & 0xffffff00); 354*1da177e4SLinus Torvalds 355*1da177e4SLinus Torvalds if (exp >= 0x7fff) { 356*1da177e4SLinus Torvalds fp_set_ovrflw(dest); 357*1da177e4SLinus Torvalds return dest; 358*1da177e4SLinus Torvalds } 359*1da177e4SLinus Torvalds dest->exp = exp; 360*1da177e4SLinus Torvalds if (exp < 0) { 361*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_UNFL); 362*1da177e4SLinus Torvalds fp_denormalize(dest, -exp); 363*1da177e4SLinus Torvalds } 364*1da177e4SLinus Torvalds 365*1da177e4SLinus Torvalds return dest; 366*1da177e4SLinus Torvalds } 367*1da177e4SLinus Torvalds 368*1da177e4SLinus Torvalds struct fp_ext * 369*1da177e4SLinus Torvalds fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src) 370*1da177e4SLinus Torvalds { 371*1da177e4SLinus Torvalds int exp; 372*1da177e4SLinus Torvalds unsigned long quot, rem; 373*1da177e4SLinus Torvalds 374*1da177e4SLinus Torvalds dprint(PINSTR, "fsgldiv\n"); 375*1da177e4SLinus Torvalds 376*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 377*1da177e4SLinus Torvalds 378*1da177e4SLinus Torvalds /* calculate the correct sign now, as it's necessary for infinities */ 379*1da177e4SLinus Torvalds dest->sign = src->sign ^ dest->sign; 380*1da177e4SLinus Torvalds 381*1da177e4SLinus Torvalds /* Handle infinities */ 382*1da177e4SLinus Torvalds if (IS_INF(dest)) { 383*1da177e4SLinus Torvalds /* infinity / infinity = NaN (quiet, as always) */ 384*1da177e4SLinus Torvalds if (IS_INF(src)) 385*1da177e4SLinus Torvalds fp_set_nan(dest); 386*1da177e4SLinus Torvalds /* infinity / anything else = infinity (with approprate sign) */ 387*1da177e4SLinus Torvalds return dest; 388*1da177e4SLinus Torvalds } 389*1da177e4SLinus Torvalds if (IS_INF(src)) { 390*1da177e4SLinus Torvalds /* anything / infinity = zero (with appropriate sign) */ 391*1da177e4SLinus Torvalds dest->exp = 0; 392*1da177e4SLinus Torvalds dest->mant.m64 = 0; 393*1da177e4SLinus Torvalds dest->lowmant = 0; 394*1da177e4SLinus Torvalds 395*1da177e4SLinus Torvalds return dest; 396*1da177e4SLinus Torvalds } 397*1da177e4SLinus Torvalds 398*1da177e4SLinus Torvalds /* zeroes */ 399*1da177e4SLinus Torvalds if (IS_ZERO(dest)) { 400*1da177e4SLinus Torvalds /* zero / zero = NaN */ 401*1da177e4SLinus Torvalds if (IS_ZERO(src)) 402*1da177e4SLinus Torvalds fp_set_nan(dest); 403*1da177e4SLinus Torvalds /* zero / anything else = zero */ 404*1da177e4SLinus Torvalds return dest; 405*1da177e4SLinus Torvalds } 406*1da177e4SLinus Torvalds if (IS_ZERO(src)) { 407*1da177e4SLinus Torvalds /* anything / zero = infinity (with appropriate sign) */ 408*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_DZ); 409*1da177e4SLinus Torvalds dest->exp = 0x7fff; 410*1da177e4SLinus Torvalds dest->mant.m64 = 0; 411*1da177e4SLinus Torvalds 412*1da177e4SLinus Torvalds return dest; 413*1da177e4SLinus Torvalds } 414*1da177e4SLinus Torvalds 415*1da177e4SLinus Torvalds exp = dest->exp - src->exp + 0x3fff; 416*1da177e4SLinus Torvalds 417*1da177e4SLinus Torvalds dest->mant.m32[0] &= 0xffffff00; 418*1da177e4SLinus Torvalds src->mant.m32[0] &= 0xffffff00; 419*1da177e4SLinus Torvalds 420*1da177e4SLinus Torvalds /* do the 32-bit divide */ 421*1da177e4SLinus Torvalds if (dest->mant.m32[0] >= src->mant.m32[0]) { 422*1da177e4SLinus Torvalds fp_sub64(dest->mant, src->mant); 423*1da177e4SLinus Torvalds fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); 424*1da177e4SLinus Torvalds dest->mant.m32[0] = 0x80000000 | (quot >> 1); 425*1da177e4SLinus Torvalds dest->mant.m32[1] = (quot & 1) | rem; /* only for rounding */ 426*1da177e4SLinus Torvalds } else { 427*1da177e4SLinus Torvalds fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); 428*1da177e4SLinus Torvalds dest->mant.m32[0] = quot; 429*1da177e4SLinus Torvalds dest->mant.m32[1] = rem; /* only for rounding */ 430*1da177e4SLinus Torvalds exp--; 431*1da177e4SLinus Torvalds } 432*1da177e4SLinus Torvalds 433*1da177e4SLinus Torvalds if (exp >= 0x7fff) { 434*1da177e4SLinus Torvalds fp_set_ovrflw(dest); 435*1da177e4SLinus Torvalds return dest; 436*1da177e4SLinus Torvalds } 437*1da177e4SLinus Torvalds dest->exp = exp; 438*1da177e4SLinus Torvalds if (exp < 0) { 439*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_UNFL); 440*1da177e4SLinus Torvalds fp_denormalize(dest, -exp); 441*1da177e4SLinus Torvalds } 442*1da177e4SLinus Torvalds 443*1da177e4SLinus Torvalds return dest; 444*1da177e4SLinus Torvalds } 445*1da177e4SLinus Torvalds 446*1da177e4SLinus Torvalds /* fp_roundint: Internal rounding function for use by several of these 447*1da177e4SLinus Torvalds emulated instructions. 448*1da177e4SLinus Torvalds 449*1da177e4SLinus Torvalds This one rounds off the fractional part using the rounding mode 450*1da177e4SLinus Torvalds specified. */ 451*1da177e4SLinus Torvalds 452*1da177e4SLinus Torvalds static void fp_roundint(struct fp_ext *dest, int mode) 453*1da177e4SLinus Torvalds { 454*1da177e4SLinus Torvalds union fp_mant64 oldmant; 455*1da177e4SLinus Torvalds unsigned long mask; 456*1da177e4SLinus Torvalds 457*1da177e4SLinus Torvalds if (!fp_normalize_ext(dest)) 458*1da177e4SLinus Torvalds return; 459*1da177e4SLinus Torvalds 460*1da177e4SLinus Torvalds /* infinities and zeroes */ 461*1da177e4SLinus Torvalds if (IS_INF(dest) || IS_ZERO(dest)) 462*1da177e4SLinus Torvalds return; 463*1da177e4SLinus Torvalds 464*1da177e4SLinus Torvalds /* first truncate the lower bits */ 465*1da177e4SLinus Torvalds oldmant = dest->mant; 466*1da177e4SLinus Torvalds switch (dest->exp) { 467*1da177e4SLinus Torvalds case 0 ... 0x3ffe: 468*1da177e4SLinus Torvalds dest->mant.m64 = 0; 469*1da177e4SLinus Torvalds break; 470*1da177e4SLinus Torvalds case 0x3fff ... 0x401e: 471*1da177e4SLinus Torvalds dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp); 472*1da177e4SLinus Torvalds dest->mant.m32[1] = 0; 473*1da177e4SLinus Torvalds if (oldmant.m64 == dest->mant.m64) 474*1da177e4SLinus Torvalds return; 475*1da177e4SLinus Torvalds break; 476*1da177e4SLinus Torvalds case 0x401f ... 0x403e: 477*1da177e4SLinus Torvalds dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp); 478*1da177e4SLinus Torvalds if (oldmant.m32[1] == dest->mant.m32[1]) 479*1da177e4SLinus Torvalds return; 480*1da177e4SLinus Torvalds break; 481*1da177e4SLinus Torvalds default: 482*1da177e4SLinus Torvalds return; 483*1da177e4SLinus Torvalds } 484*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_INEX2); 485*1da177e4SLinus Torvalds 486*1da177e4SLinus Torvalds /* We might want to normalize upwards here... however, since 487*1da177e4SLinus Torvalds we know that this is only called on the output of fp_fdiv, 488*1da177e4SLinus Torvalds or with the input to fp_fint or fp_fintrz, and the inputs 489*1da177e4SLinus Torvalds to all these functions are either normal or denormalized 490*1da177e4SLinus Torvalds (no subnormals allowed!), there's really no need. 491*1da177e4SLinus Torvalds 492*1da177e4SLinus Torvalds In the case of fp_fdiv, observe that 0x80000000 / 0xffff = 493*1da177e4SLinus Torvalds 0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the 494*1da177e4SLinus Torvalds smallest possible normal dividend and the largest possible normal 495*1da177e4SLinus Torvalds divisor will still produce a normal quotient, therefore, (normal 496*1da177e4SLinus Torvalds << 64) / normal is normal in all cases) */ 497*1da177e4SLinus Torvalds 498*1da177e4SLinus Torvalds switch (mode) { 499*1da177e4SLinus Torvalds case FPCR_ROUND_RN: 500*1da177e4SLinus Torvalds switch (dest->exp) { 501*1da177e4SLinus Torvalds case 0 ... 0x3ffd: 502*1da177e4SLinus Torvalds return; 503*1da177e4SLinus Torvalds case 0x3ffe: 504*1da177e4SLinus Torvalds /* As noted above, the input is always normal, so the 505*1da177e4SLinus Torvalds guard bit (bit 63) is always set. therefore, the 506*1da177e4SLinus Torvalds only case in which we will NOT round to 1.0 is when 507*1da177e4SLinus Torvalds the input is exactly 0.5. */ 508*1da177e4SLinus Torvalds if (oldmant.m64 == (1ULL << 63)) 509*1da177e4SLinus Torvalds return; 510*1da177e4SLinus Torvalds break; 511*1da177e4SLinus Torvalds case 0x3fff ... 0x401d: 512*1da177e4SLinus Torvalds mask = 1 << (0x401d - dest->exp); 513*1da177e4SLinus Torvalds if (!(oldmant.m32[0] & mask)) 514*1da177e4SLinus Torvalds return; 515*1da177e4SLinus Torvalds if (oldmant.m32[0] & (mask << 1)) 516*1da177e4SLinus Torvalds break; 517*1da177e4SLinus Torvalds if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) && 518*1da177e4SLinus Torvalds !oldmant.m32[1]) 519*1da177e4SLinus Torvalds return; 520*1da177e4SLinus Torvalds break; 521*1da177e4SLinus Torvalds case 0x401e: 522*1da177e4SLinus Torvalds if (!(oldmant.m32[1] >= 0)) 523*1da177e4SLinus Torvalds return; 524*1da177e4SLinus Torvalds if (oldmant.m32[0] & 1) 525*1da177e4SLinus Torvalds break; 526*1da177e4SLinus Torvalds if (!(oldmant.m32[1] << 1)) 527*1da177e4SLinus Torvalds return; 528*1da177e4SLinus Torvalds break; 529*1da177e4SLinus Torvalds case 0x401f ... 0x403d: 530*1da177e4SLinus Torvalds mask = 1 << (0x403d - dest->exp); 531*1da177e4SLinus Torvalds if (!(oldmant.m32[1] & mask)) 532*1da177e4SLinus Torvalds return; 533*1da177e4SLinus Torvalds if (oldmant.m32[1] & (mask << 1)) 534*1da177e4SLinus Torvalds break; 535*1da177e4SLinus Torvalds if (!(oldmant.m32[1] << (dest->exp - 0x401d))) 536*1da177e4SLinus Torvalds return; 537*1da177e4SLinus Torvalds break; 538*1da177e4SLinus Torvalds default: 539*1da177e4SLinus Torvalds return; 540*1da177e4SLinus Torvalds } 541*1da177e4SLinus Torvalds break; 542*1da177e4SLinus Torvalds case FPCR_ROUND_RZ: 543*1da177e4SLinus Torvalds return; 544*1da177e4SLinus Torvalds default: 545*1da177e4SLinus Torvalds if (dest->sign ^ (mode - FPCR_ROUND_RM)) 546*1da177e4SLinus Torvalds break; 547*1da177e4SLinus Torvalds return; 548*1da177e4SLinus Torvalds } 549*1da177e4SLinus Torvalds 550*1da177e4SLinus Torvalds switch (dest->exp) { 551*1da177e4SLinus Torvalds case 0 ... 0x3ffe: 552*1da177e4SLinus Torvalds dest->exp = 0x3fff; 553*1da177e4SLinus Torvalds dest->mant.m64 = 1ULL << 63; 554*1da177e4SLinus Torvalds break; 555*1da177e4SLinus Torvalds case 0x3fff ... 0x401e: 556*1da177e4SLinus Torvalds mask = 1 << (0x401e - dest->exp); 557*1da177e4SLinus Torvalds if (dest->mant.m32[0] += mask) 558*1da177e4SLinus Torvalds break; 559*1da177e4SLinus Torvalds dest->mant.m32[0] = 0x80000000; 560*1da177e4SLinus Torvalds dest->exp++; 561*1da177e4SLinus Torvalds break; 562*1da177e4SLinus Torvalds case 0x401f ... 0x403e: 563*1da177e4SLinus Torvalds mask = 1 << (0x403e - dest->exp); 564*1da177e4SLinus Torvalds if (dest->mant.m32[1] += mask) 565*1da177e4SLinus Torvalds break; 566*1da177e4SLinus Torvalds if (dest->mant.m32[0] += 1) 567*1da177e4SLinus Torvalds break; 568*1da177e4SLinus Torvalds dest->mant.m32[0] = 0x80000000; 569*1da177e4SLinus Torvalds dest->exp++; 570*1da177e4SLinus Torvalds break; 571*1da177e4SLinus Torvalds } 572*1da177e4SLinus Torvalds } 573*1da177e4SLinus Torvalds 574*1da177e4SLinus Torvalds /* modrem_kernel: Implementation of the FREM and FMOD instructions 575*1da177e4SLinus Torvalds (which are exactly the same, except for the rounding used on the 576*1da177e4SLinus Torvalds intermediate value) */ 577*1da177e4SLinus Torvalds 578*1da177e4SLinus Torvalds static struct fp_ext * 579*1da177e4SLinus Torvalds modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode) 580*1da177e4SLinus Torvalds { 581*1da177e4SLinus Torvalds struct fp_ext tmp; 582*1da177e4SLinus Torvalds 583*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 584*1da177e4SLinus Torvalds 585*1da177e4SLinus Torvalds /* Infinities and zeros */ 586*1da177e4SLinus Torvalds if (IS_INF(dest) || IS_ZERO(src)) { 587*1da177e4SLinus Torvalds fp_set_nan(dest); 588*1da177e4SLinus Torvalds return dest; 589*1da177e4SLinus Torvalds } 590*1da177e4SLinus Torvalds if (IS_ZERO(dest) || IS_INF(src)) 591*1da177e4SLinus Torvalds return dest; 592*1da177e4SLinus Torvalds 593*1da177e4SLinus Torvalds /* FIXME: there is almost certainly a smarter way to do this */ 594*1da177e4SLinus Torvalds fp_copy_ext(&tmp, dest); 595*1da177e4SLinus Torvalds fp_fdiv(&tmp, src); /* NOTE: src might be modified */ 596*1da177e4SLinus Torvalds fp_roundint(&tmp, mode); 597*1da177e4SLinus Torvalds fp_fmul(&tmp, src); 598*1da177e4SLinus Torvalds fp_fsub(dest, &tmp); 599*1da177e4SLinus Torvalds 600*1da177e4SLinus Torvalds /* set the quotient byte */ 601*1da177e4SLinus Torvalds fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7)); 602*1da177e4SLinus Torvalds return dest; 603*1da177e4SLinus Torvalds } 604*1da177e4SLinus Torvalds 605*1da177e4SLinus Torvalds /* fp_fmod: Implements the kernel of the FMOD instruction. 606*1da177e4SLinus Torvalds 607*1da177e4SLinus Torvalds Again, the argument order is backwards. The result, as defined in 608*1da177e4SLinus Torvalds the Motorola manuals, is: 609*1da177e4SLinus Torvalds 610*1da177e4SLinus Torvalds fmod(src,dest) = (dest - (src * floor(dest / src))) */ 611*1da177e4SLinus Torvalds 612*1da177e4SLinus Torvalds struct fp_ext * 613*1da177e4SLinus Torvalds fp_fmod(struct fp_ext *dest, struct fp_ext *src) 614*1da177e4SLinus Torvalds { 615*1da177e4SLinus Torvalds dprint(PINSTR, "fmod\n"); 616*1da177e4SLinus Torvalds return modrem_kernel(dest, src, FPCR_ROUND_RZ); 617*1da177e4SLinus Torvalds } 618*1da177e4SLinus Torvalds 619*1da177e4SLinus Torvalds /* fp_frem: Implements the kernel of the FREM instruction. 620*1da177e4SLinus Torvalds 621*1da177e4SLinus Torvalds frem(src,dest) = (dest - (src * round(dest / src))) 622*1da177e4SLinus Torvalds */ 623*1da177e4SLinus Torvalds 624*1da177e4SLinus Torvalds struct fp_ext * 625*1da177e4SLinus Torvalds fp_frem(struct fp_ext *dest, struct fp_ext *src) 626*1da177e4SLinus Torvalds { 627*1da177e4SLinus Torvalds dprint(PINSTR, "frem\n"); 628*1da177e4SLinus Torvalds return modrem_kernel(dest, src, FPCR_ROUND_RN); 629*1da177e4SLinus Torvalds } 630*1da177e4SLinus Torvalds 631*1da177e4SLinus Torvalds struct fp_ext * 632*1da177e4SLinus Torvalds fp_fint(struct fp_ext *dest, struct fp_ext *src) 633*1da177e4SLinus Torvalds { 634*1da177e4SLinus Torvalds dprint(PINSTR, "fint\n"); 635*1da177e4SLinus Torvalds 636*1da177e4SLinus Torvalds fp_copy_ext(dest, src); 637*1da177e4SLinus Torvalds 638*1da177e4SLinus Torvalds fp_roundint(dest, FPDATA->rnd); 639*1da177e4SLinus Torvalds 640*1da177e4SLinus Torvalds return dest; 641*1da177e4SLinus Torvalds } 642*1da177e4SLinus Torvalds 643*1da177e4SLinus Torvalds struct fp_ext * 644*1da177e4SLinus Torvalds fp_fintrz(struct fp_ext *dest, struct fp_ext *src) 645*1da177e4SLinus Torvalds { 646*1da177e4SLinus Torvalds dprint(PINSTR, "fintrz\n"); 647*1da177e4SLinus Torvalds 648*1da177e4SLinus Torvalds fp_copy_ext(dest, src); 649*1da177e4SLinus Torvalds 650*1da177e4SLinus Torvalds fp_roundint(dest, FPCR_ROUND_RZ); 651*1da177e4SLinus Torvalds 652*1da177e4SLinus Torvalds return dest; 653*1da177e4SLinus Torvalds } 654*1da177e4SLinus Torvalds 655*1da177e4SLinus Torvalds struct fp_ext * 656*1da177e4SLinus Torvalds fp_fscale(struct fp_ext *dest, struct fp_ext *src) 657*1da177e4SLinus Torvalds { 658*1da177e4SLinus Torvalds int scale, oldround; 659*1da177e4SLinus Torvalds 660*1da177e4SLinus Torvalds dprint(PINSTR, "fscale\n"); 661*1da177e4SLinus Torvalds 662*1da177e4SLinus Torvalds fp_dyadic_check(dest, src); 663*1da177e4SLinus Torvalds 664*1da177e4SLinus Torvalds /* Infinities */ 665*1da177e4SLinus Torvalds if (IS_INF(src)) { 666*1da177e4SLinus Torvalds fp_set_nan(dest); 667*1da177e4SLinus Torvalds return dest; 668*1da177e4SLinus Torvalds } 669*1da177e4SLinus Torvalds if (IS_INF(dest)) 670*1da177e4SLinus Torvalds return dest; 671*1da177e4SLinus Torvalds 672*1da177e4SLinus Torvalds /* zeroes */ 673*1da177e4SLinus Torvalds if (IS_ZERO(src) || IS_ZERO(dest)) 674*1da177e4SLinus Torvalds return dest; 675*1da177e4SLinus Torvalds 676*1da177e4SLinus Torvalds /* Source exponent out of range */ 677*1da177e4SLinus Torvalds if (src->exp >= 0x400c) { 678*1da177e4SLinus Torvalds fp_set_ovrflw(dest); 679*1da177e4SLinus Torvalds return dest; 680*1da177e4SLinus Torvalds } 681*1da177e4SLinus Torvalds 682*1da177e4SLinus Torvalds /* src must be rounded with round to zero. */ 683*1da177e4SLinus Torvalds oldround = FPDATA->rnd; 684*1da177e4SLinus Torvalds FPDATA->rnd = FPCR_ROUND_RZ; 685*1da177e4SLinus Torvalds scale = fp_conv_ext2long(src); 686*1da177e4SLinus Torvalds FPDATA->rnd = oldround; 687*1da177e4SLinus Torvalds 688*1da177e4SLinus Torvalds /* new exponent */ 689*1da177e4SLinus Torvalds scale += dest->exp; 690*1da177e4SLinus Torvalds 691*1da177e4SLinus Torvalds if (scale >= 0x7fff) { 692*1da177e4SLinus Torvalds fp_set_ovrflw(dest); 693*1da177e4SLinus Torvalds } else if (scale <= 0) { 694*1da177e4SLinus Torvalds fp_set_sr(FPSR_EXC_UNFL); 695*1da177e4SLinus Torvalds fp_denormalize(dest, -scale); 696*1da177e4SLinus Torvalds } else 697*1da177e4SLinus Torvalds dest->exp = scale; 698*1da177e4SLinus Torvalds 699*1da177e4SLinus Torvalds return dest; 700*1da177e4SLinus Torvalds } 701*1da177e4SLinus Torvalds 702