mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-09-29 17:56:59 +00:00
7f229b0fe8
It was always changing CR1 (starting at CR bit 4) instead of the CR selected by crfD. Also, it was clearing all but the FL,FG,FE,FU bits of FPRF of FPSCR.
988 lines
30 KiB
C++
988 lines
30 KiB
C++
/*
|
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
|
Copyright (C) 2018-23 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 "ppcmmu.h"
|
|
#include <stdlib.h>
|
|
#include <cfenv>
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
#include <cfloat>
|
|
|
|
// Used for FP calcs
|
|
uint64_t ppc_result64_b;
|
|
uint64_t ppc_result64_d;
|
|
|
|
double ppc_dblresult64_d;
|
|
|
|
// Storage and register retrieval functions for the floating point functions.
|
|
|
|
#define GET_FPR(reg) ppc_state.fpr[(reg)].dbl64_r
|
|
|
|
double fp_return_double(uint32_t reg) {
|
|
return ppc_state.fpr[reg].dbl64_r;
|
|
}
|
|
|
|
uint64_t fp_return_uint64(uint32_t reg) {
|
|
return ppc_state.fpr[reg].int64_r;
|
|
}
|
|
|
|
#define ppc_store_sfpresult_int(reg) \
|
|
ppc_state.fpr[(reg)].int64_r = ppc_result64_d;
|
|
|
|
#define ppc_store_sfpresult_flt(reg) \
|
|
ppc_state.fpr[(reg)].dbl64_r = ppc_dblresult64_d;
|
|
|
|
#define ppc_store_dfpresult_int(reg) \
|
|
ppc_state.fpr[(reg)].int64_r = ppc_result64_d;
|
|
|
|
#define ppc_store_dfpresult_flt(reg) \
|
|
ppc_state.fpr[(reg)].dbl64_r = ppc_dblresult64_d;
|
|
|
|
#define ppc_grab_regsfpdb() \
|
|
int reg_d = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_b = (ppc_cur_instruction >> 11) & 31;
|
|
|
|
#define ppc_grab_regsfpdiab() \
|
|
int reg_d = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
int reg_b = (ppc_cur_instruction >> 11) & 31; \
|
|
uint32_t val_reg_a = ppc_state.gpr[reg_a]; \
|
|
uint32_t val_reg_b = ppc_state.gpr[reg_b];
|
|
|
|
#define ppc_grab_regsfpdia() \
|
|
int reg_d = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
uint32_t val_reg_a = ppc_state.gpr[reg_a];
|
|
|
|
#define ppc_grab_regsfpsia() \
|
|
int reg_s = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
uint32_t val_reg_a = ppc_state.gpr[reg_a];
|
|
|
|
#define ppc_grab_regsfpsiab() \
|
|
int reg_s = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
int reg_b = (ppc_cur_instruction >> 11) & 31; \
|
|
uint32_t val_reg_a = ppc_state.gpr[reg_a]; \
|
|
uint32_t val_reg_b = ppc_state.gpr[reg_b];
|
|
|
|
#define ppc_grab_regsfpsab() \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
int reg_b = (ppc_cur_instruction >> 11) & 31; \
|
|
int crf_d = (ppc_cur_instruction >> 21) & 0x1C; \
|
|
double db_test_a = GET_FPR(reg_a); \
|
|
double db_test_b = GET_FPR(reg_b);
|
|
|
|
#define ppc_grab_regsfpdab() \
|
|
int reg_d = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
int reg_b = (ppc_cur_instruction >> 11) & 31; \
|
|
double val_reg_a = GET_FPR(reg_a); \
|
|
double val_reg_b = GET_FPR(reg_b);
|
|
|
|
#define ppc_grab_regsfpdac() \
|
|
int reg_d = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
int reg_c = (ppc_cur_instruction >> 6) & 31; \
|
|
double val_reg_a = GET_FPR(reg_a); \
|
|
double val_reg_c = GET_FPR(reg_c);
|
|
|
|
#define ppc_grab_regsfpdabc() \
|
|
int reg_d = (ppc_cur_instruction >> 21) & 31; \
|
|
int reg_a = (ppc_cur_instruction >> 16) & 31; \
|
|
int reg_b = (ppc_cur_instruction >> 11) & 31; \
|
|
int reg_c = (ppc_cur_instruction >> 6) & 31; \
|
|
double val_reg_a = GET_FPR(reg_a); \
|
|
double val_reg_b = GET_FPR(reg_b); \
|
|
double val_reg_c = GET_FPR(reg_c);
|
|
|
|
inline 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);
|
|
}
|
|
|
|
int64_t round_to_nearest(double f) {
|
|
if (f >= 0.0) {
|
|
return static_cast<int32_t>(static_cast<int64_t> (std::ceil(f)));
|
|
} else {
|
|
return static_cast<int32_t>(static_cast<int64_t> (std::floor(f)));
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int64_t round_to_zero(double f) {
|
|
return static_cast<int32_t>(std::trunc(f));
|
|
}
|
|
|
|
int64_t round_to_pos_inf(double f) {
|
|
return static_cast<int32_t>(std::ceil(f));
|
|
}
|
|
|
|
int64_t round_to_neg_inf(double f) {
|
|
return static_cast<int32_t>(std::floor(f));
|
|
}
|
|
|
|
void update_fex() {
|
|
int fex_result = !!((ppc_state.fpscr & (ppc_state.fpscr << 22)) & 0x3E000000);
|
|
ppc_state.fpscr = (ppc_state.fpscr & ~0x40000000) | (fex_result << 30);
|
|
}
|
|
|
|
template <typename T, const FPOP fpop>
|
|
void ppc_confirm_inf_nan(int chosen_reg_1, int chosen_reg_2, bool rc_flag = false) {
|
|
T input_a = T(ppc_state.fpr[chosen_reg_1].dbl64_r);
|
|
T input_b = T(ppc_state.fpr[chosen_reg_2].dbl64_r);
|
|
|
|
ppc_state.fpscr &= 0x7fbfffff;
|
|
|
|
switch (fpop) {
|
|
case FPOP::DIV:
|
|
if (std::isinf(input_a) && std::isinf(input_b)) {
|
|
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXIDI);
|
|
} else if ((input_a == FP_ZERO) && (input_b == FP_ZERO)) {
|
|
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXZDZ);
|
|
}
|
|
update_fex();
|
|
break;
|
|
case FPOP::SUB:
|
|
if (std::isinf(input_a) && std::isinf(input_b)) {
|
|
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXISI);
|
|
}
|
|
if (std::isnan(input_a) && std::isnan(input_b)) {
|
|
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXISI);
|
|
}
|
|
update_fex();
|
|
break;
|
|
case FPOP::ADD:
|
|
if (std::isnan(input_a) && std::isnan(input_b)) {
|
|
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXISI);
|
|
}
|
|
update_fex();
|
|
break;
|
|
case FPOP::SQRT:
|
|
if (std::isnan(input_b) || (input_b == -1.0)) {
|
|
ppc_state.fpscr |= (FPSCR::FX | FPSCR::VXSQRT);
|
|
}
|
|
update_fex();
|
|
break;
|
|
case FPOP::MUL:
|
|
if (std::isnan(input_a) && std::isnan(input_b)) {
|
|
ppc_state.fpscr |= (FPSCR::FX);
|
|
}
|
|
|
|
update_fex();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void fpresult_update(double set_result) {
|
|
if (std::isnan(set_result)) {
|
|
ppc_state.fpscr |= FPCC_FUNAN | FPCC_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::isinf(set_result))
|
|
ppc_state.fpscr |= FPCC_FUNAN;
|
|
}
|
|
}
|
|
|
|
// Floating Point Arithmetic
|
|
void dppc_interpreter::ppc_fadd() {
|
|
ppc_grab_regsfpdab();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
|
ppc_state.fpscr |= FPCC_FUNAN;
|
|
ppc_confirm_inf_nan<double, ADD>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = double(val_reg_a + val_reg_b);
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fsub() {
|
|
ppc_grab_regsfpdab();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
|
ppc_state.fpscr |= FPCC_FUNAN;
|
|
ppc_confirm_inf_nan<double, SUB>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = double(val_reg_a - val_reg_b);
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fdiv() {
|
|
ppc_grab_regsfpdab();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, DIV>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = val_reg_a / val_reg_b;
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fmul() {
|
|
ppc_grab_regsfpdac();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = val_reg_a * val_reg_c;
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fmadd() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, ADD>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = std::fma(val_reg_a, val_reg_c, val_reg_b);
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fmsub() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, SUB>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = (val_reg_a * val_reg_c);
|
|
ppc_dblresult64_d -= val_reg_b;
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fnmadd() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, ADD>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = std::fma(val_reg_a, val_reg_c, val_reg_b);
|
|
ppc_dblresult64_d = -(ppc_dblresult64_d);
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fnmsub() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, FNMSUB>(reg_a, reg_b, reg_c);
|
|
}
|
|
|
|
ppc_dblresult64_d = (val_reg_a * val_reg_c);
|
|
ppc_dblresult64_d -= val_reg_b;
|
|
ppc_dblresult64_d = -(ppc_dblresult64_d);
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fadds() {
|
|
ppc_grab_regsfpdab();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<float, SUB>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
float ppc_fltresult32_d = val_reg_a + val_reg_b;
|
|
ppc_dblresult64_d = (double)ppc_fltresult32_d;
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fsubs() {
|
|
ppc_grab_regsfpdab();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<float, SUB>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = (float)(val_reg_a - val_reg_b);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fdivs() {
|
|
ppc_grab_regsfpdab();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<float, DIV>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = (float)(val_reg_a / val_reg_b);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fmuls() {
|
|
ppc_grab_regsfpdac();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<float, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = (float)(val_reg_a * val_reg_c);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fmadds() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, ADD>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
ppc_dblresult64_d = static_cast<double>(
|
|
std::fma((float)val_reg_a, (float)val_reg_c, (float)val_reg_b));
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fmsubs() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, ADD>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
float intermediate = float(val_reg_a * val_reg_c);
|
|
intermediate -= (float)val_reg_b;
|
|
ppc_dblresult64_d = static_cast<double>(intermediate);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fnmadds() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, ADD>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
float intermediate = (float)val_reg_a * (float)val_reg_c;
|
|
intermediate += (float)val_reg_b;
|
|
intermediate = -intermediate;
|
|
ppc_dblresult64_d = static_cast<double>(intermediate);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fnmsubs() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
|
|
if (std::isnan(val_reg_a) || std::isnan(val_reg_c)) {
|
|
ppc_confirm_inf_nan<double, MUL>(reg_a, reg_c, rc_flag);
|
|
}
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_confirm_inf_nan<double, SUB>(reg_a, reg_b, rc_flag);
|
|
}
|
|
|
|
float intermediate = (float)val_reg_a * (float)val_reg_c;
|
|
intermediate -= (float)val_reg_b;
|
|
intermediate = -intermediate;
|
|
ppc_dblresult64_d = static_cast<double>(intermediate);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
fpresult_update(ppc_dblresult64_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fabs() {
|
|
ppc_grab_regsfpdb();
|
|
|
|
ppc_dblresult64_d = abs(GET_FPR(reg_b));
|
|
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fnabs() {
|
|
ppc_grab_regsfpdb();
|
|
|
|
ppc_dblresult64_d = abs(GET_FPR(reg_b));
|
|
ppc_dblresult64_d = -ppc_dblresult64_d;
|
|
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fneg() {
|
|
ppc_grab_regsfpdb();
|
|
|
|
ppc_dblresult64_d = -(GET_FPR(reg_b));
|
|
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fsel() {
|
|
ppc_grab_regsfpdabc();
|
|
|
|
ppc_dblresult64_d = (val_reg_a >= -0.0) ? val_reg_c : val_reg_b;
|
|
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fsqrt() {
|
|
ppc_grab_regsfpdb();
|
|
double testd2 = (double)(GET_FPR(reg_b));
|
|
ppc_dblresult64_d = std::sqrt(testd2);
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
ppc_confirm_inf_nan<double, SQRT>(0, reg_b, rc_flag);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fsqrts() {
|
|
ppc_grab_regsfpdb();
|
|
double testd2 = (double)(GET_FPR(reg_b));
|
|
ppc_dblresult64_d = (float)std::sqrt(testd2);
|
|
ppc_store_sfpresult_flt(reg_d);
|
|
ppc_confirm_inf_nan<float, SQRT>(0, reg_b, rc_flag);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_frsqrte() {
|
|
ppc_grab_regsfpdb();
|
|
double testd2 = (double)(GET_FPR(reg_b));
|
|
ppc_dblresult64_d = 1.0 / sqrt(testd2);
|
|
ppc_confirm_inf_nan<float, SQRT>(0, reg_b, rc_flag);
|
|
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_frsp() {
|
|
ppc_grab_regsfpdb();
|
|
ppc_dblresult64_d = (float)(GET_FPR(reg_b));
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fres() {
|
|
ppc_grab_regsfpdb();
|
|
double start_num = GET_FPR(reg_b);
|
|
float testf2 = (float)start_num;
|
|
testf2 = 1 / testf2;
|
|
ppc_dblresult64_d = (double)testf2;
|
|
ppc_store_dfpresult_flt(reg_d);
|
|
|
|
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 (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fctiw() {
|
|
ppc_grab_regsfpdb();
|
|
double val_reg_b = GET_FPR(reg_b);
|
|
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
|
ppc_state.fpscr |= FPSCR::VXSNAN | FPSCR::VXCVI;
|
|
}
|
|
else if (val_reg_b > static_cast<double>(0x7fffffff)) {
|
|
ppc_state.fpr[reg_d].int64_r = 0x7fffffff;
|
|
ppc_state.fpscr |= FPSCR::VXCVI;
|
|
}
|
|
else if (val_reg_b < -static_cast<double>(0x80000000)) {
|
|
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
|
ppc_state.fpscr |= FPSCR::VXCVI;
|
|
}
|
|
else {
|
|
switch (ppc_state.fpscr & 0x3) {
|
|
case 0:
|
|
ppc_result64_d = round_to_nearest(val_reg_b);
|
|
break;
|
|
case 1:
|
|
ppc_result64_d = round_to_zero(val_reg_b);
|
|
break;
|
|
case 2:
|
|
ppc_result64_d = round_to_pos_inf(val_reg_b);
|
|
break;
|
|
case 3:
|
|
ppc_result64_d = round_to_neg_inf(val_reg_b);
|
|
break;
|
|
}
|
|
|
|
ppc_store_dfpresult_int(reg_d);
|
|
|
|
}
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fctiwz() {
|
|
ppc_grab_regsfpdb();
|
|
double val_reg_b = GET_FPR(reg_b);
|
|
|
|
if (std::isnan(val_reg_b)) {
|
|
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
|
ppc_state.fpscr |= FPSCR::VXSNAN | FPSCR::VXCVI;
|
|
}
|
|
else if (val_reg_b > static_cast<double>(0x7fffffff)) {
|
|
ppc_state.fpr[reg_d].int64_r = 0x7fffffff;
|
|
ppc_state.fpscr |= FPSCR::VXCVI;
|
|
}
|
|
else if (val_reg_b < -static_cast<double>(0x80000000)) {
|
|
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
|
ppc_state.fpscr |= FPSCR::VXCVI;
|
|
}
|
|
else {
|
|
ppc_result64_d = round_to_zero(val_reg_b);
|
|
|
|
ppc_store_dfpresult_int(reg_d);
|
|
}
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
// Floating Point Store and Load
|
|
|
|
void dppc_interpreter::ppc_lfs() {
|
|
ppc_grab_regsfpdia();
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
|
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
|
ppc_store_sfpresult_int(reg_d);
|
|
}
|
|
|
|
|
|
void dppc_interpreter::ppc_lfsu() {
|
|
ppc_grab_regsfpdia();
|
|
if (reg_a) {
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
|
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
|
ppc_store_sfpresult_int(reg_d);
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_lfsx() {
|
|
ppc_grab_regsfpdiab();
|
|
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
|
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
|
ppc_store_sfpresult_int(reg_d);
|
|
}
|
|
|
|
void dppc_interpreter::ppc_lfsux() {
|
|
ppc_grab_regsfpdiab();
|
|
if (reg_a) {
|
|
ppc_effective_address = val_reg_a + val_reg_b;
|
|
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
|
ppc_store_sfpresult_int(reg_d);
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_lfd() {
|
|
ppc_grab_regsfpdia();
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
|
ppc_result64_d = mmu_read_vmem<uint64_t>(ppc_effective_address);
|
|
ppc_store_dfpresult_int(reg_d);
|
|
}
|
|
|
|
void dppc_interpreter::ppc_lfdu() {
|
|
ppc_grab_regsfpdia();
|
|
if (reg_a != 0) {
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += val_reg_a;
|
|
ppc_result64_d = mmu_read_vmem<uint64_t>(ppc_effective_address);
|
|
ppc_store_dfpresult_int(reg_d);
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_lfdx() {
|
|
ppc_grab_regsfpdiab();
|
|
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
|
ppc_result64_d = mmu_read_vmem<uint64_t>(ppc_effective_address);
|
|
ppc_store_dfpresult_int(reg_d);
|
|
}
|
|
|
|
void dppc_interpreter::ppc_lfdux() {
|
|
ppc_grab_regsfpdiab();
|
|
if (reg_a) {
|
|
ppc_effective_address = val_reg_a + val_reg_b;
|
|
ppc_result64_d = mmu_read_vmem<uint64_t>(ppc_effective_address);
|
|
ppc_store_dfpresult_int(reg_d);
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfs() {
|
|
ppc_grab_regsfpsia();
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
|
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfsu() {
|
|
ppc_grab_regsfpsia();
|
|
if (reg_a != 0) {
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += val_reg_a;
|
|
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfsx() {
|
|
ppc_grab_regsfpsiab();
|
|
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
|
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfsux() {
|
|
ppc_grab_regsfpsiab();
|
|
if (reg_a) {
|
|
ppc_effective_address = val_reg_a + val_reg_b;
|
|
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfd() {
|
|
ppc_grab_regsfpsia();
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
|
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfdu() {
|
|
ppc_grab_regsfpsia();
|
|
if (reg_a != 0) {
|
|
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
|
ppc_effective_address += val_reg_a;
|
|
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfdx() {
|
|
ppc_grab_regsfpsiab();
|
|
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
|
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfdux() {
|
|
ppc_grab_regsfpsiab();
|
|
if (reg_a != 0) {
|
|
ppc_effective_address = val_reg_a + val_reg_b;
|
|
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
|
ppc_state.gpr[reg_a] = ppc_effective_address;
|
|
} else {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_stfiwx() {
|
|
ppc_grab_regsfpsiab();
|
|
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
|
mmu_write_vmem<uint32_t>(ppc_effective_address, (uint32_t)(ppc_state.fpr[reg_s].int64_r));
|
|
}
|
|
|
|
// Floating Point Register Transfer
|
|
|
|
void dppc_interpreter::ppc_fmr() {
|
|
ppc_grab_regsfpdb();
|
|
ppc_state.fpr[reg_d].dbl64_r = ppc_state.fpr[reg_b].dbl64_r;
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_mffs() {
|
|
ppc_grab_regsda();
|
|
|
|
ppc_state.fpr[reg_d].int64_r = (uint64_t)ppc_state.fpscr;
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_mtfsf() {
|
|
uint32_t cr_mask = 0;
|
|
reg_b = (ppc_cur_instruction >> 11) & 31;
|
|
crm = (ppc_cur_instruction >> 17) & 255;
|
|
cr_mask = ((crm & 1) == 1) ? 0xF0000000 : 0x00000000;
|
|
cr_mask += (((crm >> 1) & 1) == 1) ? 0x0F000000 : 0x00000000;
|
|
cr_mask += (((crm >> 2) & 1) == 1) ? 0x00F00000 : 0x00000000;
|
|
cr_mask += (((crm >> 3) & 1) == 1) ? 0x000F0000 : 0x00000000;
|
|
cr_mask += (((crm >> 4) & 1) == 1) ? 0x0000F000 : 0x00000000;
|
|
cr_mask += (((crm >> 5) & 1) == 1) ? 0x00000F00 : 0x00000000;
|
|
cr_mask += (((crm >> 6) & 1) == 1) ? 0x000000F0 : 0x00000000;
|
|
cr_mask += (((crm >> 7) & 1) == 1) ? 0x0000000F : 0x00000000;
|
|
uint32_t quickfprval = (uint32_t)(ppc_state.fpr[reg_b].int64_r & 0xFFFFFFFF);
|
|
ppc_state.fpscr = (ppc_state.fpscr & ~cr_mask) | (quickfprval & cr_mask);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_mtfsfi() {
|
|
ppc_result_b = (ppc_cur_instruction >> 11) & 15;
|
|
crf_d = (ppc_cur_instruction >> 23) & 7;
|
|
crf_d = crf_d << 2;
|
|
ppc_state.fpscr = (ppc_state.cr & ~(0xF0000000UL >> crf_d)) |
|
|
((ppc_state.spr[SPR::XER] & 0xF0000000UL) >> crf_d);
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_mtfsb0() {
|
|
int crf_d = (ppc_cur_instruction >> 21) & 0x1F;
|
|
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitely cleared
|
|
ppc_state.fpscr &= ~(0x80000000UL >> crf_d);
|
|
}
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_mtfsb1() {
|
|
int crf_d = (ppc_cur_instruction >> 21) & 0x1F;
|
|
if (!crf_d || (crf_d > 2)) { // FEX and VX can't be explicitely set
|
|
ppc_state.fpscr |= (0x80000000UL >> crf_d);
|
|
}
|
|
|
|
if (rc_flag)
|
|
ppc_update_cr1();
|
|
}
|
|
|
|
void dppc_interpreter::ppc_mcrfs() {
|
|
crf_d = (ppc_cur_instruction >> 23) & 7;
|
|
crf_d = crf_d << 2;
|
|
crf_s = (ppc_cur_instruction >> 18) & 7;
|
|
crf_s = crf_d << 2;
|
|
ppc_state.cr = ~(ppc_state.cr & ((15 << (28 - crf_d)))) +
|
|
(ppc_state.fpscr & (15 << (28 - crf_s)));
|
|
}
|
|
|
|
// Floating Point Comparisons
|
|
|
|
void dppc_interpreter::ppc_fcmpo() {
|
|
ppc_grab_regsfpsab();
|
|
|
|
uint32_t cmp_c = 0;
|
|
|
|
if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
|
|
cmp_c |= (1 << CRx_bit::CR_SO);
|
|
}
|
|
else if (db_test_a < db_test_b) {
|
|
cmp_c |= (1 << CRx_bit::CR_LT);
|
|
}
|
|
else if (db_test_a > db_test_b) {
|
|
cmp_c |= (1 << CRx_bit::CR_GT);
|
|
}
|
|
else {
|
|
cmp_c |= (1 << CRx_bit::CR_EQ);
|
|
}
|
|
|
|
ppc_state.fpscr = (ppc_state.fpscr & 0xFFFF0FFF) | (cmp_c >> 16); // FL,FG,FE,FU
|
|
ppc_state.cr = ((ppc_state.cr & ~(0xF0000000 >> crf_d)) | ((cmp_c) >> crf_d));
|
|
|
|
//if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
|
|
// ppc_state.fpscr |= FPSCR::FX | FPSCR::VX | FPSCR::VXSNAN;
|
|
//}
|
|
}
|
|
|
|
void dppc_interpreter::ppc_fcmpu() {
|
|
ppc_grab_regsfpsab();
|
|
|
|
uint32_t cmp_c = 0;
|
|
|
|
if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
|
|
cmp_c |= (1 << CRx_bit::CR_SO);
|
|
}
|
|
else if (db_test_a < db_test_b) {
|
|
cmp_c |= (1 << CRx_bit::CR_LT);
|
|
}
|
|
else if (db_test_a > db_test_b) {
|
|
cmp_c |= (1 << CRx_bit::CR_GT);
|
|
}
|
|
else {
|
|
cmp_c |= (1 << CRx_bit::CR_EQ);
|
|
}
|
|
|
|
ppc_state.fpscr = (ppc_state.fpscr & 0xFFFF0FFF) | (cmp_c >> 16); // FL,FG,FE,FU
|
|
ppc_state.cr = ((ppc_state.cr & ~(0xF0000000 >> crf_d)) | ((cmp_c) >> crf_d));
|
|
|
|
//if (std::isnan(db_test_a) || std::isnan(db_test_b)) {
|
|
// ppc_state.fpscr |= FPSCR::VX | FPSCR::VXSNAN;
|
|
//}
|
|
}
|