From c599b07efc009477b84907faa57a185ecfc38998 Mon Sep 17 00:00:00 2001 From: Rosalie Wanders Date: Mon, 2 Oct 2023 18:53:33 +0200 Subject: [PATCH] more work:tm: --- .../src/device/r4300/cached_interp.c | 1 + .../mupen64plus-core/src/device/r4300/fpu.h | 225 +++++++++++++----- .../src/device/r4300/mips_instructions.def | 88 ++++++- .../src/device/r4300/pure_interp.c | 2 + 4 files changed, 253 insertions(+), 63 deletions(-) diff --git a/Source/3rdParty/mupen64plus-core/src/device/r4300/cached_interp.c b/Source/3rdParty/mupen64plus-core/src/device/r4300/cached_interp.c index b46d99ac..d84bb678 100644 --- a/Source/3rdParty/mupen64plus-core/src/device/r4300/cached_interp.c +++ b/Source/3rdParty/mupen64plus-core/src/device/r4300/cached_interp.c @@ -604,6 +604,7 @@ enum r4300_opcode r4300_decode(struct precomp_instr* inst, struct r4300_core* r4 idec_u53(iw, idec->u53[0], &inst->f.cf.fd); switch(dummy) { + case 0x10: inst->ops = cached_interp_CVT_S_S; return idec->opcode; case 0x11: inst->ops = cached_interp_CVT_S_D; return idec->opcode; case 0x14: inst->ops = cached_interp_CVT_S_W; return idec->opcode; case 0x15: inst->ops = cached_interp_CVT_S_L; return idec->opcode; diff --git a/Source/3rdParty/mupen64plus-core/src/device/r4300/fpu.h b/Source/3rdParty/mupen64plus-core/src/device/r4300/fpu.h index e96b1231..c94522bb 100644 --- a/Source/3rdParty/mupen64plus-core/src/device/r4300/fpu.h +++ b/Source/3rdParty/mupen64plus-core/src/device/r4300/fpu.h @@ -269,6 +269,52 @@ M64P_FPU_INLINE int fpu_check_input_double(uint32_t* fcr31, const double* value) return 0; } +M64P_FPU_INLINE int fpu_check_input_float_conv_int32(uint32_t* fcr31, const float* source) +{ + switch (fpclassify(*source)) + { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + (*fcr31) |= FCR31_CAUSE_UNIMPLOP_BIT; + return 1; + default: + break; + } + + if (*source >= 0x1p+31f || + *source < -0x1p+31f) + { + (*fcr31) |= FCR31_CAUSE_UNIMPLOP_BIT; + return 1; + } + + return 0; +} + +M64P_FPU_INLINE int fpu_check_input_double_conv_int32(uint32_t* fcr31, const double* source) +{ + switch (fpclassify(*source)) + { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + (*fcr31) |= FCR31_CAUSE_UNIMPLOP_BIT; + return 1; + default: + break; + } + + if (*source >= 0x1p+31 || + *source < -0x1p+31) + { + (*fcr31) |= FCR31_CAUSE_UNIMPLOP_BIT; + return 1; + } + + return 0; +} + M64P_FPU_INLINE void fpu_flush_output_float(uint32_t* fcr31, float* value) { switch (fegetround()) @@ -399,6 +445,16 @@ M64P_FPU_INLINE int fpu_check_input_double(uint32_t* fcr31, const double* value) return 0; } +M64P_FPU_INLINE int fpu_check_input_float_conv_int32(uint32_t* fcr31, const float* source) +{ + return 0; +} + +M64P_FPU_INLINE int fpu_check_input_double_conv_int32(uint32_t* fcr31, const double* source) +{ + return 0; +} + M64P_FPU_INLINE int fpu_check_output_float(uint32_t* fcr31, const float* value) { return 0; @@ -425,54 +481,81 @@ M64P_FPU_INLINE int cvt_s_w(uint32_t* fcr31, const int32_t* source, float* dest) *dest = value; return 0; } -M64P_FPU_INLINE void cvt_d_w(uint32_t* fcr31, const int32_t* source, double* dest) +M64P_FPU_INLINE int cvt_d_w(uint32_t* fcr31, const int32_t* source, double* dest) { + set_rounding(*fcr31); + fpu_reset_cause(fcr31); fpu_reset_exceptions(); - *dest = (double)*source; + double value = (double)*source; - fpu_check_exceptions(fcr31); - fpu_check_output_double(fcr31, dest); + if (fpu_check_exceptions(fcr31)) return 1; + if (fpu_check_output_double(fcr31, &value)) return 1; + + *dest = value; + return 0; } M64P_FPU_INLINE int cvt_s_l(uint32_t* fcr31, const int64_t* source, float* dest) { set_rounding(*fcr31); - fpu_reset_cause(fcr31); + + if (*source < (int64_t)0xff80000000000000ULL || + *source >= (int64_t)0x0080000000000000ULL) + { + (*fcr31) |= FCR31_CAUSE_UNIMPLOP_BIT; + return 1; + } + fpu_reset_exceptions(); float value = (float)*source; - if (fpu_check_exceptions(fcr31)) return 1; + if (fpu_check_exceptions(fcr31)) return 1; if (fpu_check_output_float(fcr31, &value)) return 1; *dest = value; return 0; } -M64P_FPU_INLINE void cvt_d_l(uint32_t* fcr31, const int64_t* source, double* dest) +M64P_FPU_INLINE int cvt_d_l(uint32_t* fcr31, const int64_t* source, double* dest) { set_rounding(*fcr31); - fpu_reset_cause(fcr31); + + if (*source < (int64_t)0xff80000000000000ULL || + *source >= (int64_t)0x0080000000000000ULL) + { + (*fcr31) |= FCR31_CAUSE_UNIMPLOP_BIT; + return 1; + } + fpu_reset_exceptions(); - *dest = (double)*source; + double value = (double)*source; - fpu_check_exceptions(fcr31); - fpu_check_output_double(fcr31, dest); + if (fpu_check_exceptions(fcr31)) return 1; + if (fpu_check_output_double(fcr31, &value)) return 1; + + *dest = value; + return 0; } -M64P_FPU_INLINE void cvt_d_s(uint32_t* fcr31, const float* source, double* dest) +M64P_FPU_INLINE int cvt_d_s(uint32_t* fcr31, const float* source, double* dest) { + set_rounding(*fcr31); fpu_reset_cause(fcr31); - fpu_check_input_float(fcr31, source); + + if (fpu_check_input_float(fcr31, source)) return 1; fpu_reset_exceptions(); - *dest = (double)*source; + double value = (double)*source; - fpu_check_exceptions(fcr31); - fpu_check_output_double(fcr31, dest); + if (fpu_check_exceptions(fcr31)) return 1; + if (fpu_check_output_double(fcr31, &value)) return 1; + + *dest = value; + return 0; } M64P_FPU_INLINE int cvt_s_d(uint32_t* fcr31, const double* source, float* dest) { @@ -511,40 +594,75 @@ M64P_FPU_INLINE void round_l_s(const float* source, int64_t* dest) *dest = (int64_t)roundf(*source); } } -M64P_FPU_INLINE void round_w_s(const float* source, int32_t* dest) +M64P_FPU_INLINE int round_w_s(uint32_t* fcr31, const float* source, int32_t* dest) { - float remainder = *source - floorf(*source); - if (remainder == 0.5) - { - if (*source < 0) - { - *dest = (int32_t)truncf(*source) % 2 != 0 ? (int32_t)floorf(*source) : (int32_t)ceilf(*source); - } - else - { - *dest = (int32_t)truncf(*source) % 2 != 0 ? (int32_t)ceilf(*source) : (int32_t)floorf(*source); - } - } - else + set_rounding(*fcr31); + fpu_reset_cause(fcr31); + + if (fpu_check_input_float_conv_int32(fcr31, source)) return 1; + + fpu_reset_exceptions(); + + int32_t value = (int32_t)roundf(*source); + + if (fpu_check_exceptions(fcr31)) return 1; + + if (*source != value) { - *dest = (int32_t)roundf(*source); + (*fcr31) |= FCR31_CAUSE_INEXACT_BIT; + if ((*fcr31) & FCR31_ENABLE_INEXACT_BIT) return 1; + (*fcr31) |= FCR31_FLAG_INEXACT_BIT; } + + *dest = value; + return 0; } M64P_FPU_INLINE void trunc_l_s(const float* source, int64_t* dest) { *dest = (int64_t)truncf(*source); } -M64P_FPU_INLINE void trunc_w_s(const float* source, int32_t* dest) +M64P_FPU_INLINE int trunc_w_s(uint32_t* fcr31, const float* source, int32_t* dest) { - *dest = (int32_t)truncf(*source); + set_rounding(*fcr31); + fpu_reset_cause(fcr31); + + if (fpu_check_input_float_conv_int32(fcr31, source)) return 1; + + fpu_reset_exceptions(); + + int32_t value = (int32_t)truncf(*source); + + if (fpu_check_exceptions(fcr31)) return 1; + + if (*source != value) + { + (*fcr31) |= FCR31_CAUSE_INEXACT_BIT; + if ((*fcr31) & FCR31_ENABLE_INEXACT_BIT) return 1; + (*fcr31) |= FCR31_FLAG_INEXACT_BIT; + } + + *dest = value; + return 0; } M64P_FPU_INLINE void ceil_l_s(const float* source, int64_t* dest) { *dest = (int64_t)ceilf(*source); } -M64P_FPU_INLINE void ceil_w_s(const float* source, int32_t* dest) +M64P_FPU_INLINE int ceil_w_s(uint32_t* fcr31, const float* source, int32_t* dest) { - *dest = (int32_t)ceilf(*source); + set_rounding(*fcr31); + fpu_reset_cause(fcr31); + + if (fpu_check_input_float_conv_int32(fcr31, source)) return 1; + + fpu_reset_exceptions(); + + int32_t value = (int32_t)ceilf(*source); + + if (fpu_check_exceptions(fcr31)) return 1; + + *dest = value; + return 0; } M64P_FPU_INLINE void floor_l_s(const float* source, int64_t* dest) { @@ -619,42 +737,39 @@ M64P_FPU_INLINE void floor_w_d(const double* source, int32_t* dest) *dest = (int32_t)floor(*source); } -M64P_FPU_INLINE void cvt_w_s(uint32_t* fcr31, const float* source, int32_t* dest) +M64P_FPU_INLINE int cvt_w_s(uint32_t* fcr31, const float* source, int32_t* dest) { + set_rounding(*fcr31); fpu_reset_cause(fcr31); - fpu_check_input_float(fcr31, source); + if (fpu_check_input_float_conv_int32(fcr31, source)) return 1; fpu_reset_exceptions(); - switch(*fcr31 & 3) - { - case 0: round_w_s(source, dest); return; - case 1: trunc_w_s(source, dest); return; - case 2: ceil_w_s (source, dest); return; - case 3: floor_w_s(source, dest); return; - } + int32_t value = rintf(*source); - fpu_check_exceptions(fcr31); + if (fpu_check_exceptions(fcr31)) return 1; + + *dest = value; + return 0; } -M64P_FPU_INLINE void cvt_w_d(uint32_t* fcr31, const double* source, int32_t* dest) +M64P_FPU_INLINE int cvt_w_d(uint32_t* fcr31, const double* source, int32_t* dest) { + set_rounding(*fcr31); fpu_reset_cause(fcr31); - fpu_check_input_double(fcr31, source); + if (fpu_check_input_double_conv_int32(fcr31, source)) return 1; fpu_reset_exceptions(); - switch(*fcr31 & 3) - { - case 0: round_w_d(source, dest); return; - case 1: trunc_w_d(source, dest); return; - case 2: ceil_w_d (source, dest); return; - case 3: floor_w_d(source, dest); return; - } + int32_t value = rint(*source); - fpu_check_exceptions(fcr31); + if (fpu_check_exceptions(fcr31)) return 1; + + *dest = value; + return 0; } + M64P_FPU_INLINE void cvt_l_s(uint32_t* fcr31, const float* source, int64_t* dest) { fpu_reset_cause(fcr31); diff --git a/Source/3rdParty/mupen64plus-core/src/device/r4300/mips_instructions.def b/Source/3rdParty/mupen64plus-core/src/device/r4300/mips_instructions.def index 8c749b80..a6156fea 100644 --- a/Source/3rdParty/mupen64plus-core/src/device/r4300/mips_instructions.def +++ b/Source/3rdParty/mupen64plus-core/src/device/r4300/mips_instructions.def @@ -1851,7 +1851,13 @@ DECLARE_INSTRUCTION(TRUNC_W_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - trunc_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + if (trunc_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } @@ -1883,7 +1889,13 @@ DECLARE_INSTRUCTION(ROUND_W_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - round_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + if (round_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } @@ -1915,7 +1927,13 @@ DECLARE_INSTRUCTION(CEIL_W_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - ceil_w_s((r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + if (ceil_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } @@ -1975,6 +1993,18 @@ DECLARE_INSTRUCTION(FLOOR_L_D) ADD_TO_PC(1); } +DECLARE_INSTRUCTION(CVT_S_S) +{ + DECLARE_R4300 + if (check_cop1_unusable(r4300)) { return; } + + *r4300_cp1_fcr31(&r4300->cp1) |= FCR31_CAUSE_UNIMPLOP_BIT; + + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); +} + DECLARE_INSTRUCTION(CVT_S_D) { DECLARE_R4300 @@ -2021,15 +2051,39 @@ DECLARE_INSTRUCTION(CVT_D_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_d_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + if (cvt_d_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } +DECLARE_INSTRUCTION(CVT_D_D) +{ + DECLARE_R4300 + if (check_cop1_unusable(r4300)) { return; } + + *r4300_cp1_fcr31(&r4300->cp1) |= FCR31_CAUSE_UNIMPLOP_BIT; + + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); +} + DECLARE_INSTRUCTION(CVT_D_W) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_d_w(r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + if (cvt_d_w(r4300_cp1_fcr31(&r4300->cp1), (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } @@ -2037,7 +2091,13 @@ DECLARE_INSTRUCTION(CVT_D_L) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_d_l(r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd]); + if (cvt_d_l(r4300_cp1_fcr31(&r4300->cp1), (int64_t*) (r4300_cp1_regs_double(&r4300->cp1))[cffs], (r4300_cp1_regs_double(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } @@ -2045,7 +2105,13 @@ DECLARE_INSTRUCTION(CVT_W_S) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + if (cvt_w_s(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_simple(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } @@ -2053,7 +2119,13 @@ DECLARE_INSTRUCTION(CVT_W_D) { DECLARE_R4300 if (check_cop1_unusable(r4300)) { return; } - cvt_w_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd]); + if (cvt_w_d(r4300_cp1_fcr31(&r4300->cp1), (r4300_cp1_regs_double(&r4300->cp1))[cffs], (int32_t*) (r4300_cp1_regs_simple(&r4300->cp1))[cffd])) + { + uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0); + cp0_regs[CP0_CAUSE_REG] = CP0_CAUSE_EXCCODE_FPE; + exception_general(r4300); + return; + } ADD_TO_PC(1); } diff --git a/Source/3rdParty/mupen64plus-core/src/device/r4300/pure_interp.c b/Source/3rdParty/mupen64plus-core/src/device/r4300/pure_interp.c index 1ed08f53..e89310cc 100644 --- a/Source/3rdParty/mupen64plus-core/src/device/r4300/pure_interp.c +++ b/Source/3rdParty/mupen64plus-core/src/device/r4300/pure_interp.c @@ -525,6 +525,7 @@ void InterpretOpcode(struct r4300_core* r4300) case 13: TRUNC_W_S(r4300, op); break; case 14: CEIL_W_S(r4300, op); break; case 15: FLOOR_W_S(r4300, op); break; + case 32: CVT_S_S(r4300, op); break; case 33: CVT_D_S(r4300, op); break; case 36: CVT_W_S(r4300, op); break; case 37: CVT_L_S(r4300, op); break; @@ -569,6 +570,7 @@ void InterpretOpcode(struct r4300_core* r4300) case 14: CEIL_W_D(r4300, op); break; case 15: FLOOR_W_D(r4300, op); break; case 32: CVT_S_D(r4300, op); break; + case 33: CVT_D_D(r4300, op); break; case 36: CVT_W_D(r4300, op); break; case 37: CVT_L_D(r4300, op); break; case 48: C_F_D(r4300, op); break;