/*
fp_arith.c: floating-point math routines for the Linux-m68k
floating point emulator.
Copyright (c) 1998-1999 David Huggins-Daines.
Somewhat based on the AlphaLinux floating point emulator, by David
Mosberger-Tang.
You may copy, modify, and redistribute this file under the terms of
the GNU General Public License, version 2, or any later version, at
your convenience.
*/
#include "fp_emu.h"
#include "multi_arith.h"
#include "fp_arith.h"
const struct fp_ext fp_QNaN =
{
.exp = 0x7fff,
.mant = { .m64 = ~0 }
};
const struct fp_ext fp_Inf =
{
.exp = 0x7fff,
};
/* let's start with the easy ones */
struct fp_ext *
fp_fabs(struct fp_ext *dest, struct fp_ext *src)
{
dprint(PINSTR, "fabs\n");
fp_monadic_check(dest, src);
dest->sign = 0;
return dest;
}
struct fp_ext *
fp_fneg(struct fp_ext *dest, struct fp_ext *src)
{
dprint(PINSTR, "fneg\n");
fp_monadic_check(dest, src);
dest->sign = !dest->sign;
return dest;
}
/* Now, the slightly harder ones */
/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB,
FDSUB, and FCMP instructions. */
struct fp_ext *
fp_fadd(struct fp_ext *dest, struct fp_ext *src)
{
int diff;
dprint(PINSTR, "fadd\n");
fp_dyadic_check(dest, src);
if (IS_INF(dest)) {
/* infinity - infinity == NaN */
if (IS_INF(src) && (src->sign != dest->sign))
fp_set_nan(dest);
return dest;
}
if (IS_INF(src)) {
fp_copy_ext(dest, src);
return dest;
}
if (IS_ZERO(dest)) {
if (IS_ZERO(src)) {
if (src->sign != dest->sign) {
if (FPDATA->rnd == FPCR_ROUND_RM)
dest->sign = 1;
else
dest->sign = 0;
}
} else
fp_copy_ext(dest, src);
return dest;
}
dest->lowmant = src->lowmant = 0;
if ((diff = dest->exp - src->exp) > 0)
fp_denormalize(src, diff);
else if ((diff = -diff) > 0)
fp_denormalize(dest, diff);
if (dest->sign == src->sign) {
if (fp_addmant(dest, src))
if (!fp_addcarry(dest))
return dest;
} else {
if (dest->mant.m64 < src->mant.m64) {
fp_submant(dest, src, dest);
dest->sign = !dest->sign;
} else
fp_submant(dest, dest, src);
}
return dest;
}
/* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB
instructions.
Remember that the arguments are in assembler-syntax order! */
struct fp_ext *
fp_fsub(struct fp_ext *dest, struct fp_ext *src)
{
dprint(PINSTR, "fsub ");
src->sign = !src->sign;
return fp_fadd(dest, src);
}
struct fp_ext *
fp_fcmp(struct fp_ext *dest, struct fp_ext *src)
{
dprint(PINSTR, "fcmp ");
FPDATA->temp[1] = *dest;
src->sign = !src->sign;
return fp_fadd(&FPDATA->temp[1], src);
}
struct fp_ext *
fp_ftst(struct fp_ext *dest, struct fp_ext *src)
{
dprint(PINSTR, "ftst\n");
(void)dest;
return src;
}
struct fp_ext *
fp_fmul(struct fp_ext *dest, struct fp_ext *src)
{
union fp_mant128 temp;
int exp;
dprint(PINSTR, "fmul\n");
fp_dyadic_check(dest, src);
/* calculate the correct sign now, as it's necessary for infinities */
dest->sign = src->sign ^ dest->sign;
/* Handle infinities */
if (IS_INF(dest)) {
if (IS_ZERO(src))
fp_set_nan(dest);
return dest;
}
if (IS_INF(src)) {
if (IS_ZERO(dest))
fp_set_nan(dest);
else
fp_copy_ext(dest, src);
return dest;
}
/* Of course, as we all know, zero * anything = zero. You may
not have known that it might be a positive or negative
zero... */
if (IS_ZERO(dest) || IS_ZERO(src)) {
dest->exp = 0;
dest->mant.m64 = 0;
dest->lowmant = 0;
return dest;
}
exp = dest->exp + src->exp - 0x3ffe;
/* shift up the mantissa for denormalized numbers,
so that the highest bit is set, this makes the
shift of the result below easier */
if ((long)dest->mant.m32[0] >= 0)
exp -= fp_overnormalize(dest);
if ((long)src->mant.m32[0] >= 0)
exp -= fp_overnormalize(src);
/* now, do a 64-bit multiply with expansion */
fp_multiplymant(&temp, dest, src);
/* normalize it back to 64 bits and stuff it back into the
destination struct */
if ((long)temp.m32[0] > 0) {
exp--;
fp_putmant128(dest, &temp, 1);
} else
fp_putmant128(dest, &temp, 0);
if (exp >= 0x7fff) {
fp_set_ovrflw(dest);
return dest;
}
dest->exp = exp;
if (exp < 0) {
fp_set_sr(FPSR_EXC_UNFL);
fp_denormalize(dest, -exp);
}
return dest;
}
/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and
FSGLDIV instructions.
Note that the order of the operands is counter-intuitive: instead
of src / dest, the result is actually dest / src. */
struct fp_ext *
fp_fdiv(struct fp_ext *dest, struct fp_ext *src)
{
union fp_mant128 temp;
int exp;
dprint(PINSTR, "fdiv\n");
fp_dyadic_check(dest, src);
/* calculate the correct sign now, as it's necessary for infinities */
dest->sign = src->sign ^ dest->sign;
/* Handle infinities */
if (IS_INF(dest)) {
/* infinity / infinity = NaN (quiet, as always) */
if (IS_INF(src))
fp_set_nan(dest);
/* infinity / anything else = infinity (with approprate sign) */
return dest;
}
if (IS_INF(src)) {
/* anything / infinity = zero (with appropriate sign) */
dest->exp = 0;
dest->mant.m64 = 0;
dest->lowmant = 0;
return dest;
}
/* zeroes */
if (IS_ZERO(dest)) {
/* zero / zero = NaN */
if (IS_ZERO(src))
fp_set_nan(dest);
/* zero / anything else = zero */
return dest;
}
if (IS_ZERO(src)) {
/* anything / zero = infinity (with appropriate sign) */
fp_set_sr(FPSR_EXC_DZ);
dest->exp = 0x7fff;
dest->mant.m64 = 0;
return dest;
}
exp = dest->exp - src->exp + 0x3fff;
/* shift up the mantissa for denormalized numbers,
so that the highest bit is set, this makes lots
of things below easier */
if ((long)dest->mant.m32[0] >= 0)
exp -= fp_overnormalize(dest);
if ((long)src->mant.m32[0] >= 0)
exp -= fp_overnormalize(src);
/* now, do the 64-bit divide */
fp_dividemant(&temp, dest, src);
/* normalize it back to 64 bits and stuff it back into the
destination struct */
if (!temp.m32[0]) {
exp--;
fp_putmant128(dest, &temp, 32);
} else
fp_putmant128(dest, &temp, 31);
if (exp >= 0x7fff) {
fp_set_ovrflw(dest);
return dest;
}
dest->exp = exp;
if (exp < 0) {
fp_set_sr(FPSR_EXC_UNFL);
fp_denormalize(dest, -exp);
}
return dest;
}
struct fp_ext *
fp_fsglmul(struct fp_ext *dest, struct fp_ext *src)
{
int exp;
dprint(PINSTR, "fsglmul\n");
fp_dyadic_check(dest, src);
/* calculate the correct sign now, as it's necessary for infinities */
dest->sign = src->sign ^ dest->sign;
/* Handle infinities */
if (IS_INF(dest)) {
if (IS_ZERO(src))
fp_set_nan(dest);
return dest;
}
if (IS_INF(src)) {
if (IS_ZERO(dest))
fp_set_nan(dest);
else
fp_copy_ext(dest, src);
return dest;
}
/* Of course, as we all know, zero * anything = zero. You may
not have known that it might be a positive or negative
zero... */
if (IS_ZERO(dest) || IS_ZERO(src)) {
dest->exp = 0;
dest->mant.m64 = 0;
dest->lowmant = 0;
return dest;
}
exp = dest->exp + src->exp - 0x3ffe;
/* do a 32-bit multiply */
fp_mul64(dest->mant.m32[0], dest->mant.m32[1],
dest->mant.m32[0] & 0xffffff00,
src->mant.m32[0] & 0xffffff00);
if (exp >= 0x7fff) {
fp_set_ovrflw(dest);
return dest;
}
dest->exp = exp;
if (exp < 0) {
fp_set_sr(FPSR_EXC_UNFL);
fp_denormalize(dest, -exp);
}
return dest;
}
struct fp_ext *
fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src)
{
int exp;
unsigned long quot, rem;
dprint(PINSTR, "fsgldiv\n");
fp_dyadic_check(dest, src);
/* calculate the correct sign now, as it's necessary for infinities */
dest->sign = src->sign ^ dest->sign;
/* Handle infinities */
if (IS_INF(dest)) {
/* infinity / infinity = NaN (quiet, as always) */
if (IS_INF(src))
fp_set_nan(dest);
/* infinity / anything else = infinity (with approprate sign) */
return dest;
}
if (IS_INF(src)) {
/* anything / infinity = zero (with appropriate sign) */
dest->exp = 0;
dest->mant.m64 = 0;
dest->lowmant = 0;
return dest;
}
/* zeroes */
if (IS_ZERO(dest)) {
/* z