diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f311f91..c282e8d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,8 @@ add_library(dynarmic common/fp/process_exception.cpp common/fp/process_exception.h common/fp/rounding_mode.h + common/fp/unpacked.cpp + common/fp/unpacked.h common/fp/util.h common/intrusive_list.h common/iterator_util.h diff --git a/src/common/fp/unpacked.cpp b/src/common/fp/unpacked.cpp new file mode 100644 index 00000000..1432bf36 --- /dev/null +++ b/src/common/fp/unpacked.cpp @@ -0,0 +1,54 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "common/fp/info.h" +#include "common/fp/process_exception.h" +#include "common/fp/unpacked.h" + +namespace Dynarmic::FP { + +template +std::tuple> FPUnpack(FPT op, FPCR fpcr, FPSR& fpsr) { + constexpr size_t sign_bit = FPInfo::exponent_width + FPInfo::explicit_mantissa_width; + constexpr size_t exponent_high_bit = FPInfo::exponent_width + FPInfo::explicit_mantissa_width - 1; + constexpr size_t exponent_low_bit = FPInfo::explicit_mantissa_width; + constexpr size_t mantissa_high_bit = FPInfo::explicit_mantissa_width - 1; + constexpr size_t mantissa_low_bit = 0; + constexpr int denormal_exponent = FPInfo::exponent_min - int(FPInfo::explicit_mantissa_width); + + const bool sign = Common::Bit(op); + const FPT exp_raw = Common::Bits(op); + const FPT frac_raw = Common::Bits(op); + + if (exp_raw == 0) { + if (frac_raw == 0 || fpcr.FZ()) { + if (frac_raw != 0) { + FPProcessException(FPExc::InputDenorm, fpcr, fpsr); + } + return {FPType::Zero, sign, {sign, 0, 0}}; + } + + return {FPType::Nonzero, sign, {sign, denormal_exponent, frac_raw}}; + } + + if (exp_raw == Common::Ones(FPInfo::exponent_width)) { + if (frac_raw == 0) { + return {FPType::Infinity, sign, {sign, 1000000, 1}}; + } + + const bool is_quiet = Common::Bit(frac_raw); + return {is_quiet ? FPType::QNaN : FPType::SNaN, sign, {sign, 0, 0}}; + } + + const int exp = static_cast(exp_raw) - FPInfo::exponent_bias - FPInfo::explicit_mantissa_width; + const u64 frac = frac_raw | FPInfo::implicit_leading_bit; + return {FPType::Nonzero, sign, {sign, exp, frac}}; +} + +template std::tuple> FPUnpack(u32 op, FPCR fpcr, FPSR& fpsr); +template std::tuple> FPUnpack(u64 op, FPCR fpcr, FPSR& fpsr); + +} // namespace Dynarmic::FP diff --git a/src/common/fp/unpacked.h b/src/common/fp/unpacked.h new file mode 100644 index 00000000..f815aece --- /dev/null +++ b/src/common/fp/unpacked.h @@ -0,0 +1,43 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#pragma once + +#include + +#include "common/common_types.h" +#include "common/fp/fpsr.h" +#include "frontend/A64/FPCR.h" + +namespace Dynarmic::FP { + +using FPCR = A64::FPCR; + +enum class FPType { + Nonzero, + Zero, + Infinity, + QNaN, + SNaN, +}; + +/// value = (sign ? -1 : +1) * mantissa * 2^exponent +template +struct FPUnpacked { + bool sign; + int exponent; + MantissaT mantissa; +}; + +template +inline bool operator==(const FPUnpacked& a, const FPUnpacked& b) { + return std::tie(a.sign, a.exponent, a.mantissa) == std::tie(b.sign, b.exponent, b.mantissa); +} + +template +std::tuple> FPUnpack(FPT op, FPCR fpcr, FPSR& fpsr); + +} // namespace Dynarmic::FP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7a915fab..2d1902ee 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(dynarmic_tests A64/inst_gen.cpp A64/inst_gen.h A64/testenv.h + fp/unpacked_tests.cpp main.cpp rand_int.h ) diff --git a/tests/fp/unpacked_tests.cpp b/tests/fp/unpacked_tests.cpp new file mode 100644 index 00000000..4c5a28b9 --- /dev/null +++ b/tests/fp/unpacked_tests.cpp @@ -0,0 +1,35 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include + +#include "common/fp/unpacked.h" + +using namespace Dynarmic; +using namespace Dynarmic::FP; + +TEST_CASE("FPUnpack Tests", "[fp]") { + const static std::vector>, u32>> test_cases { + {0x00000000, {FPType::Zero, false, {false, 0, 0}}, 0}, + {0x7F800000, {FPType::Infinity, false, {false, 1000000, 1}}, 0}, + {0xFF800000, {FPType::Infinity, true, {true, 1000000, 1}}, 0}, + {0x7F800001, {FPType::SNaN, false, {false, 0, 0}}, 0}, + {0xFF800001, {FPType::SNaN, true, {true, 0, 0}}, 0}, + {0x7FC00001, {FPType::QNaN, false, {false, 0, 0}}, 0}, + {0xFFC00001, {FPType::QNaN, true, {true, 0, 0}}, 0}, + {0x00000001, {FPType::Nonzero, false, {false, -149, 1}}, 0}, // Smallest single precision denormal is 2^-149. + }; + + const FPCR fpcr; + for (const auto& [input, expected_output, expected_fpsr] : test_cases) { + FPSR fpsr; + const auto output = FPUnpack(input, fpcr, fpsr); + + INFO("Input: " << std::hex << input); + REQUIRE(output == expected_output); + REQUIRE(fpsr.Value() == expected_fpsr); + } +}