Using local m68k floatx80_getman(), floatx80_getexp(), floatx80_scale() [copied from previous: Written by Andreas Grabher for Previous, NeXT Computer Emulator.] Signed-off-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20180224201802.911-5-laurent@vivier.eu>
		
			
				
	
	
		
			250 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			7.7 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);
 | 
						|
}
 |