18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci   fp_arith.c: floating-point math routines for the Linux-m68k
58c2ecf20Sopenharmony_ci   floating point emulator.
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci   Copyright (c) 1998-1999 David Huggins-Daines.
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci   Somewhat based on the AlphaLinux floating point emulator, by David
108c2ecf20Sopenharmony_ci   Mosberger-Tang.
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "fp_emu.h"
158c2ecf20Sopenharmony_ci#include "multi_arith.h"
168c2ecf20Sopenharmony_ci#include "fp_arith.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciconst struct fp_ext fp_QNaN =
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	.exp = 0x7fff,
218c2ecf20Sopenharmony_ci	.mant = { .m64 = ~0 }
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciconst struct fp_ext fp_Inf =
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	.exp = 0x7fff,
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* let's start with the easy ones */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct fp_ext *
328c2ecf20Sopenharmony_cifp_fabs(struct fp_ext *dest, struct fp_ext *src)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	dprint(PINSTR, "fabs\n");
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	fp_monadic_check(dest, src);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	dest->sign = 0;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return dest;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct fp_ext *
448c2ecf20Sopenharmony_cifp_fneg(struct fp_ext *dest, struct fp_ext *src)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	dprint(PINSTR, "fneg\n");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	fp_monadic_check(dest, src);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	dest->sign = !dest->sign;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return dest;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* Now, the slightly harder ones */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB,
588c2ecf20Sopenharmony_ci   FDSUB, and FCMP instructions. */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct fp_ext *
618c2ecf20Sopenharmony_cifp_fadd(struct fp_ext *dest, struct fp_ext *src)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int diff;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	dprint(PINSTR, "fadd\n");
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (IS_INF(dest)) {
708c2ecf20Sopenharmony_ci		/* infinity - infinity == NaN */
718c2ecf20Sopenharmony_ci		if (IS_INF(src) && (src->sign != dest->sign))
728c2ecf20Sopenharmony_ci			fp_set_nan(dest);
738c2ecf20Sopenharmony_ci		return dest;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	if (IS_INF(src)) {
768c2ecf20Sopenharmony_ci		fp_copy_ext(dest, src);
778c2ecf20Sopenharmony_ci		return dest;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (IS_ZERO(dest)) {
818c2ecf20Sopenharmony_ci		if (IS_ZERO(src)) {
828c2ecf20Sopenharmony_ci			if (src->sign != dest->sign) {
838c2ecf20Sopenharmony_ci				if (FPDATA->rnd == FPCR_ROUND_RM)
848c2ecf20Sopenharmony_ci					dest->sign = 1;
858c2ecf20Sopenharmony_ci				else
868c2ecf20Sopenharmony_ci					dest->sign = 0;
878c2ecf20Sopenharmony_ci			}
888c2ecf20Sopenharmony_ci		} else
898c2ecf20Sopenharmony_ci			fp_copy_ext(dest, src);
908c2ecf20Sopenharmony_ci		return dest;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	dest->lowmant = src->lowmant = 0;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if ((diff = dest->exp - src->exp) > 0)
968c2ecf20Sopenharmony_ci		fp_denormalize(src, diff);
978c2ecf20Sopenharmony_ci	else if ((diff = -diff) > 0)
988c2ecf20Sopenharmony_ci		fp_denormalize(dest, diff);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (dest->sign == src->sign) {
1018c2ecf20Sopenharmony_ci		if (fp_addmant(dest, src))
1028c2ecf20Sopenharmony_ci			if (!fp_addcarry(dest))
1038c2ecf20Sopenharmony_ci				return dest;
1048c2ecf20Sopenharmony_ci	} else {
1058c2ecf20Sopenharmony_ci		if (dest->mant.m64 < src->mant.m64) {
1068c2ecf20Sopenharmony_ci			fp_submant(dest, src, dest);
1078c2ecf20Sopenharmony_ci			dest->sign = !dest->sign;
1088c2ecf20Sopenharmony_ci		} else
1098c2ecf20Sopenharmony_ci			fp_submant(dest, dest, src);
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return dest;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB
1168c2ecf20Sopenharmony_ci   instructions.
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci   Remember that the arguments are in assembler-syntax order! */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistruct fp_ext *
1218c2ecf20Sopenharmony_cifp_fsub(struct fp_ext *dest, struct fp_ext *src)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	dprint(PINSTR, "fsub ");
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	src->sign = !src->sign;
1268c2ecf20Sopenharmony_ci	return fp_fadd(dest, src);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistruct fp_ext *
1318c2ecf20Sopenharmony_cifp_fcmp(struct fp_ext *dest, struct fp_ext *src)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	dprint(PINSTR, "fcmp ");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	FPDATA->temp[1] = *dest;
1368c2ecf20Sopenharmony_ci	src->sign = !src->sign;
1378c2ecf20Sopenharmony_ci	return fp_fadd(&FPDATA->temp[1], src);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct fp_ext *
1418c2ecf20Sopenharmony_cifp_ftst(struct fp_ext *dest, struct fp_ext *src)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	dprint(PINSTR, "ftst\n");
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	(void)dest;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return src;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistruct fp_ext *
1518c2ecf20Sopenharmony_cifp_fmul(struct fp_ext *dest, struct fp_ext *src)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	union fp_mant128 temp;
1548c2ecf20Sopenharmony_ci	int exp;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	dprint(PINSTR, "fmul\n");
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* calculate the correct sign now, as it's necessary for infinities */
1618c2ecf20Sopenharmony_ci	dest->sign = src->sign ^ dest->sign;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Handle infinities */
1648c2ecf20Sopenharmony_ci	if (IS_INF(dest)) {
1658c2ecf20Sopenharmony_ci		if (IS_ZERO(src))
1668c2ecf20Sopenharmony_ci			fp_set_nan(dest);
1678c2ecf20Sopenharmony_ci		return dest;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	if (IS_INF(src)) {
1708c2ecf20Sopenharmony_ci		if (IS_ZERO(dest))
1718c2ecf20Sopenharmony_ci			fp_set_nan(dest);
1728c2ecf20Sopenharmony_ci		else
1738c2ecf20Sopenharmony_ci			fp_copy_ext(dest, src);
1748c2ecf20Sopenharmony_ci		return dest;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* Of course, as we all know, zero * anything = zero.  You may
1788c2ecf20Sopenharmony_ci	   not have known that it might be a positive or negative
1798c2ecf20Sopenharmony_ci	   zero... */
1808c2ecf20Sopenharmony_ci	if (IS_ZERO(dest) || IS_ZERO(src)) {
1818c2ecf20Sopenharmony_ci		dest->exp = 0;
1828c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
1838c2ecf20Sopenharmony_ci		dest->lowmant = 0;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		return dest;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	exp = dest->exp + src->exp - 0x3ffe;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* shift up the mantissa for denormalized numbers,
1918c2ecf20Sopenharmony_ci	   so that the highest bit is set, this makes the
1928c2ecf20Sopenharmony_ci	   shift of the result below easier */
1938c2ecf20Sopenharmony_ci	if ((long)dest->mant.m32[0] >= 0)
1948c2ecf20Sopenharmony_ci		exp -= fp_overnormalize(dest);
1958c2ecf20Sopenharmony_ci	if ((long)src->mant.m32[0] >= 0)
1968c2ecf20Sopenharmony_ci		exp -= fp_overnormalize(src);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* now, do a 64-bit multiply with expansion */
1998c2ecf20Sopenharmony_ci	fp_multiplymant(&temp, dest, src);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* normalize it back to 64 bits and stuff it back into the
2028c2ecf20Sopenharmony_ci	   destination struct */
2038c2ecf20Sopenharmony_ci	if ((long)temp.m32[0] > 0) {
2048c2ecf20Sopenharmony_ci		exp--;
2058c2ecf20Sopenharmony_ci		fp_putmant128(dest, &temp, 1);
2068c2ecf20Sopenharmony_ci	} else
2078c2ecf20Sopenharmony_ci		fp_putmant128(dest, &temp, 0);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (exp >= 0x7fff) {
2108c2ecf20Sopenharmony_ci		fp_set_ovrflw(dest);
2118c2ecf20Sopenharmony_ci		return dest;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	dest->exp = exp;
2148c2ecf20Sopenharmony_ci	if (exp < 0) {
2158c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_UNFL);
2168c2ecf20Sopenharmony_ci		fp_denormalize(dest, -exp);
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return dest;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and
2238c2ecf20Sopenharmony_ci   FSGLDIV instructions.
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci   Note that the order of the operands is counter-intuitive: instead
2268c2ecf20Sopenharmony_ci   of src / dest, the result is actually dest / src. */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistruct fp_ext *
2298c2ecf20Sopenharmony_cifp_fdiv(struct fp_ext *dest, struct fp_ext *src)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	union fp_mant128 temp;
2328c2ecf20Sopenharmony_ci	int exp;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	dprint(PINSTR, "fdiv\n");
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* calculate the correct sign now, as it's necessary for infinities */
2398c2ecf20Sopenharmony_ci	dest->sign = src->sign ^ dest->sign;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/* Handle infinities */
2428c2ecf20Sopenharmony_ci	if (IS_INF(dest)) {
2438c2ecf20Sopenharmony_ci		/* infinity / infinity = NaN (quiet, as always) */
2448c2ecf20Sopenharmony_ci		if (IS_INF(src))
2458c2ecf20Sopenharmony_ci			fp_set_nan(dest);
2468c2ecf20Sopenharmony_ci		/* infinity / anything else = infinity (with approprate sign) */
2478c2ecf20Sopenharmony_ci		return dest;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	if (IS_INF(src)) {
2508c2ecf20Sopenharmony_ci		/* anything / infinity = zero (with appropriate sign) */
2518c2ecf20Sopenharmony_ci		dest->exp = 0;
2528c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
2538c2ecf20Sopenharmony_ci		dest->lowmant = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		return dest;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* zeroes */
2598c2ecf20Sopenharmony_ci	if (IS_ZERO(dest)) {
2608c2ecf20Sopenharmony_ci		/* zero / zero = NaN */
2618c2ecf20Sopenharmony_ci		if (IS_ZERO(src))
2628c2ecf20Sopenharmony_ci			fp_set_nan(dest);
2638c2ecf20Sopenharmony_ci		/* zero / anything else = zero */
2648c2ecf20Sopenharmony_ci		return dest;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	if (IS_ZERO(src)) {
2678c2ecf20Sopenharmony_ci		/* anything / zero = infinity (with appropriate sign) */
2688c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_DZ);
2698c2ecf20Sopenharmony_ci		dest->exp = 0x7fff;
2708c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		return dest;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	exp = dest->exp - src->exp + 0x3fff;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* shift up the mantissa for denormalized numbers,
2788c2ecf20Sopenharmony_ci	   so that the highest bit is set, this makes lots
2798c2ecf20Sopenharmony_ci	   of things below easier */
2808c2ecf20Sopenharmony_ci	if ((long)dest->mant.m32[0] >= 0)
2818c2ecf20Sopenharmony_ci		exp -= fp_overnormalize(dest);
2828c2ecf20Sopenharmony_ci	if ((long)src->mant.m32[0] >= 0)
2838c2ecf20Sopenharmony_ci		exp -= fp_overnormalize(src);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* now, do the 64-bit divide */
2868c2ecf20Sopenharmony_ci	fp_dividemant(&temp, dest, src);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* normalize it back to 64 bits and stuff it back into the
2898c2ecf20Sopenharmony_ci	   destination struct */
2908c2ecf20Sopenharmony_ci	if (!temp.m32[0]) {
2918c2ecf20Sopenharmony_ci		exp--;
2928c2ecf20Sopenharmony_ci		fp_putmant128(dest, &temp, 32);
2938c2ecf20Sopenharmony_ci	} else
2948c2ecf20Sopenharmony_ci		fp_putmant128(dest, &temp, 31);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (exp >= 0x7fff) {
2978c2ecf20Sopenharmony_ci		fp_set_ovrflw(dest);
2988c2ecf20Sopenharmony_ci		return dest;
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	dest->exp = exp;
3018c2ecf20Sopenharmony_ci	if (exp < 0) {
3028c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_UNFL);
3038c2ecf20Sopenharmony_ci		fp_denormalize(dest, -exp);
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return dest;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistruct fp_ext *
3108c2ecf20Sopenharmony_cifp_fsglmul(struct fp_ext *dest, struct fp_ext *src)
3118c2ecf20Sopenharmony_ci{
3128c2ecf20Sopenharmony_ci	int exp;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	dprint(PINSTR, "fsglmul\n");
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* calculate the correct sign now, as it's necessary for infinities */
3198c2ecf20Sopenharmony_ci	dest->sign = src->sign ^ dest->sign;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* Handle infinities */
3228c2ecf20Sopenharmony_ci	if (IS_INF(dest)) {
3238c2ecf20Sopenharmony_ci		if (IS_ZERO(src))
3248c2ecf20Sopenharmony_ci			fp_set_nan(dest);
3258c2ecf20Sopenharmony_ci		return dest;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	if (IS_INF(src)) {
3288c2ecf20Sopenharmony_ci		if (IS_ZERO(dest))
3298c2ecf20Sopenharmony_ci			fp_set_nan(dest);
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			fp_copy_ext(dest, src);
3328c2ecf20Sopenharmony_ci		return dest;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Of course, as we all know, zero * anything = zero.  You may
3368c2ecf20Sopenharmony_ci	   not have known that it might be a positive or negative
3378c2ecf20Sopenharmony_ci	   zero... */
3388c2ecf20Sopenharmony_ci	if (IS_ZERO(dest) || IS_ZERO(src)) {
3398c2ecf20Sopenharmony_ci		dest->exp = 0;
3408c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
3418c2ecf20Sopenharmony_ci		dest->lowmant = 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		return dest;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	exp = dest->exp + src->exp - 0x3ffe;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* do a 32-bit multiply */
3498c2ecf20Sopenharmony_ci	fp_mul64(dest->mant.m32[0], dest->mant.m32[1],
3508c2ecf20Sopenharmony_ci		 dest->mant.m32[0] & 0xffffff00,
3518c2ecf20Sopenharmony_ci		 src->mant.m32[0] & 0xffffff00);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (exp >= 0x7fff) {
3548c2ecf20Sopenharmony_ci		fp_set_ovrflw(dest);
3558c2ecf20Sopenharmony_ci		return dest;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	dest->exp = exp;
3588c2ecf20Sopenharmony_ci	if (exp < 0) {
3598c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_UNFL);
3608c2ecf20Sopenharmony_ci		fp_denormalize(dest, -exp);
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return dest;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistruct fp_ext *
3678c2ecf20Sopenharmony_cifp_fsgldiv(struct fp_ext *dest, struct fp_ext *src)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	int exp;
3708c2ecf20Sopenharmony_ci	unsigned long quot, rem;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	dprint(PINSTR, "fsgldiv\n");
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* calculate the correct sign now, as it's necessary for infinities */
3778c2ecf20Sopenharmony_ci	dest->sign = src->sign ^ dest->sign;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* Handle infinities */
3808c2ecf20Sopenharmony_ci	if (IS_INF(dest)) {
3818c2ecf20Sopenharmony_ci		/* infinity / infinity = NaN (quiet, as always) */
3828c2ecf20Sopenharmony_ci		if (IS_INF(src))
3838c2ecf20Sopenharmony_ci			fp_set_nan(dest);
3848c2ecf20Sopenharmony_ci		/* infinity / anything else = infinity (with approprate sign) */
3858c2ecf20Sopenharmony_ci		return dest;
3868c2ecf20Sopenharmony_ci	}
3878c2ecf20Sopenharmony_ci	if (IS_INF(src)) {
3888c2ecf20Sopenharmony_ci		/* anything / infinity = zero (with appropriate sign) */
3898c2ecf20Sopenharmony_ci		dest->exp = 0;
3908c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
3918c2ecf20Sopenharmony_ci		dest->lowmant = 0;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		return dest;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* zeroes */
3978c2ecf20Sopenharmony_ci	if (IS_ZERO(dest)) {
3988c2ecf20Sopenharmony_ci		/* zero / zero = NaN */
3998c2ecf20Sopenharmony_ci		if (IS_ZERO(src))
4008c2ecf20Sopenharmony_ci			fp_set_nan(dest);
4018c2ecf20Sopenharmony_ci		/* zero / anything else = zero */
4028c2ecf20Sopenharmony_ci		return dest;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci	if (IS_ZERO(src)) {
4058c2ecf20Sopenharmony_ci		/* anything / zero = infinity (with appropriate sign) */
4068c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_DZ);
4078c2ecf20Sopenharmony_ci		dest->exp = 0x7fff;
4088c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		return dest;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	exp = dest->exp - src->exp + 0x3fff;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	dest->mant.m32[0] &= 0xffffff00;
4168c2ecf20Sopenharmony_ci	src->mant.m32[0] &= 0xffffff00;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* do the 32-bit divide */
4198c2ecf20Sopenharmony_ci	if (dest->mant.m32[0] >= src->mant.m32[0]) {
4208c2ecf20Sopenharmony_ci		fp_sub64(dest->mant, src->mant);
4218c2ecf20Sopenharmony_ci		fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]);
4228c2ecf20Sopenharmony_ci		dest->mant.m32[0] = 0x80000000 | (quot >> 1);
4238c2ecf20Sopenharmony_ci		dest->mant.m32[1] = (quot & 1) | rem;	/* only for rounding */
4248c2ecf20Sopenharmony_ci	} else {
4258c2ecf20Sopenharmony_ci		fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]);
4268c2ecf20Sopenharmony_ci		dest->mant.m32[0] = quot;
4278c2ecf20Sopenharmony_ci		dest->mant.m32[1] = rem;		/* only for rounding */
4288c2ecf20Sopenharmony_ci		exp--;
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (exp >= 0x7fff) {
4328c2ecf20Sopenharmony_ci		fp_set_ovrflw(dest);
4338c2ecf20Sopenharmony_ci		return dest;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci	dest->exp = exp;
4368c2ecf20Sopenharmony_ci	if (exp < 0) {
4378c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_UNFL);
4388c2ecf20Sopenharmony_ci		fp_denormalize(dest, -exp);
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	return dest;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci/* fp_roundint: Internal rounding function for use by several of these
4458c2ecf20Sopenharmony_ci   emulated instructions.
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci   This one rounds off the fractional part using the rounding mode
4488c2ecf20Sopenharmony_ci   specified. */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic void fp_roundint(struct fp_ext *dest, int mode)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	union fp_mant64 oldmant;
4538c2ecf20Sopenharmony_ci	unsigned long mask;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (!fp_normalize_ext(dest))
4568c2ecf20Sopenharmony_ci		return;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* infinities and zeroes */
4598c2ecf20Sopenharmony_ci	if (IS_INF(dest) || IS_ZERO(dest))
4608c2ecf20Sopenharmony_ci		return;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* first truncate the lower bits */
4638c2ecf20Sopenharmony_ci	oldmant = dest->mant;
4648c2ecf20Sopenharmony_ci	switch (dest->exp) {
4658c2ecf20Sopenharmony_ci	case 0 ... 0x3ffe:
4668c2ecf20Sopenharmony_ci		dest->mant.m64 = 0;
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci	case 0x3fff ... 0x401e:
4698c2ecf20Sopenharmony_ci		dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp);
4708c2ecf20Sopenharmony_ci		dest->mant.m32[1] = 0;
4718c2ecf20Sopenharmony_ci		if (oldmant.m64 == dest->mant.m64)
4728c2ecf20Sopenharmony_ci			return;
4738c2ecf20Sopenharmony_ci		break;
4748c2ecf20Sopenharmony_ci	case 0x401f ... 0x403e:
4758c2ecf20Sopenharmony_ci		dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp);
4768c2ecf20Sopenharmony_ci		if (oldmant.m32[1] == dest->mant.m32[1])
4778c2ecf20Sopenharmony_ci			return;
4788c2ecf20Sopenharmony_ci		break;
4798c2ecf20Sopenharmony_ci	default:
4808c2ecf20Sopenharmony_ci		return;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	fp_set_sr(FPSR_EXC_INEX2);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* We might want to normalize upwards here... however, since
4858c2ecf20Sopenharmony_ci	   we know that this is only called on the output of fp_fdiv,
4868c2ecf20Sopenharmony_ci	   or with the input to fp_fint or fp_fintrz, and the inputs
4878c2ecf20Sopenharmony_ci	   to all these functions are either normal or denormalized
4888c2ecf20Sopenharmony_ci	   (no subnormals allowed!), there's really no need.
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	   In the case of fp_fdiv, observe that 0x80000000 / 0xffff =
4918c2ecf20Sopenharmony_ci	   0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the
4928c2ecf20Sopenharmony_ci	   smallest possible normal dividend and the largest possible normal
4938c2ecf20Sopenharmony_ci	   divisor will still produce a normal quotient, therefore, (normal
4948c2ecf20Sopenharmony_ci	   << 64) / normal is normal in all cases) */
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	switch (mode) {
4978c2ecf20Sopenharmony_ci	case FPCR_ROUND_RN:
4988c2ecf20Sopenharmony_ci		switch (dest->exp) {
4998c2ecf20Sopenharmony_ci		case 0 ... 0x3ffd:
5008c2ecf20Sopenharmony_ci			return;
5018c2ecf20Sopenharmony_ci		case 0x3ffe:
5028c2ecf20Sopenharmony_ci			/* As noted above, the input is always normal, so the
5038c2ecf20Sopenharmony_ci			   guard bit (bit 63) is always set.  therefore, the
5048c2ecf20Sopenharmony_ci			   only case in which we will NOT round to 1.0 is when
5058c2ecf20Sopenharmony_ci			   the input is exactly 0.5. */
5068c2ecf20Sopenharmony_ci			if (oldmant.m64 == (1ULL << 63))
5078c2ecf20Sopenharmony_ci				return;
5088c2ecf20Sopenharmony_ci			break;
5098c2ecf20Sopenharmony_ci		case 0x3fff ... 0x401d:
5108c2ecf20Sopenharmony_ci			mask = 1 << (0x401d - dest->exp);
5118c2ecf20Sopenharmony_ci			if (!(oldmant.m32[0] & mask))
5128c2ecf20Sopenharmony_ci				return;
5138c2ecf20Sopenharmony_ci			if (oldmant.m32[0] & (mask << 1))
5148c2ecf20Sopenharmony_ci				break;
5158c2ecf20Sopenharmony_ci			if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) &&
5168c2ecf20Sopenharmony_ci					!oldmant.m32[1])
5178c2ecf20Sopenharmony_ci				return;
5188c2ecf20Sopenharmony_ci			break;
5198c2ecf20Sopenharmony_ci		case 0x401e:
5208c2ecf20Sopenharmony_ci			if (oldmant.m32[1] & 0x80000000)
5218c2ecf20Sopenharmony_ci				return;
5228c2ecf20Sopenharmony_ci			if (oldmant.m32[0] & 1)
5238c2ecf20Sopenharmony_ci				break;
5248c2ecf20Sopenharmony_ci			if (!(oldmant.m32[1] << 1))
5258c2ecf20Sopenharmony_ci				return;
5268c2ecf20Sopenharmony_ci			break;
5278c2ecf20Sopenharmony_ci		case 0x401f ... 0x403d:
5288c2ecf20Sopenharmony_ci			mask = 1 << (0x403d - dest->exp);
5298c2ecf20Sopenharmony_ci			if (!(oldmant.m32[1] & mask))
5308c2ecf20Sopenharmony_ci				return;
5318c2ecf20Sopenharmony_ci			if (oldmant.m32[1] & (mask << 1))
5328c2ecf20Sopenharmony_ci				break;
5338c2ecf20Sopenharmony_ci			if (!(oldmant.m32[1] << (dest->exp - 0x401d)))
5348c2ecf20Sopenharmony_ci				return;
5358c2ecf20Sopenharmony_ci			break;
5368c2ecf20Sopenharmony_ci		default:
5378c2ecf20Sopenharmony_ci			return;
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci		break;
5408c2ecf20Sopenharmony_ci	case FPCR_ROUND_RZ:
5418c2ecf20Sopenharmony_ci		return;
5428c2ecf20Sopenharmony_ci	default:
5438c2ecf20Sopenharmony_ci		if (dest->sign ^ (mode - FPCR_ROUND_RM))
5448c2ecf20Sopenharmony_ci			break;
5458c2ecf20Sopenharmony_ci		return;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	switch (dest->exp) {
5498c2ecf20Sopenharmony_ci	case 0 ... 0x3ffe:
5508c2ecf20Sopenharmony_ci		dest->exp = 0x3fff;
5518c2ecf20Sopenharmony_ci		dest->mant.m64 = 1ULL << 63;
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci	case 0x3fff ... 0x401e:
5548c2ecf20Sopenharmony_ci		mask = 1 << (0x401e - dest->exp);
5558c2ecf20Sopenharmony_ci		if (dest->mant.m32[0] += mask)
5568c2ecf20Sopenharmony_ci			break;
5578c2ecf20Sopenharmony_ci		dest->mant.m32[0] = 0x80000000;
5588c2ecf20Sopenharmony_ci		dest->exp++;
5598c2ecf20Sopenharmony_ci		break;
5608c2ecf20Sopenharmony_ci	case 0x401f ... 0x403e:
5618c2ecf20Sopenharmony_ci		mask = 1 << (0x403e - dest->exp);
5628c2ecf20Sopenharmony_ci		if (dest->mant.m32[1] += mask)
5638c2ecf20Sopenharmony_ci			break;
5648c2ecf20Sopenharmony_ci		if (dest->mant.m32[0] += 1)
5658c2ecf20Sopenharmony_ci                        break;
5668c2ecf20Sopenharmony_ci		dest->mant.m32[0] = 0x80000000;
5678c2ecf20Sopenharmony_ci                dest->exp++;
5688c2ecf20Sopenharmony_ci		break;
5698c2ecf20Sopenharmony_ci	}
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci/* modrem_kernel: Implementation of the FREM and FMOD instructions
5738c2ecf20Sopenharmony_ci   (which are exactly the same, except for the rounding used on the
5748c2ecf20Sopenharmony_ci   intermediate value) */
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic struct fp_ext *
5778c2ecf20Sopenharmony_cimodrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct fp_ext tmp;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	/* Infinities and zeros */
5848c2ecf20Sopenharmony_ci	if (IS_INF(dest) || IS_ZERO(src)) {
5858c2ecf20Sopenharmony_ci		fp_set_nan(dest);
5868c2ecf20Sopenharmony_ci		return dest;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	if (IS_ZERO(dest) || IS_INF(src))
5898c2ecf20Sopenharmony_ci		return dest;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/* FIXME: there is almost certainly a smarter way to do this */
5928c2ecf20Sopenharmony_ci	fp_copy_ext(&tmp, dest);
5938c2ecf20Sopenharmony_ci	fp_fdiv(&tmp, src);		/* NOTE: src might be modified */
5948c2ecf20Sopenharmony_ci	fp_roundint(&tmp, mode);
5958c2ecf20Sopenharmony_ci	fp_fmul(&tmp, src);
5968c2ecf20Sopenharmony_ci	fp_fsub(dest, &tmp);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	/* set the quotient byte */
5998c2ecf20Sopenharmony_ci	fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7));
6008c2ecf20Sopenharmony_ci	return dest;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/* fp_fmod: Implements the kernel of the FMOD instruction.
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci   Again, the argument order is backwards.  The result, as defined in
6068c2ecf20Sopenharmony_ci   the Motorola manuals, is:
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci   fmod(src,dest) = (dest - (src * floor(dest / src))) */
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistruct fp_ext *
6118c2ecf20Sopenharmony_cifp_fmod(struct fp_ext *dest, struct fp_ext *src)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	dprint(PINSTR, "fmod\n");
6148c2ecf20Sopenharmony_ci	return modrem_kernel(dest, src, FPCR_ROUND_RZ);
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci/* fp_frem: Implements the kernel of the FREM instruction.
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci   frem(src,dest) = (dest - (src * round(dest / src)))
6208c2ecf20Sopenharmony_ci */
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistruct fp_ext *
6238c2ecf20Sopenharmony_cifp_frem(struct fp_ext *dest, struct fp_ext *src)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	dprint(PINSTR, "frem\n");
6268c2ecf20Sopenharmony_ci	return modrem_kernel(dest, src, FPCR_ROUND_RN);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_cistruct fp_ext *
6308c2ecf20Sopenharmony_cifp_fint(struct fp_ext *dest, struct fp_ext *src)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	dprint(PINSTR, "fint\n");
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	fp_copy_ext(dest, src);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	fp_roundint(dest, FPDATA->rnd);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	return dest;
6398c2ecf20Sopenharmony_ci}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_cistruct fp_ext *
6428c2ecf20Sopenharmony_cifp_fintrz(struct fp_ext *dest, struct fp_ext *src)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	dprint(PINSTR, "fintrz\n");
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	fp_copy_ext(dest, src);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	fp_roundint(dest, FPCR_ROUND_RZ);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	return dest;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistruct fp_ext *
6548c2ecf20Sopenharmony_cifp_fscale(struct fp_ext *dest, struct fp_ext *src)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	int scale, oldround;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	dprint(PINSTR, "fscale\n");
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	fp_dyadic_check(dest, src);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* Infinities */
6638c2ecf20Sopenharmony_ci	if (IS_INF(src)) {
6648c2ecf20Sopenharmony_ci		fp_set_nan(dest);
6658c2ecf20Sopenharmony_ci		return dest;
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci	if (IS_INF(dest))
6688c2ecf20Sopenharmony_ci		return dest;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* zeroes */
6718c2ecf20Sopenharmony_ci	if (IS_ZERO(src) || IS_ZERO(dest))
6728c2ecf20Sopenharmony_ci		return dest;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* Source exponent out of range */
6758c2ecf20Sopenharmony_ci	if (src->exp >= 0x400c) {
6768c2ecf20Sopenharmony_ci		fp_set_ovrflw(dest);
6778c2ecf20Sopenharmony_ci		return dest;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/* src must be rounded with round to zero. */
6818c2ecf20Sopenharmony_ci	oldround = FPDATA->rnd;
6828c2ecf20Sopenharmony_ci	FPDATA->rnd = FPCR_ROUND_RZ;
6838c2ecf20Sopenharmony_ci	scale = fp_conv_ext2long(src);
6848c2ecf20Sopenharmony_ci	FPDATA->rnd = oldround;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* new exponent */
6878c2ecf20Sopenharmony_ci	scale += dest->exp;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (scale >= 0x7fff) {
6908c2ecf20Sopenharmony_ci		fp_set_ovrflw(dest);
6918c2ecf20Sopenharmony_ci	} else if (scale <= 0) {
6928c2ecf20Sopenharmony_ci		fp_set_sr(FPSR_EXC_UNFL);
6938c2ecf20Sopenharmony_ci		fp_denormalize(dest, -scale);
6948c2ecf20Sopenharmony_ci	} else
6958c2ecf20Sopenharmony_ci		dest->exp = scale;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	return dest;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
700