mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-01-23 06:31:28 +00:00
564c43c907
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).
1362 lines
42 KiB
C++
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));
|
|
|
|
}
|