mirror of
https://github.com/qemu/qemu.git
synced 2025-08-04 00:12:05 +00:00

This functions is needed by upcoming m68k softfloat functions. Source code copied for WinUAE (tag 3500) (The WinUAE file has been copied from QEMU and has the QEMU licensing notice) Signed-off-by: Laurent Vivier <laurent@vivier.eu> Message-Id: <20180305203910.10391-2-laurent@vivier.eu>
277 lines
8.5 KiB
C
277 lines
8.5 KiB
C
/*
|
|
* Ported from a work by Andreas Grabher for Previous, NeXT Computer Emulator,
|
|
* derived from NetBSD M68040 FPSP functions,
|
|
* derived from release 2a of the SoftFloat IEC/IEEE Floating-point Arithmetic
|
|
* Package. Those parts of the code (and some later contributions) are
|
|
* provided under that license, as detailed below.
|
|
* It has subsequently been modified by contributors to the QEMU Project,
|
|
* so some portions are provided under:
|
|
* the SoftFloat-2a license
|
|
* the BSD license
|
|
* GPL-v2-or-later
|
|
*
|
|
* Any future contributions to this file will be taken to be licensed under
|
|
* the Softfloat-2a license unless specifically indicated otherwise.
|
|
*/
|
|
|
|
/* Portions of this work are licensed under the terms of the GNU GPL,
|
|
* version 2 or later. See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "softfloat.h"
|
|
#include "fpu/softfloat-macros.h"
|
|
|
|
static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status)
|
|
{
|
|
if (floatx80_is_signaling_nan(a, status)) {
|
|
float_raise(float_flag_invalid, status);
|
|
}
|
|
|
|
if (status->default_nan_mode) {
|
|
return floatx80_default_nan(status);
|
|
}
|
|
|
|
return floatx80_maybe_silence_nan(a, status);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
| Returns the modulo remainder of the extended double-precision floating-point
|
|
| value `a' with respect to the corresponding value `b'.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status)
|
|
{
|
|
flag aSign, zSign;
|
|
int32_t aExp, bExp, expDiff;
|
|
uint64_t aSig0, aSig1, bSig;
|
|
uint64_t qTemp, term0, term1;
|
|
|
|
aSig0 = extractFloatx80Frac(a);
|
|
aExp = extractFloatx80Exp(a);
|
|
aSign = extractFloatx80Sign(a);
|
|
bSig = extractFloatx80Frac(b);
|
|
bExp = extractFloatx80Exp(b);
|
|
|
|
if (aExp == 0x7FFF) {
|
|
if ((uint64_t) (aSig0 << 1)
|
|
|| ((bExp == 0x7FFF) && (uint64_t) (bSig << 1))) {
|
|
return propagateFloatx80NaN(a, b, status);
|
|
}
|
|
goto invalid;
|
|
}
|
|
if (bExp == 0x7FFF) {
|
|
if ((uint64_t) (bSig << 1)) {
|
|
return propagateFloatx80NaN(a, b, status);
|
|
}
|
|
return a;
|
|
}
|
|
if (bExp == 0) {
|
|
if (bSig == 0) {
|
|
invalid:
|
|
float_raise(float_flag_invalid, status);
|
|
return floatx80_default_nan(status);
|
|
}
|
|
normalizeFloatx80Subnormal(bSig, &bExp, &bSig);
|
|
}
|
|
if (aExp == 0) {
|
|
if ((uint64_t) (aSig0 << 1) == 0) {
|
|
return a;
|
|
}
|
|
normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0);
|
|
}
|
|
bSig |= LIT64(0x8000000000000000);
|
|
zSign = aSign;
|
|
expDiff = aExp - bExp;
|
|
aSig1 = 0;
|
|
if (expDiff < 0) {
|
|
return a;
|
|
}
|
|
qTemp = (bSig <= aSig0);
|
|
if (qTemp) {
|
|
aSig0 -= bSig;
|
|
}
|
|
expDiff -= 64;
|
|
while (0 < expDiff) {
|
|
qTemp = estimateDiv128To64(aSig0, aSig1, bSig);
|
|
qTemp = (2 < qTemp) ? qTemp - 2 : 0;
|
|
mul64To128(bSig, qTemp, &term0, &term1);
|
|
sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1);
|
|
shortShift128Left(aSig0, aSig1, 62, &aSig0, &aSig1);
|
|
}
|
|
expDiff += 64;
|
|
if (0 < expDiff) {
|
|
qTemp = estimateDiv128To64(aSig0, aSig1, bSig);
|
|
qTemp = (2 < qTemp) ? qTemp - 2 : 0;
|
|
qTemp >>= 64 - expDiff;
|
|
mul64To128(bSig, qTemp << (64 - expDiff), &term0, &term1);
|
|
sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1);
|
|
shortShift128Left(0, bSig, 64 - expDiff, &term0, &term1);
|
|
while (le128(term0, term1, aSig0, aSig1)) {
|
|
++qTemp;
|
|
sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1);
|
|
}
|
|
}
|
|
return
|
|
normalizeRoundAndPackFloatx80(
|
|
80, zSign, bExp + expDiff, aSig0, aSig1, status);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
| Returns the mantissa of the extended double-precision floating-point
|
|
| value `a'.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
floatx80 floatx80_getman(floatx80 a, float_status *status)
|
|
{
|
|
flag aSign;
|
|
int32_t aExp;
|
|
uint64_t aSig;
|
|
|
|
aSig = extractFloatx80Frac(a);
|
|
aExp = extractFloatx80Exp(a);
|
|
aSign = extractFloatx80Sign(a);
|
|
|
|
if (aExp == 0x7FFF) {
|
|
if ((uint64_t) (aSig << 1)) {
|
|
return propagateFloatx80NaNOneArg(a , status);
|
|
}
|
|
float_raise(float_flag_invalid , status);
|
|
return floatx80_default_nan(status);
|
|
}
|
|
|
|
if (aExp == 0) {
|
|
if (aSig == 0) {
|
|
return packFloatx80(aSign, 0, 0);
|
|
}
|
|
normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
|
|
}
|
|
|
|
return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
|
|
0x3FFF, aSig, 0, status);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
| Returns the exponent of the extended double-precision floating-point
|
|
| value `a' as an extended double-precision value.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
floatx80 floatx80_getexp(floatx80 a, float_status *status)
|
|
{
|
|
flag aSign;
|
|
int32_t aExp;
|
|
uint64_t aSig;
|
|
|
|
aSig = extractFloatx80Frac(a);
|
|
aExp = extractFloatx80Exp(a);
|
|
aSign = extractFloatx80Sign(a);
|
|
|
|
if (aExp == 0x7FFF) {
|
|
if ((uint64_t) (aSig << 1)) {
|
|
return propagateFloatx80NaNOneArg(a , status);
|
|
}
|
|
float_raise(float_flag_invalid , status);
|
|
return floatx80_default_nan(status);
|
|
}
|
|
|
|
if (aExp == 0) {
|
|
if (aSig == 0) {
|
|
return packFloatx80(aSign, 0, 0);
|
|
}
|
|
normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
|
|
}
|
|
|
|
return int32_to_floatx80(aExp - 0x3FFF, status);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------
|
|
| Scales extended double-precision floating-point value in operand `a' by
|
|
| value `b'. The function truncates the value in the second operand 'b' to
|
|
| an integral value and adds that value to the exponent of the operand 'a'.
|
|
| The operation performed according to the IEC/IEEE Standard for Binary
|
|
| Floating-Point Arithmetic.
|
|
*----------------------------------------------------------------------------*/
|
|
|
|
floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status)
|
|
{
|
|
flag aSign, bSign;
|
|
int32_t aExp, bExp, shiftCount;
|
|
uint64_t aSig, bSig;
|
|
|
|
aSig = extractFloatx80Frac(a);
|
|
aExp = extractFloatx80Exp(a);
|
|
aSign = extractFloatx80Sign(a);
|
|
bSig = extractFloatx80Frac(b);
|
|
bExp = extractFloatx80Exp(b);
|
|
bSign = extractFloatx80Sign(b);
|
|
|
|
if (bExp == 0x7FFF) {
|
|
if ((uint64_t) (bSig << 1) ||
|
|
((aExp == 0x7FFF) && (uint64_t) (aSig << 1))) {
|
|
return propagateFloatx80NaN(a, b, status);
|
|
}
|
|
float_raise(float_flag_invalid , status);
|
|
return floatx80_default_nan(status);
|
|
}
|
|
if (aExp == 0x7FFF) {
|
|
if ((uint64_t) (aSig << 1)) {
|
|
return propagateFloatx80NaN(a, b, status);
|
|
}
|
|
return packFloatx80(aSign, floatx80_infinity.high,
|
|
floatx80_infinity.low);
|
|
}
|
|
if (aExp == 0) {
|
|
if (aSig == 0) {
|
|
return packFloatx80(aSign, 0, 0);
|
|
}
|
|
if (bExp < 0x3FFF) {
|
|
return a;
|
|
}
|
|
normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
|
|
}
|
|
|
|
if (bExp < 0x3FFF) {
|
|
return a;
|
|
}
|
|
|
|
if (0x400F < bExp) {
|
|
aExp = bSign ? -0x6001 : 0xE000;
|
|
return roundAndPackFloatx80(status->floatx80_rounding_precision,
|
|
aSign, aExp, aSig, 0, status);
|
|
}
|
|
|
|
shiftCount = 0x403E - bExp;
|
|
bSig >>= shiftCount;
|
|
aExp = bSign ? (aExp - bSig) : (aExp + bSig);
|
|
|
|
return roundAndPackFloatx80(status->floatx80_rounding_precision,
|
|
aSign, aExp, aSig, 0, status);
|
|
}
|
|
|
|
floatx80 floatx80_move(floatx80 a, float_status *status)
|
|
{
|
|
flag aSign;
|
|
int32_t aExp;
|
|
uint64_t aSig;
|
|
|
|
aSig = extractFloatx80Frac(a);
|
|
aExp = extractFloatx80Exp(a);
|
|
aSign = extractFloatx80Sign(a);
|
|
|
|
if (aExp == 0x7FFF) {
|
|
if ((uint64_t)(aSig << 1)) {
|
|
return propagateFloatx80NaNOneArg(a, status);
|
|
}
|
|
return a;
|
|
}
|
|
if (aExp == 0) {
|
|
if (aSig == 0) {
|
|
return a;
|
|
}
|
|
normalizeRoundAndPackFloatx80(status->floatx80_rounding_precision,
|
|
aSign, aExp, aSig, 0, status);
|
|
}
|
|
return roundAndPackFloatx80(status->floatx80_rounding_precision, aSign,
|
|
aExp, aSig, 0, status);
|
|
}
|