diff --git a/Emulator/Components/CPU/Moira/FpuFormats.cpp b/Emulator/Components/CPU/Moira/FpuFormats.cpp deleted file mode 100644 index a46d788e1..000000000 --- a/Emulator/Components/CPU/Moira/FpuFormats.cpp +++ /dev/null @@ -1,740 +0,0 @@ -// ----------------------------------------------------------------------------- -// This file is part of Moira - A Motorola 68k emulator -// -// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de -// Published under the terms of the MIT License -// ----------------------------------------------------------------------------- - -#include "config.h" -#include "FpuFormats.h" -#include "MoiraFPU.h" -#include -#include - -namespace vamiga::moira { - -// -// FpuByte -// - -FpuByte::FpuByte(const FpuExtended &value, ExcHandler handler) -{ - softfloat::float_exception_flags = 0; - auto converted = softfloat::floatx80_to_int32(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - handler(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - handler(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - handler(FPEXP_UNFL); - } - if (converted > 0x7F) { - handler(FPEXP_OPERR); converted = 0x7F; - } - if (converted < -0x80) { - handler(FPEXP_OPERR); converted = -0x80; - } - - raw = i8(converted); -} - -FpuByte::FpuByte(const FPUReg ®, ExcHandler handler) : FpuByte(reg.val, handler) { } - - -// -// FpuWord -// - -FpuWord::FpuWord(const FpuExtended &value, ExcHandler handler) -{ - softfloat::float_exception_flags = 0; - auto converted = softfloat::floatx80_to_int32(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - handler(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - handler(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - handler(FPEXP_UNFL); - } - if (converted > 0x7FFF) { - handler(FPEXP_OPERR); converted = 0x7FFF; - } - if (converted < -0x8000) { - handler(FPEXP_OPERR); converted = -0x8000; - } - - raw = i16(converted); -} - -FpuWord::FpuWord(const FPUReg ®, ExcHandler handler) : FpuWord(reg.val, handler) { } - - -// -// FpuLong -// - -FpuLong::FpuLong(const FpuExtended &value, ExcHandler handler) -{ - softfloat::float_exception_flags = 0; - raw = u32(softfloat::floatx80_to_int32(value.raw)); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - handler(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - handler(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - handler(FPEXP_UNFL); - } -} - -FpuLong::FpuLong(const FPUReg ®, ExcHandler handler) : FpuLong(reg.val, handler) { } - - -// -// FpuSingle -// - -FpuSingle::FpuSingle(const class FpuExtended &value, ExcHandler handler) -{ - u32 flags = 0; - - long double ldv = value.asLongDouble(); - - if (ldv > std::numeric_limits::max()) { - - float posinf = std::copysign(std::numeric_limits::infinity(), 1.0f); - flags |= FPEXP_OVFL | FPEXP_INEX2; - raw = *((u32 *)&posinf); - - } else if (ldv < std::numeric_limits::lowest()) { - - float neginf = std::copysign(std::numeric_limits::infinity(), -1.0f); - flags |= FPEXP_OVFL | FPEXP_INEX2; - raw = *((u32 *)&neginf); - - } else { - - softfloat::float_exception_flags = 0; - raw = floatx80_to_float32(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - } - handler(flags); -} - -FpuSingle::FpuSingle(const class FpuExtended &value, FpuRoundingMode mode, ExcHandler handler) -{ - const float maxflt = std::numeric_limits::max(); - const float minflt = std::numeric_limits::lowest(); - const float posinf = std::copysign(std::numeric_limits::infinity(), 1.0f); - const float neginf = std::copysign(std::numeric_limits::infinity(), -1.0f); - - u32 flags = 0; - - long double ldv = value.asLongDouble(); - - if (ldv > maxflt) { - - if (mode == FPU_RND_ZERO || mode == FPU_RND_DOWNWARD) { - - flags |= FPEXP_OVFL | FPEXP_INEX2; - raw = *((u32 *)&maxflt); - - } else { - - flags |= FPEXP_OVFL | FPEXP_INEX2; - raw = *((u32 *)&posinf); - } - - } else if (ldv < minflt) { - - if (mode == FPU_RND_ZERO || mode == FPU_RND_UPWARD) { - - flags |= FPEXP_OVFL | FPEXP_INEX2; - raw = *((u32 *)&minflt); - - } else { - - flags |= FPEXP_OVFL | FPEXP_INEX2; - raw = *((u32 *)&neginf); - } - - } else { - - softfloat::float_exception_flags = 0; - raw = floatx80_to_float32(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - } - handler(flags); -} - -FpuSingle::FpuSingle(const FPUReg ®, ExcHandler handler) -{ - *this = FpuSingle(reg.val, handler); -} - -FpuSingle::FpuSingle(float value) -{ - raw = *((u32 *)&value); -} - -bool -FpuSingle::signbit() const -{ - return raw & (1 << 31); -} - -bool -FpuSingle::isinf() const -{ - float fval = *((float *)&raw); - return std::isinf(fval); -} - -bool -FpuSingle::isposinf() const -{ - return isinf() && !signbit(); -} - -bool -FpuSingle::isneginf() const -{ - return isinf() && signbit(); -} - - -// -// FpuDouble -// - -FpuDouble::FpuDouble(double value) -{ - raw = *((u64 *)&value); -} - -FpuDouble::FpuDouble(const class FpuExtended &value, ExcHandler handler) -{ - u32 flags = 0; - softfloat::float_exception_flags = 0; - - raw = floatx80_to_float64(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - - handler(flags); -} - -FpuDouble::FpuDouble(const FPUReg ®, ExcHandler handler) : FpuDouble(reg.val, handler) { } - -bool -FpuDouble::signbit() const -{ - return raw & (1L << 63); -} - -bool -FpuDouble::isinf() const -{ - double dval = *((double *)&raw); - return std::isinf(dval); -} - -bool -FpuDouble::isposinf() const -{ - return isinf() && !signbit(); -} - -bool -FpuDouble::isneginf() const -{ - return isinf() && signbit(); -} - - -// -// FpuExtended -// - -FpuExtended FpuExtended::nan = FpuExtended(0x7FFF, 0xFFFFFFFFFFFFFFFF); -FpuExtended FpuExtended::zero = FpuExtended(0x0000, 0x0000000000000000); -FpuExtended FpuExtended::posZero = FpuExtended(0x0000, 0x0000000000000000); -FpuExtended FpuExtended::negZero = FpuExtended(0x8000, 0x0000000000000000); -FpuExtended FpuExtended::inf = FpuExtended(0x7FFF, 0x0000000000000000); -FpuExtended FpuExtended::posInf = FpuExtended(0x7FFF, 0x0000000000000000); -FpuExtended FpuExtended::negInf = FpuExtended(0xFFFF, 0x0000000000000000); - -FpuExtended::FpuExtended(const FpuByte &value, ExcHandler handler) -{ - u32 flags = 0; - softfloat::float_exception_flags = 0; - - raw = softfloat::int64_to_floatx80(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - - handler(flags); -} - -FpuExtended::FpuExtended(const FpuWord &value, ExcHandler handler) -{ - u32 flags = 0; - softfloat::float_exception_flags = 0; - - raw = softfloat::int64_to_floatx80(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - - handler(flags); -} - -FpuExtended::FpuExtended(const FpuLong &value, ExcHandler handler) -{ - u32 flags = 0; - softfloat::float_exception_flags = 0; - - raw = softfloat::int32_to_floatx80(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - - handler(flags); -} - -FpuExtended::FpuExtended(const FpuSingle &value, ExcHandler handler) -{ - if (value.isposinf()) { *this = FpuExtended::posInf; return; } - if (value.isneginf()) { *this = FpuExtended::negInf; return; } - - u32 flags = 0; - softfloat::float_exception_flags = 0; - - raw = softfloat::float32_to_floatx80(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - - handler(flags); -} - -FpuExtended::FpuExtended(const FpuDouble &value, ExcHandler handler) -{ - if (value.isposinf()) { *this = FpuExtended::posInf; return; } - if (value.isneginf()) { *this = FpuExtended::negInf; return; } - - u32 flags = 0; - softfloat::float_exception_flags = 0; - - raw = softfloat::float64_to_floatx80(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - flags |= FPEXP_INEX2; - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - - handler(flags); -} - -FpuExtended::FpuExtended(const FpuPacked &packed, FpuRoundingMode mode, ExcHandler handler) -{ - char str[128], *ch = str; - i32 ex = 0; u64 mal = 0, mar = 0; - - auto dw1 = packed.data[0]; - auto dw2 = packed.data[1]; - auto dw3 = packed.data[2]; - - // Extract the sign bits - auto msign = bool(dw1 & 0x80000000); - auto esign = bool(dw1 & 0x40000000); - - // Compose the exponent - ex = (char)((dw1 >> 24) & 0xF); - ex = ex * 10 + (char)((dw1 >> 20) & 0xF); - ex = ex * 10 + (char)((dw1 >> 16) & 0xF); - - // Compose the fractional part of the mantissa - mar = (char)((dw2 >> 28) & 0xF); - mar = mar * 10 + (char)((dw2 >> 24) & 0xF); - mar = mar * 10 + (char)((dw2 >> 20) & 0xF); - mar = mar * 10 + (char)((dw2 >> 16) & 0xF); - mar = mar * 10 + (char)((dw2 >> 12) & 0xF); - mar = mar * 10 + (char)((dw2 >> 8) & 0xF); - mar = mar * 10 + (char)((dw2 >> 4) & 0xF); - mar = mar * 10 + (char)((dw2 >> 0) & 0xF); - mar = mar * 10 + (char)((dw3 >> 28) & 0xF); - mar = mar * 10 + (char)((dw3 >> 24) & 0xF); - mar = mar * 10 + (char)((dw3 >> 20) & 0xF); - mar = mar * 10 + (char)((dw3 >> 16) & 0xF); - mar = mar * 10 + (char)((dw3 >> 12) & 0xF); - mar = mar * 10 + (char)((dw3 >> 8) & 0xF); - mar = mar * 10 + (char)((dw3 >> 4) & 0xF); - mar = mar * 10 + (char)((dw3 >> 0) & 0xF); - - // Compose the integer part of the mantissa - mal = (char)((dw1 >> 0) & 0xF); - mal += mar / 10000000000000000; - mar %= 10000000000000000; - - // Check for special cases - if (ex == 1665) { - - if (mar == 0) { - - if (((dw1 >> 28) & 0x7) == 0x7) { - *this = FpuExtended(msign ? 0xFFFF : 0x7FFF, 0); // Infinity - return; - } else { - *this = FpuExtended(msign ? 0x8000 : 0, 0); // ? - return; - } - - } else { - - if (((dw1 >> 28) & 0x7) == 0x7) { - *this = FpuExtended(msign ? 0xFFFF : 0x7FFF, u64(dw2) << 32 | dw3); // NaN - return; - } else { - // *this = Float80(msign ? 0x8000 : 0, 0); // ? - // return; - } - } - } - - // Write the integer part of the mantissa - if (msign) *ch++ = '-'; - for (int i = 1; i >= 0; i--) { ch[i] = (mal % 10) + '0'; mal /= 10; } - ch += 2; - - // Write the fractional part of the mantissa - *ch++ = '.'; - for (int i = 15; i >= 0; i--) { ch[i] = (mar % 10) + '0'; mar /= 10; } - ch += 16; - - // Write the exponent - *ch++ = 'E'; - if (esign) *ch++ = '-'; - for (int i = 3; i >= 0; i--) { ch[i] = (ex % 10) + '0'; ex /= 10; } - ch += 4; - - // Terminate the string - *ch = 0; - - *this = FpuExtended(str, mode, handler); -} - -FpuExtended::FpuExtended(const std::string &s, FpuRoundingMode mode, ExcHandler handler) -{ - long double value; - - auto old = FPU::fesetround(mode); - sscanf(s.c_str(), "%Le", &value); - FPU::fesetround(old); - - *this = FpuExtended(value, mode); - normalize(); -} - -FpuExtended::FpuExtended(long double value, FpuRoundingMode mode, ExcHandler handler) -{ - // Handle special cases - if (value == 0.0) { - - *this = std::signbit(value) ? negZero : posZero; - return; - } - if (std::isinf(value)) { - - *this = std::signbit(value) ? negInf : posInf; - return; - } - - // Extract the exponent and the mantissa - int e; auto m = frexpl(value, &e); - - // Subtract one, because the first digit is left of the comma - e -= 1; - - // Round the mantissa - switch (mode) { - case FPU_RND_NEAREST: m = std::round(std::ldexpl(m, 64)); break; - case FPU_RND_ZERO: m = std::truncl(std::ldexpl(m, 64)); break; - case FPU_RND_UPWARD: m = std::ceill(std::ldexpl(m, 64)); break; - default: m = std::floorl(std::ldexpl(m, 64)); break; - } - - printf("e = %d m = %Lf %llu\n", e, m, (u64)m); - - // Compose the result - *this = FpuExtended(value < 0.0, e, (u64)std::abs(m), handler); -} - -FpuExtended::FpuExtended(bool mSign, i64 e, u64 m, ExcHandler handler) -{ - if (e < -0x3FFF) { - - printf("UNDERFLOW e = %lld\n", e); - *this = zero; - handler(FPEXP_UNFL); - return; - } - if (e > 0x3FFF) { - - printf("OVERFLOW e = %lld\n", e); - *this = inf.copysign(mSign); - handler(FPEXP_OVFL); - return; - } - - raw.high = (mSign ? 0x8000 : 0x0000) | (u16(e + 0x3FFF) & 0x7FFF); - raw.low = m; -} - -long double -FpuExtended::asLongDouble() const -{ - if (isinf()) { - return std::copysignl(std::numeric_limits::infinity(), signbit() ? -1.0L : 1.0L); - } - if (isnan()) { - return std::copysignl(std::numeric_limits::quiet_NaN(), signbit() ? -1.0L : 1.0L); - } - - auto result = std::ldexpl((long double)man(), (int)exp() - 63); - return signbit() ? -result : result; -} - -FpuExtended -FpuExtended::operator-() const -{ - auto result = *this; - result.raw.high ^= 0x8000; - return result; -} - -bool -FpuExtended::operator==(const FpuExtended& rhs) const -{ - // return softfloat::floatx80_eq(this->raw, rhs.raw); - return this->raw.high == rhs.raw.high && this->raw.low == rhs.raw.low; -} - -bool -FpuExtended::operator!=(const FpuExtended& rhs) const -{ - // return !softfloat::floatx80_eq(this->raw, rhs.raw); - return this->raw.high != rhs.raw.high || this->raw.low != rhs.raw.low; -} - -bool -FpuExtended::operator<=(const FpuExtended& rhs) const -{ - return softfloat::floatx80_le(this->raw, rhs.raw); -} - -bool -FpuExtended::operator>=(const FpuExtended& rhs) const -{ - return rhs <= *this; -} - -bool -FpuExtended::operator<(const FpuExtended& rhs) const -{ - return softfloat::floatx80_lt(this->raw, rhs.raw); -} - -bool -FpuExtended::operator>(const FpuExtended& rhs) const -{ - return rhs < *this; -} - -int -FpuExtended::fpclassify() const -{ - if (isinf()) return FP_INFINITE; - if (isnan()) return FP_NAN; - if (iszero()) return FP_ZERO; - if (issubnormal()) return FP_SUBNORMAL; - - return FP_NORMAL; -} - -void -FpuExtended::normalize() -{ - if (!minexp() && !maxexp() && man()) { - - while (!isnormal()) { - - raw.high -= 1; - raw.low <<= 1; - } - } -} - -std::pair -FpuExtended::frexp10() const -{ - long double val = asLongDouble(); - int e = iszero() ? 0 : 1 + (int)std::floor(std::log10(std::fabs(val))); - long double m = val * std::powl(10L, -e); - - return { e, m }; -}; - -FpuExtended -FpuExtended::copysign(bool sign) -{ - if (sign) { - return FpuExtended(raw.high | 0x8000, raw.low); - } else { - return FpuExtended(raw.high & 0x7FFF, raw.low); - } -} - -FpuExtended -FpuExtended::copysign(const FpuExtended &other) -{ - return copysign(other.signbit()); -} - - -// -// FpuPacked -// - -FpuPacked::FpuPacked(const FpuExtended &value, int k, FpuRoundingMode mode, ExcHandler handler) -{ - u32 statusbits = 0; - - // Get exponent - auto e = value.frexp10().first - 1; - - // Check k-factor - if (k > 17) { - statusbits = FPEXP_OPERR | FPEXP_INEX2; - k = 17; - } - if (k < -17) { - k = -17; - } - - // Setup stringstream - std::stringstream ss; - long double test; - ss.setf(std::ios::scientific, std::ios::floatfield); - ss.precision(k > 0 ? k - 1 : e - k); - - // Create string representation - auto ldval = value.asLongDouble(); - auto old = FPU::fesetround(mode); - ss << ldval; - std::stringstream ss2(ss.str()); - ss2 >> test; - FPU::fesetround(old); - - if (ldval != test) { - statusbits |= FPEXP_INEX2; - } - - // Assemble exponent - data[0] = e < 0 ? 0x40000000 : 0; - data[0] |= (e % 10) << 16; e /= 10; - data[0] |= (e % 10) << 20; e /= 10; - data[0] |= (e % 10) << 24; - - // Assemble mantisse - char c; - int shift = 64; - - while (ss.get(c)) { - - if (c == '+') continue; - if (c == '-') data[0] |= 0x80000000; - if (c >= '0' && c <= '9') { - if (shift == 64) data[0] |= u32(c - '0'); - else if (shift >= 32) data[1] |= u32(c - '0') << (shift - 32); - else if (shift >= 0) data[2] |= u32(c - '0') << shift; - shift -= 4; - } - if (c == 'e' || c == 'E') break; - } - - handler(statusbits); -} - -FpuPacked::FpuPacked(const FPUReg ®, int k, FpuRoundingMode mode, - ExcHandler handler) : FpuPacked(reg.val, k, mode, handler) { } - -} diff --git a/Emulator/Components/CPU/Moira/FpuFormats.h b/Emulator/Components/CPU/Moira/FpuFormats.h deleted file mode 100644 index 0029793fd..000000000 --- a/Emulator/Components/CPU/Moira/FpuFormats.h +++ /dev/null @@ -1,222 +0,0 @@ -// ----------------------------------------------------------------------------- -// This file is part of Moira - A Motorola 68k emulator -// -// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de -// Published under the terms of the MIT License -// ----------------------------------------------------------------------------- - -#pragma once - -#include "MoiraTypes.h" -#include "softfloat.h" -#include - -namespace vamiga::moira { - -/* The Motorola floating-point unit supports seven data formats: - * - * Byte Integer (B) - * Word Integer (W) - * Long Word Integer (L) - * Single Precision Real (S) - * Double Precision Real (D) - * Extended Precision Real (X) - * Packed Decimal String Real (P) - * - * Each of these formats is managed by a seperate struct. - */ - -struct FpuByte; -struct FpuWord; -struct FpuLong; -struct FpuSingle; -struct FpuDouble; -struct FpuExtended; -struct FpuPacked; - -class FPUReg; - -typedef std::function ExcHandler; - - -// -// FpuByte -// - -struct FpuByte { - - i8 raw; - - FpuByte() : raw(0) { }; - FpuByte(i8 value) : raw(value) { }; - FpuByte(u32 value) : raw((i8)value) { }; - FpuByte(const FpuExtended &value, ExcHandler handler = [](auto&&...) {}); - FpuByte(const FPUReg &value, ExcHandler handler = [](auto&&...) {}); -}; - - -// -// FpuWord -// - -struct FpuWord { - - i16 raw; - - FpuWord() : raw(0) { }; - FpuWord(i16 value) : raw(value) { }; - FpuWord(u32 value) : raw((i16)value) { }; - FpuWord(const FpuExtended &value, ExcHandler handler = [](auto&&...) {}); - FpuWord(const FPUReg &value, ExcHandler handler = [](auto&&...) {}); -}; - - -// -// FpuLong -// - -struct FpuLong { - - i32 raw; - - FpuLong() : raw(0) { }; - FpuLong(i32 value) : raw(value) { }; - FpuLong(u32 value) : raw((i32)value) { }; - FpuLong(const FpuExtended &value, ExcHandler handler = [](auto&&...) {}); - FpuLong(const FPUReg &value, ExcHandler handler = [](auto&&...) {}); -}; - - -// -// FpuSingle -// - -struct FpuSingle { - - u32 raw; - - FpuSingle() : raw(0) { }; - FpuSingle(u32 value) : raw(value) { }; - FpuSingle(float value); - FpuSingle(const FpuExtended &value, ExcHandler handler = [](auto&&...) {}); - FpuSingle(const FpuExtended &value, FpuRoundingMode mode, ExcHandler handler = [](auto&&...) {}); - FpuSingle(const FPUReg &value, ExcHandler handler = [](auto&&...) {}); - - bool signbit() const; - bool isinf() const; - bool isposinf() const; - bool isneginf() const; -}; - - -// -// FpuDouble -// - -struct FpuDouble { - - u64 raw; - - FpuDouble() : raw(0) { }; - FpuDouble(u64 value) : raw(value) { }; - FpuDouble(u32 hi, u32 lo) : raw(u64(hi) << 32 | lo) { }; - FpuDouble(double value); - FpuDouble(const FpuExtended &value, ExcHandler handler = [](auto&&...) {}); - FpuDouble(const class FPUReg &value, ExcHandler handler = [](auto&&...) {}); - - bool signbit() const; - bool isinf() const; - bool isposinf() const; - bool isneginf() const; -}; - - -// -// FpuExtended -// - -struct FpuExtended { - - softfloat::floatx80 raw; - - // Constants - static constexpr i64 bias = 0x3FFF; - - static FpuExtended nan; - static FpuExtended zero; - static FpuExtended posZero; - static FpuExtended negZero; - static FpuExtended inf; - static FpuExtended posInf; - static FpuExtended negInf; - - // Constructors - FpuExtended() { raw = { }; } - FpuExtended(u32 hi, u64 lo) { raw.high = u16(hi); raw.low = lo; } - FpuExtended(u32 hi, u32 lo1, u32 lo2) { raw.high = u16(hi); raw.low = u64(lo1) << 32 | lo2; } - FpuExtended(const FpuByte &value, ExcHandler handler = [](auto&&...) {}); - FpuExtended(const FpuWord &value, ExcHandler handler = [](auto&&...) {}); - FpuExtended(const FpuLong &value, ExcHandler handler = [](auto&&...) {}); - FpuExtended(const FpuSingle &value, ExcHandler handler = [](auto&&...) {}); - FpuExtended(const FpuDouble &value, ExcHandler handler = [](auto&&...) {}); - FpuExtended(const FpuPacked &value, FpuRoundingMode mode, ExcHandler handler = [](auto&&...) {}); - FpuExtended(const std::string &s, FpuRoundingMode mode, ExcHandler handler = [](auto&&...) {}); - FpuExtended(long double value, FpuRoundingMode mode = FPU_RND_NEAREST, ExcHandler handler = [](auto&&...) {}); - FpuExtended(bool mSign, i64 e, u64 m, ExcHandler handler = [](auto&&...) {}); - - // Converters - long double asLongDouble() const; - - // Operators - FpuExtended operator-() const; - bool operator==(const FpuExtended& rhs) const; - bool operator!=(const FpuExtended& rhs) const; - bool operator<=(const FpuExtended& rhs) const; - bool operator>=(const FpuExtended& rhs) const; - bool operator<(const FpuExtended& rhs) const; - bool operator>(const FpuExtended& rhs) const; - - // Analyzers - bool signbit() const { return raw.high & 0x8000; } - i64 exp() const { return i64(raw.high & 0x7FFF) - bias; } - u64 man() const { return raw.low; } - bool minexp() const { return (raw.high & 0x7FFF) == 0; } - bool maxexp() const { return (raw.high & 0x7FFF) == 0x7FFF; } - bool m62() const { return raw.low & (1L << 62); } - bool m63() const { return raw.low & (1L << 63); } - - int fpclassify() const; - bool isfinite() const { return !isnan() && !isinf(); } - bool isinf() const { return (raw.high & 0x7FFF) == 0x7FFF && raw.low == 0; } - bool isnan() const { return (raw.high & 0x7FFF) == 0x7FFF && raw.low != 0; } - bool isSignalingNaN() const { return isnan() && !m62(); } - bool isNonsignalingNaN() const { return isnan() && m62(); } - bool isnormal() const { return !minexp() && !maxexp() && m63(); } - bool issubnormal() const { return (raw.high & 0x7FFF) == 0 && raw.low && !m63(); } - bool isnegative() const { return signbit(); } - bool ispositive() const { return !signbit(); } - bool iszero() const { return (raw.high & 0x7FFF) == 0 && raw.low == 0; } - - // Misc - void normalize(); - std::pair frexp10() const; - FpuExtended copysign(bool sign); - FpuExtended copysign(const FpuExtended &other); -}; - - -// -// FpuPacked -// - -struct FpuPacked { - - u32 data[3] = { }; - - FpuPacked() { } - FpuPacked(u32 dw1, u32 dw2, u32 dw3) { data[0] = dw1; data[1] = dw2; data[2] = dw3; } - FpuPacked(const FpuExtended &value, int kfactor, FpuRoundingMode mode, ExcHandler handler = [](auto&&...) {}); - FpuPacked(const class FPUReg &value, int kfactor, FpuRoundingMode mode, ExcHandler handler = [](auto&&...) {}); -}; - -} diff --git a/Emulator/Components/CPU/Moira/MoiraFPU.cpp b/Emulator/Components/CPU/Moira/MoiraFPU.cpp deleted file mode 100644 index 244fe9424..000000000 --- a/Emulator/Components/CPU/Moira/MoiraFPU.cpp +++ /dev/null @@ -1,1710 +0,0 @@ -// ----------------------------------------------------------------------------- -// This file is part of Moira - A Motorola 68k emulator -// -// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de -// Published under the terms of the MIT License -// ----------------------------------------------------------------------------- - -#include "config.h" -#include "MoiraFPU.h" -#include "Moira.h" -#include "MoiraMacros.h" -#include -#include - -namespace vamiga::moira { - -FpuExtended -FPUReg::round() -{ - FpuExtended result = val; - - if (val.isfinite()) { - - if (fpu.getPrecision() == FPU_PREC_SINGLE) { - - result = FpuSingle(result, fpu.getRoundingMode(), fpu.exceptionHandler); - } - if (fpu.getPrecision() == FPU_PREC_DOUBLE) { - - result = FpuDouble(result, fpu.exceptionHandler); - } - if (issubnormal()) { - - fpu.setExcStatusBit(FPEXP_UNFL); - } - } - - return result; -} - -void -FPUReg::load(const FpuExtended other) -{ - val = other; - - // Round to the correct precision - val = round(); - - // Normalize the result - val.normalize(); -} - -std::ostream & -FPUReg::operator<<(std::ostream &stream) -{ - stream << val.raw.high << ":" << val.raw.low; - stream << " (" << val.asLongDouble() << ")"; - - return stream; -} - -FPU::FPU(Moira& ref) : moira(ref) -{ - static_assert(!REQUIRE_PRECISE_FPU || sizeof(long double) > 8, - "No long double support. FPU inaccuracies may occur."); - - reset(); -} - -void -FPU::reset() -{ - for (int i = 0; i < 8; i++) { - fpr[i].reset(); - } - - fpiar = 0; - fpsr = 0; - fpcr = 0; - - resetState = true; -} - -bool -FPU::inResetState() -{ - /* - for (int i = 0; i < 8; i++) { - if (fpr[i].val != FpuExtended::nan) return false; - } - - return fpiar == 0 && fpsr == 0 && fpcr == 0; - */ - return resetState; -} - -FpuPrecision -FPU::getPrecision() const -{ - switch (fpcr & 0xC0) { - - case 0x00: return FPU_PREC_EXTENDED; - case 0x40: return FPU_PREC_SINGLE; - default: return FPU_PREC_DOUBLE; - } -} - -FpuRoundingMode -FPU::getRoundingMode() const -{ - switch (fpcr & 0x30) { - - case 0x00: return FPU_RND_NEAREST; - case 0x10: return FPU_RND_ZERO; - case 0x20: return FPU_RND_DOWNWARD; - default: return FPU_RND_UPWARD; - } -} - -FpuRoundingMode -FPU::fesetround(FpuRoundingMode mode) -{ - auto oldMode = fegetround(); - - switch (mode) { - - case FPU_RND_NEAREST: ::fesetround(FE_TONEAREST); break; - case FPU_RND_ZERO: ::fesetround(FE_TOWARDZERO); break; - case FPU_RND_DOWNWARD: ::fesetround(FE_DOWNWARD); break; - default: ::fesetround(FE_UPWARD); break; - } - - switch (oldMode) { - - case FE_TONEAREST: return FPU_RND_NEAREST; - case FE_TOWARDZERO: return FPU_RND_ZERO; - case FE_DOWNWARD: return FPU_RND_DOWNWARD; - default: return FPU_RND_UPWARD; - } -} - -int -FPU::stateFrameSize(FpuFrameType type) -{ - switch (type) { - - case FPU_FRAME_IDLE: - - if (model == INTERNAL_FPU) return 0; - if (model == M68881) return 24; - if (model == M68882) return 56; - - case FPU_FRAME_UNIMP: - - if (model == INTERNAL_FPU) return 44; - if (model == M68881) return 0; - if (model == M68882) return 0; - - case FPU_FRAME_BUSY: - - if (model == INTERNAL_FPU) return 92; - if (model == M68881) return 180; - if (model == M68882) return 212; - - default: - - return 0; - } -} - -int -FPU::stateFrameSize(u32 fmtWord) -{ - return BYTE2(fmtWord); -} - -FpuFrameType -FPU::typeOfFrame(u32 fmtWord) -{ - isize size = stateFrameSize(fmtWord); - - if (size == stateFrameSize(FPU_FRAME_NULL)) return FPU_FRAME_NULL; - if (size == stateFrameSize(FPU_FRAME_IDLE)) return FPU_FRAME_IDLE; - if (size == stateFrameSize(FPU_FRAME_UNIMP)) return FPU_FRAME_UNIMP; - if (size == stateFrameSize(FPU_FRAME_BUSY)) return FPU_FRAME_BUSY; - - return FPU_FRAME_INVALID; -} - -u32 -FPU::computeFormatWord(FpuFrameType type) -{ - switch (type) { - - case FPU_FRAME_IDLE: - case FPU_FRAME_UNIMP: - case FPU_FRAME_BUSY: - - return 0x1F000000 | stateFrameSize(type) << 16; - - default: - - return 0; - } -} - -/* -bool -FPU::isValidExt(Instr I, Mode M, u16 op, u32 ext) -{ - auto cod = xxx_____________ (ext); - auto mode = ___xx___________ (ext); - auto fmt = ___xxx__________ (ext); - auto lst = ___xxx__________ (ext); - auto cmd = _________xxxxxxx (ext); - - switch (I) { - - case FDBcc: - case FScc: - case FTRAPcc: - - return (ext & 0xFFE0) == 0; - - case FMOVECR: - - return (op & 0x3F) == 0; - - case FMOVE: - - switch (cod) { - - case 0b010: - - if (M == MODE_IP) break; - return true; - - case 0b000: - - if (cmd == 0 && cod == 0 && (op & 0x3F)) break; - return true; - - case 0b011: - - if (fmt != 0b011 && fmt != 0b111 && (ext & 0x7F)) break; - - if (M == MODE_DN) { - if (fmt == 0b010 || fmt == 0b011 || fmt == 0b101 || fmt == 0b111) break; - } - if (M == MODE_AN) { - if (fmt == 0b011 || fmt == 0b111) break; - } - if (M == MODE_DIPC || M == MODE_IXPC || M == MODE_IM || M == MODE_IP) { - break; - } else { - if (fmt == 0b111 && (ext & 0xF)) break; - } - - return true; - } - - case FMOVEM: - - switch (cod) { - - case 0b101: - { - - if (ext & 0x3FF) break; - - if (M == MODE_DN || M == MODE_AN) { - if (lst != 0b000 && lst != 0b001 && lst != 0b010 && lst != 0b100) break; - } - if (M == MODE_DIPC || M == MODE_IXPC || M == MODE_IM || M == MODE_IP) { - break; - } - return true; - } - case 0b100: - - if (ext & 0x3FF) break; - if (M == MODE_IP) break; - return true; - - case 0b110: - case 0b111: - - if (ext & 0x0700) break; - if (mode == 3 && (ext & 0x8F)) break; - - if (M == MODE_DN || M == MODE_AN) { - break; - } - if (M == MODE_DIPC || M == MODE_IXPC || M == MODE_IM || M == MODE_IP) { - break; - } - if (M == MODE_AI) { - if (mode == 0 || mode == 1) break; - } - if (M == MODE_PI) { - if (mode == 0 || mode == 1 || cod == 0b111) break; - } - if (M == MODE_PD) { - if (cod == 0b110) break; - if (cod == 0b111 && (mode == 1) && (ext & 0x8F)) break; - if (cod == 0b111 && (mode == 2 || mode == 3)) break; - } - if (M == MODE_DI || M == MODE_IX || M == MODE_AW || M == MODE_AL) { - if (mode == 0 || mode == 1) break; - } - return true; - } - return false; - - default: - fatalError; - } -} -*/ - -void -FPU::setFPCR(u32 value) -{ - fpcr = value & 0x0000FFF0; - - softfloat::float_rounding_mode = (value & 0b110000) >> 4; -} - -void -FPU::setFPSR(u32 value) -{ - fpsr = value & 0x0FFFFFF8; -} - -void -FPU::setExcStatusBit(u32 mask) -{ - assert((mask & ~0xFF00) == 0); - - fpsr |= mask; - - // Set sticky bits (accrued exception byte) - if (fpsr & (FPEXP_SNAN | FPEXP_OPERR)) SET_BIT(fpsr, 7); - if (fpsr & FPEXP_OVFL) SET_BIT(fpsr, 6); - if ((fpsr & FPEXP_UNFL) && (fpsr & FPEXP_INEX2)) SET_BIT(fpsr, 5); - if (fpsr & FPEXP_DZ) SET_BIT(fpsr, 4); - if (fpsr & (FPEXP_INEX1 | FPEXP_INEX2 | FPEXP_OVFL)) SET_BIT(fpsr, 3); -} - -void -FPU::clearExcStatusBit(u32 mask) -{ - assert((mask & ~0xFF00) == 0); - - fpsr &= ~mask; -} - -void -FPU::setConditionCodes(int reg) -{ - assert(reg >= 0 && reg <= 7); - setConditionCodes(fpr[reg].val); -} - -void -FPU::setConditionCodes(const FpuExtended &value) -{ - // printf("setConditionCodes: %x:%x %d %d %d %d\n", value.raw.high, value.raw.low, value.isnegative(), value.iszero(), value.isinf(), value.isnan()); - REPLACE_BIT(fpsr, 27, value.isnegative()); - REPLACE_BIT(fpsr, 26, value.iszero()); - REPLACE_BIT(fpsr, 25, value.isinf()); - REPLACE_BIT(fpsr, 24, value.isnan()); -} - -void -FPU::setQuotientByte(u8 byte) -{ - fpsr &= 0xFF00FFFF; - fpsr |= byte << 16; -} - -void -FPU::setQuotientByte(u8 byte, bool sign) -{ - setQuotientByte((byte & 0x7F) | (sign ? 0x80 : 0x00)); -} - -void -FPU::setFPIAR(u32 value) -{ - fpiar = value; -} - -FpuExtended -FPU::readCR(unsigned nr) -{ - FpuExtended result; - - typedef struct { u16 hi; u64 lo; i64 r1; i64 r2; bool inex; } RomEntry; - - static constexpr RomEntry rom1[] = { - - { 0x4000, 0xc90fdaa22168c235, -1, 0, 1 }, // 0x00: Pi - { 0x4001, 0xfe00068200000000, 0, 0, 0 }, // 0x01: Undocumented - { 0x4001, 0xffc0050380000000, 0, 0, 0 }, // 0x02: Undocumented - { 0x2000, 0x7FFFFFFF00000000, 0, 0, 0 }, // 0x03: Undocumented - { 0x0000, 0xFFFFFFFFFFFFFFFF, 0, 0, 0 }, // 0x04: Undocumented - { 0x3C00, 0xFFFFFFFFFFFFF800, 0, 0, 0 }, // 0x05: Undocumented - { 0x3F80, 0xFFFFFF0000000000, 0, 0, 0 }, // 0x06: Undocumented - { 0x0001, 0xF65D8D9C00000000, 0, 0, 0 }, // 0x07: Undocumented - { 0x7FFF, 0x401E000000000000, 0, 0, 0 }, // 0x08: Undocumented - { 0x43F3, 0xE000000000000000, 0, 0, 0 }, // 0x09: Undocumented - { 0x4072, 0xC000000000000000, 0, 0, 0 }, // 0x0A: Undocumented - { 0x3ffd, 0x9a209a84fbcff798, 0, 1, 1 }, // 0x0B: Log10(2) - { 0x4000, 0xadf85458a2bb4a9a, 0, 1, 1 }, // 0x0C: E - { 0x3fff, 0xb8aa3b295c17f0bc, -1, 0, 1 }, // 0x0D: Log2(e) - { 0x3ffd, 0xde5bd8a937287195, 0, 0, 0 }, // 0x0E: Log10(e) - { 0x0000, 0x0000000000000000, 0, 0, 0 } // 0x0F: 0.0 - }; - - static constexpr RomEntry rom2[] = { - - { 0x3ffe, 0xb17217f7d1cf79ac, -1, 0, 1 }, // 0x00: Ln(2) - { 0x4000, 0x935d8dddaaa8ac17, -1, 0, 1 }, // 0x01: Ln(10) - { 0x3FFF, 0x8000000000000000, 0, 0, 0 }, // 0x02: 10^0 - { 0x4002, 0xA000000000000000, 0, 0, 0 }, // 0x03: 10^1 - { 0x4005, 0xC800000000000000, 0, 0, 0 }, // 0x04: 10^2 - { 0x400C, 0x9C40000000000000, 0, 0, 0 }, // 0x05: 10^4 - { 0x4019, 0xBEBC200000000000, 0, 0, 0 }, // 0x06: 10^8 - { 0x4034, 0x8E1BC9BF04000000, 0, 0, 0 }, // 0x07: 10^16 - { 0x4069, 0x9DC5ADA82B70B59E, -1, 0, 1 }, // 0x08: 10^32 - { 0x40D3, 0xC2781F49FFCFA6D5, 0, 1, 1 }, // 0x09: 10^64 - { 0x41A8, 0x93BA47C980E98CE0, -1, 0, 1 }, // 0x0A: 10^128 - { 0x4351, 0xAA7EEBFB9DF9DE8E, -1, 0, 1 }, // 0x0B: 10^256 - { 0x46A3, 0xE319A0AEA60E91C7, -1, 0, 1 }, // 0x0C: 10^512 - { 0x4D48, 0xC976758681750C17, 0, 1, 1 }, // 0x0D: 10^1024 - { 0x5A92, 0x9E8B3B5DC53D5DE5, -1, 0, 1 }, // 0x0E: 10^2048 - { 0x7525, 0xC46052028A20979B, -1, 0, 1 } // 0x0F: 10^4096 - }; - - auto readRom = [&](const RomEntry &entry) { - - auto result = FpuExtended(entry.hi, entry.lo); - - // Round if necessary - if ((fpcr & 0b110000) == 0b010000) result.raw.low += entry.r1; - if ((fpcr & 0b110000) == 0b100000) result.raw.low += entry.r1; - if ((fpcr & 0b110000) == 0b110000) result.raw.low += entry.r2; - - // Mark value as inexact if necessary - if (entry.inex) setExcStatusBit(FPEXP_INEX2); - - return result; - }; - - if (nr >= 0x40) { - // Values in this range seem to produce a Guru on the real machine - } - - if (nr >= 0x00 && nr < 0x10) result = readRom(rom1[nr]); - if (nr >= 0x30 && nr < 0x40) result = readRom(rom2[nr - 0x30]); - - return result; -} - -void -FPU::clearHostFpuFlags() -{ - feclearexcept(FE_ALL_EXCEPT); -} - -void -FPU::copyHostFpuFlags() -{ - if (fetestexcept(FE_INEXACT)) { setExcStatusBit(FPEXP_INEX2); } - if (fetestexcept(FE_UNDERFLOW)) { setExcStatusBit(FPEXP_UNFL); } - if (fetestexcept(FE_OVERFLOW)) { setExcStatusBit(FPEXP_OVFL); } - if (fetestexcept(FE_DIVBYZERO)) { setExcStatusBit(FPEXP_DZ); } - if (fetestexcept(FE_INVALID)) { setExcStatusBit(FPEXP_OPERR); } -} - -std::optional -FPU::resolveNan(const FpuExtended &op1, const FpuExtended &op2) -{ - /* MC68881/MC68882 Floating-Point Coprocessor User's Manual: - * - * 4.5.4.1 NON-SIGNALING NANS - * - * "If either, but not both, operand of an operation is a NAN, and it is a - * non-signaling NAN, then that NAN is returned as the result. If both - * operands are non-signaling NANs, then the destination operand - * non-signaling NAN is returned as the result. - * - * 4.5.4.2 SIGNALING NANS - * - * If either operand to an operation is a signaling NAN (SNAN), then the - * SNAN bit is set in the FPSR EXC byte. If the SNAN trap enable bit is - * set in the FPCR ENABLE byte, then the trap is taken and the destination - * is not modified. If the SNAN trap enable bit is not set, then the SNAN - * is converted to a non-signaling NAN (by setting the SNAN bit in the - * operand to a one), and the operation continues as described in the - * preceding section for non-signaling NANs. - */ - - if (!op1.isnan() && !op2.isnan()) return {}; - - auto o1 = op1; - auto o2 = op2; - - if (o1.isSignalingNaN()) { - - o1.raw.low |= (1LL << 62); - setExcStatusBit(FPEXP_SNAN); - } - if (o2.isSignalingNaN()) { - - o2.raw.low |= (1LL << 62); - setExcStatusBit(FPEXP_SNAN); - } - - return o2.isnan() ? o2 : o1; -} - -std::optional -FPU::resolveNan(const FpuExtended &op) -{ - if (!op.isnan()) return {}; - - auto o = op; - - if (o.isSignalingNaN()) { - - o.raw.low |= (1LL << 62); - setExcStatusBit(FPEXP_SNAN); - } - - return o; -} - -FpuExtended -FPU::monadic(const FpuExtended &value, std::function func) -{ - clearHostFpuFlags(); - auto result = func(value.asLongDouble()); - copyHostFpuFlags(); - - if (fetestexcept(FE_INVALID)) { - // printf("FPU::monadic FE_INVALID\n"); - return FpuExtended(0x7FFF, 0x7FFFFFFFFFFFFFFF); - } - if (std::isnan(result)) { - // printf("FPU::monadic is_nan\n"); - return FpuExtended(0x7FFF, 0x7FFFFFFFFFFFFFFF); - } - - // printf("FPU::monadic\n"); - return FpuExtended(result, getRoundingMode(), exceptionHandler); -} - -FpuExtended -FPU::fabs(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | abs(x) | +0.0 +0.0 | +inf +inf | - // ------------------------------------------- - - return FpuExtended(value.raw.high & 0x7FFF, value.raw.low); -} - -FpuExtended -FPU::facos(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | acos(x) | pi/2 pi/2 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return FpuExtended(0x3FFF, 0xc90fdaa22168c235); - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value < -1.0 || value > 1.0) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return monadic(value, [&](long double x) { return std::acos(x); }); -} - -FpuExtended -FPU::fasin(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | asin(x) | +0.0 -0.0 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value < -1.0 || value > 1.0) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return monadic(value, [&](long double x) { return std::asin(x); }); -} - -FpuExtended -FPU::fatan(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | atan(x) | +0.0 -0.0 | +pi/2 -pi/2 | - // ------------------------------------------- - - if (value.iszero()) { - - return FpuExtended::zero.copysign(value); - } - if (value.isinf()) { - - return FpuExtended(0x3FFF, 0xc90fdaa22168c235).copysign(value); - } - - return monadic(value, [&](long double x) { return std::atan(x); }); -} - -FpuExtended -FPU::fatanh(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | atanh(x) | +0.0 -0.0 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value <= -1.0 || value >= 1.0) { - - if (value == -1.0) { - - setExcStatusBit(FPEXP_DZ); - return FpuExtended::negInf; - } - if (value == 1.0) { - - setExcStatusBit(FPEXP_DZ); - return FpuExtended::posInf; - } - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return monadic(value, [&](long double x) { return std::atanh(x); }); -} - -FpuExtended -FPU::fcos(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | cos(x) | +1.0 +1.0 | NaN NaN | - // ------------------------------------------- - - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return monadic(value, [&](long double x) { return std::cos(x); }); -} - -FpuExtended -FPU::fcosh(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | cosh(x) | +1.0 +1.0 | +inf +inf | - // ------------------------------------------- - - if (value.isinf()) { - - return FpuExtended::posInf; - } - - return monadic(value, [&](long double x) { return std::cosh(x); }); -} - -FpuExtended -FPU::fetox(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | e^x | +1.0 +1.0 | +inf +0.0 | - // ------------------------------------------- - - if (value.isinf()) { - return value.ispositive() ? value : FpuExtended::posZero; - } - - return monadic(value, [&](long double x) { return std::expl(x); }); -} - -FpuExtended -FPU::fetoxm1(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | e^x - 1 | +0.0 -0.0 | +inf -1.0 | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - return value.ispositive() ? value : -1.0; - } - - return monadic(value, [&](long double x) { return std::expl(x) - 1.0L; }); -} - -FpuExtended -FPU::fgetexp(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | e^x - 1 | +0.0 -0.0 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return FpuExtended((value.raw.high & 0x7FFF) - FpuExtended::bias); -} - -FpuExtended -FPU::fgetman(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | Mantissa | +0.0 -0.0 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return FpuExtended((value.raw.high & 0x8000) | 0x3FFF, value.raw.low); -} - -FpuExtended -FPU::fint(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | Integer | +0.0 -0.0 | +inf -inf | - // ------------------------------------------- - - if (value.iszero() || value.isinf()) { - - return value; - } - - softfloat::float_exception_flags = 0; - auto rounded = softfloat::floatx80_round_to_int(value.raw); - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - /* - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - */ - - return FpuExtended(rounded.high, rounded.low); -} - -FpuExtended -FPU::fintrz(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | Integer | +0.0 -0.0 | +inf -inf | - // ------------------------------------------- - - if (value.iszero() || value.isinf()) { - - return value; - } - - softfloat::float_exception_flags = 0; - auto oldmode = softfloat::float_rounding_mode; - softfloat::float_rounding_mode = 1; - auto rounded = softfloat::floatx80_round_to_int(value.raw); - softfloat::float_rounding_mode = oldmode; - - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - /* - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - flags |= FPEXP_OVFL; - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - flags |= FPEXP_UNFL; - } - */ - - return FpuExtended(rounded.high, rounded.low); -} - -FpuExtended -FPU::flog10(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | log10(x) | -inf -inf | +inf NaN | - // ------------------------------------------- - - if (value.iszero()) { - - setExcStatusBit(FPEXP_DZ); - return FpuExtended::negInf; - } - if (value.isnegative()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value.isinf()) { - - if (value.ispositive()) { - - return FpuExtended::posInf; - - } else { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - } - - return monadic(value, [&](long double x) { return std::log10(x); }); -} - -FpuExtended -FPU::flog2(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | ld(x) | -inf -inf | +inf NaN | - // ------------------------------------------- - - if (value.iszero()) { - - setExcStatusBit(FPEXP_DZ); - return FpuExtended::negInf; - } - if (value.isnegative()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value.isinf()) { - - if (value.ispositive()) { - - return FpuExtended::posInf; - - } else { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - } - - return monadic(value, [&](long double x) { return std::log2(x); }); -} - -FpuExtended -FPU::flogn(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | ln(x) | -inf -inf | +inf NaN | - // ------------------------------------------- - - if (value.iszero()) { - - setExcStatusBit(FPEXP_DZ); - return FpuExtended::negInf; - } - if (value.isnegative()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value.isinf()) { - - if (value.ispositive()) { - - return FpuExtended::posInf; - - } else { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - } - - return monadic(value, [&](long double x) { return std::log(x); }); -} - -FpuExtended -FPU::flognp1(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | ln(x - 1) | +0.0 -0.0 | +inf NaN | - // ------------------------------------------- - - - if (value.iszero()) { - - return value; - } - if (value == -1.0) { - - setExcStatusBit(FPEXP_DZ); - return FpuExtended::negInf; - } - if (value < -1.0) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value.isinf()) { - - if (value.ispositive()) { - - return FpuExtended::posInf; - - } else { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - } - - return monadic(value, [&](long double x) { return std::log(x + 1); }); -} - -FpuExtended -FPU::fneg(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | -x | -0.0 +0.0 | -inf +inf | - // ------------------------------------------- - - return FpuExtended(value.raw.high ^0x8000, value.raw.low); -} - -FpuExtended -FPU::fsin(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | sin(x) | +0.0 -0.0 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - return monadic(value, [&](long double x) { return std::sin(x); }); -} - -FpuExtended -FPU::fsinh(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | sinh(x) | +0.0 -0.0 | +inf -inf | - // ------------------------------------------- - - if (value.iszero() || value.isinf()) { - - return value; - } - - return monadic(value, [&](long double x) { return std::sinh(x); }); -} - -FpuExtended -FPU::fsqrt(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | sqrt(x) | +0.0 -0.0 | +inf NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isnegative()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (value.isinf()) { - - return value; - } - - return monadic(value, [&](long double x) { return std::sqrt(x); }); -} - -FpuExtended -FPU::ftan(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | tan(x) | +0.0 -0.0 | NaN NaN | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - - return monadic(value, [&](long double x) { return std::tan(x); }); -} - -FpuExtended -FPU::ftanh(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | tanh(x) | +0.0 -0.0 | +1.0 -1.0 | - // ------------------------------------------- - - if (value.iszero()) { - - return value; - } - if (value.isinf()) { - - return value.ispositive() ? 1.0 : -1.0; - } - - return monadic(value, [&](long double x) { return std::tanh(x); }); -} - -FpuExtended -FPU::ftentox(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | 10^x | +1.0 +1.0 | +inf +0.0 | - // ------------------------------------------- - - if (value.isinf()) { - - return value.ispositive() ? value : FpuExtended::posZero; - } - - return monadic(value, [&](long double x) { return std::powl(10.0L, x); }); -} - -FpuExtended -FPU::ftst(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | { } N | Z NZ | I NI | - // ------------------------------------------- - - return value; -} - -FpuExtended -FPU::ftwotox(const FpuExtended &value) -{ - // ------------------------------------------- - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------- - // | 2^x | +1.0 +1.0 | +inf +0.0 | - // ------------------------------------------- - - if (value.isinf()) { - - return value.ispositive() ? value : FpuExtended::posZero; - } - - return monadic(value, [&](long double x) { return std::powl(2.0L, x); }); -} - -FpuExtended -FPU::fadd(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | Addition | Addition | +0.0 -0.0 | - // - | | | +0.0 -0.0 | - // ------------------------------------------------------ - // Zero + | Addition | +0.0 ?0.0 | +0.0 -0.0 | - // - | | ?0.0 -0.0 | +0.0 -0.0 | - // ------------------------------------------------------ - // Infinity + | +inf +inf | +inf +inf | +inf NaN | - // - | -inf -inf | -inf -inf | NaN -inf | - // ------------------------------------------------------ - // ? : +/- sign depends on rounding mode - - if (op1.iszero() && op2.iszero()) { - - if (op1.signbit() == op2.signbit()) { - return op1; - } else if (getRoundingMode() == FPU_RND_DOWNWARD) { - return FpuExtended::negZero; - } else { - return FpuExtended::posZero; - } - } - if (op1.isinf() && op2.isinf() && op1.signbit() != op2.signbit()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (op1.isinf()) { - - return op1; - } - if (op2.isinf()) { - - return op2; - } - - softfloat::float_exception_flags = 0; - - auto result = softfloat::floatx80_add(op1.raw, op2.raw); - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - setExcStatusBit(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - setExcStatusBit(FPEXP_UNFL); - } - - return FpuExtended(result.high, result.low); -} - -FpuExtended -FPU::fcmp(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | {NZ} - | - - | N - | - // - | N {NZ} | N N | N - | - // ------------------------------------------------------ - // Zero + | N - | Z Z | N - | - // - | N - | NZ NZ | N - | - // ------------------------------------------------------ - // Infinity + | - - | - - | Z - | - // - | N N | N N | N NZ | - // ------------------------------------------------------ - - if (op1.iszero() && op2.iszero()) { - - return op2; - } - if (op1.iszero()) { - - return op2.signbit() ? -1 : 1; - } - if (op2.iszero()) { - - return op1.signbit() ? 1 : -1; - } - if (op1.isinf() && op2.isinf()) { - - if (op1.signbit() == 0 && op2.signbit() == 0) return FpuExtended::posZero; - if (op1.signbit() == 0 && op2.signbit() == 1) return -1; - if (op1.signbit() == 1 && op2.signbit() == 0) return 1; - if (op1.signbit() == 1 && op2.signbit() == 1) return FpuExtended::negZero; - } - if (op1.isinf()) { - - return op1.signbit() ? 1 : -1; - } - if (op2.isinf()) { - - return op2.signbit() ? -1 : 1; - } - if (op1 == op2) { - - return op1.signbit() ? FpuExtended::negZero : FpuExtended::posZero; - } - if (op2.ispositive()) { - - return op1 > op2 ? -1.0 : 1.0; - } - return op1 < op2 ? 1.0 : -1.0; -} - -FpuExtended -FPU::fdiv(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | y/x | +inf* -inf* | +0.0 -0.0 | - // - | | -inf* +inf* | -0.0 +0.0 | - // ------------------------------------------------------ - // Zero + | +0.0 -0.0 | NaN NaN | +0.0 -0.0 | - // - | -0.0 +0.0 | NaN NaN | -0.0 +0.0 | - // ------------------------------------------------------ - // Infinity + | +inf -inf | +inf -inf | NaN NaN | - // - | -inf +inf | -inf +inf | NaN NaN | - // ------------------------------------------------------ - // * : Sets DZ flag - - if ((op1.iszero() && op2.iszero()) || (op1.isinf() && op2.isinf())) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (op1.isinf()) { - return op1.signbit() == op2.signbit() ? FpuExtended::posZero : FpuExtended::negZero; - } - if (op2.isinf()) { - return op1.signbit() == op2.signbit() ? FpuExtended::posInf : FpuExtended::negInf; - } - if (op1.iszero()) { - - setExcStatusBit(FPEXP_DZ); - return op1.signbit() == op2.signbit() ? FpuExtended::posInf : FpuExtended::negInf; - } - if (op2.iszero()) { - return op1.signbit() == op2.signbit() ? FpuExtended::posZero : FpuExtended::negZero; - } - - softfloat::float_exception_flags = 0; - auto result = softfloat::floatx80_div(op2.raw, op1.raw); - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - setExcStatusBit(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - setExcStatusBit(FPEXP_UNFL); - } - - return FpuExtended(result.high, result.low); -} - -FpuExtended -FPU::fmod(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | Modulo | NaN NaN | FPn | - // - | Remainder | NaN NaN | | - // ------------------------------------------------------ - // Zero + | +0.0 +0.0 | NaN NaN | +0.0 +0.0 | - // - | -0.0 -0.0 | NaN NaN | -0.0 -0.0 | - // ------------------------------------------------------ - // Infinity + | NaN NaN | NaN NaN | NaN NaN | - // - | NaN NaN | NaN NaN | NaN NaN | - // ------------------------------------------------------ - - long double modulus; - - if (op1.iszero() || op2.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (op2.iszero()) { - - return op2; - } - if (op1.isinf()) { - - modulus = op2.asLongDouble(); - - } else { - - clearHostFpuFlags(); - modulus = std::fmodl(op2.asLongDouble(), op1.asLongDouble()); - copyHostFpuFlags(); - } - - auto result = FpuExtended(modulus, getRoundingMode(), exceptionHandler); - setQuotientByte((u8)result.raw.low, op1.signbit() != op2.signbit()); - return result; -} - -FpuExtended -FPU::fmul(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | x * y | +0.0 -0.0 | +inf -inf | - // - | | -0.0 +0.0 | -inf +inf | - // ------------------------------------------------------ - // Zero + | +0.0 -0.0 | +0.0 -0.0 | NaN NaN | - // - | -0.0 +0.0 | -0.0 +0.0 | NaN NaN | - // ------------------------------------------------------ - // Infinity + | +inf -inf | NaN NaN | +inf -inf | - // - | -inf +inf | NaN NaN | -inf +inf | - // ------------------------------------------------------ - - if (op1.isinf() || op2.isinf()) { - - if (op1.iszero() || op2.iszero()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - - } else { - - return FpuExtended::inf.copysign(op1.signbit() != op2.signbit()); - } - } - if (op1.iszero() || op2.iszero()) { - - return FpuExtended::zero.copysign(op1.signbit() != op2.signbit()); - } - - softfloat::float_exception_flags = 0; - auto result = softfloat::floatx80_mul(op1.raw, op2.raw); - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - setExcStatusBit(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - setExcStatusBit(FPEXP_UNFL); - } - - return FpuExtended(result.high, result.low); -} - -FpuExtended -FPU::frem(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | IEEE | NaN NaN | FPn FPn | - // - | Remainder | NaN NaN | FPn FPn | - // ------------------------------------------------------ - // Zero + | +0.0 +0.0 | NaN NaN | +0.0 +0.0 | - // - | -0.0 -0.0 | NaN NaN | -0.0 -0.0 | - // ------------------------------------------------------ - // Infinity + | NaN NaN | NaN NaN | NaN NaN | - // - | NaN NaN | NaN NaN | NaN NaN | - // ------------------------------------------------------ - - if (op1.iszero() || op2.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (op2.iszero()) { - - return op2; - } - if (op1.isinf()) { - - return op2; - } - - softfloat::float_exception_flags = 0; - auto remainder = softfloat::floatx80_rem(op2.raw, op1.raw); - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - setExcStatusBit(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - setExcStatusBit(FPEXP_UNFL); - } - - setQuotientByte((u8)remainder.low, op1.signbit() != op2.signbit()); - return FpuExtended(remainder.high, remainder.low); -} - -FpuExtended -FPU::fscal(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | Scale | FPn | NaN NaN | - // - | Exponent | FPn | NaN NaN | - // ------------------------------------------------------ - // Zero + | +0.0 +0.0 | +0.0 +0.0 | NaN NaN | - // - | -0.0 -0.0 | -0.0 -0.0 | NaN NaN | - // ------------------------------------------------------ - // Infinity + | +inf +inf | +inf +inf | NaN NaN | - // - | -inf -inf | -inf -inf | NaN NaN | - // ------------------------------------------------------ - - if (op1.isinf()) { - - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } - if (op2.isinf() || op2.iszero()) { - - return op2; - } - if (op1.iszero()) { - - return op2; - } - - long offset = (long)op1.asLongDouble(); - - printf("fscale: op1 = %Lf offset = %ld\n", op1.asLongDouble(), offset); - - auto sgn = op2.signbit(); - auto man = op2.man(); - auto exp = op2.exp(); - - return FpuExtended(sgn, exp + offset, man, exceptionHandler); -} - -FpuExtended -FPU::fsgldiv(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | y/x | +inf* -inf* | +0.0 -0.0 | - // - | | -inf* +inf* | -0.0 +0.0 | - // ------------------------------------------------------ - // Zero + | +0.0 -0.0 | NaN NaN | +0.0 -0.0 | - // - | -0.0 +0.0 | NaN NaN | -0.0 +0.0 | - // ------------------------------------------------------ - // Infinity + | +inf -inf | +inf -inf | NaN NaN | - // - | -inf +inf | -inf +inf | NaN NaN | - // ------------------------------------------------------ - // * : Sets DZ flag - - auto result = fdiv(op1, op2); - - // TODO: Round - - return result; -} - -FpuExtended -FPU::fsglmul(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | x * y | +0.0 -0.0 | +inf -inf | - // - | | -0.0 +0.0 | -inf +inf | - // ------------------------------------------------------ - // Zero + | +0.0 -0.0 | +0.0 -0.0 | NaN NaN | - // - | -0.0 +0.0 | -0.0 +0.0 | NaN NaN | - // ------------------------------------------------------ - // Infinity + | +inf -inf | NaN NaN | +inf -inf | - // - | -inf +inf | NaN NaN | -inf +inf | - // ------------------------------------------------------ - - auto result = fmul(op1, op2); - - // TODO: Round - - return result; -} - -FpuExtended -FPU::fsub(const FpuExtended &op1, const FpuExtended &op2) -{ - // ------------------------------------------------------ - // | In Range | Zero | Infinity | - // | + - | + - | + - | - // ------------------------------------------------------ - // In Range + | Subtract | Subtract | -inf +inf | - // - | | | -inf +inf | - // ------------------------------------------------------ - // Zero + | Subtract | ?0.0 -0.0 | -inf +inf | - // - | | -0.0 ?0.0 | -inf +inf | - // ------------------------------------------------------ - // Infinity + | +inf +inf | +inf +inf | NaN -inf | - // - | -inf -inf | -inf -inf | -inf NaN | - // ------------------------------------------------------ - // ? : +/- sign depends on rounding mode - - if (op1.iszero() && op2.iszero()) { - - if (op1.signbit() != op2.signbit()) { - return FpuExtended::negZero; - } else if (getRoundingMode() == FPU_RND_DOWNWARD) { - return FpuExtended::negZero; - } else { - return FpuExtended::posZero; - } - } - if (op1.isinf() && op2.isinf()) { - - if (op1.signbit() == op2.signbit()) { - setExcStatusBit(FPEXP_OPERR); - return FpuExtended::nan; - } else { - return FpuExtended::negInf; - } - } - if (op1.isinf()) { - - return -op1; - } - if (op2.isinf()) { - - return op2; - } - - softfloat::float_exception_flags = 0; - - auto result = softfloat::floatx80_sub(op2.raw, op1.raw); - if (softfloat::float_exception_flags & softfloat::float_flag_inexact) { - setExcStatusBit(FPEXP_INEX2); - } - if (softfloat::float_exception_flags & softfloat::float_flag_overflow) { - setExcStatusBit(FPEXP_OVFL); - } - if (softfloat::float_exception_flags & softfloat::float_flag_underflow) { - setExcStatusBit(FPEXP_UNFL); - } - - return FpuExtended(result.high, result.low); -} - -bool -FPU::cpcc(u8 condition) const -{ - bool n = fpsr & FPCC_N; - bool z = fpsr & FPCC_Z; - bool nan = fpsr & FPCC_NAN; - - switch (condition) - { - case 0x10: - case 0x00: return false; - case 0x11: - case 0x01: return z; - case 0x12: - case 0x02: return !(nan || z || n); - case 0x13: - case 0x03: return z || !(nan || n); - case 0x14: - case 0x04: return n && !(nan || z); - case 0x15: - case 0x05: return z || (n && !nan); - case 0x16: - case 0x06: return !nan && !z; - case 0x17: - case 0x07: return !nan || z; - case 0x18: - case 0x08: return nan; - case 0x19: - case 0x09: return nan || z; - case 0x1a: - case 0x0a: return nan || !(n || z); - case 0x1b: - case 0x0b: return nan || z || !n; - case 0x1c: - case 0x0c: return nan || (n && !z); - case 0x1d: - case 0x0d: return nan || z || n; - case 0x1e: - case 0x0e: return nan || !z; - case 0x1f: - case 0x0f: return true; - - default: - fatalError; - } -} - -} diff --git a/Emulator/Components/CPU/Moira/MoiraFPU.h b/Emulator/Components/CPU/Moira/MoiraFPU.h deleted file mode 100644 index 997250ed3..000000000 --- a/Emulator/Components/CPU/Moira/MoiraFPU.h +++ /dev/null @@ -1,333 +0,0 @@ -// ----------------------------------------------------------------------------- -// This file is part of Moira - A Motorola 68k emulator -// -// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de -// Published under the terms of the MIT License -// ----------------------------------------------------------------------------- - -#pragma once - -#include "MoiraTypes.h" -#include "FpuFormats.h" - -namespace vamiga::moira { - -class FPUReg { - - // Reference to the FPU - class FPU &fpu; - -public: - - // Register value - FpuExtended val; - - - // - // Constructing - // - - FPUReg(FPU& fpu) : fpu(fpu) { } - void reset() { val = FpuExtended::nan; } - - - // - // Accessing - // - - // Getter and setter - FpuExtended get() const { return val; } - void set(const FpuExtended other) { val = other; } - - // Rounds the stored value according to the current rounding mode - FpuExtended round(); - - // Same as set with additional rounding and normalization - void load(const FpuExtended other); - - - // - // Classifying - // - - int fpclassify() const { return val.fpclassify(); } - bool isfinite() const { return val.isfinite(); } - bool isinf() const { return val.isinf(); } - bool isnan() const { return val.isnan(); } - bool isnormal() const { return val.isnormal(); } - bool issubnormal() const { return val.issubnormal(); } - bool signbit() const { return val.signbit(); } - - bool isNegative() const { return val.isnegative(); } - bool isZero() const { return val.iszero(); } - bool isSignalingNaN() const { return val.isSignalingNaN(); } - bool isNonsignalingNaN() const { return val.isNonsignalingNaN(); } - - std::ostream &operator<<(std::ostream &stream); -}; - -class FPU { - -public: - - // Reference to the CPU - class Moira &moira; - - // Emulated FPU model - FPUModel model = INTERNAL_FPU; - -public: - - // Floating-point data registers FP0 to FP7 - FPUReg fpr[8] = { - - FPUReg(*this), - FPUReg(*this), - FPUReg(*this), - FPUReg(*this), - FPUReg(*this), - FPUReg(*this), - FPUReg(*this), - FPUReg(*this) - }; - - // Control register - u32 fpcr; - - // Status register - u32 fpsr; - - // Instruction address register - u32 fpiar; - - // Indicates whether the FPU is in reset state (affects FSAVE) - bool resetState; - - - // - // Constructing - // - -public: - - FPU(Moira& ref); - - // Initializes all registers with their reset values - void reset(); - - // Indicates whether the FPU is in reset state (used by FSAVE) - bool inResetState(); - - - // - // Configuring - // - -public: - - // Gets or sets the emulated CPU model - FPUModel getModel() const { return model; } - void setModel(FPUModel newModel) { model = newModel; } - - // Returns the precision and rounding mode, as specified in the FPCR - FpuPrecision getPrecision() const; - FpuRoundingMode getRoundingMode() const; - - // Configures the rounding mode of the host FPU - static FpuRoundingMode fesetround(FpuRoundingMode mode); - - - // - // Accessing registers - // - -public: - - // Accesses the control register - u32 getFPCR() const { return fpcr & 0x0000FFF0; } - void setFPCR(u32 value); - - // Accesses the status register - u32 getFPSR() const { return fpsr & 0x0FFFFFF8; } - void setFPSR(u32 value); - - void clearExcStatusByte() { fpsr &= 0xFFFF00FF; } - void clearExcStatusBit(u32 mask); - void setExcStatusBit(u32 mask); - - void setConditionCodes(int reg); - void setConditionCodes(const FpuExtended &value); - - void setQuotientByte(u8 byte); - void setQuotientByte(u8 byte, bool sign); - - // Accesses the instruction address register - u32 getFPIAR() const { return fpiar; } - void setFPIAR(u32 value); - - - // - // Accessing the constant Rom - // - - // Reads a value from the constant Rom - FpuExtended readCR(unsigned nr); - - - // - // Analyzing instructions - // - -public: - - // Returns true iff instruction I is supported by a certain FPU model - template static bool isSupported(FPUModel model) - { - switch (I) { - - case FACOS: case FASIN: case FATANH: case FCOS: - case FCOSH: case FETOX: case FETOXM1: case FGETEXP: - case FGETMAN: case FINTRZ: case FLOG10: case FLOG2: - case FLOGN: case FLOGNP1: case FMOD: case FREM: - case FSCAL: case FSIN: case FSINCOS: case FSINH: - case FTAN: case FTANH: case FTENTOX: case FTWOTOX: - - return model == M68881 || model == M68882; - - default: - - return true; - } - } - template bool isSupported() { return isSupported(model); } - - // Returns true iff instruction I is a monadic arithmetic instruction - template static bool isMonadic() - { - switch (I) { - - case FABS: case FACOS: case FASIN: case FATAN: - case FATANH: case FCOS: case FCOSH: case FETOX: - case FETOXM1: case FGETEXP: case FGETMAN: case FINT: - case FINTRZ: case FLOG10: case FLOG2: case FLOGN: - case FLOGNP1: case FNEG: case FSIN: case FSINCOS: - case FSINH: case FSQRT: case FTAN: case FTANH: - case FTENTOX: case FTST: case FTWOTOX: - - return true; - - default: - - return false; - } - } - - // Returns true iff instruction I is a dyadic arithmetic instruction - template static bool isDyadic() - { - switch (I) { - - case FADD: case FCMP: case FDIV: case FMOD: - case FMUL: case FREM: case FSCAL: case FSGLDIV: - case FSGLMUL: case FSUB: - - return true; - - default: - - return false; - } - } - - // Checks the validity of the extension words - // static bool isValidExt(Instr I, Mode M, u16 op, u32 ext); - - - // - // Working with state frames (FSAVE, FRESTORE) - // - - // Determines the size of a state frame (varies between FPU models) - int stateFrameSize(FpuFrameType type); - int stateFrameSize(u32 fmtWord); - - // Determines the type a state frame - FpuFrameType typeOfFrame(u32 fmtWord); - - // Computes a format word for a fiven frame type - u32 computeFormatWord(FpuFrameType type); - - - // - // Managing the host FPU - // - - // Clears the exception flags of the host FPU - void clearHostFpuFlags(); - - // Copies the exception flags of the host FPU into FPSR - void copyHostFpuFlags(); - - - // - // Handling special values - // - - // Checks the arguments for NaNs and computes the result NaN if applicable - std::optional resolveNan(const FpuExtended &op1, const FpuExtended &op2); - std::optional resolveNan(const FpuExtended &op); - - - // - // Executing instructions - // - - std::function exceptionHandler = [this](int flags) { setExcStatusBit(flags); }; - - FpuExtended monadic(const FpuExtended &value, std::function func); - - // Monadic operations - FpuExtended fabs(const FpuExtended &value); - FpuExtended facos(const FpuExtended &value); - FpuExtended fasin(const FpuExtended &value); - FpuExtended fatan(const FpuExtended &value); - FpuExtended fatanh(const FpuExtended &value); - FpuExtended fcos(const FpuExtended &value); - FpuExtended fcosh(const FpuExtended &value); - FpuExtended fetox(const FpuExtended &value); - FpuExtended fetoxm1(const FpuExtended &value); - FpuExtended fgetexp(const FpuExtended &value); - FpuExtended fgetman(const FpuExtended &value); - FpuExtended fint(const FpuExtended &value); - FpuExtended fintrz(const FpuExtended &value); - FpuExtended flog10(const FpuExtended &value); - FpuExtended flog2(const FpuExtended &value); - FpuExtended flogn(const FpuExtended &value); - FpuExtended flognp1(const FpuExtended &value); - FpuExtended fneg(const FpuExtended &value); - FpuExtended fsin(const FpuExtended &value); - FpuExtended fsinh(const FpuExtended &value); - FpuExtended fsqrt(const FpuExtended &value); - FpuExtended ftan(const FpuExtended &value); - FpuExtended ftanh(const FpuExtended &value); - FpuExtended ftentox(const FpuExtended &value); - FpuExtended ftst(const FpuExtended &value); - FpuExtended ftwotox(const FpuExtended &value); - - // Dyadic operations - FpuExtended fadd(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fcmp(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fdiv(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fmod(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fmul(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended frem(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fscal(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fsgldiv(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fsglmul(const FpuExtended &op1, const FpuExtended &op2); - FpuExtended fsub(const FpuExtended &op1, const FpuExtended &op2); - - // Performs a coprocessor condition test - bool cpcc(u8 condition) const; -}; - -}