Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion ps2xRuntime/include/ps2_runtime_macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,17 @@ inline __m128i ps2_u64_to_epi64_pair(uint64_t value)
#define FPU_FLOOR_W_S(a) ((int32_t)floorf((float)(a)))
#define FPU_CVT_S_W(a) ((float)(int32_t)(a))
#define FPU_CVT_S_L(a) ((float)(int64_t)(a))
#define FPU_CVT_W_S(a) ((int32_t)nearbyintf((float)(a)))
// CVT.W.S: truncate toward zero regardless of the rounding mode.
// Clamp to either INT32_MIN/INT32_MAX on over/underflow.
static inline int32_t ps2_fpu_cvt_w_s(float a)
{
if (a >= 2147483648.0f)
return INT32_MAX;
if (a <= -2147483648.0f)
return INT32_MIN;
return (int32_t)a;
}
#define FPU_CVT_W_S(a) (ps2_fpu_cvt_w_s((float)(a)))
#define FPU_CVT_L_S(a) ((int64_t)(float)(a))
#define FPU_C_F_S(a, b) (0)
#define FPU_C_UN_S(a, b) (isnan((float)(a)) || isnan((float)(b)))
Expand Down
1 change: 1 addition & 0 deletions ps2xTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ target_include_directories(ps2_test_function_table PRIVATE
add_library(ps2_test_lib STATIC
src/code_generator_tests.cpp
src/r5900_decoder_tests.cpp
src/ps2_fpu_macros_tests.cpp
src/elf_analyzer_tests.cpp
src/pad_input_tests.cpp
src/ps2_runtime_io_tests.cpp
Expand Down
2 changes: 2 additions & 0 deletions ps2xTest/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ void register_ps2_sif_rpc_tests();
void register_ps2_sif_dma_tests();
void register_ps2_recompiler_tests();
void register_ps2_runtime_expansion_tests();
void register_ps2_fpu_macros_tests();
void reset_ps2_test_function_table();

int main()
Expand All @@ -40,6 +41,7 @@ int main()
register_ps2_sif_dma_tests();
register_ps2_recompiler_tests();
register_ps2_runtime_expansion_tests();
register_ps2_fpu_macros_tests();
int res = MiniTest::Run();
std::cout.flush();
std::cerr.flush();
Expand Down
41 changes: 41 additions & 0 deletions ps2xTest/src/ps2_fpu_macros_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "MiniTest.h"

#include <cfenv>
#include <cstdint>
#include <cstring>

#include "ps2_runtime_macros.h"

// R5900 CVT.W.S reference behavior (matches the PCSX2 interpreter's CVT_W):
// truncate toward zero, saturate to 0x7FFFFFFF / 0x80000000 on overflow.
// The EE FPU ignores the host/guest rounding mode for this conversion.

void register_ps2_fpu_macros_tests()
{
MiniTest::Case("FpuMacros", [](TestCase &tc)
{
tc.Run("CVT_W_S truncates toward zero", [](TestCase &t) {
t.Equals((uint32_t)FPU_CVT_W_S(2.7f), 2u, "2.7 must truncate to 2");
t.Equals((uint32_t)FPU_CVT_W_S(3.5f), 3u, "3.5 must truncate to 3 (not round to 4)");
t.Equals((uint32_t)FPU_CVT_W_S(0.99f), 0u, "0.99 must truncate to 0");
t.Equals((uint32_t)FPU_CVT_W_S(-2.7f), 0xFFFFFFFEu, "-2.7 must truncate to -2");
});

tc.Run("CVT_W_S ignores host rounding mode", [](TestCase &t) {
const int previous = std::fegetround();
std::fesetround(FE_TONEAREST);
t.Equals((uint32_t)FPU_CVT_W_S(2.7f), 2u, "must truncate even under FE_TONEAREST");
std::fesetround(FE_UPWARD);
t.Equals((uint32_t)FPU_CVT_W_S(2.1f), 2u, "must truncate even under FE_UPWARD");
std::fesetround(previous);
});

tc.Run("CVT_W_S saturates on overflow", [](TestCase &t) {
t.Equals((uint32_t)FPU_CVT_W_S(3e9f), 0x7FFFFFFFu, "positive overflow -> 0x7FFFFFFF");
t.Equals((uint32_t)FPU_CVT_W_S(-3e9f), 0x80000000u, "negative overflow -> 0x80000000");
t.Equals((uint32_t)FPU_CVT_W_S(2147483648.0f), 0x7FFFFFFFu, "2^31 -> 0x7FFFFFFF");
t.Equals((uint32_t)FPU_CVT_W_S(-2147483648.0f), 0x80000000u, "-2^31 -> 0x80000000");
t.Equals((uint32_t)FPU_CVT_W_S(2147483520.0f), 0x7FFFFF80u,
"largest float below 2^31 converts exactly");
}); });
}