162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci fp_arith.c: floating-point math routines for the Linux-m68k 562306a36Sopenharmony_ci floating point emulator. 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Copyright (c) 1998-1999 David Huggins-Daines. 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci Somewhat based on the AlphaLinux floating point emulator, by David 1062306a36Sopenharmony_ci Mosberger-Tang. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "fp_emu.h" 1562306a36Sopenharmony_ci#include "multi_arith.h" 1662306a36Sopenharmony_ci#include "fp_arith.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciconst struct fp_ext fp_QNaN = 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci .exp = 0x7fff, 2162306a36Sopenharmony_ci .mant = { .m64 = ~0 } 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ciconst struct fp_ext fp_Inf = 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci .exp = 0x7fff, 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* let's start with the easy ones */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct fp_ext * 3262306a36Sopenharmony_cifp_fabs(struct fp_ext *dest, struct fp_ext *src) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci dprint(PINSTR, "fabs\n"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci fp_monadic_check(dest, src); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci dest->sign = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return dest; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct fp_ext * 4462306a36Sopenharmony_cifp_fneg(struct fp_ext *dest, struct fp_ext *src) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci dprint(PINSTR, "fneg\n"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci fp_monadic_check(dest, src); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci dest->sign = !dest->sign; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return dest; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Now, the slightly harder ones */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB, 5862306a36Sopenharmony_ci FDSUB, and FCMP instructions. */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct fp_ext * 6162306a36Sopenharmony_cifp_fadd(struct fp_ext *dest, struct fp_ext *src) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int diff; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci dprint(PINSTR, "fadd\n"); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci fp_dyadic_check(dest, src); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (IS_INF(dest)) { 7062306a36Sopenharmony_ci /* infinity - infinity == NaN */ 7162306a36Sopenharmony_ci if (IS_INF(src) && (src->sign != dest->sign)) 7262306a36Sopenharmony_ci fp_set_nan(dest); 7362306a36Sopenharmony_ci return dest; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci if (IS_INF(src)) { 7662306a36Sopenharmony_ci fp_copy_ext(dest, src); 7762306a36Sopenharmony_ci return dest; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (IS_ZERO(dest)) { 8162306a36Sopenharmony_ci if (IS_ZERO(src)) { 8262306a36Sopenharmony_ci if (src->sign != dest->sign) { 8362306a36Sopenharmony_ci if (FPDATA->rnd == FPCR_ROUND_RM) 8462306a36Sopenharmony_ci dest->sign = 1; 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci dest->sign = 0; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } else 8962306a36Sopenharmony_ci fp_copy_ext(dest, src); 9062306a36Sopenharmony_ci return dest; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci dest->lowmant = src->lowmant = 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if ((diff = dest->exp - src->exp) > 0) 9662306a36Sopenharmony_ci fp_denormalize(src, diff); 9762306a36Sopenharmony_ci else if ((diff = -diff) > 0) 9862306a36Sopenharmony_ci fp_denormalize(dest, diff); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (dest->sign == src->sign) { 10162306a36Sopenharmony_ci if (fp_addmant(dest, src)) 10262306a36Sopenharmony_ci if (!fp_addcarry(dest)) 10362306a36Sopenharmony_ci return dest; 10462306a36Sopenharmony_ci } else { 10562306a36Sopenharmony_ci if (dest->mant.m64 < src->mant.m64) { 10662306a36Sopenharmony_ci fp_submant(dest, src, dest); 10762306a36Sopenharmony_ci dest->sign = !dest->sign; 10862306a36Sopenharmony_ci } else 10962306a36Sopenharmony_ci fp_submant(dest, dest, src); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return dest; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB 11662306a36Sopenharmony_ci instructions. 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci Remember that the arguments are in assembler-syntax order! */ 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct fp_ext * 12162306a36Sopenharmony_cifp_fsub(struct fp_ext *dest, struct fp_ext *src) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci dprint(PINSTR, "fsub "); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci src->sign = !src->sign; 12662306a36Sopenharmony_ci return fp_fadd(dest, src); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct fp_ext * 13162306a36Sopenharmony_cifp_fcmp(struct fp_ext *dest, struct fp_ext *src) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci dprint(PINSTR, "fcmp "); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci FPDATA->temp[1] = *dest; 13662306a36Sopenharmony_ci src->sign = !src->sign; 13762306a36Sopenharmony_ci return fp_fadd(&FPDATA->temp[1], src); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct fp_ext * 14162306a36Sopenharmony_cifp_ftst(struct fp_ext *dest, struct fp_ext *src) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci dprint(PINSTR, "ftst\n"); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci (void)dest; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return src; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct fp_ext * 15162306a36Sopenharmony_cifp_fmul(struct fp_ext *dest, struct fp_ext *src) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci union fp_mant128 temp; 15462306a36Sopenharmony_ci int exp; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dprint(PINSTR, "fmul\n"); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci fp_dyadic_check(dest, src); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* calculate the correct sign now, as it's necessary for infinities */ 16162306a36Sopenharmony_ci dest->sign = src->sign ^ dest->sign; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Handle infinities */ 16462306a36Sopenharmony_ci if (IS_INF(dest)) { 16562306a36Sopenharmony_ci if (IS_ZERO(src)) 16662306a36Sopenharmony_ci fp_set_nan(dest); 16762306a36Sopenharmony_ci return dest; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci if (IS_INF(src)) { 17062306a36Sopenharmony_ci if (IS_ZERO(dest)) 17162306a36Sopenharmony_ci fp_set_nan(dest); 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci fp_copy_ext(dest, src); 17462306a36Sopenharmony_ci return dest; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Of course, as we all know, zero * anything = zero. You may 17862306a36Sopenharmony_ci not have known that it might be a positive or negative 17962306a36Sopenharmony_ci zero... */ 18062306a36Sopenharmony_ci if (IS_ZERO(dest) || IS_ZERO(src)) { 18162306a36Sopenharmony_ci dest->exp = 0; 18262306a36Sopenharmony_ci dest->mant.m64 = 0; 18362306a36Sopenharmony_ci dest->lowmant = 0; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return dest; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci exp = dest->exp + src->exp - 0x3ffe; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* shift up the mantissa for denormalized numbers, 19162306a36Sopenharmony_ci so that the highest bit is set, this makes the 19262306a36Sopenharmony_ci shift of the result below easier */ 19362306a36Sopenharmony_ci if ((long)dest->mant.m32[0] >= 0) 19462306a36Sopenharmony_ci exp -= fp_overnormalize(dest); 19562306a36Sopenharmony_ci if ((long)src->mant.m32[0] >= 0) 19662306a36Sopenharmony_ci exp -= fp_overnormalize(src); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* now, do a 64-bit multiply with expansion */ 19962306a36Sopenharmony_ci fp_multiplymant(&temp, dest, src); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* normalize it back to 64 bits and stuff it back into the 20262306a36Sopenharmony_ci destination struct */ 20362306a36Sopenharmony_ci if ((long)temp.m32[0] > 0) { 20462306a36Sopenharmony_ci exp--; 20562306a36Sopenharmony_ci fp_putmant128(dest, &temp, 1); 20662306a36Sopenharmony_ci } else 20762306a36Sopenharmony_ci fp_putmant128(dest, &temp, 0); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (exp >= 0x7fff) { 21062306a36Sopenharmony_ci fp_set_ovrflw(dest); 21162306a36Sopenharmony_ci return dest; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci dest->exp = exp; 21462306a36Sopenharmony_ci if (exp < 0) { 21562306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_UNFL); 21662306a36Sopenharmony_ci fp_denormalize(dest, -exp); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return dest; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and 22362306a36Sopenharmony_ci FSGLDIV instructions. 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci Note that the order of the operands is counter-intuitive: instead 22662306a36Sopenharmony_ci of src / dest, the result is actually dest / src. */ 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct fp_ext * 22962306a36Sopenharmony_cifp_fdiv(struct fp_ext *dest, struct fp_ext *src) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci union fp_mant128 temp; 23262306a36Sopenharmony_ci int exp; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dprint(PINSTR, "fdiv\n"); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci fp_dyadic_check(dest, src); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* calculate the correct sign now, as it's necessary for infinities */ 23962306a36Sopenharmony_ci dest->sign = src->sign ^ dest->sign; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Handle infinities */ 24262306a36Sopenharmony_ci if (IS_INF(dest)) { 24362306a36Sopenharmony_ci /* infinity / infinity = NaN (quiet, as always) */ 24462306a36Sopenharmony_ci if (IS_INF(src)) 24562306a36Sopenharmony_ci fp_set_nan(dest); 24662306a36Sopenharmony_ci /* infinity / anything else = infinity (with appropriate sign) */ 24762306a36Sopenharmony_ci return dest; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci if (IS_INF(src)) { 25062306a36Sopenharmony_ci /* anything / infinity = zero (with appropriate sign) */ 25162306a36Sopenharmony_ci dest->exp = 0; 25262306a36Sopenharmony_ci dest->mant.m64 = 0; 25362306a36Sopenharmony_ci dest->lowmant = 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return dest; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* zeroes */ 25962306a36Sopenharmony_ci if (IS_ZERO(dest)) { 26062306a36Sopenharmony_ci /* zero / zero = NaN */ 26162306a36Sopenharmony_ci if (IS_ZERO(src)) 26262306a36Sopenharmony_ci fp_set_nan(dest); 26362306a36Sopenharmony_ci /* zero / anything else = zero */ 26462306a36Sopenharmony_ci return dest; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci if (IS_ZERO(src)) { 26762306a36Sopenharmony_ci /* anything / zero = infinity (with appropriate sign) */ 26862306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_DZ); 26962306a36Sopenharmony_ci dest->exp = 0x7fff; 27062306a36Sopenharmony_ci dest->mant.m64 = 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return dest; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci exp = dest->exp - src->exp + 0x3fff; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* shift up the mantissa for denormalized numbers, 27862306a36Sopenharmony_ci so that the highest bit is set, this makes lots 27962306a36Sopenharmony_ci of things below easier */ 28062306a36Sopenharmony_ci if ((long)dest->mant.m32[0] >= 0) 28162306a36Sopenharmony_ci exp -= fp_overnormalize(dest); 28262306a36Sopenharmony_ci if ((long)src->mant.m32[0] >= 0) 28362306a36Sopenharmony_ci exp -= fp_overnormalize(src); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* now, do the 64-bit divide */ 28662306a36Sopenharmony_ci fp_dividemant(&temp, dest, src); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* normalize it back to 64 bits and stuff it back into the 28962306a36Sopenharmony_ci destination struct */ 29062306a36Sopenharmony_ci if (!temp.m32[0]) { 29162306a36Sopenharmony_ci exp--; 29262306a36Sopenharmony_ci fp_putmant128(dest, &temp, 32); 29362306a36Sopenharmony_ci } else 29462306a36Sopenharmony_ci fp_putmant128(dest, &temp, 31); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (exp >= 0x7fff) { 29762306a36Sopenharmony_ci fp_set_ovrflw(dest); 29862306a36Sopenharmony_ci return dest; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci dest->exp = exp; 30162306a36Sopenharmony_ci if (exp < 0) { 30262306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_UNFL); 30362306a36Sopenharmony_ci fp_denormalize(dest, -exp); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return dest; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistruct fp_ext * 31062306a36Sopenharmony_cifp_fsglmul(struct fp_ext *dest, struct fp_ext *src) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci int exp; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci dprint(PINSTR, "fsglmul\n"); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci fp_dyadic_check(dest, src); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* calculate the correct sign now, as it's necessary for infinities */ 31962306a36Sopenharmony_ci dest->sign = src->sign ^ dest->sign; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Handle infinities */ 32262306a36Sopenharmony_ci if (IS_INF(dest)) { 32362306a36Sopenharmony_ci if (IS_ZERO(src)) 32462306a36Sopenharmony_ci fp_set_nan(dest); 32562306a36Sopenharmony_ci return dest; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci if (IS_INF(src)) { 32862306a36Sopenharmony_ci if (IS_ZERO(dest)) 32962306a36Sopenharmony_ci fp_set_nan(dest); 33062306a36Sopenharmony_ci else 33162306a36Sopenharmony_ci fp_copy_ext(dest, src); 33262306a36Sopenharmony_ci return dest; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Of course, as we all know, zero * anything = zero. You may 33662306a36Sopenharmony_ci not have known that it might be a positive or negative 33762306a36Sopenharmony_ci zero... */ 33862306a36Sopenharmony_ci if (IS_ZERO(dest) || IS_ZERO(src)) { 33962306a36Sopenharmony_ci dest->exp = 0; 34062306a36Sopenharmony_ci dest->mant.m64 = 0; 34162306a36Sopenharmony_ci dest->lowmant = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return dest; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci exp = dest->exp + src->exp - 0x3ffe; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* do a 32-bit multiply */ 34962306a36Sopenharmony_ci fp_mul64(dest->mant.m32[0], dest->mant.m32[1], 35062306a36Sopenharmony_ci dest->mant.m32[0] & 0xffffff00, 35162306a36Sopenharmony_ci src->mant.m32[0] & 0xffffff00); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (exp >= 0x7fff) { 35462306a36Sopenharmony_ci fp_set_ovrflw(dest); 35562306a36Sopenharmony_ci return dest; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci dest->exp = exp; 35862306a36Sopenharmony_ci if (exp < 0) { 35962306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_UNFL); 36062306a36Sopenharmony_ci fp_denormalize(dest, -exp); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return dest; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistruct fp_ext * 36762306a36Sopenharmony_cifp_fsgldiv(struct fp_ext *dest, struct fp_ext *src) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci int exp; 37062306a36Sopenharmony_ci unsigned long quot, rem; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci dprint(PINSTR, "fsgldiv\n"); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci fp_dyadic_check(dest, src); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* calculate the correct sign now, as it's necessary for infinities */ 37762306a36Sopenharmony_ci dest->sign = src->sign ^ dest->sign; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* Handle infinities */ 38062306a36Sopenharmony_ci if (IS_INF(dest)) { 38162306a36Sopenharmony_ci /* infinity / infinity = NaN (quiet, as always) */ 38262306a36Sopenharmony_ci if (IS_INF(src)) 38362306a36Sopenharmony_ci fp_set_nan(dest); 38462306a36Sopenharmony_ci /* infinity / anything else = infinity (with approprate sign) */ 38562306a36Sopenharmony_ci return dest; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci if (IS_INF(src)) { 38862306a36Sopenharmony_ci /* anything / infinity = zero (with appropriate sign) */ 38962306a36Sopenharmony_ci dest->exp = 0; 39062306a36Sopenharmony_ci dest->mant.m64 = 0; 39162306a36Sopenharmony_ci dest->lowmant = 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return dest; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* zeroes */ 39762306a36Sopenharmony_ci if (IS_ZERO(dest)) { 39862306a36Sopenharmony_ci /* zero / zero = NaN */ 39962306a36Sopenharmony_ci if (IS_ZERO(src)) 40062306a36Sopenharmony_ci fp_set_nan(dest); 40162306a36Sopenharmony_ci /* zero / anything else = zero */ 40262306a36Sopenharmony_ci return dest; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci if (IS_ZERO(src)) { 40562306a36Sopenharmony_ci /* anything / zero = infinity (with appropriate sign) */ 40662306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_DZ); 40762306a36Sopenharmony_ci dest->exp = 0x7fff; 40862306a36Sopenharmony_ci dest->mant.m64 = 0; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return dest; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci exp = dest->exp - src->exp + 0x3fff; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci dest->mant.m32[0] &= 0xffffff00; 41662306a36Sopenharmony_ci src->mant.m32[0] &= 0xffffff00; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* do the 32-bit divide */ 41962306a36Sopenharmony_ci if (dest->mant.m32[0] >= src->mant.m32[0]) { 42062306a36Sopenharmony_ci fp_sub64(dest->mant, src->mant); 42162306a36Sopenharmony_ci fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); 42262306a36Sopenharmony_ci dest->mant.m32[0] = 0x80000000 | (quot >> 1); 42362306a36Sopenharmony_ci dest->mant.m32[1] = (quot & 1) | rem; /* only for rounding */ 42462306a36Sopenharmony_ci } else { 42562306a36Sopenharmony_ci fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]); 42662306a36Sopenharmony_ci dest->mant.m32[0] = quot; 42762306a36Sopenharmony_ci dest->mant.m32[1] = rem; /* only for rounding */ 42862306a36Sopenharmony_ci exp--; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (exp >= 0x7fff) { 43262306a36Sopenharmony_ci fp_set_ovrflw(dest); 43362306a36Sopenharmony_ci return dest; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci dest->exp = exp; 43662306a36Sopenharmony_ci if (exp < 0) { 43762306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_UNFL); 43862306a36Sopenharmony_ci fp_denormalize(dest, -exp); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return dest; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* fp_roundint: Internal rounding function for use by several of these 44562306a36Sopenharmony_ci emulated instructions. 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci This one rounds off the fractional part using the rounding mode 44862306a36Sopenharmony_ci specified. */ 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic void fp_roundint(struct fp_ext *dest, int mode) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci union fp_mant64 oldmant; 45362306a36Sopenharmony_ci unsigned long mask; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!fp_normalize_ext(dest)) 45662306a36Sopenharmony_ci return; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* infinities and zeroes */ 45962306a36Sopenharmony_ci if (IS_INF(dest) || IS_ZERO(dest)) 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* first truncate the lower bits */ 46362306a36Sopenharmony_ci oldmant = dest->mant; 46462306a36Sopenharmony_ci switch (dest->exp) { 46562306a36Sopenharmony_ci case 0 ... 0x3ffe: 46662306a36Sopenharmony_ci dest->mant.m64 = 0; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case 0x3fff ... 0x401e: 46962306a36Sopenharmony_ci dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp); 47062306a36Sopenharmony_ci dest->mant.m32[1] = 0; 47162306a36Sopenharmony_ci if (oldmant.m64 == dest->mant.m64) 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci case 0x401f ... 0x403e: 47562306a36Sopenharmony_ci dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp); 47662306a36Sopenharmony_ci if (oldmant.m32[1] == dest->mant.m32[1]) 47762306a36Sopenharmony_ci return; 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci default: 48062306a36Sopenharmony_ci return; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_INEX2); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* We might want to normalize upwards here... however, since 48562306a36Sopenharmony_ci we know that this is only called on the output of fp_fdiv, 48662306a36Sopenharmony_ci or with the input to fp_fint or fp_fintrz, and the inputs 48762306a36Sopenharmony_ci to all these functions are either normal or denormalized 48862306a36Sopenharmony_ci (no subnormals allowed!), there's really no need. 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci In the case of fp_fdiv, observe that 0x80000000 / 0xffff = 49162306a36Sopenharmony_ci 0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the 49262306a36Sopenharmony_ci smallest possible normal dividend and the largest possible normal 49362306a36Sopenharmony_ci divisor will still produce a normal quotient, therefore, (normal 49462306a36Sopenharmony_ci << 64) / normal is normal in all cases) */ 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci switch (mode) { 49762306a36Sopenharmony_ci case FPCR_ROUND_RN: 49862306a36Sopenharmony_ci switch (dest->exp) { 49962306a36Sopenharmony_ci case 0 ... 0x3ffd: 50062306a36Sopenharmony_ci return; 50162306a36Sopenharmony_ci case 0x3ffe: 50262306a36Sopenharmony_ci /* As noted above, the input is always normal, so the 50362306a36Sopenharmony_ci guard bit (bit 63) is always set. therefore, the 50462306a36Sopenharmony_ci only case in which we will NOT round to 1.0 is when 50562306a36Sopenharmony_ci the input is exactly 0.5. */ 50662306a36Sopenharmony_ci if (oldmant.m64 == (1ULL << 63)) 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci case 0x3fff ... 0x401d: 51062306a36Sopenharmony_ci mask = 1 << (0x401d - dest->exp); 51162306a36Sopenharmony_ci if (!(oldmant.m32[0] & mask)) 51262306a36Sopenharmony_ci return; 51362306a36Sopenharmony_ci if (oldmant.m32[0] & (mask << 1)) 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) && 51662306a36Sopenharmony_ci !oldmant.m32[1]) 51762306a36Sopenharmony_ci return; 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci case 0x401e: 52062306a36Sopenharmony_ci if (oldmant.m32[1] & 0x80000000) 52162306a36Sopenharmony_ci return; 52262306a36Sopenharmony_ci if (oldmant.m32[0] & 1) 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci if (!(oldmant.m32[1] << 1)) 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci case 0x401f ... 0x403d: 52862306a36Sopenharmony_ci mask = 1 << (0x403d - dest->exp); 52962306a36Sopenharmony_ci if (!(oldmant.m32[1] & mask)) 53062306a36Sopenharmony_ci return; 53162306a36Sopenharmony_ci if (oldmant.m32[1] & (mask << 1)) 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci if (!(oldmant.m32[1] << (dest->exp - 0x401d))) 53462306a36Sopenharmony_ci return; 53562306a36Sopenharmony_ci break; 53662306a36Sopenharmony_ci default: 53762306a36Sopenharmony_ci return; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci case FPCR_ROUND_RZ: 54162306a36Sopenharmony_ci return; 54262306a36Sopenharmony_ci default: 54362306a36Sopenharmony_ci if (dest->sign ^ (mode - FPCR_ROUND_RM)) 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci switch (dest->exp) { 54962306a36Sopenharmony_ci case 0 ... 0x3ffe: 55062306a36Sopenharmony_ci dest->exp = 0x3fff; 55162306a36Sopenharmony_ci dest->mant.m64 = 1ULL << 63; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case 0x3fff ... 0x401e: 55462306a36Sopenharmony_ci mask = 1 << (0x401e - dest->exp); 55562306a36Sopenharmony_ci if (dest->mant.m32[0] += mask) 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci dest->mant.m32[0] = 0x80000000; 55862306a36Sopenharmony_ci dest->exp++; 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci case 0x401f ... 0x403e: 56162306a36Sopenharmony_ci mask = 1 << (0x403e - dest->exp); 56262306a36Sopenharmony_ci if (dest->mant.m32[1] += mask) 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci if (dest->mant.m32[0] += 1) 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci dest->mant.m32[0] = 0x80000000; 56762306a36Sopenharmony_ci dest->exp++; 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* modrem_kernel: Implementation of the FREM and FMOD instructions 57362306a36Sopenharmony_ci (which are exactly the same, except for the rounding used on the 57462306a36Sopenharmony_ci intermediate value) */ 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic struct fp_ext * 57762306a36Sopenharmony_cimodrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct fp_ext tmp; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci fp_dyadic_check(dest, src); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Infinities and zeros */ 58462306a36Sopenharmony_ci if (IS_INF(dest) || IS_ZERO(src)) { 58562306a36Sopenharmony_ci fp_set_nan(dest); 58662306a36Sopenharmony_ci return dest; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci if (IS_ZERO(dest) || IS_INF(src)) 58962306a36Sopenharmony_ci return dest; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* FIXME: there is almost certainly a smarter way to do this */ 59262306a36Sopenharmony_ci fp_copy_ext(&tmp, dest); 59362306a36Sopenharmony_ci fp_fdiv(&tmp, src); /* NOTE: src might be modified */ 59462306a36Sopenharmony_ci fp_roundint(&tmp, mode); 59562306a36Sopenharmony_ci fp_fmul(&tmp, src); 59662306a36Sopenharmony_ci fp_fsub(dest, &tmp); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* set the quotient byte */ 59962306a36Sopenharmony_ci fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7)); 60062306a36Sopenharmony_ci return dest; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci/* fp_fmod: Implements the kernel of the FMOD instruction. 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci Again, the argument order is backwards. The result, as defined in 60662306a36Sopenharmony_ci the Motorola manuals, is: 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci fmod(src,dest) = (dest - (src * floor(dest / src))) */ 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistruct fp_ext * 61162306a36Sopenharmony_cifp_fmod(struct fp_ext *dest, struct fp_ext *src) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci dprint(PINSTR, "fmod\n"); 61462306a36Sopenharmony_ci return modrem_kernel(dest, src, FPCR_ROUND_RZ); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* fp_frem: Implements the kernel of the FREM instruction. 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci frem(src,dest) = (dest - (src * round(dest / src))) 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistruct fp_ext * 62362306a36Sopenharmony_cifp_frem(struct fp_ext *dest, struct fp_ext *src) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci dprint(PINSTR, "frem\n"); 62662306a36Sopenharmony_ci return modrem_kernel(dest, src, FPCR_ROUND_RN); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistruct fp_ext * 63062306a36Sopenharmony_cifp_fint(struct fp_ext *dest, struct fp_ext *src) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci dprint(PINSTR, "fint\n"); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci fp_copy_ext(dest, src); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci fp_roundint(dest, FPDATA->rnd); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return dest; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistruct fp_ext * 64262306a36Sopenharmony_cifp_fintrz(struct fp_ext *dest, struct fp_ext *src) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci dprint(PINSTR, "fintrz\n"); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci fp_copy_ext(dest, src); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci fp_roundint(dest, FPCR_ROUND_RZ); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return dest; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistruct fp_ext * 65462306a36Sopenharmony_cifp_fscale(struct fp_ext *dest, struct fp_ext *src) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci int scale, oldround; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci dprint(PINSTR, "fscale\n"); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci fp_dyadic_check(dest, src); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* Infinities */ 66362306a36Sopenharmony_ci if (IS_INF(src)) { 66462306a36Sopenharmony_ci fp_set_nan(dest); 66562306a36Sopenharmony_ci return dest; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci if (IS_INF(dest)) 66862306a36Sopenharmony_ci return dest; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* zeroes */ 67162306a36Sopenharmony_ci if (IS_ZERO(src) || IS_ZERO(dest)) 67262306a36Sopenharmony_ci return dest; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Source exponent out of range */ 67562306a36Sopenharmony_ci if (src->exp >= 0x400c) { 67662306a36Sopenharmony_ci fp_set_ovrflw(dest); 67762306a36Sopenharmony_ci return dest; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* src must be rounded with round to zero. */ 68162306a36Sopenharmony_ci oldround = FPDATA->rnd; 68262306a36Sopenharmony_ci FPDATA->rnd = FPCR_ROUND_RZ; 68362306a36Sopenharmony_ci scale = fp_conv_ext2long(src); 68462306a36Sopenharmony_ci FPDATA->rnd = oldround; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* new exponent */ 68762306a36Sopenharmony_ci scale += dest->exp; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (scale >= 0x7fff) { 69062306a36Sopenharmony_ci fp_set_ovrflw(dest); 69162306a36Sopenharmony_ci } else if (scale <= 0) { 69262306a36Sopenharmony_ci fp_set_sr(FPSR_EXC_UNFL); 69362306a36Sopenharmony_ci fp_denormalize(dest, -scale); 69462306a36Sopenharmony_ci } else 69562306a36Sopenharmony_ci dest->exp = scale; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return dest; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 700