mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-01-07 23:18:10 +01:00
This function is defined as always disabling the AHP bit in the fpcr before performing any operations. At the same time, rename the original FPUnpack function to FPUnpackBase to match the pseudocode in the ARM reference manual.
180 lines
6.9 KiB
C++
180 lines
6.9 KiB
C++
/* 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/fpsr.h"
|
|
#include "common/fp/info.h"
|
|
#include "common/fp/mantissa_util.h"
|
|
#include "common/fp/process_exception.h"
|
|
#include "common/fp/rounding_mode.h"
|
|
#include "common/fp/unpacked.h"
|
|
#include "common/safe_ops.h"
|
|
|
|
namespace Dynarmic::FP {
|
|
|
|
template<typename FPT>
|
|
std::tuple<FPType, bool, FPUnpacked> FPUnpackBase(FPT op, FPCR fpcr, FPSR& fpsr) {
|
|
constexpr size_t sign_bit = FPInfo<FPT>::exponent_width + FPInfo<FPT>::explicit_mantissa_width;
|
|
constexpr size_t exponent_high_bit = FPInfo<FPT>::exponent_width + FPInfo<FPT>::explicit_mantissa_width - 1;
|
|
constexpr size_t exponent_low_bit = FPInfo<FPT>::explicit_mantissa_width;
|
|
constexpr size_t mantissa_high_bit = FPInfo<FPT>::explicit_mantissa_width - 1;
|
|
constexpr size_t mantissa_low_bit = 0;
|
|
constexpr int denormal_exponent = FPInfo<FPT>::exponent_min - int(FPInfo<FPT>::explicit_mantissa_width);
|
|
|
|
const bool sign = Common::Bit<sign_bit>(op);
|
|
const FPT exp_raw = Common::Bits<exponent_low_bit, exponent_high_bit>(op);
|
|
const FPT frac_raw = Common::Bits<mantissa_low_bit, mantissa_high_bit>(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, ToNormalized(sign, denormal_exponent, frac_raw)};
|
|
}
|
|
|
|
if (exp_raw == Common::Ones<FPT>(FPInfo<FPT>::exponent_width)) {
|
|
if (frac_raw == 0) {
|
|
return {FPType::Infinity, sign, ToNormalized(sign, 1000000, 1)};
|
|
}
|
|
|
|
const bool is_quiet = Common::Bit<mantissa_high_bit>(frac_raw);
|
|
return {is_quiet ? FPType::QNaN : FPType::SNaN, sign, {sign, 0, 0}};
|
|
}
|
|
|
|
const int exp = static_cast<int>(exp_raw) - FPInfo<FPT>::exponent_bias;
|
|
const u64 frac = static_cast<u64>(frac_raw | FPInfo<FPT>::implicit_leading_bit) << (normalized_point_position - FPInfo<FPT>::explicit_mantissa_width);
|
|
return {FPType::Nonzero, sign, {sign, exp, frac}};
|
|
}
|
|
|
|
template std::tuple<FPType, bool, FPUnpacked> FPUnpackBase<u32>(u32 op, FPCR fpcr, FPSR& fpsr);
|
|
template std::tuple<FPType, bool, FPUnpacked> FPUnpackBase<u64>(u64 op, FPCR fpcr, FPSR& fpsr);
|
|
|
|
template<size_t F>
|
|
std::tuple<bool, int, u64, ResidualError> Normalize(FPUnpacked op, int extra_right_shift = 0) {
|
|
const int highest_set_bit = Common::HighestSetBit(op.mantissa);
|
|
const int shift_amount = highest_set_bit - static_cast<int>(F) + extra_right_shift;
|
|
const u64 mantissa = Safe::LogicalShiftRight(op.mantissa, shift_amount);
|
|
const ResidualError error = ResidualErrorOnRightShift(op.mantissa, shift_amount);
|
|
const int exponent = op.exponent + highest_set_bit - normalized_point_position;
|
|
return std::make_tuple(op.sign, exponent, mantissa, error);
|
|
}
|
|
|
|
template<typename FPT>
|
|
FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) {
|
|
ASSERT(op.mantissa != 0);
|
|
ASSERT(rounding != RoundingMode::ToNearest_TieAwayFromZero);
|
|
|
|
constexpr int minimum_exp = FPInfo<FPT>::exponent_min;
|
|
constexpr size_t E = FPInfo<FPT>::exponent_width;
|
|
constexpr size_t F = FPInfo<FPT>::explicit_mantissa_width;
|
|
constexpr bool isFP16 = FPInfo<FPT>::total_width == 16;
|
|
|
|
auto [sign, exponent, mantissa, error] = Normalize<F>(op);
|
|
|
|
if (((!isFP16 && fpcr.FZ()) || (isFP16 && fpcr.FZ16())) && exponent < minimum_exp) {
|
|
fpsr.UFC(true);
|
|
return FPInfo<FPT>::Zero(sign);
|
|
}
|
|
|
|
int biased_exp = std::max<int>(exponent - minimum_exp + 1, 0);
|
|
if (biased_exp == 0) {
|
|
std::tie(sign, exponent, mantissa, error) = Normalize<F>(op, minimum_exp - exponent);
|
|
}
|
|
|
|
if (biased_exp == 0 && (error != ResidualError::Zero || fpcr.UFE())) {
|
|
FPProcessException(FPExc::Underflow, fpcr, fpsr);
|
|
}
|
|
|
|
bool round_up = false, overflow_to_inf = false;
|
|
switch (rounding) {
|
|
case RoundingMode::ToNearest_TieEven: {
|
|
round_up = (error > ResidualError::Half) || (error == ResidualError::Half && Common::Bit<0>(mantissa));
|
|
overflow_to_inf = true;
|
|
break;
|
|
}
|
|
case RoundingMode::TowardsPlusInfinity:
|
|
round_up = error != ResidualError::Zero && !sign;
|
|
overflow_to_inf = !sign;
|
|
break;
|
|
case RoundingMode::TowardsMinusInfinity:
|
|
round_up = error != ResidualError::Zero && sign;
|
|
overflow_to_inf = sign;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (round_up) {
|
|
if ((mantissa & FPInfo<FPT>::mantissa_mask) == FPInfo<FPT>::mantissa_mask) {
|
|
// Overflow on rounding up is going to happen
|
|
if (mantissa == FPInfo<FPT>::mantissa_mask) {
|
|
// Rounding up from denormal to normal
|
|
mantissa++;
|
|
biased_exp++;
|
|
} else {
|
|
// Rounding up to next exponent
|
|
mantissa = (mantissa + 1) / 2;
|
|
biased_exp++;
|
|
}
|
|
} else {
|
|
mantissa++;
|
|
}
|
|
}
|
|
|
|
if (error != ResidualError::Zero && rounding == RoundingMode::ToOdd) {
|
|
mantissa = Common::ModifyBit<0>(mantissa, true);
|
|
}
|
|
|
|
FPT result = 0;
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4127) // C4127: conditional expression is constant
|
|
#endif
|
|
if (!isFP16 || !fpcr.AHP()) {
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
constexpr int max_biased_exp = (1 << E) - 1;
|
|
if (biased_exp >= max_biased_exp) {
|
|
result = overflow_to_inf ? FPInfo<FPT>::Infinity(sign) : FPInfo<FPT>::MaxNormal(sign);
|
|
FPProcessException(FPExc::Overflow, fpcr, fpsr);
|
|
FPProcessException(FPExc::Inexact, fpcr, fpsr);
|
|
} else {
|
|
result = sign ? 1 : 0;
|
|
result <<= E;
|
|
result += biased_exp;
|
|
result <<= F;
|
|
result |= static_cast<FPT>(mantissa) & FPInfo<FPT>::mantissa_mask;
|
|
if (error != ResidualError::Zero) {
|
|
FPProcessException(FPExc::Inexact, fpcr, fpsr);
|
|
}
|
|
}
|
|
} else {
|
|
constexpr int max_biased_exp = (1 << E);
|
|
if (biased_exp >= max_biased_exp) {
|
|
result = sign ? 0xFFFF : 0x7FFF;
|
|
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
|
} else {
|
|
result = sign ? 1 : 0;
|
|
result <<= E;
|
|
result += biased_exp;
|
|
result <<= F;
|
|
result |= static_cast<FPT>(mantissa) & FPInfo<FPT>::mantissa_mask;
|
|
if (error != ResidualError::Zero) {
|
|
FPProcessException(FPExc::Inexact, fpcr, fpsr);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template u32 FPRoundBase<u32>(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
|
|
template u64 FPRoundBase<u64>(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
|
|
|
|
} // namespace Dynarmic::FP
|