diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b7b88d6c..6b8bdcbb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,8 @@ add_library(dynarmic common/fp/info.h common/fp/mantissa_util.h common/fp/op.h + common/fp/op/FPRoundInt.cpp + common/fp/op/FPRoundInt.h common/fp/op/FPToFixed.cpp common/fp/op/FPToFixed.h common/fp/process_exception.cpp diff --git a/src/common/fp/op.h b/src/common/fp/op.h index cd28bf73..25d199a1 100644 --- a/src/common/fp/op.h +++ b/src/common/fp/op.h @@ -6,4 +6,5 @@ #pragma once +#include "common/fp/op/FPRoundInt.h" #include "common/fp/op/FPToFixed.h" diff --git a/src/common/fp/op/FPRoundInt.cpp b/src/common/fp/op/FPRoundInt.cpp new file mode 100644 index 00000000..2bc0e1be --- /dev/null +++ b/src/common/fp/op/FPRoundInt.cpp @@ -0,0 +1,89 @@ +/* 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/assert.h" +#include "common/bit_util.h" +#include "common/common_types.h" +#include "common/fp/fpsr.h" +#include "common/fp/info.h" +#include "common/fp/mantissa_util.h" +#include "common/fp/op/FPRoundInt.h" +#include "common/fp/process_exception.h" +#include "common/fp/process_nan.h" +#include "common/fp/rounding_mode.h" +#include "common/fp/unpacked.h" +#include "common/safe_ops.h" +#include "frontend/A64/FPCR.h" + +namespace Dynarmic::FP { + +template +u64 FPRoundInt(FPT op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr) { + ASSERT(rounding != RoundingMode::ToOdd); + + auto [type, sign, value] = FPUnpack(op, fpcr, fpsr); + + if (type == FPType::SNaN || type == FPType::QNaN) { + return FPProcessNaN(type, op, fpcr, fpsr); + } + + if (type == FPType::Infinity) { + return FPInfo::Infinity(sign); + } + + if (type == FPType::Zero) { + return FPInfo::Zero(sign); + } + + if (value.exponent >= 0) { + // Guaranteed to be an integer + return op; + } + + u64 int_result = value.mantissa; + const ResidualError error = ResidualErrorOnRightShift(int_result, -value.exponent); + int_result = Safe::ArithmeticShiftLeft(int_result, value.exponent); + + bool round_up = false; + switch (rounding) { + case RoundingMode::ToNearest_TieEven: + round_up = error > ResidualError::Half || (error == ResidualError::Half && Common::Bit<0>(int_result)); + break; + case RoundingMode::TowardsPlusInfinity: + round_up = error != ResidualError::Zero; + break; + case RoundingMode::TowardsMinusInfinity: + round_up = false; + break; + case RoundingMode::TowardsZero: + round_up = error != ResidualError::Zero && Common::MostSignificantBit(int_result); + break; + case RoundingMode::ToNearest_TieAwayFromZero: + round_up = error > ResidualError::Half || (error == ResidualError::Half && !Common::MostSignificantBit(int_result)); + break; + case RoundingMode::ToOdd: + UNREACHABLE(); + } + + if (round_up) { + int_result++; + } + + const FPT result = int_result == 0 + ? FPInfo::Zero(sign) + : FPRound(FPUnpacked{sign, 0, int_result}, fpcr, RoundingMode::TowardsZero, fpsr); + + if (error != ResidualError::Zero && exact) { + FPProcessException(FPExc::Inexact, fpcr, fpsr); + } + + return result; +} + +template u64 FPRoundInt(u32 op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr); +template u64 FPRoundInt(u64 op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr); + +} // namespace Dynarmic::FP diff --git a/src/common/fp/op/FPRoundInt.h b/src/common/fp/op/FPRoundInt.h new file mode 100644 index 00000000..9ab71740 --- /dev/null +++ b/src/common/fp/op/FPRoundInt.h @@ -0,0 +1,21 @@ +/* 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 "common/common_types.h" +#include "common/fp/fpsr.h" +#include "common/fp/rounding_mode.h" +#include "frontend/A64/FPCR.h" + +namespace Dynarmic::FP { + +using FPCR = A64::FPCR; + +template +u64 FPRoundInt(FPT op, FPCR fpcr, RoundingMode rounding, bool exact, FPSR& fpsr); + +} // namespace Dynarmic::FP