// Reciprocal
static void
ps_res(cpu::Core *state, Instruction instr)
{
   const double b0 = state->fpr[instr.frB].paired0;
   const double b1 = state->fpr[instr.frB].paired1;

   const bool vxsnan0 = is_signalling_nan(b0);
   const bool vxsnan1 = is_signalling_nan(b1);
   const bool zx0 = is_zero(b0);
   const bool zx1 = is_zero(b1);

   const uint32_t oldFPSCR = state->fpscr.value;
   state->fpscr.vxsnan |= vxsnan0 || vxsnan1;
   state->fpscr.zx |= zx0 || zx1;

   float d0, d1;
   auto write = true;

   if ((vxsnan0 && state->fpscr.ve) || (zx0 && state->fpscr.ze)) {
      write = false;
   } else {
      d0 = ppc_estimate_reciprocal(truncate_double(b0));
      updateFPRF(state, d0);
   }

   if ((vxsnan1 && state->fpscr.ve) || (zx1 && state->fpscr.ze)) {
      write = false;
   } else {
      d1 = ppc_estimate_reciprocal(truncate_double(b1));
   }

   if (write) {
      state->fpr[instr.frD].paired0 = extend_float(d0);
      state->fpr[instr.frD].paired1 = extend_float(d1);
   }

   if (std::fetestexcept(FE_INEXACT)) {
      // On inexact result, ps_res sets FPSCR[FI] without also setting
      // FPSCR[XX] (like fres).
      std::feclearexcept(FE_INEXACT);
      updateFPSCR(state, oldFPSCR);
      state->fpscr.fi = 1;
   } else {
      updateFPSCR(state, oldFPSCR);
   }

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Convert to Integer Word with Round toward Zero
static void
fctiwz(ThreadState *state, Instruction instr)
{
   double b;
   int32_t bi;
   b = state->fpr[instr.frB].paired0;

   if (b > static_cast<double>(INT_MAX)) {
      bi = INT_MAX;
      state->fpscr.vxcvi = 1;
   } else if (b < static_cast<double>(INT_MIN)) {
      bi = INT_MIN;
      state->fpscr.vxcvi = 1;
   } else {
      bi = static_cast<int32_t>(std::trunc(b));
   }

   auto vxsnan = is_signalling_nan(b);
   state->fpscr.vxsnan |= vxsnan;
   state->fpscr.vxcvi |= vxsnan;

   updateFPSCR(state);
   state->fpr[instr.frD].iw0 = bi;
   state->fpr[instr.frD].iw1 = 0xFFF80000 | (is_negative_zero(b) ? 1 : 0);

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Round to Single
static void
frsp(ThreadState *state, Instruction instr)
{
   auto b = state->fpr[instr.frB].paired0;
   state->fpscr.vxsnan |= is_signalling_nan(b);

   auto d = static_cast<float>(b);
   updateFPSCR(state);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = static_cast<double>(d);

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Multiply-Add Single
static void
fmadds(ThreadState *state, Instruction instr)
{
   double a, b, c, d;
   a = state->fpr[instr.frA].paired0;
   b = state->fpr[instr.frB].paired0;
   c = state->fpr[instr.frC].paired0;

   state->fpscr.vxsnan = is_signalling_nan(a) || is_signalling_nan(b) || is_signalling_nan(c);
   state->fpscr.vxisi = is_infinity(a * c) || is_infinity(c);
   state->fpscr.vximz = is_infinity(a * c) && is_zero(c);

   d = a * c;
   if (is_nan(d)) {
      if (is_nan(a)) {
         d = make_quiet(a);
      } else if (is_nan(b)) {
         d = make_quiet(b);
      } else if (is_nan(c)) {
         d = make_quiet(c);
      } else {
         d = make_nan<double>();
      }
   } else {
      if (is_infinity(d) && is_infinity(b) && !(is_infinity(a) || is_infinity(c))) {
         d = b;
      } else {
         d = d + b;
         if (is_nan(d)) {
            if (is_nan(b)) {
               d = make_quiet(b);
            } else {
               d = make_nan<double>();
            }
         }
      }
   }

   updateFPSCR(state);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = static_cast<float>(d);

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Reciprocal Estimate Single
static void
fres(ThreadState *state, Instruction instr)
{
   double b, d;
   b = state->fpr[instr.frB].paired0;

   state->fpscr.vxsnan |= is_signalling_nan(b);

   d = ppc_estimate_reciprocal(b);

   updateFPSCR(state);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = d;

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Reciprocal Square Root Estimate
static void
frsqrte(ThreadState *state, Instruction instr)
{
   double b, d;
   b = state->fpr[instr.frB].paired0;
   d = 1.0 / std::sqrt(b);

   auto vxsnan = is_signalling_nan(b);
   state->fpscr.vxsnan |= vxsnan;
   state->fpscr.vxsqrt |= vxsnan;

   updateFPSCR(state);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = d;

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Convert to Integer Word
static void
fctiw(ThreadState *state, Instruction instr)
{
   double b;
   int32_t bi;
   b = state->fpr[instr.frB].paired0;

   if (b > static_cast<double>(INT_MAX)) {
      bi = INT_MAX;
      state->fpscr.vxcvi = 1;
   } else if (b < static_cast<double>(INT_MIN)) {
      bi = INT_MIN;
      state->fpscr.vxcvi = 1;
   } else {
      switch (state->fpscr.rn) {
      case FloatingPointRoundMode::Nearest:
         bi = static_cast<int32_t>(std::round(b));
         break;
      case FloatingPointRoundMode::Positive:
         bi = static_cast<int32_t>(std::ceil(b));
         break;
      case FloatingPointRoundMode::Negative:
         bi = static_cast<int32_t>(std::floor(b));
         break;
      case FloatingPointRoundMode::Zero:
         bi = static_cast<int32_t>(std::trunc(b));
         break;
      }
   }

   auto vxsnan = is_signalling_nan(b);
   state->fpscr.vxsnan |= vxsnan;
   state->fpscr.vxcvi |= vxsnan;

   updateFPSCR(state);
   state->fpr[instr.frD].iw0 = bi;
   state->fpr[instr.frD].iw1 = 0xFFF80000 | (is_negative_zero(b) ? 1 : 0);

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Floating Negative Multiply-Sub
static void
fnmsub(ThreadState *state, Instruction instr)
{
   double a, b, c, d;
   a = state->fpr[instr.frA].paired0;
   b = state->fpr[instr.frB].paired0;
   c = state->fpr[instr.frC].paired0;

   state->fpscr.vximz = is_infinity(a * c) && is_zero(c);
   state->fpscr.vxisi = is_infinity(a * c) || is_infinity(c);
   state->fpscr.vxsnan = is_signalling_nan(a) || is_signalling_nan(b) || is_signalling_nan(c);

   d = -((a * c) - b);
   updateFPSCR(state);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = d;

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
static void
fsubGeneric(ThreadState *state, Instruction instr)
{
   double a, b, d;
   a = state->fpr[instr.frA].paired0;
   b = state->fpr[instr.frB].paired0;

   state->fpscr.vxisi = is_infinity(a) && is_infinity(b);
   state->fpscr.vxsnan = is_signalling_nan(a) || is_signalling_nan(b);

   d = static_cast<Type>(a - b);
   updateFPSCR(state);

   d = checkNan<Type>(d, a, b);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = d;

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
static void
fmulGeneric(ThreadState *state, Instruction instr)
{
   double a, c, d;
   a = state->fpr[instr.frA].paired0;
   c = state->fpr[instr.frC].paired0;

   state->fpscr.vximz = is_infinity(a) && is_zero(c);
   state->fpscr.vxsnan = is_signalling_nan(a) || is_signalling_nan(c);

   d = static_cast<Type>(a * c);
   updateFPSCR(state);

   d = checkNan<Type>(d, a, c);
   updateFPRF(state, d);
   state->fpr[instr.frD].paired0 = d;

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
static void
fmaGeneric(cpu::Core *state, Instruction instr)
{
   const uint32_t oldFPSCR = state->fpscr.value;

   float d0, d1;
   const bool wrote0 = fmaSingle<flags, 0, slotC0>(state, instr, &d0);
   const bool wrote1 = fmaSingle<flags, 1, slotC1>(state, instr, &d1);
   if (wrote0 && wrote1) {
      state->fpr[instr.frD].paired0 = extend_float(d0);
      state->fpr[instr.frD].paired1 = extend_float(d1);
   }

   if (wrote0) {
      updateFPRF(state, d0);
   }
   updateFPSCR(state, oldFPSCR);

   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}
// Reciprocal Square Root
static void
ps_rsqrte(cpu::Core *state, Instruction instr)
{
   const double b0 = state->fpr[instr.frB].paired0;
   const double b1 = state->fpr[instr.frB].paired1;

   const bool vxsnan0 = is_signalling_nan(b0);
   const bool vxsnan1 = is_signalling_nan(b1);
   const bool vxsqrt0 = !vxsnan0 && std::signbit(b0) && !is_zero(b0);
   const bool vxsqrt1 = !vxsnan1 && std::signbit(b1) && !is_zero(b1);
   const bool zx0 = is_zero(b0);
   const bool zx1 = is_zero(b1);

   const uint32_t oldFPSCR = state->fpscr.value;
   state->fpscr.vxsnan |= vxsnan0 || vxsnan1;
   state->fpscr.vxsqrt |= vxsqrt0 || vxsqrt1;
   state->fpscr.zx |= zx0 || zx1;

   double d0, d1;
   bool write = true;
   if (((vxsnan0 || vxsqrt0) && state->fpscr.ve) || (zx0 && state->fpscr.ze)) {
      write = false;
   } else {
      d0 = ppc_estimate_reciprocal_root(b0);
      updateFPRF(state, d0);
   }
   if (((vxsnan1 || vxsqrt1) && state->fpscr.ve) || (zx1 && state->fpscr.ze)) {
      write = false;
   } else {
      d1 = ppc_estimate_reciprocal_root(b1);
   }

   if (write) {
      // ps_rsqrte behaves strangely when the result's magnitude is out of
      // range: ps0 keeps its double-precision exponent, while ps1 appears
      // to get an arbitrary value from the floating-point circuitry.  The
      // details of how ps1's exponent is affected are unknown, but the
      // logic below works for double-precision inputs 0x7FE...FFF (maximum
      // normal) and 0x000...001 (minimum denormal).

      auto bits0 = get_float_bits(d0);
      bits0.mantissa &= UINT64_C(0xFFFFFE0000000);
      state->fpr[instr.frD].paired0 = bits0.v;

      auto bits1 = get_float_bits(d1);
      if (bits1.exponent == 0) {
         // Leave as zero (reciprocal square root can never be a denormal).
      } else if (bits1.exponent < 1151) {
         int8_t exponent8 = (bits1.exponent - 1023) & 0xFF;
         bits1.exponent = 1023 + exponent8;
      } else if (bits1.exponent < 2047) {
         bits1.exponent = 1022;
      }
      bits1.mantissa &= UINT64_C(0xFFFFFE0000000);
      state->fpr[instr.frD].paired1 = bits1.v;
   }

   updateFPSCR(state, oldFPSCR);
   if (instr.rc) {
      updateFloatConditionRegister(state);
   }
}