mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-12-27 00:29:18 +00:00
991 lines
30 KiB
C++
991 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();
|
|
|
|
ppc_state.fpscr &= 0xFFFF0FFF;
|
|
cmp_c = 0;
|
|
crf_d = 4;
|
|
|
|
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 = (cmp_c >> 16);
|
|
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();
|
|
|
|
cmp_c = 0;
|
|
crf_d = 4;
|
|
|
|
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 = (cmp_c >> 16);
|
|
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;
|
|
//}
|
|
}
|