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