From 790aaf4cda81fed3fb816e352a7e8dc446990c41 Mon Sep 17 00:00:00 2001 From: Ranieri Date: Sat, 13 Jun 2026 16:39:39 -0300 Subject: [PATCH] feat: invert codegen hight to low convertion (#131) * feat: invert codegen hight to low convertion feat: added copy and GetEntryAddress feat: handle truncated DMAC * feat: always use address on analyzer now * feat: correct pick syscalls ID * feat: added deci2Call * feat: added wip dbcmain IOP * feat: added InitTLB feat: added err logs on thread for debug sus crash * fix: fix SetupHeap for strange cases * feat: fix incorrect SetupHeap test(it use a wrong idea on how heap allocate memory) * feat: added memalign and memalign_r feat: added GetOsdConfigParam2 and SetOsdConfigParam2 but idk if was a good idea * feat: added more memory stuff * feat: back to library functions --- ps2xAnalyzer/src/function_classifier.cpp | 2 +- ps2xAnalyzer/src/toml_generator.cpp | 6 - ps2xRecomp/src/lib/code_generator.cpp | 130 +++++----- .../tools/ghidra/ExportPS2Functions.java | 18 +- ps2xRuntime/CMakeLists.txt | 1 + ps2xRuntime/include/ps2_call_list.h | 13 + ps2xRuntime/include/ps2_runtime.h | 1 + ps2xRuntime/include/runtime/ps2_iop_dbcman.h | 14 ++ .../src/lib/Kernel/Stubs/Compatibility.cpp | 36 +++ .../src/lib/Kernel/Stubs/Compatibility.h | 5 + ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp | 63 +++++ ps2xRuntime/src/lib/Kernel/Stubs/LibC.h | 3 + ps2xRuntime/src/lib/Kernel/Syscalls/Deci2.cpp | 235 ++++++++++++++++++ .../src/lib/Kernel/Syscalls/Dispatcher.cpp | 20 +- .../src/lib/Kernel/Syscalls/Helpers/Runtime.h | 51 ++++ .../src/lib/Kernel/Syscalls/Helpers/State.h | 1 + .../src/lib/Kernel/Syscalls/System.cpp | 225 +++++++++++++++-- ps2xRuntime/src/lib/Kernel/Syscalls/System.h | 5 + .../src/lib/Kernel/Syscalls/Thread.cpp | 6 + ps2xRuntime/src/lib/ps2_iop.cpp | 15 ++ ps2xRuntime/src/lib/ps2_iop_dbcman.cpp | 83 +++++++ ps2xRuntime/src/lib/ps2_memory.cpp | 115 ++++++--- ps2xRuntime/src/lib/ps2_runtime.cpp | 26 +- ps2xTest/src/elf_analyzer_tests.cpp | 14 ++ ps2xTest/src/ps2_memory_tests.cpp | 152 +++++++++++ ps2xTest/src/ps2_recompiler_tests.cpp | 24 ++ ps2xTest/src/ps2_runtime_kernel_tests.cpp | 193 +++++++++++++- 27 files changed, 1299 insertions(+), 158 deletions(-) create mode 100644 ps2xRuntime/include/runtime/ps2_iop_dbcman.h create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Deci2.cpp create mode 100644 ps2xRuntime/src/lib/ps2_iop_dbcman.cpp diff --git a/ps2xAnalyzer/src/function_classifier.cpp b/ps2xAnalyzer/src/function_classifier.cpp index 2db285e..a0bfd74 100644 --- a/ps2xAnalyzer/src/function_classifier.cpp +++ b/ps2xAnalyzer/src/function_classifier.cpp @@ -29,7 +29,7 @@ namespace ps2recomp const std::vector stdLibFuncs = { "printf", "sprintf", "snprintf", "fprintf", "vprintf", "vfprintf", "vsprintf", "vsnprintf", "puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf", - "sprint", "sbprintf", + "sprint", "sbprintf", "__mcmp", "__sbprintf", "__sprint", "__sprint_r", "malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign", "memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero", "strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr", diff --git a/ps2xAnalyzer/src/toml_generator.cpp b/ps2xAnalyzer/src/toml_generator.cpp index bf16da2..d083c80 100644 --- a/ps2xAnalyzer/src/toml_generator.cpp +++ b/ps2xAnalyzer/src/toml_generator.cpp @@ -72,12 +72,6 @@ namespace ps2recomp auto makeSelector = [&](const std::string &name, uint32_t start) -> std::string { - auto it = functionNameCounts.find(name); - if (it == functionNameCounts.end() || it->second <= 1) - { - return name; - } - std::stringstream selector; selector << name << "@0x" << std::hex << std::uppercase << std::setw(8) << std::setfill('0') diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 96aba20..9aceed1 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -2527,8 +2527,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rd, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, inst.rt, inst.rt); } case VU0_S2_VMOVE: @@ -2678,7 +2678,7 @@ namespace ps2recomp uint8_t dest_mask = inst.vectorInfo.vectorField; uint8_t field = inst.function & 0x3; std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); - return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd); + return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } std::string CodeGenerator::translateVU_VSUB_Field(const Instruction &inst) @@ -2689,7 +2689,7 @@ namespace ps2recomp uint8_t dest_mask = inst.vectorInfo.vectorField; uint8_t field = inst.function & 0x3; std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); - return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd); + return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } std::string CodeGenerator::translateVU_VMUL_Field(const Instruction &inst) @@ -2700,7 +2700,7 @@ namespace ps2recomp uint8_t dest_mask = inst.vectorInfo.vectorField; uint8_t field = inst.function & 0x3; std::string shuffle_pattern = fmt::format("_MM_SHUFFLE({},{},{},{})", field, field, field, field); - return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd); + return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], _mm_shuffle_ps(ctx->vu0_vf[{}], ctx->vu0_vf[{}], {})); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } std::string CodeGenerator::translateVU_VADD(const Instruction &inst) @@ -2709,7 +2709,7 @@ namespace ps2recomp uint8_t vfs = inst.rd; uint8_t vft = inst.rt; uint8_t dest_mask = inst.vectorInfo.vectorField; - return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd); + return fmt::format("{{ __m128 res = PS2_VADD(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } std::string CodeGenerator::translateVU_VSUB(const Instruction &inst) @@ -2718,7 +2718,7 @@ namespace ps2recomp uint8_t vfs = inst.rd; uint8_t vft = inst.rt; uint8_t dest_mask = inst.vectorInfo.vectorField; - return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd); + return fmt::format("{{ __m128 res = PS2_VSUB(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } std::string CodeGenerator::translateVU_VMUL(const Instruction &inst) @@ -2727,7 +2727,7 @@ namespace ps2recomp uint8_t vfs = inst.rd; uint8_t vft = inst.rt; uint8_t dest_mask = inst.vectorInfo.vectorField; - return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, vfd, vfd); + return fmt::format("{{ __m128 res = PS2_VMUL(ctx->vu0_vf[{}], ctx->vu0_vf[{}]); __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = PS2_VBLEND(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } std::string CodeGenerator::translatePEXT5(const Instruction &inst) @@ -3080,8 +3080,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", inst.rd, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, inst.rt, inst.rt); } @@ -3186,8 +3186,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, vft, vft, shuffle_pattern, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3207,8 +3207,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, vft, vft, shuffle_pattern, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3226,8 +3226,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3245,8 +3245,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, vft, shuffle_pattern, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3262,8 +3262,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, vft, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3278,8 +3278,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3294,8 +3294,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3309,8 +3309,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3323,8 +3323,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3337,8 +3337,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3351,8 +3351,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3365,8 +3365,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3383,8 +3383,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, vft, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3399,8 +3399,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3413,8 +3413,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3432,8 +3432,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, vft, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3447,8 +3447,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, vft, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3463,8 +3463,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3477,8 +3477,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3494,8 +3494,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3510,8 +3510,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vu0_acc = res; }}", vfs, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vfd, vfd); } @@ -3713,8 +3713,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, formatFloatLiteral(scale), - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, inst.rt, inst.rt); } @@ -3731,8 +3731,8 @@ namespace ps2recomp "__m128i mask = _mm_set_epi32({}, {}, {}, {}); " "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vfs, formatFloatLiteral(scale), - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, inst.rt, inst.rt); } @@ -3746,8 +3746,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); " "ctx->vi[{}] = (ctx->vi[{}] + 1) & 0x3FF; }}", vis, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, inst.rt, inst.rt, vis, vis); } @@ -3763,8 +3763,8 @@ namespace ps2recomp "ctx->vi[{}] = (ctx->vi[{}] + 1) & 0x3FF; }}", vis, inst.rt, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, vis, vis); } @@ -3779,8 +3779,8 @@ namespace ps2recomp "ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", vis, vis, vis, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, inst.rt, inst.rt); } @@ -3796,15 +3796,15 @@ namespace ps2recomp vis, vis, vis, inst.rt, - (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, - (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0); + (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, + (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0); } std::string CodeGenerator::translateVU_VRGET(const Instruction &inst) { uint8_t dest_mask = inst.vectorInfo.vectorField; uint8_t ft_reg = inst.rt; - return fmt::format("{{ __m128 res = ctx->vu0_r; __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", (dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0, ft_reg, ft_reg); + return fmt::format("{{ __m128 res = ctx->vu0_r; __m128i mask = _mm_set_epi32({}, {}, {}, {}); ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}", (dest_mask & 0x1) ? -1 : 0, (dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0, (dest_mask & 0x8) ? -1 : 0, ft_reg, ft_reg); } std::string CodeGenerator::translateVU_VRINIT(const Instruction &inst) diff --git a/ps2xRecomp/tools/ghidra/ExportPS2Functions.java b/ps2xRecomp/tools/ghidra/ExportPS2Functions.java index f68e285..f84e8a6 100644 --- a/ps2xRecomp/tools/ghidra/ExportPS2Functions.java +++ b/ps2xRecomp/tools/ghidra/ExportPS2Functions.java @@ -53,19 +53,21 @@ public class ExportPS2Functions extends GhidraScript { "iGsPutIMR", "SetVSyncFlag", "SetSyscall", "GsSetVideoMode", "GetOsdConfigParam", "SetOsdConfigParam", "EnableCache", "DisableCache", "GetRomName", "SifLoadElfPart", "sceSifLoadElf", "sceSifLoadElfPart", "sceSifLoadModule", "sceSifLoadModuleBuffer", "SetupThread", "EndOfHeap", "GetMemorySize", "Deci2Call", - "QueryBootMode", "GetThreadTLS", "RegisterExitHandler", "ret0", "ret1", "reta0", - "calloc_r", "free_r", "malloc_r", "malloc_trim_r", "mbtowc_r", "printf_r", + "QueryBootMode", "GetThreadTLS", "Copy", "GetEntryAddress", "RegisterExitHandler", "ret0", "ret1", "reta0", + "calloc_r", "free_r", "realloc_r", "memalign_r", "malloc_r", "malloc_extend_top", "malloc_trim_r", "mbtowc_r", "printf_r", + "__malloc_lock", "__malloc_unlock", "abs", "__ieee754_rem_pio2f", "__kernel_cosf", "__kernel_sinf", "atan", "atan2", "calloc", "ceil", "close", "cos", "exit", "exp", "fabs", "fclose", "fflush", "floor", "fopen", "fprintf", "fread", "free", "fseek", "fstat", "ftell", "fwrite", - "getpid", "log", "log10", "lseek", "malloc", "memchr", + "getpid", "log", "log10", "lseek", "malloc", "memalign", "memclr", "memchr", "memcmp", "memcpy", "memmove", "memset", "open", "pow", "printf", "puts", "rand", "read", "realloc", "sin", "snprintf", "sprintf", "sqrt", "srand", "stat", "strcasecmp", "strcat", "strchr", "strcmp", "strcpy", "strlen", "strncat", "strncmp", "strncpy", "strrchr", "strstr", "tan", "vfprintf", - "vsprintf", "write", "DmaAddr", "builtin_set_imask", "sceCdRI", "sceCdRM", + "vsprintf", "write", "__divdi3", + "DmaAddr", "builtin_set_imask", "sceCdRI", "sceCdRM", "sceDevVif0Reset", "sceDevVu0Reset", "sceFsDbChk", "sceFsIntrSigSema", "sceFsSemExit", "sceFsSemInit", "sceFsSigSema", "sceIDC", "sceMpegFlush", "sceRpcFreePacket", "sceRpcGetFPacket", "sceRpcGetFPacket2", "sceSDC", "sceSifCmdIntrHdlr", "sceVu0ecossin", "mcCallMessageTypeSe", "mcCheckReadStartConfigFile", "mcCheckReadStartSaveFile", @@ -141,7 +143,7 @@ public class ExportPS2Functions extends GhidraScript { "sceVu0InterVectorXYZ", "sceVu0InversMatrix", "sceVu0LightColorMatrix", "sceVu0MulMatrix", "sceVu0MulVector", "sceVu0NormalLightMatrix", "sceVu0Normalize", "sceVu0OuterProduct", "sceVu0RotMatrix", "sceVu0RotMatrixX", "sceVu0RotMatrixY", "sceVu0RotMatrixZ", "sceVu0RotTransPers", "sceVu0RotTransPersN", "sceVu0ScaleVector", "sceVu0ScaleVectorXYZ", "sceVu0SubVector", "sceVu0TransMatrix", - "sceVu0TransposeMatrix", "sceVu0UnitMatrix", "sceVu0ViewScreenMatrix", "sceWrite" + "sceVu0TransposeMatrix", "sceVu0UnitMatrix", "sceVu0ViewScreenMatrix", "sceWrite", "InitTLB" )); private static final Set PS2_API_PREFIXES = new HashSet<>(Arrays.asList( @@ -158,9 +160,9 @@ public class ExportPS2Functions extends GhidraScript { private static final Set KNOWN_STDLIB_NAMES = new HashSet<>(Arrays.asList( "printf", "sprintf", "snprintf", "fprintf", "vprintf", "vfprintf", "vsprintf", "vsnprintf", - "puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf", - "sprint", "sbprintf", - "malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign", + "puts", "putchar", "getchar", "gets", "fgets", + "__mcmp", "__sbprintf", "__sprint", "__sprint_r", + "malloc", "free", "calloc", "realloc", "memalign", "memclr", "aligned_alloc", "posix_memalign", "memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero", "strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr", "strchr", "strrchr", "strdup", "strtok", "strtok_r", "strerror", diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index ddaf5fa..d2386f1 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -170,6 +170,7 @@ add_library(ps2_runtime STATIC src/lib/ps2_gs_rasterizer.cpp src/lib/ps2_iop.cpp src/lib/ps2_iop_audio.cpp + src/lib/ps2_iop_dbcman.cpp src/lib/ps2_memory.cpp src/lib/ps2_pad.cpp src/lib/ps2_runtime.cpp diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index 39fbf1e..9c03db2 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -116,6 +116,8 @@ \ X(GetOsdConfigParam) \ X(SetOsdConfigParam) \ + X(GetOsdConfigParam2) \ + X(SetOsdConfigParam2) \ X(EnableCache) \ X(DisableCache) \ X(GetRomName) \ @@ -128,9 +130,12 @@ X(SetupThread) \ X(EndOfHeap) \ X(GetMemorySize) \ + X(InitTLB) \ X(Deci2Call) \ X(QueryBootMode) \ X(GetThreadTLS) \ + X(Copy) \ + X(GetEntryAddress) \ X(RegisterExitHandler) // Stubs @@ -141,10 +146,15 @@ X(reta0) \ X(calloc_r) \ X(free_r) \ + X(realloc_r) \ + X(memalign_r) \ X(malloc_r) \ + X(malloc_extend_top) \ X(malloc_trim_r) \ X(mbtowc_r) \ X(printf_r) \ + X(__malloc_lock) \ + X(__malloc_unlock) \ X(abs) \ X(__ieee754_rem_pio2f) \ X(__kernel_cosf) \ @@ -174,6 +184,8 @@ X(log10) \ X(lseek) \ X(malloc) \ + X(memalign) \ + X(memclr) \ X(memchr) \ X(memcmp) \ X(memcpy) \ @@ -207,6 +219,7 @@ X(vfprintf) \ X(vsprintf) \ X(write) \ + X(__divdi3) \ /* PS2 native */ \ X(DmaAddr) \ X(builtin_set_imask) \ diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index e40d96b..4c4f051 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -511,6 +511,7 @@ public: void guestFree(uint32_t guestAddr); uint32_t guestHeapBase() const; uint32_t guestHeapEnd() const; + uint32_t guestHeapLimit() const; uint32_t reserveAsyncCallbackStack(uint32_t size, uint32_t alignment = 16u); void dispatchLoop(uint8_t *rdram, R5900Context *ctx); bool shouldPreemptGuestExecution(); diff --git a/ps2xRuntime/include/runtime/ps2_iop_dbcman.h b/ps2xRuntime/include/runtime/ps2_iop_dbcman.h new file mode 100644 index 0000000..6d1aa48 --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_iop_dbcman.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +class PS2Runtime; + +namespace ps2_iop_dbcman +{ + bool handleDbcManRpc(uint8_t *rdram, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBufAddr, uint32_t sendSize, + uint32_t recvBufAddr, uint32_t recvSize, + uint32_t &resultPtr); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp index f548b64..9f8f59f 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp @@ -39,6 +39,22 @@ namespace ps2_stubs } } + void realloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t oldGuestAddr = getRegU32(ctx, 5); // $a1 + const uint32_t newSize = getRegU32(ctx, 6); // $a2 + const uint32_t guestAddr = runtime ? runtime->guestRealloc(oldGuestAddr, newSize) : 0u; + setReturnU32(ctx, guestAddr); + } + + void memalign_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t alignment = getRegU32(ctx, 5); // $a1 + const uint32_t size = getRegU32(ctx, 6); // $a2 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size, alignment) : 0u; + setReturnU32(ctx, guestAddr); + } + void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t size = getRegU32(ctx, 5); // $a1 @@ -46,11 +62,31 @@ namespace ps2_stubs setReturnU32(ctx, guestAddr); } + void malloc_extend_top(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + // newlib allocator internal. The runtime owns guest heap growth. + setReturnU32(ctx, 0u); + } + void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); } + void __malloc_lock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)ctx; + (void)runtime; + } + + void __malloc_unlock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)ctx; + (void)runtime; + } + void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t wcAddr = getRegU32(ctx, 5); // $a1 diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h index 2159c44..b1146dd 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h @@ -9,8 +9,13 @@ namespace ps2_stubs void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void realloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void memalign_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void malloc_extend_top(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void __malloc_lock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void __malloc_unlock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp index 253540e..d467c4b 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp @@ -44,6 +44,7 @@ namespace ps2_stubs } return (offset < PS2_RAM_SIZE) ? (PS2_RAM_SIZE - offset) : 0u; } + } void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -53,6 +54,14 @@ namespace ps2_stubs setReturnU32(ctx, guestAddr); } + void memalign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t alignment = getRegU32(ctx, 4); // $a0 + const uint32_t size = getRegU32(ctx, 5); // $a1 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size, alignment) : 0u; + setReturnU32(ctx, guestAddr); + } + void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 @@ -158,6 +167,42 @@ namespace ps2_stubs ctx->r[2] = ctx->r[4]; } + void memclr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t size = getRegU32(ctx, 5); // $a1 + size = sanitizeMemTransferSize(size, "memclr"); + + uint32_t written = 0u; + uint32_t curDst = destAddr; + while (written < size) + { + uint8_t *hostDest = getMemPtr(rdram, curDst); + if (!hostDest) + { + break; + } + + uint32_t chunk = size - written; + chunk = std::min(chunk, guestContiguousBytes(curDst)); + if (chunk == 0u) + { + break; + } + + ::memset(hostDest, 0, chunk); + written += chunk; + curDst += chunk; + } + + if (written != 0u) + { + ps2TraceGuestRangeWrite(rdram, destAddr, written, "memclr", ctx); + } + + ctx->r[2] = ctx->r[4]; + } + void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t destAddr = getRegU32(ctx, 4); // $a0 @@ -1129,4 +1174,22 @@ namespace ps2_stubs setReturnS32(ctx, ret); } + + void __divdi3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int64_t num = GPR_S64(ctx, 4); + const int64_t den = GPR_S64(ctx, 5); + if (den == 0) + { + setReturnU64(ctx, 0u); + return; + } + if (num == std::numeric_limits::min() && den == -1) + { + setReturnU64(ctx, static_cast(num)); + return; + } + setReturnU64(ctx, static_cast(num / den)); + } + } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h index 01792bf..1d376bb 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h @@ -5,11 +5,13 @@ namespace ps2_stubs { void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void memalign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void memclr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); @@ -57,4 +59,5 @@ namespace ps2_stubs void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void __divdi3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Deci2.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Deci2.cpp new file mode 100644 index 0000000..039da2f --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Deci2.cpp @@ -0,0 +1,235 @@ +#include "System.h" +#include "Common.h" +#include "ps2_runtime.h" + +namespace +{ + struct Deci2Session + { + uint16_t protocol = 0; + uint32_t opt = 0; + uint32_t handler = 0; + uint32_t userArea = 0; + bool locked = false; + }; + + std::mutex g_deci2Mutex; + std::unordered_map g_deci2Sessions; + int32_t g_nextDeci2Socket = 1; + std::atomic g_deci2LogCount{0}; + + static bool readDeci2U32(uint8_t *rdram, uint32_t guestAddr, uint32_t &value) + { + value = 0; + + const uint8_t *ptr = getConstMemPtr(rdram, guestAddr); + if (!ptr) + { + return false; + } + + std::memcpy(&value, ptr, sizeof(value)); + return true; + } + + static void readDeci2Args(uint8_t *rdram, uint32_t argsAddr, uint32_t args[4]) + { + for (uint32_t argIndex = 0; argIndex < 4; argIndex++) + { + uint32_t value = 0; + readDeci2U32(rdram, argsAddr + (argIndex * sizeof(uint32_t)), value); + args[argIndex] = value; + } + } + + static std::string sanitizeDeci2Text(std::string text) + { + for (char &ch : text) + { + const unsigned char value = static_cast(ch); + if (ch == '\r') + { + ch = '\n'; + continue; + } + + if (ch != '\n' && ch != '\t' && !std::isprint(value)) + { + ch = '.'; + } + } + + return text; + } + + static void logDeci2Text(const char *prefix, const std::string &text) + { + constexpr uint32_t kMaxDeci2TextLogs = 256u; + const uint32_t logIndex = g_deci2LogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex >= kMaxDeci2TextLogs) + { + return; + } + + const std::string safeText = sanitizeDeci2Text(text); + std::cerr << prefix << safeText; + if (safeText.empty() || safeText.back() != '\n') + { + std::cerr << std::endl; + } + } + + static int32_t allocateDeci2Socket(uint16_t protocol, uint32_t opt, uint32_t handler, uint32_t userArea) + { + std::lock_guard lock(g_deci2Mutex); + + if (g_nextDeci2Socket <= 0) + { + g_nextDeci2Socket = 1; + } + + const int32_t socket = g_nextDeci2Socket; + Deci2Session session; + session.protocol = protocol; + session.opt = opt; + session.handler = handler; + session.userArea = userArea; + session.locked = false; + + g_deci2Sessions[socket] = session; + return socket; + } + + static bool updateDeci2LockState(int32_t socket, bool locked) + { + std::lock_guard lock(g_deci2Mutex); + + auto sessionIt = g_deci2Sessions.find(socket); + if (sessionIt == g_deci2Sessions.end()) + { + return false; + } + + sessionIt->second.locked = locked; + return true; + } + + static bool closeDeci2Socket(int32_t socket) + { + std::lock_guard lock(g_deci2Mutex); + return g_deci2Sessions.erase(socket) != 0; + } +} + +namespace ps2_syscalls +{ + void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { +#if defined(_DEBUG) || defined(RUNTIME_DECI2CALL) + const int32_t code = static_cast(getRegU32(ctx, 4)); + const uint32_t argsAddr = getRegU32(ctx, 5); + + uint32_t args[4] = {}; + readDeci2Args(rdram, argsAddr, args); + + switch (code) + { + case 1: // sceDeci2Open(protocol, opt, handler) + { + const uint16_t protocol = static_cast(args[0] & 0xFFFFu); + const uint32_t opt = args[1]; + const uint32_t handler = args[2]; + const uint32_t userArea = args[3]; + + const int32_t socket = allocateDeci2Socket(protocol, opt, handler, userArea); + setReturnS32(ctx, socket); + return; + } + case 2: // sceDeci2Close(socket) + { + const int32_t socket = static_cast(args[0]); + setReturnS32(ctx, closeDeci2Socket(socket) ? KE_OK : KE_ERROR); + return; + } + // WE dont need to do thouses + case 3: // sceDeci2ReqSend(socket, dest) + case 4: // sceDeci2Poll(socket) + case -7: // sceDeci2ExReqSend(socket, dest) + { + setReturnS32(ctx, KE_OK); + return; + } + case -5: // sceDeci2ExRecv(socket, buf, len) + { + // No host debugger channel is attached, so report "no bytes available". + setReturnS32(ctx, 0); + return; + } + case -6: // sceDeci2ExSend(socket, buf, len) + { + const uint32_t bufferAddr = args[1]; + const uint32_t length = args[2] & 0xFFFFu; + + if (bufferAddr != 0u && length != 0u) + { + const uint32_t clampedLength = std::min(length, 512u); + std::string text; + text.reserve(clampedLength); + for (uint32_t byteIndex = 0; byteIndex < clampedLength; byteIndex++) + { + text.push_back(static_cast(rdram[(bufferAddr + byteIndex) & PS2_RAM_MASK])); + } + logDeci2Text("[Deci2Call:send] ", text); + } + + // Treat send as successful and return the amount accepted. + setReturnS32(ctx, static_cast(length)); + return; + } + case -8: // sceDeci2ExLock(socket) + { + const int32_t socket = static_cast(args[0]); + setReturnS32(ctx, updateDeci2LockState(socket, true) ? KE_OK : KE_ERROR); + return; + } + case -9: // sceDeci2ExUnLock(socket) + { + const int32_t socket = static_cast(args[0]); + setReturnS32(ctx, updateDeci2LockState(socket, false) ? KE_OK : KE_ERROR); + return; + } + case 16: // kputs(char *s) + { + constexpr size_t kMaxDeci2KputsBytes = 4096u; + const uint32_t textAddr = args[0]; + const std::string text = readGuestCStringBounded(rdram, textAddr, kMaxDeci2KputsBytes); + + logDeci2Text("[Deci2Call:kputs] ", text); + setReturnS32(ctx, static_cast(text.size())); + return; + } + default: + { + static std::atomic s_unknownDeci2Logs{0u}; + constexpr uint32_t kMaxUnknownDeci2Logs = 64u; + const uint32_t logIndex = s_unknownDeci2Logs.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < kMaxUnknownDeci2Logs) + { + std::cerr << "[Deci2Call:unknown]" + << " code=" << code + << " args=0x" << std::hex << argsAddr + << " a0=0x" << args[0] + << " a1=0x" << args[1] + << " a2=0x" << args[2] + << " a3=0x" << args[3] + << " pc=0x" << ctx->pc + << std::dec << std::endl; + } + + setReturnS32(ctx, KE_OK); + return; + } + } +#endif + } +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp index c792e6f..89de596 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp @@ -223,10 +223,10 @@ namespace ps2_syscalls iReferEventFlagStatus(rdram, ctx, runtime); return true; case 0x5A: - QueryBootMode(rdram, ctx, runtime); + Copy(rdram, ctx, runtime); return true; case 0x5B: - GetThreadTLS(rdram, ctx, runtime); + GetEntryAddress(rdram, ctx, runtime); return true; case 0x5C: case static_cast(-0x5C): @@ -253,6 +253,12 @@ namespace ps2_syscalls case 0x64: FlushCache(rdram, ctx, runtime); return true; + case 0x6E: + SetOsdConfigParam2(rdram, ctx, runtime); + return true; + case 0x6F: + GetOsdConfigParam2(rdram, ctx, runtime); + return true; case 0x70: GsGetIMR(rdram, ctx, runtime); return true; @@ -283,6 +289,16 @@ namespace ps2_syscalls case static_cast(-0x78): ps2_stubs::sceSifSetDChain(rdram, ctx, runtime); return true; + case 0x7F: + GetMemorySize(rdram, ctx, runtime); + return true; + case 0x82: + InitTLB(rdram, ctx, runtime); + return true; + case 0x7C: + case static_cast(-0x7C): + Deci2Call(rdram, ctx, runtime); + return true; case 0x83: FindAddress(rdram, ctx, runtime); return true; diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h index 76cb558..fd9a2a5 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h @@ -593,6 +593,20 @@ static uint32_t packOsdConfig(uint32_t spdifMode, uint32_t screenType, uint32_t return raw; } +static uint32_t packOsdConfig2(uint32_t format, uint32_t daylightSaving, uint32_t timeFormat, + uint32_t dateFormat, uint32_t version, uint32_t language) +{ + const uint32_t flags = + ((daylightSaving & 0x1u) << 4) | + ((timeFormat & 0x1u) << 5) | + ((dateFormat & 0x3u) << 6); + + return (format & 0xFFu) | + ((flags & 0xFFu) << 8) | + ((version & 0xFFu) << 16) | + ((language & 0xFFu) << 24); +} + static int decodeTimezoneOffset(uint32_t raw) { int tz = static_cast((raw >> 21) & 0x7FF); @@ -627,6 +641,42 @@ static uint32_t sanitizeOsdConfigRaw(uint32_t raw) return packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz); } +static uint32_t sanitizeOsdConfig2Raw(uint32_t raw) +{ + const uint32_t format = raw & 0xFFu; + const uint32_t flags = (raw >> 8) & 0xFFu; + const uint32_t daylightSaving = (flags >> 4) & 0x1u; + const uint32_t timeFormat = (flags >> 5) & 0x1u; + uint32_t dateFormat = (flags >> 6) & 0x3u; + if (dateFormat > 2u) + dateFormat = 0u; + + uint32_t version = (raw >> 16) & 0xFFu; + if (version > 2u) + version = 1u; + + const uint32_t language = (raw >> 24) & 0xFFu; + return packOsdConfig2(format, daylightSaving, timeFormat, dateFormat, version, language); +} + +static uint32_t syncOsdConfigRawVersionLanguage(uint32_t raw, uint32_t version, uint32_t language) +{ + raw &= ~((0x7u << 13) | (0x1Fu << 16)); + raw |= (version & 0x7u) << 13; + raw |= (language & 0x1Fu) << 16; + return sanitizeOsdConfigRaw(raw); +} + +static uint32_t makeReadableOsdConfig2RawLocked() +{ + const uint32_t version = (g_osd_config_raw >> 13) & 0x7u; + const uint32_t language = (g_osd_config_raw >> 16) & 0x1Fu; + uint32_t raw = g_osd_config2_raw & 0x0000FFFFu; + raw |= (version & 0xFFu) << 16; + raw |= (language & 0xFFu) << 24; + return sanitizeOsdConfig2Raw(raw); +} + static void ensureOsdConfigInitialized() { std::lock_guard lock(g_osd_mutex); @@ -642,6 +692,7 @@ static void ensureOsdConfigInitialized() uint32_t version = 1; // OSD2 uint32_t language = 1; // English g_osd_config_raw = packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz); + g_osd_config2_raw = packOsdConfig2(0, 0, 0, 0, version, language); g_osd_config_initialized = true; } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h index fac2c0e..eb3e182 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h @@ -523,6 +523,7 @@ inline uint32_t g_tls_index = 0; inline std::mutex g_osd_mutex; inline bool g_osd_config_initialized = false; inline uint32_t g_osd_config_raw = 0; +inline uint32_t g_osd_config2_raw = 0; inline std::mutex g_ps2_path_mutex; inline bool g_ps2_paths_initialized = false; diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp index 0752220..bd808a2 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp @@ -126,12 +126,121 @@ namespace ps2_syscalls { std::lock_guard lock(g_osd_mutex); g_osd_config_raw = raw; + g_osd_config2_raw = makeReadableOsdConfig2RawLocked(); g_osd_config_initialized = true; } setReturnS32(ctx, 0); } + void SetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)runtime; + const uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - Config2Param* + const uint32_t size = getRegU32(ctx, 5); // $a1 - sizeof(Config2Param), normally 4 + + ensureOsdConfigInitialized(); + + if (size == 0u) + { + setReturnS32(ctx, 0); + return; + } + + uint32_t raw = 0; + { + std::lock_guard lock(g_osd_mutex); + raw = makeReadableOsdConfig2RawLocked(); + } + + const uint32_t copyBytes = std::min(size, 4u); + uint8_t rawBytes[4] = { + static_cast(raw & 0xFFu), + static_cast((raw >> 8) & 0xFFu), + static_cast((raw >> 16) & 0xFFu), + static_cast((raw >> 24) & 0xFFu), + }; + + for (uint32_t i = 0; i < copyBytes; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, paramAddr + i); + if (!src) + { + std::cerr << "PS2 SetOsdConfigParam2 error: Invalid parameter address: 0x" + << std::hex << (paramAddr + i) << std::dec << std::endl; + setReturnS32(ctx, -1); + return; + } + rawBytes[i] = *src; + } + + raw = static_cast(rawBytes[0]) | + (static_cast(rawBytes[1]) << 8) | + (static_cast(rawBytes[2]) << 16) | + (static_cast(rawBytes[3]) << 24); + raw = sanitizeOsdConfig2Raw(raw); + + { + std::lock_guard lock(g_osd_mutex); + g_osd_config2_raw = raw; + + uint32_t version = (g_osd_config_raw >> 13) & 0x7u; + uint32_t language = (g_osd_config_raw >> 16) & 0x1Fu; + if (copyBytes >= 3u) + version = (raw >> 16) & 0xFFu; + if (copyBytes >= 4u) + language = (raw >> 24) & 0xFFu; + g_osd_config_raw = syncOsdConfigRawVersionLanguage(g_osd_config_raw, version, language); + g_osd_config_initialized = true; + } + + setReturnS32(ctx, 0); + } + + void GetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)runtime; + const uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - Config2Param* + const uint32_t size = getRegU32(ctx, 5); // $a1 - sizeof(Config2Param), normally 4 + + ensureOsdConfigInitialized(); + + if (size == 0u) + { + setReturnS32(ctx, 0); + return; + } + + uint32_t raw = 0; + { + std::lock_guard lock(g_osd_mutex); + raw = makeReadableOsdConfig2RawLocked(); + } + + const uint8_t rawBytes[4] = { + static_cast(raw & 0xFFu), + static_cast((raw >> 8) & 0xFFu), + static_cast((raw >> 16) & 0xFFu), + static_cast((raw >> 24) & 0xFFu), + }; + const uint32_t copyBytes = std::min(size, 4u); + + for (uint32_t i = 0; i < copyBytes; ++i) + { + uint8_t *dst = getMemPtr(rdram, paramAddr + i); + if (!dst) + { + std::cerr << "PS2 GetOsdConfigParam2 error: Invalid parameter address: 0x" + << std::hex << (paramAddr + i) << std::dec << std::endl; + setReturnS32(ctx, -1); + return; + } + *dst = rawBytes[i]; + } + + setReturnS32(ctx, 0); + } + void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t bufAddr = getRegU32(ctx, 4); // $a0 @@ -244,7 +353,7 @@ namespace ps2_syscalls uint32_t syscallId = encodedSyscallId; if (syscallId == 0u) { - syscallId = (v0 != 0u) ? v0 : v1; + syscallId = v1; } std::cerr << "Warning: Unimplemented PS2 syscall called. PC=0x" << std::hex << ctx->pc @@ -573,18 +682,38 @@ namespace ps2_syscalls // 0x3D SetupHeap: returns heap base/start pointer void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const uint32_t heapBase = getRegU32(ctx, 4); // $a0 + const uint32_t heapBaseRaw = getRegU32(ctx, 4); // $a0 const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) + const uint32_t heapBase = (heapBaseRaw + 0xFu) & ~0xFu; + + // Silent Hill and other games often pass -1 (0xFFFFFFFF) to mean "rest of RAM". + static constexpr uint32_t kDefaultGuestHeapEnd = 0x01F00000u; + uint32_t heapLimit = kDefaultGuestHeapEnd; + + if (heapSize != 0u && heapSize != 0xFFFFFFFFu) + { + const uint64_t candidate = static_cast(heapBase) + static_cast(heapSize); + heapLimit = static_cast(std::min(candidate, kDefaultGuestHeapEnd)); + } + + if (heapLimit <= heapBase) + { + heapLimit = kDefaultGuestHeapEnd; + } + if (runtime) { - uint32_t heapLimit = PS2_RAM_SIZE; - if (heapSize != 0u && heapBase < PS2_RAM_SIZE) - { - const uint64_t candidateLimit = static_cast(heapBase) + static_cast(heapSize); - heapLimit = static_cast(std::min(candidateLimit, PS2_RAM_SIZE)); - } runtime->configureGuestHeap(heapBase, heapLimit); + + std::cerr << "[SetupHeap]" + << " base=0x" << std::hex << heapBaseRaw + << " alignedBase=0x" << heapBase + << " size=0x" << heapSize + << " runtimeBase=0x" << runtime->guestHeapBase() + << " runtimeEnd=0x" << runtime->guestHeapEnd() + << std::dec << std::endl; + setReturnU32(ctx, runtime->guestHeapBase()); return; } @@ -595,13 +724,15 @@ namespace ps2_syscalls // 0x3E EndOfHeap: commonly returns current heap end; keep it stable for now. void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (runtime) - { - setReturnU32(ctx, runtime->guestHeapEnd()); - return; - } + (void)rdram; - setReturnU32(ctx, getRegU32(ctx, 4)); + static constexpr uint32_t kDefaultGuestHeapEnd = 0x01F00000u; + + const uint32_t ret = runtime + ? runtime->guestHeapLimit() + : kDefaultGuestHeapEnd; + + setReturnU32(ctx, ret); } void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -611,6 +742,30 @@ namespace ps2_syscalls setReturnU32(ctx, PS2_RAM_SIZE); } + void InitTLB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + // TODO I`m 99% sure we dont need this we could just return ok and should be fine. + auto &memory = runtime->memory(); + + const uint32_t entryCount = static_cast(memory.tlbEntryCount()); + for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) + { + memory.tlbWrite(entryIndex, 0u, 0u, 0u, false); + } + + // Reset basic COP0 TLB bookkeeping to sane post-init values. + ctx->cop0_index = 0u; + ctx->cop0_random = entryCount > 0u ? (entryCount - 1u) : 0u; + ctx->cop0_entrylo0 = 0u; + ctx->cop0_entrylo1 = 0u; + ctx->cop0_context = 0u; + ctx->cop0_pagemask = 0u; + ctx->cop0_entryhi = 0u; + + setReturnS32(ctx, KE_OK); + } + static inline uint32_t normalizeKernelAlias(uint32_t addr) { if (addr >= 0x80000000u && addr < 0xC0000000u) @@ -897,14 +1052,7 @@ namespace ps2_syscalls setReturnU32(ctx, resultAddr); } - void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - setReturnS32(ctx, KE_OK); - } - - // 0x5A QueryBootMode (stub): return 0 for now + // QueryBootMode (stub): return 0 for now void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t mode = getRegU32(ctx, 4); @@ -919,7 +1067,7 @@ namespace ps2_syscalls setReturnU32(ctx, addr); } - // 0x5B GetThreadTLS (stub): return 0 + // GetThreadTLS (stub): return 0 void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { auto info = ensureCurrentThreadInfo(ctx); @@ -937,6 +1085,37 @@ namespace ps2_syscalls setReturnU32(ctx, info->tlsBase); } + void Copy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t dest = getRegU32(ctx, 4); + const uint32_t src = getRegU32(ctx, 5); + const uint32_t size = getRegU32(ctx, 6); + + if (rdram && size > 0) + { + uint8_t *destPtr = getMemPtr(rdram, dest); + const uint8_t *srcPtr = getConstMemPtr(rdram, src); + if (destPtr && srcPtr) + { + std::memcpy(destPtr, srcPtr, size); + } + } + setReturnS32(ctx, 0); + } + + void GetEntryAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t syscallNum = getRegU32(ctx, 4); + + const uint32_t entryAddr = kGuestSyscallTableGuestBase + (syscallNum * 4u); + uint32_t handler = 0; + if (const uint8_t *ptr = getConstMemPtr(rdram, entryAddr)) + { + std::memcpy(&handler, ptr, sizeof(handler)); + } + setReturnU32(ctx, handler); + } + // 0x74 RegisterExitHandler (stub): return 0 void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/System.h b/ps2xRuntime/src/lib/Kernel/Syscalls/System.h index b668620..02d9e91 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/System.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.h @@ -14,6 +14,8 @@ namespace ps2_syscalls void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void GetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); @@ -27,9 +29,12 @@ namespace ps2_syscalls void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void InitTLB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void Copy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void GetEntryAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp index e024063..83324f6 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp @@ -520,6 +520,9 @@ namespace ps2_syscalls void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + RUNTIME_LOG("[ExitThread] Game requested thread exit! PC=0x" << std::hex << ctx->pc + << " RA=0x" << getRegU32(ctx, 31) << std::dec << " tid=" << g_currentThreadId << std::endl); + runExitHandlersForThread(g_currentThreadId, rdram, ctx, runtime); auto info = ensureCurrentThreadInfo(ctx); if (info) @@ -541,6 +544,9 @@ namespace ps2_syscalls void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int tid = g_currentThreadId; + RUNTIME_LOG("[ExitDeleteThread] Game requested thread exit & delete! PC=0x" << std::hex << ctx->pc + << " RA=0x" << getRegU32(ctx, 31) << std::dec << " tid=" << tid << std::endl); + runExitHandlersForThread(tid, rdram, ctx, runtime); auto info = ensureCurrentThreadInfo(ctx); if (info) diff --git a/ps2xRuntime/src/lib/ps2_iop.cpp b/ps2xRuntime/src/lib/ps2_iop.cpp index e266509..62cc5ac 100644 --- a/ps2xRuntime/src/lib/ps2_iop.cpp +++ b/ps2xRuntime/src/lib/ps2_iop.cpp @@ -1,9 +1,15 @@ #include "runtime/ps2_iop.h" #include "runtime/ps2_iop_audio.h" +#include "runtime/ps2_iop_dbcman.h" #include "runtime/ps2_memory.h" #include "ps2_runtime.h" #include "Kernel/Syscalls/RPC.h" +// ps2_iop.cpp + +namespace +{ +} ps2_iop::ps2_iop() { reset(); @@ -43,6 +49,15 @@ bool ps2_iop::handleRPC(PS2Runtime *runtime, return true; } + if (ps2_iop_dbcman::handleDbcManRpc(m_rdram, + sid, rpcNum, + sendBufAddr, sendSize, + recvBufAddr, recvSize, + resultPtr)) + { + return true; + } + if (sid == IOP_SID_LIBSD) { const uint8_t *sendPtr = sendBufAddr ? getConstMemPtr(m_rdram, sendBufAddr) : nullptr; diff --git a/ps2xRuntime/src/lib/ps2_iop_dbcman.cpp b/ps2xRuntime/src/lib/ps2_iop_dbcman.cpp new file mode 100644 index 0000000..309d943 --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_iop_dbcman.cpp @@ -0,0 +1,83 @@ +#include "runtime/ps2_iop_dbcman.h" +#include "runtime/ps2_memory.h" +#include +#include + +namespace +{ + constexpr uint32_t IOP_SID_DBCMAN = 0x80001300u; + constexpr uint32_t DBCMAN_RPC_CHECK_VERSION = 0x80001363u; + + static bool writeIopU32(uint8_t *rdram, uint32_t addr, uint32_t value) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + + std::memcpy(ptr, &value, sizeof(value)); + return true; + } +} + +namespace ps2_iop_dbcman +{ + bool handleDbcManRpc(uint8_t *rdram, + uint32_t sid, + uint32_t rpcNum, + uint32_t sendBufAddr, + uint32_t sendSize, + uint32_t recvBufAddr, + uint32_t recvSize, + uint32_t &resultPtr) + { + if (sid != IOP_SID_DBCMAN) + { + return false; + } + + if (recvBufAddr == 0u || recvSize == 0u) + { + resultPtr = recvBufAddr; + return true; + } + + switch (rpcNum) + { + case DBCMAN_RPC_CHECK_VERSION: + { + // TODO move this to compile flag + // libdbc expects dbcman.irx version 3.20 + constexpr uint32_t DBCMAN_VERSION = 0x0320; + + writeIopU32(rdram, recvBufAddr + 0x00u, DBCMAN_VERSION); + writeIopU32(rdram, recvBufAddr + 0x04u, DBCMAN_VERSION); + writeIopU32(rdram, recvBufAddr + 0x08u, DBCMAN_VERSION); + writeIopU32(rdram, recvBufAddr + 0x0Cu, DBCMAN_VERSION); + + resultPtr = recvBufAddr; + return true; + } + default: + { + static uint32_t dbcLogCount = 0; + if (dbcLogCount < 32) + { + std::cerr << "[DBCMAN:stub]" + << " sid=0x" << std::hex << sid + << " rpc=0x" << rpcNum + << " send=0x" << sendBufAddr + << " sendSize=0x" << sendSize + << " recv=0x" << recvBufAddr + << " recvSize=0x" << recvSize + << std::dec << std::endl; + ++dbcLogCount; + } + + resultPtr = recvBufAddr; + return true; + } + } + } +} diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index c1afdf3..907e6b3 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -882,13 +882,19 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) base2 = m_rdram; maxSz2 = PS2_RAM_SIZE; } - if (src >= maxSz2) - return; - if (src + bytes > maxSz2) - bytes = maxSz2 - src; - if (bytes == 0) - return; - chainBuf.insert(chainBuf.end(), base2 + src, base2 + src + bytes); + while (bytes > 0) + { + if (src >= maxSz2) + src = 0; + uint32_t chunk = bytes; + if (src + chunk > maxSz2) + chunk = maxSz2 - src; + if (chunk == 0) + break; + chainBuf.insert(chainBuf.end(), base2 + src, base2 + src + chunk); + bytes -= chunk; + src += chunk; + } }; auto appendCompactVif1TagData = [&](uint32_t localTagAddr, uint32_t qwCount) @@ -1114,22 +1120,40 @@ void PS2Memory::processPendingTransfers() } if (p.fromScratchpad) { - if (srcPhys + sizeBytes <= PS2_SCRATCHPAD_SIZE && sizeBytes >= 16) + uint32_t bytesLeft = sizeBytes; + while (bytesLeft >= 16) { + if (srcPhys >= PS2_SCRATCHPAD_SIZE) + srcPhys = 0; + uint32_t chunk = bytesLeft; + if (srcPhys + chunk > PS2_SCRATCHPAD_SIZE) + chunk = PS2_SCRATCHPAD_SIZE - srcPhys; + if (chunk == 0) + break; m_seenGifCopy = true; m_gifCopyCount.fetch_add(1, std::memory_order_relaxed); - submitGifPacket(GifPathId::Path3, m_scratchpad + srcPhys, sizeBytes, false); + submitGifPacket(GifPathId::Path3, m_scratchpad + srcPhys, chunk, false); + bytesLeft -= chunk; + srcPhys += chunk; } } - else if (srcPhys < PS2_RAM_SIZE) + else { - if (static_cast(srcPhys) + sizeBytes > PS2_RAM_SIZE) - sizeBytes = PS2_RAM_SIZE - srcPhys; - if (sizeBytes >= 16) + uint32_t bytesLeft = sizeBytes; + while (bytesLeft >= 16) { + if (srcPhys >= PS2_RAM_SIZE) + srcPhys = 0; + uint32_t chunk = bytesLeft; + if (srcPhys + chunk > PS2_RAM_SIZE) + chunk = PS2_RAM_SIZE - srcPhys; + if (chunk == 0) + break; m_seenGifCopy = true; m_gifCopyCount.fetch_add(1, std::memory_order_relaxed); - submitGifPacket(GifPathId::Path3, m_rdram + srcPhys, sizeBytes, false); + submitGifPacket(GifPathId::Path3, m_rdram + srcPhys, chunk, false); + bytesLeft -= chunk; + srcPhys += chunk; } } } @@ -1158,15 +1182,37 @@ void PS2Memory::processPendingTransfers() } if (p.fromScratchpad) { - if (srcPhys + sizeBytes <= PS2_SCRATCHPAD_SIZE && sizeBytes > 0u) - processVIF1Data(m_scratchpad + srcPhys, sizeBytes); + uint32_t bytesLeft = sizeBytes; + while (bytesLeft > 0) + { + if (srcPhys >= PS2_SCRATCHPAD_SIZE) + srcPhys = 0; + uint32_t chunk = bytesLeft; + if (srcPhys + chunk > PS2_SCRATCHPAD_SIZE) + chunk = PS2_SCRATCHPAD_SIZE - srcPhys; + if (chunk == 0) + break; + processVIF1Data(m_scratchpad + srcPhys, chunk); + bytesLeft -= chunk; + srcPhys += chunk; + } } - else if (srcPhys < PS2_RAM_SIZE) + else { - if (srcPhys + sizeBytes > PS2_RAM_SIZE) - sizeBytes = PS2_RAM_SIZE - srcPhys; - if (sizeBytes > 0) - processVIF1Data(srcPhys, sizeBytes); + uint32_t bytesLeft = sizeBytes; + while (bytesLeft > 0) + { + if (srcPhys >= PS2_RAM_SIZE) + srcPhys = 0; + uint32_t chunk = bytesLeft; + if (srcPhys + chunk > PS2_RAM_SIZE) + chunk = PS2_RAM_SIZE - srcPhys; + if (chunk == 0) + break; + processVIF1Data(srcPhys, chunk); + bytesLeft -= chunk; + srcPhys += chunk; + } } } } @@ -1262,15 +1308,24 @@ void PS2Memory::processGIFPacket(uint32_t srcPhysAddr, uint32_t qwCount) return; const uint64_t bytes64 = static_cast(qwCount) * 16ull; uint32_t sizeBytes = (bytes64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast(bytes64); - if (srcPhysAddr >= PS2_RAM_SIZE) - return; - if (static_cast(srcPhysAddr) + static_cast(sizeBytes) > static_cast(PS2_RAM_SIZE)) - sizeBytes = PS2_RAM_SIZE - srcPhysAddr; - if (sizeBytes < 16) - return; - m_seenGifCopy = true; - m_gifCopyCount.fetch_add(1, std::memory_order_relaxed); - submitGifPacket(GifPathId::Path3, m_rdram + srcPhysAddr, sizeBytes); + uint32_t bytesLeft = sizeBytes; + while (bytesLeft >= 16) + { + if (srcPhysAddr >= PS2_RAM_SIZE) + srcPhysAddr = 0; + uint32_t chunk = bytesLeft; + if (srcPhysAddr + chunk > PS2_RAM_SIZE) + chunk = PS2_RAM_SIZE - srcPhysAddr; + if (chunk == 0) + break; + + m_seenGifCopy = true; + m_gifCopyCount.fetch_add(1, std::memory_order_relaxed); + submitGifPacket(GifPathId::Path3, m_rdram + srcPhysAddr, chunk); + + bytesLeft -= chunk; + srcPhysAddr += chunk; + } } void PS2Memory::processGIFPacket(const uint8_t *data, uint32_t sizeBytes) diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 9834840..06ecc57 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -1163,23 +1163,11 @@ void PS2Runtime::handleSyscall(uint8_t *rdram, R5900Context *ctx, uint32_t encod "This breaks the atomic basic block model and is structurally unsupported by the emulator."); } - // Try immediate first - if (encodedSyscallId != 0 && ps2_syscalls::dispatchNumericSyscall(encodedSyscallId, rdram, ctx, this)) - { - return; - } + const uint32_t syscallId = (encodedSyscallId != 0u) + ? encodedSyscallId + : getRegU32(ctx, 3); // $v1 / $3 is the EE kernel syscall number - // Try $v1 (standard) - const uint32_t syscallFromV1 = getRegU32(ctx, 3); // $v1 - if (ps2_syscalls::dispatchNumericSyscall(syscallFromV1, rdram, ctx, this)) - { - return; - } - - // Try $v0 (negative syscalls) - const uint32_t syscallFromV0 = getRegU32(ctx, 2); // $v0 (some ABIs) - if (syscallFromV0 != syscallFromV1 && - ps2_syscalls::dispatchNumericSyscall(syscallFromV0, rdram, ctx, this)) + if (ps2_syscalls::dispatchNumericSyscall(syscallId, rdram, ctx, this)) { return; } @@ -1671,6 +1659,12 @@ uint32_t PS2Runtime::guestHeapEnd() const return m_guestHeapConfigured ? m_guestHeapEnd : m_guestHeapSuggestedBase; } +uint32_t PS2Runtime::guestHeapLimit() const +{ + std::lock_guard lock(m_guestHeapMutex); + return m_guestHeapConfigured ? m_guestHeapLimit : m_guestHeapSuggestedBase; +} + uint32_t PS2Runtime::reserveAsyncCallbackStack(uint32_t size, uint32_t alignment) { if (size == 0u) diff --git a/ps2xTest/src/elf_analyzer_tests.cpp b/ps2xTest/src/elf_analyzer_tests.cpp index 64df23f..0a6dc10 100644 --- a/ps2xTest/src/elf_analyzer_tests.cpp +++ b/ps2xTest/src/elf_analyzer_tests.cpp @@ -44,6 +44,8 @@ void register_elf_analyzer_tests() "AddIntcHandler2 kernel wrapper should be classified as library/runtime"); t.IsTrue(analyzer.isLibrarySymbolNameForHeuristics("SetGsCrt"), "SetGsCrt kernel wrapper should be classified as library/runtime"); + t.IsTrue(analyzer.isLibrarySymbolNameForHeuristics("__sbprintf"), + "optional stdio internals should be classified as library functions"); t.IsFalse(analyzer.isLibrarySymbolNameForHeuristics("bhEne13_Brain"), "named game function should not be classified as library"); @@ -62,6 +64,18 @@ void register_elf_analyzer_tests() "double-underscore libm helpers should be active stubs only when the runtime knows them"); t.IsTrue(FunctionClassifier::hasRuntimeHandler("__kernel_cosf"), "runtime-known libm kernel helpers should resolve exactly"); + t.IsTrue(FunctionClassifier::hasRuntimeHandler("_realloc_r"), + "reentrant allocator aliases should resolve to runtime stubs"); + t.IsTrue(FunctionClassifier::hasRuntimeHandler("__malloc_lock"), + "newlib malloc locks should resolve to runtime stubs"); + t.IsTrue(FunctionClassifier::hasRuntimeHandler("memclr"), + "libdma memclr should resolve to a runtime stub"); + t.IsTrue(FunctionClassifier::hasRuntimeHandler("__divdi3"), + "libgcc 64-bit division should resolve to a runtime stub"); + t.IsFalse(FunctionClassifier::hasRuntimeHandler("__sbprintf"), + "optional stdio internals should not resolve as automatic runtime stubs"); + t.IsFalse(FunctionClassifier::hasRuntimeHandler("__sprint"), + "optional stdio internals should be TOML opt-in only"); t.IsFalse(FunctionClassifier::hasRuntimeHandler("scePP1_Kick"), "SDK functions without runtime handlers should not be active stubs"); }); diff --git a/ps2xTest/src/ps2_memory_tests.cpp b/ps2xTest/src/ps2_memory_tests.cpp index 8edcfc9..0e0f2fc 100644 --- a/ps2xTest/src/ps2_memory_tests.cpp +++ b/ps2xTest/src/ps2_memory_tests.cpp @@ -55,6 +55,52 @@ namespace std::memcpy(rdram + tagAddr, &tagLo, sizeof(tagLo)); } + void writeU64(uint8_t *rdram, uint32_t addr, uint64_t value) + { + std::memcpy(rdram + addr, &value, sizeof(value)); + } + + uint64_t makeGifTag(uint16_t nloop, uint8_t flg, uint8_t nreg, bool eop); + + uint64_t makeBitbltbuf(uint32_t dbp, uint32_t dbw, uint32_t dpsm) + { + return (static_cast(dbp & 0x3FFFu) << 32) | + (static_cast(dbw & 0x3Fu) << 48) | + (static_cast(dpsm & 0x3Fu) << 56); + } + + uint32_t writeGifAd(uint8_t *rdram, uint32_t addr, uint64_t value, uint64_t reg) + { + writeU64(rdram, addr + 0u, value); + writeU64(rdram, addr + 8u, reg); + return addr + 16u; + } + + uint32_t writeTextureUploadSetup(uint8_t *rdram, uint32_t addr, uint32_t dbp, uint32_t dpsm) + { + writeDmaTag(rdram, addr, makeDmaTag(5u, 1u, 0u, false)); // CNT: setup tag + four A+D writes. + addr += 16u; + writeU64(rdram, addr + 0u, makeGifTag(4u, GIF_FMT_PACKED, 1u, false)); + writeU64(rdram, addr + 8u, 0x0Eull); // GIF PACKED A+D descriptor. + addr += 16u; + addr = writeGifAd(rdram, addr, makeBitbltbuf(dbp, 1u, dpsm), GS_REG_BITBLTBUF); + addr = writeGifAd(rdram, addr, 0ull, GS_REG_TRXPOS); + addr = writeGifAd(rdram, addr, (16ull << 0) | (16ull << 32), GS_REG_TRXREG); + addr = writeGifAd(rdram, addr, 0ull, GS_REG_TRXDIR); + return addr; + } + + uint32_t writeTextureImageRef(uint8_t *rdram, uint32_t addr, uint32_t qwc, uint32_t dataAddr) + { + writeDmaTag(rdram, addr, makeDmaTag(1u, 1u, 0u, false)); // CNT: GIF IMAGE tag. + addr += 16u; + writeU64(rdram, addr + 0u, makeGifTag(static_cast(qwc), GIF_FMT_IMAGE, 0u, false)); + writeU64(rdram, addr + 8u, 0ull); + addr += 16u; + writeDmaTag(rdram, addr, makeDmaTag(static_cast(qwc), 3u, dataAddr, false)); // REF: image payload. + return addr + 16u; + } + uint64_t makeGifTag(uint16_t nloop, uint8_t flg, uint8_t nreg, bool eop = true) { uint64_t tag = static_cast(nloop & 0x7FFFu); @@ -861,6 +907,112 @@ void register_ps2_memory_tests() t.IsTrue(contentOk, "scratchpad alias chain payload should match scratchpad bytes"); }); + tc.Run("GIF DMA chain REF keeps CT32 image data after paletted upload", [](TestCase &t) + { + PS2Memory mem; + t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed"); + + GS gs; + gs.init(mem.getGSVRAM(), static_cast(PS2_GS_VRAM_SIZE), &mem.gs()); + std::vector capturedGifPacket; + mem.setGifPacketCallback([&](const uint8_t *data, uint32_t sizeBytes) + { + capturedGifPacket.assign(data, data + sizeBytes); + gs.processGIFPacket(data, sizeBytes); + }); + + constexpr uint32_t kGifCh = 0x1000A000u; + constexpr uint32_t kChain = 0x00028000u; + constexpr uint32_t kT4Data = 0x00029000u; + constexpr uint32_t kCt32Data = 0x0002A000u; + constexpr uint32_t kT4Dbp = 0u; + constexpr uint32_t kCt32Dbp = 32u; + constexpr uint32_t kWidth = 16u; + constexpr uint32_t kHeight = 16u; + constexpr uint32_t kT4Bytes = kWidth * kHeight / 2u; + constexpr uint32_t kCt32Bytes = kWidth * kHeight * 4u; + + uint8_t *rdram = mem.getRDRAM(); + uint32_t pixel = 0u; + for (uint32_t i = 0; i < kT4Bytes; ++i) + { + const uint8_t lo = static_cast(pixel & 0xFu); + const uint8_t hi = static_cast((pixel + 1u) & 0xFu); + rdram[kT4Data + i] = static_cast(lo | (hi << 4)); + pixel += 2u; + if (pixel > 0xEu) + { + pixel -= 0xEu; + } + } + + uint32_t color = 0u; + for (uint32_t i = 0; i < kCt32Bytes; i += 4u) + { + rdram[kCt32Data + i + 0u] = static_cast((color >> 0) & 0xFFu); + rdram[kCt32Data + i + 1u] = static_cast((color >> 8) & 0xFFu); + rdram[kCt32Data + i + 2u] = static_cast((color >> 16) & 0xFFu); + rdram[kCt32Data + i + 3u] = 0x80u; + color += 0xF1u; + if (color >= 0xFFFFFFu) + { + color = 0u; + } + } + + uint32_t chain = kChain; + chain = writeTextureUploadSetup(rdram, chain, kT4Dbp, GS_PSM_T4HL); + chain = writeTextureImageRef(rdram, chain, kT4Bytes / 16u, kT4Data); + chain = writeTextureUploadSetup(rdram, chain, kCt32Dbp, GS_PSM_CT32); + chain = writeTextureImageRef(rdram, chain, kCt32Bytes / 16u, kCt32Data); + writeDmaTag(rdram, chain, makeDmaTag(0u, 7u, 0u, false)); // END. + + t.IsTrue(mem.writeIORegister(kGifCh + 0x30u, kChain), "write GIF TADR should succeed"); + t.IsTrue(mem.writeIORegister(kGifCh + 0x00u, 0x104u), "write GIF CHCR STR|CHAIN should succeed"); + + mem.processPendingTransfers(); + + constexpr uint32_t kCt32PayloadOffset = 5u * 16u + 16u + kT4Bytes + 5u * 16u + 16u; + t.IsTrue(capturedGifPacket.size() >= kCt32PayloadOffset + kCt32Bytes, + "flattened GIF chain should contain the full CT32 REF payload"); + bool ct32PayloadOk = capturedGifPacket.size() >= kCt32PayloadOffset + kCt32Bytes; + for (uint32_t i = 0; i < kCt32Bytes && ct32PayloadOk; ++i) + { + if (capturedGifPacket[kCt32PayloadOffset + i] != rdram[kCt32Data + i]) + { + ct32PayloadOk = false; + break; + } + } + t.IsTrue(ct32PayloadOk, "flattened GIF chain should preserve CT32 REF bytes after the T4 REF payload"); + + const uint32_t row1Off = GSPSMCT32::addrPSMCT32(kCt32Dbp, 1u, 0u, 1u); + uint32_t actualRow1X0 = 0u; + uint32_t expectedRow1X0 = 0u; + std::memcpy(&actualRow1X0, mem.getGSVRAM() + row1Off, sizeof(actualRow1X0)); + std::memcpy(&expectedRow1X0, rdram + kCt32Data + kWidth * 4u, sizeof(expectedRow1X0)); + t.Equals(actualRow1X0, expectedRow1X0, "CT32 row 1 must come from CT32 data, not the previous T4 REF payload"); + + bool ct32Ok = true; + for (uint32_t y = 0; y < kHeight && ct32Ok; ++y) + { + for (uint32_t x = 0; x < kWidth && ct32Ok; ++x) + { + const uint32_t dstOff = GSPSMCT32::addrPSMCT32(kCt32Dbp, 1u, x, y); + const uint32_t srcOff = kCt32Data + ((y * kWidth + x) * 4u); + for (uint32_t c = 0; c < 4u; ++c) + { + if (mem.getGSVRAM()[dstOff + c] != rdram[srcOff + c]) + { + ct32Ok = false; + break; + } + } + } + } + t.IsTrue(ct32Ok, "all CT32 pixels should survive a preceding paletted upload in the same GIF DMA chain"); + }); + tc.Run("VIF1 DMA DIRECT forwards payload to GIF callback and clears channel", [](TestCase &t) { PS2Memory mem; diff --git a/ps2xTest/src/ps2_recompiler_tests.cpp b/ps2xTest/src/ps2_recompiler_tests.cpp index de6c29b..3980bde 100644 --- a/ps2xTest/src/ps2_recompiler_tests.cpp +++ b/ps2xTest/src/ps2_recompiler_tests.cpp @@ -864,6 +864,30 @@ void register_ps2_recompiler_tests() "isceSifSetDma should resolve as a stub name"); t.Equals(ps2_runtime_calls::resolveStubName("isceSifSetDChain"), std::string_view{"isceSifSetDChain"}, "isceSifSetDChain should resolve as a stub name"); + t.Equals(ps2_runtime_calls::resolveStubName("memalign"), std::string_view{"memalign"}, + "memalign should resolve as a stub name"); + t.Equals(ps2_runtime_calls::resolveStubName("_memalign_r"), std::string_view{"memalign_r"}, + "_memalign_r should resolve to the memalign_r stub"); + t.Equals(ps2_runtime_calls::resolveStubName("_realloc_r"), std::string_view{"realloc_r"}, + "_realloc_r should resolve to the realloc_r stub"); + t.Equals(ps2_runtime_calls::resolveStubName("malloc_extend_top"), std::string_view{"malloc_extend_top"}, + "malloc_extend_top should resolve as an allocator compatibility stub"); + t.Equals(ps2_runtime_calls::resolveStubName("__malloc_lock"), std::string_view{"__malloc_lock"}, + "__malloc_lock should resolve as an allocator compatibility stub"); + t.Equals(ps2_runtime_calls::resolveStubName("__malloc_unlock"), std::string_view{"__malloc_unlock"}, + "__malloc_unlock should resolve as an allocator compatibility stub"); + t.Equals(ps2_runtime_calls::resolveStubName("memclr"), std::string_view{"memclr"}, + "memclr should resolve as a runtime stub"); + t.Equals(ps2_runtime_calls::resolveStubName("__divdi3"), std::string_view{"__divdi3"}, + "__divdi3 should resolve as a runtime stub"); + t.Equals(ps2_runtime_calls::resolveStubName("__mcmp"), std::string_view{}, + "__mcmp should be left for recompilation"); + t.Equals(ps2_runtime_calls::resolveStubName("__sprint"), std::string_view{}, + "__sprint should be left for recompilation"); + t.Equals(ps2_runtime_calls::resolveStubName("__sprint_r"), std::string_view{}, + "__sprint_r should be left for recompilation"); + t.Equals(ps2_runtime_calls::resolveStubName("__sbprintf"), std::string_view{}, + "__sbprintf should be left for recompilation"); }); tc.Run("respect max length for .cpp filenames", [](TestCase& t) { diff --git a/ps2xTest/src/ps2_runtime_kernel_tests.cpp b/ps2xTest/src/ps2_runtime_kernel_tests.cpp index 7dbe672..ef8c713 100644 --- a/ps2xTest/src/ps2_runtime_kernel_tests.cpp +++ b/ps2xTest/src/ps2_runtime_kernel_tests.cpp @@ -2,6 +2,7 @@ #include "ps2_runtime.h" #include "ps2_runtime_macros.h" #include "ps2_syscalls.h" +#include "ps2_stubs.h" #include #include @@ -558,7 +559,7 @@ void register_ps2_runtime_kernel_tests() t.Equals(getRegS32(env.ctx, 2), KE_OK, "DeleteThread should clean up the waiter thread"); }); - tc.Run("setup heap and allocator primitives track end-of-heap", [](TestCase &t) + tc.Run("setup heap configures limits and EndOfHeap reports the limit", [](TestCase &t) { TestEnv env; @@ -569,17 +570,13 @@ void register_ps2_runtime_kernel_tests() t.Equals(heapBase, 0x00180010u, "SetupHeap should return configured base"); t.IsTrue(callSyscall(0x3Eu, env.rdram.data(), &env.ctx, &env.runtime), "EndOfHeap syscall should dispatch"); - const uint32_t heapEndBefore = static_cast(getRegS32(env.ctx, 2)); - t.Equals(heapEndBefore, heapBase, "EndOfHeap should start at heap base before allocation"); + const uint32_t heapLimit = static_cast(getRegS32(env.ctx, 2)); + t.Equals(heapLimit, 0x00181010u, "EndOfHeap should report the upper limit of the configured heap"); const uint32_t alignedAlloc = env.runtime.guestMalloc(0x20u, 64u); t.IsTrue(alignedAlloc != 0u, "guestMalloc should allocate inside configured heap"); t.Equals(alignedAlloc & 0x3Fu, 0u, "guestMalloc should honor 64-byte alignment"); - t.IsTrue(callSyscall(0x3Eu, env.rdram.data(), &env.ctx, &env.runtime), "EndOfHeap syscall should dispatch"); - const uint32_t heapEndAfter = static_cast(getRegS32(env.ctx, 2)); - t.IsTrue(heapEndAfter >= alignedAlloc + 0x20u, "EndOfHeap should advance after allocation"); - env.runtime.guestFree(alignedAlloc); const uint32_t a = env.runtime.guestMalloc(0x100u, 16u); @@ -595,6 +592,84 @@ void register_ps2_runtime_kernel_tests() t.Equals(reused, heapBase, "guestFree should make the head block reusable"); }); + tc.Run("memalign stubs allocate aligned guest memory", [](TestCase &t) + { + TestEnv env; + + env.runtime.configureGuestHeap(0x00180010u, 0x00182010u); + + setRegU32(env.ctx, 4, 128u); + setRegU32(env.ctx, 5, 0x40u); + ps2_stubs::memalign(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t direct = ::getRegU32(&env.ctx, 2); + t.IsTrue(direct != 0u, "memalign should return a guest address"); + t.Equals(direct & 0x7Fu, 0u, "memalign should honor 128-byte alignment"); + + setRegU32(env.ctx, 5, 64u); + setRegU32(env.ctx, 6, 0x40u); + ps2_stubs::memalign_r(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t reent = ::getRegU32(&env.ctx, 2); + t.IsTrue(reent != 0u, "_memalign_r should return a guest address"); + t.Equals(reent & 0x3Fu, 0u, "_memalign_r should honor 64-byte alignment"); + t.IsTrue(reent != direct, "_memalign_r should allocate a distinct block"); + }); + + tc.Run("allocator compatibility stubs use the runtime guest heap", [](TestCase &t) + { + TestEnv env; + + env.runtime.configureGuestHeap(0x00180010u, 0x00183010u); + + setRegU32(env.ctx, 5, 0x20u); + ps2_stubs::malloc_r(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t initial = ::getRegU32(&env.ctx, 2); + t.IsTrue(initial != 0u, "_malloc_r should allocate guest memory"); + + writeGuestU32(env.rdram.data(), initial, 0xAABBCCDDu); + + setRegU32(env.ctx, 5, initial); + setRegU32(env.ctx, 6, 0x80u); + ps2_stubs::realloc_r(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t grown = ::getRegU32(&env.ctx, 2); + t.IsTrue(grown != 0u, "_realloc_r should return a guest block"); + t.Equals(readGuestU32(env.rdram.data(), grown), 0xAABBCCDDu, + "_realloc_r should preserve existing guest bytes"); + + setRegU32(env.ctx, 5, grown); + ps2_stubs::free_r(env.rdram.data(), &env.ctx, &env.runtime); + + setRegU32(env.ctx, 5, 0x100u); + ps2_stubs::malloc_extend_top(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(::getRegU32(&env.ctx, 2), 0u, + "malloc_extend_top should be a safe runtime-owned heap no-op"); + + ps2_stubs::__malloc_lock(env.rdram.data(), &env.ctx, &env.runtime); + ps2_stubs::__malloc_unlock(env.rdram.data(), &env.ctx, &env.runtime); + }); + + tc.Run("libc helper stubs cover memclr and libgcc div", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kBuf = 0x5000u; + std::memset(env.rdram.data() + kBuf, 0xCD, 16u); + setRegU32(env.ctx, 4, kBuf); + setRegU32(env.ctx, 5, 12u); + ps2_stubs::memclr(env.rdram.data(), &env.ctx, &env.runtime); + for (uint32_t i = 0; i < 12u; ++i) + { + t.Equals(env.rdram[kBuf + i], static_cast(0), + "memclr should zero the requested byte range"); + } + t.Equals(env.rdram[kBuf + 12u], static_cast(0xCD), + "memclr should not write past the requested byte range"); + + SET_GPR_S64(&env.ctx, 4, -9); + SET_GPR_S64(&env.ctx, 5, 2); + ps2_stubs::__divdi3(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), -4, "__divdi3 should divide signed 64-bit values"); + }); + tc.Run("ReleaseAlarm aliases CancelAlarm and cache toggles succeed", [](TestCase &t) { TestEnv env; @@ -667,6 +742,56 @@ void register_ps2_runtime_kernel_tests() t.Equals(setupSp & 0xFu, 0u, "SetupThread should always return a 16-byte aligned stack pointer"); }); + tc.Run("OSD config2 syscalls round-trip extended config", [](TestCase &t) + { + TestEnv env; + constexpr uint32_t kConfig2Addr = 0x00005000u; + constexpr uint32_t kConfig2OutAddr = 0x00005010u; + constexpr uint32_t kConfig1OutAddr = 0x00005020u; + constexpr uint32_t kInitialConfig1 = + (1u << 0) | // SPDIF disabled + (1u << 4) | // non-Japanese language flag + (1u << 13) | // OSD2 + (1u << 16); // English + constexpr uint32_t kConfig2Raw = + 0xABu | // format + (0xB0u << 8) | // daylightSaving=1, timeFormat=1, dateFormat=2 + (2u << 16) | // extended OSD version + (10u << 24); // traditional Chinese + + writeGuestU32(env.rdram.data(), K_PARAM_ADDR, kInitialConfig1); + setRegU32(env.ctx, 4, K_PARAM_ADDR); + t.IsTrue(callSyscall(0x4Au, env.rdram.data(), &env.ctx, &env.runtime), + "SetOsdConfigParam syscall should dispatch"); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SetOsdConfigParam should seed base OSD state"); + + writeGuestU32(env.rdram.data(), kConfig2Addr, kConfig2Raw); + setRegU32(env.ctx, 4, kConfig2Addr); + setRegU32(env.ctx, 5, 4u); + setRegU32(env.ctx, 6, 0u); + t.IsTrue(callSyscall(0x6Eu, env.rdram.data(), &env.ctx, &env.runtime), + "SetOsdConfigParam2 syscall should dispatch"); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SetOsdConfigParam2 should succeed"); + + writeGuestU32(env.rdram.data(), kConfig2OutAddr, 0xFFFFFFFFu); + setRegU32(env.ctx, 4, kConfig2OutAddr); + setRegU32(env.ctx, 5, 4u); + setRegU32(env.ctx, 6, 0u); + t.IsTrue(callSyscall(0x6Fu, env.rdram.data(), &env.ctx, &env.runtime), + "GetOsdConfigParam2 syscall should dispatch"); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "GetOsdConfigParam2 should succeed"); + const uint32_t readConfig2 = readGuestU32(env.rdram.data(), kConfig2OutAddr); + t.Equals(readConfig2, kConfig2Raw, "GetOsdConfigParam2 should round-trip the sanitized Config2Param bytes"); + t.Equals((readConfig2 >> 12) & 1u, 1u, "Config2 daylightSaving should live at bit 12 for libosd callers"); + + setRegU32(env.ctx, 4, kConfig1OutAddr); + t.IsTrue(callSyscall(0x4Bu, env.rdram.data(), &env.ctx, &env.runtime), + "GetOsdConfigParam syscall should dispatch after Config2 update"); + const uint32_t readConfig1 = readGuestU32(env.rdram.data(), kConfig1OutAddr); + t.Equals((readConfig1 >> 13) & 0x7u, 2u, "SetOsdConfigParam2 should sync ConfigParam.version"); + t.Equals((readConfig1 >> 16) & 0x1Fu, 10u, "SetOsdConfigParam2 should sync ConfigParam.language"); + }); + tc.Run("numeric syscall 0x83 finds matching table entry", [](TestCase &t) { TestEnv env; @@ -965,5 +1090,59 @@ void register_ps2_runtime_kernel_tests() notifyRuntimeStop(); }); + + tc.Run("Copy syscall (0x5A) performs a memory copy", [](TestCase &t) + { + TestEnv env; + constexpr uint32_t kDestAddr = 0x00005000u; + constexpr uint32_t kSrcAddr = 0x00006000u; + constexpr uint32_t kSize = 16u; + constexpr uint32_t kValues[] = { + 0x11223344u, + 0x55667788u, + 0x99AABBCCu, + 0xDDEEFF00u + }; + + writeGuestWords(env.rdram.data(), kSrcAddr, kValues, std::size(kValues)); + + setRegU32(env.ctx, 4, kDestAddr); + setRegU32(env.ctx, 5, kSrcAddr); + setRegU32(env.ctx, 6, kSize); + + t.IsTrue(callSyscall(0x5Au, env.rdram.data(), &env.ctx, &env.runtime), + "Copy syscall should dispatch"); + + for (size_t i = 0; i < std::size(kValues); ++i) + { + uint32_t destVal = readGuestU32(env.rdram.data(), kDestAddr + static_cast(i * sizeof(uint32_t))); + t.Equals(destVal, kValues[i], "Copy should correctly transfer bytes"); + } + }); + + tc.Run("GetEntryAddress syscall (0x5B) returns handler from guest table", [](TestCase &t) + { + notifyRuntimeStop(); + TestEnv env; + initializeGuestKernelState(env.rdram.data()); + + constexpr uint32_t kGuestSyscallTableGuestBase = 0x80011F80u; + constexpr uint32_t kSyscallIndex = 0x5Au; + constexpr uint32_t kExpectedHandler = 0x00383548u; + constexpr uint32_t kEntryPhysAddr = (kGuestSyscallTableGuestBase + (kSyscallIndex * 4u)) & 0x1FFFFFFFu; + + writeGuestU32(env.rdram.data(), kEntryPhysAddr, kExpectedHandler); + + setRegU32(env.ctx, 4, kSyscallIndex); + + t.IsTrue(callSyscall(0x5Bu, env.rdram.data(), &env.ctx, &env.runtime), + "GetEntryAddress syscall should dispatch"); + + t.Equals(static_cast(getRegS32(env.ctx, 2)), + kExpectedHandler, + "GetEntryAddress should read and return the handler address from the table"); + + notifyRuntimeStop(); + }); }); }