mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2025-02-16 17:35:39 +00:00
Implement correct "round to nearest" mode
On PowerPC round to nearest ties to even, not away from zero Also implement correct behavior for ppc_fresx Fixes "tips to win" sequence in Daytona 2 BOTE
This commit is contained in:
parent
d32641030d
commit
392900fee2
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
// it really seems like this should be elsewhere - like maybe the floating point checks can hang out someplace else
|
// it really seems like this should be elsewhere - like maybe the floating point checks can hang out someplace else
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <cfenv>
|
||||||
|
|
||||||
static void ppc_unimplemented(UINT32 op)
|
static void ppc_unimplemented(UINT32 op)
|
||||||
{
|
{
|
||||||
|
@ -1661,8 +1662,23 @@ inline int sign_double(FPR x)
|
||||||
return ((x.id & DOUBLE_SIGN) != 0);
|
return ((x.id & DOUBLE_SIGN) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void set_rounding_mode(void)
|
||||||
|
{
|
||||||
|
// may require compiler option to work correctly (-frounding-math for GCC, /fp:strict for Visual Studio)
|
||||||
|
// unknown if any games actually change this
|
||||||
|
switch (ppc.fpscr & 3)
|
||||||
|
{
|
||||||
|
case 0: fesetround(FE_TONEAREST); break;
|
||||||
|
case 1: fesetround(FE_TOWARDZERO); break;
|
||||||
|
case 2: fesetround(FE_UPWARD); break;
|
||||||
|
case 3: fesetround(FE_DOWNWARD); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
inline INT64 smround_to_nearest(FPR f)
|
inline INT64 smround_to_nearest(FPR f)
|
||||||
{
|
{
|
||||||
|
// This method is incorrect; it ties away from zero, while PowerPC ties to even
|
||||||
if (f.fd >= 0)
|
if (f.fd >= 0)
|
||||||
{
|
{
|
||||||
return (INT64)(f.fd + 0.5);
|
return (INT64)(f.fd + 0.5);
|
||||||
|
@ -1689,6 +1705,7 @@ inline INT64 round_toward_negative_infinity(FPR f)
|
||||||
double r = floor(f.fd);
|
double r = floor(f.fd);
|
||||||
return (INT64)(r);
|
return (INT64)(r);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#define SET_VXSNAN(a, b) if (is_snan_double(a) || is_snan_double(b)) ppc.fpscr |= 0x80000000
|
#define SET_VXSNAN(a, b) if (is_snan_double(a) || is_snan_double(b)) ppc.fpscr |= 0x80000000
|
||||||
#define SET_VXSNAN_1(c) if (is_snan_double(c)) ppc.fpscr |= 0x80000000
|
#define SET_VXSNAN_1(c) if (is_snan_double(c)) ppc.fpscr |= 0x80000000
|
||||||
|
@ -2174,10 +2191,12 @@ static void ppc_fctiwx(UINT32 op)
|
||||||
|
|
||||||
switch(ppc.fpscr & 3)
|
switch(ppc.fpscr & 3)
|
||||||
{
|
{
|
||||||
case 0: r = (INT64)smround_to_nearest(FPR(b)); break;
|
// nearbyint() uses rounding mode set by fesetround()
|
||||||
case 1: r = (INT64)smround_toward_zero(FPR(b)); break;
|
// this should be FE_TONEAREST (ties to even) if the case is 0
|
||||||
case 2: r = (INT64)round_toward_positive_infinity(FPR(b)); break;
|
case 0: r = (INT64)nearbyint(FPR(b).fd); break;
|
||||||
case 3: r = (INT64)round_toward_negative_infinity(FPR(b)); break;
|
case 1: r = (INT64)trunc(FPR(b).fd); break;
|
||||||
|
case 2: r = (INT64)ceil(FPR(b).fd); break;
|
||||||
|
case 3: r = (INT64)floor(FPR(b).fd); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r > (INT64)((INT32)0x7FFFFFFF))
|
if(r > (INT64)((INT32)0x7FFFFFFF))
|
||||||
|
@ -2219,7 +2238,7 @@ static void ppc_fctiwzx(UINT32 op)
|
||||||
CHECK_FPU_AVAILABLE();
|
CHECK_FPU_AVAILABLE();
|
||||||
|
|
||||||
SET_VXSNAN_1(FPR(b));
|
SET_VXSNAN_1(FPR(b));
|
||||||
r = smround_toward_zero(FPR(b));
|
r = (INT64)trunc(FPR(b).fd);
|
||||||
|
|
||||||
if(r > (INT64)((INT32)0x7fffffff))
|
if(r > (INT64)((INT32)0x7fffffff))
|
||||||
{
|
{
|
||||||
|
@ -2398,6 +2417,8 @@ static void ppc_mtfsb0x(UINT32 op)
|
||||||
if (crbD != 1 && crbD != 2) // these bits cannot be explicitly cleared
|
if (crbD != 1 && crbD != 2) // these bits cannot be explicitly cleared
|
||||||
ppc.fpscr &= ~(1 << (31 - crbD));
|
ppc.fpscr &= ~(1 << (31 - crbD));
|
||||||
|
|
||||||
|
set_rounding_mode();
|
||||||
|
|
||||||
if( RCBIT ) {
|
if( RCBIT ) {
|
||||||
SET_CR1();
|
SET_CR1();
|
||||||
}
|
}
|
||||||
|
@ -2412,6 +2433,8 @@ static void ppc_mtfsb1x(UINT32 op)
|
||||||
if (crbD != 1 && crbD != 2) // these bits cannot be explicitly cleared
|
if (crbD != 1 && crbD != 2) // these bits cannot be explicitly cleared
|
||||||
ppc.fpscr |= (1 << (31 - crbD));
|
ppc.fpscr |= (1 << (31 - crbD));
|
||||||
|
|
||||||
|
set_rounding_mode();
|
||||||
|
|
||||||
if( RCBIT ) {
|
if( RCBIT ) {
|
||||||
SET_CR1();
|
SET_CR1();
|
||||||
}
|
}
|
||||||
|
@ -2425,6 +2448,8 @@ static void ppc_mtfsfx(UINT32 op)
|
||||||
ppc.fpscr &= (~f) | ~(FPSCR_FEX | FPSCR_VX);
|
ppc.fpscr &= (~f) | ~(FPSCR_FEX | FPSCR_VX);
|
||||||
ppc.fpscr |= (UINT32)(FPR(b).id) & ~(FPSCR_FEX | FPSCR_VX);
|
ppc.fpscr |= (UINT32)(FPR(b).id) & ~(FPSCR_FEX | FPSCR_VX);
|
||||||
|
|
||||||
|
set_rounding_mode();
|
||||||
|
|
||||||
// FEX, VX
|
// FEX, VX
|
||||||
|
|
||||||
if( RCBIT ) {
|
if( RCBIT ) {
|
||||||
|
@ -2459,6 +2484,8 @@ static void ppc_mtfsfix(UINT32 op)
|
||||||
ppc.fpscr &= ~(0xf << crfd); // clear field
|
ppc.fpscr &= ~(0xf << crfd); // clear field
|
||||||
ppc.fpscr |= (imm << crfd); // insert new data
|
ppc.fpscr |= (imm << crfd); // insert new data
|
||||||
|
|
||||||
|
set_rounding_mode();
|
||||||
|
|
||||||
if( RCBIT ) {
|
if( RCBIT ) {
|
||||||
SET_CR1();
|
SET_CR1();
|
||||||
}
|
}
|
||||||
|
@ -2541,7 +2568,8 @@ static void ppc_fresx(UINT32 op)
|
||||||
|
|
||||||
SET_VXSNAN_1(FPR(b));
|
SET_VXSNAN_1(FPR(b));
|
||||||
|
|
||||||
FPR(t).fd = 1.0 / FPR(b).fd; /* ??? */
|
// On the 603 fres behaves the same as fdivs RT, 1.0, RB
|
||||||
|
FPR(t).fd = (float)(1.0 / FPR(b).fd);
|
||||||
|
|
||||||
set_fprf(FPR(t));
|
set_fprf(FPR(t));
|
||||||
if( RCBIT ) {
|
if( RCBIT ) {
|
||||||
|
|
Loading…
Reference in a new issue