Add rounding control for the host FPU.

This commit is contained in:
Maxim Poliakovski 2023-11-30 12:00:50 +01:00
parent b59c2be12d
commit 6abb07e61b
4 changed files with 65 additions and 30 deletions

View File

@ -402,7 +402,8 @@ extern void ppc_store_result_regd();
extern void ppc_store_result_rega();
void ppc_changecrf0(uint32_t set_result);
void ppc_fp_changecrf1();
void set_host_rounding_mode(uint8_t mode);
void update_fpscr(uint32_t new_fpscr);
/* Exception handlers. */
void ppc_exception_handler(Except_Type exception_type, uint32_t srr1_bits);

View File

@ -751,6 +751,17 @@ void initialize_ppc_opcode_tables() {
}
}
void ppc_fpu_init() {
// zero all FPRs as prescribed for MPC601
// For later PPC CPUs, GPR content is undefined
for (int i = 0; i < 32; i++) {
ppc_state.fpr[i].int64_r = 0;
}
ppc_state.fpscr = 0;
set_host_rounding_mode(0);
}
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
{
int i;
@ -783,12 +794,6 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
ppc_state.gpr[i] = 0;
}
/* zero all FPRs as prescribed for MPC601 */
/* For later PPC CPUs, GPR content is undefined */
for (i = 0; i < 32; i++) {
ppc_state.fpr[i].int64_r = 0;
}
/* zero all segment registers as prescribed for MPC601 */
/* For later PPC CPUs, SR content is undefined */
for (i = 0; i < 16; i++) {
@ -796,7 +801,8 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
}
ppc_state.cr = 0;
ppc_state.fpscr = 0;
ppc_fpu_init();
ppc_state.pc = 0;

View File

@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ppcemu.h"
#include "ppcmmu.h"
#include <stdlib.h>
#include <cfenv>
#include <cinttypes>
#include <cmath>
#include <cfloat>
@ -134,6 +135,30 @@ int64_t round_to_nearest(double 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));
}

View File

@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../ppcdisasm.h"
#include "../ppcemu.h"
#include <cfenv>
#include <cmath>
#include <fstream>
#include <iomanip>
@ -186,7 +187,7 @@ static void read_test_float_data() {
lineno++;
if (line.empty() || !line.rfind("#", 0))
continue; /* skip empty/comment lines */
continue; // skip empty/comment lines
istringstream lnstream(line);
@ -208,16 +209,15 @@ static void read_test_float_data() {
check_xer = 0;
check_cr = 0;
check_fpscr = 0;
//sfp_dest = 0.0;
//sfp_src1 = 0.0;
//sfp_src2 = 0.0;
//sfp_src3 = 0.0;
dfp_dest = 0.0;
dfp_src1 = 0.0;
dfp_src2 = 0.0;
dfp_src3 = 0.0;
dest_64 = 0;
// switch to default rounding
fesetround(FE_TONEAREST);
for (i = 2; i < tokens.size(); i++) {
if (tokens[i].rfind("frD=", 0) == 0) {
dest_64 = stoull(tokens[i].substr(4), NULL, 16);
@ -229,22 +229,6 @@ static void read_test_float_data() {
dfp_src3 = stod(tokens[i].substr(4), NULL);
} else if (tokens[i].rfind("round=", 0) == 0) {
rounding_mode = tokens[i].substr(6, 3);
ppc_state.fpscr = 0;
if (rounding_mode.compare("RTN") == 0) {
ppc_state.fpscr = 0x0;
} else if (rounding_mode.compare("RTZ") == 0) {
ppc_state.fpscr = 0x1;
} else if (rounding_mode.compare("RPI") == 0) {
ppc_state.fpscr = 0x2;
} else if (rounding_mode.compare("RNI") == 0) {
ppc_state.fpscr = 0x3;
} else if (rounding_mode.compare("VEN") == 0) {
ppc_state.fpscr = FPSCR::VE;
} else {
cout << "ILLEGAL ROUNDING METHOD: " << tokens[i] << " in line " << lineno
<< ". Exiting..." << endl;
exit(0);
}
} else if (tokens[i].rfind("FPSCR=", 0) == 0) {
check_fpscr = stoul(tokens[i].substr(6), NULL, 16);
} else if (tokens[i].rfind("CR=", 0) == 0) {
@ -256,6 +240,22 @@ static void read_test_float_data() {
}
}
if (rounding_mode.compare("RTN") == 0) {
update_fpscr(0);
} else if (rounding_mode.compare("RTZ") == 0) {
update_fpscr(1);
} else if (rounding_mode.compare("RPI") == 0) {
update_fpscr(2);
} else if (rounding_mode.compare("RNI") == 0) {
update_fpscr(3);
} else if (rounding_mode.compare("VEN") == 0) {
update_fpscr(FPSCR::VE);
} else {
cout << "ILLEGAL ROUNDING METHOD: " << tokens[i] << " in line " << lineno
<< ". Exiting..." << endl;
exit(0);
}
ppc_state.gpr[3] = src1;
ppc_state.gpr[4] = src2;
@ -263,7 +263,7 @@ static void read_test_float_data() {
ppc_state.fpr[4].dbl64_r = dfp_src2;
ppc_state.fpr[5].dbl64_r = dfp_src3;
ppc_state.cr = 0;
ppc_state.cr = 0;
ppc_cur_instruction = opcode;
@ -271,6 +271,9 @@ static void read_test_float_data() {
ntested++;
// switch to default rounding
fesetround(FE_TONEAREST);
if ((tokens[0].rfind("FCMP") && (ppc_state.fpr[3].int64_r != dest_64)) ||
(ppc_state.fpscr != check_fpscr) ||
(ppc_state.cr != check_cr)) {