dingusppc/cpu/ppc/ppcfpopcodes.cpp
Mihai Parparita 564c43c907 Remove ppc_cur_instruction global variable
Replace it wth an explicit opcode parameter that is passed around. That
is both slightly easier to reason about (to trace where it comes from)
and slightly faster, since it can be read from a register.

On my machine takes booting to "Welcome to Macintosh" being output in
a verbose boot of Mac OS X 10.2.8 from 31.8s to 30.6s (average of 5
runs, measured using deterministic mode and looking at when execution
reaches PC 0x90004a88).
2024-11-28 23:41:38 +01:00

1362 lines
42 KiB
C++

/*
DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-24 divingkatae and maximum
(theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// The floating point opcodes for the processor - ppcfpopcodes.cpp
#include "ppcemu.h"
#include "ppcmacros.h"
#include "ppcmmu.h"
#include <stdlib.h>
#include <cfenv>
#include <cinttypes>
#include <cmath>
#include <cfloat>
inline static void ppc_update_cr1() {
// copy FPSCR[FX|FEX|VX|OX] to CR1
ppc_state.cr = (ppc_state.cr & ~CR_select::CR1_field) |
((ppc_state.fpscr >> 4) & CR_select::CR1_field);
}
static int32_t round_to_nearest(double f) {
return static_cast<int32_t>(static_cast<int64_t> (std::floor(f + 0.5)));
}
void set_host_rounding_mode(uint8_t mode) {
switch(mode & FPSCR::RN_MASK) {
case 0:
std::fesetround(FE_TONEAREST);
break;
case 1:
std::fesetround(FE_TOWARDZERO);
break;
case 2:
std::fesetround(FE_UPWARD);
break;
case 3:
std::fesetround(FE_DOWNWARD);
break;
}
}
void update_fpscr(uint32_t new_fpscr) {
if ((new_fpscr & FPSCR::RN_MASK) != (ppc_state.fpscr & FPSCR::RN_MASK))
set_host_rounding_mode(new_fpscr & FPSCR::RN_MASK);
ppc_state.fpscr = new_fpscr;
}
static int32_t round_to_zero(double f) {
return static_cast<int32_t>(std::trunc(f));
}
static int32_t round_to_pos_inf(double f) {
return static_cast<int32_t>(std::ceil(f));
}
static int32_t round_to_neg_inf(double f) {
return static_cast<int32_t>(std::floor(f));
}
inline static bool check_snan(int check_reg) {
uint64_t check_int = ppc_state.fpr[check_reg].int64_r;
return (((check_int & (0x7FFULL << 52)) == (0x7FFULL << 52)) &&
((check_int & ~(0xFFFULL << 52)) != 0ULL) &&
((check_int & (0x1ULL << 51)) == 0ULL));
}
inline static bool snan_single_check(int reg_a) {
if (check_snan(reg_a)) {
ppc_state.fpscr |= FX | VX | VXSNAN;
return true;
}
return false;
}
inline static bool snan_double_check(int reg_a, int reg_b) {
if (check_snan(reg_a) || check_snan(reg_b)) {
ppc_state.fpscr |= FX | VX | VXSNAN;
return true;
}
return false;
}
inline static void max_double_check(double value_a, double value_b) {
if (((value_a == DBL_MAX) && (value_b == DBL_MAX)) ||
((value_a == -DBL_MAX) && (value_b == -DBL_MAX)))
ppc_state.fpscr |= FX | OX | XX | FI;
}
inline static bool check_qnan(int check_reg) {
uint64_t check_int = ppc_state.fpr[check_reg].int64_r;
return (((check_int & (0x7FFULL << 52)) == (0x7FFULL << 52)) &&
((check_int & (0x1ULL << 51)) == (0x1ULL << 51)));
}
static double set_endresult_nan(double ppc_result_d) {
ppc_state.fpscr += (FX + VX);
return std::numeric_limits<double>::quiet_NaN();
}
static void fpresult_update(double set_result) {
if (std::isnan(set_result)) {
ppc_state.fpscr |= FPCC_FUNAN | FPRCD;
} else {
if (set_result > 0.0) {
ppc_state.fpscr |= FPCC_POS;
} else if (set_result < 0.0) {
ppc_state.fpscr |= FPCC_NEG;
} else {
ppc_state.fpscr |= FPCC_ZERO;
}
if (std::fetestexcept(FE_OVERFLOW)) {
ppc_state.fpscr |= (OX + FX);
}
if (std::fetestexcept(FE_UNDERFLOW)) {
ppc_state.fpscr |= (UX + FX);
}
if (std::fetestexcept(FE_DIVBYZERO)) {
ppc_state.fpscr |= (ZX + FX);
}
std::feclearexcept(FE_ALL_EXCEPT);
if (std::isinf(set_result))
ppc_state.fpscr |= FPCC_FUNAN;
}
}
static void ppc_update_vx() {
uint32_t fpscr_check = ppc_state.fpscr & 0x1F80700U;
if (fpscr_check)
ppc_state.fpscr |= VX;
else
ppc_state.fpscr &= ~VX;
}
static void ppc_update_fex() {
uint32_t fpscr_check = (((ppc_state.fpscr >> 25) & 0x1F) &
((ppc_state.fpscr >> 3) & 0x1F));
if (fpscr_check)
ppc_state.fpscr |= VX;
else
ppc_state.fpscr &= ~VX;
}
// Floating Point Arithmetic
template <field_rc rec>
void dppc_interpreter::ppc_fadd(uint32_t opcode) {
ppc_grab_regsfpdab(opcode);
max_double_check(val_reg_a, val_reg_b);
double ppc_dblresult64_d = val_reg_a + val_reg_b;
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || \
((val_reg_a == -inf) && (val_reg_b == inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_double_check(reg_a, reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
}
else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fadd<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fadd<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fsub(uint32_t opcode) {
ppc_grab_regsfpdab(opcode);
double ppc_dblresult64_d = val_reg_a - val_reg_b;
double inf = std::numeric_limits<double>::infinity();
if ((val_reg_a == inf) && (val_reg_b == inf)) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_double_check(reg_a, reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fsub<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fsub<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fdiv(uint32_t opcode) {
ppc_grab_regsfpdab(opcode);
double ppc_dblresult64_d = val_reg_a / val_reg_b;
if (val_reg_b == 0.0) {
ppc_state.fpscr |= 0xa0000000;
ppc_dblresult64_d = -ppc_dblresult64_d;
}
if (std::isinf(val_reg_a) && std::isinf(val_reg_b)) {
ppc_state.fpscr |= VXIDI;
}
if ((val_reg_a == 0.0) && (val_reg_b == 0.0)) {
ppc_state.fpscr |= VXZDZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_double_check(reg_a, reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
}
else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fdiv<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fdiv<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fmul(uint32_t opcode) {
ppc_grab_regsfpdac(opcode);
double ppc_dblresult64_d = val_reg_a * val_reg_c;
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
if (snan_double_check(reg_a, reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmul<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmul<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fmadd(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = std::fma(val_reg_a, val_reg_c, val_reg_b);
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || \
((val_reg_a == -inf) && (val_reg_b == inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmadd<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmadd<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fmsub(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = std::fma(val_reg_a, val_reg_c, -val_reg_b);
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || ((val_reg_a == -inf) && (val_reg_b == inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (std::isnan(val_reg_a) || std::isnan(val_reg_b) || std::isnan(val_reg_c)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmsub<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmsub<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fnmadd(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = -std::fma(val_reg_a, val_reg_c, val_reg_b);
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || ((val_reg_a == -inf) && (val_reg_b == inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fnmadd<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fnmadd<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fnmsub(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = -std::fma(val_reg_a, val_reg_c, -val_reg_b);
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || ((val_reg_a == -inf) && (val_reg_b == inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fnmsub<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fnmsub<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fadds(uint32_t opcode) {
ppc_grab_regsfpdab(opcode);
max_double_check(val_reg_a, val_reg_b);
double ppc_dblresult64_d = (float)(val_reg_a + val_reg_b);
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || ((val_reg_a == -inf) && (val_reg_b == inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_double_check(reg_a, reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fadds<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fadds<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fsubs(uint32_t opcode) {
ppc_grab_regsfpdab(opcode);
double ppc_dblresult64_d = (float)(val_reg_a - val_reg_b);
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == inf)) ||
((val_reg_a == -inf) && (val_reg_b == -inf))) {
ppc_state.fpscr |= VXISI;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_double_check(reg_a, reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fsubs<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fsubs<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fdivs(uint32_t opcode) {
ppc_grab_regsfpdab(opcode);
double ppc_dblresult64_d = (float)(val_reg_a / val_reg_b);
if (val_reg_b == 0.0) {
ppc_state.fpscr |= 0xa0000000;
ppc_dblresult64_d = -ppc_dblresult64_d;
}
if (std::isinf(val_reg_a) && std::isinf(val_reg_b)) {
ppc_state.fpscr |= VXIDI;
}
if ((val_reg_a == 0.0) && (val_reg_b == 0.0)) {
ppc_state.fpscr |= VXZDZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
if (snan_double_check(reg_a, reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fdivs<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fdivs<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fmuls(uint32_t opcode) {
ppc_grab_regsfpdac(opcode);
double ppc_dblresult64_d = (float)(val_reg_a * val_reg_c);
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
if (snan_double_check(reg_a, reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmuls<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmuls<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fmadds(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = (float)std::fma(val_reg_a, val_reg_c, val_reg_b);
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || ((val_reg_a == -inf) && (val_reg_b == inf)))
ppc_state.fpscr |= VXISI;
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmadds<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmadds<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fmsubs(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = (float)std::fma(val_reg_a, val_reg_c, -val_reg_b);
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
double inf = std::numeric_limits<double>::infinity();
if ((val_reg_a == inf) && (val_reg_b == inf))
ppc_state.fpscr |= VXISI;
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmsubs<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmsubs<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fnmadds(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = -(float)std::fma(val_reg_a, val_reg_c, val_reg_b);
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
double inf = std::numeric_limits<double>::infinity();
if (((val_reg_a == inf) && (val_reg_b == -inf)) || ((val_reg_a == -inf) && (val_reg_b == inf)))
ppc_state.fpscr |= VXISI;
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fnmadds<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fnmadds<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fnmsubs(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
snan_double_check(reg_a, reg_c);
snan_single_check(reg_b);
double ppc_dblresult64_d = -(float)std::fma(val_reg_a, val_reg_c, -val_reg_b);
if ((std::isinf(val_reg_a) && (val_reg_c == 0.0)) ||
(std::isinf(val_reg_c) && (val_reg_a == 0.0))) {
ppc_state.fpscr |= VXIMZ;
if (std::isnan(ppc_dblresult64_d)) {
ppc_dblresult64_d = set_endresult_nan(ppc_dblresult64_d);
}
}
double inf = std::numeric_limits<double>::infinity();
if ((val_reg_a == inf) && (val_reg_b == inf))
ppc_state.fpscr |= VXISI;
if (std::isnan(val_reg_a) || std::isnan(val_reg_b) || std::isnan(val_reg_c)) {
ppc_dblresult64_d = std::numeric_limits<double>::quiet_NaN();
}
if (snan_single_check(reg_a) || snan_single_check(reg_b) || snan_single_check(reg_c)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
fpresult_update(ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fnmsubs<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fnmsubs<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fabs(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
uint64_t ppc_result64_d = FPR_INT(reg_b) & ~0x8000000000000000U;
ppc_store_fpresult_int(reg_d, ppc_result64_d);
snan_single_check(reg_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fabs<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fabs<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fnabs(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
uint64_t ppc_result64_d = FPR_INT(reg_b) | 0x8000000000000000U;
ppc_store_fpresult_int(reg_d, ppc_result64_d);
snan_single_check(reg_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fnabs<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fnabs<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fneg(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
uint64_t ppc_result64_d = FPR_INT(reg_b) ^ 0x8000000000000000U;
ppc_store_fpresult_int(reg_d, ppc_result64_d);
snan_single_check(reg_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fneg<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fneg<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fsel(uint32_t opcode) {
ppc_grab_regsfpdabc(opcode);
double ppc_dblresult64_d = (val_reg_a >= -0.0) ? val_reg_c : val_reg_b;
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fsel<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fsel<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fsqrt(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
double testd2 = (double)(GET_FPR(reg_b));
double ppc_dblresult64_d = std::sqrt(testd2);
if (snan_single_check(reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fsqrt<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fsqrt<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fsqrts(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
double testd2 = (double)(GET_FPR(reg_b));
double ppc_dblresult64_d = (float)std::sqrt(testd2);
if (snan_single_check(reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fsqrts<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fsqrts<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_frsqrte(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
double testd2 = (double)(GET_FPR(reg_b));
double ppc_dblresult64_d = 1.0 / sqrt(testd2);
if (snan_single_check(reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_frsqrte<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_frsqrte<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_frsp(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
double ppc_dblresult64_d = (float)(GET_FPR(reg_b));
if (snan_single_check(reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_frsp<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_frsp<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fres(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
double start_num = GET_FPR(reg_b);
double ppc_dblresult64_d = (float)(1.0 / start_num);
if (start_num == 0.0) {
ppc_state.fpscr |= FPSCR::ZX;
}
else if (std::isnan(start_num)) {
ppc_state.fpscr |= FPSCR::VXSNAN;
}
else if (std::isinf(start_num)){
ppc_state.fpscr &= 0xFFF9FFFF;
ppc_state.fpscr |= FPSCR::VXSNAN;
}
if (snan_single_check(reg_b)) {
uint64_t qnan = 0x7FFC000000000000;
ppc_store_fpresult_int(reg_d, qnan);
} else {
ppc_store_fpresult_flt(reg_d, ppc_dblresult64_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fres<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fres<RC1>(uint32_t opcode);
static void round_to_int(uint32_t opcode, const uint8_t mode, field_rc rec) {
ppc_grab_regsfpdb(opcode);
double val_reg_b = GET_FPR(reg_b);
if (std::isnan(val_reg_b)) {
ppc_state.fpscr &= ~(FPSCR::FR | FPSCR::FI);
ppc_state.fpscr |= (FPSCR::VXCVI | FPSCR::VX);
if (check_snan(reg_b)) // issnan
ppc_state.fpscr |= FPSCR::VXSNAN;
if (ppc_state.fpscr & FPSCR::VE) {
ppc_state.fpscr |= FPSCR::FEX; // VX=1 and VE=1 cause FEX to be set
ppc_floating_point_exception(opcode);
} else {
ppc_state.fpr[reg_d].int64_r = 0xFFF8000080000000ULL;
}
} else if (val_reg_b > static_cast<double>(0x7fffffff) ||
val_reg_b < -static_cast<double>(0x80000000)) {
ppc_state.fpscr &= ~(FPSCR::FR | FPSCR::FI);
ppc_state.fpscr |= (FPSCR::VXCVI | FPSCR::VX);
if (ppc_state.fpscr & FPSCR::VE) {
ppc_state.fpscr |= FPSCR::FEX; // VX=1 and VE=1 cause FEX to be set
ppc_floating_point_exception(opcode);
} else {
if (val_reg_b >= 0.0f)
ppc_state.fpr[reg_d].int64_r = 0xFFF800007FFFFFFFULL;
else
ppc_state.fpr[reg_d].int64_r = 0xFFF8000080000000ULL;
}
} else {
uint64_t ppc_result64_d;
switch (mode & 0x3) {
case 0:
ppc_result64_d = uint32_t(round_to_nearest(val_reg_b));
break;
case 1:
ppc_result64_d = uint32_t(round_to_zero(val_reg_b));
break;
case 2:
ppc_result64_d = uint32_t(round_to_pos_inf(val_reg_b));
break;
case 3:
ppc_result64_d = uint32_t(round_to_neg_inf(val_reg_b));
break;
}
ppc_result64_d |= 0xFFF8000000000000ULL;
ppc_store_fpresult_int(reg_d, ppc_result64_d);
}
if (rec)
ppc_update_cr1();
}
template <field_rc rec>
void dppc_interpreter::ppc_fctiw(uint32_t opcode) {
round_to_int(opcode, ppc_state.fpscr & 0x3, rec);
}
template void dppc_interpreter::ppc_fctiw<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fctiw<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_fctiwz(uint32_t opcode) {
round_to_int(opcode, 1, rec);
}
template void dppc_interpreter::ppc_fctiwz<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fctiwz<RC1>(uint32_t opcode);
// Floating Point Store and Load
void dppc_interpreter::ppc_lfs(uint32_t opcode) {
ppc_grab_regsfpdia(opcode);
uint32_t ea = int32_t(int16_t(opcode));
ea += (reg_a) ? val_reg_a : 0;
uint32_t result = mmu_read_vmem<uint32_t>(opcode, ea);
ppc_state.fpr[reg_d].dbl64_r = *(float*)(&result);
}
void dppc_interpreter::ppc_lfsu(uint32_t opcode) {
ppc_grab_regsfpdia(opcode);
if (reg_a) {
uint32_t ea = int32_t(int16_t(opcode));
ea += (reg_a) ? val_reg_a : 0;
uint32_t result = mmu_read_vmem<uint32_t>(opcode, ea);
ppc_state.fpr[reg_d].dbl64_r = *(float*)(&result);
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_lfsx(uint32_t opcode) {
ppc_grab_regsfpdiab(opcode);
uint32_t ea = val_reg_b + (reg_a ? val_reg_a : 0);
uint32_t result = mmu_read_vmem<uint32_t>(opcode, ea);
ppc_state.fpr[reg_d].dbl64_r = *(float*)(&result);
}
void dppc_interpreter::ppc_lfsux(uint32_t opcode) {
ppc_grab_regsfpdiab(opcode);
if (reg_a) {
uint32_t ea = val_reg_a + val_reg_b;
uint32_t result = mmu_read_vmem<uint32_t>(opcode, ea);
ppc_state.fpr[reg_d].dbl64_r = *(float*)(&result);
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_lfd(uint32_t opcode) {
ppc_grab_regsfpdia(opcode);
uint32_t ea = int32_t(int16_t(opcode));
ea += (reg_a) ? val_reg_a : 0;
uint64_t ppc_result64_d = mmu_read_vmem<uint64_t>(opcode, ea);
ppc_store_fpresult_int(reg_d, ppc_result64_d);
}
void dppc_interpreter::ppc_lfdu(uint32_t opcode) {
ppc_grab_regsfpdia(opcode);
if (reg_a != 0) {
uint32_t ea = int32_t(int16_t(opcode));
ea += val_reg_a;
uint64_t ppc_result64_d = mmu_read_vmem<uint64_t>(opcode, ea);
ppc_store_fpresult_int(reg_d, ppc_result64_d);
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_lfdx(uint32_t opcode) {
ppc_grab_regsfpdiab(opcode);
uint32_t ea = val_reg_b + (reg_a ? val_reg_a : 0);
uint64_t ppc_result64_d = mmu_read_vmem<uint64_t>(opcode, ea);
ppc_store_fpresult_int(reg_d, ppc_result64_d);
}
void dppc_interpreter::ppc_lfdux(uint32_t opcode) {
ppc_grab_regsfpdiab(opcode);
if (reg_a) {
uint32_t ea = val_reg_a + val_reg_b;
uint64_t ppc_result64_d = mmu_read_vmem<uint64_t>(opcode, ea);
ppc_store_fpresult_int(reg_d, ppc_result64_d);
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_stfs(uint32_t opcode) {
ppc_grab_regsfpsia(opcode);
uint32_t ea = int32_t(int16_t(opcode));
ea += (reg_a) ? val_reg_a : 0;
float result = float(ppc_state.fpr[reg_s].dbl64_r);
mmu_write_vmem<uint32_t>(opcode, ea, *(uint32_t*)(&result));
}
void dppc_interpreter::ppc_stfsu(uint32_t opcode) {
ppc_grab_regsfpsia(opcode);
if (reg_a != 0) {
uint32_t ea = int32_t(int16_t(opcode));
ea += val_reg_a;
float result = float(ppc_state.fpr[reg_s].dbl64_r);
mmu_write_vmem<uint32_t>(opcode, ea, *(uint32_t*)(&result));
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_stfsx(uint32_t opcode) {
ppc_grab_regsfpsiab(opcode);
uint32_t ea = val_reg_b + (reg_a ? val_reg_a : 0);
float result = float(ppc_state.fpr[reg_s].dbl64_r);
mmu_write_vmem<uint32_t>(opcode, ea, *(uint32_t*)(&result));
}
void dppc_interpreter::ppc_stfsux(uint32_t opcode) {
ppc_grab_regsfpsiab(opcode);
if (reg_a) {
uint32_t ea = val_reg_a + val_reg_b;
float result = float(ppc_state.fpr[reg_s].dbl64_r);
mmu_write_vmem<uint32_t>(opcode, ea, *(uint32_t*)(&result));
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_stfd(uint32_t opcode) {
ppc_grab_regsfpsia(opcode);
uint32_t ea = int32_t(int16_t(opcode));
ea += reg_a ? val_reg_a : 0;
mmu_write_vmem<uint64_t>(opcode, ea, ppc_state.fpr[reg_s].int64_r);
}
void dppc_interpreter::ppc_stfdu(uint32_t opcode) {
ppc_grab_regsfpsia(opcode);
if (reg_a != 0) {
uint32_t ea = int32_t(int16_t(opcode));
ea += val_reg_a;
mmu_write_vmem<uint64_t>(opcode, ea, ppc_state.fpr[reg_s].int64_r);
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_stfdx(uint32_t opcode) {
ppc_grab_regsfpsiab(opcode);
uint32_t ea = val_reg_b + (reg_a ? val_reg_a : 0);
mmu_write_vmem<uint64_t>(opcode, ea, ppc_state.fpr[reg_s].int64_r);
}
void dppc_interpreter::ppc_stfdux(uint32_t opcode) {
ppc_grab_regsfpsiab(opcode);
if (reg_a != 0) {
uint32_t ea = val_reg_a + val_reg_b;
mmu_write_vmem<uint64_t>(opcode, ea, ppc_state.fpr[reg_s].int64_r);
ppc_state.gpr[reg_a] = ea;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_stfiwx(uint32_t opcode) {
ppc_grab_regsfpsiab(opcode);
uint32_t ea = val_reg_b + (reg_a ? val_reg_a : 0);
mmu_write_vmem<uint32_t>(opcode, ea, uint32_t(ppc_state.fpr[reg_s].int64_r));
}
// Floating Point Register Transfer
template <field_rc rec>
void dppc_interpreter::ppc_fmr(uint32_t opcode) {
ppc_grab_regsfpdb(opcode);
ppc_state.fpr[reg_d].dbl64_r = ppc_state.fpr[reg_b].dbl64_r;
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_fmr<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_fmr<RC1>(uint32_t opcode);
template <field_601 for601, field_rc rec>
void dppc_interpreter::ppc_mffs(uint32_t opcode) {
int reg_d = (opcode >> 21) & 31;
ppc_state.fpr[reg_d].int64_r = uint64_t(ppc_state.fpscr) | (for601 ? 0xFFFFFFFF00000000ULL : 0xFFF8000000000000ULL);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_mffs<NOT601, RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_mffs<NOT601, RC1>(uint32_t opcode);
template void dppc_interpreter::ppc_mffs<IS601, RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_mffs<IS601, RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_mtfsf(uint32_t opcode) {
int reg_b = (opcode >> 11) & 0x1F;
uint8_t fm = (opcode >> 17) & 0xFF;
uint32_t cr_mask = 0;
if (fm == 0xFFU) // the fast case
cr_mask = 0xFFFFFFFFUL;
else { // the slow case
if (fm & 0x80) cr_mask |= 0xF0000000UL;
if (fm & 0x40) cr_mask |= 0x0F000000UL;
if (fm & 0x20) cr_mask |= 0x00F00000UL;
if (fm & 0x10) cr_mask |= 0x000F0000UL;
if (fm & 0x08) cr_mask |= 0x0000F000UL;
if (fm & 0x04) cr_mask |= 0x00000F00UL;
if (fm & 0x02) cr_mask |= 0x000000F0UL;
if (fm & 0x01) cr_mask |= 0x0000000FUL;
}
// ensure neither FEX nor VX will be changed
cr_mask &= ~(FPSCR::FEX | FPSCR::VX);
// copy FPR[reg_b] to FPSCR under control of cr_mask
ppc_state.fpscr = (ppc_state.fpscr & ~cr_mask) | (ppc_state.fpr[reg_b].int64_r & cr_mask);
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_mtfsf<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_mtfsf<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_mtfsfi(uint32_t opcode) {
int crf_d = (opcode >> 21) & 0x1C;
uint32_t imm = (opcode << 16) & 0xF0000000UL;
// prepare field mask and ensure that neither FEX nor VX will be changed
uint32_t mask = (0xF0000000UL >> crf_d) & ~(FPSCR::FEX | FPSCR::VX);
// copy imm to FPSCR[crf_d] under control of the field mask
ppc_state.fpscr = (ppc_state.fpscr & ~mask) | ((imm >> crf_d) & mask);
// Update FEX and VX according to the "usual rule"
ppc_update_vx();
ppc_update_fex();
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_mtfsfi<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_mtfsfi<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_mtfsb0(uint32_t opcode) {
int crf_d = (opcode >> 21) & 0x1F;
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitly cleared
ppc_state.fpscr &= ~(0x80000000UL >> crf_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_mtfsb0<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_mtfsb0<RC1>(uint32_t opcode);
template <field_rc rec>
void dppc_interpreter::ppc_mtfsb1(uint32_t opcode) {
int crf_d = (opcode >> 21) & 0x1F;
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitly set
ppc_state.fpscr |= (0x80000000UL >> crf_d);
}
if (rec)
ppc_update_cr1();
}
template void dppc_interpreter::ppc_mtfsb1<RC0>(uint32_t opcode);
template void dppc_interpreter::ppc_mtfsb1<RC1>(uint32_t opcode);
void dppc_interpreter::ppc_mcrfs(uint32_t opcode) {
int crf_d = (opcode >> 21) & 0x1C;
int crf_s = (opcode >> 16) & 0x1C;
ppc_state.cr = (
(ppc_state.cr & ~(0xF0000000UL >> crf_d)) |
(((ppc_state.fpscr << crf_s) & 0xF0000000UL) >> crf_d)
);
ppc_state.fpscr &= ~((0xF0000000UL >> crf_s) & (
// keep only the FPSCR bits that can be explicitly cleared
FPSCR::FX | FPSCR::OX |
FPSCR::UX | FPSCR::ZX | FPSCR::XX | FPSCR::VXSNAN |
FPSCR::VXISI | FPSCR::VXIDI | FPSCR::VXZDZ | FPSCR::VXIMZ |
FPSCR::VXVC |
FPSCR::VXSOFT | FPSCR::VXSQRT | FPSCR::VXCVI
));
}
// Floating Point Comparisons
void dppc_interpreter::ppc_fcmpo(uint32_t opcode) {
ppc_grab_regsfpsab(opcode);
uint32_t cmp_c = 0;
if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
cmp_c |= CRx_bit::CR_SO;
ppc_state.fpscr |= FX | VX;
if (check_snan(reg_a) || check_snan(reg_b)) {
ppc_state.fpscr |= VXSNAN;
}
if (!(ppc_state.fpscr & FEX) || check_qnan(reg_a) || check_qnan(reg_b)) {
ppc_state.fpscr |= VXVC;
}
}
else if (db_test_a < db_test_b) {
cmp_c |= CRx_bit::CR_LT;
}
else if (db_test_a > db_test_b) {
cmp_c |= CRx_bit::CR_GT;
}
else {
cmp_c |= CRx_bit::CR_EQ;
}
ppc_state.fpscr &= ~VE; //kludge to pass tests
ppc_state.fpscr = (ppc_state.fpscr & ~FPSCR::FPCC_MASK) | (cmp_c >> 16); // update FPCC
ppc_state.cr = ((ppc_state.cr & ~(0xF0000000 >> crf_d)) | (cmp_c >> crf_d));
}
void dppc_interpreter::ppc_fcmpu(uint32_t opcode) {
ppc_grab_regsfpsab(opcode);
uint32_t cmp_c = 0;
if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
cmp_c |= CRx_bit::CR_SO;
if (check_snan(reg_a) || check_snan(reg_b)) {
ppc_state.fpscr |= FX | VX | VXSNAN;
}
}
else if (db_test_a < db_test_b) {
cmp_c |= CRx_bit::CR_LT;
}
else if (db_test_a > db_test_b) {
cmp_c |= CRx_bit::CR_GT;
}
else {
cmp_c |= CRx_bit::CR_EQ;
}
ppc_state.fpscr &= ~VE; //kludge to pass tests
ppc_state.fpscr = (ppc_state.fpscr & ~FPSCR::FPCC_MASK) | (cmp_c >> 16); // update FPCC
ppc_state.cr = ((ppc_state.cr & ~(0xF0000000UL >> crf_d)) | (cmp_c >> crf_d));
}