From 1c941424de30238b3c2eb4fed210c57a448a4af2 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:22:34 -0300 Subject: [PATCH 01/30] feat: remove memory and pad from stub section --- ps2xAnalyzer/src/elf_analyzer.cpp | 19 +++++++++++++++++-- .../tools/ghidra/ExportPS2Functions.java | 16 ++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/ps2xAnalyzer/src/elf_analyzer.cpp b/ps2xAnalyzer/src/elf_analyzer.cpp index 245322f3..1b7cd847 100644 --- a/ps2xAnalyzer/src/elf_analyzer.cpp +++ b/ps2xAnalyzer/src/elf_analyzer.cpp @@ -24,6 +24,7 @@ namespace ps2recomp static bool hasPs2ApiPrefix(const std::string &name); static bool hasReliableSymbolName(const std::string &name); static bool isDoNotSkipOrStub(const std::string &name); + static bool isKnownLocalHelperName(const std::string &name); static bool matchesKernelRuntimeName(const std::string &name); static uint32_t decodeAbsoluteJumpTarget(uint32_t instructionAddress, uint32_t targetField); static bool tryReadWord(const ElfParser *parser, uint32_t address, uint32_t &outWord); @@ -313,7 +314,7 @@ namespace ps2recomp "malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign", // Memory manipulation - "memcpy", "memset", "memmove", "memcmp", "memcpy2", "memchr", "bcopy", "bzero", + "memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero", // String manipulation "strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr", @@ -2020,7 +2021,6 @@ namespace ps2recomp const std::vector libraryPrefixes = { "sce", "Sce", "SCE", // Sony prefixes "sif", "Sif", "SIF", // SIF functions - "pad", "Pad", "PAD", // Pad functions "gs", "Gs", "GS", // Graphics Synthesizer "dma", "Dma", "DMA", // DMA functions "iop", "Iop", "IOP", // IOP functions @@ -2068,6 +2068,15 @@ namespace ps2recomp return kDoNotSkipOrStub.contains(name); } + static bool isKnownLocalHelperName(const std::string &name) + { + static const std::unordered_set kKnownLocalHelpers = { + "memcpy2", + "_memcpy2"}; + + return kKnownLocalHelpers.contains(name); + } + static bool hasReliableSymbolName(const std::string &name) { if (name.empty()) @@ -2182,12 +2191,18 @@ namespace ps2recomp if (!hasReliableSymbolName(name)) return false; + if (isKnownLocalHelperName(name)) + return false; + std::string normalizedName = name; if (normalizedName[0] == '_' && normalizedName.size() > 1) { normalizedName = normalizedName.substr(1); } + if (isKnownLocalHelperName(normalizedName)) + return false; + if (matchesKernelRuntimeName(normalizedName)) return true; diff --git a/ps2xRecomp/tools/ghidra/ExportPS2Functions.java b/ps2xRecomp/tools/ghidra/ExportPS2Functions.java index 4f2987b1..4b107665 100644 --- a/ps2xRecomp/tools/ghidra/ExportPS2Functions.java +++ b/ps2xRecomp/tools/ghidra/ExportPS2Functions.java @@ -52,8 +52,13 @@ public class ExportPS2Functions extends GhidraScript { "cmd_sem_init" )); + private static final Set KNOWN_LOCAL_HELPER_NAMES = new HashSet<>(Arrays.asList( + "memcpy2", + "_memcpy2" + )); + private static final Set PS2_API_PREFIXES = new HashSet<>(Arrays.asList( - "sce", "sif", "pad", "gs", "dma", "iop", "vif", "spu", "mc", "libc" + "sce", "sif", "gs", "dma", "iop", "vif", "spu", "mc", "libc" )); private static final Set KNOWN_STDLIB_NAMES = new HashSet<>(Arrays.asList( @@ -61,7 +66,7 @@ public class ExportPS2Functions extends GhidraScript { "puts", "putchar", "getchar", "gets", "fgets", "fputs", "scanf", "fscanf", "sscanf", "sprint", "sbprintf", "malloc", "free", "calloc", "realloc", "aligned_alloc", "posix_memalign", - "memcpy", "memset", "memmove", "memcmp", "memcpy2", "memchr", "bcopy", "bzero", + "memcpy", "memset", "memmove", "memcmp", "memchr", "bcopy", "bzero", "strcpy", "strncpy", "strcat", "strncat", "strcmp", "strncmp", "strlen", "strstr", "strchr", "strrchr", "strdup", "strtok", "strtok_r", "strerror", "fopen", "fclose", "fread", "fwrite", "fseek", "ftell", "rewind", "fflush", @@ -237,7 +242,14 @@ private static boolean isLibraryFunctionName(String name) { return false; } + if (KNOWN_LOCAL_HELPER_NAMES.contains(name)) { + return false; + } + String normalized = normalizeOptionalLeadingUnderscore(name); + if (KNOWN_LOCAL_HELPER_NAMES.contains(normalized)) { + return false; + } if (KERNEL_RUNTIME_NAME_PATTERN.matcher(normalized).matches()) { return true; } From b3940782afa8ba40457fb087637336295486d819 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:26:14 -0300 Subject: [PATCH 02/30] feat: add support for resume entry targets in CodeGenerator (this allow jumps in address outside function) feat: refactor entry point discovery one more try to reduce big generated file --- ps2xRecomp/include/ps2recomp/code_generator.h | 7 +- ps2xRecomp/include/ps2recomp/ps2_recompiler.h | 1 + ps2xRecomp/src/lib/code_generator.cpp | 161 +++++++++- ps2xRecomp/src/lib/ps2_recompiler.cpp | 304 ++++++++++++------ 4 files changed, 371 insertions(+), 102 deletions(-) diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 04adfdea..e5ce9e13 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -37,6 +37,8 @@ namespace ps2recomp struct AnalysisResult { std::unordered_set entryPoints; + std::unordered_set externalEntryPoints; + std::unordered_set resumeEntryPoints; std::unordered_map> jumpTableTargets; }; @@ -49,15 +51,18 @@ namespace ps2recomp void setBootstrapInfo(const BootstrapInfo &info); void setRelocationCallNames(const std::unordered_map &callNames); void setConfiguredJumpTables(const std::vector &jumpTables); + void setResumeEntryTargets(const std::unordered_map> &resumeTargetsByOwner); AnalysisResult collectInternalBranchTargets(const Function &function, - const std::vector &instructions); + const std::vector &instructions, + const std::vector *allFunctions = nullptr); public: std::unordered_map m_symbols; std::unordered_map m_renamedFunctions; std::unordered_map m_relocationCallNames; std::unordered_map> m_configJumpTableTargetsByAddress; + std::unordered_map> m_resumeEntryTargetsByOwner; const std::vector
& m_sections; BootstrapInfo m_bootstrapInfo; diff --git a/ps2xRecomp/include/ps2recomp/ps2_recompiler.h b/ps2xRecomp/include/ps2recomp/ps2_recompiler.h index 10c9f5c2..5740de13 100644 --- a/ps2xRecomp/include/ps2recomp/ps2_recompiler.h +++ b/ps2xRecomp/include/ps2recomp/ps2_recompiler.h @@ -62,6 +62,7 @@ namespace ps2recomp std::unordered_map m_stubHandlerBindingsByStart; std::map m_generatedStubs; std::unordered_map m_functionRenames; + std::unordered_map> m_resumeEntryTargetsByOwner; CodeGenerator::BootstrapInfo m_bootstrapInfo; bool decodeFunction(Function &function); diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 666a59e7..5f6cacaf 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -153,6 +153,17 @@ namespace ps2recomp } } + void CodeGenerator::setResumeEntryTargets(const std::unordered_map> &resumeTargetsByOwner) + { + m_resumeEntryTargetsByOwner = resumeTargetsByOwner; + for (auto &[owner, targets] : m_resumeEntryTargetsByOwner) + { + (void)owner; + std::sort(targets.begin(), targets.end()); + targets.erase(std::unique(targets.begin(), targets.end()), targets.end()); + } + } + std::string CodeGenerator::getFunctionName(uint32_t address) const { auto it = m_renamedFunctions.find(address); @@ -652,7 +663,7 @@ namespace ps2recomp CodeGenerator::~CodeGenerator() = default; CodeGenerator::AnalysisResult CodeGenerator::collectInternalBranchTargets( - const Function &function, const std::vector &instructions) + const Function &function, const std::vector &instructions, const std::vector *allFunctions) { AnalysisResult result; std::unordered_set instructionAddresses; @@ -660,6 +671,87 @@ namespace ps2recomp bool hasIndirectRegisterJump = false; std::vector indirectJumps; + auto isExecutableAddress = [&](uint32_t address) -> bool + { + for (const auto §ion : m_sections) + { + if (!section.isCode) + { + continue; + } + if (address >= section.address && address < (section.address + section.size)) + { + return true; + } + } + return false; + }; + + auto findContainingExternalFunction = [&](uint32_t address) -> const Function * + { + if (!allFunctions || !isExecutableAddress(address)) + { + return nullptr; + } + + const Function *best = nullptr; + for (const auto &candidateFn : *allFunctions) + { + if (!candidateFn.isRecompiled || candidateFn.isStub || candidateFn.isSkipped) + { + continue; + } + + if (candidateFn.name.rfind("entry_", 0) == 0) + { + continue; + } + + if (address < candidateFn.start || address >= candidateFn.end) + { + continue; + } + + if (!best || candidateFn.start > best->start) + { + best = &candidateFn; + } + } + + return best; + }; + + auto queueExternalEntryTarget = [&](uint32_t target) + { + const Function *containingFn = findContainingExternalFunction(target); + if (!containingFn) + { + return; + } + + if (containingFn->start == function.start) + { + return; + } + + if (target == containingFn->start) + { + return; + } + + result.externalEntryPoints.insert(target); + }; + + auto queueResumeEntryTarget = [&](uint32_t resumeAddr) + { + if (resumeAddr >= function.start && resumeAddr < function.end && + instructionAddresses.contains(resumeAddr)) + { + result.entryPoints.insert(resumeAddr); + result.resumeEntryPoints.insert(resumeAddr); + } + }; + for (const auto &inst : instructions) { instructionAddresses.insert(inst.address); @@ -686,6 +778,10 @@ namespace ps2recomp { result.entryPoints.insert(target); } + else + { + queueExternalEntryTarget(target); + } } else if (isStaticJump) { @@ -697,12 +793,16 @@ namespace ps2recomp if (inst.opcode == OPCODE_JAL) { - uint32_t returnAddr = inst.address + 8; - if (returnAddr >= function.start && returnAddr < function.end && - instructionAddresses.contains(returnAddr)) - { - result.entryPoints.insert(returnAddr); - } + queueResumeEntryTarget(inst.address + 8u); + } + } + else + { + queueExternalEntryTarget(target); + + if (inst.opcode == OPCODE_JAL) + { + queueResumeEntryTarget(inst.address + 8u); } } } @@ -712,6 +812,11 @@ namespace ps2recomp { bool needsJrFallback = false; for (const Instruction* jrInst : indirectJumps) { + if (jrInst->function == SPECIAL_JALR) + { + queueResumeEntryTarget(jrInst->address + 8u); + } + bool foundTable = false; uint32_t jrReg = jrInst->rs; @@ -790,6 +895,10 @@ namespace ps2recomp { jrTargets.push_back(target); } + else + { + queueExternalEntryTarget(target); + } } if (!jrTargets.empty()) @@ -849,6 +958,10 @@ namespace ps2recomp uniqueTargets.insert(target); } } + else + { + queueExternalEntryTarget(target); + } } else { validJumpTable = false; break; @@ -909,6 +1022,15 @@ namespace ps2recomp } AnalysisResult analysisResult = collectInternalBranchTargets(function, instructions); + auto resumeIt = m_resumeEntryTargetsByOwner.find(function.start); + if (resumeIt != m_resumeEntryTargetsByOwner.end()) + { + for (uint32_t target : resumeIt->second) + { + analysisResult.entryPoints.insert(target); + } + } + const std::unordered_set& internalTargets = analysisResult.entryPoints; ss << "// Function: " << function.name << "\n"; ss << "// Address: 0x" << std::hex << function.start << " - 0x" << function.end << std::dec << "\n"; @@ -926,6 +1048,16 @@ namespace ps2recomp ss << " PS_LOG_ENTRY(\"" << sanitizedName << "\");\n"; ss << "#endif\n"; ss << "\n"; + if (resumeIt != m_resumeEntryTargetsByOwner.end() && !resumeIt->second.empty()) + { + ss << " switch (ctx->pc) {\n"; + for (uint32_t target : resumeIt->second) + { + ss << " case 0x" << std::hex << target << "u: goto label_" << target << ";\n" << std::dec; + } + ss << " default: break;\n"; + ss << " }\n\n"; + } ss << " ctx->pc = 0x" << std::hex << function.start << "u;\n" << std::dec; ss << "\n"; @@ -3782,6 +3914,21 @@ namespace ps2recomp emitRegistration(first, second); } + ss << "\n // Register resumable entry points\n"; + for (const auto &[ownerStart, targets] : m_resumeEntryTargetsByOwner) + { + const std::string ownerName = getFunctionName(ownerStart); + if (ownerName.empty()) + { + continue; + } + + for (uint32_t target : targets) + { + emitRegistration(target, ownerName); + } + } + ss << "\n // Register stub functions\n"; for (const auto &[first, second] : stubFunctions) { diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index 2ea7d54c..42b212ab 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -241,16 +241,60 @@ namespace ps2recomp size_t passCount = 0; }; - struct StaticEntryTarget + bool isEntryFunctionName(const std::string &name) { - uint32_t target = 0u; - bool isCall = false; - }; + return name.rfind("entry_", 0) == 0; + } + + void removeStaleGeneratedOutputs(const fs::path &outputDir) + { + if (!fs::exists(outputDir) || !fs::is_directory(outputDir)) + { + return; + } + + for (const auto &entry : fs::directory_iterator(outputDir)) + { + if (!entry.is_regular_file()) + { + continue; + } + + const fs::path &path = entry.path(); + const std::string filename = path.filename().string(); + const std::string extension = path.extension().string(); + + const bool isPerFunctionSource = + extension == ".cpp" && + (filename.find("_0x") != std::string::npos || + filename.rfind("entry_", 0) == 0); + const bool isAggregateSource = + filename == "ps2_recompiled_functions.cpp" || + filename == "register_functions.cpp"; + const bool isGeneratedHeader = + filename == "ps2_recompiled_functions.h" || + filename == "ps2_recompiled_stubs.h"; + + if (!isPerFunctionSource && !isAggregateSource && !isGeneratedHeader) + { + continue; + } + + std::error_code removeError; + fs::remove(path, removeError); + if (removeError) + { + std::cerr << "Warning: failed to remove stale generated file " + << path << ": " << removeError.message() << std::endl; + } + } + } EntryDiscoveryStats discoverAdditionalEntryPointsImpl( std::vector &functions, std::unordered_map> &decodedFunctions, const std::vector
§ions, + CodeGenerator *codeGenerator, const std::function &decodeExternalFunction) { std::unordered_set existingStarts; @@ -275,25 +319,6 @@ namespace ps2recomp return false; }; - auto getStaticEntryTarget = [](const Instruction &inst) -> std::optional - { - if (inst.opcode == OPCODE_J || inst.opcode == OPCODE_JAL) - { - StaticEntryTarget target{}; - target.target = decodeAbsoluteJumpTarget(inst.address, inst.target); - target.isCall = (inst.opcode == OPCODE_JAL); - return target; - } - - if (inst.opcode == OPCODE_SPECIAL && - (inst.function == SPECIAL_JR || inst.function == SPECIAL_JALR)) - { - return std::nullopt; - } - - return std::nullopt; - }; - auto isSimpleReturnThunkStart = [](const Instruction &inst) -> bool { return inst.opcode == OPCODE_SPECIAL && @@ -355,6 +380,30 @@ namespace ps2recomp std::vector newEntries; std::unordered_set pendingStarts; + auto queuePendingEntry = [&](uint32_t target) + { + if (!isExecutableAddress(target)) + { + return; + } + + if (existingStarts.contains(target) || pendingStarts.contains(target)) + { + return; + } + + PendingEntry pending{}; + pending.target = target; + if (const Function *containingFunction = findContainingFunction(target)) + { + pending.containingStart = containingFunction->start; + pending.containingEnd = containingFunction->end; + } + + pendingEntries.push_back(pending); + pendingStarts.insert(target); + }; + for (const auto &function : functions) { if (!function.isRecompiled || function.isStub || function.isSkipped) @@ -362,6 +411,11 @@ namespace ps2recomp continue; } + if (isEntryFunctionName(function.name)) + { + continue; + } + auto decodedIt = decodedFunctions.find(function.start); if (decodedIt == decodedFunctions.end()) { @@ -369,60 +423,26 @@ namespace ps2recomp } const auto &instructions = decodedIt->second; - - for (const auto &inst : instructions) + CodeGenerator::AnalysisResult analysisResult{}; + if (codeGenerator) { - auto targetOpt = getStaticEntryTarget(inst); - if (!targetOpt.has_value()) - { - continue; - } - - const StaticEntryTarget staticTarget = targetOpt.value(); - const uint32_t target = staticTarget.target; - - if ((target & 0x3) != 0 || !isExecutableAddress(target)) - { - continue; - } - - if (existingStarts.contains(target) || pendingStarts.contains(target)) - { - continue; - } - - const bool targetInCurrentFunction = std::any_of( - instructions.begin(), instructions.end(), - [&](const Instruction &candidate) - { return candidate.address == target; }); - if (targetInCurrentFunction && !staticTarget.isCall) - { - // jumps with the current decoded function remain labels/gotos. - continue; - } - - const Function *containingFunction = findContainingFunction(target); - if (targetInCurrentFunction) - { - PendingEntry pending{}; - pending.target = target; - pending.containingStart = function.start; - pending.containingEnd = function.end; - pendingEntries.push_back(pending); - pendingStarts.insert(target); - continue; - } + analysisResult = codeGenerator->collectInternalBranchTargets( + function, instructions, &functions); + } + else + { + analysisResult.resumeEntryPoints.clear(); + analysisResult.externalEntryPoints.clear(); + } - PendingEntry pending{}; - pending.target = target; - if (containingFunction) - { - pending.containingStart = containingFunction->start; - pending.containingEnd = containingFunction->end; - } + for (uint32_t target : analysisResult.externalEntryPoints) + { + queuePendingEntry(target); + } - pendingEntries.push_back(pending); - pendingStarts.insert(target); + for (uint32_t target : analysisResult.resumeEntryPoints) + { + queuePendingEntry(target); } } @@ -553,12 +573,6 @@ namespace ps2recomp return stats; } - - bool isEntryFunctionName(const std::string &name) - { - return name.rfind("entry_", 0) == 0; - } - size_t resliceEntryFunctionsImpl( std::vector &functions, std::unordered_map> &decodedFunctions) @@ -1078,6 +1092,7 @@ namespace ps2recomp } } + removeStaleGeneratedOutputs(fs::path(m_config.outputPath)); generateFunctionHeader(); if (m_config.singleFileOutput) @@ -1293,25 +1308,124 @@ namespace ps2recomp void PS2Recompiler::discoverAdditionalEntryPoints() { - const EntryDiscoveryStats stats = discoverAdditionalEntryPointsImpl( - m_functions, - m_decodedFunctions, - m_sections, - [&](Function &entryFunction) - { return decodeFunction(entryFunction); }); + m_resumeEntryTargetsByOwner.clear(); + if (!m_codeGenerator) + { + return; + } + + auto findContainingFunction = [&](uint32_t address) -> const Function * + { + const Function *best = nullptr; + for (const auto &function : m_functions) + { + if (!function.isRecompiled || function.isStub || function.isSkipped) + { + continue; + } + + if (isEntryFunctionName(function.name)) + { + continue; + } + + if (address < function.start || address >= function.end) + { + continue; + } + + auto decodedIt = m_decodedFunctions.find(function.start); + if (decodedIt == m_decodedFunctions.end()) + { + continue; + } + + const auto &decoded = decodedIt->second; + const bool hasAddress = std::any_of(decoded.begin(), decoded.end(), + [&](const Instruction &candidate) + { return candidate.address == address; }); + if (!hasAddress) + { + continue; + } + + if (!best || function.start > best->start) + { + best = &function; + } + } + return best; + }; + + for (const auto &function : m_functions) + { + if (!function.isRecompiled || function.isStub || function.isSkipped) + { + continue; + } + + if (isEntryFunctionName(function.name)) + { + continue; + } + + auto decodedIt = m_decodedFunctions.find(function.start); + if (decodedIt == m_decodedFunctions.end()) + { + continue; + } - if (stats.discoveredCount > 0) + const auto &instructions = decodedIt->second; + CodeGenerator::AnalysisResult analysisResult = + m_codeGenerator->collectInternalBranchTargets(function, instructions, &m_functions); + + auto &ownerTargets = m_resumeEntryTargetsByOwner[function.start]; + ownerTargets.insert(ownerTargets.end(), + analysisResult.resumeEntryPoints.begin(), + analysisResult.resumeEntryPoints.end()); + + for (uint32_t target : analysisResult.externalEntryPoints) + { + const Function *owner = findContainingFunction(target); + if (!owner) + { + continue; + } + + if (owner->start == target) + { + continue; + } + + auto &targets = m_resumeEntryTargetsByOwner[owner->start]; + targets.push_back(target); + } + } + + size_t totalTargets = 0u; + for (auto it = m_resumeEntryTargetsByOwner.begin(); it != m_resumeEntryTargetsByOwner.end();) { - std::cout << "Discovered " << stats.discoveredCount - << " additional entry point(s) inside existing functions across " - << stats.passCount << " pass(es)." << std::endl; + auto &targets = it->second; + std::sort(targets.begin(), targets.end()); + targets.erase(std::unique(targets.begin(), targets.end()), targets.end()); + if (targets.empty()) + { + it = m_resumeEntryTargetsByOwner.erase(it); + continue; + } + + totalTargets += targets.size(); + ++it; } - const size_t reslicedCount = resliceEntryFunctionsImpl(m_functions, m_decodedFunctions); - if (reslicedCount > 0) + m_codeGenerator->setResumeEntryTargets(m_resumeEntryTargetsByOwner); + + if (totalTargets > 0u) { - std::cout << "Resliced " << reslicedCount - << " entry function(s) after discovery." << std::endl; + std::cout << "Collected " << totalTargets + << " resumable entry point(s) across " + << m_resumeEntryTargetsByOwner.size() + << " owner function(s)." << std::endl; } } @@ -1542,10 +1656,12 @@ namespace ps2recomp std::unordered_map> &decodedFunctions, const std::vector
§ions) { + CodeGenerator codeGenerator({}, sections); const EntryDiscoveryStats stats = discoverAdditionalEntryPointsImpl( functions, decodedFunctions, sections, + &codeGenerator, [](Function &) { return false; }); return stats.discoveredCount; From 55d482ab9ee1da2627c7140df76df3a2ef4146dd Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:29:02 -0300 Subject: [PATCH 03/30] feat: optmizations for release build --- ps2xRuntime/CMakeLists.txt | 29 ++++++++++++++++++-- ps2xRuntime/cmake/ReleaseMode.cmake | 42 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 ps2xRuntime/cmake/ReleaseMode.cmake diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index d6b481a8..c6c04f75 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -10,6 +10,8 @@ set(FETCHCONTENT_QUIET FALSE) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) set(BUILD_GAMES OFF CACHE BOOL "" FORCE) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ReleaseMode.cmake") + FetchContent_Declare( raylib GIT_REPOSITORY "https://github.com/raysan5/raylib.git" @@ -30,12 +32,18 @@ add_library(ps2_runtime STATIC src/lib/ps2_memory.cpp src/lib/ps2_pad.cpp src/lib/ps2_runtime.cpp - src/lib/ps2_stubs.cpp - src/lib/ps2_syscalls.cpp src/lib/ps2_vif1_interpreter.cpp src/lib/ps2_vu1.cpp ) +file(GLOB_RECURSE KERNEL_SRC_FILES CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel/*.cpp" +) + +target_sources(ps2_runtime PRIVATE + ${KERNEL_SRC_FILES} +) + file(GLOB RUNNER_SRC_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/runner/*.cpp" ) @@ -68,6 +76,23 @@ target_link_libraries(ps2EntryRunner raylib ) +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + EnableFastReleaseMode(ps2_runtime) + EnableFastReleaseMode(ps2EntryRunner) + + if(MSVC AND WIN32) + target_link_options(ps2EntryRunner PRIVATE + $<$,$>:/SUBSYSTEM:WINDOWS> + $<$,$>:/ENTRY:mainCRTStartup> + ) + elseif(MINGW AND WIN32) + target_link_options(ps2EntryRunner PRIVATE + $<$,$>:-mwindows> + ) + endif() + +endif() + # Work around WinAPI vs raylib symbol clash for CloseWindow on x64 if(MSVC) target_link_options(ps2EntryRunner PRIVATE "/FORCE:MULTIPLE") diff --git a/ps2xRuntime/cmake/ReleaseMode.cmake b/ps2xRuntime/cmake/ReleaseMode.cmake new file mode 100644 index 00000000..92ef0d54 --- /dev/null +++ b/ps2xRuntime/cmake/ReleaseMode.cmake @@ -0,0 +1,42 @@ +include(CheckIPOSupported) + +check_ipo_supported(RESULT IPO_SUPPORTED OUTPUT IPO_ERROR) + +function(EnableFastReleaseMode TargetName) + message("> Enabling optimization for: ${TargetName}") + if(MSVC) + target_compile_options(${TargetName} PRIVATE + $<$: + /O2 # speed + /Ob2 # inline aggressively + /Oi # intrinsics + /GL # whole program opt + /Gy # function-level linking + /Gw # global data in COMDAT + /GF # string pooling + /Zc:inline # remove unreferenced inline + /fp:fast # fast math (graphics friendly) + /DNDEBUG + /arch:AVX2 # Advanced Vector Extensions 2 + /GS- # Disable Buffer Security Check (faster) + /Qspectre- # Disable Spectre mitigations (faster) + > + ) + + if(TARGET ${TargetName}) + target_link_options(${TargetName} PRIVATE + $<$: + /LTCG # link-time code generation + /OPT:REF # remove unreferenced + /OPT:ICF # fold identical COMDATs + > + ) + endif() + endif() + + if(IPO_SUPPORTED) + set_property(TARGET ${TargetName} PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) + else() + message(WARNING "Interprocedural optimization not supported: ${ipo_error}") + endif() +endfunction() \ No newline at end of file From 7b7771d31a12ee3fe54c17974164a826caad29cf Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:29:06 -0300 Subject: [PATCH 04/30] feat: remove unused file --- ps2xRuntime/main_example.cpp | 108 ----------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 ps2xRuntime/main_example.cpp diff --git a/ps2xRuntime/main_example.cpp b/ps2xRuntime/main_example.cpp deleted file mode 100644 index 7b1ce7e1..00000000 --- a/ps2xRuntime/main_example.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "ps2_runtime.h" -#include -#include - -// Example of how to use the PS2 runtime with recompiled code - -// Stub implementation for PS2 syscalls -void syscall(uint8_t *rdram, R5900Context *ctx) -{ - uint32_t syscallNum = ctx->r[4].m128i_u32[0]; - std::cout << "Syscall " << syscallNum << " called" << std::endl; - - switch (syscallNum) - { - case 0x01: // Exit program - std::cout << "Program requested exit with code: " << ctx->r[5].m128i_u32[0] << std::endl; - break; - - case 0x3C: // PutChar - print a character to stdout - std::cout << (char)ctx->r[5].m128i_u32[0]; - break; - - case 0x3D: // PutString - print a string to stdout - { - uint32_t strAddr = ctx->r[5].m128i_u32[0]; - if (strAddr == 0) - { - std::cout << "(null)"; - } - else - { - uint32_t physAddr = strAddr & 0x1FFFFFFF; - const char *str = reinterpret_cast(rdram + physAddr); - std::cout << str; - } - } - break; - - default: - std::cout << "Unhandled syscall: " << syscallNum << std::endl; - break; - } -} - -// Example implementation of FlushCache -void FlushCache(uint8_t *rdram, R5900Context *ctx) -{ - uint32_t cacheType = ctx->r[4].m128i_u32[0]; - std::cout << "FlushCache called with type: " << cacheType << std::endl; -} - -// Example implementation of a recompiled function -void recompiled_main(uint8_t *rdram, R5900Context *ctx) -{ - std::cout << "Running recompiled main function" << std::endl; - - // Example of memory access - uint32_t addr = 0x100000; // Some address in memory - uint32_t physAddr = addr & 0x1FFFFFFF; - uint32_t value = *reinterpret_cast(rdram + physAddr); - std::cout << "Value at 0x" << std::hex << addr << " = 0x" << value << std::dec << std::endl; - - // Example of register manipulation - ctx->r[2] = _mm_set1_epi32(0x12345678); // Set register v0 - ctx->r[4] = _mm_set1_epi32(0x3D); // Set register a0 for syscall (PutString) - ctx->r[5] = _mm_set1_epi32(0x10000); // Set register a1 with string address - - // Call a "syscall" function - syscall(rdram, ctx); - - // Example of returning a value - ctx->r[2] = _mm_set1_epi32(0); // Return 0 (success) -} - -int main(int argc, char *argv[]) -{ - if (argc < 2) - { - std::cout << "Usage: " << argv[0] << " " << std::endl; - return 1; - } - - std::string elfPath = argv[1]; - - PS2Runtime runtime; - if (!runtime.initialize()) - { - std::cerr << "Failed to initialize PS2 runtime" << std::endl; - return 1; - } - - // Register built-in functions - runtime.registerFunction(0x00000001, syscall); - runtime.registerFunction(0x00000002, FlushCache); - runtime.registerFunction(0x00100000, recompiled_main); // Example address for main - - // Load the ELF file - if (!runtime.loadELF(elfPath)) - { - std::cerr << "Failed to load ELF file: " << elfPath << std::endl; - return 1; - } - - // Run the program - runtime.run(); - - return 0; -} \ No newline at end of file From 63e32a231277572e8812ecb5fe8509c089d4a7d2 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:29:52 -0300 Subject: [PATCH 05/30] feat: added some test cases for code gen --- ps2xTest/src/code_generator_tests.cpp | 196 ++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/ps2xTest/src/code_generator_tests.cpp b/ps2xTest/src/code_generator_tests.cpp index 1bc55eb0..1550a0dd 100644 --- a/ps2xTest/src/code_generator_tests.cpp +++ b/ps2xTest/src/code_generator_tests.cpp @@ -204,6 +204,202 @@ void register_code_generator_tests() t.IsTrue(generated.find("goto label_2004;") != std::string::npos, "branch to delay slot should use goto"); }); + tc.Run("control-flow analysis keeps same-function JAL target internal and promotes only the return pc", [](TestCase &t) { + Function func; + func.name = "same_function_call"; + func.start = 0x1000; + func.end = 0x101C; + func.isRecompiled = true; + func.isStub = false; + + std::vector instructions; + instructions.push_back(makeJal(0x1000, 0x100C)); + instructions.push_back(makeNop(0x1004)); + instructions.push_back(makeNop(0x1008)); + instructions.push_back(makeNop(0x100C)); + instructions.push_back(makeNop(0x1010)); + instructions.push_back(makeJr(0x1014, 31)); + instructions.push_back(makeNop(0x1018)); + + std::vector functions{func}; + std::vector
sections = { + {".text", 0x1000u, 0x100u, 0u, true, false, false, true, nullptr} + }; + + CodeGenerator gen({}, sections); + CodeGenerator::AnalysisResult analysis = + gen.collectInternalBranchTargets(func, instructions, &functions); + + t.IsTrue(analysis.entryPoints.contains(0x100Cu), + "same-function JAL target should stay as an internal label target"); + t.IsTrue(analysis.resumeEntryPoints.contains(0x1008u), + "same-function JAL should mark the fallthrough pc as resumable"); + t.IsFalse(analysis.externalEntryPoints.contains(0x100Cu), + "same-function JAL target should not become an external entry candidate"); + }); + + tc.Run("control-flow analysis reports cross-function mid-function jumps as external entry candidates", [](TestCase &t) { + Function caller; + caller.name = "caller"; + caller.start = 0x4000; + caller.end = 0x4008; + caller.isRecompiled = true; + caller.isStub = false; + + Function target; + target.name = "target"; + target.start = 0x5000; + target.end = 0x5010; + target.isRecompiled = true; + target.isStub = false; + + Instruction j{}; + j.address = 0x4000; + j.opcode = OPCODE_J; + j.target = (0x5004u >> 2) & 0x3FFFFFFu; + j.hasDelaySlot = true; + j.raw = (OPCODE_J << 26) | (j.target & 0x3FFFFFFu); + + std::vector instructions{j, makeNop(0x4004)}; + std::vector functions{caller, target}; + std::vector
sections = { + {".text", 0x4000u, 0x2000u, 0u, true, false, false, true, nullptr} + }; + + CodeGenerator gen({}, sections); + CodeGenerator::AnalysisResult analysis = + gen.collectInternalBranchTargets(caller, instructions, &functions); + + t.IsTrue(analysis.externalEntryPoints.contains(0x5004u), + "cross-function jump into the middle of a function should become an external entry candidate"); + }); + + tc.Run("control-flow analysis promotes JALR fallthrough as a resumable entry", [](TestCase &t) { + Function func; + func.name = "jalr_resume"; + func.start = 0x1200; + func.end = 0x1218; + func.isRecompiled = true; + func.isStub = false; + + std::vector instructions; + instructions.push_back(makeNop(0x1200)); + instructions.push_back(makeJalr(0x1204, 2, 31)); + instructions.push_back(makeNop(0x1208)); + instructions.push_back(makeNop(0x120C)); + instructions.push_back(makeJr(0x1210, 31)); + instructions.push_back(makeNop(0x1214)); + + std::vector functions{func}; + std::vector
sections = { + {".text", 0x1200u, 0x100u, 0u, true, false, false, true, nullptr} + }; + + CodeGenerator gen({}, sections); + CodeGenerator::AnalysisResult analysis = + gen.collectInternalBranchTargets(func, instructions, &functions); + + t.IsTrue(analysis.resumeEntryPoints.contains(0x120Cu), + "JALR should mark its return/fallthrough pc as resumable"); + t.IsTrue(analysis.entryPoints.contains(0x120Cu), + "JALR resume pc should also be emitted as an internal label"); + }); + + tc.Run("resume entry targets emit a top-level pc switch in the owner wrapper", [](TestCase &t) { + Function func; + func.name = "resume_owner"; + func.start = 0x6000; + func.end = 0x6010; + func.isRecompiled = true; + func.isStub = false; + + std::vector instructions{ + makeNop(0x6000), + makeNop(0x6004), + makeNop(0x6008), + makeNop(0x600C) + }; + + CodeGenerator gen({}, {}); + gen.setResumeEntryTargets({{0x6000u, {0x6008u}}}); + + std::string generated = gen.generateFunction(func, instructions, false); + printGeneratedCode("resume entry targets emit a top-level pc switch in the owner wrapper", generated); + + t.IsTrue(generated.find("switch (ctx->pc)") != std::string::npos, + "owner wrapper should dispatch resumable pcs with a switch"); + t.IsTrue(generated.find("case 0x6008u: goto label_6008;") != std::string::npos, + "resume pc should jump directly to the internal label"); + t.IsTrue(generated.find("label_6008:") != std::string::npos, + "resume pc should force label emission for that instruction"); + }); + + tc.Run("resume entry targets register to the owner wrapper", [](TestCase &t) { + Function func; + func.name = "resume_owner"; + func.start = 0x7000; + func.end = 0x7010; + func.isRecompiled = true; + func.isStub = false; + + CodeGenerator gen({}, {}); + gen.setRenamedFunctions({{0x7000u, "resume_owner_0x7000"}}); + gen.setResumeEntryTargets({{0x7000u, {0x7008u, 0x700Cu}}}); + + std::string registration = gen.generateFunctionRegistration({func}, {}); + printGeneratedCode("resume entry targets register to the owner wrapper", registration); + + t.IsTrue(registration.find("runtime.registerFunction(0x7008, resume_owner_0x7000);") != std::string::npos, + "resume entry pc should register to the owner wrapper"); + t.IsTrue(registration.find("runtime.registerFunction(0x700c, resume_owner_0x7000);") != std::string::npos, + "multiple resume pcs should register to the same owner wrapper"); + }); + + tc.Run("external mid-function entry can register to the owner wrapper", [](TestCase &t) { + Function caller; + caller.name = "caller"; + caller.start = 0x4000; + caller.end = 0x4008; + caller.isRecompiled = true; + caller.isStub = false; + + Function owner; + owner.name = "owner"; + owner.start = 0x5000; + owner.end = 0x5010; + owner.isRecompiled = true; + owner.isStub = false; + + Instruction j{}; + j.address = 0x4000; + j.opcode = OPCODE_J; + j.target = (0x5004u >> 2) & 0x3FFFFFFu; + j.hasDelaySlot = true; + j.raw = (OPCODE_J << 26) | (j.target & 0x3FFFFFFu); + + std::vector callerInstructions{j, makeNop(0x4004)}; + std::vector functions{caller, owner}; + std::vector
sections = { + {".text", 0x4000u, 0x2000u, 0u, true, false, false, true, nullptr} + }; + + CodeGenerator gen({}, sections); + CodeGenerator::AnalysisResult analysis = + gen.collectInternalBranchTargets(caller, callerInstructions, &functions); + + t.IsTrue(analysis.externalEntryPoints.contains(0x5004u), + "cross-function jump should identify the mid-function target as externally reachable"); + + gen.setRenamedFunctions({{0x5000u, "owner_0x5000"}}); + gen.setResumeEntryTargets({{0x5000u, {0x5004u}}}); + + std::string registration = gen.generateFunctionRegistration({owner}, {}); + printGeneratedCode("external mid-function entry can register to the owner wrapper", registration); + + t.IsTrue(registration.find("runtime.registerFunction(0x5004, owner_0x5000);") != std::string::npos, + "mid-function external entry should register back to the owner wrapper"); + }); + tc.Run("branches outside function still set pc", [](TestCase &t) { Function func; func.name = "external_branch"; From fe287bfe8574dc84a7107f7936a67137fb94b8bb Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:30:06 -0300 Subject: [PATCH 06/30] feat: added log macro and remove win specific code --- ps2xRuntime/include/ps2_log.h | 36 ++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/ps2xRuntime/include/ps2_log.h b/ps2xRuntime/include/ps2_log.h index e743e667..ef32c8f7 100644 --- a/ps2xRuntime/include/ps2_log.h +++ b/ps2xRuntime/include/ps2_log.h @@ -4,27 +4,31 @@ #include #include #include -#if defined(_WIN32) -#define NOMINMAX -#include + +#if defined(AGRESSIVE_LOGS) +#define PS2_AGRESSIVE_LOGS_ENABLED 1 +#else +#define PS2_AGRESSIVE_LOGS_ENABLED 0 #endif -#ifdef _DEBUG +#if defined(_DEBUG) +#define RUNTIME_LOG(x) do { std::cout << x; } while (0) +#else +#define RUNTIME_LOG(x) do {} while(0) +#endif + +#ifdef neverDone namespace ps2_log { +inline constexpr bool agressive_logs_enabled = PS2_AGRESSIVE_LOGS_ENABLED != 0; + inline std::string log_path() { static std::string path; if (path.empty()) { -#if defined(_WIN32) - char buf[MAX_PATH]; - if (GetModuleFileNameA(nullptr, buf, sizeof(buf))) - path = (std::filesystem::path(buf).parent_path() / "ps2_log.txt").string(); -#endif - if (path.empty()) - path = (std::filesystem::current_path() / "ps2_log.txt").string(); + path = (std::filesystem::current_path() / "ps2_log.txt").string(); } return path; } @@ -64,14 +68,24 @@ inline void print_saved_location() ps2_log::log_entry(name); \ struct _ps2_log_guard_ { const char *_n; _ps2_log_guard_(const char *n) : _n(n) {} \ ~_ps2_log_guard_() { ps2_log::log_exit(_n); } } _ps2_log_guard_(name) +#define PS2_IF_AGRESSIVE_LOGS(code) \ + do \ + { \ + if constexpr (ps2_log::agressive_logs_enabled) \ + { \ + code; \ + } \ + } while (0) #else namespace ps2_log { +inline constexpr bool agressive_logs_enabled = false; inline void print_saved_location() {} } #define PS_LOG_ENTRY(name) ((void)0) +#define PS2_IF_AGRESSIVE_LOGS(code) ((void)0) #endif From 06c011c1e1eca1c5f127e7d9e7164cf6ba1d0715 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:35:09 -0300 Subject: [PATCH 07/30] feat: refactor runtime folder structure feat: added reset sound driver RPC state and compatibility layout feat: rename and added new test feat: update RPC calls to use defined constants feat: added more PSMC(16, 32) feat: change cd read to try find the asset ignoring case sensitive fix: fix some render problems feat: add logs on pad feat: added more RPC handles --- ps2xRuntime/include/ps2_call_list.h | 273 +- ps2xRuntime/include/ps2_gs_psmt4.h | 53 - ps2xRuntime/include/ps2_gs_psmt8.h | 256 - ps2xRuntime/include/ps2_iop.h | 25 - ps2xRuntime/include/ps2_runtime.h | 91 +- ps2xRuntime/include/ps2_stubs.h | 574 ++- ps2xRuntime/include/ps2_syscalls.h | 14 +- ps2xRuntime/include/{ => runtime}/ps2_audio.h | 1 + .../include/{ => runtime}/ps2_gif_arbiter.h | 0 .../include/{ => runtime}/ps2_gs_common.h | 5 + .../include/{ => runtime}/ps2_gs_gpu.h | 31 + ps2xRuntime/include/runtime/ps2_gs_psmct16.h | 96 + ps2xRuntime/include/runtime/ps2_gs_psmct32.h | 40 + ps2xRuntime/include/runtime/ps2_gs_psmt4.h | 51 + ps2xRuntime/include/runtime/ps2_gs_psmt8.h | 47 + .../include/{ => runtime}/ps2_gs_rasterizer.h | 0 ps2xRuntime/include/runtime/ps2_iop.h | 36 + .../include/{ => runtime}/ps2_iop_audio.h | 0 .../include/{ => runtime}/ps2_memory.h | 0 ps2xRuntime/include/{ => runtime}/ps2_pad.h | 0 ps2xRuntime/include/{ => runtime}/ps2_vu1.h | 0 ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp | 546 ++ ps2xRuntime/src/lib/Kernel/Stubs/Audio.h | 101 + ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp | 576 +++ ps2xRuntime/src/lib/Kernel/Stubs/CD.h | 48 + ps2xRuntime/src/lib/Kernel/Stubs/Common.h | 25 + .../src/lib/Kernel/Stubs/Compatibility.cpp | 123 + .../src/lib/Kernel/Stubs/Compatibility.h | 16 + ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp | 113 + ps2xRuntime/src/lib/Kernel/Stubs/DMA.h | 28 + ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp | 58 + ps2xRuntime/src/lib/Kernel/Stubs/Deci2.h | 16 + ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp | 171 + ps2xRuntime/src/lib/Kernel/Stubs/FileIO.h | 28 + ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp | 667 +++ ps2xRuntime/src/lib/Kernel/Stubs/Font.h | 17 + .../ps2_stubs_gs.inl => Kernel/Stubs/GS.cpp} | 529 +- ps2xRuntime/src/lib/Kernel/Stubs/GS.h | 29 + .../Stubs/Helpers/Support.h} | 458 +- ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp | 97 + ps2xRuntime/src/lib/Kernel/Stubs/IPU.h | 11 + .../Stubs/LibC.cpp} | 201 +- ps2xRuntime/src/lib/Kernel/Stubs/LibC.h | 60 + ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp | 425 ++ ps2xRuntime/src/lib/Kernel/Stubs/MPEG.h | 33 + .../src/lib/Kernel/Stubs/MemoryCard.cpp | 1444 ++++++ ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h | 70 + ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp | 760 +++ ps2xRuntime/src/lib/Kernel/Stubs/Pad.h | 39 + ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp | 22 + ps2xRuntime/src/lib/Kernel/Stubs/RPC.h | 10 + ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp | 708 +++ ps2xRuntime/src/lib/Kernel/Stubs/SIF.h | 55 + ps2xRuntime/src/lib/Kernel/Stubs/System.cpp | 67 + ps2xRuntime/src/lib/Kernel/Stubs/System.h | 16 + ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp | 61 + ps2xRuntime/src/lib/Kernel/Stubs/TTY.h | 13 + .../src/lib/Kernel/Stubs/Unimplemented.cpp | 49 + .../src/lib/Kernel/Stubs/Unimplemented.h | 9 + ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp | 435 ++ ps2xRuntime/src/lib/Kernel/Stubs/VU.h | 50 + ps2xRuntime/src/lib/Kernel/Syscalls/Common.h | 36 + .../Syscalls/Dispatcher.cpp} | 162 +- .../src/lib/Kernel/Syscalls/Dispatcher.h | 8 + .../Syscalls/FileIO.cpp} | 18 +- ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.h | 17 + .../Syscalls/Helpers/Loader.h} | 12 +- .../Syscalls/Helpers/Path.h} | 0 .../Syscalls/Helpers/Runtime.h} | 3 +- .../Syscalls/Helpers/State.h} | 212 +- .../Syscalls/Interrupt.cpp} | 118 +- .../src/lib/Kernel/Syscalls/Interrupt.h | 54 + .../src/lib/Kernel/Syscalls/Lifecycle.cpp | 118 + .../src/lib/Kernel/Syscalls/Lifecycle.h | 10 + ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp | 2488 +++++++++ ps2xRuntime/src/lib/Kernel/Syscalls/RPC.h | 29 + .../Syscalls/Sync.cpp} | 55 +- ps2xRuntime/src/lib/Kernel/Syscalls/Sync.h | 35 + .../Syscalls/System.cpp} | 36 +- ps2xRuntime/src/lib/Kernel/Syscalls/System.h | 35 + .../Syscalls/Thread.cpp} | 81 +- ps2xRuntime/src/lib/Kernel/Syscalls/Thread.h | 36 + ps2xRuntime/src/lib/ps2_audio.cpp | 14 +- ps2xRuntime/src/lib/ps2_audio_vag.cpp | 2 +- ps2xRuntime/src/lib/ps2_gif_arbiter.cpp | 11 +- ps2xRuntime/src/lib/ps2_gs_gpu.cpp | 1234 ++++- ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp | 590 ++- ps2xRuntime/src/lib/ps2_iop.cpp | 42 +- ps2xRuntime/src/lib/ps2_iop_audio.cpp | 2 +- ps2xRuntime/src/lib/ps2_memory.cpp | 15 +- ps2xRuntime/src/lib/ps2_pad.cpp | 2 +- ps2xRuntime/src/lib/ps2_runtime.cpp | 608 +-- ps2xRuntime/src/lib/ps2_stubs.cpp | 283 -- ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp | 17 +- ps2xRuntime/src/lib/ps2_vu1.cpp | 13 +- ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl | 4430 ----------------- ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl | 150 - .../lib/stubs/ps2_stubs_residentEvilCV.inl | 670 --- .../src/lib/syscalls/ps2_syscalls_rpc.inl | 1320 ----- ps2xRuntime/src/runner/games_database.cpp | 804 --- ps2xRuntime/src/runner/register_functions.cpp | 6 - ps2xTest/src/pad_input_tests.cpp | 199 +- ps2xTest/src/ps2_gs_tests.cpp | 1983 +++++++- ps2xTest/src/ps2_memory_tests.cpp | 31 +- ps2xTest/src/ps2_recompiler_tests.cpp | 217 +- ps2xTest/src/ps2_runtime_expansion_tests.cpp | 230 +- ps2xTest/src/ps2_runtime_io_tests.cpp | 296 ++ ps2xTest/src/ps2_runtime_kernel_tests.cpp | 224 + ps2xTest/src/ps2_sif_dma_tests.cpp | 554 +++ ps2xTest/src/ps2_sif_rpc_tests.cpp | 112 +- 110 files changed, 17593 insertions(+), 9806 deletions(-) delete mode 100644 ps2xRuntime/include/ps2_gs_psmt4.h delete mode 100644 ps2xRuntime/include/ps2_gs_psmt8.h delete mode 100644 ps2xRuntime/include/ps2_iop.h rename ps2xRuntime/include/{ => runtime}/ps2_audio.h (96%) rename ps2xRuntime/include/{ => runtime}/ps2_gif_arbiter.h (100%) rename ps2xRuntime/include/{ => runtime}/ps2_gs_common.h (92%) rename ps2xRuntime/include/{ => runtime}/ps2_gs_gpu.h (75%) create mode 100644 ps2xRuntime/include/runtime/ps2_gs_psmct16.h create mode 100644 ps2xRuntime/include/runtime/ps2_gs_psmct32.h create mode 100644 ps2xRuntime/include/runtime/ps2_gs_psmt4.h create mode 100644 ps2xRuntime/include/runtime/ps2_gs_psmt8.h rename ps2xRuntime/include/{ => runtime}/ps2_gs_rasterizer.h (100%) create mode 100644 ps2xRuntime/include/runtime/ps2_iop.h rename ps2xRuntime/include/{ => runtime}/ps2_iop_audio.h (100%) rename ps2xRuntime/include/{ => runtime}/ps2_memory.h (100%) rename ps2xRuntime/include/{ => runtime}/ps2_pad.h (100%) rename ps2xRuntime/include/{ => runtime}/ps2_vu1.h (100%) create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Audio.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/CD.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Common.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/DMA.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Deci2.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/FileIO.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Font.h rename ps2xRuntime/src/lib/{stubs/ps2_stubs_gs.inl => Kernel/Stubs/GS.cpp} (58%) create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/GS.h rename ps2xRuntime/src/lib/{stubs/helpers/ps2_stubs_helpers.inl => Kernel/Stubs/Helpers/Support.h} (85%) create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/IPU.h rename ps2xRuntime/src/lib/{stubs/ps2_stubs_libc.inl => Kernel/Stubs/LibC.cpp} (84%) create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/LibC.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/MPEG.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Pad.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/RPC.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/SIF.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/System.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/System.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/TTY.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.h create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Stubs/VU.h create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Common.h rename ps2xRuntime/src/lib/{ps2_syscalls.cpp => Kernel/Syscalls/Dispatcher.cpp} (68%) create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.h rename ps2xRuntime/src/lib/{syscalls/ps2_syscalls_fileio.inl => Kernel/Syscalls/FileIO.cpp} (96%) create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.h rename ps2xRuntime/src/lib/{syscalls/helpers/ps2_syscalls_helpers_loader.inl => Kernel/Syscalls/Helpers/Loader.h} (97%) rename ps2xRuntime/src/lib/{syscalls/helpers/ps2_syscalls_helpers_path.inl => Kernel/Syscalls/Helpers/Path.h} (100%) rename ps2xRuntime/src/lib/{syscalls/helpers/ps2_syscalls_helpers_runtime.inl => Kernel/Syscalls/Helpers/Runtime.h} (99%) rename ps2xRuntime/src/lib/{syscalls/helpers/ps2_syscalls_helpers_state.inl => Kernel/Syscalls/Helpers/State.h} (70%) rename ps2xRuntime/src/lib/{syscalls/ps2_syscalls_interrupt.inl => Kernel/Syscalls/Interrupt.cpp} (82%) create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.h create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.h create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/RPC.h rename ps2xRuntime/src/lib/{syscalls/ps2_syscalls_flags.inl => Kernel/Syscalls/Sync.cpp} (94%) create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Sync.h rename ps2xRuntime/src/lib/{syscalls/ps2_syscalls_system.inl => Kernel/Syscalls/System.cpp} (96%) create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/System.h rename ps2xRuntime/src/lib/{syscalls/ps2_syscalls_thread.inl => Kernel/Syscalls/Thread.cpp} (91%) create mode 100644 ps2xRuntime/src/lib/Kernel/Syscalls/Thread.h delete mode 100644 ps2xRuntime/src/lib/ps2_stubs.cpp delete mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl delete mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl delete mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl delete mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl delete mode 100644 ps2xRuntime/src/runner/games_database.cpp delete mode 100644 ps2xRuntime/src/runner/register_functions.cpp diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index 8ad4b4ca..a7464944 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -4,127 +4,132 @@ // I know ugly, but will work for now. -#define PS2_SYSCALL_LIST(X) \ - X(FlushCache) \ - X(iFlushCache) \ - X(ResetEE) \ - X(SetMemoryMode) \ - \ - X(CreateThread) \ - X(DeleteThread) \ - X(StartThread) \ - X(ExitThread) \ - X(ExitDeleteThread) \ - X(TerminateThread) \ - X(SuspendThread) \ - X(ResumeThread) \ - X(GetThreadId) \ - X(ReferThreadStatus) \ - X(iReferThreadStatus) \ - X(SleepThread) \ - X(WakeupThread) \ - X(iWakeupThread) \ - X(CancelWakeupThread) \ - X(iCancelWakeupThread) \ - X(ChangeThreadPriority) \ - X(iChangeThreadPriority) \ - X(RotateThreadReadyQueue) \ - X(iRotateThreadReadyQueue)\ - X(ReleaseWaitThread) \ - X(iReleaseWaitThread) \ - \ - X(CreateSema) \ - X(DeleteSema) \ - X(SignalSema) \ - X(iSignalSema) \ - X(WaitSema) \ - X(PollSema) \ - X(iPollSema) \ - X(ReferSemaStatus) \ - X(iReferSemaStatus) \ - \ - X(CreateEventFlag) \ - X(DeleteEventFlag) \ - X(SetEventFlag) \ - X(iSetEventFlag) \ - X(ClearEventFlag) \ - X(iClearEventFlag) \ - X(WaitEventFlag) \ - X(PollEventFlag) \ - X(iPollEventFlag) \ - X(ReferEventFlagStatus) \ - X(iReferEventFlagStatus) \ - \ - X(SetAlarm) \ - X(iSetAlarm) \ - X(CancelAlarm) \ - X(iCancelAlarm) \ - \ - X(AddIntcHandler) \ - X(AddIntcHandler2) \ - X(RemoveIntcHandler) \ - X(AddDmacHandler) \ - X(AddDmacHandler2) \ - X(RemoveDmacHandler) \ - X(EnableIntc) \ - X(iEnableIntc) \ - X(DisableIntc) \ - X(iDisableIntc) \ - X(EnableDmac) \ - X(iEnableDmac) \ - X(DisableDmac) \ - X(iDisableDmac) \ - \ - X(SifStopModule) \ - X(SifLoadModule) \ - X(SifInitRpc) \ - X(SifBindRpc) \ - X(SifCallRpc) \ - X(SifRegisterRpc) \ - X(SifCheckStatRpc) \ - X(SifSetRpcQueue) \ - X(SifRemoveRpcQueue) \ - X(SifRemoveRpc) \ - X(sceSifCallRpc) \ - X(sceSifSendCmd) \ - X(sceRpcGetPacket) \ - \ - X(fioOpen) \ - X(fioClose) \ - X(fioRead) \ - X(fioWrite) \ - X(fioLseek) \ - X(fioMkdir) \ - X(fioChdir) \ - X(fioRmdir) \ - X(fioGetstat) \ - X(fioRemove) \ - \ - X(SetGsCrt) \ - X(GsSetCrt) \ - X(GsGetIMR) \ - X(iGsGetIMR) \ - X(GsPutIMR) \ - X(iGsPutIMR) \ - X(SetVSyncFlag) \ - X(SetSyscall) \ - X(GsSetVideoMode) \ - \ - X(GetOsdConfigParam) \ - X(SetOsdConfigParam) \ - X(GetRomName) \ - X(SifLoadElfPart) \ - X(sceSifLoadElf) \ - X(sceSifLoadElfPart) \ - X(sceSifLoadModule) \ - X(sceSifLoadModuleBuffer) \ - \ - X(SetupThread) \ - X(EndOfHeap) \ - X(GetMemorySize) \ - X(Deci2Call) \ - X(QueryBootMode) \ - X(GetThreadTLS) \ +#define PS2_SYSCALL_LIST(X) \ + X(FlushCache) \ + X(iFlushCache) \ + X(ResetEE) \ + X(SetMemoryMode) \ + \ + X(CreateThread) \ + X(DeleteThread) \ + X(StartThread) \ + X(ExitThread) \ + X(ExitDeleteThread) \ + X(TerminateThread) \ + X(SuspendThread) \ + X(ResumeThread) \ + X(GetThreadId) \ + X(ReferThreadStatus) \ + X(iReferThreadStatus) \ + X(SleepThread) \ + X(WakeupThread) \ + X(iWakeupThread) \ + X(CancelWakeupThread) \ + X(iCancelWakeupThread) \ + X(ChangeThreadPriority) \ + X(iChangeThreadPriority) \ + X(RotateThreadReadyQueue) \ + X(iRotateThreadReadyQueue) \ + X(ReleaseWaitThread) \ + X(iReleaseWaitThread) \ + \ + X(CreateSema) \ + X(DeleteSema) \ + X(SignalSema) \ + X(iSignalSema) \ + X(WaitSema) \ + X(PollSema) \ + X(iPollSema) \ + X(ReferSemaStatus) \ + X(iReferSemaStatus) \ + \ + X(CreateEventFlag) \ + X(DeleteEventFlag) \ + X(SetEventFlag) \ + X(iSetEventFlag) \ + X(ClearEventFlag) \ + X(iClearEventFlag) \ + X(WaitEventFlag) \ + X(PollEventFlag) \ + X(iPollEventFlag) \ + X(ReferEventFlagStatus) \ + X(iReferEventFlagStatus) \ + \ + X(InitAlarm) \ + X(SetAlarm) \ + X(iSetAlarm) \ + X(CancelAlarm) \ + X(iCancelAlarm) \ + X(ReleaseAlarm) \ + X(iReleaseAlarm) \ + \ + X(AddIntcHandler) \ + X(AddIntcHandler2) \ + X(RemoveIntcHandler) \ + X(AddDmacHandler) \ + X(AddDmacHandler2) \ + X(RemoveDmacHandler) \ + X(EnableIntc) \ + X(iEnableIntc) \ + X(DisableIntc) \ + X(iDisableIntc) \ + X(EnableDmac) \ + X(iEnableDmac) \ + X(DisableDmac) \ + X(iDisableDmac) \ + \ + X(SifStopModule) \ + X(SifLoadModule) \ + X(SifInitRpc) \ + X(SifBindRpc) \ + X(SifCallRpc) \ + X(SifRegisterRpc) \ + X(SifCheckStatRpc) \ + X(SifSetRpcQueue) \ + X(SifRemoveRpcQueue) \ + X(SifRemoveRpc) \ + X(sceSifCallRpc) \ + X(sceSifSendCmd) \ + X(sceRpcGetPacket) \ + \ + X(fioOpen) \ + X(fioClose) \ + X(fioRead) \ + X(fioWrite) \ + X(fioLseek) \ + X(fioMkdir) \ + X(fioChdir) \ + X(fioRmdir) \ + X(fioGetstat) \ + X(fioRemove) \ + \ + X(SetGsCrt) \ + X(GsSetCrt) \ + X(GsGetIMR) \ + X(iGsGetIMR) \ + X(GsPutIMR) \ + X(iGsPutIMR) \ + X(SetVSyncFlag) \ + X(SetSyscall) \ + X(GsSetVideoMode) \ + \ + X(GetOsdConfigParam) \ + X(SetOsdConfigParam) \ + X(EnableCache) \ + X(DisableCache) \ + X(GetRomName) \ + X(SifLoadElfPart) \ + X(sceSifLoadElf) \ + X(sceSifLoadElfPart) \ + X(sceSifLoadModule) \ + X(sceSifLoadModuleBuffer) \ + \ + X(SetupThread) \ + X(EndOfHeap) \ + X(GetMemorySize) \ + X(Deci2Call) \ + X(QueryBootMode) \ + X(GetThreadTLS) \ X(RegisterExitHandler) // Stubs @@ -203,11 +208,11 @@ X(write) \ /* PS2 native */ \ X(DmaAddr) \ - X(Pad_init) \ - X(Pad_set) \ X(builtin_set_imask) \ X(sceCdRI) \ X(sceCdRM) \ + X(sceDevVif0Reset) \ + X(sceDevVu0Reset) \ X(sceFsDbChk) \ X(sceFsIntrSigSema) \ X(sceFsSemExit) \ @@ -224,7 +229,6 @@ X(sceSifLoadModule) \ X(sceSifSendCmd) \ X(sceVu0ecossin) \ - X(iopGetArea) \ X(mcCallMessageTypeSe) \ X(mcCheckReadStartConfigFile) \ X(mcCheckReadStartSaveFile) \ @@ -268,8 +272,6 @@ X(mceGetInfoApdx) \ X(mceIntrReadFixAlign) \ X(mceStorePwd) \ - X(pdGetPeripheral) \ - X(pdInitPeripheral) \ X(sceCdApplyNCmd) \ X(sceCdBreak) \ X(sceCdCallback) \ @@ -350,14 +352,12 @@ X(sceGsResetPath) \ X(sceGsSetDefClear) \ X(sceGsSetDefDBuffDc) \ - X(sceGsSetDefDBuff) \ X(sceGsSetDefDispEnv) \ X(sceGsSetDefDrawEnv) \ X(sceGsSetDefDrawEnv2) \ X(sceGsSetDefLoadImage) \ X(sceGsSetDefStoreImage) \ X(sceGsSwapDBuffDc) \ - X(sceGsSwapDBuff) \ X(sceGsSyncPath) \ X(sceGsSyncV) \ X(sceGsSyncVCallback) \ @@ -509,6 +509,8 @@ X(sceSifSetCmdBuffer) \ X(sceSifSetDChain) \ X(sceSifSetDma) \ + X(isceSifSetDChain) \ + X(isceSifSetDma) \ X(sceSifSetIopAddr) \ X(sceSifSetReg) \ X(sceSifSetRpcQueue) \ @@ -639,19 +641,10 @@ X(sceVu0UnitMatrix) \ X(sceVu0ViewScreenMatrix) \ X(sceWrite) \ - X(sdDrvInit) \ - X(sdSndStopAll) \ - X(sdSysFinish) \ - X(syFree) \ - X(syHwInit) \ - X(syHwInit2) \ - X(syMallocInit) \ - X(syRtcInit) \ - X(InitThread) \ /* Game/middleware */ // Test hooks: override pad input for scePadRead. -#define PS2_TEST_HOOK_LIST(X) \ - X(setPadOverrideState, (uint16_t buttons, uint8_t lx, uint8_t ly, \ - uint8_t rx, uint8_t ry)) \ +#define PS2_TEST_HOOK_LIST(X) \ + X(setPadOverrideState, (uint16_t buttons, uint8_t lx, uint8_t ly, \ + uint8_t rx, uint8_t ry)) \ X(clearPadOverrideState, (void)) diff --git a/ps2xRuntime/include/ps2_gs_psmt4.h b/ps2xRuntime/include/ps2_gs_psmt4.h deleted file mode 100644 index 1e5ce353..00000000 --- a/ps2xRuntime/include/ps2_gs_psmt4.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef PS2_GS_PSMT4_H -#define PS2_GS_PSMT4_H - -#include - -namespace GSPSMT4 -{ - - static const uint16_t columnTable4[16][32] = { - {0, 8, 16, 24, 32, 40, 48, 56, 2, 10, 18, 26, 34, 42, 50, 58, 4, 12, 20, 28, 36, 44, 52, 60, 6, 14, 22, 30, 38, 46, 54, 62}, - {512, 520, 528, 536, 544, 552, 560, 568, 514, 522, 530, 538, 546, 554, 562, 570, 516, 524, 532, 540, 548, 556, 564, 572, 518, 526, 534, 542, 550, 558, 566, 574}, - {33, 41, 49, 57, 1, 9, 17, 25, 35, 43, 51, 59, 3, 11, 19, 27, 37, 45, 53, 61, 5, 13, 21, 29, 39, 47, 55, 63, 7, 15, 23, 31}, - {545, 553, 561, 569, 513, 521, 529, 537, 547, 555, 563, 571, 515, 523, 531, 539, 549, 557, 565, 573, 517, 525, 533, 541, 551, 559, 567, 575, 519, 527, 535, 543}, - {1056, 1064, 1072, 1080, 1024, 1032, 1040, 1048, 1058, 1066, 1074, 1082, 1026, 1034, 1042, 1050, 1060, 1068, 1076, 1084, 1028, 1036, 1044, 1052, 1062, 1070, 1078, 1086, 1030, 1038, 1046, 1054}, - {1568, 1576, 1584, 1592, 1536, 1544, 1552, 1560, 1570, 1578, 1586, 1594, 1538, 1546, 1554, 1562, 1572, 1580, 1588, 1596, 1540, 1548, 1556, 1564, 1574, 1582, 1590, 1598, 1542, 1550, 1558, 1566}, - {1025, 1033, 1041, 1049, 1057, 1065, 1073, 1081, 1027, 1035, 1043, 1051, 1059, 1067, 1075, 1083, 1029, 1037, 1045, 1053, 1061, 1069, 1077, 1085, 1031, 1039, 1047, 1055, 1063, 1071, 1079, 1087}, - {1537, 1545, 1553, 1561, 1569, 1577, 1585, 1593, 1539, 1547, 1555, 1563, 1571, 1579, 1587, 1595, 1541, 1549, 1557, 1565, 1573, 1581, 1589, 1597, 1543, 1551, 1559, 1567, 1575, 1583, 1591, 1599}, - {2048, 2056, 2064, 2072, 2080, 2088, 2096, 2104, 2050, 2058, 2066, 2074, 2082, 2090, 2098, 2106, 2052, 2060, 2068, 2076, 2084, 2092, 2100, 2108, 2054, 2062, 2070, 2078, 2086, 2094, 2102, 2110}, - {2560, 2568, 2576, 2584, 2592, 2600, 2608, 2616, 2562, 2570, 2578, 2586, 2594, 2602, 2610, 2618, 2564, 2572, 2580, 2588, 2596, 2604, 2612, 2620, 2566, 2574, 2582, 2590, 2598, 2606, 2614, 2622}, - {2081, 2089, 2097, 2105, 2049, 2057, 2065, 2073, 2083, 2091, 2099, 2107, 2051, 2059, 2067, 2075, 2085, 2093, 2101, 2109, 2053, 2061, 2069, 2077, 2087, 2095, 2103, 2111, 2055, 2063, 2071, 2079}, - {2593, 2601, 2609, 2617, 2561, 2569, 2577, 2585, 2595, 2603, 2611, 2619, 2563, 2571, 2579, 2587, 2597, 2605, 2613, 2621, 2565, 2573, 2581, 2589, 2599, 2607, 2615, 2623, 2567, 2575, 2583, 2591}, - {3104, 3112, 3120, 3128, 3072, 3080, 3088, 3096, 3106, 3114, 3122, 3130, 3074, 3082, 3090, 3098, 3108, 3116, 3124, 3132, 3076, 3084, 3092, 3100, 3110, 3118, 3126, 3134, 3078, 3086, 3094, 3102}, - {3616, 3624, 3632, 3640, 3584, 3592, 3600, 3608, 3618, 3626, 3634, 3642, 3586, 3594, 3602, 3610, 3620, 3628, 3636, 3644, 3588, 3596, 3604, 3612, 3622, 3630, 3638, 3646, 3590, 3598, 3606, 3614}, - {3073, 3081, 3089, 3097, 3105, 3113, 3121, 3129, 3075, 3083, 3091, 3099, 3107, 3115, 3123, 3131, 3077, 3085, 3093, 3101, 3109, 3117, 3125, 3133, 3079, 3087, 3095, 3103, 3111, 3119, 3127, 3135}, - {3585, 3593, 3601, 3609, 3617, 3625, 3633, 3641, 3587, 3595, 3603, 3611, 3619, 3627, 3635, 3643, 3589, 3597, 3605, 3613, 3621, 3629, 3637, 3645, 3591, 3599, 3607, 3615, 3623, 3631, 3639, 3647}, - }; - - inline uint32_t pageLocalNibbleOffset(uint32_t x, uint32_t y) - { - uint32_t yy = y & 0x7Fu; - uint32_t xx = x & 0x7Fu; - uint32_t blockBase = (((xx >> 5) & 3u) << 12) + - (((yy >> 4) & 7u) << 6); - return blockBase + columnTable4[yy & 15u][xx & 31u]; - } - - inline uint32_t addrPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y) - { - const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u; - const uint32_t localNibble = pageLocalNibbleOffset(x, y); - const uint32_t localByte = localNibble >> 1u; - const uint32_t localRow = localByte >> 8u; - const uint32_t localColumnByte = localByte & 0xFFu; - const uint32_t globalByte = - block * 256u + - (((y >> 7u) * 32u + localRow) * (pagesPerRow * 256u)) + - ((x >> 7u) * 256u + localColumnByte); - return (globalByte << 1u) | (localNibble & 1u); - } - -} - -#endif diff --git a/ps2xRuntime/include/ps2_gs_psmt8.h b/ps2xRuntime/include/ps2_gs_psmt8.h deleted file mode 100644 index 994b67b3..00000000 --- a/ps2xRuntime/include/ps2_gs_psmt8.h +++ /dev/null @@ -1,256 +0,0 @@ -#ifndef PS2_GS_PSMT8_H -#define PS2_GS_PSMT8_H - -#include -#include - -namespace GSPSMT8 -{ - - static constexpr uint8_t blockTable8[4][8] = { - {0, 1, 4, 5, 16, 17, 20, 21}, - {2, 3, 6, 7, 18, 19, 22, 23}, - {8, 9, 12, 13, 24, 25, 28, 29}, - {10, 11, 14, 15, 26, 27, 30, 31}, - }; - - static constexpr uint8_t blockTable32[32] = { - 0, - 1, - 4, - 5, - 16, - 17, - 20, - 21, - 2, - 3, - 6, - 7, - 18, - 19, - 22, - 23, - 8, - 9, - 12, - 13, - 24, - 25, - 28, - 29, - 10, - 11, - 14, - 15, - 26, - 27, - 30, - 31, - }; - - inline const std::array &index32X() - { - static const std::array table = [] - { - std::array result{}; - for (uint8_t i = 0; i < 4; ++i) - { - for (uint8_t j = 0; j < 8; ++j) - { - const uint8_t index = blockTable32[i * 8u + j]; - result[index] = j; - } - } - return result; - }(); - return table; - } - - inline const std::array &index32Y() - { - static const std::array table = [] - { - std::array result{}; - for (uint8_t i = 0; i < 4; ++i) - { - for (uint8_t j = 0; j < 8; ++j) - { - const uint8_t index = blockTable32[i * 8u + j]; - result[index] = i; - } - } - return result; - }(); - return table; - } - - inline const std::array &columnTable8() - { - static const std::array table = [] - { - std::array result{}; - static constexpr uint8_t lut[128] = { - 0, - 36, - 8, - 44, - 1, - 37, - 9, - 45, - 2, - 38, - 10, - 46, - 3, - 39, - 11, - 47, - 4, - 32, - 12, - 40, - 5, - 33, - 13, - 41, - 6, - 34, - 14, - 42, - 7, - 35, - 15, - 43, - 16, - 52, - 24, - 60, - 17, - 53, - 25, - 61, - 18, - 54, - 26, - 62, - 19, - 55, - 27, - 63, - 20, - 48, - 28, - 56, - 21, - 49, - 29, - 57, - 22, - 50, - 30, - 58, - 23, - 51, - 31, - 59, - 4, - 32, - 12, - 40, - 5, - 33, - 13, - 41, - 6, - 34, - 14, - 42, - 7, - 35, - 15, - 43, - 0, - 36, - 8, - 44, - 1, - 37, - 9, - 45, - 2, - 38, - 10, - 46, - 3, - 39, - 11, - 47, - 20, - 48, - 28, - 56, - 21, - 49, - 29, - 57, - 22, - 50, - 30, - 58, - 23, - 51, - 31, - 59, - 16, - 52, - 24, - 60, - 17, - 53, - 25, - 61, - 18, - 54, - 26, - 62, - 19, - 55, - 27, - 63, - }; - - uint32_t outputIndex = 0u; - for (uint32_t k = 0; k < 4u; ++k) - { - uint32_t inputBase = (k % 2u) * 64u; - for (uint32_t i = 0; i < 16u; ++i) - { - for (uint32_t j = 0; j < 4u; ++j) - { - result[k * 64u + lut[inputBase++]] = static_cast(outputIndex++); - } - } - } - - return result; - }(); - return table; - } - - inline uint32_t addrPSMT8(uint32_t block, uint32_t width, uint32_t x, uint32_t y) - { - const uint32_t page = (block >> 5) + (y >> 6) * (width >> 1) + (x >> 7); - const uint32_t blockId = (block & 0x1Fu) + blockTable8[(y >> 4) & 3u][(x >> 4) & 7u]; - const uint32_t pageOffset = (blockId >> 5) << 13; - const uint32_t localBlock = blockId & 0x1Fu; - const uint32_t blockBase = static_cast(index32Y()[localBlock]) * 2048u + - static_cast(index32X()[localBlock]) * 32u; - const uint32_t column = columnTable8()[(y & 0xFu) * 16u + (x & 0xFu)]; - return (page << 13) + pageOffset + blockBase + column; - } - -} - -#endif diff --git a/ps2xRuntime/include/ps2_iop.h b/ps2xRuntime/include/ps2_iop.h deleted file mode 100644 index 4f7aac10..00000000 --- a/ps2xRuntime/include/ps2_iop.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef PS2_IOP_H -#define PS2_IOP_H - -#include - -constexpr uint32_t IOP_SID_LIBSD = 0x80000701u; - -class ps2_iop -{ -public: - ps2_iop(); - ~ps2_iop() = default; - - void init(uint8_t *rdram); - void reset(); - - bool handleRPC(uint32_t sid, uint32_t rpcNum, - uint32_t sendBufAddr, uint32_t sendSize, - uint32_t recvBufAddr, uint32_t recvSize); - -private: - uint8_t *m_rdram = nullptr; -}; - -#endif diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index a3bba0a2..2a75ff00 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -16,18 +16,20 @@ #include // For SSE4.1 instructions #endif #include +#include #include #include #include #include -#include "ps2_gif_arbiter.h" -#include "ps2_memory.h" -#include "ps2_gs_gpu.h" -#include "ps2_iop.h" -#include "ps2_vu1.h" -#include "ps2_audio.h" -#include "ps2_pad.h" +#include "ps2_log.h" +#include "runtime/ps2_gif_arbiter.h" +#include "runtime/ps2_memory.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_iop.h" +#include "runtime/ps2_vu1.h" +#include "runtime/ps2_audio.h" +#include "runtime/ps2_pad.h" enum PS2Exception { @@ -356,6 +358,78 @@ inline void ps2TraceGuestRangeWrite(uint8_t *rdram, std::cout << std::endl; } +struct PS2SoundDriverCompatLayout +{ + uint32_t primarySeCheckAddr = 0; + uint32_t primaryMidiCheckAddr = 0; + uint32_t fallbackSeCheckAddr = 0; + uint32_t fallbackMidiCheckAddr = 0; + uint32_t busyFlagAddr = 0; + std::array completionCallbacks{}; + std::array clearBusyCallbacks{}; + + [[nodiscard]] bool hasChecksumTables() const + { + return primarySeCheckAddr != 0u || primaryMidiCheckAddr != 0u || + fallbackSeCheckAddr != 0u || fallbackMidiCheckAddr != 0u; + } + + [[nodiscard]] bool matchesCompletionCallback(uint32_t addr) const + { + for (const uint32_t candidate : completionCallbacks) + { + if (candidate != 0u && candidate == addr) + { + return true; + } + } + return false; + } + + [[nodiscard]] bool matchesClearBusyCallback(uint32_t addr) const + { + for (const uint32_t candidate : clearBusyCallbacks) + { + if (candidate != 0u && candidate == addr) + { + return true; + } + } + return false; + } +}; + +struct PS2DtxCompatLayout +{ + uint32_t rpcSid = 0; + uint32_t urpcObjBase = 0; + uint32_t urpcObjLimit = 0; + uint32_t urpcObjStride = 0x20u; + uint32_t urpcFnTableBase = 0; + uint32_t urpcObjTableBase = 0; + uint32_t dispatcherFuncAddr = 0; + + [[nodiscard]] bool isConfigured() const + { + return rpcSid != 0u; + } + + [[nodiscard]] bool hasUrpcObjectRange() const + { + return urpcObjBase != 0u && urpcObjLimit > urpcObjBase && urpcObjStride != 0u; + } + + [[nodiscard]] bool hasUrpcTables() const + { + return urpcFnTableBase != 0u && urpcObjTableBase != 0u; + } + + [[nodiscard]] bool isUrpcRpc(uint32_t sid, uint32_t rpcNum) const + { + return isConfigured() && sid == rpcSid && rpcNum >= 0x400u && rpcNum < 0x500u; + } +}; + class PS2Runtime { public: @@ -373,6 +447,7 @@ class PS2Runtime ~PS2Runtime(); bool initialize(const char *title = "PS2 Game"); + bool ensureCoreSubsystemsInitialized(); bool loadELF(const std::string &elfPath); void run(); @@ -579,6 +654,8 @@ class PS2Runtime }; std::vector m_loadedModules; + uint8_t *m_boundRdram = nullptr; + uint8_t *m_boundGSVram = nullptr; }; #endif // PS2_RUNTIME_H diff --git a/ps2xRuntime/include/ps2_stubs.h b/ps2xRuntime/include/ps2_stubs.h index b9857e8e..11e6419f 100644 --- a/ps2xRuntime/include/ps2_stubs.h +++ b/ps2xRuntime/include/ps2_stubs.h @@ -1,28 +1,564 @@ -#ifndef PS2_STUBS_H -#define PS2_STUBS_H - -#include "ps2_runtime.h" -#include "ps2_call_list.h" -#include +#include "Common.h" +#include "CD.h" namespace ps2_stubs { - #define PS2_DECLARE_STUB(name) void name(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - PS2_STUB_LIST(PS2_DECLARE_STUB) - #undef PS2_DECLARE_STUB +void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t a0 = getRegU32(ctx, 4); // usually lbn + const uint32_t a1 = getRegU32(ctx, 5); // usually sector count + const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer + + struct CdReadArgs + { + uint32_t lbn = 0; + uint32_t sectors = 0; + uint32_t buf = 0; + const char *tag = ""; + }; + + auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t + { + const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); + if (requested == 0) + { + return 0; + } + + const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); + const uint64_t clamped = std::min(requested, maxBytes); + return static_cast(clamped); + }; + + auto tryRead = [&](const CdReadArgs &args) -> bool + { + const uint32_t offset = args.buf & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(args.sectors, offset); + if (bytes == 0) + { + return true; + } + + return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); + }; + + CdReadArgs selected{a0, a1, a2, "a0/a1/a2"}; + bool ok = tryRead(selected); + + if (!ok) + { + // Some game-side wrappers use a nonstandard register layout. + // If primary decode does not resolve to a known LBN, try safe alternatives. + constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize; + if (!isResolvableCdLbn(selected.lbn)) + { + const std::array alternatives = { + CdReadArgs{a2, a1, a0, "a2/a1/a0"}, + CdReadArgs{a0, a2, a1, "a0/a2/a1"}, + CdReadArgs{a1, a0, a2, "a1/a0/a2"}, + CdReadArgs{a1, a2, a0, "a1/a2/a0"}, + CdReadArgs{a2, a0, a1, "a2/a0/a1"}}; + + for (const CdReadArgs &candidate : alternatives) + { + if (candidate.sectors > kMaxReasonableSectors) + { + continue; + } + if (!isResolvableCdLbn(candidate.lbn)) + { + continue; + } + + if (tryRead(candidate)) + { + static uint32_t recoverLogCount = 0; + if (recoverLogCount < 16) + { + RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag + << " (pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << ")" << std::endl); + ++recoverLogCount; + } + selected = candidate; + ok = true; + break; + } + } + } + + if (!ok) + { + const uint32_t offset = a2 & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(a1, offset); + if (bytes > 0) + { + std::memset(rdram + offset, 0, bytes); + } + + static uint32_t unresolvedLogCount = 0; + if (unresolvedLogCount < 32) + { + std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << std::endl; + ++unresolvedLogCount; + } + } + } + + if (ok) + { + g_cdStreamingLbn = selected.lbn + selected.sectors; + setReturnS32(ctx, 1); // command accepted/success + return; + } + + setReturnS32(ctx, 0); +} + + +void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); // 0 = completed/not busy +} + + +void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_lastCdError); +} + + +void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceCdRI", rdram, ctx, runtime); +} + + +void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceCdRM", rdram, ctx, runtime); +} + + +void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 2); +} + +void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + // SCECdPS2DVD + setReturnS32(ctx, 0x14); +} + +void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, g_cdStreamingLbn); +} + +void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t tocAddr = getRegU32(ctx, 4); + if (uint8_t *toc = getMemPtr(rdram, tocAddr)) + { + std::memset(toc, 0, 1024); + } + setReturnS32(ctx, 1); +} + +void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdInitialized = true; + g_lastCdError = 0; + setReturnS32(ctx, 1); +} + +void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t lsn = getRegU32(ctx, 4); + uint32_t posAddr = getRegU32(ctx, 5); + uint8_t *pos = getMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, 0); + return; + } + + uint32_t adjusted = lsn + 150; + const uint32_t minutes = adjusted / (60 * 75); + adjusted %= (60 * 75); + const uint32_t seconds = adjusted / 75; + const uint32_t sectors = adjusted % 75; + + pos[0] = toBcd(minutes); + pos[1] = toBcd(seconds); + pos[2] = toBcd(sectors); + pos[3] = 0; + setReturnS32(ctx, 1); +} + +void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdMode = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 2); +} + +void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t posAddr = getRegU32(ctx, 4); + const uint8_t *pos = getConstMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t minutes = fromBcd(pos[0]); + const uint32_t seconds = fromBcd(pos[1]); + const uint32_t sectors = fromBcd(pos[2]); + const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; + const int32_t lsn = static_cast(absolute) - 150; + setReturnS32(ctx, lsn); +} + +void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t chainAddr = getRegU32(ctx, 4); + bool ok = true; + + for (int i = 0; i < 64; ++i) + { + uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); + if (!entry) + { + ok = false; + break; + } + + const uint32_t lbn = entry[0]; + const uint32_t sectors = entry[1]; + const uint32_t buf = entry[2]; + if (lbn == 0xFFFFFFFFu || sectors == 0) + { + break; + } + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) + { + ok = false; + break; + } + + g_cdStreamingLbn = lbn + sectors; + } + + setReturnS32(ctx, ok ? 1 : 0); +} + +void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clockAddr = getRegU32(ctx, 4); + uint8_t *clockData = getMemPtr(rdram, clockAddr); + if (!clockData) + { + setReturnS32(ctx, 0); + return; + } + + std::time_t now = std::time(nullptr); + std::tm localTm{}; +#ifdef _WIN32 + localtime_s(&localTm, &now); +#else + localtime_r(&now, &localTm); +#endif + + // sceCdCLOCK format (BCD fields). + clockData[0] = 0; + clockData[1] = toBcd(static_cast(localTm.tm_sec)); + clockData[2] = toBcd(static_cast(localTm.tm_min)); + clockData[3] = toBcd(static_cast(localTm.tm_hour)); + clockData[4] = 0; + clockData[5] = toBcd(static_cast(localTm.tm_mday)); + clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); + clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); + setReturnS32(ctx, 1); +} + +void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + sceCdRead(rdram, ctx, runtime); +} + +void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t fileAddr = getRegU32(ctx, 4); + uint32_t pathAddr = getRegU32(ctx, 5); + const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); + const std::string normalizedPath = normalizeCdPathNoPrefix(path); + static uint32_t traceCount = 0; + const uint32_t callerRa = getRegU32(ctx, 31); + const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); + if (shouldTrace) + { + RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc + << " ra=0x" << callerRa + << " file=0x" << fileAddr + << " pathAddr=0x" << pathAddr + << " path=\"" << sanitizeForLog(path) << "\"" + << std::dec << std::endl); + } + ++traceCount; + + if (path.empty()) + { + static uint32_t emptyPathCount = 0; + if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) + { + std::ostringstream preview; + preview << std::hex; + for (uint32_t i = 0; i < 16; ++i) + { + const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); + preview << (i == 0 ? "" : " ") << static_cast(byte); + } + std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr + << " preview=" << preview.str() + << " ra=0x" << callerRa << std::dec << std::endl; + } + ++emptyPathCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + if (normalizedPath.empty()) + { + static uint32_t emptyNormalizedCount = 0; + if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (normalized path is empty, root: " << getCdRootPath().string() << ")" + << std::endl; + } + ++emptyNormalizedCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + CdFileEntry entry; + bool found = registerCdFile(path, entry); + CdFileEntry resolvedEntry = entry; + std::string resolvedPath; + + if (!found) + { + static std::string lastFailedPath; + static uint32_t samePathFailCount = 0; + if (path == lastFailedPath) + { + ++samePathFailCount; + } + else + { + lastFailedPath = path; + samePathFailCount = 1; + } + + if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (root: " << getCdRootPath().string() + << ", repeat=" << samePathFailCount << ")" << std::endl; + } + setReturnS32(ctx, 0); + return; + } + + if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) + { + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } - #define PS2_DECLARE_TEST_HOOK(name, signature) void name signature; - PS2_TEST_HOOK_LIST(PS2_DECLARE_TEST_HOOK) - #undef PS2_DECLARE_TEST_HOOK - void resetGsSyncVCallbackState(); - void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick); + g_cdStreamingLbn = resolvedEntry.baseLbn; + if (shouldTrace) + { + RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) + << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn + << " size=0x" << resolvedEntry.sizeBytes + << " sectors=0x" << resolvedEntry.sectors + << std::dec << std::endl); + } + setReturnS32(ctx, 1); +} - void syMalloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} - void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_cdInitialized ? 6 : 0); +} +void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); } -#endif // PS2_STUBS_H +void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sectors = getRegU32(ctx, 4); + uint32_t buf = getRegU32(ctx, 5); + uint32_t errAddr = getRegU32(ctx, 7); + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); + if (ok) + { + g_cdStreamingLbn += sectors; + } + + if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + { + *err = ok ? 0 : g_lastCdError; + } + + setReturnS32(ctx, ok ? static_cast(sectors) : 0); +} + +void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t statusPtr = getRegU32(ctx, 5); + if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) + { + *status = 0; + } + setReturnS32(ctx, 1); +} +} diff --git a/ps2xRuntime/include/ps2_syscalls.h b/ps2xRuntime/include/ps2_syscalls.h index 45f036e1..0411a2af 100644 --- a/ps2xRuntime/include/ps2_syscalls.h +++ b/ps2xRuntime/include/ps2_syscalls.h @@ -13,7 +13,7 @@ std::string translatePs2Path(const char *ps2Path); extern std::atomic g_activeThreads; -static std::mutex g_sys_fd_mutex; +inline std::mutex g_sys_fd_mutex; namespace ps2_syscalls { @@ -21,6 +21,12 @@ namespace ps2_syscalls PS2_SYSCALL_LIST(PS2_DECLARE_SYSCALL) #undef PS2_DECLARE_SYSCALL + void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause); void initializeGuestKernelState(uint8_t *rdram); @@ -28,6 +34,11 @@ namespace ps2_syscalls void notifyRuntimeStop(); void joinAllGuestHostThreads(); void detachAllGuestHostThreads(); + void resetSoundDriverRpcState(); + void setSoundDriverCompatLayout(const PS2SoundDriverCompatLayout &layout); + void clearSoundDriverCompatLayout(); + void setDtxCompatLayout(const PS2DtxCompatLayout &layout); + void clearDtxCompatLayout(); void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime); uint64_t GetCurrentVSyncTick(); uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime); @@ -35,3 +46,4 @@ namespace ps2_syscalls } #endif // PS2_SYSCALLS_H + diff --git a/ps2xRuntime/include/ps2_audio.h b/ps2xRuntime/include/runtime/ps2_audio.h similarity index 96% rename from ps2xRuntime/include/ps2_audio.h rename to ps2xRuntime/include/runtime/ps2_audio.h index dc33a40d..a8c2a555 100644 --- a/ps2xRuntime/include/ps2_audio.h +++ b/ps2xRuntime/include/runtime/ps2_audio.h @@ -37,6 +37,7 @@ class PS2AudioBackend bool m_audioReady = false; uint32_t m_mostRecentSampleKey = 0; std::vector m_loadOrderSamples; + std::vector m_loadOrderSampleKeys; std::unordered_map m_sampleBank; std::mutex m_mutex; diff --git a/ps2xRuntime/include/ps2_gif_arbiter.h b/ps2xRuntime/include/runtime/ps2_gif_arbiter.h similarity index 100% rename from ps2xRuntime/include/ps2_gif_arbiter.h rename to ps2xRuntime/include/runtime/ps2_gif_arbiter.h diff --git a/ps2xRuntime/include/ps2_gs_common.h b/ps2xRuntime/include/runtime/ps2_gs_common.h similarity index 92% rename from ps2xRuntime/include/ps2_gs_common.h rename to ps2xRuntime/include/runtime/ps2_gs_common.h index 5cc8b45a..3dfa8eb1 100644 --- a/ps2xRuntime/include/ps2_gs_common.h +++ b/ps2xRuntime/include/runtime/ps2_gs_common.h @@ -39,6 +39,11 @@ static inline uint32_t fbStride(uint32_t fbw, uint8_t psm) return pixelsPerRow * (bitsPerPixel(psm) / 8u); } +static inline uint32_t framePageBaseToBlock(uint32_t fbp) +{ + return fbp << 5u; +} + static inline int clampInt(int v, int lo, int hi) { if (v < lo) return lo; diff --git a/ps2xRuntime/include/ps2_gs_gpu.h b/ps2xRuntime/include/runtime/ps2_gs_gpu.h similarity index 75% rename from ps2xRuntime/include/ps2_gs_gpu.h rename to ps2xRuntime/include/runtime/ps2_gs_gpu.h index 6eeef871..511c2a2c 100644 --- a/ps2xRuntime/include/ps2_gs_gpu.h +++ b/ps2xRuntime/include/runtime/ps2_gs_gpu.h @@ -220,6 +220,16 @@ class GS { return m_ctx[(index != 0) ? 1 : 0].frame; } + bool getPreferredDisplaySource(GSFrameReg &outSource, uint32_t &outDestFbp) const; + void latchHostPresentationFrame(); + bool copyLatchedHostPresentationFrame(std::vector &outPixels, + uint32_t &outWidth, + uint32_t &outHeight, + uint32_t *outDisplayFbp = nullptr, + uint32_t *outSourceFbp = nullptr, + bool *outUsedPreferred = nullptr) const; + bool clearFramebufferContext(uint32_t contextIndex, uint32_t rgba); + bool clearActiveFramebuffer(uint32_t rgba); uint32_t consumeLocalToHostBytes(uint8_t *dst, uint32_t maxBytes); @@ -233,12 +243,22 @@ class GS void processImageData(const uint8_t *data, uint32_t sizeBytes); void performLocalToLocalTransfer(); void performLocalToHostToBuffer(); + bool copyFrameToHostRgbaUnlocked(const GSFrameReg &frame, + uint32_t width, + uint32_t height, + std::vector &outPixels, + bool preserveAlpha = false, + bool useLocalMemoryLayout = false, + bool frameBaseIsPages = true, + uint32_t sourceOriginX = 0u, + uint32_t sourceOriginY = 0u) const; GSContext &activeContext(); uint8_t *m_vram = nullptr; uint32_t m_vramSize = 0; struct GSRegisters *m_privRegs = nullptr; + mutable std::recursive_mutex m_stateMutex; GSContext m_ctx[2]; GSPrimReg m_prim{}; @@ -250,6 +270,7 @@ class GS uint8_t m_curFog = 0; bool m_prmodecont = true; + bool m_pabe = false; GSBitBltBuf m_bitbltbuf{}; GSTrxPos m_trxpos{}; @@ -266,6 +287,16 @@ class GS std::vector m_displaySnapshot; std::mutex m_snapshotMutex; uint32_t m_lastDisplayBaseBytes = 0; + GSFrameReg m_preferredDisplaySourceFrame{}; + uint32_t m_preferredDisplayDestFbp = 0; + bool m_hasPreferredDisplaySource = false; + std::vector m_hostPresentationFrame; + uint32_t m_hostPresentationWidth = 0; + uint32_t m_hostPresentationHeight = 0; + uint32_t m_hostPresentationDisplayFbp = 0; + uint32_t m_hostPresentationSourceFbp = 0; + bool m_hostPresentationUsedPreferred = false; + bool m_hasHostPresentationFrame = false; std::vector m_localToHostBuffer; size_t m_localToHostReadPos = 0; diff --git a/ps2xRuntime/include/runtime/ps2_gs_psmct16.h b/ps2xRuntime/include/runtime/ps2_gs_psmct16.h new file mode 100644 index 00000000..5a285e4c --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_gs_psmct16.h @@ -0,0 +1,96 @@ +#ifndef PS2_GS_PSMCT16_H +#define PS2_GS_PSMCT16_H + +#include + +namespace GSPSMCT16 +{ + + static constexpr uint8_t blockTable16[8][4] = { + {0, 2, 8, 10}, + {1, 3, 9, 11}, + {4, 6, 12, 14}, + {5, 7, 13, 15}, + {16, 18, 24, 26}, + {17, 19, 25, 27}, + {20, 22, 28, 30}, + {21, 23, 29, 31}, + }; + + static constexpr uint8_t blockTable16S[8][4] = { + {0, 2, 16, 18}, + {1, 3, 17, 19}, + {8, 10, 24, 26}, + {9, 11, 25, 27}, + {4, 6, 20, 22}, + {5, 7, 21, 23}, + {12, 14, 28, 30}, + {13, 15, 29, 31}, + }; + + static constexpr uint8_t blockTableZ16[8][4] = { + {24, 26, 16, 18}, + {25, 27, 17, 19}, + {28, 30, 20, 22}, + {29, 31, 21, 23}, + {8, 10, 0, 2}, + {9, 11, 1, 3}, + {12, 14, 4, 6}, + {13, 15, 5, 7}, + }; + + static constexpr uint8_t blockTableZ16S[8][4] = { + {24, 26, 8, 10}, + {25, 27, 9, 11}, + {16, 18, 0, 2}, + {17, 19, 1, 3}, + {28, 30, 12, 14}, + {29, 31, 13, 15}, + {20, 22, 4, 6}, + {21, 23, 5, 7}, + }; + + static constexpr uint8_t columnTable16[2][16] = { + {0, 2, 8, 10, 16, 18, 24, 26, 1, 3, 9, 11, 17, 19, 25, 27}, + {4, 6, 12, 14, 20, 22, 28, 30, 5, 7, 13, 15, 21, 23, 29, 31}, + }; + + inline uint32_t addrPSMCT16Like(uint32_t block, + uint32_t width, + uint32_t x, + uint32_t y, + const uint8_t (&blockTable)[8][4]) + { + const uint32_t pagesPerRow = (width != 0u) ? width : 1u; + const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 6u); + const uint32_t blockId = (block & 0x1Fu) + blockTable[(y >> 3u) & 0x7u][(x >> 4u) & 0x3u]; + const uint32_t pageOffset = (blockId >> 5u) << 13u; + const uint32_t localBlock = blockId & 0x1Fu; + const uint32_t columnOffset = ((y >> 1u) & 0x3u) * 64u; + return (page << 13u) + pageOffset + localBlock * 256u + columnOffset + + static_cast(columnTable16[y & 0x1u][x & 0x0Fu]) * 2u; + } + + inline uint32_t addrPSMCT16(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + return addrPSMCT16Like(block, width, x, y, blockTable16); + } + + inline uint32_t addrPSMCT16S(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + return addrPSMCT16Like(block, width, x, y, blockTable16S); + } + + inline uint32_t addrPSMZ16(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + return addrPSMCT16Like(block, width, x, y, blockTableZ16); + } + + inline uint32_t addrPSMZ16S(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + return addrPSMCT16Like(block, width, x, y, blockTableZ16S); + } + +} + +#endif diff --git a/ps2xRuntime/include/runtime/ps2_gs_psmct32.h b/ps2xRuntime/include/runtime/ps2_gs_psmct32.h new file mode 100644 index 00000000..32da4f1c --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_gs_psmct32.h @@ -0,0 +1,40 @@ +#ifndef PS2_GS_PSMCT32_H +#define PS2_GS_PSMCT32_H + +#include + +namespace GSPSMCT32 +{ + + static constexpr uint8_t blockTable32[4][8] = { + {0, 1, 4, 5, 16, 17, 20, 21}, + {2, 3, 6, 7, 18, 19, 22, 23}, + {8, 9, 12, 13, 24, 25, 28, 29}, + {10, 11, 14, 15, 26, 27, 30, 31}, + }; + + static constexpr uint8_t columnTable32[8][8] = { + {0, 1, 4, 5, 8, 9, 12, 13}, + {2, 3, 6, 7, 10, 11, 14, 15}, + {16, 17, 20, 21, 24, 25, 28, 29}, + {18, 19, 22, 23, 26, 27, 30, 31}, + {32, 33, 36, 37, 40, 41, 44, 45}, + {34, 35, 38, 39, 42, 43, 46, 47}, + {48, 49, 52, 53, 56, 57, 60, 61}, + {50, 51, 54, 55, 58, 59, 62, 63}, + }; + + inline uint32_t addrPSMCT32(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + const uint32_t pagesPerRow = (width != 0u) ? width : 1u; + const uint32_t page = (block >> 5u) + (y >> 5u) * pagesPerRow + (x >> 6u); + const uint32_t blockId = (block & 0x1Fu) + blockTable32[(y >> 3u) & 3u][(x >> 3u) & 7u]; + const uint32_t pageOffset = (blockId >> 5u) << 13u; + const uint32_t localBlock = blockId & 0x1Fu; + return (page << 13u) + pageOffset + localBlock * 256u + + static_cast(columnTable32[y & 0x7u][x & 0x7u]) * 4u; + } + +} + +#endif diff --git a/ps2xRuntime/include/runtime/ps2_gs_psmt4.h b/ps2xRuntime/include/runtime/ps2_gs_psmt4.h new file mode 100644 index 00000000..7fbe64f2 --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_gs_psmt4.h @@ -0,0 +1,51 @@ +#ifndef PS2_GS_PSMT4_H +#define PS2_GS_PSMT4_H + +#include + +namespace GSPSMT4 +{ + + static constexpr uint8_t blockTable4[8][4] = { + {0, 2, 8, 10}, + {1, 3, 9, 11}, + {4, 6, 12, 14}, + {5, 7, 13, 15}, + {16, 18, 24, 26}, + {17, 19, 25, 27}, + {20, 22, 28, 30}, + {21, 23, 29, 31}, + }; + + static const uint16_t columnTable4[16][32] = { + {0, 8, 32, 40, 64, 72, 96, 104, 2, 10, 34, 42, 66, 74, 98, 106, 4, 12, 36, 44, 68, 76, 100, 108, 6, 14, 38, 46, 70, 78, 102, 110}, + {16, 24, 48, 56, 80, 88, 112, 120, 18, 26, 50, 58, 82, 90, 114, 122, 20, 28, 52, 60, 84, 92, 116, 124, 22, 30, 54, 62, 86, 94, 118, 126}, + {65, 73, 97, 105, 1, 9, 33, 41, 67, 75, 99, 107, 3, 11, 35, 43, 69, 77, 101, 109, 5, 13, 37, 45, 71, 79, 103, 111, 7, 15, 39, 47}, + {81, 89, 113, 121, 17, 25, 49, 57, 83, 91, 115, 123, 19, 27, 51, 59, 85, 93, 117, 125, 21, 29, 53, 61, 87, 95, 119, 127, 23, 31, 55, 63}, + {192, 200, 224, 232, 128, 136, 160, 168, 194, 202, 226, 234, 130, 138, 162, 170, 196, 204, 228, 236, 132, 140, 164, 172, 198, 206, 230, 238, 134, 142, 166, 174}, + {208, 216, 240, 248, 144, 152, 176, 184, 210, 218, 242, 250, 146, 154, 178, 186, 212, 220, 244, 252, 148, 156, 180, 188, 214, 222, 246, 254, 150, 158, 182, 190}, + {129, 137, 161, 169, 193, 201, 225, 233, 131, 139, 163, 171, 195, 203, 227, 235, 133, 141, 165, 173, 197, 205, 229, 237, 135, 143, 167, 175, 199, 207, 231, 239}, + {145, 153, 177, 185, 209, 217, 241, 249, 147, 155, 179, 187, 211, 219, 243, 251, 149, 157, 181, 189, 213, 221, 245, 253, 151, 159, 183, 191, 215, 223, 247, 255}, + {256, 264, 288, 296, 320, 328, 352, 360, 258, 266, 290, 298, 322, 330, 354, 362, 260, 268, 292, 300, 324, 332, 356, 364, 262, 270, 294, 302, 326, 334, 358, 366}, + {272, 280, 304, 312, 336, 344, 368, 376, 274, 282, 306, 314, 338, 346, 370, 378, 276, 284, 308, 316, 340, 348, 372, 380, 278, 286, 310, 318, 342, 350, 374, 382}, + {321, 329, 353, 361, 257, 265, 289, 297, 323, 331, 355, 363, 259, 267, 291, 299, 325, 333, 357, 365, 261, 269, 293, 301, 327, 335, 359, 367, 263, 271, 295, 303}, + {337, 345, 369, 377, 273, 281, 305, 313, 339, 347, 371, 379, 275, 283, 307, 315, 341, 349, 373, 381, 277, 285, 309, 317, 343, 351, 375, 383, 279, 287, 311, 319}, + {448, 456, 480, 488, 384, 392, 416, 424, 450, 458, 482, 490, 386, 394, 418, 426, 452, 460, 484, 492, 388, 396, 420, 428, 454, 462, 486, 494, 390, 398, 422, 430}, + {464, 472, 496, 504, 400, 408, 432, 440, 466, 474, 498, 506, 402, 410, 434, 442, 468, 476, 500, 508, 404, 412, 436, 444, 470, 478, 502, 510, 406, 414, 438, 446}, + {385, 393, 417, 425, 449, 457, 481, 489, 387, 395, 419, 427, 451, 459, 483, 491, 389, 397, 421, 429, 453, 461, 485, 493, 391, 399, 423, 431, 455, 463, 487, 495}, + {401, 409, 433, 441, 465, 473, 497, 505, 403, 411, 435, 443, 467, 475, 499, 507, 405, 413, 437, 445, 469, 477, 501, 509, 407, 415, 439, 447, 471, 479, 503, 511}, + }; + + inline uint32_t addrPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u; + const uint32_t page = (block >> 5u) + (y >> 7u) * pagesPerRow + (x >> 7u); + const uint32_t blockId = (block & 0x1Fu) + blockTable4[(y >> 4u) & 7u][(x >> 5u) & 3u]; + const uint32_t pageOffset = (blockId >> 5u) << 14u; + const uint32_t localBlock = blockId & 0x1Fu; + return (page << 14u) + pageOffset + localBlock * 512u + columnTable4[y & 0x0Fu][x & 0x1Fu]; + } + +} + +#endif diff --git a/ps2xRuntime/include/runtime/ps2_gs_psmt8.h b/ps2xRuntime/include/runtime/ps2_gs_psmt8.h new file mode 100644 index 00000000..dd4c8004 --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_gs_psmt8.h @@ -0,0 +1,47 @@ +#ifndef PS2_GS_PSMT8_H +#define PS2_GS_PSMT8_H + +#include + +namespace GSPSMT8 +{ + + static constexpr uint8_t blockTable8[4][8] = { + {0, 1, 4, 5, 16, 17, 20, 21}, + {2, 3, 6, 7, 18, 19, 22, 23}, + {8, 9, 12, 13, 24, 25, 28, 29}, + {10, 11, 14, 15, 26, 27, 30, 31}, + }; + + static constexpr uint8_t columnTable8[16][16] = { + {0, 4, 16, 20, 32, 36, 48, 52, 2, 6, 18, 22, 34, 38, 50, 54}, + {8, 12, 24, 28, 40, 44, 56, 60, 10, 14, 26, 30, 42, 46, 58, 62}, + {33, 37, 49, 53, 1, 5, 17, 21, 35, 39, 51, 55, 3, 7, 19, 23}, + {41, 45, 57, 61, 9, 13, 25, 29, 43, 47, 59, 63, 11, 15, 27, 31}, + {96, 100, 112, 116, 64, 68, 80, 84, 98, 102, 114, 118, 66, 70, 82, 86}, + {104, 108, 120, 124, 72, 76, 88, 92, 106, 110, 122, 126, 74, 78, 90, 94}, + {65, 69, 81, 85, 97, 101, 113, 117, 67, 71, 83, 87, 99, 103, 115, 119}, + {73, 77, 89, 93, 105, 109, 121, 125, 75, 79, 91, 95, 107, 111, 123, 127}, + {128, 132, 144, 148, 160, 164, 176, 180, 130, 134, 146, 150, 162, 166, 178, 182}, + {136, 140, 152, 156, 168, 172, 184, 188, 138, 142, 154, 158, 170, 174, 186, 190}, + {161, 165, 177, 181, 129, 133, 145, 149, 163, 167, 179, 183, 131, 135, 147, 151}, + {169, 173, 185, 189, 137, 141, 153, 157, 171, 175, 187, 191, 139, 143, 155, 159}, + {224, 228, 240, 244, 192, 196, 208, 212, 226, 230, 242, 246, 194, 198, 210, 214}, + {232, 236, 248, 252, 200, 204, 216, 220, 234, 238, 250, 254, 202, 206, 218, 222}, + {193, 197, 209, 213, 225, 229, 241, 245, 195, 199, 211, 215, 227, 231, 243, 247}, + {201, 205, 217, 221, 233, 237, 249, 253, 203, 207, 219, 223, 235, 239, 251, 255}, + }; + + inline uint32_t addrPSMT8(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u; + const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 7u); + const uint32_t blockId = (block & 0x1Fu) + blockTable8[(y >> 4) & 3u][(x >> 4) & 7u]; + const uint32_t pageOffset = (blockId >> 5u) << 13u; + const uint32_t localBlock = blockId & 0x1Fu; + return (page << 13u) + pageOffset + localBlock * 256u + columnTable8[y & 0x0Fu][x & 0x0Fu]; + } + +} + +#endif diff --git a/ps2xRuntime/include/ps2_gs_rasterizer.h b/ps2xRuntime/include/runtime/ps2_gs_rasterizer.h similarity index 100% rename from ps2xRuntime/include/ps2_gs_rasterizer.h rename to ps2xRuntime/include/runtime/ps2_gs_rasterizer.h diff --git a/ps2xRuntime/include/runtime/ps2_iop.h b/ps2xRuntime/include/runtime/ps2_iop.h new file mode 100644 index 00000000..5f78d3df --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_iop.h @@ -0,0 +1,36 @@ +#ifndef PS2_IOP_H +#define PS2_IOP_H + +#include + +class PS2Runtime; + +constexpr uint32_t IOP_SID_SNDDRV_COMMAND = 0x00000000u; +constexpr uint32_t IOP_SID_SNDDRV_STATE = 0x00000001u; +constexpr uint32_t IOP_SID_LIBSD = 0x80000701u; + +constexpr uint32_t IOP_RPC_SNDDRV_SUBMIT = 0x00000000u; +constexpr uint32_t IOP_RPC_SNDDRV_GET_STATUS_ADDR = 0x00000012u; +constexpr uint32_t IOP_RPC_SNDDRV_GET_ADDR_TABLE = 0x00000013u; + +class ps2_iop +{ +public: + ps2_iop(); + ~ps2_iop() = default; + + void init(uint8_t *rdram); + void reset(); + + bool handleRPC(PS2Runtime *runtime, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBufAddr, uint32_t sendSize, + uint32_t recvBufAddr, uint32_t recvSize, + uint32_t &resultPtr, + bool &signalNowaitCompletion); + +private: + uint8_t *m_rdram = nullptr; +}; + +#endif diff --git a/ps2xRuntime/include/ps2_iop_audio.h b/ps2xRuntime/include/runtime/ps2_iop_audio.h similarity index 100% rename from ps2xRuntime/include/ps2_iop_audio.h rename to ps2xRuntime/include/runtime/ps2_iop_audio.h diff --git a/ps2xRuntime/include/ps2_memory.h b/ps2xRuntime/include/runtime/ps2_memory.h similarity index 100% rename from ps2xRuntime/include/ps2_memory.h rename to ps2xRuntime/include/runtime/ps2_memory.h diff --git a/ps2xRuntime/include/ps2_pad.h b/ps2xRuntime/include/runtime/ps2_pad.h similarity index 100% rename from ps2xRuntime/include/ps2_pad.h rename to ps2xRuntime/include/runtime/ps2_pad.h diff --git a/ps2xRuntime/include/ps2_vu1.h b/ps2xRuntime/include/runtime/ps2_vu1.h similarity index 100% rename from ps2xRuntime/include/ps2_vu1.h rename to ps2xRuntime/include/runtime/ps2_vu1.h diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp new file mode 100644 index 00000000..99fe1c29 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp @@ -0,0 +1,546 @@ +#include "Common.h" +#include "Audio.h" + +namespace ps2_stubs +{ +namespace +{ +constexpr uint32_t kLibSdCmdSetParam = 0x8010u; +constexpr uint32_t kLibSdCmdBlockTrans = 0x80E0u; +constexpr uint32_t kAudioPositionMask = 0x00FFFFFFu; + +struct AudioStubState +{ + bool initialized = false; + uint32_t currentBlockBase = 0u; + uint32_t currentBlockSize = 0u; + uint32_t currentPauseBase = 0u; +}; + +std::mutex g_audio_stub_mutex; +AudioStubState g_audio_stub_state; + +void resetAudioStubStateUnlocked() +{ + g_audio_stub_state = {}; +} +} + +void resetAudioStubState() +{ + std::lock_guard lock(g_audio_stub_mutex); + resetAudioStubStateUnlocked(); +} + +void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSdCallBack", rdram, ctx, runtime); +} + + +void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + + const uint32_t cmd = getRegU32(ctx, 5); + const uint32_t cmdArg0 = getRegU32(ctx, 6); + const uint32_t cmdArg1 = getRegU32(ctx, 7); + const uint32_t sp = getRegU32(ctx, 29); + const uint32_t arg4 = FAST_READ32(sp + 0x10u); + const uint32_t arg5 = FAST_READ32(sp + 0x14u); + const uint32_t arg6 = FAST_READ32(sp + 0x18u); + + std::lock_guard lock(g_audio_stub_mutex); + g_audio_stub_state.initialized = true; + + if (cmd == kLibSdCmdBlockTrans) + { + if (arg4 != 0u) + { + g_audio_stub_state.currentBlockBase = arg4 & kAudioPositionMask; + } + if (arg5 != 0u) + { + g_audio_stub_state.currentBlockSize = arg5; + } + if (arg6 != 0u) + { + g_audio_stub_state.currentPauseBase = arg6 & kAudioPositionMask; + } + } + else if (cmd == kLibSdCmdSetParam) + { + (void)cmdArg0; + (void)cmdArg1; + } + + // Some games only sample the low 24 bits of the reported SPU transfer head. + // Returning the last configured transfer base keeps the ring-buffer math + // stable without emulating SPU DMA progress. + setReturnU32(ctx, g_audio_stub_state.currentBlockBase & kAudioPositionMask); +} + + +void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + std::lock_guard lock(g_audio_stub_mutex); + resetAudioStubStateUnlocked(); + g_audio_stub_state.initialized = true; + setReturnS32(ctx, 0); +} + + +void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime); +} + + +void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime); +} + +void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime); +} + +void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime); +} + +void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime); +} + +void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime); +} + +void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime); +} + +void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime); +} + +void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime); +} + +void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime); +} + +void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime); +} + +void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime); +} + +void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime); +} + +void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime); +} + +void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime); +} + +void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime); +} + +void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime); +} + +void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime); +} + +void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime); +} + +void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime); +} + +void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime); +} + +void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime); +} + +void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime); +} + +void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime); +} + +void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime); +} + +void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime); +} + +void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime); +} + +void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime); +} + +void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime); +} + +void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime); +} + +void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime); +} + +void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime); +} + +void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime); +} + +void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime); +} + +void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime); +} + +void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime); +} + +void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime); +} + +void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime); +} + +void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime); +} + +void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime); +} + +void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime); +} + +void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime); +} + +void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime); +} + +void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime); +} + +void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime); +} + +void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime); +} + +void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime); +} + +void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime); +} + +void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime); +} + +void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime); +} + +void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime); +} + +void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime); +} + +void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime); +} + +void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime); +} + +void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime); +} + +void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime); +} + +void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime); +} + +void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime); +} + +void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime); +} + +void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime); +} + +void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime); +} + +void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime); +} + +void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime); +} + +void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime); +} + +void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime); +} + +void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime); +} + +void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime); +} + +void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime); +} + +void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime); +} + +void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime); +} + +void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime); +} + +void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime); +} + +void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime); +} + +void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime); +} + +void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime); +} + +void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime); +} + +void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime); +} + +void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Audio.h b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.h new file mode 100644 index 00000000..b450e446 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.h @@ -0,0 +1,101 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void resetAudioStubState(); + void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp new file mode 100644 index 00000000..579e0fa9 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp @@ -0,0 +1,576 @@ +#include "Common.h" +#include "CD.h" +#include "MpegSource.h" + +namespace ps2_stubs +{ +void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t a0 = getRegU32(ctx, 4); // usually lbn + const uint32_t a1 = getRegU32(ctx, 5); // usually sector count + const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer + + struct CdReadArgs + { + uint32_t lbn = 0; + uint32_t sectors = 0; + uint32_t buf = 0; + const char *tag = ""; + }; + + auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t + { + const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); + if (requested == 0) + { + return 0; + } + + const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); + const uint64_t clamped = std::min(requested, maxBytes); + return static_cast(clamped); + }; + + auto tryRead = [&](const CdReadArgs &args) -> bool + { + const uint32_t offset = args.buf & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(args.sectors, offset); + if (bytes == 0) + { + return true; + } + + return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); + }; + + CdReadArgs selected{a0, a1, a2, "a0/a1/a2"}; + bool ok = tryRead(selected); + + if (!ok) + { + // Some game-side wrappers use a nonstandard register layout. + // If primary decode does not resolve to a known LBN, try safe alternatives. + constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize; + if (!isResolvableCdLbn(selected.lbn)) + { + const std::array alternatives = { + CdReadArgs{a2, a1, a0, "a2/a1/a0"}, + CdReadArgs{a0, a2, a1, "a0/a2/a1"}, + CdReadArgs{a1, a0, a2, "a1/a0/a2"}, + CdReadArgs{a1, a2, a0, "a1/a2/a0"}, + CdReadArgs{a2, a0, a1, "a2/a0/a1"}}; + + for (const CdReadArgs &candidate : alternatives) + { + if (candidate.sectors > kMaxReasonableSectors) + { + continue; + } + if (!isResolvableCdLbn(candidate.lbn)) + { + continue; + } + + if (tryRead(candidate)) + { + static uint32_t recoverLogCount = 0; + if (recoverLogCount < 16) + { + RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag + << " (pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << ")" << std::endl); + ++recoverLogCount; + } + selected = candidate; + ok = true; + break; + } + } + } + + if (!ok) + { + const uint32_t offset = a2 & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(a1, offset); + if (bytes > 0) + { + std::memset(rdram + offset, 0, bytes); + } + + static uint32_t unresolvedLogCount = 0; + if (unresolvedLogCount < 32) + { + std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << std::endl; + ++unresolvedLogCount; + } + } + } + + if (ok) + { + g_cdStreamingLbn = selected.lbn + selected.sectors; + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, 1); // command accepted/success + return; + } + + setReturnS32(ctx, 0); +} + + +void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); // 0 = completed/not busy +} + + +void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_lastCdError); +} + + +void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceCdRI", rdram, ctx, runtime); +} + + +void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceCdRM", rdram, ctx, runtime); +} + + +void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 2); +} + +void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + // SCECdPS2DVD + setReturnS32(ctx, 0x14); +} + +void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, g_cdStreamingLbn); +} + +void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t tocAddr = getRegU32(ctx, 4); + if (uint8_t *toc = getMemPtr(rdram, tocAddr)) + { + std::memset(toc, 0, 1024); + } + setReturnS32(ctx, 1); +} + +void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdInitialized = true; + g_lastCdError = 0; + setReturnS32(ctx, 1); +} + +void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t lsn = getRegU32(ctx, 4); + uint32_t posAddr = getRegU32(ctx, 5); + uint8_t *pos = getMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, 0); + return; + } + + uint32_t adjusted = lsn + 150; + const uint32_t minutes = adjusted / (60 * 75); + adjusted %= (60 * 75); + const uint32_t seconds = adjusted / 75; + const uint32_t sectors = adjusted % 75; + + pos[0] = toBcd(minutes); + pos[1] = toBcd(seconds); + pos[2] = toBcd(sectors); + pos[3] = 0; + setReturnS32(ctx, 1); +} + +void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdMode = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 2); +} + +void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t posAddr = getRegU32(ctx, 4); + const uint8_t *pos = getConstMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t minutes = fromBcd(pos[0]); + const uint32_t seconds = fromBcd(pos[1]); + const uint32_t sectors = fromBcd(pos[2]); + const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; + const int32_t lsn = static_cast(absolute) - 150; + setReturnS32(ctx, lsn); +} + +void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t chainAddr = getRegU32(ctx, 4); + bool ok = true; + + for (int i = 0; i < 64; ++i) + { + uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); + if (!entry) + { + ok = false; + break; + } + + const uint32_t lbn = entry[0]; + const uint32_t sectors = entry[1]; + const uint32_t buf = entry[2]; + if (lbn == 0xFFFFFFFFu || sectors == 0) + { + break; + } + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) + { + ok = false; + break; + } + + g_cdStreamingLbn = lbn + sectors; + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + } + + setReturnS32(ctx, ok ? 1 : 0); +} + +void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clockAddr = getRegU32(ctx, 4); + uint8_t *clockData = getMemPtr(rdram, clockAddr); + if (!clockData) + { + setReturnS32(ctx, 0); + return; + } + + std::time_t now = std::time(nullptr); + std::tm localTm{}; +#ifdef _WIN32 + localtime_s(&localTm, &now); +#else + localtime_r(&now, &localTm); +#endif + + // sceCdCLOCK format (BCD fields). + clockData[0] = 0; + clockData[1] = toBcd(static_cast(localTm.tm_sec)); + clockData[2] = toBcd(static_cast(localTm.tm_min)); + clockData[3] = toBcd(static_cast(localTm.tm_hour)); + clockData[4] = 0; + clockData[5] = toBcd(static_cast(localTm.tm_mday)); + clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); + clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); + setReturnS32(ctx, 1); +} + +void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + sceCdRead(rdram, ctx, runtime); +} + +void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t fileAddr = getRegU32(ctx, 4); + uint32_t pathAddr = getRegU32(ctx, 5); + const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); + const std::string normalizedPath = normalizeCdPathNoPrefix(path); + static uint32_t traceCount = 0; + const uint32_t callerRa = getRegU32(ctx, 31); + const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); + if (shouldTrace) + { + RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc + << " ra=0x" << callerRa + << " file=0x" << fileAddr + << " pathAddr=0x" << pathAddr + << " path=\"" << sanitizeForLog(path) << "\"" + << std::dec << std::endl); + } + ++traceCount; + + if (path.empty()) + { + static uint32_t emptyPathCount = 0; + if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) + { + std::ostringstream preview; + preview << std::hex; + for (uint32_t i = 0; i < 16; ++i) + { + const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); + preview << (i == 0 ? "" : " ") << static_cast(byte); + } + std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr + << " preview=" << preview.str() + << " ra=0x" << callerRa << std::dec << std::endl; + } + ++emptyPathCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + if (normalizedPath.empty()) + { + static uint32_t emptyNormalizedCount = 0; + if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (normalized path is empty, root: " << getCdRootPath().string() << ")" + << std::endl; + } + ++emptyNormalizedCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + CdFileEntry entry; + bool found = registerCdFile(path, entry); + CdFileEntry resolvedEntry = entry; + std::string resolvedPath; + + if (!found) + { + static std::string lastFailedPath; + static uint32_t samePathFailCount = 0; + if (path == lastFailedPath) + { + ++samePathFailCount; + } + else + { + lastFailedPath = path; + samePathFailCount = 1; + } + + if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (root: " << getCdRootPath().string() + << ", repeat=" << samePathFailCount << ")" << std::endl; + } + setReturnS32(ctx, 0); + return; + } + + if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) + { + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + g_cdStreamingLbn = resolvedEntry.baseLbn; + detail::trackCdFileForMpeg(resolvedEntry.baseLbn, + resolvedEntry.sizeBytes, + resolvedEntry.hostPath); + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + if (shouldTrace) + { + RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) + << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn + << " size=0x" << resolvedEntry.sizeBytes + << " sectors=0x" << resolvedEntry.sectors + << std::dec << std::endl); + } + setReturnS32(ctx, 1); +} + +void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, 1); +} + +void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_cdInitialized ? 6 : 0); +} + +void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sectors = getRegU32(ctx, 4); + uint32_t buf = getRegU32(ctx, 5); + uint32_t errAddr = getRegU32(ctx, 7); + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); + if (ok) + { + g_cdStreamingLbn += sectors; + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + } + + if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + { + *err = ok ? 0 : g_lastCdError; + } + + setReturnS32(ctx, ok ? static_cast(sectors) : 0); +} + +void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, 1); +} + +void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, 1); +} + +void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, 1); +} + +void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t statusPtr = getRegU32(ctx, 5); + if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) + { + *status = 0; + } + setReturnS32(ctx, 1); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/CD.h b/ps2xRuntime/src/lib/Kernel/Stubs/CD.h new file mode 100644 index 00000000..9bf87603 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/CD.h @@ -0,0 +1,48 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Common.h b/ps2xRuntime/src/lib/Kernel/Stubs/Common.h new file mode 100644 index 00000000..6f2e3b82 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Common.h @@ -0,0 +1,25 @@ +#include "ps2_stubs.h" +#include "ps2_runtime.h" +#include "ps2_runtime_macros.h" +#include "ps2_syscalls.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "raylib.h" +#include "Helpers/Support.h" diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp new file mode 100644 index 00000000..f3808bf0 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp @@ -0,0 +1,123 @@ +#include "Common.h" +#include "Compatibility.h" +#include "ps2_log.h" + +namespace ps2_stubs +{ +void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t count = getRegU32(ctx, 5); // $a1 + const uint32_t size = getRegU32(ctx, 6); // $a2 + const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; + setReturnU32(ctx, guestAddr); +} + +void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, 0u); + ctx->pc = getRegU32(ctx, 31); +} + +void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, 1u); + ctx->pc = getRegU32(ctx, 31); +} + +void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, getRegU32(ctx, 4)); + ctx->pc = getRegU32(ctx, 31); +} + +void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t guestAddr = getRegU32(ctx, 5); // $a1 + if (runtime && guestAddr != 0u) + { + runtime->guestFree(guestAddr); + } +} + +void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t size = getRegU32(ctx, 5); // $a1 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; + setReturnU32(ctx, guestAddr); +} + +void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t wcAddr = getRegU32(ctx, 5); // $a1 + const uint32_t strAddr = getRegU32(ctx, 6); // $a2 + const int32_t n = static_cast(getRegU32(ctx, 7)); // $a3 + if (n <= 0 || strAddr == 0u) + { + setReturnS32(ctx, 0); + return; + } + + const uint8_t *src = getConstMemPtr(rdram, strAddr); + if (!src) + { + setReturnS32(ctx, -1); + return; + } + + const uint8_t ch = *src; + if (wcAddr != 0u) + { + if (uint8_t *dst = getMemPtr(rdram, wcAddr)) + { + const uint32_t out = static_cast(ch); + std::memcpy(dst, &out, sizeof(out)); + } + } + setReturnS32(ctx, (ch == 0u) ? 0 : 1); +} + +void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + if (rendered.size() > 2048) + { + rendered.resize(2048); + } + PS2_IF_AGRESSIVE_LOGS({ + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + RUNTIME_LOG("PS2 printf: " << logLine); + RUNTIME_LOG(std::flush); + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + }); + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; + } + + setReturnS32(ctx, ret); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h new file mode 100644 index 00000000..2159c441 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + 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 malloc_r(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); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp new file mode 100644 index 00000000..6533aa09 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp @@ -0,0 +1,113 @@ +#include "Common.h" +#include "DMA.h" + +namespace ps2_stubs +{ +void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, getRegU32(ctx, 4)); +} + + +void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaCallback", rdram, ctx, runtime); +} + +void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaDebug", rdram, ctx, runtime); +} + +void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t chanArg = getRegU32(ctx, 4); + const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); + setReturnU32(ctx, channelBase); +} + +void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); +} + +void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime); +} + +void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaPause", rdram, ctx, runtime); +} + +void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); +} + +void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); +} + +void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRecv", rdram, ctx, runtime); +} + +void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime); +} + +void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime); +} + +void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRestart", rdram, ctx, runtime); +} + +void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); +} + +void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); +} + +void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); +} + +void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true)); +} + +void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); +} + +void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); +} + +void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaWatch", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.h b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.h new file mode 100644 index 00000000..fdbcfa53 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.h @@ -0,0 +1,28 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp new file mode 100644 index 00000000..16f34d4d --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp @@ -0,0 +1,58 @@ +#include "Common.h" +#include "Deci2.h" + +namespace ps2_stubs +{ +void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2Close", rdram, ctx, runtime); +} + + +void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime); +} + + +void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime); +} + + +void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime); +} + + +void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime); +} + + +void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime); +} + + +void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2Open", rdram, ctx, runtime); +} + + +void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime); +} + + +void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.h b/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.h new file mode 100644 index 00000000..cf6ec07d --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp new file mode 100644 index 00000000..76bc35dc --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp @@ -0,0 +1,171 @@ +#include "Common.h" +#include "FileIO.h" + +namespace ps2_stubs +{ +void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsDbChk", rdram, ctx, runtime); +} + + +void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsIntrSigSema", rdram, ctx, runtime); +} + + +void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsSemExit", rdram, ctx, runtime); +} + + +void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsSemInit", rdram, ctx, runtime); +} + + +void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsSigSema", rdram, ctx, runtime); +} + + +void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioClose(rdram, ctx, runtime); +} + + +void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t statAddr = getRegU32(ctx, 5); + if (uint8_t *statBuf = getMemPtr(rdram, statAddr)) + { + std::memset(statBuf, 0, 128); + setReturnS32(ctx, 0); + return; + } + setReturnS32(ctx, -1); +} + + +void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioLseek(rdram, ctx, runtime); +} + + +void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioOpen(rdram, ctx, runtime); +} + + +void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioRead(rdram, ctx, runtime); +} + + +void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioClose(rdram, ctx, runtime); +} + + +void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + + +void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + + +void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t cmd = static_cast(getRegU32(ctx, 5)); + const uint32_t argAddr = getRegU32(ctx, 6); + + // HTCI wait paths poll sceIoctl(fd, 1, &state) and expect state to move + // away from 1 once host-side I/O is no longer busy. + if (cmd == 1 && argAddr != 0u) + { + uint8_t *argPtr = getMemPtr(rdram, argAddr); + if (!argPtr) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t ready = 0u; + std::memcpy(argPtr, &ready, sizeof(ready)); + } + + setReturnS32(ctx, 0); +} + + +void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioLseek(rdram, ctx, runtime); +} + + +void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioOpen(rdram, ctx, runtime); +} + + +void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioRead(rdram, ctx, runtime); +} + + +void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioWrite(rdram, ctx, runtime); +} + + +void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t statAddr = getRegU32(ctx, 5); + uint8_t *statBuf = getMemPtr(rdram, statAddr); + if (!statBuf) + { + setReturnS32(ctx, -1); + return; + } + + // Minimal fake stat payload: zeroed structure indicates a valid, readable file. + std::memset(statBuf, 0, 128); + setReturnS32(ctx, 0); +} + + +void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioWrite(rdram, ctx, runtime); +} + + +void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + RUNTIME_LOG("ps2_stub cvFsSetDefDev"); + ++logCount; + } + setReturnS32(ctx, 0); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.h b/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.h new file mode 100644 index 00000000..dc9268f3 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.h @@ -0,0 +1,28 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp new file mode 100644 index 00000000..877e3aa5 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp @@ -0,0 +1,667 @@ +#include "Common.h" +#include "Font.h" + +namespace ps2_stubs +{ +static void writeU32AtGp(uint8_t *rdram, uint32_t gp, int32_t offset, uint32_t value) +{ + const uint32_t addr = gp + static_cast(offset); + if (uint8_t *p = getMemPtr(rdram, addr)) + *reinterpret_cast(p) = value; +} + +void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t gp = getRegU32(ctx, 28); + const uint32_t a0 = getRegU32(ctx, 4); + const uint32_t a1 = getRegU32(ctx, 5); + const uint32_t a2 = getRegU32(ctx, 6); + const uint32_t a3 = getRegU32(ctx, 7); + writeU32AtGp(rdram, gp, -0x7b60, a1); + writeU32AtGp(rdram, gp, -0x7b5c, a2); + writeU32AtGp(rdram, gp, -0x7b64, a0); + writeU32AtGp(rdram, gp, -0x7c98, a3); + writeU32AtGp(rdram, gp, -0x7b4c, 0x7f7f7f7f); + writeU32AtGp(rdram, gp, -0x7b50, 0x3f800000); + writeU32AtGp(rdram, gp, -0x7b54, 0x3f800000); + writeU32AtGp(rdram, gp, -0x7b58, 0); + + if (runtime && a0 != 0u) + { + if ((a0 * 256u) + 64u <= PS2_GS_VRAM_SIZE) + { + uint32_t clutData[16]; + for (uint32_t i = 0; i < 16u; ++i) + { + uint8_t alpha = static_cast((i * 0x80u) / 15u); + clutData[i] = (i == 0) + ? 0x00000000u + : (0x80u | (0x80u << 8) | (0x80u << 16) | (static_cast(alpha) << 24)); + } + constexpr uint32_t kClutQwc = 4u; + constexpr uint32_t kHeaderQwc = 6u; + constexpr uint32_t kTotalQwc = kHeaderQwc + kClutQwc; + uint32_t pktAddr = runtime->guestMalloc(kTotalQwc * 16u, 16u); + if (pktAddr != 0u) + { + uint8_t *pkt = getMemPtr(rdram, pktAddr); + if (pkt) + { + uint64_t *q = reinterpret_cast(pkt); + const uint32_t dbp = a0 & 0x3FFFu; + constexpr uint8_t psm = 0u; + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; + q[2] = (static_cast(dbp) << 32) | (1ULL << 48) | (static_cast(psm) << 56); + q[3] = 0x50ULL; + q[4] = 0ULL; + q[5] = 0x51ULL; + q[6] = 16ULL | (1ULL << 32); + q[7] = 0x52ULL; + q[8] = 0ULL; + q[9] = 0x53ULL; + q[10] = (2ULL << 58) | (kClutQwc & 0x7FFF) | (1ULL << 15); + q[11] = 0ULL; + std::memcpy(pkt + 12u * 8u, clutData, 64u); + constexpr uint32_t GIF_CHANNEL = 0x1000A000; + constexpr uint32_t CHCR_STR_MODE0 = 0x101u; + runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, kTotalQwc & 0xFFFFu); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + runtime->memory().processPendingTransfers(); + } + } + } + } + + setReturnS32(ctx, static_cast(a0 + 4)); +} + +void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static constexpr uint32_t kFontBase = 0x176148u; + static constexpr uint32_t kFontEntrySz = 0x24u; + + const uint32_t fontDataAddr = getRegU32(ctx, 4); + const int fontId = static_cast(getRegU32(ctx, 5)); + const int tbp0 = static_cast(getRegU32(ctx, 7)); + + if (!fontDataAddr || !runtime) + { + setReturnS32(ctx, tbp0); + return; + } + + const uint8_t *fontPtr = getConstMemPtr(rdram, fontDataAddr); + if (!fontPtr) + { + setReturnS32(ctx, tbp0); + return; + } + + int width = static_cast(*reinterpret_cast(fontPtr + 0x00u)); + int height = static_cast(*reinterpret_cast(fontPtr + 0x04u)); + uint32_t raw8 = *reinterpret_cast(fontPtr + 0x08u); + int fontDataSz = static_cast(*reinterpret_cast(fontPtr + 0x0cu)); + + uint32_t pointsize = raw8; + uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); + if (raw8 & 0x40000000u) + { + pointsize = raw8 - 0x40000000u; + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) + *reinterpret_cast(p) = 1u; + } + else + { + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) + *reinterpret_cast(p) = 0u; + } + + int tw = (width >= 0) ? (width >> 6) : ((width + 0x3f) >> 6); + int qwc = (fontDataSz >= 0) ? (fontDataSz >> 4) : ((fontDataSz + 0xf) >> 4); + + uint32_t glyphSrc = fontDataAddr + static_cast(fontDataSz) + 0x10u; + uint32_t glyphAlloc = runtime->guestMalloc(0x2010u, 0x40u); + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) + *reinterpret_cast(p) = glyphAlloc; + + if (glyphAlloc != 0u) + { + uint8_t *dst = getMemPtr(rdram, glyphAlloc); + const uint8_t *src = getConstMemPtr(rdram, glyphSrc); + if (dst && src) + std::memcpy(dst, src, 0x2010u); + } + + uint32_t isDoubleByte = 0; + if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff + 0x20u)) + isDoubleByte = *reinterpret_cast(p); + if (isDoubleByte == 0u) + { + uint32_t kernSrc = glyphSrc + 0x2010u; + uint32_t kernAlloc = runtime->guestMalloc(0xc400u, 0x40u); + if (glyphAlloc != 0u) + *reinterpret_cast(getMemPtr(rdram, glyphAlloc + 0x2000u)) = kernAlloc; + if (kernAlloc != 0u) + { + uint8_t *dst = getMemPtr(rdram, kernAlloc); + const uint8_t *src = getConstMemPtr(rdram, kernSrc); + if (dst && src) + std::memcpy(dst, src, 0xc400u); + } + } + + auto writeFontField = [&](uint32_t off, uint32_t val) + { + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + off)) + *reinterpret_cast(p) = val; + }; + writeFontField(0x18u, pointsize); + writeFontField(0x08u, static_cast(tbp0)); + writeFontField(0x0cu, static_cast(tw)); + + int logW = 0; + for (int w = width; w != 1 && w != 0; w = static_cast(static_cast(w) >> 1)) + logW++; + writeFontField(0x10u, static_cast(logW)); + + int logH = 0; + for (int h = height; h != 1 && h != 0; h = static_cast(static_cast(h) >> 1)) + logH++; + writeFontField(0x14u, static_cast(logH)); + writeFontField(0x04u, 0u); + writeFontField(0x1cu, getRegU32(ctx, 6)); + + if (qwc > 0) + { + const uint32_t imageBytes = static_cast(qwc) * 16u; + const uint8_t psm = 20u; + const uint32_t headerQwc = 12u; + const uint32_t imageQwc = static_cast(qwc); + const uint32_t totalQwc = headerQwc + imageQwc; + uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u); + if (pktAddr != 0u) + { + uint8_t *pkt = getMemPtr(rdram, pktAddr); + const uint8_t *imgSrc = getConstMemPtr(rdram, fontDataAddr + 0x10u); + if (pkt && imgSrc) + { + uint64_t *q = reinterpret_cast(pkt); + const uint32_t dbp = static_cast(tbp0) & 0x3FFFu; + const uint32_t dbw = static_cast(tw > 0 ? tw : 1) & 0x3Fu; + const uint32_t rrw = static_cast(width > 0 ? width : 64); + const uint32_t rrh = static_cast(height > 0 ? height : 1); + + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; + q[2] = (static_cast(psm) << 24) | (1ULL << 16) | + (static_cast(dbp) << 32) | (static_cast(dbw) << 48) | + (static_cast(psm) << 56); + q[3] = 0x50ULL; + q[4] = 0ULL; + q[5] = 0x51ULL; + q[6] = (static_cast(rrh) << 32) | static_cast(rrw); + q[7] = 0x52ULL; + q[8] = 0ULL; + q[9] = 0x53ULL; + q[10] = (2ULL << 58) | (imageQwc & 0x7FFF) | (1ULL << 15); + q[11] = 0ULL; + std::memcpy(pkt + 12 * 8, imgSrc, imageBytes); + + constexpr uint32_t GIF_CHANNEL = 0x1000A000; + constexpr uint32_t CHCR_STR_MODE0 = 0x101u; + runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + } + } + } + + int retTbp = tbp0 + ((fontDataSz >= 0 ? fontDataSz : fontDataSz + 0x7f) >> 7); + setReturnS32(ctx, retTbp); +} + +static constexpr uint32_t kFontBase = 0x176148u; +static constexpr uint32_t kFontEntrySz = 0x24u; + +void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const float sclx = ctx->f[12]; + const float scly = ctx->f[13]; + const uint32_t bufAddr = getRegU32(ctx, 4); + const uint64_t paramX = GPR_U64(ctx, 5); + const int64_t paramY = GPR_S64(ctx, 6); + const int paramW = static_cast(getRegU32(ctx, 7)); + const int paramH = static_cast(getRegU32(ctx, 8)); + const uint32_t colour = getRegU32(ctx, 9); + const int alignCh = static_cast(getRegU32(ctx, 10) & 0xffu); + const int fontId = static_cast(getRegU32(ctx, 11)); + + const uint32_t sp = getRegU32(ctx, 29); + const uint32_t strAddr = FAST_READ32(sp + 0x00u); + const uint32_t param14 = FAST_READ32(sp + 0x18u); + + if (bufAddr == 0u) + { + setReturnS32(ctx, 0); + ctx->pc = getRegU32(ctx, 31); + return; + } + + const uint32_t gp = getRegU32(ctx, 28); + const uint32_t fontModeAdj = FAST_READ32(gp + static_cast(static_cast(-0x7c98))); + const uint32_t shiftAmt = fontModeAdj & 0x1fu; + const int scrHeight = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b5c)))); + const int scrWidth = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b60)))); + const uint32_t fontClut = FAST_READ32(gp + static_cast(static_cast(-0x7b64))); + + const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); + const int lineH = static_cast(FAST_READ32(kFontBase + fontOff + 0x18u)); + + int iVar21 = 0; + int iStack_dc = 0; + uint32_t uStack_d8 = 0; + int iVar15 = 0; + + int16_t sVar8; + { + int yStepRaw = static_cast(static_cast((lineH + 6) * 16) * scly); + sVar8 = static_cast((static_cast(paramY) + 0x700) * 16) + static_cast(yStepRaw >> static_cast(shiftAmt)); + } + + int16_t baseX = static_cast((static_cast(paramX) + 0x6c0) * 16); + + if (param14 != 0u) + { + int64_t clipY1 = static_cast(static_cast(paramY) + paramH); + int64_t clipX1 = static_cast(static_cast(paramX) + paramW); + if (clipY1 > scrHeight - 1) clipY1 = static_cast(scrHeight - 1); + if (clipX1 > scrWidth - 1) clipX1 = static_cast(scrWidth - 1); + int64_t clipY0 = 0; + if (paramY > 0) clipY0 = paramY; + uint64_t clipX0 = 0; + if (static_cast(paramX) > 0) clipX0 = paramX; + + uint64_t scissor = clipX0 | (static_cast(static_cast(clipX1)) << 16) + | (static_cast(static_cast(clipY0)) << 32) | (static_cast(static_cast(clipY1)) << 48); + + FAST_WRITE64(bufAddr + 0x00, 0x1000000000000005ull); + FAST_WRITE64(bufAddr + 0x08, 0x0eull); + FAST_WRITE64(bufAddr + 0x10, scissor); + FAST_WRITE64(bufAddr + 0x18, 0x40ull); + FAST_WRITE64(bufAddr + 0x20, 0x20000ull); + FAST_WRITE64(bufAddr + 0x28, 0x47ull); + FAST_WRITE64(bufAddr + 0x30, 0x44ull); + FAST_WRITE64(bufAddr + 0x38, 0x42ull); + FAST_WRITE64(bufAddr + 0x40, 0x160ull); + FAST_WRITE64(bufAddr + 0x48, 0x14ull); + FAST_WRITE64(bufAddr + 0x50, 0x156ull); + FAST_WRITE64(bufAddr + 0x58, 0ull); + FAST_WRITE64(bufAddr + 0x60, 0x1000000000000001ull); + FAST_WRITE64(bufAddr + 0x68, 0x0eull); + + uint64_t iVar5 = static_cast(FAST_READ32(kFontBase + fontOff + 0x08u)); + uint64_t iVar22 = static_cast(FAST_READ32(kFontBase + fontOff + 0x0cu)); + uint64_t iVar3 = static_cast(FAST_READ32(kFontBase + fontOff + 0x10u)); + uint64_t iVar4 = static_cast(FAST_READ32(kFontBase + fontOff + 0x14u)); + + uint64_t tex0 = iVar5 + | 0x2000000000000000ull + | (iVar22 << 14) + | 0x400000000ull + | (iVar3 << 26) + | 0x1400000ull + | (iVar4 << 30) + | (static_cast(fontClut) << 37); + + FAST_WRITE64(bufAddr + 0x70, tex0); + FAST_WRITE64(bufAddr + 0x78, 6ull); + FAST_WRITE64(bufAddr + 0x80, 0x1000000000000001ull); + FAST_WRITE64(bufAddr + 0x88, 0x0eull); + FAST_WRITE64(bufAddr + 0x90, static_cast(colour)); + FAST_WRITE64(bufAddr + 0x98, 1ull); + + iVar21 = 10; + } + + int iVar22_qw = iVar21 + 1; + uint32_t s2 = bufAddr + static_cast(iVar22_qw * 16); + uint32_t uVar20 = 0; + + size_t sLen = 0; + { + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + if (hostStr) sLen = ::strlen(hostStr); + } + + while (uVar20 < sLen) + { + uint8_t bVar1 = FAST_READ8(strAddr + uVar20); + uint32_t uVar9 = static_cast(bVar1); + int8_t chSigned = static_cast(bVar1); + + if (uStack_d8 < 0x21u) + { + goto label_check_printable; + } + + if (uVar9 > 0x20u) + { + uint32_t dat176168 = FAST_READ32(kFontBase + fontOff + 0x20u); + if (dat176168 == 0u) + { + uint32_t fontPtr0 = FAST_READ32(kFontBase + fontOff); + uint32_t tableAddr = FAST_READ32(fontPtr0 + 0x2000u); + int8_t kern = static_cast(FAST_READ8(tableAddr - 0x1c20u + uStack_d8 * 0xe0u + uVar9)); + iVar15 += static_cast(static_cast(static_cast(kern)) * sclx); + } + goto label_check_printable; + } + + goto label_space; + +label_check_printable: + if (uVar9 < 0x21u) + { + goto label_space; + } + + { + int glyphIdx = static_cast(chSigned); + uint32_t iVar19_off = static_cast(glyphIdx * 0x20); + + if (param14 != 0u) + { + uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); + int16_t sVar7 = baseX + static_cast(iVar15); + + iVar22_qw += 2; + iStack_dc += 1; + + uint16_t wU0 = FAST_READ16(fontPtr + iVar19_off + 0); + uint16_t wV0 = FAST_READ16(fontPtr + iVar19_off + 2); + FAST_WRITE16(s2 + 0x00, wU0); + FAST_WRITE16(s2 + 0x02, wV0); + + int16_t dx0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 8)); + int16_t dy0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 10)); + uint16_t wX0 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx0)) * sclx))); + int yVal0 = static_cast(static_cast(static_cast(dy0)) * scly) >> static_cast(shiftAmt); + uint16_t wY0 = static_cast(sVar8 + static_cast(yVal0)); + FAST_WRITE16(s2 + 0x08, wX0); + FAST_WRITE16(s2 + 0x0a, wY0); + FAST_WRITE32(s2 + 0x0c, 1u); + + s2 += 0x10u; + + uint16_t wU1 = FAST_READ16(fontPtr + iVar19_off + 4); + uint16_t wV1 = FAST_READ16(fontPtr + iVar19_off + 6); + FAST_WRITE16(s2 + 0x00, wU1); + FAST_WRITE16(s2 + 0x02, wV1); + + int16_t dx1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 12)); + int16_t dy1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 14)); + uint16_t wX1 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx1)) * sclx))); + int yVal1 = static_cast(static_cast(static_cast(dy1)) * scly) >> static_cast(shiftAmt); + uint16_t wY1 = static_cast(sVar8 + static_cast(yVal1)); + FAST_WRITE16(s2 + 0x08, wX1); + FAST_WRITE16(s2 + 0x0a, wY1); + FAST_WRITE32(s2 + 0x0c, 1u); + + s2 += 0x10u; + } + + { + uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); + uint32_t advOff = static_cast((glyphIdx * 2 + 1) * 16 + 8); + int16_t advW = static_cast(FAST_READ16(fontPtr + advOff)); + iVar15 += static_cast(static_cast(static_cast(advW)) * sclx); + } + } + goto label_next; + +label_space: + { + int spaceW = static_cast(FAST_READ32(kFontBase + fontOff + 0x1cu)); + iVar15 += static_cast(static_cast(spaceW) * sclx); + } + +label_next: + uStack_d8 = uVar9; + uVar20++; + } + + if (param14 != 0u) + { + if (alignCh != 'L') + { + if (alignCh == 'C' || alignCh == 'R') + { + int shift = paramW * 16 - iVar15; + if (alignCh == 'C') shift >>= 1; + if (iStack_dc > 0) + { + uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; + for (int k = 0; k < iStack_dc; k++) + { + int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); + int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); + FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(shift))); + FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(shift))); + adj += 0x20u; + } + } + } + else if (alignCh == 'J' && sLen > 1) + { + int iVar19_div = static_cast(sLen) - 1; + if (iVar19_div == 0) iVar19_div = 1; + int spacePer = (paramW * 16 - iVar15) / iVar19_div; + uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; + int accum = 0; + for (uint32_t jj = 0; jj < sLen; jj++) + { + int8_t jch = static_cast(FAST_READ8(strAddr + jj)); + if (jch > 0x20) + { + int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); + int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); + FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(accum))); + FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(accum))); + adj += 0x20u; + } + accum += spacePer; + } + } + } + + if (param14 != 0u) + { + uint32_t tagAddr = bufAddr + static_cast(iVar21 * 16); + FAST_WRITE64(tagAddr + 0x00, static_cast(static_cast(iStack_dc)) | 0x4400000000000000ull); + FAST_WRITE64(tagAddr + 0x08, 0x5353ull); + + uint32_t endAddr = bufAddr + static_cast(iVar22_qw * 16); + FAST_WRITE64(endAddr + 0x00, 0x1000000000008001ull); + FAST_WRITE64(endAddr + 0x08, 0x0eull); + + int iVar19_end = iVar22_qw + 1; + uint32_t endAddr2 = bufAddr + static_cast(iVar19_end * 16); + FAST_WRITE64(endAddr2 + 0x00, 0x01ff0000027f0000ull); + FAST_WRITE64(endAddr2 + 0x08, 0x40ull); + + iVar22_qw += 2; + } + } + + int ret = 0; + if (param14 != 0u) ret = iVar22_qw; + setReturnS32(ctx, ret); + ctx->pc = getRegU32(ctx, 31); +} + +void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t oldSp = getRegU32(ctx, 29); + const uint32_t frame = oldSp - 0x900u; + + const uint32_t bufAddr = getRegU32(ctx, 4); + const uint32_t paramX = getRegU32(ctx, 5); + const uint32_t paramY = getRegU32(ctx, 6); + const uint32_t fmtAddr = getRegU32(ctx, 7); + + const uint8_t *callerVa = getConstMemPtr(rdram, oldSp + 16u); + uint8_t *frameVa = getMemPtr(rdram, frame + 0x8f8u); + if (callerVa && frameVa) + std::memcpy(frameVa, callerVa, 64u); + + SET_GPR_U32(ctx, 4, frame + 0x20u); + SET_GPR_U32(ctx, 5, fmtAddr); + SET_GPR_U32(ctx, 6, frame + 0x8f8u); + vsprintf(rdram, ctx, runtime); + + const uint32_t gp = getRegU32(ctx, 28); + uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); + uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); + uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); + uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); + uint32_t scrWidth = FAST_READ32(gp + static_cast(static_cast(-0x7b60))); + uint32_t scrHeight = FAST_READ32(gp + static_cast(static_cast(-0x7b5c))); + + std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); + std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); + + FAST_WRITE32(frame + 0x00u, frame + 0x20u); + FAST_WRITE32(frame + 0x08u, frame + 0x820u); + FAST_WRITE32(frame + 0x10u, frame + 0x824u); + FAST_WRITE32(frame + 0x18u, 1u); + + SET_GPR_U32(ctx, 29, frame); + SET_GPR_U32(ctx, 4, bufAddr); + SET_GPR_U32(ctx, 5, paramX); + SET_GPR_U32(ctx, 6, paramY); + SET_GPR_U32(ctx, 7, scrWidth); + SET_GPR_U32(ctx, 8, scrHeight); + SET_GPR_U32(ctx, 9, defaultColour); + SET_GPR_U32(ctx, 10, 0x4cu); + SET_GPR_U32(ctx, 11, defaultFontId); + + sceeFontGenerateString(rdram, ctx, runtime); + + SET_GPR_U32(ctx, 29, oldSp); + ctx->pc = getRegU32(ctx, 31); +} + +void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t oldSp = getRegU32(ctx, 29); + const uint32_t frame = oldSp - 0x900u; + + const uint32_t bufAddr = getRegU32(ctx, 4); + const uint32_t paramX = getRegU32(ctx, 5); + const uint32_t paramY = getRegU32(ctx, 6); + const uint32_t paramW = getRegU32(ctx, 7); + const uint32_t paramH = getRegU32(ctx, 8); + const uint32_t alignRaw = getRegU32(ctx, 9); + const uint32_t fmtAddr = getRegU32(ctx, 10); + const uint64_t param8 = GPR_U64(ctx, 11); + + int8_t alignChar = static_cast(alignRaw & 0xffu); + + FAST_WRITE64(frame + 0x8f8u, param8); + + SET_GPR_U32(ctx, 4, frame + 0x20u); + SET_GPR_U32(ctx, 5, fmtAddr); + SET_GPR_U32(ctx, 6, frame + 0x8f8u); + vsprintf(rdram, ctx, runtime); + + const uint32_t gp = getRegU32(ctx, 28); + uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); + uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); + uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); + uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); + + std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); + std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); + + FAST_WRITE32(frame + 0x00u, frame + 0x20u); + FAST_WRITE32(frame + 0x08u, frame + 0x820u); + FAST_WRITE32(frame + 0x10u, frame + 0x824u); + FAST_WRITE32(frame + 0x18u, 1u); + + SET_GPR_U32(ctx, 29, frame); + SET_GPR_U32(ctx, 4, bufAddr); + SET_GPR_U32(ctx, 5, paramX); + SET_GPR_U32(ctx, 6, paramY); + SET_GPR_U32(ctx, 7, paramW); + SET_GPR_U32(ctx, 8, paramH); + SET_GPR_U32(ctx, 9, defaultColour); + SET_GPR_U32(ctx, 10, static_cast(static_cast(alignChar))); + SET_GPR_U32(ctx, 11, defaultFontId); + + sceeFontGenerateString(rdram, ctx, runtime); + + SET_GPR_U32(ctx, 29, oldSp); + ctx->pc = getRegU32(ctx, 31); +} + +void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static constexpr uint32_t kFontBase = 0x176148u; + static constexpr uint32_t kFontEntrySz = 0x24u; + const int fontId = static_cast(getRegU32(ctx, 4)); + const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); + uint32_t glyphPtr = 0; + if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff)) + glyphPtr = *reinterpret_cast(p); + if (glyphPtr != 0u) + { + if (runtime) + { + uint32_t kernPtr = 0; + if (const uint8_t *kp = getConstMemPtr(rdram, glyphPtr + 0x2000u)) + kernPtr = *reinterpret_cast(kp); + if (kernPtr != 0u) + runtime->guestFree(kernPtr); + runtime->guestFree(glyphPtr); + } + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) + *reinterpret_cast(p) = 0u; + setReturnS32(ctx, 0); + } + else + { + setReturnS32(ctx, -1); + } +} + +void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t gp = getRegU32(ctx, 28); + writeU32AtGp(rdram, gp, -0x7b4c, getRegU32(ctx, 4)); +} + +void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t gp = getRegU32(ctx, 28); + writeU32AtGp(rdram, gp, -0x7c98, getRegU32(ctx, 4)); + setReturnS32(ctx, 0); +} + +void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t gp = getRegU32(ctx, 28); + writeU32AtGp(rdram, gp, -0x7b58, getRegU32(ctx, 4)); +} + +void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t gp = getRegU32(ctx, 28); + uint32_t sclx_bits, scly_bits; + std::memcpy(&sclx_bits, &ctx->f[12], sizeof(float)); + std::memcpy(&scly_bits, &ctx->f[13], sizeof(float)); + writeU32AtGp(rdram, gp, -0x7b54, sclx_bits); + writeU32AtGp(rdram, gp, -0x7b50, scly_bits); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Font.h b/ps2xRuntime/src/lib/Kernel/Stubs/Font.h new file mode 100644 index 00000000..d1fa521f --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Font.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp similarity index 58% rename from ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl rename to ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp index f7a07f7c..47100835 100644 --- a/ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp @@ -1,3 +1,11 @@ +#include "Common.h" +#include "GS.h" +#include "ps2_log.h" +#include "runtime/ps2_gs_common.h" +#include "runtime/ps2_gs_psmct16.h" + +namespace ps2_stubs +{ namespace { std::mutex g_gs_sync_v_mutex; @@ -9,6 +17,268 @@ namespace uint32_t g_gs_sync_v_callback_stack_base = 0u; uint32_t g_gs_sync_v_callback_stack_top = 0u; uint32_t g_gs_sync_v_callback_bad_pc_logs = 0u; + struct GsDebugProbePoint + { + uint32_t x; + uint32_t y; + }; + + constexpr GsDebugProbePoint kGhostProbePoints[] = { + {220u, 176u}, + {260u, 208u}, + {320u, 208u}, + {260u, 240u}, + {320u, 240u}, + {260u, 272u}, + {320u, 272u}, + }; + + uint64_t makeClearPrim(bool useContext2) + { + return static_cast(GS_PRIM_SPRITE) | + (static_cast(useContext2 ? 1u : 0u) << 9); + } + + uint64_t makeClearRgbaq(uint32_t rgba) + { + return static_cast(rgba); + } + + uint64_t makeClearXyz(int32_t x, int32_t y) + { + return static_cast(static_cast(x << 4)) | + (static_cast(static_cast(y << 4)) << 16); + } + + void seedGsClearPacket(GsClearMem &clear, + int32_t width, + int32_t height, + uint32_t rgba, + uint32_t ztest, + bool useContext2) + { + const int32_t offX = 0x800 - (width >> 1); + const int32_t offY = 0x800 - (height >> 1); + const uint64_t clearTest = makeTest(0u); + const uint64_t restoreTest = makeTest(ztest); + const uint64_t prim = makeClearPrim(useContext2); + const uint64_t rgbaq = makeClearRgbaq(rgba); + const uint64_t xyz0 = makeClearXyz(offX, offY); + const uint64_t xyz1 = makeClearXyz(offX + width, offY + height); + const uint64_t testReg = useContext2 ? GS_REG_TEST_2 : GS_REG_TEST_1; + + clear.testa = {clearTest, testReg}; + clear.prim = {prim, GS_REG_PRIM}; + clear.rgbaq = {rgbaq, GS_REG_RGBAQ}; + clear.xyz2a = {xyz0, GS_REG_XYZ2}; + clear.xyz2b = {xyz1, GS_REG_XYZ2}; + clear.testb = {restoreTest, testReg}; + } + + bool hasSeededGsClearPacket(const GsClearMem &clear) + { + return clear.rgbaq.reg == GS_REG_RGBAQ && + clear.xyz2a.reg == GS_REG_XYZ2 && + clear.xyz2b.reg == GS_REG_XYZ2; + } + + struct GsTrailingArgs2 + { + uint32_t arg0 = 0u; + uint32_t arg1 = 0u; + }; + + struct GsTrailingArgs3 + { + uint32_t arg0 = 0u; + uint32_t arg1 = 0u; + uint32_t arg2 = 0u; + }; + + GsTrailingArgs2 decodeGsTrailingArgs2(uint8_t *rdram, R5900Context *ctx) + { + const uint32_t reg8 = getRegU32(ctx, 8); + const uint32_t reg9 = getRegU32(ctx, 9); + const uint32_t stack0 = readStackU32(rdram, ctx, 16); + const uint32_t stack1 = readStackU32(rdram, ctx, 20); + + const bool hasRegArgs = (reg8 != 0u || reg9 != 0u); + const bool hasStackArgs = (stack0 != 0u || stack1 != 0u); + if (hasRegArgs || !hasStackArgs) + { + return {reg8, reg9}; + } + + return {stack0, stack1}; + } + + GsTrailingArgs3 decodeGsTrailingArgs3(uint8_t *rdram, R5900Context *ctx) + { + const uint32_t reg8 = getRegU32(ctx, 8); + const uint32_t reg9 = getRegU32(ctx, 9); + const uint32_t reg10 = getRegU32(ctx, 10); + const uint32_t stack0 = readStackU32(rdram, ctx, 16); + const uint32_t stack1 = readStackU32(rdram, ctx, 20); + const uint32_t stack2 = readStackU32(rdram, ctx, 24); + + const bool hasRegArgs = (reg8 != 0u || reg9 != 0u || reg10 != 0u); + const bool hasStackArgs = (stack0 != 0u || stack1 != 0u || stack2 != 0u); + if (hasRegArgs || !hasStackArgs) + { + return {reg8, reg9, reg10}; + } + + return {stack0, stack1, stack2}; + } + + bool sampleFramebufferPixel(uint8_t *vram, + uint32_t vramSize, + uint32_t fbp, + uint32_t fbw, + uint32_t psm, + uint32_t x, + uint32_t y, + uint32_t &outPixel) + { + if (!vram || fbw == 0u) + { + return false; + } + + const uint32_t bytesPerPixel = + (psm == GS_PSM_CT16 || psm == GS_PSM_CT16S) ? 2u : + (psm == GS_PSM_CT32 || psm == GS_PSM_CT24) ? 4u : + 0u; + if (bytesPerPixel == 0u) + { + return false; + } + + const uint32_t widthBlocks = (fbw != 0u) ? fbw : 1u; + const uint32_t strideBytes = fbw * 64u * bytesPerPixel; + const uint32_t baseBytes = fbp * 8192u; + uint32_t offset = baseBytes + (y * strideBytes) + (x * bytesPerPixel); + if (psm == GS_PSM_CT16) + { + offset = GSPSMCT16::addrPSMCT16(GSInternal::framePageBaseToBlock(fbp), widthBlocks, x, y); + } + else if (psm == GS_PSM_CT16S) + { + offset = GSPSMCT16::addrPSMCT16S(GSInternal::framePageBaseToBlock(fbp), widthBlocks, x, y); + } + if (offset + bytesPerPixel > vramSize) + { + return false; + } + + if (bytesPerPixel == 4u) + { + std::memcpy(&outPixel, vram + offset, sizeof(outPixel)); + if (psm == GS_PSM_CT24) + { + outPixel |= 0xFF000000u; + } + return true; + } + + uint16_t packed = 0u; + std::memcpy(&packed, vram + offset, sizeof(packed)); + const uint32_t r = ((packed >> 0) & 0x1Fu) << 3; + const uint32_t g = ((packed >> 5) & 0x1Fu) << 3; + const uint32_t b = ((packed >> 10) & 0x1Fu) << 3; + const uint32_t a = (packed & 0x8000u) ? 0x80u : 0x00u; + outPixel = r | (g << 8) | (b << 16) | (a << 24); + return true; + } + + bool sampleFrameRegPixel(PS2Runtime *runtime, + uint64_t frameReg, + uint32_t x, + uint32_t y, + uint32_t &outPixel) + { + if (!runtime) + { + return false; + } + + const uint32_t fbp = static_cast(frameReg & 0x1FFu); + const uint32_t fbw = static_cast((frameReg >> 16) & 0x3Fu); + const uint32_t psm = static_cast((frameReg >> 24) & 0x3Fu); + return sampleFramebufferPixel(runtime->memory().getGSVRAM(), PS2_GS_VRAM_SIZE, fbp, fbw, psm, x, y, outPixel); + } + + bool sampleDispFbPixel(PS2Runtime *runtime, + uint64_t dispfb, + uint32_t x, + uint32_t y, + uint32_t &outPixel) + { + if (!runtime) + { + return false; + } + + const uint32_t fbp = static_cast(dispfb & 0x1FFu); + const uint32_t fbw = static_cast((dispfb >> 9) & 0x3Fu); + const uint32_t psm = static_cast((dispfb >> 15) & 0x1Fu); + return sampleFramebufferPixel(runtime->memory().getGSVRAM(), PS2_GS_VRAM_SIZE, fbp, fbw, psm, x, y, outPixel); + } + + void logSwapProbeStage(PS2Runtime *runtime, + const char *stage, + uint32_t which, + uint64_t drawFrameReg, + uint64_t dispfb, + bool hasClearPacket) + { + static uint32_t s_swapProbeCount = 0u; + if (!runtime || s_swapProbeCount >= 24u) + { + return; + } + + PS2_IF_AGRESSIVE_LOGS({ + RUNTIME_LOG("[gs:probe] stage=" << stage + << " which=" << which + << " clear=" << static_cast(hasClearPacket ? 1u : 0u)); + + for (const auto& probe : kGhostProbePoints) + { + uint32_t page0Pixel = 0u; + uint32_t page150Pixel = 0u; + const bool havePage0 = sampleFrameRegPixel(runtime, drawFrameReg, probe.x, probe.y, page0Pixel); + const bool havePage150 = sampleDispFbPixel(runtime, dispfb, probe.x, probe.y, page150Pixel); + if (havePage0) + { + RUNTIME_LOG(" p0[" << probe.x << "," << probe.y << "]=0x" + << std::hex << page0Pixel << std::dec); + } + if (havePage150) + { + RUNTIME_LOG(" p150[" << probe.x << "," << probe.y << "]=0x" + << std::hex << page150Pixel << std::dec); + } + } + RUNTIME_LOG(std::endl); + ++s_swapProbeCount; + }); + } + + void applyGsClearPacket(PS2Runtime *runtime, const GsClearMem &clear) + { + if (!runtime || !runtime->ensureCoreSubsystemsInitialized() || !hasSeededGsClearPacket(clear)) + { + return; + } + + runtime->gs().writeRegister(static_cast(clear.testa.reg & 0xFFu), clear.testa.value); + runtime->gs().writeRegister(static_cast(clear.prim.reg & 0xFFu), clear.prim.value); + runtime->gs().writeRegister(static_cast(clear.rgbaq.reg & 0xFFu), clear.rgbaq.value); + runtime->gs().writeRegister(static_cast(clear.xyz2a.reg & 0xFFu), clear.xyz2a.value); + runtime->gs().writeRegister(static_cast(clear.xyz2b.reg & 0xFFu), clear.xyz2b.value); + runtime->gs().writeRegister(static_cast(clear.testb.reg & 0xFFu), clear.testb.value); + } } static void resetGsSyncVState() @@ -66,6 +336,15 @@ void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick) if (!runtime->hasFunction(callback)) { + static uint32_t s_missingCallbackLogCount = 0u; + if (s_missingCallbackLogCount < 32u) + { + std::cerr << "[sceGsSyncVCallback:missing] cb=0x" << std::hex << callback + << " gp=0x" << gp + << " tick=0x" << callbackTick + << std::dec << std::endl; + ++s_missingCallbackLogCount; + } return; } @@ -94,6 +373,17 @@ void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick) SET_GPR_U32(&callbackCtx, 4, static_cast(callbackTick)); callbackCtx.pc = callback; + static uint32_t s_dispatchLogCount = 0u; + const bool shouldLogDispatch = (s_dispatchLogCount < 64u); + if (shouldLogDispatch) + { + RUNTIME_LOG("[sceGsSyncVCallback:dispatch] cb=0x" << std::hex << callback + << " gp=0x" << gp + << " sp=0x" << getRegU32(&callbackCtx, 29) + << " tick=0x" << callbackTick + << std::dec << std::endl); + } + uint32_t steps = 0u; while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < 1024u) { @@ -120,6 +410,16 @@ void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick) ++steps; step(rdram, &callbackCtx, runtime); } + + if (shouldLogDispatch) + { + RUNTIME_LOG("[sceGsSyncVCallback:return] cb=0x" << std::hex << callback + << " finalPc=0x" << callbackCtx.pc + << " ra=0x" << getRegU32(&callbackCtx, 31) + << " steps=0x" << steps + << std::dec << std::endl); + ++s_dispatchLogCount; + } } catch (const std::exception &e) { @@ -138,7 +438,7 @@ void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t srcAddr = getRegU32(ctx, 5); GsImageMem img{}; - if (!runtime || !readGsImage(rdram, imgAddr, img)) + if (!runtime || !runtime->ensureCoreSubsystemsInitialized() || !readGsImage(rdram, imgAddr, img)) { setReturnS32(ctx, -1); return; @@ -153,7 +453,7 @@ void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); const uint32_t totalImageBytes = rowBytes * static_cast(img.height); - const uint32_t headerQwc = 12u; + const uint32_t headerQwc = 6u; const uint32_t imageQwc = (totalImageBytes + 15u) / 16u; const uint32_t totalQwc = headerQwc + imageQwc; @@ -168,6 +468,7 @@ void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint8_t *src = getConstMemPtr(rdram, srcAddr); if (!pkt || !src) { + runtime->guestFree(pktAddr); setReturnS32(ctx, -1); return; } @@ -178,8 +479,8 @@ void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) // Full messy uint64_t *q = reinterpret_cast(pkt); - q[0] = 0x1000000000000004ULL; - q[1] = 0x0E0E0E0E0E0E0E0EULL; + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; q[2] = (static_cast(img.psm & 0x3Fu) << 24) | (static_cast(1u) << 16) | (static_cast(dbp & 0x3FFFu) << 32) | (static_cast(fbw & 0x3Fu) << 48) | (static_cast(img.psm & 0x3Fu) << 56); @@ -194,7 +495,7 @@ void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) (1ULL << 15); q[11] = 0ULL; - std::memcpy(pkt + 12 * 8, src, totalImageBytes); + std::memcpy(pkt + headerQwc * 16u, src, totalImageBytes); constexpr uint32_t GIF_CHANNEL = 0x1000A000; constexpr uint32_t CHCR_STR_MODE0 = 0x101u; @@ -202,6 +503,8 @@ void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); mem.writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + mem.processPendingTransfers(); + runtime->guestFree(pktAddr); setReturnS32(ctx, 0); } @@ -212,7 +515,7 @@ void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t dstAddr = getRegU32(ctx, 5); GsImageMem img{}; - if (!runtime || !readGsImage(rdram, imgAddr, img)) + if (!runtime || !runtime->ensureCoreSubsystemsInitialized() || !readGsImage(rdram, imgAddr, img)) { setReturnS32(ctx, -1); return; @@ -258,13 +561,14 @@ void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint8_t *pkt = getMemPtr(rdram, pktAddr); if (!pkt) { + runtime->guestFree(pktAddr); setReturnS32(ctx, -1); return; } uint64_t *q = reinterpret_cast(pkt); - q[0] = 0x1000000000000004ULL; - q[1] = 0x0E0E0E0E0E0E0E0EULL; + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; q[2] = bitbltbuf; q[3] = 0x50ULL; q[4] = trxpos; @@ -283,6 +587,7 @@ void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) mem.processPendingTransfers(); runtime->gs().consumeLocalToHostBytes(dst, totalImageBytes); + runtime->guestFree(pktAddr); setReturnS32(ctx, 0); } @@ -328,6 +633,12 @@ void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) if (mode == 0) { + if (runtime && !runtime->ensureCoreSubsystemsInitialized()) + { + setReturnS32(ctx, -1); + return; + } + g_gparam.interlace = static_cast(interlace & 0x1); g_gparam.omode = static_cast(omode & 0xFF); g_gparam.ffmode = static_cast(ffmode & 0x1); @@ -342,15 +653,15 @@ void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) if (runtime) { - uint32_t pktAddr = runtime->guestMalloc(192u, 16u); + uint32_t pktAddr = runtime->guestMalloc(128u, 16u); if (pktAddr != 0u) { uint8_t *pkt = getMemPtr(rdram, pktAddr); if (pkt) { uint64_t *q = reinterpret_cast(pkt); - q[0] = 0x1000000000000005ULL; - q[1] = 0x0E0E0E0E0E0E0E0EULL; + q[0] = makeGiftagAplusD(7u); + q[1] = 0xEULL; q[2] = pmode; q[3] = 0x41ULL; q[4] = smode2; @@ -359,14 +670,24 @@ void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) q[7] = 0x59ULL; q[8] = display; q[9] = 0x5aULL; - q[10] = bgcolor; - q[11] = 0x5fULL; + q[10] = dispfb; + q[11] = 0x5bULL; + q[12] = display; + q[13] = 0x5cULL; + q[14] = bgcolor; + q[15] = 0x5fULL; constexpr uint32_t GIF_CHANNEL = 0x1000A000; constexpr uint32_t CHCR_STR_MODE0 = 0x101u; auto &mem = runtime->memory(); mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - mem.writeIORegister(GIF_CHANNEL + 0x20u, 12u); + mem.writeIORegister(GIF_CHANNEL + 0x20u, 8u); mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + mem.processPendingTransfers(); + runtime->guestFree(pktAddr); + } + else + { + runtime->guestFree(pktAddr); } } } @@ -394,10 +715,10 @@ void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t psm = getRegU32(ctx, 5); uint32_t w = getRegU32(ctx, 6); uint32_t h = getRegU32(ctx, 7); - const uint32_t ztest = readStackU32(rdram, ctx, 16); - const uint32_t zpsm = readStackU32(rdram, ctx, 20); - const uint32_t clear = readStackU32(rdram, ctx, 24); - (void)clear; + const GsTrailingArgs3 trailing = decodeGsTrailingArgs3(rdram, ctx); + const uint32_t ztest = trailing.arg0; + const uint32_t zpsm = trailing.arg1; + const uint32_t clear = trailing.arg2; if (w == 0u) { @@ -413,7 +734,6 @@ void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint64_t smode2 = (static_cast(g_gparam.interlace & 0x1u) << 0) | (static_cast(g_gparam.ffmode & 0x1u) << 1); - const uint64_t dispfb = makeDispFb(0u, fbw, psm, 0u, 0u); const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u); const int32_t drawWidth = static_cast(w); @@ -426,81 +746,34 @@ void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) zbufAddr = getRegU32(&temp, 2); } + const uint32_t fbp1 = zbufAddr; + const uint64_t dispfb0 = makeDispFb(fbp1, fbw, psm, 0u, 0u); + const uint64_t dispfb1 = makeDispFb(0u, fbw, psm, 0u, 0u); + + GsDBuffDcMem db{}; db.disp[0].pmode = pmode; db.disp[0].smode2 = smode2; - db.disp[0].dispfb = dispfb; + db.disp[0].dispfb = dispfb0; db.disp[0].display = display; db.disp[0].bgcolor = 0u; db.disp[1] = db.disp[0]; + db.disp[1].dispfb = dispfb1; - db.giftag0 = {makeGiftagAplusD(14u), 0x0E0E0E0E0E0E0E0EULL}; + const bool seedClear = clear != 0u; + db.giftag0 = {makeGiftagAplusD(seedClear ? 22u : 16u), 0xEULL}; seedGsDrawEnv1(db.draw01, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); seedGsDrawEnv2(db.draw02, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); db.giftag1 = db.giftag0; - seedGsDrawEnv1(db.draw11, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); - seedGsDrawEnv2(db.draw12, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); - - if (!writeGsDBuffDc(rdram, envAddr, db)) - { - setReturnS32(ctx, -1); - return; - } - setReturnS32(ctx, 0); -} - -void sceGsSetDefDBuff(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) -{ - const uint32_t envAddr = getRegU32(ctx, 4); - uint32_t psm = getRegU32(ctx, 5); - uint32_t w = getRegU32(ctx, 6); - uint32_t h = getRegU32(ctx, 7); - const uint32_t ztest = readStackU32(rdram, ctx, 16); - const uint32_t zpsm = readStackU32(rdram, ctx, 20); - const uint32_t clear = readStackU32(rdram, ctx, 24); - (void)clear; - - if (w == 0u) - { - w = 640u; - } - if (h == 0u) + seedGsDrawEnv1(db.draw11, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false); + seedGsDrawEnv2(db.draw12, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false); + if (seedClear) { - h = 448u; + seedGsClearPacket(db.clear0, drawWidth, drawHeight, 0u, ztest, false); + seedGsClearPacket(db.clear1, drawWidth, drawHeight, 0u, ztest, true); } - const uint32_t fbw = std::max(1u, (w + 63u) / 64u); - const uint64_t pmode = makePmode(1u, 1u, 0u, 0u, 0u, 0x80u); - const uint64_t smode2 = - (static_cast(g_gparam.interlace & 0x1u) << 0) | - (static_cast(g_gparam.ffmode & 0x1u) << 1); - const uint64_t dispfb = makeDispFb(0u, fbw, psm, 0u, 0u); - const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u); - - const int32_t drawWidth = static_cast(w); - const int32_t drawHeight = static_cast(h); - - uint32_t zbufAddr = 0u; - { - R5900Context temp = *ctx; - sceGszbufaddr(rdram, &temp, runtime); - zbufAddr = getRegU32(&temp, 2); - } - - GsDBuffMem db{}; - db.disp[0].pmode = pmode; - db.disp[0].smode2 = smode2; - db.disp[0].dispfb = dispfb; - db.disp[0].display = display; - db.disp[0].bgcolor = 0u; - db.disp[1] = db.disp[0]; - - db.giftag0 = { makeGiftagAplusD(14u), 0x0E0E0E0E0E0E0E0EULL }; - seedGsDrawEnv1(db.draw0, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); - db.giftag1 = db.giftag0; - seedGsDrawEnv1(db.draw1, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); - - if (!writeGsDBuff(rdram, envAddr, db)) + if (!writeGsDBuffDc(rdram, envAddr, db)) { setReturnS32(ctx, -1); return; @@ -514,8 +787,9 @@ void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t psm = getRegU32(ctx, 5); uint32_t w = getRegU32(ctx, 6); uint32_t h = getRegU32(ctx, 7); - uint32_t dx = readStackU32(rdram, ctx, 16); - uint32_t dy = readStackU32(rdram, ctx, 20); + const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); + uint32_t dx = trailing.arg0; + uint32_t dy = trailing.arg1; if (w == 0) w = 640; @@ -536,8 +810,9 @@ void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t param_2 = getRegU32(ctx, 5); int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); - uint32_t param_5 = readStackU32(rdram, ctx, 16); - uint32_t param_6 = readStackU32(rdram, ctx, 20); + const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); + uint32_t param_5 = trailing.arg0; + uint32_t param_6 = trailing.arg1; if (w <= 0) w = 640; @@ -578,8 +853,9 @@ void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) uint32_t param_2 = getRegU32(ctx, 5); int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); - uint32_t param_5 = readStackU32(rdram, ctx, 16); - uint32_t param_6 = readStackU32(rdram, ctx, 20); + const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); + uint32_t param_5 = trailing.arg0; + uint32_t param_6 = trailing.arg1; if (w <= 0) w = 640; @@ -649,41 +925,55 @@ void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) return; } + const bool hasClearPacket = (which == 0u) ? hasSeededGsClearPacket(db.clear0) + : hasSeededGsClearPacket(db.clear1); + const uint64_t debugDrawFrameReg = (which == 0u) ? db.draw01.frame1.value + : db.draw11.frame1.value; + applyGsDispEnv(runtime, db.disp[which]); + static uint32_t s_swapDbuffLogCount = 0u; + if (s_swapDbuffLogCount < 32u) + { + const uint32_t dispFbp = static_cast(db.disp[which].dispfb & 0x1FFu); + const uint32_t clearContext = (which == 0u) + ? static_cast((db.clear0.prim.value >> 9) & 0x1u) + : static_cast((db.clear1.prim.value >> 9) & 0x1u); + RUNTIME_LOG("[gs:swapdbuff] which=" << which + << " env=0x" << std::hex << envAddr + << " dispfb=0x" << db.disp[which].dispfb + << " display=0x" << db.disp[which].display + << " pmode=0x" << db.disp[which].pmode + << " dispFbp=" << dispFbp + << " clearCtxt=" << clearContext + << std::dec << std::endl); + ++s_swapDbuffLogCount; + } + logSwapProbeStage(runtime, "swap-pre", which, debugDrawFrameReg, db.disp[which].dispfb, hasClearPacket); if (which == 0u) { applyGsRegPairs(runtime, reinterpret_cast(&db.draw01), 8u); applyGsRegPairs(runtime, reinterpret_cast(&db.draw02), 8u); + if (hasSeededGsClearPacket(db.clear0)) + { + const uint32_t clearContext = static_cast((db.clear0.prim.value >> 9) & 0x1u); + runtime->gs().clearFramebufferContext(clearContext, static_cast(db.clear0.rgbaq.value)); + logSwapProbeStage(runtime, "swap-post-clear", which, db.draw01.frame1.value, db.disp[which].dispfb, true); + } + applyGsClearPacket(runtime, db.clear0); + logSwapProbeStage(runtime, "swap-post", which, db.draw01.frame1.value, db.disp[which].dispfb, hasClearPacket); } else { applyGsRegPairs(runtime, reinterpret_cast(&db.draw11), 8u); applyGsRegPairs(runtime, reinterpret_cast(&db.draw12), 8u); - } - - setReturnS32(ctx, static_cast(which ^ 1u)); -} - -void sceGsSwapDBuff(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) -{ - const uint32_t envAddr = getRegU32(ctx, 4); - const uint32_t which = getRegU32(ctx, 5) & 1u; - - GsDBuffMem db{}; - if (!runtime || !readGsDBuff(rdram, envAddr, db)) - { - setReturnS32(ctx, -1); - return; - } - - applyGsDispEnv(runtime, db.disp[which]); - if (which == 0u) - { - applyGsRegPairs(runtime, reinterpret_cast(&db.draw0), 8u); - } - else - { - applyGsRegPairs(runtime, reinterpret_cast(&db.draw1), 8u); + if (hasSeededGsClearPacket(db.clear1)) + { + const uint32_t clearContext = static_cast((db.clear1.prim.value >> 9) & 0x1u); + runtime->gs().clearFramebufferContext(clearContext, static_cast(db.clear1.rgbaq.value)); + logSwapProbeStage(runtime, "swap-post-clear", which, db.draw11.frame1.value, db.disp[which].dispfb, true); + } + applyGsClearPacket(runtime, db.clear1); + logSwapProbeStage(runtime, "swap-post", which, db.draw11.frame1.value, db.disp[which].dispfb, hasClearPacket); } setReturnS32(ctx, static_cast(which ^ 1u)); @@ -791,13 +1081,13 @@ void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) static uint32_t s_syncVCallbackLogCount = 0u; if (s_syncVCallbackLogCount < 128u) { - std::cout << "[sceGsSyncVCallback:set] new=0x" << std::hex << newCallback + RUNTIME_LOG("[sceGsSyncVCallback:set] new=0x" << std::hex << newCallback << " old=0x" << oldCallback << " callerPc=0x" << callerPc << " callerRa=0x" << callerRa << " gp=0x" << gp << " sp=0x" << sp - << std::dec << std::endl; + << std::dec << std::endl); ++s_syncVCallbackLogCount; } @@ -854,3 +1144,14 @@ void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) setReturnS32(ctx, product); } +void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + RUNTIME_LOG("ps2_stub Ps2SwapDBuff"); + ++logCount; + } + setReturnS32(ctx, 0); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/GS.h b/ps2xRuntime/src/lib/Kernel/Stubs/GS.h new file mode 100644 index 00000000..82ce7b9d --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.h @@ -0,0 +1,29 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void resetGsSyncVCallbackState(); + void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick); + void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h similarity index 85% rename from ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl rename to ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h index d09bf1cb..831c1f83 100644 --- a/ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h @@ -1,7 +1,3 @@ -#ifndef PS2_CD_REMAP_IDX_TO_AFS -#define PS2_CD_REMAP_IDX_TO_AFS 1 -#endif - namespace { constexpr uint32_t kCdSectorSize = 2048; @@ -17,12 +13,10 @@ namespace std::unordered_map g_cdFilesByKey; std::unordered_map g_cdLeafIndex; + std::unordered_map g_cdLoosePathIndex; std::filesystem::path g_cdLeafIndexRoot; bool g_cdLeafIndexBuilt = false; uint32_t g_nextPseudoLbn = kCdPseudoLbnStart; - std::filesystem::path g_cdAutoImagePath; - std::filesystem::path g_cdAutoImageRoot; - bool g_cdAutoImageSearched = false; std::filesystem::path g_cdImageSizePath; uint64_t g_cdImageSizeBytes = 0; bool g_cdImageSizeValid = false; @@ -109,167 +103,83 @@ namespace return path; } - std::filesystem::path getCdRootPath() + std::string normalizeCdLooseNumericKey(std::string value) { - const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - if (!paths.cdRoot.empty()) - { - return paths.cdRoot; - } - if (!paths.elfDirectory.empty()) + value = toLowerAscii(stripIsoVersionSuffix(normalizePathSeparators(std::move(value)))); + std::string normalized; + normalized.reserve(value.size()); + for (std::size_t i = 0; i < value.size();) { - return paths.elfDirectory; - } - - std::error_code ec; - const std::filesystem::path cwd = std::filesystem::current_path(ec); - return ec ? std::filesystem::path(".") : cwd.lexically_normal(); - } - - bool hasCdImageExtension(const std::filesystem::path &path) - { - const std::string ext = toLowerAscii(path.extension().string()); - return ext == ".iso" || ext == ".bin" || ext == ".img" || ext == ".mdf" || ext == ".nrg"; - } - - bool trySelectBestDiscImageFromDirectory(const std::filesystem::path &dir, - std::filesystem::path &pathOut) - { - std::error_code ec; - if (!std::filesystem::exists(dir, ec) || ec || !std::filesystem::is_directory(dir, ec)) - { - return false; - } - - std::filesystem::path bestPath; - uint64_t bestSize = 0; - for (const auto &entry : std::filesystem::directory_iterator( - dir, std::filesystem::directory_options::skip_permission_denied, ec)) - { - if (ec) - { - break; - } - - if (!entry.is_regular_file()) - { - continue; - } - if (!hasCdImageExtension(entry.path())) + if (!std::isdigit(static_cast(value[i]))) { + normalized.push_back(value[i]); + ++i; continue; } - std::error_code sizeEc; - const uint64_t size = static_cast(entry.file_size(sizeEc)); - if (sizeEc || size < (64ull * 1024ull * 1024ull)) + std::size_t end = i + 1; + while (end < value.size() && std::isdigit(static_cast(value[end]))) { - continue; + ++end; } - if (size > bestSize) + std::size_t firstNonZero = i; + while (firstNonZero + 1 < end && value[firstNonZero] == '0') { - bestSize = size; - bestPath = entry.path(); + ++firstNonZero; } - } - if (bestPath.empty()) - { - return false; + normalized.append(value, firstNonZero, end - firstNonZero); + i = end; } - pathOut = bestPath; - return true; + return normalized; } - std::filesystem::path autoDetectCdImagePath() + std::string cdLoosePathKeyFromRelative(const std::filesystem::path &relative) { - const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - std::vector roots; - - const std::filesystem::path cdRoot = getCdRootPath(); - if (!cdRoot.empty()) + const std::string normalized = normalizeCdPathNoPrefix(relative.generic_string()); + if (normalized.empty()) { - roots.push_back(cdRoot); - std::filesystem::path parent = cdRoot; - for (int i = 0; i < 4; ++i) - { - parent = parent.parent_path(); - if (parent.empty()) - { - break; - } - roots.push_back(parent); - } + return {}; } - if (!paths.hostRoot.empty()) - { - roots.push_back(paths.hostRoot); - } - if (!paths.elfDirectory.empty()) - { - roots.push_back(paths.elfDirectory); - } + const std::filesystem::path relPath(normalized); + std::string parent = toLowerAscii(normalizePathSeparators(relPath.parent_path().generic_string())); + const std::string leaf = normalizeCdLooseNumericKey(relPath.filename().string()); - std::filesystem::path bestPath; - uint64_t bestSize = 0; - std::unordered_set seenRoots; - for (const std::filesystem::path &root : roots) + if (parent.empty()) { - if (root.empty()) - { - continue; - } - - const std::string key = toLowerAscii(root.lexically_normal().string()); - if (!seenRoots.emplace(key).second) - { - continue; - } - - std::filesystem::path candidate; - if (!trySelectBestDiscImageFromDirectory(root, candidate)) - { - continue; - } - - std::error_code sizeEc; - const uint64_t size = static_cast(std::filesystem::file_size(candidate, sizeEc)); - if (sizeEc || size <= bestSize) - { - continue; - } - - bestSize = size; - bestPath = candidate; + return leaf; } + return parent + "/" + leaf; + } - if (!bestPath.empty()) - { - std::cout << "[CD] Auto-detected disc image: " << bestPath.string() << std::endl; - } - return bestPath; + std::string cdLoosePathKey(const std::string &ps2Path) + { + return cdLoosePathKeyFromRelative(std::filesystem::path(normalizeCdPathNoPrefix(ps2Path))); } - std::filesystem::path getCdImagePath() + std::filesystem::path getCdRootPath() { const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - if (!paths.cdImage.empty()) + if (!paths.cdRoot.empty()) { - return paths.cdImage; + return paths.cdRoot; } - - const std::filesystem::path cdRoot = getCdRootPath(); - if (!g_cdAutoImageSearched || g_cdAutoImageRoot != cdRoot) + if (!paths.elfDirectory.empty()) { - g_cdAutoImageRoot = cdRoot; - g_cdAutoImagePath = autoDetectCdImagePath(); - g_cdAutoImageSearched = true; + return paths.elfDirectory; } - return g_cdAutoImagePath; + std::error_code ec; + const std::filesystem::path cwd = std::filesystem::current_path(ec); + return ec ? std::filesystem::path(".") : cwd.lexically_normal(); + } + + std::filesystem::path getCdImagePath() + { + return PS2Runtime::getIoPaths().cdImage; } bool tryGetCdImageTotalSectors(uint64_t &totalSectorsOut) @@ -375,6 +285,7 @@ namespace } g_cdLeafIndex.clear(); + g_cdLoosePathIndex.clear(); g_cdLeafIndexRoot = root; g_cdLeafIndexBuilt = true; @@ -398,6 +309,17 @@ namespace const std::string leaf = toLowerAscii(entry.path().filename().string()); g_cdLeafIndex.emplace(leaf, entry.path()); + + std::error_code relEc; + const std::filesystem::path relative = std::filesystem::relative(entry.path(), root, relEc); + if (!relEc) + { + const std::string looseKey = cdLoosePathKeyFromRelative(relative); + if (!looseKey.empty()) + { + g_cdLoosePathIndex.emplace(looseKey, entry.path()); + } + } } } @@ -442,8 +364,18 @@ namespace } else { - g_lastCdError = -1; - return false; + const std::string looseKey = cdLoosePathKey(ps2Path); + auto looseIt = g_cdLoosePathIndex.find(looseKey); + if (looseIt != g_cdLoosePathIndex.end()) + { + path = looseIt->second; + ec.clear(); + } + else + { + g_lastCdError = -1; + return false; + } } } } @@ -584,73 +516,6 @@ namespace return true; } - bool hostFileHasAfsMagic(const std::filesystem::path &path) - { - std::ifstream file(path, std::ios::binary); - if (!file.is_open()) - { - return false; - } - - char magic[4] = {}; - file.read(magic, sizeof(magic)); - if (file.gcount() < 3) - { - return false; - } - - return magic[0] == 'A' && magic[1] == 'F' && magic[2] == 'S'; - } - - bool tryRemapGdInitSearchToAfs(const std::string &ps2Path, - uint32_t callerRa, - const CdFileEntry &foundEntry, - CdFileEntry &entryOut, - std::string &resolvedPathOut) - { -#if !PS2_CD_REMAP_IDX_TO_AFS - { - return false; - } -#endif - - if (callerRa != 0x2d9444u) - { - return false; - } - - std::filesystem::path relative(normalizeCdPathNoPrefix(ps2Path)); - const std::string ext = toLowerAscii(relative.extension().string()); - const std::string leaf = toLowerAscii(relative.filename().string()); - - if (ext == ".idx") - { - if (foundEntry.sizeBytes > (kCdSectorSize * 8u)) - { - return false; - } - - std::filesystem::path afsRelative = relative; - afsRelative.replace_extension(".AFS"); - - CdFileEntry afsEntry; - if (!registerCdFile(afsRelative.generic_string(), afsEntry)) - { - return false; - } - if (!hostFileHasAfsMagic(afsEntry.hostPath)) - { - return false; - } - - entryOut = afsEntry; - resolvedPathOut = afsRelative.generic_string(); - return true; - } - - return false; - } - uint8_t toBcd(uint32_t value) { const uint32_t clamped = value % 100; @@ -925,13 +790,13 @@ namespace private: uint32_t readWordAtSlot(uint32_t slotIndex) const { - if (slotIndex < 4u) + if (slotIndex < 8u) { - // slot0..slot3 -> a0..a3 (r4..r7) + // EE calls use eight integer argument registers (a0-a3, t0-t3 / r4-r11). return getRegU32(m_ctx, 4 + static_cast(slotIndex)); } - const uint32_t stackIndex = slotIndex - 4u; + const uint32_t stackIndex = slotIndex - 8u; const uint32_t stackAddr = m_stackBase + stackIndex * 4u; uint32_t value = 0; (void)tryReadWordFromGuest(m_rdram, m_runtime, stackAddr, value); @@ -1006,15 +871,20 @@ namespace int parsedWidth = -1; int parsedPrecision = -1; + bool widthSpecified = false; + bool precisionSpecified = false; + std::string parsedFlags; while (*p && std::strchr("-+ #0", *p)) { + parsedFlags.push_back(*p); ++p; } if (*p == '*') { parsedWidth = static_cast(nextU32()); + widthSpecified = true; ++p; } else @@ -1022,6 +892,7 @@ namespace if (*p && std::isdigit(static_cast(*p))) { parsedWidth = 0; + widthSpecified = true; } while (*p && std::isdigit(static_cast(*p))) { @@ -1033,6 +904,7 @@ namespace if (*p == '.') { ++p; + precisionSpecified = true; if (*p == '*') { parsedPrecision = static_cast(nextU32()); @@ -1052,7 +924,6 @@ namespace { parsedPrecision = -1; } - (void)parsedWidth; enum class LengthMod { @@ -1121,6 +992,51 @@ namespace break; } + auto buildHostSpec = [&](char spec, const char *lengthOverride = nullptr) -> std::string + { + std::string specText; + specText.reserve(32); + specText.push_back('%'); + + if (widthSpecified && parsedWidth < 0 && + parsedFlags.find('-') == std::string::npos) + { + specText.push_back('-'); + } + + specText.append(parsedFlags); + if (widthSpecified) + { + const int hostWidth = (parsedWidth < 0) ? -parsedWidth : parsedWidth; + specText.append(std::to_string(hostWidth)); + } + if (precisionSpecified) + { + specText.push_back('.'); + specText.append(std::to_string(std::max(parsedPrecision, 0))); + } + if (lengthOverride != nullptr) + { + specText.append(lengthOverride); + } + specText.push_back(spec); + return specText; + }; + + auto appendFormatted = [&](const std::string &specText, auto value) -> bool + { + const int needed = std::snprintf(nullptr, 0, specText.c_str(), value); + if (needed < 0) + { + return false; + } + + std::string chunk(static_cast(needed), '\0'); + std::snprintf(chunk.data(), chunk.size() + 1u, specText.c_str(), value); + out.append(chunk); + return true; + }; + const bool use64Integer = (length == LengthMod::LL || length == LengthMod::J); auto readUnsignedInteger = [&]() -> uint64_t { @@ -1141,59 +1057,72 @@ namespace case 's': { const uint32_t strAddr = nextU32(); - if (strAddr == 0) + const char *text = "(null)"; + std::string ownedText; + if (strAddr != 0u) { - out.append("(null)"); + ownedText = readString(strAddr); + text = ownedText.c_str(); } - else + if (!appendFormatted(buildHostSpec(spec), text)) { - std::string str = readString(strAddr); - if (parsedPrecision >= 0 && - str.size() > static_cast(parsedPrecision)) - { - str.resize(static_cast(parsedPrecision)); - } - out.append(str); + out.append(text); } break; } case 'c': { - const char ch = static_cast(nextU32() & 0xFF); - out.push_back(ch); + const int ch = static_cast(nextU32() & 0xFFu); + if (!appendFormatted(buildHostSpec(spec), ch)) + { + out.push_back(static_cast(ch)); + } break; } case 'd': case 'i': - out.append(std::to_string(readSignedInteger())); - break; - case 'u': - out.append(std::to_string(readUnsignedInteger())); - break; - case 'x': - case 'X': { - std::ostringstream ss; - if (spec == 'X') + const long long value = static_cast(readSignedInteger()); + if (!appendFormatted(buildHostSpec(spec, "ll"), value)) { - ss.setf(std::ios::uppercase); + out.append(std::to_string(value)); } - ss << std::hex << readUnsignedInteger(); - out.append(ss.str()); break; } + case 'u': + case 'x': + case 'X': case 'o': { - std::ostringstream ss; - ss << std::oct << readUnsignedInteger(); - out.append(ss.str()); + const unsigned long long value = static_cast(readUnsignedInteger()); + if (!appendFormatted(buildHostSpec(spec, "ll"), value)) + { + std::ostringstream ss; + if (spec == 'o') + { + ss << std::oct << value; + } + else + { + if (spec == 'X') + { + ss.setf(std::ios::uppercase); + } + ss << std::hex << value; + } + out.append(ss.str()); + } break; } case 'p': { - std::ostringstream ss; - ss << "0x" << std::hex << nextU32(); - out.append(ss.str()); + const uint32_t ptrValue = nextU32(); + if (!appendFormatted(buildHostSpec(spec), reinterpret_cast(static_cast(ptrValue)))) + { + std::ostringstream ss; + ss << "0x" << std::hex << ptrValue; + out.append(ss.str()); + } break; } case 'f': @@ -1208,9 +1137,17 @@ namespace const uint64_t bits = nextU64(); double value = 0.0; std::memcpy(&value, &bits, sizeof(value)); - char numBuf[128]; - std::snprintf(numBuf, sizeof(numBuf), "%g", value); - out.append(numBuf); + if (length == LengthMod::BigL) + { + if (!appendFormatted(buildHostSpec(spec, "L"), static_cast(value))) + { + out.append(std::to_string(value)); + } + } + else if (!appendFormatted(buildHostSpec(spec), value)) + { + out.append(std::to_string(value)); + } break; } case 'n': @@ -1418,11 +1355,11 @@ namespace g_dmaPendingPolls[channelBase] = 1; if (g_dmaStubLogCount < kMaxDmaStubLogs) { - std::cout << "[sceDmaSend] ch=0x" << std::hex << channelBase + RUNTIME_LOG("[sceDmaSend] ch=0x" << std::hex << channelBase << " madr=0x" << madr << " qwc=0x" << qwc << " tadr=0x" << tadr - << " chcr=0x" << chcr << std::dec << std::endl; + << " chcr=0x" << chcr << std::dec << std::endl); ++g_dmaStubLogCount; } @@ -1552,17 +1489,6 @@ namespace GsClearMem clear1; }; - struct GsDBuffMem - { - GsDispEnvMem disp[2]; - GsGiftagMem giftag0; - GsDrawEnv1Mem draw0; - GsClearMem clear0; - GsGiftagMem giftag1; - GsDrawEnv1Mem draw1; - GsClearMem clear1; - }; - struct GsImageMem { uint16_t x; @@ -1820,15 +1746,6 @@ namespace return true; } - static bool readGsDBuff(uint8_t* rdram, uint32_t addr, GsDBuffMem& out) - { - const uint8_t* ptr = getConstMemPtr(rdram, addr); - if (!ptr) - return false; - std::memcpy(&out, ptr, sizeof(out)); - return true; - } - static bool writeGsDBuffDc(uint8_t *rdram, uint32_t addr, const GsDBuffDcMem &db) { uint8_t *ptr = getMemPtr(rdram, addr); @@ -1838,15 +1755,6 @@ namespace return true; } - static bool writeGsDBuff(uint8_t* rdram, uint32_t addr, const GsDBuffMem& db) - { - uint8_t* ptr = getMemPtr(rdram, addr); - if (!ptr) - return false; - std::memcpy(ptr, &db, sizeof(db)); - return true; - } - static bool readGsRegPairs(uint8_t *rdram, uint32_t addr, GsRegPairMem *pairs, size_t pairCount) { if (!pairs || pairCount == 0u) @@ -1860,19 +1768,21 @@ namespace static void applyGsDispEnv(PS2Runtime *runtime, const GsDispEnvMem &env) { - if (!runtime) + if (!runtime || !runtime->ensureCoreSubsystemsInitialized()) return; auto ®s = runtime->memory().gs(); regs.pmode = env.pmode; regs.smode2 = env.smode2; regs.dispfb1 = env.dispfb; regs.display1 = env.display; + regs.dispfb2 = env.dispfb; + regs.display2 = env.display; regs.bgcolor = env.bgcolor; } static void applyGsRegPairs(PS2Runtime *runtime, const GsRegPairMem *pairs, size_t pairCount) { - if (!runtime || !pairs) + if (!runtime || !pairs || !runtime->ensureCoreSubsystemsInitialized()) return; for (size_t i = 0; i < pairCount; ++i) { diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp new file mode 100644 index 00000000..35761848 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp @@ -0,0 +1,97 @@ +#include "Common.h" +#include "IPU.h" + +namespace ps2_stubs +{ +void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static constexpr uint32_t REG_IPU_CTRL = 0x10002010u; + static constexpr uint32_t REG_IPU_CMD = 0x10002000u; + static constexpr uint32_t REG_IPU_IN_FIFO = 0x10007010u; + static constexpr uint32_t IQVAL_BASE = 0x1721e0u; + static constexpr uint32_t VQVAL_BASE = 0x172230u; + static constexpr uint32_t SETD4_CHCR_ENTRY = 0x126428u; + + if (!runtime) + return; + + if (!runtime->memory().getRDRAM()) + { + if (!runtime->memory().initialize()) + { + setReturnS32(ctx, -1); + return; + } + } + + if (!runtime->ensureCoreSubsystemsInitialized()) + { + setReturnS32(ctx, -1); + return; + } + + PS2Memory &mem = runtime->memory(); + + if (runtime->hasFunction(SETD4_CHCR_ENTRY)) + { + auto setD4 = runtime->lookupFunction(SETD4_CHCR_ENTRY); + ctx->r[4] = _mm_set_epi64x(0, 1); + { + PS2Runtime::GuestExecutionScope guestExecution(runtime); + setD4(rdram, ctx, runtime); + } + } + + mem.write32(REG_IPU_CTRL, 0x40000000u); + mem.write32(REG_IPU_CMD, 0u); + + __m128i v; + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x00u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x10u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x20u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x30u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x40u); + mem.write128(REG_IPU_IN_FIFO, v); + mem.write128(REG_IPU_IN_FIFO, v); + mem.write128(REG_IPU_IN_FIFO, v); + mem.write128(REG_IPU_IN_FIFO, v); + + mem.write32(REG_IPU_CMD, 0x50000000u); + mem.write32(REG_IPU_CMD, 0x58000000u); + + v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x00u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x10u); + mem.write128(REG_IPU_IN_FIFO, v); + + mem.write32(REG_IPU_CMD, 0x60000000u); + mem.write32(REG_IPU_CMD, 0x90000000u); + + mem.write32(REG_IPU_CTRL, 0x40000000u); + mem.write32(REG_IPU_CMD, 0u); + + setReturnS32(ctx, 0); +} + + +void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + + +void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + + +void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/IPU.h b/ps2xRuntime/src/lib/Kernel/Stubs/IPU.h new file mode 100644 index 00000000..17d902ff --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/IPU.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp similarity index 84% rename from ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl rename to ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp index 94ff6599..f7cbdb2f 100644 --- a/ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl +++ b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp @@ -1,3 +1,9 @@ +#include "Common.h" +#include "LibC.h" +#include "ps2_log.h" + +namespace ps2_stubs +{ namespace { uint32_t sanitizeMemTransferSize(uint32_t size, const char *op) @@ -483,21 +489,23 @@ void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { rendered.resize(2048); } - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - std::cout << "PS2 printf: " << logLine; - std::cout << std::flush; - } - else if (count == kMaxPrintfLogs + 1) - { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + RUNTIME_LOG("PS2 printf: " << logLine); + RUNTIME_LOG(std::flush); + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + }); ret = static_cast(rendered.size()); } else @@ -529,14 +537,14 @@ void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t arg0 = getRegU32(ctx, 6); const uint32_t arg1 = getRegU32(ctx, 7); - std::cout << "[watch:sprintf] dest=0x" << std::hex << str_addr + RUNTIME_LOG("[watch:sprintf] dest=0x" << std::hex << str_addr << " fmt@0x" << format_addr << " arg0=0x" << arg0 << " arg1=0x" << arg1 << " fmt=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, format_addr, 64)) << "\"" << " s0=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg0, 64)) << "\"" << " s1=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg1, 64)) << "\"" - << std::dec << std::endl; + << std::dec << std::endl); ++watchSprintfLogCount; } @@ -648,14 +656,14 @@ void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { // TODO: Add translation for PS2 paths like mc0:, host:, cdrom:, etc. // treating as direct host path - std::cout << "ps2_stub fopen: path='" << hostPath << "', mode='" << hostMode << "'" << std::endl; + RUNTIME_LOG("ps2_stub fopen: path='" << hostPath << "', mode='" << hostMode << "'"); FILE *fp = ::fopen(hostPath, hostMode); if (fp) { std::lock_guard lock(g_file_mutex); file_handle = generate_file_handle(); g_file_map[file_handle] = fp; - std::cout << " -> handle=0x" << std::hex << file_handle << std::dec << std::endl; + RUNTIME_LOG(" -> handle=0x" << std::hex << file_handle << std::dec); } else { @@ -974,3 +982,158 @@ void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) float arg = ctx->f[12]; ctx->f[0] = ::fabsf(arg); } +void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t value = static_cast(getRegU32(ctx, 4)); + if (value == std::numeric_limits::min()) + { + setReturnS32(ctx, std::numeric_limits::max()); + return; + } + setReturnS32(ctx, value < 0 ? -value : value); +} + + +void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float in = ctx ? ctx->f[12] : 0.0f; + if (in == 0.0f) + { + uint32_t raw = getRegU32(ctx, 4); + std::memcpy(&in, &raw, sizeof(in)); + } + const float out = std::atan(in); + if (ctx) + { + ctx->f[0] = out; + } + + uint32_t outRaw = 0u; + std::memcpy(&outRaw, &out, sizeof(outRaw)); + setReturnU32(ctx, outRaw); +} + + +void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t srcAddr = getRegU32(ctx, 4); + const uint8_t needle = static_cast(getRegU32(ctx, 5) & 0xFFu); + const uint32_t size = getRegU32(ctx, 6); + + for (uint32_t i = 0; i < size; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + if (!src) + { + break; + } + if (*src == needle) + { + setReturnU32(ctx, srcAddr + i); + return; + } + } + + setReturnU32(ctx, 0u); +} + + +void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, std::rand() & 0x7FFF); +} + + +void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::srand(getRegU32(ctx, 4)); + setReturnS32(ctx, 0); +} + + +void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t lhsAddr = getRegU32(ctx, 4); + const uint32_t rhsAddr = getRegU32(ctx, 5); + const std::string lhs = readPs2CStringBounded(rdram, runtime, lhsAddr, 1024); + const std::string rhs = readPs2CStringBounded(rdram, runtime, rhsAddr, 1024); + + const size_t n = std::min(lhs.size(), rhs.size()); + for (size_t i = 0; i < n; ++i) + { + const int a = std::tolower(static_cast(lhs[i])); + const int b = std::tolower(static_cast(rhs[i])); + if (a != b) + { + setReturnS32(ctx, a - b); + return; + } + } + + setReturnS32(ctx, static_cast(lhs.size()) - static_cast(rhs.size())); +} + + +void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 + FILE *fp = get_file_ptr(file_handle); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (fp && format_addr != 0) + { + std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); + ret = std::fprintf(fp, "%s", rendered.c_str()); + } + else + { + std::cerr << "vfprintf error: Invalid file handle or format address." + << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + setReturnS32(ctx, ret); +} + + +void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 + constexpr size_t kSafeVsprintfBytes = 256u; // Keep guest stack temporaries from being overwritten. + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); + if (rendered.size() >= kSafeVsprintfBytes) + { + rendered.resize(kSafeVsprintfBytes - 1); + } + if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), rendered.size() + 1u)) + { + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "vsprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + } + } + else + { + std::cerr << "vsprintf error: Invalid address provided." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + setReturnS32(ctx, ret); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h new file mode 100644 index 00000000..01792bfe --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.h @@ -0,0 +1,60 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void malloc(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 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); + void strncpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strlen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strncmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strcat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strncat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strrchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void strstr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void snprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void puts(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fclose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fwrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ftell(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fflush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sqrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void __kernel_sinf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void cos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void __kernel_cosf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void __ieee754_rem_pio2f(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void tan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void atan2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void pow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void exp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void log(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void log10(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ceil(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void floor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + 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); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp new file mode 100644 index 00000000..f9c0e424 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp @@ -0,0 +1,425 @@ +#include "Common.h" +#include "MPEG.h" + +namespace ps2_stubs +{ +namespace +{ +struct MpegRegisteredCallback +{ + uint32_t type = 0u; + uint32_t func = 0u; + uint32_t data = 0u; + uint32_t handle = 0u; +}; + +struct MpegPlaybackState +{ + uint32_t picturesServed = 0u; +}; + +struct MpegStubState +{ + bool initialized = false; + uint32_t nextCallbackHandle = 1u; + std::unordered_map> callbacksByMpeg; + std::unordered_map playbackByMpeg; + PS2MpegCompatLayout compat; +}; + +std::mutex g_mpeg_stub_mutex; +MpegStubState g_mpeg_stub_state; + +constexpr uint32_t kStubMovieWidth = 320u; +constexpr uint32_t kStubMovieHeight = 240u; + +uint32_t mpegCompatSyntheticFrames(const PS2MpegCompatLayout &layout) +{ + return layout.syntheticFramesBeforeEnd != 0u ? layout.syntheticFramesBeforeEnd : 1u; +} + +MpegPlaybackState &getPlaybackState(uint32_t mpegAddr) +{ + return g_mpeg_stub_state.playbackByMpeg[mpegAddr]; +} + +void resetMpegStubStateUnlocked() +{ + const PS2MpegCompatLayout compat = g_mpeg_stub_state.compat; + g_mpeg_stub_state.initialized = false; + g_mpeg_stub_state.nextCallbackHandle = 1u; + g_mpeg_stub_state.callbacksByMpeg.clear(); + g_mpeg_stub_state.playbackByMpeg.clear(); + g_mpeg_stub_state.compat = compat; +} +} + +void setMpegCompatLayout(const PS2MpegCompatLayout &layout) +{ + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.compat = layout; +} + +void clearMpegCompatLayout() +{ + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.compat = {}; +} + +void resetMpegStubState() +{ + std::lock_guard lock(g_mpeg_stub_mutex); + resetMpegStubStateUnlocked(); +} + +void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegFlush", rdram, ctx, runtime); +} + + +void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime); +} + +void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + const uint32_t mpegAddr = getRegU32(ctx, 4); + const uint32_t callbackType = getRegU32(ctx, 5); + const uint32_t callbackFunc = getRegU32(ctx, 6); + const uint32_t callbackData = getRegU32(ctx, 7); + + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.initialized = true; + (void)getPlaybackState(mpegAddr); + + const uint32_t handle = g_mpeg_stub_state.nextCallbackHandle++; + g_mpeg_stub_state.callbacksByMpeg[mpegAddr].push_back( + MpegRegisteredCallback{callbackType, callbackFunc, callbackData, handle}); + + setReturnU32(ctx, handle); +} + +void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnU32(ctx, 0u); +} + +void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)ctx; + (void)runtime; + static const uint32_t kRefGlobalAddrs[] = { + 0x171800u, 0x17180Cu, 0x171818u, 0x171804u, 0x171810u, 0x17181Cu + }; + for (uint32_t addr : kRefGlobalAddrs) + { + uint8_t *p = getMemPtr(rdram, addr); + if (!p) + continue; + uint32_t ptr = *reinterpret_cast(p); + if (ptr != 0u) + { + uint8_t *q = getMemPtr(rdram, ptr + 0x28u); + if (q) + *reinterpret_cast(q) = 0u; + } + } + setReturnU32(ctx, 1u); +} + +static void mpegGuestWrite32(uint8_t *rdram, uint32_t addr, uint32_t value) +{ + if (uint8_t *p = getMemPtr(rdram, addr)) + *reinterpret_cast(p) = value; +} +static void mpegGuestWrite64(uint8_t *rdram, uint32_t addr, uint64_t value) +{ + if (uint8_t *p = getMemPtr(rdram, addr)) + { + *reinterpret_cast(p) = static_cast(value); + *reinterpret_cast(p + 4) = static_cast(value >> 32); + } +} + +void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t param_1 = getRegU32(ctx, 4); // a0 + const uint32_t param_2 = getRegU32(ctx, 5); // a1 + const uint32_t param_3 = getRegU32(ctx, 6); // a2 + + const uint32_t uVar3 = (param_2 + 3u) & 0xFFFFFFFCu; + const int32_t iVar2_signed = static_cast(param_3) - static_cast(uVar3 - param_2); + + if (iVar2_signed <= 0x117) + { + setReturnU32(ctx, 0u); + return; + } + + { + std::lock_guard lock(g_mpeg_stub_mutex); + getPlaybackState(param_1) = {}; + } + + const uint32_t puVar4 = uVar3 + 0x108u; + const uint32_t innerSize = static_cast(iVar2_signed) - 0x118u; + + mpegGuestWrite32(rdram, param_1 + 0x40, uVar3); + + const uint32_t a1_init = uVar3 + 0x118u; + mpegGuestWrite32(rdram, puVar4 + 0x0, a1_init); + mpegGuestWrite32(rdram, puVar4 + 0x4, innerSize); + mpegGuestWrite32(rdram, puVar4 + 0x8, a1_init); + mpegGuestWrite32(rdram, puVar4 + 0xC, a1_init); + + const uint32_t allocResult = runtime ? runtime->guestMalloc(0x600, 8u) : (uVar3 + 0x200u); + mpegGuestWrite32(rdram, uVar3 + 0x44, allocResult); + + // param_1[0..2] = 0; param_1[4..0xe] = 0xffffffff/0 as per decompilation + mpegGuestWrite32(rdram, param_1 + 0x00, 0); + mpegGuestWrite32(rdram, param_1 + 0x04, 0); + mpegGuestWrite32(rdram, param_1 + 0x08, 0); + mpegGuestWrite64(rdram, param_1 + 0x10, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x18, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x20, 0); + mpegGuestWrite64(rdram, param_1 + 0x28, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x30, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x38, 0); + + static const unsigned s_zeroOffsets[] = { + 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0, 0xE4, 0xE8, 0xF8, + 0x0C, 0x14, 0x2C, 0x34, 0x3C, + 0x48, 0xFC, 0x100, 0x104, 0x70, 0x90, 0xAC + }; + for (unsigned off : s_zeroOffsets) + mpegGuestWrite32(rdram, uVar3 + off, 0u); + mpegGuestWrite64(rdram, uVar3 + 0x78, 0); + mpegGuestWrite64(rdram, uVar3 + 0x88, 0); + + mpegGuestWrite64(rdram, uVar3 + 0xF0, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite32(rdram, uVar3 + 0x1C, 0x1209F8u); + mpegGuestWrite32(rdram, uVar3 + 0x24, 0x120A08u); + mpegGuestWrite32(rdram, uVar3 + 0xB0, 1u); + mpegGuestWrite32(rdram, uVar3 + 0x9C, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, uVar3 + 0x80, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, uVar3 + 0x94, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, uVar3 + 0x98, 0xFFFFFFFFu); + + mpegGuestWrite32(rdram, 0x1717BCu, param_1); + + static const uint32_t s_refValues[] = { + 0x171A50u, 0x171C58u, 0x171CC0u, 0x171D28u, 0x171D90u, + 0x171AB8u, 0x171B20u, 0x171B88u, 0x171BF0u + }; + for (unsigned i = 0; i < 9u; ++i) + mpegGuestWrite32(rdram, 0x171800u + i * 4u, s_refValues[i]); + + uint32_t setDynamicRet = a1_init; + if (uint8_t *p = getMemPtr(rdram, puVar4 + 8)) + setDynamicRet = *reinterpret_cast(p); + mpegGuestWrite32(rdram, puVar4 + 12, setDynamicRet); + + setReturnU32(ctx, setDynamicRet); +} + +void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + const uint32_t mpegAddr = getRegU32(ctx, 4); + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.callbacksByMpeg.erase(mpegAddr); + g_mpeg_stub_state.playbackByMpeg.erase(mpegAddr); + setReturnU32(ctx, 0u); +} + +void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime); +} + +void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + const uint32_t availableBytes = getRegU32(ctx, 6); + setReturnS32(ctx, static_cast(availableBytes)); +} + +void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime); +} + +void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime); +} + +void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime); +} + +void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime); +} + +void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime); +} + +void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + const uint32_t mpegAddr = getRegU32(ctx, 4); + uint32_t picturesServed = 0u; + PS2MpegCompatLayout compat{}; + { + std::lock_guard lock(g_mpeg_stub_mutex); + MpegPlaybackState &playback = getPlaybackState(mpegAddr); + mpegGuestWrite32(rdram, mpegAddr + 0x00u, kStubMovieWidth); + mpegGuestWrite32(rdram, mpegAddr + 0x04u, kStubMovieHeight); + mpegGuestWrite32(rdram, mpegAddr + 0x08u, playback.picturesServed); + picturesServed = playback.picturesServed; + compat = g_mpeg_stub_state.compat; + playback.picturesServed += 1u; + } + + if (uint8_t *base = getMemPtr(rdram, mpegAddr)) + { + const uint32_t iVar1 = *reinterpret_cast(base + 0x40); + if (uint8_t *inner = getMemPtr(rdram, iVar1)) + { + *reinterpret_cast(inner + 0xb0) = 1; + *reinterpret_cast(inner + 0xd8) = (getRegU32(ctx, 5) & 0x0FFFFFFFu) | 0x20000000u; + *reinterpret_cast(inner + 0xe4) = getRegU32(ctx, 6); + *reinterpret_cast(inner + 0xdc) = 0; + *reinterpret_cast(inner + 0xe0) = 0; + } + } + + if (compat.matchesMpegObject(mpegAddr) && + compat.hasFinishTargets() && + (picturesServed + 1u) >= mpegCompatSyntheticFrames(compat)) + { + // No decoder yet: synthesize a safe frame so the guest can + // initialize its movie presentation path, then mark playback finished. + if (compat.videoStateAddr != 0u) + { + mpegGuestWrite32(rdram, compat.videoStateAddr, compat.finishedVideoStateValue); + } + if (compat.movieStateAddr != 0u) + { + mpegGuestWrite32(rdram, compat.movieStateAddr, compat.finishedMovieStateValue); + } + } + + setReturnU32(ctx, 0u); +} + +void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime); +} + +void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime); +} + +void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + std::lock_guard lock(g_mpeg_stub_mutex); + resetMpegStubStateUnlocked(); + g_mpeg_stub_state.initialized = true; + setReturnU32(ctx, 0u); +} + +void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + const uint32_t mpegAddr = getRegU32(ctx, 4); + + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.initialized = true; + const MpegPlaybackState &playback = getPlaybackState(mpegAddr); + if (g_mpeg_stub_state.compat.matchesMpegObject(mpegAddr)) + { + setReturnS32(ctx, playback.picturesServed >= mpegCompatSyntheticFrames(g_mpeg_stub_state.compat) ? 1 : 0); + return; + } + + // Generic fallback: keep decode threads alive until a game-specific path + // decides to stop playback. + setReturnS32(ctx, 0); +} + +void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime); +} + +void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + const uint32_t param_1 = getRegU32(ctx, 4); + { + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.playbackByMpeg[param_1] = {}; + } + uint8_t *base = getMemPtr(rdram, param_1); + if (!base) + { + return; + } + uint32_t inner = *reinterpret_cast(base + 0x40); + if (inner == 0u) + return; + mpegGuestWrite32(rdram, param_1 + 0x00u, 0u); + mpegGuestWrite32(rdram, param_1 + 0x04u, 0u); + mpegGuestWrite32(rdram, param_1 + 0x08u, 0u); + mpegGuestWrite32(rdram, inner + 0x00, 0u); + mpegGuestWrite32(rdram, inner + 0x04, 0u); + mpegGuestWrite32(rdram, inner + 0x08, 0u); + mpegGuestWrite32(rdram, param_1 + 0x08, 0u); + mpegGuestWrite32(rdram, inner + 0x80, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, inner + 0xAC, 0u); + mpegGuestWrite32(rdram, 0x171904u, 0u); +} + +void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime); +} + +void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime); +} + +void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime); +} + +void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.h b/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.h new file mode 100644 index 00000000..a77dacc5 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void resetMpegStubState(); + void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp new file mode 100644 index 00000000..8a741f84 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp @@ -0,0 +1,1444 @@ +#include "Common.h" +#include "MemoryCard.h" + +namespace ps2_stubs +{ +namespace +{ + constexpr int32_t kMcCmdGetInfo = 0x01; + constexpr int32_t kMcCmdOpen = 0x02; + constexpr int32_t kMcCmdClose = 0x03; + constexpr int32_t kMcCmdSeek = 0x04; + constexpr int32_t kMcCmdRead = 0x05; + constexpr int32_t kMcCmdWrite = 0x06; + constexpr int32_t kMcCmdFlush = 0x0A; + constexpr int32_t kMcCmdMkdir = 0x0B; + constexpr int32_t kMcCmdChdir = 0x0C; + constexpr int32_t kMcCmdGetDir = 0x0D; + constexpr int32_t kMcCmdSetFileInfo = 0x0E; + constexpr int32_t kMcCmdDelete = 0x0F; + constexpr int32_t kMcCmdFormat = 0x10; + constexpr int32_t kMcCmdUnformat = 0x11; + constexpr int32_t kMcCmdGetEntSpace = 0x12; + constexpr int32_t kMcCmdRename = 0x13; + + constexpr int32_t kMcResultSucceed = 0; + constexpr int32_t kMcResultChangedCard = -1; + constexpr int32_t kMcResultNoFormat = -2; + constexpr int32_t kMcResultNoEntry = -4; + constexpr int32_t kMcResultDeniedPermit = -5; + constexpr int32_t kMcResultNotEmpty = -6; + constexpr int32_t kMcResultUpLimitHandle = -7; + + constexpr int32_t kMcTypePs2 = 2; + constexpr int32_t kMcFormatted = 1; + constexpr int32_t kMcUnformatted = 0; + constexpr int32_t kMcFreeClusters = 0x2000; + constexpr size_t kMcMaxPathLen = 1024; + constexpr size_t kMcMaxOpenFiles = 32; + + constexpr uint16_t kMcAttrReadable = 0x0001; + constexpr uint16_t kMcAttrWriteable = 0x0002; + constexpr uint16_t kMcAttrFile = 0x0010; + constexpr uint16_t kMcAttrSubdir = 0x0020; + constexpr uint16_t kMcAttrClosed = 0x0080; + constexpr uint16_t kMcAttrExists = 0x8000; + + struct SceMcStDateTime + { + uint8_t Resv2 = 0; + uint8_t Sec = 0; + uint8_t Min = 0; + uint8_t Hour = 0; + uint8_t Day = 0; + uint8_t Month = 0; + uint16_t Year = 0; + }; + + struct SceMcTblGetDir + { + SceMcStDateTime _Create{}; + SceMcStDateTime _Modify{}; + uint32_t FileSizeByte = 0; + uint16_t AttrFile = 0; + uint16_t Reserve1 = 0; + uint32_t Reserve2 = 0; + uint32_t PdaAplNo = 0; + char EntryName[32]{}; + }; + + static_assert(sizeof(SceMcTblGetDir) == 64, "sceMcTblGetDir size mismatch"); + + struct McOpenFile + { + FILE *file = nullptr; + int32_t port = 0; + std::filesystem::path hostPath; + }; + + struct McPortState + { + std::string currentDir = "/"; + bool formatted = true; + }; + + std::mutex g_mcStateMutex; + int32_t g_mcNextFd = 1; + int32_t g_mcLastCmd = 0; + int32_t g_mcLastResult = 0; + std::unordered_map g_mcFiles; + std::array g_mcPorts{}; + int32_t g_cvMcFileCursor = 0; + constexpr int32_t kCvMcFreeCapacityBytes = 0x01000000; + constexpr int32_t kCvMcSaveCapacityBytes = 0x00080000; + constexpr int32_t kCvMcConfigCapacityBytes = 0x00008000; + constexpr int32_t kCvMcIconCapacityBytes = 0x00004000; + + bool isValidMcPortSlot(int32_t port, int32_t slot) + { + return port >= 0 && port < static_cast(g_mcPorts.size()) && slot == 0; + } + + std::filesystem::path getMcRootPath(int32_t port) + { + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + std::filesystem::path root = paths.mcRoot; + if (root.empty()) + { + if (!paths.elfDirectory.empty()) + { + root = paths.elfDirectory / "mc0"; + } + else + { + std::error_code ec; + const std::filesystem::path cwd = std::filesystem::current_path(ec); + root = ec ? std::filesystem::path("mc0") : (cwd / "mc0"); + } + } + + root = root.lexically_normal(); + if (port <= 0) + { + return root; + } + + const std::filesystem::path parent = root.parent_path(); + const std::string leaf = root.filename().string(); + const std::string lowerLeaf = toLowerAscii(leaf); + if (lowerLeaf == "mc0") + { + return (parent / "mc1").lexically_normal(); + } + if (leaf.empty()) + { + return (root / "mc1").lexically_normal(); + } + + return (parent / (leaf + "_slot" + std::to_string(port))).lexically_normal(); + } + + void ensureMcRootExists(int32_t port) + { + std::error_code ec; + std::filesystem::create_directories(getMcRootPath(port), ec); + } + + std::vector splitMcPathComponents(const std::string &value) + { + std::vector parts; + std::string current; + for (char c : value) + { + if (c == '/' || c == '\\') + { + if (!current.empty()) + { + parts.push_back(current); + current.clear(); + } + } + else + { + current.push_back(c); + } + } + + if (!current.empty()) + { + parts.push_back(current); + } + + return parts; + } + + std::string joinMcPathComponents(const std::vector &parts) + { + if (parts.empty()) + { + return "/"; + } + + std::string joined = "/"; + for (size_t i = 0; i < parts.size(); ++i) + { + if (i != 0u) + { + joined.push_back('/'); + } + joined.append(parts[i]); + } + return joined; + } + + std::string normalizeGuestMcPathLocked(int32_t port, std::string path) + { + std::replace(path.begin(), path.end(), '\\', '/'); + const std::string lower = toLowerAscii(path); + if (lower.rfind("mc0:", 0) == 0 || lower.rfind("mc1:", 0) == 0) + { + path = path.substr(4); + } + + const bool absolute = !path.empty() && path.front() == '/'; + std::vector parts; + if (!absolute && port >= 0 && port < static_cast(g_mcPorts.size())) + { + parts = splitMcPathComponents(g_mcPorts[static_cast(port)].currentDir); + } + + for (const std::string &part : splitMcPathComponents(path)) + { + if (part.empty() || part == ".") + { + continue; + } + + if (part == "..") + { + if (!parts.empty()) + { + parts.pop_back(); + } + continue; + } + + parts.push_back(part); + } + + return joinMcPathComponents(parts); + } + + std::filesystem::path guestMcPathToHostPath(int32_t port, const std::string &guestPath) + { + std::filesystem::path resolved = getMcRootPath(port); + if (guestPath.size() > 1u) + { + resolved /= std::filesystem::path(guestPath.substr(1)); + } + return resolved.lexically_normal(); + } + + bool localtimeSafeMc(const std::time_t *value, std::tm *out) + { +#ifdef _WIN32 + return localtime_s(out, value) == 0; +#else + return localtime_r(value, out) != nullptr; +#endif + } + + std::time_t fileTimeToTimeTMc(std::filesystem::file_time_type value) + { + const auto systemTime = std::chrono::time_point_cast( + value - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); + return std::chrono::system_clock::to_time_t(systemTime); + } + + void writeMcCString(uint8_t *rdram, uint32_t addr, const std::string &value) + { + if (addr == 0u) + { + return; + } + + uint8_t *dst = getMemPtr(rdram, addr); + if (!dst) + { + return; + } + + std::memcpy(dst, value.c_str(), value.size() + 1u); + } + + void writeMcDateTime(SceMcStDateTime &out, std::time_t value) + { + std::tm tm{}; + if (!localtimeSafeMc(&value, &tm)) + { + std::memset(&out, 0, sizeof(out)); + return; + } + + out.Resv2 = 0; + out.Sec = static_cast(tm.tm_sec); + out.Min = static_cast(tm.tm_min); + out.Hour = static_cast(tm.tm_hour); + out.Day = static_cast(tm.tm_mday); + out.Month = static_cast(tm.tm_mon + 1); + out.Year = static_cast(tm.tm_year + 1900); + } + + void fillMcDirTableEntry(SceMcTblGetDir &entry, + const std::string &name, + bool isDirectory, + uint32_t sizeBytes, + std::time_t modifiedTime) + { + std::memset(&entry, 0, sizeof(entry)); + writeMcDateTime(entry._Create, modifiedTime); + writeMcDateTime(entry._Modify, modifiedTime); + entry.FileSizeByte = isDirectory ? 0u : sizeBytes; + entry.AttrFile = static_cast(kMcAttrReadable | + kMcAttrWriteable | + (isDirectory ? kMcAttrSubdir : kMcAttrFile) | + kMcAttrClosed | + kMcAttrExists); + std::strncpy(entry.EntryName, name.c_str(), sizeof(entry.EntryName) - 1u); + entry.EntryName[sizeof(entry.EntryName) - 1u] = '\0'; + } + + bool wildcardMatch(const std::string &pattern, const std::string &value) + { + size_t patternPos = 0u; + size_t valuePos = 0u; + size_t starPos = std::string::npos; + size_t matchPos = 0u; + + while (valuePos < value.size()) + { + if (patternPos < pattern.size() && + (pattern[patternPos] == '?' || pattern[patternPos] == value[valuePos])) + { + ++patternPos; + ++valuePos; + } + else if (patternPos < pattern.size() && pattern[patternPos] == '*') + { + starPos = patternPos++; + matchPos = valuePos; + } + else if (starPos != std::string::npos) + { + patternPos = starPos + 1u; + valuePos = ++matchPos; + } + else + { + return false; + } + } + + while (patternPos < pattern.size() && pattern[patternPos] == '*') + { + ++patternPos; + } + + return patternPos == pattern.size(); + } + + void setMcCommandResultLocked(int32_t cmd, int32_t result) + { + g_mcLastCmd = cmd; + g_mcLastResult = result; + } + + void closeMcFilesLocked() + { + for (auto &[fd, openFile] : g_mcFiles) + { + if (openFile.file) + { + std::fclose(openFile.file); + openFile.file = nullptr; + } + } + g_mcFiles.clear(); + } + + void closeMcFilesForPortLocked(int32_t port) + { + for (auto it = g_mcFiles.begin(); it != g_mcFiles.end();) + { + if (it->second.port == port) + { + if (it->second.file) + { + std::fclose(it->second.file); + } + it = g_mcFiles.erase(it); + } + else + { + ++it; + } + } + } + + int32_t allocateMcFdLocked(FILE *file, int32_t port, const std::filesystem::path &hostPath) + { + if (!file) + { + return kMcResultDeniedPermit; + } + if (g_mcFiles.size() >= kMcMaxOpenFiles) + { + return kMcResultUpLimitHandle; + } + + for (int attempt = 0; attempt < 0x10000; ++attempt) + { + if (g_mcNextFd <= 0) + { + g_mcNextFd = 1; + } + + const int32_t fd = g_mcNextFd++; + if (g_mcFiles.find(fd) != g_mcFiles.end()) + { + continue; + } + + g_mcFiles.emplace(fd, McOpenFile{file, port, hostPath}); + return fd; + } + + return kMcResultUpLimitHandle; + } + + FILE *openMcHostFile(const std::filesystem::path &hostPath, uint32_t flags) + { + const uint32_t access = flags & PS2_FIO_O_RDWR; + const bool read = (access == PS2_FIO_O_RDONLY) || (access == PS2_FIO_O_RDWR); + const bool write = (access == PS2_FIO_O_WRONLY) || (access == PS2_FIO_O_RDWR); + const bool append = (flags & PS2_FIO_O_APPEND) != 0u; + const bool create = (flags & PS2_FIO_O_CREAT) != 0u; + const bool truncate = (flags & PS2_FIO_O_TRUNC) != 0u; + + std::error_code ec; + const bool exists = std::filesystem::exists(hostPath, ec) && !ec; + + const char *mode = "rb"; + if (read && write) + { + if (append) + { + mode = exists ? "a+b" : "w+b"; + } + else if (truncate || (create && !exists)) + { + mode = "w+b"; + } + else + { + mode = "r+b"; + } + } + else if (write) + { + if (append) + { + mode = exists ? "ab" : "wb"; + } + else if (truncate || (create && !exists)) + { + mode = "wb"; + } + else + { + mode = "r+b"; + } + } + + return std::fopen(hostPath.string().c_str(), mode); + } +} + +void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const uint32_t pathAddr = getRegU32(ctx, 6); + const uint32_t currentDirAddr = getRegU32(ctx, 7); + const std::string requestedDir = + (pathAddr != 0u) ? readPs2CStringBounded(rdram, pathAddr, kMcMaxPathLen) : std::string{}; + + std::string currentDir = "/"; + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + currentDir = state.currentDir; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + ensureMcRootExists(port); + const std::string resolvedDir = + requestedDir.empty() ? state.currentDir : normalizeGuestMcPathLocked(port, requestedDir); + const std::filesystem::path hostDir = guestMcPathToHostPath(port, resolvedDir); + std::error_code ec; + if (std::filesystem::exists(hostDir, ec) && !ec && + std::filesystem::is_directory(hostDir, ec)) + { + state.currentDir = resolvedDir; + currentDir = resolvedDir; + result = kMcResultSucceed; + } + } + } + + setMcCommandResultLocked(kMcCmdChdir, result); + } + + writeMcCString(rdram, currentDirAddr, currentDir); + setReturnS32(ctx, 0); +} + +void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t fd = static_cast(getRegU32(ctx, 4)); + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (it != g_mcFiles.end()) + { + if (!it->second.file || std::fclose(it->second.file) == 0) + { + result = kMcResultSucceed; + } + g_mcFiles.erase(it); + } + setMcCommandResultLocked(kMcCmdClose, result); + } + setReturnS32(ctx, 0); +} + +void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + const std::string guestPath = normalizeGuestMcPathLocked(port, path); + if (guestPath != "/") + { + const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); + std::error_code ec; + if (std::filesystem::exists(hostPath, ec) && !ec) + { + if (std::filesystem::is_directory(hostPath, ec) && + !std::filesystem::is_empty(hostPath, ec)) + { + result = kMcResultNotEmpty; + } + else if (std::filesystem::remove(hostPath, ec) && !ec) + { + result = kMcResultSucceed; + } + else + { + result = kMcResultDeniedPermit; + } + } + } + } + } + + setMcCommandResultLocked(kMcCmdDelete, result); + } + setReturnS32(ctx, 0); +} + +void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t fd = static_cast(getRegU32(ctx, 4)); + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (it != g_mcFiles.end() && it->second.file) + { + result = (std::fflush(it->second.file) == 0) ? kMcResultSucceed : kMcResultDeniedPermit; + } + setMcCommandResultLocked(kMcCmdFlush, result); + } + setReturnS32(ctx, 0); +} + +void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + closeMcFilesForPortLocked(port); + const std::filesystem::path root = getMcRootPath(port); + std::error_code ec; + std::filesystem::remove_all(root, ec); + ec.clear(); + std::filesystem::create_directories(root, ec); + if (!ec) + { + McPortState &state = g_mcPorts[static_cast(port)]; + state.currentDir = "/"; + state.formatted = true; + result = kMcResultSucceed; + } + } + + setMcCommandResultLocked(kMcCmdFormat, result); + } + setReturnS32(ctx, 0); +} + +void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string rawPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + const int32_t maxEntries = static_cast(readStackU32(rdram, ctx, 16)); + const uint32_t tableAddr = readStackU32(rdram, ctx, 20); + + std::vector entries; + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + ensureMcRootExists(port); + const std::string guestQuery = + normalizeGuestMcPathLocked(port, rawPath.empty() ? "." : rawPath); + const bool hasWildcard = + guestQuery.find('*') != std::string::npos || guestQuery.find('?') != std::string::npos; + + const std::filesystem::path queryRel = + (guestQuery.size() > 1u) ? std::filesystem::path(guestQuery.substr(1)) : std::filesystem::path{}; + + std::filesystem::path parentRel; + std::string pattern; + if (hasWildcard) + { + parentRel = queryRel.parent_path(); + pattern = queryRel.filename().string(); + } + else + { + const std::filesystem::path queryHostPath = guestMcPathToHostPath(port, guestQuery); + std::error_code queryEc; + if (std::filesystem::exists(queryHostPath, queryEc) && !queryEc && + std::filesystem::is_directory(queryHostPath, queryEc)) + { + parentRel = queryRel; + pattern = "*"; + } + else + { + parentRel = queryRel.parent_path(); + pattern = queryRel.filename().string(); + } + } + + if (pattern.empty()) + { + pattern = "*"; + } + + std::filesystem::path hostDir = getMcRootPath(port); + if (!parentRel.empty()) + { + hostDir /= parentRel; + } + hostDir = hostDir.lexically_normal(); + + std::error_code ec; + if (std::filesystem::exists(hostDir, ec) && !ec && + std::filesystem::is_directory(hostDir, ec)) + { + const std::time_t now = std::time(nullptr); + auto appendSpecial = [&](const std::string &name) + { + if (!wildcardMatch(pattern, name)) + { + return; + } + SceMcTblGetDir entry{}; + fillMcDirTableEntry(entry, name, true, 0u, now); + entries.push_back(entry); + }; + + appendSpecial("."); + appendSpecial(".."); + + std::vector dirEntries; + for (const auto &entry : std::filesystem::directory_iterator( + hostDir, std::filesystem::directory_options::skip_permission_denied, ec)) + { + if (ec) + { + break; + } + dirEntries.push_back(entry); + } + + std::sort(dirEntries.begin(), dirEntries.end(), + [](const std::filesystem::directory_entry &lhs, + const std::filesystem::directory_entry &rhs) + { + return toLowerAscii(lhs.path().filename().string()) < + toLowerAscii(rhs.path().filename().string()); + }); + + for (const auto &entry : dirEntries) + { + const std::string name = entry.path().filename().string(); + if (!wildcardMatch(pattern, name)) + { + continue; + } + + std::error_code entryEc; + const bool isDirectory = entry.is_directory(entryEc) && !entryEc; + const uint32_t sizeBytes = + isDirectory ? 0u : static_cast(entry.file_size(entryEc)); + entryEc.clear(); + const std::time_t modifiedTime = fileTimeToTimeTMc(entry.last_write_time(entryEc)); + SceMcTblGetDir tableEntry{}; + fillMcDirTableEntry(tableEntry, + name, + isDirectory, + sizeBytes, + entryEc ? now : modifiedTime); + entries.push_back(tableEntry); + } + + const size_t entryCount = + std::min(entries.size(), maxEntries > 0 ? static_cast(maxEntries) : 0u); + if (entryCount == 0u || tableAddr == 0u) + { + result = static_cast(entryCount); + } + else if (uint8_t *dst = getMemPtr(rdram, tableAddr)) + { + std::memcpy(dst, entries.data(), entryCount * sizeof(SceMcTblGetDir)); + result = static_cast(entryCount); + } + else + { + result = kMcResultDeniedPermit; + } + } + } + } + + setMcCommandResultLocked(kMcCmdGetDir, result); + } + setReturnS32(ctx, 0); +} + +void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1024); +} + +void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const uint32_t typePtr = getRegU32(ctx, 6); + const uint32_t freePtr = getRegU32(ctx, 7); + const uint32_t formatPtr = readStackU32(rdram, ctx, 16); + + int32_t cardType = 0; + int32_t freeBlocks = 0; + int32_t format = kMcUnformatted; + int32_t result = kMcResultNoEntry; + + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + cardType = kMcTypePs2; + freeBlocks = state.formatted ? kMcFreeClusters : 0; + format = state.formatted ? kMcFormatted : kMcUnformatted; + result = state.formatted ? kMcResultSucceed : kMcResultNoFormat; + } + + setMcCommandResultLocked(kMcCmdGetInfo, result); + } + + if (typePtr != 0u) + { + if (uint8_t *out = getMemPtr(rdram, typePtr)) + { + std::memcpy(out, &cardType, sizeof(cardType)); + } + } + if (freePtr != 0u) + { + if (uint8_t *out = getMemPtr(rdram, freePtr)) + { + std::memcpy(out, &freeBlocks, sizeof(freeBlocks)); + } + } + if (formatPtr != 0u) + { + if (uint8_t *out = getMemPtr(rdram, formatPtr)) + { + std::memcpy(out, &format, sizeof(format)); + } + } + + setReturnS32(ctx, 0); +} + +void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + { + std::lock_guard lock(g_mcStateMutex); + closeMcFilesLocked(); + g_mcNextFd = 1; + g_mcLastCmd = 0; + g_mcLastResult = 0; + for (McPortState &state : g_mcPorts) + { + state.currentDir = "/"; + state.formatted = true; + } + } + ensureMcRootExists(0); + ensureMcRootExists(1); + setReturnS32(ctx, 0); +} + +void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + ensureMcRootExists(port); + const std::string guestPath = normalizeGuestMcPathLocked(port, path); + const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); + std::error_code ec; + if (std::filesystem::exists(hostPath, ec) && !ec) + { + result = std::filesystem::is_directory(hostPath, ec) ? kMcResultSucceed : kMcResultDeniedPermit; + } + else if (std::filesystem::create_directory(hostPath, ec) && !ec) + { + result = kMcResultSucceed; + } + else + { + result = std::filesystem::exists(hostPath.parent_path(), ec) && !ec + ? kMcResultDeniedPermit + : kMcResultNoEntry; + } + } + } + + setMcCommandResultLocked(kMcCmdMkdir, result); + } + setReturnS32(ctx, 0); +} + +void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + const uint32_t flags = getRegU32(ctx, 7); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + const std::string guestPath = normalizeGuestMcPathLocked(port, path); + const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); + std::error_code ec; + const bool create = (flags & PS2_FIO_O_CREAT) != 0u; + const bool exists = std::filesystem::exists(hostPath, ec) && !ec; + if (guestPath == "/") + { + result = kMcResultDeniedPermit; + } + else if (exists && std::filesystem::is_directory(hostPath, ec)) + { + result = kMcResultDeniedPermit; + } + else if (!exists && !create) + { + result = kMcResultNoEntry; + } + else if (!std::filesystem::exists(hostPath.parent_path(), ec) || ec) + { + result = kMcResultNoEntry; + } + else + { + FILE *file = openMcHostFile(hostPath, flags); + if (!file) + { + result = exists ? kMcResultDeniedPermit : kMcResultNoEntry; + } + else + { + result = allocateMcFdLocked(file, port, hostPath); + if (result < 0) + { + std::fclose(file); + } + } + } + } + } + setMcCommandResultLocked(kMcCmdOpen, result); + } + setReturnS32(ctx, 0); +} + +void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t fd = static_cast(getRegU32(ctx, 4)); + const uint32_t dstAddr = getRegU32(ctx, 5); + const int32_t size = static_cast(getRegU32(ctx, 6)); + uint8_t *dst = (size > 0) ? getMemPtr(rdram, dstAddr) : nullptr; + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (size <= 0) + { + result = 0; + } + else if (it == g_mcFiles.end() || !it->second.file) + { + result = kMcResultNoEntry; + } + else if (!dst) + { + result = kMcResultDeniedPermit; + } + else + { + const size_t bytesRead = std::fread(dst, 1u, static_cast(size), it->second.file); + result = std::ferror(it->second.file) ? kMcResultDeniedPermit : static_cast(bytesRead); + if (std::ferror(it->second.file)) + { + std::clearerr(it->second.file); + } + } + + setMcCommandResultLocked(kMcCmdRead, result); + } + setReturnS32(ctx, 0); +} + +void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string oldPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + const std::string newPath = readPs2CStringBounded(rdram, getRegU32(ctx, 7), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + const std::filesystem::path oldHostPath = + guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, oldPath)); + const std::filesystem::path newHostPath = + guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, newPath)); + std::error_code ec; + if (std::filesystem::exists(oldHostPath, ec) && !ec && + std::filesystem::exists(newHostPath.parent_path(), ec) && !ec) + { + std::filesystem::rename(oldHostPath, newHostPath, ec); + result = ec ? kMcResultDeniedPermit : kMcResultSucceed; + } + } + } + + setMcCommandResultLocked(kMcCmdRename, result); + } + setReturnS32(ctx, 0); +} + +void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t fd = static_cast(getRegU32(ctx, 4)); + const int32_t offset = static_cast(getRegU32(ctx, 5)); + const int32_t origin = static_cast(getRegU32(ctx, 6)); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (it != g_mcFiles.end() && it->second.file) + { + int whence = SEEK_SET; + if (origin == PS2_FIO_SEEK_CUR) + { + whence = SEEK_CUR; + } + else if (origin == PS2_FIO_SEEK_END) + { + whence = SEEK_END; + } + + if (std::fseek(it->second.file, offset, whence) == 0) + { + const long position = std::ftell(it->second.file); + result = (position >= 0) ? static_cast(position) : kMcResultDeniedPermit; + } + else + { + result = kMcResultDeniedPermit; + } + } + + setMcCommandResultLocked(kMcCmdSeek, result); + } + setReturnS32(ctx, 0); +} + +void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + const std::filesystem::path hostPath = + guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, path)); + std::error_code ec; + if (std::filesystem::exists(hostPath, ec) && !ec) + { + result = kMcResultSucceed; + } + } + } + + setMcCommandResultLocked(kMcCmdSetFileInfo, result); + } + setReturnS32(ctx, 0); +} + +void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t cmdPtr = getRegU32(ctx, 5); + const uint32_t resultPtr = getRegU32(ctx, 6); + int32_t cmd = 0; + int32_t result = 0; + { + std::lock_guard lock(g_mcStateMutex); + cmd = g_mcLastCmd; + result = g_mcLastResult; + } + + if (cmdPtr != 0u) + { + if (uint8_t *out = getMemPtr(rdram, cmdPtr)) + { + std::memcpy(out, &cmd, sizeof(cmd)); + } + } + if (resultPtr != 0u) + { + if (uint8_t *out = getMemPtr(rdram, resultPtr)) + { + std::memcpy(out, &result, sizeof(result)); + } + } + + // 1 = command finished in this runtime's immediate model. + setReturnS32(ctx, 1); +} + +void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + closeMcFilesForPortLocked(port); + const std::filesystem::path root = getMcRootPath(port); + std::error_code ec; + std::filesystem::remove_all(root, ec); + ec.clear(); + std::filesystem::create_directories(root, ec); + if (!ec) + { + McPortState &state = g_mcPorts[static_cast(port)]; + state.currentDir = "/"; + state.formatted = false; + result = kMcResultSucceed; + } + } + + setMcCommandResultLocked(kMcCmdUnformat, result); + } + setReturnS32(ctx, 0); +} + +void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t fd = static_cast(getRegU32(ctx, 4)); + const uint32_t srcAddr = getRegU32(ctx, 5); + const int32_t size = static_cast(getRegU32(ctx, 6)); + const uint8_t *src = (size > 0) ? getConstMemPtr(rdram, srcAddr) : nullptr; + + int32_t result = kMcResultNoEntry; + { + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (size <= 0) + { + result = 0; + } + else if (it == g_mcFiles.end() || !it->second.file) + { + result = kMcResultNoEntry; + } + else if (!src) + { + result = kMcResultDeniedPermit; + } + else + { + const size_t bytesWritten = std::fwrite(src, 1u, static_cast(size), it->second.file); + result = std::ferror(it->second.file) ? kMcResultDeniedPermit : static_cast(bytesWritten); + if (!std::ferror(it->second.file)) + { + std::fflush(it->second.file); + } + else + { + std::clearerr(it->second.file); + } + } + + setMcCommandResultLocked(kMcCmdWrite, result); + } + setReturnS32(ctx, 0); +} + + +void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, kCvMcConfigCapacityBytes); +} + +void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_cvMcFileCursor); +} + +void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, kCvMcFreeCapacityBytes); +} + +void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, kCvMcIconCapacityBytes); +} + +void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, kCvMcIconCapacityBytes); +} + +void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, kCvMcSaveCapacityBytes); +} + +void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t strAddr = getRegU32(ctx, 4); + const std::string value = readPs2CStringBounded(rdram, runtime, strAddr, 1024); + setReturnU32(ctx, strAddr + static_cast(value.size())); +} + +void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t delta = static_cast(getRegU32(ctx, 5)); + g_cvMcFileCursor += delta; + g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); + setReturnS32(ctx, 0); +} + +void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cvMcFileCursor = 0; + setReturnS32(ctx, 1); +} + +void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cvMcFileCursor = static_cast(getRegU32(ctx, 5)); + g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); + setReturnS32(ctx, 0); +} + +void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cvMcFileCursor = 0; + setReturnS32(ctx, 0); +} + +void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h new file mode 100644 index 00000000..08e1bbf1 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h @@ -0,0 +1,70 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp new file mode 100644 index 00000000..be31d7f6 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp @@ -0,0 +1,760 @@ +#include "Common.h" +#include "Pad.h" + +namespace ps2_stubs +{ + namespace + { + constexpr uint8_t kPadModeDigital = 0x41; + constexpr uint8_t kPadModeDualShock = 0x73; + constexpr uint8_t kPadAnalogCenter = 0x80; + constexpr int32_t kPadTypeDigital = 4; + constexpr int32_t kPadTypeDualShock = 7; + constexpr int32_t kPadStateDisconnected = 0; + constexpr int32_t kPadStateStable = 6; + constexpr size_t kPadPortCount = 2; + constexpr size_t kPadSlotCount = 1; + + constexpr uint16_t kPadBtnSelect = 1u << 0; + constexpr uint16_t kPadBtnL3 = 1u << 1; + constexpr uint16_t kPadBtnR3 = 1u << 2; + constexpr uint16_t kPadBtnStart = 1u << 3; + constexpr uint16_t kPadBtnUp = 1u << 4; + constexpr uint16_t kPadBtnRight = 1u << 5; + constexpr uint16_t kPadBtnDown = 1u << 6; + constexpr uint16_t kPadBtnLeft = 1u << 7; + constexpr uint16_t kPadBtnL2 = 1u << 8; + constexpr uint16_t kPadBtnR2 = 1u << 9; + constexpr uint16_t kPadBtnL1 = 1u << 10; + constexpr uint16_t kPadBtnR1 = 1u << 11; + constexpr uint16_t kPadBtnTriangle = 1u << 12; + constexpr uint16_t kPadBtnCircle = 1u << 13; + constexpr uint16_t kPadBtnCross = 1u << 14; + constexpr uint16_t kPadBtnSquare = 1u << 15; + + struct PadInputState + { + uint16_t buttons = 0xFFFF; // active-low + uint8_t rx = kPadAnalogCenter; + uint8_t ry = kPadAnalogCenter; + uint8_t lx = kPadAnalogCenter; + uint8_t ly = kPadAnalogCenter; + }; + + struct PadPortState + { + bool open = false; + bool analogMode = true; + bool pressureEnabled = false; + uint16_t buttonMask = 0xFFFFu; + uint32_t dmaAddr = 0u; + uint32_t reqState = 0u; + }; + + std::mutex g_padOverrideMutex; + std::mutex g_padStateMutex; + bool g_padOverrideEnabled = false; + PadInputState g_padOverrideState{}; + PadPortState g_padPorts[kPadPortCount]{}; + int g_padReadLogCount = 0; + + uint8_t axisToByte(float axis) + { + axis = std::clamp(axis, -1.0f, 1.0f); + const float mapped = (axis + 1.0f) * 127.5f; + return static_cast(std::lround(mapped)); + } + + void setButton(PadInputState &state, uint16_t mask, bool pressed) + { + if (pressed) + { + state.buttons = static_cast(state.buttons & ~mask); + } + } + + int findFirstGamepad() + { + for (int i = 0; i < 4; ++i) + { + if (IsGamepadAvailable(i)) + { + return i; + } + } + return -1; + } + + void applyGamepadState(PadInputState &state) + { + if (!IsWindowReady()) + { + return; + } + + const int gamepad = findFirstGamepad(); + if (gamepad < 0) + { + return; + } + + // Raylib mapping (PS2 -> raylib buttons/axes): + // D-Pad -> LEFT_FACE_*, Cross/Circle/Square/Triangle -> RIGHT_FACE_* + // L1/R1 -> TRIGGER_1, L2/R2 -> TRIGGER_2, L3/R3 -> THUMB + // Select/Start -> MIDDLE_LEFT/MIDDLE_RIGHT + state.lx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X)); + state.ly = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y)); + state.rx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X)); + state.ry = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y)); + + setButton(state, kPadBtnUp, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)); + setButton(state, kPadBtnDown, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)); + setButton(state, kPadBtnLeft, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)); + setButton(state, kPadBtnRight, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)); + + setButton(state, kPadBtnCross, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)); + setButton(state, kPadBtnCircle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)); + setButton(state, kPadBtnSquare, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)); + setButton(state, kPadBtnTriangle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)); + + setButton(state, kPadBtnL1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)); + setButton(state, kPadBtnR1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)); + setButton(state, kPadBtnL2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2)); + setButton(state, kPadBtnR2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)); + + setButton(state, kPadBtnL3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)); + setButton(state, kPadBtnR3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)); + + setButton(state, kPadBtnSelect, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)); + setButton(state, kPadBtnStart, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)); + } + + void applyKeyboardState(PadInputState &state, bool allowAnalog) + { + if (!IsWindowReady()) + { + return; + } + + // Keyboard mapping (PS2 -> keys): + // D-Pad: arrows, Square/Cross/Circle/Triangle: Z/X/C/V + // L1/R1: Q/E, L2/R2: 1/3, Start/Select: Enter/RightShift + // L3/R3: LeftCtrl/RightCtrl, Analog left: WASD + setButton(state, kPadBtnUp, IsKeyDown(KEY_UP)); + setButton(state, kPadBtnDown, IsKeyDown(KEY_DOWN)); + setButton(state, kPadBtnLeft, IsKeyDown(KEY_LEFT)); + setButton(state, kPadBtnRight, IsKeyDown(KEY_RIGHT)); + + setButton(state, kPadBtnSquare, IsKeyDown(KEY_Z)); + setButton(state, kPadBtnCross, IsKeyDown(KEY_X)); + setButton(state, kPadBtnCircle, IsKeyDown(KEY_C)); + setButton(state, kPadBtnTriangle, IsKeyDown(KEY_V)); + + setButton(state, kPadBtnL1, IsKeyDown(KEY_Q)); + setButton(state, kPadBtnR1, IsKeyDown(KEY_E)); + setButton(state, kPadBtnL2, IsKeyDown(KEY_ONE)); + setButton(state, kPadBtnR2, IsKeyDown(KEY_THREE)); + + setButton(state, kPadBtnStart, IsKeyDown(KEY_ENTER)); + setButton(state, kPadBtnSelect, IsKeyDown(KEY_RIGHT_SHIFT)); + setButton(state, kPadBtnL3, IsKeyDown(KEY_LEFT_CONTROL)); + setButton(state, kPadBtnR3, IsKeyDown(KEY_RIGHT_CONTROL)); + + if (!allowAnalog) + { + return; + } + + float ax = 0.0f; + float ay = 0.0f; + if (IsKeyDown(KEY_D)) + ax += 1.0f; + if (IsKeyDown(KEY_A)) + ax -= 1.0f; + if (IsKeyDown(KEY_S)) + ay += 1.0f; + if (IsKeyDown(KEY_W)) + ay -= 1.0f; + + if (ax != 0.0f || ay != 0.0f) + { + state.lx = axisToByte(ax); + state.ly = axisToByte(ay); + } + } + + void resetPadStateLocked() + { + for (PadPortState &portState : g_padPorts) + { + portState = PadPortState{}; + } + } + + PadPortState *lookupPadPortStateLocked(int port, int slot) + { + if (port < 0 || port >= static_cast(kPadPortCount)) + { + return nullptr; + } + if (slot < 0 || slot >= static_cast(kPadSlotCount)) + { + return nullptr; + } + return &g_padPorts[port]; + } + + void initializePadPortLocked(PadPortState &portState, uint32_t dmaAddr) + { + portState.open = true; + portState.analogMode = true; + portState.pressureEnabled = false; + portState.buttonMask = 0xFFFFu; + portState.dmaAddr = dmaAddr; + portState.reqState = 0u; + } + + uint8_t pressureValue(const PadInputState &state, const PadPortState &portState, uint16_t mask) + { + if (!portState.pressureEnabled) + { + return 0u; + } + if ((portState.buttonMask & mask) == 0u) + { + return 0u; + } + return ((state.buttons & mask) == 0u) ? 0xFFu : 0u; + } + + void fillPadStatus(uint8_t *data, const PadInputState &state, const PadPortState &portState) + { + std::memset(data, 0, 32); + data[1] = portState.analogMode ? kPadModeDualShock : kPadModeDigital; + data[2] = static_cast(state.buttons & 0xFFu); + data[3] = static_cast((state.buttons >> 8) & 0xFFu); + data[4] = state.rx; + data[5] = state.ry; + data[6] = state.lx; + data[7] = state.ly; + data[8] = pressureValue(state, portState, kPadBtnRight); + data[9] = pressureValue(state, portState, kPadBtnLeft); + data[10] = pressureValue(state, portState, kPadBtnUp); + data[11] = pressureValue(state, portState, kPadBtnDown); + data[12] = pressureValue(state, portState, kPadBtnTriangle); + data[13] = pressureValue(state, portState, kPadBtnCircle); + data[14] = pressureValue(state, portState, kPadBtnCross); + data[15] = pressureValue(state, portState, kPadBtnSquare); + data[16] = pressureValue(state, portState, kPadBtnL1); + data[17] = pressureValue(state, portState, kPadBtnL2); + data[18] = pressureValue(state, portState, kPadBtnR1); + data[19] = pressureValue(state, portState, kPadBtnR2); + } + + bool readPadPortData(int port, int slot, PS2Runtime *runtime, uint8_t *outData) + { + if (!outData) + { + return false; + } + + PadPortState portState; + { + std::lock_guard lock(g_padStateMutex); + const PadPortState *sharedPortState = lookupPadPortStateLocked(port, slot); + if (!sharedPortState || !sharedPortState->open) + { + return false; + } + portState = *sharedPortState; + } + + PadInputState state; + bool useOverride = false; + { + std::lock_guard lock(g_padOverrideMutex); + if (g_padOverrideEnabled) + { + state = g_padOverrideState; + useOverride = true; + } + } + + if (!useOverride) + { + uint8_t backendData[32]{}; + if (runtime && runtime->padBackend().readState(port, slot, backendData, sizeof(backendData))) + { + state.buttons = static_cast(backendData[2] | (backendData[3] << 8)); + state.rx = backendData[4]; + state.ry = backendData[5]; + state.lx = backendData[6]; + state.ly = backendData[7]; + } + else + { + applyGamepadState(state); + applyKeyboardState(state, portState.analogMode); + } + } + + fillPadStatus(outData, state, portState); + + return true; + } + } + +void PadSyncCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + { + std::lock_guard lock(g_padStateMutex); + resetPadStateLocked(); + } + setReturnS32(ctx, 1); +} + +void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } + + portState->pressureEnabled = true; + portState->reqState = 0u; + setReturnS32(ctx, 1); +} + +void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } + + portState->pressureEnabled = false; + portState->reqState = 0u; + setReturnS32(ctx, 1); +} + +void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + const uint16_t mask = portState ? portState->buttonMask : 0xFFFFu; + setReturnS32(ctx, static_cast(mask)); +} + +void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + const uint32_t dmaAddr = portState ? portState->dmaAddr : getRegU32(ctx, 6); + setReturnU32(ctx, dmaAddr); +} + +void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + static std::atomic frameCount{0}; + setReturnU32(ctx, frameCount++); +} + +void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // Arbitrary non-zero module version. + setReturnS32(ctx, 0x0200); +} + +void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 2); +} + +void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + setReturnS32(ctx, static_cast(portState ? portState->reqState : 0u)); +} + +void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // Most games use one slot unless multitap is active. + setReturnS32(ctx, 1); +} + +void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + setReturnS32(ctx, (portState && portState->open) ? kPadStateStable : kPadStateDisconnected); +} + +void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t act = static_cast(getRegU32(ctx, 6)); + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } + + if (act < 0) + { + setReturnS32(ctx, 2); // small + large motors + return; + } + setReturnS32(ctx, (act < 2) ? 1 : 0); +} + +void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // No combined modes reported. + setReturnS32(ctx, 0); +} + +void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + const int32_t infoMode = static_cast(getRegU32(ctx, 6)); // a2 + const int32_t index = static_cast(getRegU32(ctx, 7)); // a3 + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } + + const int32_t currentId = portState->analogMode ? kPadTypeDualShock : kPadTypeDigital; + switch (infoMode) + { + case 1: // PAD_MODECURID + setReturnS32(ctx, currentId); + return; + case 2: // PAD_MODECUREXID + setReturnS32(ctx, currentId); + return; + case 3: // PAD_MODECUROFFS + setReturnS32(ctx, 0); + return; + case 4: // PAD_MODETABLE + if (index == -1) + { + setReturnS32(ctx, 1); // one available mode + } + else if (index == 0) + { + setReturnS32(ctx, currentId); + } + else + { + setReturnS32(ctx, 0); + } + return; + default: + setReturnS32(ctx, 0); + return; + } +} + +void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + setReturnS32(ctx, (portState && portState->open) ? 1 : 0); +} + +void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + { + std::lock_guard lock(g_padStateMutex); + resetPadStateLocked(); + } + setReturnS32(ctx, 1); +} + +void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + scePadInit(rdram, ctx, runtime); +} + +void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState) + { + setReturnS32(ctx, 0); + return; + } + + portState->open = false; + portState->pressureEnabled = false; + portState->reqState = 0u; + setReturnS32(ctx, 1); +} + +void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + const uint32_t dmaAddr = getRegU32(ctx, 6); + uint8_t *dmaStr = getMemPtr(rdram, dmaAddr); + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || (dmaAddr != 0u && !dmaStr)) + { + setReturnS32(ctx, 0); + return; + } + + portState->open = true; + portState->analogMode = true; + portState->pressureEnabled = false; + portState->buttonMask = 0xFFFFu; + portState->dmaAddr = dmaAddr; + portState->reqState = 0u; + if (dmaStr) + { + std::memset(dmaStr, 0, 32); + } + setReturnS32(ctx, 1); +} + +void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int port = static_cast(getRegU32(ctx, 4)); + const int slot = static_cast(getRegU32(ctx, 5)); + const uint32_t dataAddr = getRegU32(ctx, 6); + uint8_t *data = getMemPtr(rdram, dataAddr); + if (!data) + { + setReturnS32(ctx, 0); + return; + } + + if (!readPadPortData(port, slot, runtime, data)) + { + setReturnS32(ctx, 0); + return; + } + + if (g_padReadLogCount < 48) + { + const int gamepad = findFirstGamepad(); + const bool gamepadStartPressed = + (gamepad >= 0) && IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT); + const bool startPressed = (data[2] != 0xFFu || data[3] != 0xFFu || + IsKeyDown(KEY_ENTER) || gamepadStartPressed); + if (startPressed) + { + const uint32_t guestButtons = + (static_cast(static_cast(data[2] ^ 0xFFu)) << 8) | + static_cast(static_cast(data[3] ^ 0xFFu)); + std::printf("[padread] port=%d slot=%d data2=0x%02x data3=0x%02x guestButtons=0x%04x enter=%d gamepadStart=%d\n", + port, slot, data[2], data[3], guestButtons, + IsKeyDown(KEY_ENTER) ? 1 : 0, gamepadStartPressed ? 1 : 0); + ++g_padReadLogCount; + } + } + + setReturnS32(ctx, 1); +} + +void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + const uint32_t state = getRegU32(ctx, 4); + const uint32_t strAddr = getRegU32(ctx, 5); + char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); + if (!buf) + { + setReturnS32(ctx, -1); + return; + } + + const char *text = (state == 0) ? "COMPLETE" : "BUSY"; + std::strncpy(buf, text, 31); + buf[31] = '\0'; + setReturnS32(ctx, 0); +} + +void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (portState && portState->open) + { + portState->buttonMask = static_cast(getRegU32(ctx, 6)); + portState->reqState = 0u; + } + setReturnS32(ctx, 1); +} + +void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } + + portState->analogMode = (getRegU32(ctx, 6) != 0u); + portState->reqState = 0u; + setReturnS32(ctx, 1); +} + +void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (portState && portState->open) + { + portState->reqState = static_cast(getRegU32(ctx, 6) ? 1u : 0u); + } + setReturnS32(ctx, 1); +} + +void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 0); +} + +void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + const uint32_t state = getRegU32(ctx, 4); + const uint32_t strAddr = getRegU32(ctx, 5); + char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); + if (!buf) + { + setReturnS32(ctx, -1); + return; + } + + const char *text = "UNKNOWN"; + if (state == 6) + { + text = "STABLE"; + } + else if (state == 1) + { + text = "FINDPAD"; + } + else if (state == 0) + { + text = "DISCONNECTED"; + } + + std::strncpy(buf, text, 31); + buf[31] = '\0'; + setReturnS32(ctx, 0); +} + + void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry) + { + std::lock_guard lock(g_padOverrideMutex); + g_padOverrideEnabled = true; + g_padOverrideState.buttons = buttons; + g_padOverrideState.lx = lx; + g_padOverrideState.ly = ly; + g_padOverrideState.rx = rx; + g_padOverrideState.ry = ry; + } + + + void clearPadOverrideState() + { + std::lock_guard lock(g_padOverrideMutex); + g_padOverrideEnabled = false; + g_padOverrideState = PadInputState{}; + } +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Pad.h b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.h new file mode 100644 index 00000000..5c284d12 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.h @@ -0,0 +1,39 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void PadSyncCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry); + void clearPadOverrideState(); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp new file mode 100644 index 00000000..4fa86617 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp @@ -0,0 +1,22 @@ +#include "Common.h" +#include "RPC.h" + +namespace ps2_stubs +{ +void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceRpcFreePacket", rdram, ctx, runtime); +} + + +void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceRpcGetFPacket", rdram, ctx, runtime); +} + + +void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceRpcGetFPacket2", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/RPC.h b/ps2xRuntime/src/lib/Kernel/Stubs/RPC.h new file mode 100644 index 00000000..abc4e23d --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/RPC.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp new file mode 100644 index 00000000..cd8c20a4 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp @@ -0,0 +1,708 @@ +#include "Common.h" +#include "SIF.h" +#include "../Syscalls/RPC.h" + +namespace ps2_stubs +{ +void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifCmdIntrHdlr", rdram, ctx, runtime); +} + + +void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifLoadModule(rdram, ctx, runtime); +} + + +void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t srcAddr = getRegU32(ctx, 7); // $a3 + const uint32_t dstAddr = readStackU32(rdram, ctx, 16); + const uint32_t size = readStackU32(rdram, ctx, 20); + if (size != 0u && srcAddr != 0u && dstAddr != 0u) + { + for (uint32_t i = 0; i < size; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + uint8_t *dst = getMemPtr(rdram, dstAddr + i); + if (!src || !dst) + { + break; + } + *dst = *src; + } + } + + setReturnS32(ctx, 1); +} + + +namespace +{ + struct Ps2SifDmaTransfer + { + uint32_t src = 0; + uint32_t dest = 0; + int32_t size = 0; + int32_t attr = 0; + }; + static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected SIF DMA descriptor size"); + + std::mutex g_sifDmaTransferMutex; + uint32_t g_nextSifDmaTransferId = 1u; + std::mutex g_sifCmdStateMutex; + std::unordered_map g_sifRegs; + std::unordered_map g_sifSregs; + std::unordered_map g_sifCmdHandlers; + uint32_t g_sifCmdBuffer = 0u; + uint32_t g_sifSysCmdBuffer = 0u; + bool g_sifCmdInitialized = false; + uint32_t g_sifGetRegLogCount = 0u; + uint32_t g_sifSetRegLogCount = 0u; + + constexpr uint32_t kSifRegBootStatus = 0x4u; + constexpr uint32_t kSifRegMainAddr = 0x80000000u; + constexpr uint32_t kSifRegSubAddr = 0x80000001u; + constexpr uint32_t kSifRegMsCom = 0x80000002u; + constexpr uint32_t kSifBootReadyMask = 0x00020000u; + + void seedDefaultSifRegsLocked() + { + g_sifRegs.clear(); + g_sifSregs.clear(); + g_sifCmdHandlers.clear(); + g_sifCmdBuffer = 0u; + g_sifSysCmdBuffer = 0u; + g_sifCmdInitialized = false; + g_sifGetRegLogCount = 0u; + g_sifSetRegLogCount = 0u; + + g_sifRegs[kSifRegBootStatus] = kSifBootReadyMask; + g_sifRegs[kSifRegMainAddr] = 0u; + g_sifRegs[kSifRegSubAddr] = 0u; + g_sifRegs[kSifRegMsCom] = 0u; + } + + bool shouldTraceSifReg(uint32_t reg) + { + switch (reg) + { + case 0x2u: + case 0x4u: + case 0x80000000u: + case 0x80000001u: + case 0x80000002u: + return true; + default: + return false; + } + } + + struct SifStateInitializer + { + SifStateInitializer() + { + std::lock_guard lock(g_sifCmdStateMutex); + seedDefaultSifRegsLocked(); + } + } g_sifStateInitializer; + + uint32_t allocateSifDmaTransferId() + { + std::lock_guard lock(g_sifDmaTransferMutex); + uint32_t id = g_nextSifDmaTransferId++; + if (id == 0u) + { + id = g_nextSifDmaTransferId++; + } + return id; + } + + bool isCopyableGuestAddress(uint32_t addr) + { + if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) + { + return true; + } + + if (addr < 0x20000000u) + { + return true; + } + + if (addr >= 0x20000000u && addr < 0x40000000u) + { + return true; + } + + if (addr >= 0x80000000u && addr < 0xC0000000u) + { + return true; + } + + return false; + } + + bool canCopyGuestByteRange(const uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) + { + if (!rdram) + { + return false; + } + + if (sizeBytes == 0u) + { + return true; + } + + for (uint32_t i = 0u; i < sizeBytes; ++i) + { + const uint32_t srcByteAddr = srcAddr + i; + const uint32_t dstByteAddr = dstAddr + i; + + if (!isCopyableGuestAddress(srcByteAddr) || !isCopyableGuestAddress(dstByteAddr)) + { + return false; + } + + const uint8_t *src = getConstMemPtr(rdram, srcByteAddr); + const uint8_t *dst = getConstMemPtr(rdram, dstByteAddr); + if (!src || !dst) + { + return false; + } + } + + return true; + } + + bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) + { + if (!canCopyGuestByteRange(rdram, dstAddr, srcAddr, sizeBytes)) + { + return false; + } + + if (sizeBytes == 0u) + { + return true; + } + + const uint64_t srcBegin = srcAddr; + const uint64_t srcEnd = srcBegin + static_cast(sizeBytes); + const uint64_t dstBegin = dstAddr; + const bool copyBackward = (dstBegin > srcBegin) && (dstBegin < srcEnd); + + if (copyBackward) + { + for (uint32_t i = sizeBytes; i > 0u; --i) + { + const uint32_t index = i - 1u; + const uint8_t *src = getConstMemPtr(rdram, srcAddr + index); + uint8_t *dst = getMemPtr(rdram, dstAddr + index); + if (!src || !dst) + { + return false; + } + *dst = *src; + } + return true; + } + + for (uint32_t i = 0; i < sizeBytes; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + uint8_t *dst = getMemPtr(rdram, dstAddr + i); + if (!src || !dst) + { + return false; + } + *dst = *src; + } + return true; + } +} + +void resetSifState() +{ + std::lock_guard lock(g_sifCmdStateMutex); + seedDefaultSifRegsLocked(); +} + +void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t cid = getRegU32(ctx, 4); + const uint32_t handler = getRegU32(ctx, 5); + std::lock_guard lock(g_sifCmdStateMutex); + g_sifCmdHandlers[cid] = handler; + setReturnS32(ctx, 0); +} + +void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t reqSize = getRegU32(ctx, 4); + const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); + if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) + { + setReturnS32(ctx, 0); + return; + } + + const uint32_t allocAddr = g_iopHeapNext; + g_iopHeapNext += alignedSize; + setReturnS32(ctx, static_cast(allocAddr)); +} + +void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifBindRpc(rdram, ctx, runtime); +} + +void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime); +} + +void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + (void)getRegU32(ctx, 4); // trid + + // Transfers are applied immediately by sceSifSetDma in this runtime. + setReturnS32(ctx, -1); +} + +void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::lock_guard lock(g_sifCmdStateMutex); + seedDefaultSifRegsLocked(); + setReturnS32(ctx, 0); +} + +void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::lock_guard lock(g_sifCmdStateMutex); + setReturnU32(ctx, g_sifCmdBuffer); +} + +void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, getRegU32(ctx, 4)); +} + +void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + + const uint32_t rdAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + const uint32_t dstAddr = getRegU32(ctx, 6); + const int32_t sizeSigned = static_cast(getRegU32(ctx, 7)); + + if (sizeSigned <= 0) + { + setReturnS32(ctx, 0); + return; + } + + const uint32_t size = static_cast(sizeSigned); + if (size > PS2_RAM_SIZE) + { + static uint32_t warnCount = 0; + if (warnCount < 32u) + { + std::cerr << "sceSifGetOtherData rejected oversized transfer size=0x" + << std::hex << size << std::dec << std::endl; + ++warnCount; + } + setReturnS32(ctx, -1); + return; + } + + ps2_syscalls::prepareSoundDriverStatusTransfer(rdram, srcAddr, size); + + if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size)) + { + static uint32_t warnCount = 0; + if (warnCount < 32u) + { + std::cerr << "sceSifGetOtherData copy failed src=0x" << std::hex << srcAddr + << " dst=0x" << dstAddr + << " size=0x" << size + << std::dec << std::endl; + ++warnCount; + } + setReturnS32(ctx, -1); + return; + } + + // SifRpcReceiveData_t keeps src/dest/size at offsets 0x10/0x14/0x18. + if (uint8_t *rd = getMemPtr(rdram, rdAddr)) + { + std::memcpy(rd + 0x10u, &srcAddr, sizeof(srcAddr)); + std::memcpy(rd + 0x14u, &dstAddr, sizeof(dstAddr)); + std::memcpy(rd + 0x18u, &size, sizeof(size)); + } + + ps2_syscalls::finalizeSoundDriverStatusTransfer(rdram, srcAddr, dstAddr, size); + + setReturnS32(ctx, 0); +} + +void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t reg = getRegU32(ctx, 4); + uint32_t value = 0u; + bool shouldLog = false; + { + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifRegs.find(reg); + if (it != g_sifRegs.end()) + { + value = it->second; + } + shouldLog = shouldTraceSifReg(reg) && g_sifGetRegLogCount < 128u; + if (shouldLog) + { + ++g_sifGetRegLogCount; + } + } + if (shouldLog) + { + auto flags = std::cerr.flags(); + std::cerr << "[sceSifGetReg] reg=0x" << std::hex << reg + << " value=0x" << value + << " pc=0x" << (ctx ? ctx->pc : 0u) + << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) + << std::dec << std::endl; + std::cerr.flags(flags); + } + setReturnU32(ctx, value); +} + +void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t reg = getRegU32(ctx, 4); + uint32_t value = 0u; + { + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifSregs.find(reg); + if (it != g_sifSregs.end()) + { + value = it->second; + } + } + setReturnU32(ctx, value); +} + +void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::lock_guard lock(g_sifCmdStateMutex); + g_sifCmdInitialized = true; + setReturnS32(ctx, 0); +} + +void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_iopHeapNext = kIopHeapBase; + setReturnS32(ctx, 0); +} + +void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifInitRpc(rdram, ctx, runtime); +} + +void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime); +} + +void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime); +} + +void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime); +} + +void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime); +} + +void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t cid = getRegU32(ctx, 4); + std::lock_guard lock(g_sifCmdStateMutex); + g_sifCmdHandlers.erase(cid); + setReturnS32(ctx, 0); +} + +void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime); +} + +void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime); +} + +void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t newBuffer = getRegU32(ctx, 4); + uint32_t prev = 0u; + { + std::lock_guard lock(g_sifCmdStateMutex); + prev = g_sifCmdBuffer; + g_sifCmdBuffer = newBuffer; + } + setReturnU32(ctx, prev); +} + +void isceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + sceSifSetDChain(rdram, ctx, runtime); +} + +void isceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + sceSifSetDma(rdram, ctx, runtime); +} + +void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 0); +} + +void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + + const uint32_t dmatAddr = getRegU32(ctx, 4); + const uint32_t count = getRegU32(ctx, 5); + if (!dmatAddr || count == 0u || count > 32u) + { + setReturnS32(ctx, 0); + return; + } + + std::array pending{}; + uint32_t pendingCount = 0u; + bool ok = true; + for (uint32_t i = 0; i < count; ++i) + { + const uint32_t entryAddr = dmatAddr + (i * static_cast(sizeof(Ps2SifDmaTransfer))); + const uint8_t *entry = getConstMemPtr(rdram, entryAddr); + if (!entry) + { + ok = false; + break; + } + + Ps2SifDmaTransfer xfer{}; + std::memcpy(&xfer, entry, sizeof(xfer)); + if (xfer.size <= 0) + { + continue; + } + + const uint32_t sizeBytes = static_cast(xfer.size); + if (sizeBytes > PS2_RAM_SIZE) + { + ok = false; + break; + } + if (!canCopyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes)) + { + ok = false; + break; + } + + pending[pendingCount++] = xfer; + } + + if (ok) + { + for (uint32_t i = 0; i < pendingCount; ++i) + { + const Ps2SifDmaTransfer &xfer = pending[i]; + if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast(xfer.size))) + { + ok = false; + break; + } + + ps2_syscalls::noteDtxSifDmaTransfer( + rdram, + xfer.src, + xfer.dest, + static_cast(xfer.size)); + } + } + + if (!ok) + { + static uint32_t warnCount = 0; + if (warnCount < 32u) + { + std::cerr << "sceSifSetDma failed dmat=0x" << std::hex << dmatAddr + << " count=0x" << count + << std::dec << std::endl; + ++warnCount; + } + setReturnS32(ctx, 0); + return; + } + + ps2_syscalls::dispatchDmacHandlersForCause(rdram, runtime, 5u); + + setReturnS32(ctx, static_cast(allocateSifDmaTransferId())); +} + +void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, getRegU32(ctx, 5)); +} + +void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t reg = getRegU32(ctx, 4); + const uint32_t value = getRegU32(ctx, 5); + uint32_t prev = 0u; + bool shouldLog = false; + { + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifRegs.find(reg); + if (it != g_sifRegs.end()) + { + prev = it->second; + } + g_sifRegs[reg] = value; + shouldLog = shouldTraceSifReg(reg) && g_sifSetRegLogCount < 128u; + if (shouldLog) + { + ++g_sifSetRegLogCount; + } + } + if (shouldLog) + { + auto flags = std::cerr.flags(); + std::cerr << "[sceSifSetReg] reg=0x" << std::hex << reg + << " prev=0x" << prev + << " value=0x" << value + << " pc=0x" << (ctx ? ctx->pc : 0u) + << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) + << std::dec << std::endl; + std::cerr.flags(flags); + } + setReturnU32(ctx, prev); +} + +void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime); +} + +void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t reg = getRegU32(ctx, 4); + const uint32_t value = getRegU32(ctx, 5); + uint32_t prev = 0u; + { + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifSregs.find(reg); + if (it != g_sifSregs.end()) + { + prev = it->second; + } + g_sifSregs[reg] = value; + } + setReturnU32(ctx, prev); +} + +void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t newBuffer = getRegU32(ctx, 4); + uint32_t prev = 0u; + { + std::lock_guard lock(g_sifCmdStateMutex); + prev = g_sifSysCmdBuffer; + g_sifSysCmdBuffer = newBuffer; + } + setReturnU32(ctx, prev); +} + +void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h new file mode 100644 index 00000000..63fc8b07 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void resetSifState(); + void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void isceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void isceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp new file mode 100644 index 00000000..a87653c3 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp @@ -0,0 +1,67 @@ +#include "Common.h" +#include "System.h" + +namespace ps2_stubs +{ +void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + RUNTIME_LOG("ps2_stub builtin_set_imask"); + ++logCount; + } + setReturnS32(ctx, 0); +} + + +void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + + +void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + + +void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + if (runtime) + { + runtime->requestStop(); + } + setReturnS32(ctx, 0); +} + + +void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + + +void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime); +} + + +void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSetPtm", rdram, ctx, runtime); +} + +void sceDevVif0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceDevVu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/System.h b/ps2xRuntime/src/lib/Kernel/Stubs/System.h new file mode 100644 index 00000000..39f0ad4a --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/System.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDevVif0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceDevVu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp new file mode 100644 index 00000000..57063b00 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp @@ -0,0 +1,61 @@ +#include "Common.h" +#include "TTY.h" +#include "ps2_log.h" + +namespace ps2_stubs +{ +void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t format_addr = getRegU32(ctx, 4); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + if (format_addr == 0) + return; + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); + if (rendered.size() > 2048) + rendered.resize(2048); + PS2_IF_AGRESSIVE_LOGS({ + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + RUNTIME_LOG("PS2 scePrintf: " << logLine); + RUNTIME_LOG(std::flush); + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + }); +} + + +void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceResetttyinit", rdram, ctx, runtime); +} + + +void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyHandler", rdram, ctx, runtime); +} + +void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyInit", rdram, ctx, runtime); +} + +void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyRead", rdram, ctx, runtime); +} + +void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyWrite", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/TTY.h b/ps2xRuntime/src/lib/Kernel/Stubs/TTY.h new file mode 100644 index 00000000..b63af005 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/TTY.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp new file mode 100644 index 00000000..6050b5a6 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp @@ -0,0 +1,49 @@ +#include "Common.h" +#include "Unimplemented.h" + +namespace ps2_stubs +{ + void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("unknown", rdram, ctx, runtime); + } + + + void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const std::string stubName = name ? name : "unknown"; + + uint32_t callCount = 0; + { + std::lock_guard lock(g_stubWarningMutex); + callCount = ++g_stubWarningCount[stubName]; + } + + if (callCount > kMaxStubWarningsPerName) + { + if (callCount == (kMaxStubWarningsPerName + 1)) + { + std::cerr << "Warning: Further calls to PS2 stub '" << stubName + << "' are suppressed after " << kMaxStubWarningsPerName << " warnings" << std::endl; + } + setReturnS32(ctx, -1); + return; + } + + uint32_t stub_num = getRegU32(ctx, 2); // $v0 + uint32_t caller_ra = getRegU32(ctx, 31); // $ra + + std::cerr << "Warning: Unimplemented PS2 stub called. name=" << stubName + << " PC=0x" << std::hex << ctx->pc + << ", RA=0x" << caller_ra + << ", Stub# guess (from $v0)=0x" << stub_num << std::dec << std::endl; + + // More context for debugging + std::cerr << " Args: $a0=0x" << std::hex << getRegU32(ctx, 4) + << ", $a1=0x" << getRegU32(ctx, 5) + << ", $a2=0x" << getRegU32(ctx, 6) + << ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl; + + setReturnS32(ctx, -1); // Return error + } +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.h b/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.h new file mode 100644 index 00000000..75c65c33 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp new file mode 100644 index 00000000..309d9e9a --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp @@ -0,0 +1,435 @@ +#include "Common.h" +#include "VU.h" + +namespace ps2_stubs +{ +void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime); +} + + +namespace +{ + bool readVuVec4f(uint8_t *rdram, uint32_t addr, float (&out)[4]) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(out, ptr, sizeof(out)); + return true; + } + + bool writeVuVec4f(uint8_t *rdram, uint32_t addr, const float (&in)[4]) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, in, sizeof(in)); + return true; + } + + bool readVuVec4i(uint8_t *rdram, uint32_t addr, int32_t (&out)[4]) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(out, ptr, sizeof(out)); + return true; + } + + bool writeVuVec4i(uint8_t *rdram, uint32_t addr, const int32_t (&in)[4]) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, in, sizeof(in)); + return true; + } +} + +void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t lhsAddr = getRegU32(ctx, 5); + const uint32_t rhsAddr = getRegU32(ctx, 6); + float lhs[4]{}, rhs[4]{}, out[4]{}; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = lhs[i] + rhs[i]; + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); +} + +void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime); +} + +void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime); +} + +void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime); +} + +void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime); +} + +void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime); +} + +void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); +} + +void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); +} + +void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime); +} + +void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime); +} + +void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}; + int32_t out[4]{}; + if (readVuVec4f(rdram, srcAddr, src)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]); + } + (void)writeVuVec4i(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}; + int32_t out[4]{}; + if (readVuVec4f(rdram, srcAddr, src)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i] * 16.0f); + } + (void)writeVuVec4i(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t lhsAddr = getRegU32(ctx, 4); + const uint32_t rhsAddr = getRegU32(ctx, 5); + float lhs[4]{}, rhs[4]{}; + float dot = 0.0f; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + { + dot = (lhs[0] * rhs[0]) + (lhs[1] * rhs[1]) + (lhs[2] * rhs[2]) + (lhs[3] * rhs[3]); + } + + if (ctx) + { + ctx->f[0] = dot; + } + uint32_t raw = 0u; + std::memcpy(&raw, &dot, sizeof(raw)); + setReturnU32(ctx, raw); +} + +void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime); +} + +void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime); +} + +void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + int32_t src[4]{}; + float out[4]{}; + if (readVuVec4i(rdram, srcAddr, src)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]); + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + int32_t src[4]{}; + float out[4]{}; + if (readVuVec4i(rdram, srcAddr, src)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]) / 4096.0f; + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + int32_t src[4]{}; + float out[4]{}; + if (readVuVec4i(rdram, srcAddr, src)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]) / 16.0f; + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime); +} + +void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime); +} + +void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime); +} + +void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}, out[4]{}; + if (readVuVec4f(rdram, srcAddr, src)) + { + const float len = std::sqrt((src[0] * src[0]) + (src[1] * src[1]) + (src[2] * src[2]) + (src[3] * src[3])); + if (len > 1.0e-6f) + { + const float invLen = 1.0f / len; + for (int i = 0; i < 4; ++i) + { + out[i] = src[i] * invLen; + } + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime); +} + +void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t lhsAddr = getRegU32(ctx, 5); + const uint32_t rhsAddr = getRegU32(ctx, 6); + float lhs[4]{}, rhs[4]{}, out[4]{}; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + { + out[0] = (lhs[1] * rhs[2]) - (lhs[2] * rhs[1]); + out[1] = (lhs[2] * rhs[0]) - (lhs[0] * rhs[2]); + out[2] = (lhs[0] * rhs[1]) - (lhs[1] * rhs[0]); + out[3] = 0.0f; + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime); +} + +void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); +} + +void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); +} + +void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); +} + +void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime); +} + +void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime); +} + +void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}, out[4]{}; + float scale = ctx ? ctx->f[12] : 0.0f; + if (scale == 0.0f) + { + uint32_t raw = getRegU32(ctx, 6); + std::memcpy(&scale, &raw, sizeof(scale)); + if (scale == 0.0f) + { + scale = static_cast(getRegU32(ctx, 6)); + } + } + + if (readVuVec4f(rdram, srcAddr, src)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = src[i] * scale; + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t lhsAddr = getRegU32(ctx, 5); + const uint32_t rhsAddr = getRegU32(ctx, 6); + float lhs[4]{}, rhs[4]{}, out[4]{}; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + { + for (int i = 0; i < 4; ++i) + { + out[i] = lhs[i] - rhs[i]; + } + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); +} + +void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime); +} + +void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); +} + +void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst + alignas(16) const float identity[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + + if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast(identity), sizeof(identity))) + { + static uint32_t warnCount = 0; + if (warnCount < 8) + { + std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x" + << std::hex << dstAddr << std::dec << std::endl; + ++warnCount; + } + } + + setReturnS32(ctx, 0); +} + +void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/VU.h b/ps2xRuntime/src/lib/Kernel/Stubs/VU.h new file mode 100644 index 00000000..42acefa6 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Stubs/VU.h @@ -0,0 +1,50 @@ +#pragma once + +#include "ps2_stubs.h" + +namespace ps2_stubs +{ + void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h new file mode 100644 index 00000000..5a9f392b --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h @@ -0,0 +1,36 @@ +#include "ps2_syscalls.h" +#include "ps2_runtime.h" +#include "runtime/ps2_iop_audio.h" +#include "ps2_runtime_macros.h" +#include "ps2_stubs.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif +#include + +std::string translatePs2Path(const char *ps2Path); + +#include "Helpers/Path.h" +#include "Helpers/State.h" +#include "Helpers/Loader.h" +#include "Helpers/Runtime.h" diff --git a/ps2xRuntime/src/lib/ps2_syscalls.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp similarity index 68% rename from ps2xRuntime/src/lib/ps2_syscalls.cpp rename to ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp index 43cdf8b8..c792e6f2 100644 --- a/ps2xRuntime/src/lib/ps2_syscalls.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp @@ -1,46 +1,9 @@ -#include "ps2_syscalls.h" -#include "ps2_runtime.h" -#include "ps2_iop_audio.h" -#include "ps2_runtime_macros.h" -#include "ps2_stubs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include // for unlink,rmdir,chdir -#include // for mkdir -#endif -#include - -std::string translatePs2Path(const char *ps2Path); - -#include "syscalls/helpers/ps2_syscalls_helpers_path.inl" -#include "syscalls/helpers/ps2_syscalls_helpers_state.inl" -#include "syscalls/helpers/ps2_syscalls_helpers_loader.inl" -#include "syscalls/helpers/ps2_syscalls_helpers_runtime.inl" +#include "Common.h" +#include "Dispatcher.h" +#include "System.h" namespace ps2_syscalls { -#include "syscalls/ps2_syscalls_interrupt.inl" -#include "syscalls/ps2_syscalls_system.inl" - void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { if (dispatchSyscallOverride(syscallNumber, rdram, ctx, runtime)) @@ -281,6 +244,12 @@ namespace ps2_syscalls case static_cast(-0x5F): DisableDmacHandler(rdram, ctx, runtime); return true; + case 0x61: + EnableCache(rdram, ctx, runtime); + return true; + case 0x62: + DisableCache(rdram, ctx, runtime); + return true; case 0x64: FlushCache(rdram, ctx, runtime); return true; @@ -324,117 +293,4 @@ namespace ps2_syscalls return false; } } - -#include "syscalls/ps2_syscalls_thread.inl" -#include "syscalls/ps2_syscalls_flags.inl" -#include "syscalls/ps2_syscalls_rpc.inl" -#include "syscalls/ps2_syscalls_fileio.inl" - - void notifyRuntimeStop() - { - stopInterruptWorker(); - { - std::lock_guard lock(g_irq_handler_mutex); - g_intcHandlers.clear(); - g_dmacHandlers.clear(); - g_nextIntcHandlerId = 1; - g_nextDmacHandlerId = 1; - g_enabled_intc_mask = 0xFFFFFFFFu; - g_enabled_dmac_mask = 0xFFFFFFFFu; - } - { - std::lock_guard lock(g_vsync_flag_mutex); - g_vsync_registration = {}; - g_vsync_tick_counter = 0u; - } - - std::vector> threads; - threads.reserve(32); - { - std::lock_guard lock(g_thread_map_mutex); - for (const auto &entry : g_threads) - { - if (entry.second) - { - threads.push_back(entry.second); - } - } - g_threads.clear(); - g_nextThreadId = 2; // Reserve id 1 for main thread. - } - g_currentThreadId = 1; - - for (const auto &threadInfo : threads) - { - { - std::lock_guard lock(threadInfo->m); - threadInfo->forceRelease = true; - threadInfo->terminated = true; - } - threadInfo->cv.notify_all(); - } - - std::vector> semas; - { - std::lock_guard lock(g_sema_map_mutex); - semas.reserve(g_semas.size()); - for (const auto &entry : g_semas) - { - if (entry.second) - { - semas.push_back(entry.second); - } - } - g_semas.clear(); - g_nextSemaId = 1; - } - for (const auto &sema : semas) - { - sema->cv.notify_all(); - } - - std::vector> eventFlags; - { - std::lock_guard lock(g_event_flag_map_mutex); - eventFlags.reserve(g_eventFlags.size()); - for (const auto &entry : g_eventFlags) - { - if (entry.second) - { - eventFlags.push_back(entry.second); - } - } - g_eventFlags.clear(); - g_nextEventFlagId = 1; - } - for (const auto &eventFlag : eventFlags) - { - eventFlag->cv.notify_all(); - } - - { - std::lock_guard lock(g_alarm_mutex); - g_alarms.clear(); - } - g_alarm_cv.notify_all(); - - { - std::lock_guard lock(g_exit_handler_mutex); - g_exit_handlers.clear(); - } - { - std::lock_guard lock(g_syscall_override_mutex); - g_syscall_overrides.clear(); - } - } - - void joinAllGuestHostThreads() - { - joinAllHostThreads(); - } - - void detachAllGuestHostThreads() - { - detachAllHostThreads(); - } } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.h new file mode 100644 index 00000000..06f2a519 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.h @@ -0,0 +1,8 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp similarity index 96% rename from ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp index f9e689a4..555921d0 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp @@ -1,3 +1,8 @@ +#include "Common.h" +#include "FileIO.h" + +namespace ps2_syscalls +{ static int allocatePs2Fd(FILE *file) { if (!file) @@ -90,7 +95,7 @@ void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) } const char *mode = translateFioMode(flags); - std::cout << "fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'" << std::endl; + RUNTIME_LOG("fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'"); FILE *fp = ::fopen(hostPath.c_str(), mode); if (!fp) @@ -363,7 +368,7 @@ void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) } else { - std::cout << "fioMkdir: Created directory '" << hostPath << "'" << std::endl; + RUNTIME_LOG("fioMkdir: Created directory '" << hostPath << "'"); setReturnS32(ctx, 0); // Success } } @@ -398,7 +403,7 @@ void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) } else { - std::cout << "fioChdir: Changed directory to '" << hostPath << "'" << std::endl; + RUNTIME_LOG("fioChdir: Changed directory to '" << hostPath << "'"); setReturnS32(ctx, 0); // Success } } @@ -432,7 +437,7 @@ void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) } else { - std::cout << "fioRmdir: Removed directory '" << hostPath << "'" << std::endl; + RUNTIME_LOG("fioRmdir: Removed directory '" << hostPath << "'"); setReturnS32(ctx, 0); // Success } } @@ -500,7 +505,8 @@ void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) } else { - std::cout << "fioRemove: Removed file '" << hostPath << "'" << std::endl; + RUNTIME_LOG("fioRemove: Removed file '" << hostPath << "'"); setReturnS32(ctx, 0); // Success } -} \ No newline at end of file +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.h b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.h new file mode 100644 index 00000000..b4bb7cbf --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h similarity index 97% rename from ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h index 0c50928d..d9da8eed 100644 --- a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h @@ -67,11 +67,11 @@ namespace return; } - std::cout << "[SIF module] " << op + RUNTIME_LOG("[SIF module] " << op << " id=" << moduleId << " ref=" << refCount << " path=\"" << path << "\"" - << std::endl; + << std::endl); ++g_sif_module_log_count; } @@ -259,8 +259,8 @@ namespace static uint32_t secFilterLogCount = 0; if (!loadAll && secFilterLogCount < 8u) { - std::cout << "[SifLoadElfPart] section filter \"" << sectionName - << "\" requested; loading PT_LOAD segments only." << std::endl; + RUNTIME_LOG("[SifLoadElfPart] section filter \"" << sectionName + << "\" requested; loading PT_LOAD segments only." << std::endl); ++secFilterLogCount; } @@ -412,8 +412,8 @@ namespace static uint32_t successLogs = 0; if (successLogs < 16u) { - std::cout << "[SifLoadElfPart] loaded \"" << ps2Path << "\" epc=0x" - << std::hex << execData.epc << " gp=0x" << execData.gp << std::dec << std::endl; + RUNTIME_LOG("[SifLoadElfPart] loaded \"" << ps2Path << "\" epc=0x" + << std::hex << execData.epc << " gp=0x" << execData.gp << std::dec << std::endl); ++successLogs; } diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_path.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Path.h similarity index 100% rename from ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_path.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Path.h diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h similarity index 99% rename from ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h index 25bf34bc..76cb5583 100644 --- a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h @@ -463,7 +463,7 @@ static int g_intc_tail_order = 1000; static int g_dmac_head_order = 0; static int g_dmac_tail_order = 1000; -std::string translatePs2Path(const char *ps2Path) +inline std::string translatePs2Path(const char *ps2Path) { if (!ps2Path || !*ps2Path) { @@ -718,3 +718,4 @@ static void ensureBootModeTable(uint8_t *rdram) g_bootmode_initialized = true; } + diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h similarity index 70% rename from ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h index 6cb51f43..fac2c0e8 100644 --- a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h @@ -1,5 +1,5 @@ -std::unordered_map g_fileDescriptors; -int g_nextFd = 3; // Start after stdin, stdout, stderr +inline std::unordered_map g_fileDescriptors; +inline int g_nextFd = 3; // Start after stdin, stdout, stderr struct ThreadInfo { @@ -197,26 +197,26 @@ static constexpr uint32_t kFioSoIROth = 0x0004; static constexpr uint32_t kFioSoIWOth = 0x0002; static constexpr uint32_t kFioSoIXOth = 0x0001; -static std::unordered_map> g_threads; -static int g_nextThreadId = 2; // Reserve 1 for the main thread -static thread_local int g_currentThreadId = 1; -static std::mutex g_thread_map_mutex; -static std::unordered_map g_hostThreads; -static std::mutex g_host_thread_mutex; - -static std::unordered_map> g_semas; -static int g_nextSemaId = 1; -static std::mutex g_sema_map_mutex; -static std::unordered_map> g_eventFlags; -static int g_nextEventFlagId = 1; -static std::mutex g_event_flag_map_mutex; -static std::unordered_map> g_alarms; -static int g_nextAlarmId = 1; -static std::mutex g_alarm_mutex; -static std::condition_variable g_alarm_cv; -static std::once_flag g_alarm_worker_once; -std::atomic g_activeThreads{0}; -static std::mutex g_fd_mutex; +inline std::unordered_map> g_threads; +inline int g_nextThreadId = 2; // Reserve 1 for the main thread +inline thread_local int g_currentThreadId = 1; +inline std::mutex g_thread_map_mutex; +inline std::unordered_map g_hostThreads; +inline std::mutex g_host_thread_mutex; + +inline std::unordered_map> g_semas; +inline int g_nextSemaId = 1; +inline std::mutex g_sema_map_mutex; +inline std::unordered_map> g_eventFlags; +inline int g_nextEventFlagId = 1; +inline std::mutex g_event_flag_map_mutex; +inline std::unordered_map> g_alarms; +inline int g_nextAlarmId = 1; +inline std::mutex g_alarm_mutex; +inline std::condition_variable g_alarm_cv; +inline std::once_flag g_alarm_worker_once; +inline std::atomic g_activeThreads{0}; +inline std::mutex g_fd_mutex; static void registerHostThread(int tid, std::thread worker) { @@ -340,23 +340,71 @@ struct RpcClientState uint32_t sid = 0; }; -static std::unordered_map g_rpc_servers; -static std::unordered_map g_rpc_clients; -static std::mutex g_rpc_mutex; -static std::recursive_mutex g_sif_call_rpc_mutex; -static bool g_rpc_initialized = false; -static uint32_t g_rpc_next_id = 1; -static uint32_t g_rpc_packet_index = 0; -static uint32_t g_rpc_server_index = 0; -static uint32_t g_rpc_active_queue = 0; -static constexpr uint32_t kDtxRpcSid = 0x7D000000u; -static constexpr uint32_t kDtxUrpcObjBase = 0x01F18000u; -static constexpr uint32_t kDtxUrpcObjLimit = 0x01F1FF00u; -static constexpr uint32_t kDtxUrpcFnTableBase = 0x0034FED0u; -static constexpr uint32_t kDtxUrpcObjTableBase = 0x0034FFD0u; -static std::mutex g_dtx_rpc_mutex; -static std::unordered_map g_dtx_remote_by_id; -static uint32_t g_dtx_next_urpc_obj = kDtxUrpcObjBase; +struct SoundDriverRpcState +{ + uintptr_t ownerRuntime = 0; + bool initialized = false; + uint32_t storageBaseAddr = 0; + uint32_t storageSize = 0; + uint32_t statusAddr = 0; + uint32_t addrTableAddr = 0; + uint32_t hdBaseAddr = 0; + uint32_t sqBaseAddr = 0; + uint32_t dataBaseAddr = 0; +}; + +inline std::unordered_map g_rpc_servers; +inline std::unordered_map g_rpc_clients; +inline std::mutex g_rpc_mutex; +inline std::recursive_mutex g_sif_call_rpc_mutex; +inline bool g_rpc_initialized = false; +inline uint32_t g_rpc_next_id = 1; +inline uint32_t g_rpc_packet_index = 0; +inline uint32_t g_rpc_server_index = 0; +inline uint32_t g_rpc_active_queue = 0; +inline SoundDriverRpcState g_soundDriverRpcState; +inline PS2SoundDriverCompatLayout g_soundDriverCompatLayout; +inline PS2DtxCompatLayout g_dtxCompatLayout; +inline std::mutex g_dtx_rpc_mutex; +inline std::unordered_map g_dtx_remote_by_id; +inline uint32_t g_dtx_next_urpc_obj = 0u; + +struct DtxTransferState +{ + uint32_t dtxId = 0; + uint32_t remoteHandle = 0; + uint32_t eeWorkAddr = 0; + uint32_t iopWorkAddr = 0; + uint32_t wkSize = 0; +}; + +inline std::unordered_map g_dtx_transfer_by_id; + +struct DtxSjxState +{ + uint32_t handle = 0; + uint32_t srcSjHandle = 0; + uint32_t dstSjHandle = 0; + uint32_t line = 0; + uint32_t eeObjAddr = 0; + uint16_t xid = 0; +}; + +inline std::unordered_map g_dtx_sjx_by_handle; + +struct DtxPs2RnaState +{ + uint32_t handle = 0; + uint32_t maxChannels = 0; + uint32_t sjHandle0 = 0; + uint32_t sjHandle1 = 0; + uint32_t channelCount = 0; + uint32_t sampleFreq = 0; + uint32_t volume = 0; + bool playEnabled = false; +}; + +inline std::unordered_map g_dtx_ps2rna_by_handle; struct DtxSjrmtState { @@ -374,7 +422,7 @@ struct DtxSjrmtState uint32_t uuid3 = 0; }; -static std::unordered_map g_dtx_sjrmt_by_handle; +inline std::unordered_map g_dtx_sjrmt_by_handle; static uint32_t dtxNormalizeSjrmtCapacity(uint32_t requestedBytes) { @@ -387,16 +435,27 @@ static uint32_t dtxNormalizeSjrmtCapacity(uint32_t requestedBytes) static uint32_t dtxAllocUrpcHandleLocked() { + const PS2DtxCompatLayout &layout = g_dtxCompatLayout; + if (!layout.hasUrpcObjectRange()) + { + return 0u; + } + + if (g_dtx_next_urpc_obj < layout.urpcObjBase || g_dtx_next_urpc_obj >= layout.urpcObjLimit) + { + g_dtx_next_urpc_obj = layout.urpcObjBase; + } + for (uint32_t i = 0; i < 4096u; ++i) { uint32_t candidate = g_dtx_next_urpc_obj; - g_dtx_next_urpc_obj += 0x20u; - if (g_dtx_next_urpc_obj < kDtxUrpcObjBase || g_dtx_next_urpc_obj >= kDtxUrpcObjLimit) + g_dtx_next_urpc_obj += layout.urpcObjStride; + if (g_dtx_next_urpc_obj < layout.urpcObjBase || g_dtx_next_urpc_obj >= layout.urpcObjLimit) { - g_dtx_next_urpc_obj = kDtxUrpcObjBase; + g_dtx_next_urpc_obj = layout.urpcObjBase; } - if (candidate < kDtxUrpcObjBase || candidate >= kDtxUrpcObjLimit) + if (candidate < layout.urpcObjBase || candidate >= layout.urpcObjLimit) { continue; } @@ -406,6 +465,16 @@ static uint32_t dtxAllocUrpcHandleLocked() continue; } + if (g_dtx_sjx_by_handle.find(candidate) != g_dtx_sjx_by_handle.end()) + { + continue; + } + + if (g_dtx_ps2rna_by_handle.find(candidate) != g_dtx_ps2rna_by_handle.end()) + { + continue; + } + bool inUseByDtxRemote = false; for (const auto &entry : g_dtx_remote_by_id) { @@ -422,7 +491,7 @@ static uint32_t dtxAllocUrpcHandleLocked() } } - return kDtxUrpcObjBase; + return layout.urpcObjBase; } struct ExitHandlerEntry @@ -431,37 +500,37 @@ struct ExitHandlerEntry uint32_t arg = 0; }; -static std::mutex g_exit_handler_mutex; -static std::unordered_map> g_exit_handlers; +inline std::mutex g_exit_handler_mutex; +inline std::unordered_map> g_exit_handlers; -static std::mutex g_bootmode_mutex; -static bool g_bootmode_initialized = false; -static uint32_t g_bootmode_pool_offset = 0; -static std::unordered_map g_bootmode_addresses; +inline std::mutex g_bootmode_mutex; +inline bool g_bootmode_initialized = false; +inline uint32_t g_bootmode_pool_offset = 0; +inline std::unordered_map g_bootmode_addresses; -static std::mutex g_syscall_override_mutex; -static std::unordered_map g_syscall_overrides; -static std::unordered_set g_syscall_mirror_addrs; +inline std::mutex g_syscall_override_mutex; +inline std::unordered_map g_syscall_overrides; +inline std::unordered_set g_syscall_mirror_addrs; static constexpr uint32_t kGuestSyscallTableGuestBase = 0x80011F80u; static constexpr uint32_t kGuestSyscallTablePhysBase = kGuestSyscallTableGuestBase & 0x1FFFFFFFu; static constexpr uint32_t kGuestSyscallMirrorLimit = 0x00080000u; static constexpr uint32_t kGuestSyscallTableProbeBase = 0x000002F0u; -static std::mutex g_tls_mutex; -static uint32_t g_tls_index = 0; +inline std::mutex g_tls_mutex; +inline uint32_t g_tls_index = 0; -static std::mutex g_osd_mutex; -static bool g_osd_config_initialized = false; -static uint32_t g_osd_config_raw = 0; +inline std::mutex g_osd_mutex; +inline bool g_osd_config_initialized = false; +inline uint32_t g_osd_config_raw = 0; -static std::mutex g_ps2_path_mutex; -static bool g_ps2_paths_initialized = false; -static std::filesystem::path g_host_base; -static std::filesystem::path g_cdrom_base; -static std::filesystem::path g_host_cwd; -static std::filesystem::path g_cdrom_cwd; -static std::string g_ps2_cwd_device = "host0"; +inline std::mutex g_ps2_path_mutex; +inline bool g_ps2_paths_initialized = false; +inline std::filesystem::path g_host_base; +inline std::filesystem::path g_cdrom_base; +inline std::filesystem::path g_host_cwd; +inline std::filesystem::path g_cdrom_cwd; +inline std::string g_ps2_cwd_device = "host0"; static constexpr uint32_t kRpcPacketSize = 64; static constexpr uint32_t kRpcPacketPoolBase = 0x01F00000; @@ -568,8 +637,9 @@ struct SifModuleRecord bool loaded = false; }; -static std::mutex g_sif_module_mutex; -static std::unordered_map g_sif_modules_by_id; -static std::unordered_map g_sif_module_id_by_path; -static int32_t g_next_sif_module_id = 1; -static uint32_t g_sif_module_log_count = 0; +inline std::mutex g_sif_module_mutex; +inline std::unordered_map g_sif_modules_by_id; +inline std::unordered_map g_sif_module_id_by_path; +inline int32_t g_next_sif_module_id = 1; +inline uint32_t g_sif_module_log_count = 0; + diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp similarity index 82% rename from ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp index cc3b7ea2..1b9021f2 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp @@ -1,29 +1,31 @@ -namespace +#include "Common.h" +#include "Interrupt.h" +#include "ps2_log.h" + +namespace ps2_syscalls +{ +namespace interrupt_state { constexpr uint32_t kIntcVblankStart = 2u; constexpr uint32_t kIntcVblankEnd = 3u; constexpr auto kVblankPeriod = std::chrono::microseconds(16667); constexpr int kMaxCatchupTicks = 4; - struct VSyncFlagRegistration - { - uint32_t flagAddr = 0; - uint32_t tickAddr = 0; - }; - - static std::mutex g_irq_handler_mutex; - static std::mutex g_irq_worker_mutex; - static std::condition_variable g_irq_worker_cv; - static std::mutex g_vsync_flag_mutex; - static std::condition_variable g_vsync_cv; - static std::atomic g_irq_worker_stop{false}; - static std::atomic g_irq_worker_running{false}; - static uint32_t g_enabled_intc_mask = 0xFFFFFFFFu; - static uint32_t g_enabled_dmac_mask = 0xFFFFFFFFu; - static uint64_t g_vsync_tick_counter = 0u; - static VSyncFlagRegistration g_vsync_registration{}; + std::mutex g_irq_handler_mutex; + std::mutex g_irq_worker_mutex; + std::condition_variable g_irq_worker_cv; + std::mutex g_vsync_flag_mutex; + std::condition_variable g_vsync_cv; + std::atomic g_irq_worker_stop{false}; + std::atomic g_irq_worker_running{false}; + uint32_t g_enabled_intc_mask = 0xFFFFFFFFu; + uint32_t g_enabled_dmac_mask = 0xFFFFFFFFu; + uint64_t g_vsync_tick_counter = 0u; + VSyncFlagRegistration g_vsync_registration{}; } +using namespace interrupt_state; + static void writeGuestU32NoThrow(uint8_t *rdram, uint32_t addr, uint32_t value) { if (addr == 0u) @@ -54,6 +56,24 @@ static void writeGuestU64NoThrow(uint8_t *rdram, uint32_t addr, uint64_t value) std::memcpy(dst, &value, sizeof(value)); } +static uint32_t readGuestU32NoThrow(uint8_t *rdram, uint32_t addr) +{ + if (addr == 0u) + { + return 0u; + } + + uint8_t *src = getMemPtr(rdram, addr); + if (!src) + { + return 0u; + } + + uint32_t value = 0u; + std::memcpy(&value, src, sizeof(value)); + return value; +} + static uint32_t getAsyncHandlerStackTop(PS2Runtime *runtime) { constexpr uint32_t kAsyncHandlerStackSize = 0x4000u; @@ -115,6 +135,23 @@ static void dispatchIntcHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, ui { if (!runtime->hasFunction(info.handler)) { + if (cause == kIntcVblankStart) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_missingHandlerLogCount{0u}; + const uint32_t logIndex = s_missingHandlerLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + auto flags = std::cout.flags(); + std::cout << "[INTC:missing] cause=" << cause + << " handler=0x" << std::hex << info.handler + << std::dec + << " id=" << info.id + << std::endl; + std::cout.flags(flags); + } + }); + } continue; } @@ -141,6 +178,7 @@ static void dispatchIntcHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, ui // spinning on interrupt-produced state, such as a vblank counter. step(rdram, &irqCtx, runtime); } + } catch (const ThreadExitException &) { @@ -396,6 +434,17 @@ void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) std::lock_guard lock(g_irq_handler_mutex); g_enabled_intc_mask |= (1u << cause); } + if (cause == kIntcVblankStart || cause == kIntcVblankEnd) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_enableLogCount{0u}; + const uint32_t logIndex = s_enableLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + RUNTIME_LOG("[EnableIntc] cause=" << cause); + } + }); + } setReturnS32(ctx, KE_OK); } @@ -412,6 +461,17 @@ void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) std::lock_guard lock(g_irq_handler_mutex); g_enabled_intc_mask &= ~(1u << cause); } + if (cause == kIntcVblankStart || cause == kIntcVblankEnd) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_disableLogCount{0u}; + const uint32_t logIndex = s_disableLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + RUNTIME_LOG("[DisableIntc] cause=" << cause); + } + }); + } setReturnS32(ctx, KE_OK); } @@ -440,6 +500,27 @@ void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) g_intcHandlers[handlerId] = info; } + if (info.cause == kIntcVblankStart) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_addHandlerLogCount{0u}; + const uint32_t logIndex = s_addHandlerLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + auto flags = std::cout.flags(); + std::cout << "[AddIntcHandler] cause=" << info.cause + << " handler=0x" << std::hex << info.handler + << " arg=0x" << info.arg + << " gp=0x" << info.gp + << " sp=0x" << info.sp + << std::dec + << " id=" << handlerId + << std::endl; + std::cout.flags(flags); + } + }); + } + ensureInterruptWorkerRunning(rdram, runtime); setReturnS32(ctx, handlerId); } @@ -591,3 +672,4 @@ void iDisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { DisableDmac(rdram, ctx, runtime); } +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.h new file mode 100644 index 00000000..eb9ba5f0 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + namespace interrupt_state + { + struct VSyncFlagRegistration + { + uint32_t flagAddr; + uint32_t tickAddr; + }; + + extern std::mutex g_irq_handler_mutex; + extern std::mutex g_irq_worker_mutex; + extern std::condition_variable g_irq_worker_cv; + extern std::mutex g_vsync_flag_mutex; + extern std::condition_variable g_vsync_cv; + extern std::atomic g_irq_worker_stop; + extern std::atomic g_irq_worker_running; + extern uint32_t g_enabled_intc_mask; + extern uint32_t g_enabled_dmac_mask; + extern uint64_t g_vsync_tick_counter; + extern VSyncFlagRegistration g_vsync_registration; + } + + void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause); + void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime); + uint64_t GetCurrentVSyncTick(); + void stopInterruptWorker(); + uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime); + void WaitVSyncTick(uint8_t *rdram, PS2Runtime *runtime); + void SetVSyncFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iEnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iDisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void AddIntcHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void AddDmacHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iEnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iDisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp new file mode 100644 index 00000000..d2a1a83f --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp @@ -0,0 +1,118 @@ +#include "Common.h" +#include "Interrupt.h" +#include "Lifecycle.h" + +namespace ps2_syscalls +{ + using namespace interrupt_state; + + void notifyRuntimeStop() + { + stopInterruptWorker(); + { + std::lock_guard lock(g_irq_handler_mutex); + g_intcHandlers.clear(); + g_dmacHandlers.clear(); + g_nextIntcHandlerId = 1; + g_nextDmacHandlerId = 1; + g_enabled_intc_mask = 0xFFFFFFFFu; + g_enabled_dmac_mask = 0xFFFFFFFFu; + } + { + std::lock_guard lock(g_vsync_flag_mutex); + g_vsync_registration = {}; + g_vsync_tick_counter = 0u; + } + + std::vector> threads; + threads.reserve(32); + { + std::lock_guard lock(g_thread_map_mutex); + for (const auto &entry : g_threads) + { + if (entry.second) + { + threads.push_back(entry.second); + } + } + g_threads.clear(); + g_nextThreadId = 2; // Reserve id 1 for main thread. + } + g_currentThreadId = 1; + + for (const auto &threadInfo : threads) + { + { + std::lock_guard lock(threadInfo->m); + threadInfo->forceRelease = true; + threadInfo->terminated = true; + } + threadInfo->cv.notify_all(); + } + + std::vector> semas; + { + std::lock_guard lock(g_sema_map_mutex); + semas.reserve(g_semas.size()); + for (const auto &entry : g_semas) + { + if (entry.second) + { + semas.push_back(entry.second); + } + } + g_semas.clear(); + g_nextSemaId = 1; + } + for (const auto &sema : semas) + { + sema->cv.notify_all(); + } + + std::vector> eventFlags; + { + std::lock_guard lock(g_event_flag_map_mutex); + eventFlags.reserve(g_eventFlags.size()); + for (const auto &entry : g_eventFlags) + { + if (entry.second) + { + eventFlags.push_back(entry.second); + } + } + g_eventFlags.clear(); + g_nextEventFlagId = 1; + } + for (const auto &eventFlag : eventFlags) + { + eventFlag->cv.notify_all(); + } + + { + std::lock_guard lock(g_alarm_mutex); + g_alarms.clear(); + } + g_alarm_cv.notify_all(); + + { + std::lock_guard lock(g_exit_handler_mutex); + g_exit_handlers.clear(); + } + { + std::lock_guard lock(g_syscall_override_mutex); + g_syscall_overrides.clear(); + } + } + + + void joinAllGuestHostThreads() + { + joinAllHostThreads(); + } + + + void detachAllGuestHostThreads() + { + detachAllHostThreads(); + } +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.h new file mode 100644 index 00000000..749ca1e2 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.h @@ -0,0 +1,10 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + void notifyRuntimeStop(); + void joinAllGuestHostThreads(); + void detachAllGuestHostThreads(); +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp new file mode 100644 index 00000000..b3c90557 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp @@ -0,0 +1,2488 @@ +#include "Common.h" +#include "RPC.h" + +namespace ps2_syscalls +{ +namespace +{ +constexpr uint32_t kSoundDriverStatusSize = 0x42u; +constexpr uint32_t kSoundDriverSeInfoOffset = 0x00u; +constexpr uint32_t kSoundDriverMidiInfoOffset = 0x0Cu; +constexpr uint32_t kSoundDriverMidiSumOffset = 0x1Eu; +constexpr uint32_t kSoundDriverSeSumOffset = 0x26u; +constexpr uint32_t kSoundDriverAddrTableEntries = 16u; +constexpr uint32_t kSoundDriverHdRegionSize = 0x4000u; +constexpr uint32_t kSoundDriverSqRegionSize = 0x18000u; +constexpr uint32_t kSoundDriverDataRegionSize = 0x40000u; +constexpr uint32_t kSoundDriverStatusAlignment = 0x100u; +constexpr uint32_t kSoundDriverAddrTableAlignment = 0x100u; +constexpr uint32_t kSoundDriverStorageAlignment = 0x1000u; +constexpr uint32_t kSoundDriverGuestPoolBase = 0x00120000u; +constexpr uint32_t kSoundDriverGuestPoolLimit = 0x00200000u; + +void resetDtxRpcStateUnlocked() +{ + g_dtx_remote_by_id.clear(); + g_dtx_transfer_by_id.clear(); + g_dtx_sjx_by_handle.clear(); + g_dtx_ps2rna_by_handle.clear(); + g_dtx_sjrmt_by_handle.clear(); + g_dtx_next_urpc_obj = g_dtxCompatLayout.urpcObjBase; +} + +bool readGuestU16(const uint8_t *rdram, uint32_t addr, uint16_t &out) +{ + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + out = 0u; + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; +} + +bool readGuestS16(const uint8_t *rdram, uint32_t addr, int16_t &out) +{ + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + out = 0; + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; +} + +bool writeGuestU16(uint8_t *rdram, uint32_t addr, uint16_t value) +{ + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, &value, sizeof(value)); + return true; +} + +bool writeGuestS16(uint8_t *rdram, uint32_t addr, int16_t value) +{ + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, &value, sizeof(value)); + return true; +} + +bool readGuestU32(const uint8_t *rdram, uint32_t addr, uint32_t &out) +{ + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + out = 0u; + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; +} + +bool writeGuestU32(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; +} + +bool normalizeGuestLinearAddr(uint32_t addr, uint32_t &out) +{ + uint32_t offset = 0u; + bool scratch = false; + if (!ps2ResolveGuestPointer(addr, offset, scratch) || scratch) + { + out = 0u; + return false; + } + out = offset; + return true; +} + +bool hasAnyNonZero(const uint8_t *ptr, size_t bytes) +{ + if (!ptr) + { + return false; + } + for (size_t i = 0; i < bytes; ++i) + { + if (ptr[i] != 0u) + { + return true; + } + } + return false; +} + +uint32_t alignUp(uint32_t value, uint32_t alignment) +{ + if (alignment == 0u) + { + return value; + } + return (value + (alignment - 1u)) & ~(alignment - 1u); +} + +void resetSoundDriverRpcStateUnlocked() +{ + const PS2SoundDriverCompatLayout compat = g_soundDriverCompatLayout; + g_soundDriverRpcState = {}; + g_soundDriverCompatLayout = compat; +} + +void adoptSoundDriverRuntimeUnlocked(PS2Runtime *runtime) +{ + const uintptr_t runtimeCookie = reinterpret_cast(runtime); + if (g_soundDriverRpcState.ownerRuntime != runtimeCookie) + { + resetSoundDriverRpcStateUnlocked(); + g_soundDriverRpcState.ownerRuntime = runtimeCookie; + } +} + +bool ensureSoundDriverMemoryUnlocked(uint8_t *rdram, PS2Runtime *runtime) +{ + if (!rdram || !runtime) + { + return false; + } + + adoptSoundDriverRuntimeUnlocked(runtime); + + if (g_soundDriverRpcState.statusAddr == 0u) + { + const uint32_t statusAddr = alignUp(kSoundDriverGuestPoolBase, kSoundDriverStatusAlignment); + const uint32_t addrTableAddr = + alignUp(statusAddr + kSoundDriverStatusSize, kSoundDriverAddrTableAlignment); + const uint32_t hdBaseAddr = + alignUp(addrTableAddr + (kSoundDriverAddrTableEntries * sizeof(uint32_t)), kSoundDriverStorageAlignment); + const uint32_t sqBaseAddr = alignUp(hdBaseAddr + kSoundDriverHdRegionSize, kSoundDriverStorageAlignment); + const uint32_t dataBaseAddr = alignUp(sqBaseAddr + kSoundDriverSqRegionSize, kSoundDriverStorageAlignment); + const uint32_t storageEnd = dataBaseAddr + kSoundDriverDataRegionSize; + if (storageEnd > kSoundDriverGuestPoolLimit) + { + return false; + } + + g_soundDriverRpcState.statusAddr = statusAddr; + g_soundDriverRpcState.addrTableAddr = addrTableAddr; + g_soundDriverRpcState.hdBaseAddr = hdBaseAddr; + g_soundDriverRpcState.sqBaseAddr = sqBaseAddr; + g_soundDriverRpcState.dataBaseAddr = dataBaseAddr; + g_soundDriverRpcState.storageBaseAddr = hdBaseAddr; + g_soundDriverRpcState.storageSize = + (dataBaseAddr + kSoundDriverDataRegionSize) - hdBaseAddr; + } + + if (g_soundDriverRpcState.statusAddr == 0u || + g_soundDriverRpcState.addrTableAddr == 0u || + g_soundDriverRpcState.storageBaseAddr == 0u) + { + return false; + } + + if (!g_soundDriverRpcState.initialized) + { + rpcZeroRdram(rdram, g_soundDriverRpcState.statusAddr, kSoundDriverStatusSize); + rpcZeroRdram(rdram, + g_soundDriverRpcState.addrTableAddr, + kSoundDriverAddrTableEntries * sizeof(uint32_t)); + rpcZeroRdram(rdram, g_soundDriverRpcState.storageBaseAddr, g_soundDriverRpcState.storageSize); + (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (0u * sizeof(uint32_t)), g_soundDriverRpcState.hdBaseAddr); + (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (1u * sizeof(uint32_t)), g_soundDriverRpcState.sqBaseAddr); + (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (2u * sizeof(uint32_t)), g_soundDriverRpcState.dataBaseAddr); + g_soundDriverRpcState.initialized = true; + } + + return true; +} + +int16_t soundDriverCheckValue(const uint8_t *rdram, uint32_t primaryBase, uint32_t fallbackBase, uint32_t index, uint32_t count) +{ + if (index >= count) + { + return 0; + } + + int16_t value = 0; + if (readGuestS16(rdram, primaryBase + (index * sizeof(int16_t)), value) && value != 0) + { + return value; + } + + (void)readGuestS16(rdram, fallbackBase + (index * sizeof(int16_t)), value); + return value; +} + +bool selectSoundDriverCompatChecks(const uint8_t *rdram, uint32_t &seBase, uint32_t &midiBase) +{ + const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; + if (!compat.hasChecksumTables()) + { + return false; + } + + seBase = compat.primarySeCheckAddr; + midiBase = compat.primaryMidiCheckAddr; + + const uint8_t *selectedSe = getConstMemPtr(rdram, seBase); + const uint8_t *selectedMidi = getConstMemPtr(rdram, midiBase); + const bool primaryLooksLive = + hasAnyNonZero(selectedSe, 5u * sizeof(int16_t)) || + hasAnyNonZero(selectedMidi, 4u * sizeof(int16_t)); + + if ((!selectedSe || !selectedMidi) || !primaryLooksLive) + { + const uint8_t *fallbackSe = getConstMemPtr(rdram, compat.fallbackSeCheckAddr); + const uint8_t *fallbackMidi = getConstMemPtr(rdram, compat.fallbackMidiCheckAddr); + const bool fallbackLooksLive = + hasAnyNonZero(fallbackSe, 5u * sizeof(int16_t)) || + hasAnyNonZero(fallbackMidi, 4u * sizeof(int16_t)); + if (fallbackLooksLive) + { + seBase = compat.fallbackSeCheckAddr; + midiBase = compat.fallbackMidiCheckAddr; + return true; + } + } + + return selectedSe != nullptr && selectedMidi != nullptr; +} + +void backfillSoundDriverStatusFromCompatUnlocked(uint8_t *rdram) +{ + if (!rdram || g_soundDriverRpcState.statusAddr == 0u || !g_soundDriverCompatLayout.hasChecksumTables()) + { + return; + } + + uint32_t seBase = 0u; + uint32_t midiBase = 0u; + if (!selectSoundDriverCompatChecks(rdram, seBase, midiBase)) + { + return; + } + + const uint8_t *selectedSe = getConstMemPtr(rdram, seBase); + const uint8_t *selectedMidi = getConstMemPtr(rdram, midiBase); + uint8_t *status = getMemPtr(rdram, g_soundDriverRpcState.statusAddr); + if (!selectedSe || !selectedMidi || !status) + { + return; + } + + auto backfillZeroS16Slots = [&](uint32_t statusOffset, const uint8_t *compatBase, uint32_t slotCount) + { + for (uint32_t slot = 0u; slot < slotCount; ++slot) + { + int16_t liveValue = 0; + if (!readGuestS16(rdram, + g_soundDriverRpcState.statusAddr + statusOffset + (slot * sizeof(int16_t)), + liveValue)) + { + continue; + } + + if (liveValue != 0) + { + continue; + } + + int16_t compatValue = 0; + std::memcpy(&compatValue, compatBase + (slot * sizeof(int16_t)), sizeof(compatValue)); + if (compatValue == 0) + { + continue; + } + + (void)writeGuestS16(rdram, + g_soundDriverRpcState.statusAddr + statusOffset + (slot * sizeof(int16_t)), + compatValue); + } + }; + + backfillZeroS16Slots(kSoundDriverSeSumOffset, selectedSe, 5u); + backfillZeroS16Slots(kSoundDriverMidiSumOffset, selectedMidi, 4u); +} + +size_t soundDriverCommandLength(uint8_t command) +{ + const uint8_t hi = static_cast(command & 0xF0u); + switch (hi) + { + case 0x00u: + { + size_t len = 4u; + if ((command & 0x01u) != 0u) + { + ++len; + } + if ((command & 0x02u) != 0u) + { + ++len; + } + if ((command & 0x04u) != 0u) + { + len += 2u; + } + return len; + } + case 0x10u: + return (command == 0x11u) ? 3u : 1u; + case 0x20u: + if (command == 0x22u || command == 0x23u || command == 0x24u || command == 0x25u) + { + return 3u; + } + if (command == 0x26u) + { + return 4u; + } + if (command == 0x20u) + { + return 5u; + } + if (command == 0x27u || command == 0x28u || command == 0x29u || command == 0x2Cu || command == 0x2Du) + { + return 8u; + } + return 2u; + case 0x40u: + if (command == 0x47u || command == 0x48u || command == 0x49u || command == 0x4Au || + command == 0x41u || command == 0x42u) + { + return 2u; + } + if (command == 0x4Bu) + { + return 3u; + } + if (command == 0x45u || command == 0x4Cu) + { + return 4u; + } + if (command == 0x44u) + { + return 6u; + } + if (command == 0x4Du || command == 0x4Eu) + { + return 3u; + } + if (command == 0x4Fu) + { + return 6u; + } + return 1u; + case 0x50u: + case 0x60u: + if (command == 0x51u || command == 0x52u || command == 0x53u || command == 0x54u) + { + return 8u; + } + return 2u; + default: + return 0u; + } +} + +void applySoundDriverCommandUnlocked(uint8_t *rdram, const uint8_t *cmd) +{ + if (!rdram || !cmd || g_soundDriverRpcState.statusAddr == 0u) + { + return; + } + + const uint8_t op = cmd[0]; + switch (op) + { + case 0x20u: // SdrBgmReq + { + const uint32_t port = cmd[1] & 0x0Fu; + uint16_t midiInfo = 0u; + (void)readGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + midiInfo = static_cast(midiInfo | static_cast(1u << port)); + (void)writeGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + break; + } + case 0x21u: // SdrBgmStop + { + const uint32_t port = cmd[1] & 0x0Fu; + uint16_t midiInfo = 0u; + (void)readGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + midiInfo = static_cast(midiInfo & ~static_cast(1u << port)); + (void)writeGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + break; + } + case 0x28u: // SdrHDDataSet + { + const uint32_t port = cmd[1] & 0x0Fu; + const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; + if (compat.primaryMidiCheckAddr != 0u || compat.fallbackMidiCheckAddr != 0u) + { + const int16_t checksum = + soundDriverCheckValue(rdram, compat.primaryMidiCheckAddr, compat.fallbackMidiCheckAddr, port, 4u); + (void)writeGuestS16(rdram, + g_soundDriverRpcState.statusAddr + kSoundDriverMidiSumOffset + (port * sizeof(int16_t)), + checksum); + } + break; + } + case 0x29u: // SdrHDDataSet2 + { + const uint32_t port = cmd[1] & 0x0Fu; + const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; + if (compat.primarySeCheckAddr != 0u || compat.fallbackSeCheckAddr != 0u) + { + const int16_t checksum = + soundDriverCheckValue(rdram, compat.primarySeCheckAddr, compat.fallbackSeCheckAddr, port, 5u); + (void)writeGuestS16(rdram, + g_soundDriverRpcState.statusAddr + kSoundDriverSeSumOffset + (port * sizeof(int16_t)), + checksum); + } + break; + } + case 0x10u: // SdrSeAllStop + rpcZeroRdram(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverSeInfoOffset, 6u * sizeof(uint16_t)); + break; + default: + break; + } +} + +void handleSoundDriverCommandBuffer(uint8_t *rdram, PS2Runtime *runtime, uint32_t sendBuf, uint32_t sendSize) +{ + if (!rdram || !runtime || !sendBuf || sendSize == 0u) + { + return; + } + + std::lock_guard lock(g_rpc_mutex); + if (!ensureSoundDriverMemoryUnlocked(rdram, runtime)) + { + return; + } + + const uint8_t *packet = getConstMemPtr(rdram, sendBuf); + if (!packet) + { + return; + } + + for (uint32_t offset = 0u; offset < sendSize;) + { + const uint8_t op = packet[offset]; + if (op == 0xFFu) + { + break; + } + + const size_t len = soundDriverCommandLength(op); + if (len == 0u || (offset + len) > sendSize) + { + break; + } + + applySoundDriverCommandUnlocked(rdram, packet + offset); + offset += static_cast(len); + } +} + +bool handleSoundDriverRpcServiceImpl(uint8_t *rdram, PS2Runtime *runtime, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBuf, uint32_t sendSize, + uint32_t recvBuf, uint32_t recvSize, + uint32_t &resultPtr, + bool &signalNowaitCompletion) +{ + resultPtr = 0u; + signalNowaitCompletion = false; + + if (!runtime || !rdram) + { + return false; + } + + if (sid == IOP_SID_SNDDRV_COMMAND && rpcNum == IOP_RPC_SNDDRV_SUBMIT) + { + handleSoundDriverCommandBuffer(rdram, runtime, sendBuf, sendSize); + return true; + } + + if (sid == IOP_SID_SNDDRV_STATE && + (rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR || rpcNum == IOP_RPC_SNDDRV_GET_ADDR_TABLE)) + { + uint32_t responseWord = 0u; + { + std::lock_guard lock(g_rpc_mutex); + if (!ensureSoundDriverMemoryUnlocked(rdram, runtime)) + { + return false; + } + responseWord = + (rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR) ? g_soundDriverRpcState.statusAddr + : g_soundDriverRpcState.addrTableAddr; + } + + if (recvBuf && recvSize >= sizeof(uint32_t)) + { + (void)writeGuestU32(rdram, recvBuf, responseWord); + if (recvSize > sizeof(uint32_t)) + { + rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + } + resultPtr = recvBuf; + } + + signalNowaitCompletion = true; + return true; + } + + return false; +} + +} // namespace + +bool handleSoundDriverRpcService(uint8_t *rdram, PS2Runtime *runtime, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBuf, uint32_t sendSize, + uint32_t recvBuf, uint32_t recvSize, + uint32_t &resultPtr, + bool &signalNowaitCompletion) +{ + return handleSoundDriverRpcServiceImpl(rdram, runtime, + sid, rpcNum, + sendBuf, sendSize, + recvBuf, recvSize, + resultPtr, + signalNowaitCompletion); +} + +namespace +{ + +bool signalRpcCompletionSema(uint32_t semaId) +{ + if (semaId == 0u || semaId > 0xFFFFu) + { + return false; + } + + auto sema = lookupSemaInfo(static_cast(semaId)); + if (!sema) + { + return false; + } + + bool signaled = false; + { + std::lock_guard lock(sema->m); + if (!sema->deleted && sema->count < sema->maxCount) + { + sema->count++; + signaled = true; + } + } + + if (signaled) + { + sema->cv.notify_one(); + } + return signaled; +} + +const char *dtxUrpcCommandName(uint32_t command) +{ + switch (command) + { + case 0u: return "SJX_CREATE"; + case 1u: return "SJX_DESTROY"; + case 2u: return "SJX_RESET"; + case 3u: return "SJX_REINIT"; + case 8u: return "PS2RNA_CREATE"; + case 9u: return "PS2RNA_DESTROY"; + case 10u: return "PS2RNA_REINIT"; + case 32u: return "SJRMT_RBF_CREATE"; + case 33u: return "SJRMT_MEM_CREATE"; + case 34u: return "SJRMT_UNI_CREATE"; + case 35u: return "SJRMT_DESTROY"; + case 36u: return "SJRMT_GET_UUID"; + case 37u: return "SJRMT_RESET"; + case 38u: return "SJRMT_GET_CHUNK"; + case 39u: return "SJRMT_UNGET_CHUNK"; + case 40u: return "SJRMT_PUT_CHUNK"; + case 41u: return "SJRMT_GET_NUM_DATA"; + case 42u: return "SJRMT_IS_GET_CHUNK"; + case 43u: return "SJRMT_INIT"; + case 44u: return "SJRMT_FINISH"; + default: return "UNKNOWN"; + } +} + +const char *dtxStreamName(uint32_t streamId) +{ + switch (streamId) + { + case 0u: return "room"; + case 1u: return "data"; + default: return "other"; + } +} + +bool dtxMatchesTransfer(const DtxTransferState &state, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes) +{ + (void)dstAddr; + return state.eeWorkAddr != 0u && + state.wkSize >= 64u && + state.eeWorkAddr == srcAddr && + state.wkSize == sizeBytes; +} + +bool dtxCopyBytes(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t len) +{ + for (uint32_t i = 0; i < len; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + uint8_t *dst = getMemPtr(rdram, dstAddr + i); + if (!src || !dst) + { + return false; + } + *dst = *src; + } + return true; +} + +void dtxAppendToSjrmtData(uint8_t *rdram, DtxSjrmtState &state, uint32_t srcDataAddr, uint32_t requestedLen) +{ + if (!rdram) + { + return; + } + + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (cap == 0u || state.wkAddr == 0u || state.roomBytes == 0u || requestedLen == 0u) + { + return; + } + + const uint32_t requestedCopyLen = std::min(requestedLen, state.roomBytes); + uint32_t copiedLen = 0u; + for (uint32_t i = 0; i < requestedCopyLen; ++i) + { + const uint32_t writeOffset = (state.writePos + i) % cap; + if (!dtxCopyBytes(rdram, state.wkAddr + writeOffset, srcDataAddr + i, 1u)) + { + break; + } + ++copiedLen; + } + + state.writePos = (state.writePos + copiedLen) % cap; + state.roomBytes -= copiedLen; + state.dataBytes = std::min(cap, state.dataBytes + copiedLen); +} + +void dtxConsumeSjrmtDataLocked(DtxSjrmtState &state) +{ + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (cap == 0u || state.dataBytes == 0u) + { + return; + } + + const uint32_t consumed = std::min(cap, state.dataBytes); + state.readPos = (state.readPos + consumed) % cap; + state.dataBytes -= consumed; + state.roomBytes = std::min(cap, state.roomBytes + consumed); +} + +void dtxConsumeActivePs2RnaStreamsLocked() +{ + for (const auto &entry : g_dtx_ps2rna_by_handle) + { + const DtxPs2RnaState &rna = entry.second; + if (!rna.playEnabled) + { + continue; + } + + auto consumeHandle = [&](uint32_t sjHandle) + { + if (sjHandle == 0u) + { + return; + } + + auto it = g_dtx_sjrmt_by_handle.find(sjHandle); + if (it == g_dtx_sjrmt_by_handle.end()) + { + return; + } + + dtxConsumeSjrmtDataLocked(it->second); + }; + + consumeHandle(rna.sjHandle0); + consumeHandle(rna.sjHandle1); + } +} + +void dtxApplySjxPayload(uint8_t *rdram, const DtxTransferState &transfer) +{ + if (!rdram || transfer.eeWorkAddr == 0u || transfer.wkSize < 64u) + { + return; + } + + uint32_t commandCount = 0u; + if (!readGuestU32(rdram, transfer.eeWorkAddr, commandCount)) + { + return; + } + + commandCount = std::min(commandCount, 128u); + if (commandCount == 0u) + { + return; + } + + constexpr uint32_t kSjxHeaderSize = 16u; + constexpr uint32_t kSjxCommandSize = 16u; + + std::lock_guard lock(g_dtx_rpc_mutex); + for (uint32_t i = 0; i < commandCount; ++i) + { + const uint32_t cmdAddr = transfer.eeWorkAddr + kSjxHeaderSize + (i * kSjxCommandSize); + uint8_t *cmdPtr = getMemPtr(rdram, cmdAddr); + if (!cmdPtr) + { + break; + } + + const uint8_t cmdNo = cmdPtr[0]; + const uint8_t cmdLine = cmdPtr[1]; + uint16_t cmdXid = 0u; + uint32_t sjxHandle = 0u; + uint32_t chunkDataAddr = 0u; + uint32_t chunkLen = 0u; + std::memcpy(&cmdXid, cmdPtr + 2u, sizeof(cmdXid)); + std::memcpy(&sjxHandle, cmdPtr + 4u, sizeof(sjxHandle)); + std::memcpy(&chunkDataAddr, cmdPtr + 8u, sizeof(chunkDataAddr)); + std::memcpy(&chunkLen, cmdPtr + 12u, sizeof(chunkLen)); + + if (cmdNo != 0u || chunkLen == 0u) + { + continue; + } + + auto sjxIt = g_dtx_sjx_by_handle.find(sjxHandle); + if (sjxIt == g_dtx_sjx_by_handle.end()) + { + continue; + } + + DtxSjxState &sjx = sjxIt->second; + if (sjx.xid != 0u && sjx.xid != cmdXid) + { + continue; + } + + auto sjrmtIt = g_dtx_sjrmt_by_handle.find(sjx.dstSjHandle); + if (sjrmtIt == g_dtx_sjrmt_by_handle.end()) + { + continue; + } + + if (cmdLine == sjx.line) + { + dtxAppendToSjrmtData(rdram, sjrmtIt->second, chunkDataAddr, chunkLen); + + // The IOP side consumes data from the remote SJ stream and returns the + // chunk to the EE-side room queue. Rewriting the ack packet to line 0 + // makes the EE sjx_rcvcbf recycle the chunk instead of requeueing it + // as playable data forever. + cmdPtr[1] = 0u; + } + } + + dtxConsumeActivePs2RnaStreamsLocked(); +} + +void dtxApplyPs2RnaPayload(uint8_t *rdram, const DtxTransferState &transfer) +{ + if (!rdram || transfer.eeWorkAddr == 0u || transfer.wkSize < 64u) + { + return; + } + + uint32_t commandCount = 0u; + if (!readGuestU32(rdram, transfer.eeWorkAddr, commandCount)) + { + return; + } + + commandCount = std::min(commandCount, 128u); + if (commandCount == 0u) + { + return; + } + + constexpr uint32_t kPs2RnaHeaderSize = 16u; + constexpr uint32_t kPs2RnaCommandSize = 16u; + + std::lock_guard lock(g_dtx_rpc_mutex); + for (uint32_t i = 0; i < commandCount; ++i) + { + const uint32_t cmdAddr = transfer.eeWorkAddr + kPs2RnaHeaderSize + (i * kPs2RnaCommandSize); + const uint8_t *cmdPtr = getConstMemPtr(rdram, cmdAddr); + if (!cmdPtr) + { + break; + } + + uint16_t cmdNo = 0u; + uint32_t rnaHandle = 0u; + uint32_t arg1 = 0u; + uint32_t arg2 = 0u; + std::memcpy(&cmdNo, cmdPtr + 0u, sizeof(cmdNo)); + std::memcpy(&rnaHandle, cmdPtr + 4u, sizeof(rnaHandle)); + std::memcpy(&arg1, cmdPtr + 8u, sizeof(arg1)); + std::memcpy(&arg2, cmdPtr + 12u, sizeof(arg2)); + + auto it = g_dtx_ps2rna_by_handle.find(rnaHandle); + if (it == g_dtx_ps2rna_by_handle.end()) + { + continue; + } + + DtxPs2RnaState &state = it->second; + switch (cmdNo) + { + case 0u: // IOPRNA_CMD_START + state.playEnabled = true; + break; + case 1u: // IOPRNA_CMD_STOP + state.playEnabled = false; + break; + case 2u: // IOPRNA_CMD_SETPSW + state.playEnabled = (arg1 != 0u); + break; + case 3u: // IOPRNA_CMD_SETNCH + state.channelCount = arg1; + break; + case 4u: // IOPRNA_CMD_SETSFREQ + state.sampleFreq = arg1; + break; + case 5u: // IOPRNA_CMD_SETVOL + state.volume = arg2; + break; + default: + break; + } + } + + dtxConsumeActivePs2RnaStreamsLocked(); +} + +} // namespace + +void noteDtxSifDmaTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes) +{ + if (!rdram || sizeBytes < 64u) + { + return; + } + + uint32_t normalizedSrc = 0u; + uint32_t normalizedDst = 0u; + if (!normalizeGuestLinearAddr(srcAddr, normalizedSrc) || + !normalizeGuestLinearAddr(dstAddr, normalizedDst)) + { + return; + } + + DtxTransferState matched{}; + bool found = false; + { + std::lock_guard lock(g_dtx_rpc_mutex); + if (!g_dtxCompatLayout.isConfigured()) + { + return; + } + for (const auto &entry : g_dtx_transfer_by_id) + { + if (dtxMatchesTransfer(entry.second, normalizedSrc, normalizedDst, sizeBytes)) + { + matched = entry.second; + found = true; + break; + } + } + } + + if (!found) + { + static uint32_t dtxMissLogCount = 0u; + if (dtxMissLogCount < 64u) + { + uint32_t knownTransfers = 0u; + uint32_t sampleDtxId = 0u; + uint32_t sampleSrc = 0u; + uint32_t sampleSize = 0u; + { + std::lock_guard lock(g_dtx_rpc_mutex); + knownTransfers = static_cast(g_dtx_transfer_by_id.size()); + if (!g_dtx_transfer_by_id.empty()) + { + const auto &sample = *g_dtx_transfer_by_id.begin(); + sampleDtxId = sample.second.dtxId; + sampleSrc = sample.second.eeWorkAddr; + sampleSize = sample.second.wkSize; + } + } + + if (knownTransfers != 0u) + { + RUNTIME_LOG("[sceSifSetDma:DTX_MISS] src=0x" << std::hex << normalizedSrc + << " dst=0x" << normalizedDst + << " size=0x" << sizeBytes + << " known=" << std::dec << knownTransfers + << " sampleDtxId=0x" << std::hex << sampleDtxId + << " sampleSrc=0x" << sampleSrc + << " sampleSize=0x" << sampleSize + << std::dec << std::endl); + ++dtxMissLogCount; + } + } + return; + } + + const uint32_t footerTicketAddr = matched.eeWorkAddr + matched.wkSize - sizeof(uint32_t); + uint32_t ticketNo = 0u; + if (!readGuestU32(rdram, footerTicketAddr, ticketNo)) + { + return; + } + + (void)writeGuestU32(rdram, footerTicketAddr, ticketNo + 1u); + + if (matched.dtxId == 0u) + { + dtxApplySjxPayload(rdram, matched); + } + else if (matched.dtxId == 1u) + { + dtxApplyPs2RnaPayload(rdram, matched); + } + + static uint32_t dtxAckLogCount = 0u; + if (dtxAckLogCount < 32u) + { + RUNTIME_LOG("[sceSifSetDma:DTX_ACK] dtxId=0x" << std::hex << matched.dtxId + << " ee=0x" << matched.eeWorkAddr + << " iop=0x" << matched.iopWorkAddr + << " size=0x" << matched.wkSize + << " ticket=0x" << ticketNo + << "->0x" << (ticketNo + 1u)); + if (matched.dtxId == 0u) + { + uint32_t totalData = 0u; + std::lock_guard lock(g_dtx_rpc_mutex); + for (const auto &entry : g_dtx_sjrmt_by_handle) + { + totalData += entry.second.dataBytes; + } + RUNTIME_LOG(" sjrmtData=0x" << totalData); + } + RUNTIME_LOG(std::dec << std::endl); + ++dtxAckLogCount; + } +} + +void resetSoundDriverRpcState() +{ + std::lock_guard lock(g_rpc_mutex); + resetSoundDriverRpcStateUnlocked(); +} + +void setSoundDriverCompatLayout(const PS2SoundDriverCompatLayout &layout) +{ + std::lock_guard lock(g_rpc_mutex); + g_soundDriverCompatLayout = layout; +} + +void clearSoundDriverCompatLayout() +{ + std::lock_guard lock(g_rpc_mutex); + g_soundDriverCompatLayout = {}; +} + +void setDtxCompatLayout(const PS2DtxCompatLayout &layout) +{ + std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); + g_dtxCompatLayout = layout; + resetDtxRpcStateUnlocked(); +} + +void clearDtxCompatLayout() +{ + std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); + g_dtxCompatLayout = {}; + resetDtxRpcStateUnlocked(); +} + +void prepareSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t size) +{ + std::lock_guard lock(g_rpc_mutex); + if (srcAddr != g_soundDriverRpcState.statusAddr || size != kSoundDriverStatusSize) + { + return; + } + + backfillSoundDriverStatusFromCompatUnlocked(rdram); +} + +void finalizeSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t size) +{ + (void)rdram; + (void)srcAddr; + (void)dstAddr; + (void)size; +} + +void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t moduleId = static_cast(getRegU32(ctx, 4)); // $a0 + const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional) + + uint32_t refsLeft = 0; + const bool knownModule = trackSifModuleStop(moduleId, &refsLeft); + const int32_t ret = knownModule ? 0 : -1; + + if (resultAddr != 0) + { + int32_t *hostResult = reinterpret_cast(getMemPtr(rdram, resultAddr)); + if (hostResult) + { + *hostResult = knownModule ? 0 : -1; + } + } + + if (knownModule) + { + std::string modulePath; + { + std::lock_guard lock(g_sif_module_mutex); + auto it = g_sif_modules_by_id.find(moduleId); + if (it != g_sif_modules_by_id.end()) + { + modulePath = it->second.path; + } + } + logSifModuleAction("stop", moduleId, modulePath, refsLeft); + } + + setReturnS32(ctx, ret); +} + +void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes); + if (modulePath.empty()) + { + setReturnS32(ctx, -1); + return; + } + + const int32_t moduleId = trackSifModuleLoad(modulePath); + if (moduleId <= 0) + { + setReturnS32(ctx, -1); + return; + } + + uint32_t refs = 0; + { + std::lock_guard lock(g_sif_module_mutex); + auto it = g_sif_modules_by_id.find(moduleId); + if (it != g_sif_modules_by_id.end()) + { + refs = it->second.refCount; + } + } + logSifModuleAction("load", moduleId, modulePath, refs); + + setReturnS32(ctx, moduleId); +} + +void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); + if (runtime) + { + runtime->iop().init(rdram); + } + if (!g_rpc_initialized) + { + g_rpc_servers.clear(); + g_rpc_clients.clear(); + g_rpc_next_id = 1; + g_rpc_packet_index = 0; + g_rpc_server_index = 0; + g_rpc_active_queue = 0; + resetDtxRpcStateUnlocked(); + g_soundDriverRpcState.initialized = false; + g_rpc_initialized = true; + RUNTIME_LOG("[SifInitRpc] Initialized"); + } + else + { + resetDtxRpcStateUnlocked(); + g_soundDriverRpcState.initialized = false; + } + setReturnS32(ctx, 0); +} + +void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clientPtr = getRegU32(ctx, 4); + uint32_t rpcId = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + + t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + + if (!client) + { + setReturnS32(ctx, -1); + return; + } + + client->command = 0; + client->buf = 0; + client->cbuf = 0; + client->end_function = 0; + client->end_param = 0; + client->server = 0; + client->hdr.pkt_addr = 0; + client->hdr.sema_id = -1; + client->hdr.mode = mode; + + uint32_t serverPtr = 0; + { + std::lock_guard lock(g_rpc_mutex); + client->hdr.rpc_id = g_rpc_next_id++; + auto it = g_rpc_servers.find(rpcId); + if (it != g_rpc_servers.end()) + { + serverPtr = it->second.sd_ptr; + } + g_rpc_clients[clientPtr] = {}; + g_rpc_clients[clientPtr].sid = rpcId; + } + + if (!serverPtr) + { + // Allocate a dummy server so bind loops can proceed. + serverPtr = rpcAllocServerAddr(rdram); + if (serverPtr) + { + t_SifRpcServerData *dummy = reinterpret_cast(getMemPtr(rdram, serverPtr)); + if (dummy) + { + std::memset(dummy, 0, sizeof(*dummy)); + dummy->sid = static_cast(rpcId); + } + std::lock_guard lock(g_rpc_mutex); + g_rpc_servers[rpcId] = {rpcId, serverPtr}; + } + } + + if (serverPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, serverPtr)); + client->server = serverPtr; + client->buf = sd ? sd->buf : 0; + client->cbuf = sd ? sd->cbuf : 0; + } + else + { + client->server = 0; + client->buf = 0; + client->cbuf = 0; + } + + setReturnS32(ctx, 0); +} + +void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::lock_guard rpcCallLock(g_sif_call_rpc_mutex); + + uint32_t clientPtr = getRegU32(ctx, 4); + uint32_t rpcNum = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t sendBuf = getRegU32(ctx, 7); + uint32_t sendSize = 0; + uint32_t recvBuf = 0; + uint32_t recvSize = 0; + uint32_t endFunc = 0; + uint32_t endParam = 0; + + // Decode both extended-reg convention (EE default) and standard O32 stack convention, + // picking REG whenever plausible, to avoid zero-collision on the stack. + uint32_t sp = getRegU32(ctx, 29); + + uint32_t sendSizeReg = getRegU32(ctx, 8); + uint32_t recvBufReg = getRegU32(ctx, 9); + uint32_t recvSizeReg = getRegU32(ctx, 10); + uint32_t endFuncReg = getRegU32(ctx, 11); + uint32_t endParamReg = 0; + (void)readStackU32(rdram, sp, 0x0, endParamReg); + + uint32_t sendSizeStk = 0; + uint32_t recvBufStk = 0; + uint32_t recvSizeStk = 0; + uint32_t endFuncStk = 0; + uint32_t endParamStk = 0; + (void)readStackU32(rdram, sp, 0x10, sendSizeStk); + (void)readStackU32(rdram, sp, 0x14, recvBufStk); + (void)readStackU32(rdram, sp, 0x18, recvSizeStk); + (void)readStackU32(rdram, sp, 0x1C, endFuncStk); + (void)readStackU32(rdram, sp, 0x20, endParamStk); + + auto looksLikeGuestPtr = [&](uint32_t v) -> bool + { + if (v == 0) + return true; + const uint32_t norm = v & 0x1FFFFFFFu; + return norm >= 0x10000u && norm < PS2_RAM_SIZE; + }; + + auto looksLikeSize = [&](uint32_t v) -> bool + { + return v <= 0x2000000u; + }; + + auto looksLikeFunc = [&](uint32_t v) -> bool + { + return v == 0 || looksLikeGuestPtr(v); + }; + + auto plausiblePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz, uint32_t endFn) -> bool + { + return looksLikeSize(sendSz) && looksLikeGuestPtr(rbuf) && looksLikeSize(rsz) && looksLikeFunc(endFn); + }; + + const bool regPackPlausible = plausiblePack(sendSizeReg, recvBufReg, recvSizeReg, endFuncReg); + const bool stackPackPlausible = plausiblePack(sendSizeStk, recvBufStk, recvSizeStk, endFuncStk); + + uint32_t boundSidHint = 0u; + { + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it != g_rpc_clients.end()) + { + boundSidHint = it->second.sid; + } + } + PS2DtxCompatLayout dtxCompat{}; + { + std::lock_guard lock(g_dtx_rpc_mutex); + dtxCompat = g_dtxCompatLayout; + } + + auto looksLikeDtxCreatePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz) -> bool + { + return rbuf != 0u && rsz >= 4u && rsz <= 0x40u && + sendSz >= 12u && sendSz <= 0x1000u; + }; + + const bool isDtxCreate34Call = dtxCompat.isConfigured() && + (boundSidHint == dtxCompat.rpcSid) && + (rpcNum == 0x422u); + const bool forceStackForDtxCreate34 = + isDtxCreate34Call && + stackPackPlausible && + looksLikeDtxCreatePack(sendSizeStk, recvBufStk, recvSizeStk) && + !looksLikeDtxCreatePack(sendSizeReg, recvBufReg, recvSizeReg); + + bool useRegConvention = true; + if (forceStackForDtxCreate34) + { + useRegConvention = false; + } + else if (!regPackPlausible && stackPackPlausible) + { + const bool regHasValidCallback = (endFuncReg != 0u) && looksLikeFunc(endFuncReg); + const bool stackHasValidCallback = (endFuncStk != 0u) && looksLikeFunc(endFuncStk); + if (!(regHasValidCallback && !stackHasValidCallback)) + { + useRegConvention = false; + } + } + + sendSize = useRegConvention ? sendSizeReg : sendSizeStk; + recvBuf = useRegConvention ? recvBufReg : recvBufStk; + recvSize = useRegConvention ? recvSizeReg : recvSizeStk; + endFunc = useRegConvention ? endFuncReg : endFuncStk; + endParam = useRegConvention ? endParamReg : endParamStk; + + const bool isDtxLikeRpc = dtxCompat.isConfigured() && + ((boundSidHint == dtxCompat.rpcSid) || ((rpcNum & 0xFF00u) == 0x0400u)); + static uint32_t dtxAbiLogCount = 0u; + if (isDtxLikeRpc && dtxAbiLogCount < 96u) + { + RUNTIME_LOG("[SifCallRpc:ABI] client=0x" << std::hex << clientPtr + << " rpc=0x" << rpcNum + << " sidHint=0x" << boundSidHint + << " useReg=" << (useRegConvention ? 1 : 0) + << " reg=(" << sendSizeReg << "," << recvBufReg << "," << recvSizeReg << "," << endFuncReg << "," << endParamReg << ")" + << " stk=(" << sendSizeStk << "," << recvBufStk << "," << recvSizeStk << "," << endFuncStk << "," << endParamStk << ")" + << " plausible=(" << (regPackPlausible ? 1 : 0) << "," << (stackPackPlausible ? 1 : 0) << ")" + << " force34=" << (forceStackForDtxCreate34 ? 1 : 0) + << std::dec << std::endl); + ++dtxAbiLogCount; + } + + t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + + if (!client) + { + setReturnS32(ctx, -1); + return; + } + + client->command = rpcNum; + client->end_function = endFunc; + client->end_param = endParam; + client->hdr.mode = mode; + + { + std::lock_guard lock(g_rpc_mutex); + g_rpc_clients[clientPtr].busy = true; + g_rpc_clients[clientPtr].last_rpc = rpcNum; + uint32_t sid = g_rpc_clients[clientPtr].sid; + if (sid) + { + auto it = g_rpc_servers.find(sid); + if (it != g_rpc_servers.end()) + { + uint32_t mappedServer = it->second.sd_ptr; + if (mappedServer && client->server != mappedServer) + { + client->server = mappedServer; + } + } + } + } + + uint32_t sid = 0; + { + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it != g_rpc_clients.end()) + { + sid = it->second.sid; + } + } + + uint32_t serverPtr = client->server; + t_SifRpcServerData *sd = serverPtr ? reinterpret_cast(getMemPtr(rdram, serverPtr)) : nullptr; + + if (sd) + { + sd->client = clientPtr; + sd->pkt_addr = client->hdr.pkt_addr; + sd->rpc_number = rpcNum; + sd->size = static_cast(sendSize); + sd->recvbuf = recvBuf; + sd->rsize = static_cast(recvSize); + sd->rmode = ((mode & kSifRpcModeNowait) && endFunc == 0) ? 0 : 1; + sd->rid = 0; + } + + if (sd && sd->buf && sendBuf && sendSize > 0) + { + rpcCopyToRdram(rdram, sd->buf, sendBuf, sendSize); + } + + uint32_t resultPtr = 0; + bool handled = false; + + auto readRpcU32 = [&](uint32_t addr, uint32_t &out) -> bool + { + if (!addr) + { + return false; + } + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; + }; + + auto writeRpcU32 = [&](uint32_t addr, uint32_t value) -> bool + { + if (!addr) + { + return false; + } + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, &value, sizeof(value)); + return true; + }; + + if (!handled && runtime) + { + runtime->iop().init(rdram); + uint32_t iopResultPtr = 0u; + bool iopSignalNowaitCompletion = false; + if (runtime->iop().handleRPC(runtime, + sid, rpcNum, + sendBuf, sendSize, + recvBuf, recvSize, + iopResultPtr, + iopSignalNowaitCompletion)) + { + handled = true; + resultPtr = iopResultPtr; + if (iopSignalNowaitCompletion && (mode & kSifRpcModeNowait) != 0u) + { + uint32_t semaId = static_cast(client->hdr.sema_id); + if (semaId == 0xFFFFFFFFu || semaId == 0u) + { + semaId = endParam; + } + (void)signalRpcCompletionSema(semaId); + } + } + } + + const bool isDtxUrpc = dtxCompat.isUrpcRpc(sid, rpcNum); + uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; + uint32_t dtxUrpcFn = 0; + uint32_t dtxUrpcObj = 0; + uint32_t dtxUrpcSend0 = 0; + bool dtxUrpcDispatchAttempted = false; + bool dtxUrpcFallbackEmulated = false; + bool dtxUrpcFallbackCreate34 = false; + bool hasUrpcHandler = false; + if (isDtxUrpc) + { + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf, dtxUrpcSend0); + } + if (dtxCompat.hasUrpcTables() && dtxUrpcCommand < 64u) + { + (void)readRpcU32(dtxCompat.urpcFnTableBase + (dtxUrpcCommand * 4u), dtxUrpcFn); + (void)readRpcU32(dtxCompat.urpcObjTableBase + (dtxUrpcCommand * 4u), dtxUrpcObj); + } + hasUrpcHandler = (dtxUrpcCommand < 64u) && (dtxUrpcFn != 0u); + } + const bool allowServerDispatch = !isDtxUrpc || hasUrpcHandler; + const bool isDtxSid = dtxCompat.isConfigured() && sid == dtxCompat.rpcSid; + + if (sd && sd->func && (!isDtxSid || isDtxUrpc) && allowServerDispatch) + { + dtxUrpcDispatchAttempted = dtxUrpcDispatchAttempted || isDtxUrpc; + handled = rpcInvokeFunction(rdram, ctx, runtime, sd->func, rpcNum, sd->buf, sendSize, 0, &resultPtr); + if (handled && resultPtr == 0 && sd->buf) + { + resultPtr = sd->buf; + } + if (handled && resultPtr == 0 && recvBuf) + { + resultPtr = recvBuf; + } + } + + if (!handled && isDtxUrpc && sendBuf && sendSize > 0) + { + // Only dispatch through dtx_rpc_func when a URPC handler is registered in the table. + // If the slot is empty, defer to the fallback emulation below. + if (hasUrpcHandler && dtxCompat.dispatcherFuncAddr != 0u) + { + dtxUrpcDispatchAttempted = true; + handled = rpcInvokeFunction(rdram, ctx, runtime, dtxCompat.dispatcherFuncAddr, rpcNum, sendBuf, sendSize, 0, &resultPtr); + if (handled && resultPtr == 0) + { + resultPtr = sendBuf; + } + } + } + + if (!handled && isDtxSid) + { + if (rpcNum == 2 && recvBuf && recvSize >= sizeof(uint32_t)) + { + uint32_t dtxId = 0; + uint32_t eeWorkAddr = 0u; + uint32_t iopWorkAddr = 0u; + uint32_t wkSize = 0u; + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf + 0u, dtxId); + } + if (sendBuf && sendSize >= (4u * sizeof(uint32_t))) + { + (void)readRpcU32(sendBuf + 4u, eeWorkAddr); + (void)readRpcU32(sendBuf + 8u, iopWorkAddr); + (void)readRpcU32(sendBuf + 12u, wkSize); + } + + uint32_t normalizedEeWorkAddr = 0u; + uint32_t normalizedIopWorkAddr = 0u; + (void)normalizeGuestLinearAddr(eeWorkAddr, normalizedEeWorkAddr); + (void)normalizeGuestLinearAddr(iopWorkAddr, normalizedIopWorkAddr); + + uint32_t remoteHandle = 0; + { + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_remote_by_id.find(dtxId); + if (it != g_dtx_remote_by_id.end()) + { + remoteHandle = it->second; + } + if (!remoteHandle) + { + remoteHandle = rpcAllocServerAddr(rdram); + if (!remoteHandle) + { + remoteHandle = rpcAllocPacketAddr(rdram); + } + if (!remoteHandle) + { + remoteHandle = kRpcServerPoolBase + ((dtxId & 0xFFu) * kRpcServerStride); + } + g_dtx_remote_by_id[dtxId] = remoteHandle; + } + + DtxTransferState state{}; + state.dtxId = dtxId; + state.remoteHandle = remoteHandle; + state.eeWorkAddr = normalizedEeWorkAddr; + state.iopWorkAddr = normalizedIopWorkAddr; + state.wkSize = wkSize; + g_dtx_transfer_by_id[dtxId] = state; + } + + (void)writeRpcU32(recvBuf, remoteHandle); + if (recvSize > sizeof(uint32_t)) + { + rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + } + static uint32_t dtxCreateLogCount = 0; + if (dtxCreateLogCount < 64u) + { + RUNTIME_LOG("[SifCallRpc:DTX_CREATE] dtxId=0x" << std::hex << dtxId + << " remote=0x" << remoteHandle + << " recvBuf=0x" << recvBuf + << " recvSize=0x" << recvSize + << std::dec << std::endl); + ++dtxCreateLogCount; + } + handled = true; + resultPtr = recvBuf; + } + else if (rpcNum == 3) + { + uint32_t remoteHandle = 0; + if (sendBuf && sendSize >= sizeof(uint32_t) && readRpcU32(sendBuf, remoteHandle) && remoteHandle) + { + std::lock_guard lock(g_dtx_rpc_mutex); + for (auto it = g_dtx_remote_by_id.begin(); it != g_dtx_remote_by_id.end(); ++it) + { + if (it->second == remoteHandle) + { + g_dtx_transfer_by_id.erase(it->first); + g_dtx_remote_by_id.erase(it); + break; + } + } + } + if (recvBuf && recvSize > 0) + { + rpcZeroRdram(rdram, recvBuf, recvSize); + } + handled = true; + resultPtr = recvBuf; + } + else if (rpcNum >= 0x400 && rpcNum < 0x500) + { + dtxUrpcFallbackEmulated = true; + const uint32_t urpcCommand = rpcNum & 0xFFu; + uint32_t outWords[4] = {1u, 0u, 0u, 0u}; + uint32_t outWordCount = 1u; + + auto readSendWord = [&](uint32_t index, uint32_t &out) -> bool + { + const uint64_t byteOffset = static_cast(index) * sizeof(uint32_t); + if (!sendBuf || sendSize < (byteOffset + sizeof(uint32_t))) + { + return false; + } + return readRpcU32(sendBuf + static_cast(byteOffset), out); + }; + + switch (urpcCommand) + { + case 0u: // SJX_CREATE + { + uint32_t srcSjHandle = 0u; + uint32_t dstSjHandle = 0u; + uint32_t line = 0u; + uint32_t eeObjAddr = 0u; + (void)readSendWord(0u, srcSjHandle); + (void)readSendWord(1u, dstSjHandle); + (void)readSendWord(2u, line); + (void)readSendWord(3u, eeObjAddr); + + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxSjxState state{}; + state.handle = handle; + state.srcSjHandle = srcSjHandle; + state.dstSjHandle = dstSjHandle; + state.line = line; + state.eeObjAddr = eeObjAddr; + g_dtx_sjx_by_handle[handle] = state; + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; + } + case 1u: // SJX_DESTROY + { + uint32_t handle = 0u; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_sjx_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 2u: // SJX_RESET + { + uint32_t handle = 0u; + uint32_t xid = 0u; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, xid); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjx_by_handle.find(handle); + if (it != g_dtx_sjx_by_handle.end()) + { + it->second.xid = static_cast(xid & 0xFFFFu); + } + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 8u: // PS2RNA_CREATE + { + uint32_t maxChannels = 0u; + uint32_t sjHandle0 = 0u; + uint32_t sjHandle1 = 0u; + (void)readSendWord(0u, maxChannels); + (void)readSendWord(2u, sjHandle0); + (void)readSendWord(3u, sjHandle1); + + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxPs2RnaState state{}; + state.handle = handle; + state.maxChannels = maxChannels; + state.sjHandle0 = sjHandle0; + state.sjHandle1 = sjHandle1; + state.channelCount = maxChannels; + g_dtx_ps2rna_by_handle[handle] = state; + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; + } + case 9u: // PS2RNA_DESTROY + { + uint32_t handle = 0u; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_ps2rna_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 32u: // SJRMT_RBF_CREATE + case 33u: // SJRMT_MEM_CREATE + case 34u: // SJRMT_UNI_CREATE + { + uint32_t arg0 = 0; + uint32_t arg1 = 0; + uint32_t arg2 = 0; + (void)readSendWord(0u, arg0); + (void)readSendWord(1u, arg1); + (void)readSendWord(2u, arg2); + + uint32_t mode = 0; + uint32_t wkAddr = 0; + uint32_t wkSize = 0; + if (urpcCommand == 34u) + { + mode = arg0; + wkAddr = arg1; + wkSize = arg2; + dtxUrpcFallbackCreate34 = true; + } + else if (urpcCommand == 33u) + { + wkAddr = arg0; + wkSize = arg1; + } + else + { + wkAddr = arg0; + wkSize = (arg1 != 0u) ? arg1 : arg2; + } + + wkSize = dtxNormalizeSjrmtCapacity(wkSize); + + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxSjrmtState state{}; + state.handle = handle; + state.mode = mode; + state.wkAddr = wkAddr; + state.wkSize = wkSize; + state.readPos = 0u; + state.writePos = 0u; + state.roomBytes = wkSize; + state.dataBytes = 0u; + state.uuid0 = 0x53524D54u; // "SRMT" + state.uuid1 = handle; + state.uuid2 = wkAddr; + state.uuid3 = wkSize; + g_dtx_sjrmt_by_handle[handle] = state; + + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; + } + case 35u: // SJRMT_DESTROY + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_sjrmt_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 36u: // SJRMT_GET_UUID + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + outWords[0] = it->second.uuid0; + outWords[1] = it->second.uuid1; + outWords[2] = it->second.uuid2; + outWords[3] = it->second.uuid3; + } + else + { + outWords[0] = 0u; + outWords[1] = 0u; + outWords[2] = 0u; + outWords[3] = 0u; + } + outWordCount = 4u; + break; + } + case 37u: // SJRMT_RESET + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + const uint32_t cap = (it->second.wkSize == 0u) ? 0x4000u : it->second.wkSize; + it->second.readPos = 0u; + it->second.writePos = 0u; + it->second.roomBytes = cap; + it->second.dataBytes = 0u; + } + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 38u: // SJRMT_GET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t nbyte = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(2u, nbyte); + + uint32_t ptr = 0u; + uint32_t len = 0u; + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + + if (streamId == 0u) + { + len = std::min(nbyte, state.roomBytes); + ptr = state.wkAddr + (cap ? (state.writePos % cap) : 0u); + if (cap != 0u) + { + state.writePos = (state.writePos + len) % cap; + } + state.roomBytes -= len; + } + else if (streamId == 1u) + { + len = std::min(nbyte, state.dataBytes); + ptr = state.wkAddr + (cap ? (state.readPos % cap) : 0u); + if (cap != 0u) + { + state.readPos = (state.readPos + len) % cap; + } + state.dataBytes -= len; + } + } + + outWords[0] = ptr; + outWords[1] = len; + outWordCount = 2u; + break; + } + case 39u: // SJRMT_UNGET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t len = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(3u, len); + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (streamId == 0u) + { + const uint32_t delta = (cap == 0u) ? 0u : (len % cap); + if (cap != 0u) + { + state.writePos = (state.writePos + cap - delta) % cap; + } + state.roomBytes = std::min(cap, state.roomBytes + len); + } + else if (streamId == 1u) + { + const uint32_t delta = (cap == 0u) ? 0u : (len % cap); + if (cap != 0u) + { + state.readPos = (state.readPos + cap - delta) % cap; + } + state.dataBytes = std::min(cap, state.dataBytes + len); + } + } + + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 40u: // SJRMT_PUT_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t len = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(3u, len); + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (streamId == 0u) + { + state.roomBytes = std::min(cap, state.roomBytes + len); + } + else if (streamId == 1u) + { + state.dataBytes = std::min(cap, state.dataBytes + len); + } + } + + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 41u: // SJRMT_GET_NUM_DATA + { + uint32_t handle = 0; + uint32_t streamId = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + outWords[0] = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; + } + else + { + outWords[0] = 0u; + } + outWordCount = 1u; + break; + } + case 42u: // SJRMT_IS_GET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t nbyte = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(2u, nbyte); + + uint32_t available = 0u; + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + available = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; + } + outWords[0] = (available >= nbyte) ? 1u : 0u; + outWords[1] = available; + outWordCount = 2u; + break; + } + case 43u: // SJRMT_INIT + case 44u: // SJRMT_FINISH + { + outWords[0] = 1u; + outWordCount = 1u; + break; + } + default: + { + uint32_t urpcRet = 1u; + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf, urpcRet); + } + if (urpcCommand == 0u) + { + std::lock_guard lock(g_dtx_rpc_mutex); + urpcRet = dtxAllocUrpcHandleLocked(); + } + if (urpcRet == 0u) + { + urpcRet = 1u; + } + outWords[0] = urpcRet; + outWordCount = 1u; + break; + } + } + + if (recvBuf && recvSize > 0u) + { + const uint32_t recvWordCapacity = static_cast(recvSize / sizeof(uint32_t)); + const uint32_t wordsToWrite = std::min(outWordCount, recvWordCapacity); + for (uint32_t i = 0; i < wordsToWrite; ++i) + { + (void)writeRpcU32(recvBuf + (i * sizeof(uint32_t)), outWords[i]); + } + + // SJRMT_IsGetChunk callers read rbuf[1] even when nout==1. + if (urpcCommand == 42u && outWordCount > 1u) + { + (void)writeRpcU32(recvBuf + sizeof(uint32_t), outWords[1]); + } + + if (recvSize > (wordsToWrite * sizeof(uint32_t))) + { + rpcZeroRdram(rdram, recvBuf + (wordsToWrite * sizeof(uint32_t)), + recvSize - (wordsToWrite * sizeof(uint32_t))); + } + } + + handled = true; + resultPtr = recvBuf; + } + } + + if (recvBuf && recvSize > 0) + { + if (handled && resultPtr && resultPtr != recvBuf) + { + rpcCopyToRdram(rdram, recvBuf, resultPtr, recvSize); + } + else if (!handled && sendBuf && sendSize > 0 && sendBuf != recvBuf) + { + size_t copySize = (sendSize < recvSize) ? sendSize : recvSize; + rpcCopyToRdram(rdram, recvBuf, sendBuf, copySize); + } + else if (!handled) + { + rpcZeroRdram(rdram, recvBuf, recvSize); + } + } + + if (isDtxUrpc) + { + static int dtxUrpcLogCount = 0; + if (dtxUrpcLogCount < 64) + { + uint32_t dtxUrpcRecv0 = 0; + if (recvBuf && recvSize >= sizeof(uint32_t)) + { + (void)readRpcU32(recvBuf, dtxUrpcRecv0); + } + RUNTIME_LOG("[SifCallRpc:DTX] rpcNum=0x" << std::hex << rpcNum + << " cmd=0x" << dtxUrpcCommand + << " name=" << dtxUrpcCommandName(dtxUrpcCommand) + << " fn=0x" << dtxUrpcFn + << " obj=0x" << dtxUrpcObj + << " send0=0x" << dtxUrpcSend0 + << " recv0=0x" << dtxUrpcRecv0 + << " resultPtr=0x" << resultPtr + << " handled=" << std::dec << (handled ? 1 : 0) + << " dispatch=" << (dtxUrpcDispatchAttempted ? 1 : 0) + << " emu=" << (dtxUrpcFallbackEmulated ? 1 : 0) + << " emu34=" << (dtxUrpcFallbackCreate34 ? 1 : 0) + << std::endl); + + if (dtxUrpcCommand >= 37u && dtxUrpcCommand <= 42u) + { + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(dtxUrpcSend0); + if (it != g_dtx_sjrmt_by_handle.end()) + { + const DtxSjrmtState &state = it->second; + RUNTIME_LOG("[SifCallRpc:DTX_SJRMT] handle=0x" << std::hex << dtxUrpcSend0 + << " cmd=" << dtxUrpcCommandName(dtxUrpcCommand) + << " wk=0x" << state.wkAddr + << " size=0x" << state.wkSize + << " read=0x" << state.readPos + << " write=0x" << state.writePos + << std::dec + << " room=" << state.roomBytes + << " data=" << state.dataBytes); + if ((dtxUrpcCommand == 38u || dtxUrpcCommand == 42u) && sendBuf && sendSize >= 12u) + { + uint32_t streamId = 0u; + uint32_t nbyte = 0u; + (void)readRpcU32(sendBuf + 4u, streamId); + (void)readRpcU32(sendBuf + 8u, nbyte); + RUNTIME_LOG(" stream=" << dtxStreamName(streamId) + << " req=" << nbyte); + } + RUNTIME_LOG(std::endl); + } + } + ++dtxUrpcLogCount; + } + } + + if (endFunc) + { + bool callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr); + + if (!callbackInvoked && endFunc >= 0x10000u) + { + const uint32_t normalizedEndFunc = endFunc - 0x10000u; + if (runtime->hasFunction(normalizedEndFunc)) + { + callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, normalizedEndFunc, endParam, 0, 0, 0, nullptr); + } + } + + PS2SoundDriverCompatLayout soundCompat{}; + { + std::lock_guard lock(g_rpc_mutex); + soundCompat = g_soundDriverCompatLayout; + } + + if (soundCompat.matchesCompletionCallback(endFunc)) + { + uint32_t semaId = static_cast(client->hdr.sema_id); + if (semaId == 0xFFFFFFFFu || semaId == 0u) + { + semaId = endParam; + } + (void)signalRpcCompletionSema(semaId); + if (rdram && soundCompat.busyFlagAddr != 0u && soundCompat.matchesClearBusyCallback(endFunc)) + { + if (uint32_t *busy = reinterpret_cast(getMemPtr(rdram, soundCompat.busyFlagAddr))) + { + *busy = 0u; + } + } + } + + if (!callbackInvoked) + { + uint32_t semaId = static_cast(client->hdr.sema_id); + if (semaId == 0xFFFFFFFFu || semaId == 0u) + { + semaId = endParam; + } + const bool fallbackSignaledSema = signalRpcCompletionSema(semaId); + + static uint32_t unresolvedEndFuncWarnCount = 0; + if (unresolvedEndFuncWarnCount < 32u) + { + std::cerr << "[SifCallRpc] unresolved end callback endFunc=0x" << std::hex << endFunc + << " semaId=0x" << semaId + << " fallbackSignal=" << std::dec << (fallbackSignaledSema ? 1 : 0) + << std::endl; + ++unresolvedEndFuncWarnCount; + } + } + } + + static int logCount = 0; + if (logCount < 10) + { + RUNTIME_LOG("[SifCallRpc] client=0x" << std::hex << clientPtr + << " sid=0x" << sid + << " rpcNum=0x" << rpcNum + << " mode=0x" << mode + << " sendBuf=0x" << sendBuf + << " recvBuf=0x" << recvBuf + << " recvSize=0x" << recvSize + << " size=" << std::dec << sendSize << std::endl); + ++logCount; + } + + { + std::lock_guard lock(g_rpc_mutex); + g_rpc_clients[clientPtr].busy = false; + } + + setReturnS32(ctx, 0); +} + +void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sdPtr = getRegU32(ctx, 4); + uint32_t sid = getRegU32(ctx, 5); + uint32_t func = getRegU32(ctx, 6); + uint32_t buf = getRegU32(ctx, 7); + // stack args: cfunc, cbuf, qd... + uint32_t sp = getRegU32(ctx, 29); + uint32_t cfunc = 0; + uint32_t cbuf = 0; + uint32_t qd = 0; + readStackU32(rdram, sp, 0x10, cfunc); + readStackU32(rdram, sp, 0x14, cbuf); + readStackU32(rdram, sp, 0x18, qd); + + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + if (!sd) + { + setReturnS32(ctx, -1); + return; + } + + sd->sid = static_cast(sid); + sd->func = func; + sd->buf = buf; + sd->size = 0; + sd->cfunc = cfunc; + sd->cbuf = cbuf; + sd->size2 = 0; + sd->client = 0; + sd->pkt_addr = 0; + sd->rpc_number = 0; + sd->recvbuf = 0; + sd->rsize = 0; + sd->rmode = 0; + sd->rid = 0; + sd->base = qd; + sd->link = 0; + sd->next = 0; + + { + std::lock_guard lock(g_rpc_mutex); + + if (qd) + { + t_SifRpcDataQueue *queue = reinterpret_cast(getMemPtr(rdram, qd)); + if (queue) + { + if (!queue->link) + { + queue->link = sdPtr; + } + else + { + uint32_t curPtr = queue->link; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (!cur->link) + { + cur->link = sdPtr; + break; + } + if (cur->link == sdPtr) + break; + curPtr = cur->link; + } + } + } + } + + g_rpc_servers[sid] = {sid, sdPtr}; + for (auto &entry : g_rpc_clients) + { + if (entry.second.sid == sid) + { + t_SifRpcClientData *cd = reinterpret_cast(getMemPtr(rdram, entry.first)); + if (cd) + { + cd->server = sdPtr; + cd->buf = sd->buf; + cd->cbuf = sd->cbuf; + } + } + } + } + + RUNTIME_LOG("[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec); + setReturnS32(ctx, 0); +} + +void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clientPtr = getRegU32(ctx, 4); + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it == g_rpc_clients.end()) + { + setReturnS32(ctx, 0); + return; + } + setReturnS32(ctx, it->second.busy ? 1 : 0); +} + +void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t qdPtr = getRegU32(ctx, 4); + int threadId = static_cast(getRegU32(ctx, 5)); + + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + if (!qd) + { + setReturnS32(ctx, -1); + return; + } + + qd->thread_id = threadId; + qd->active = 0; + qd->link = 0; + qd->start = 0; + qd->end = 0; + qd->next = 0; + + { + std::lock_guard lock(g_rpc_mutex); + if (!g_rpc_active_queue) + { + g_rpc_active_queue = qdPtr; + } + else + { + uint32_t curPtr = g_rpc_active_queue; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + if (curPtr == qdPtr) + break; + t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (!cur->next) + { + cur->next = qdPtr; + break; + } + curPtr = cur->next; + } + } + } + + setReturnS32(ctx, 0); +} + +void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t qdPtr = getRegU32(ctx, 4); + if (!qdPtr) + { + setReturnU32(ctx, 0); + return; + } + + std::lock_guard lock(g_rpc_mutex); + if (!g_rpc_active_queue) + { + setReturnU32(ctx, 0); + return; + } + + if (g_rpc_active_queue == qdPtr) + { + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + g_rpc_active_queue = qd ? qd->next : 0; + setReturnU32(ctx, qdPtr); + return; + } + + uint32_t curPtr = g_rpc_active_queue; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (cur->next == qdPtr) + { + t_SifRpcDataQueue *rem = reinterpret_cast(getMemPtr(rdram, qdPtr)); + cur->next = rem ? rem->next : 0; + setReturnU32(ctx, qdPtr); + return; + } + curPtr = cur->next; + } + + setReturnU32(ctx, 0); +} + +void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sdPtr = getRegU32(ctx, 4); + uint32_t qdPtr = getRegU32(ctx, 5); + + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + if (!qd || !sdPtr) + { + setReturnU32(ctx, 0); + return; + } + + std::lock_guard lock(g_rpc_mutex); + + if (qd->link == sdPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + qd->link = sd ? sd->link : 0; + if (sd) + sd->link = 0; + setReturnU32(ctx, sdPtr); + return; + } + + uint32_t curPtr = qd->link; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (cur->link == sdPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + cur->link = sd ? sd->link : 0; + if (sd) + sd->link = 0; + setReturnU32(ctx, sdPtr); + return; + } + curPtr = cur->link; + } + + setReturnU32(ctx, 0); +} + +void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + SifCallRpc(rdram, ctx, runtime); +} + +void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t cid = getRegU32(ctx, 4); + uint32_t packetAddr = getRegU32(ctx, 5); + uint32_t packetSize = getRegU32(ctx, 6); + uint32_t srcExtra = getRegU32(ctx, 7); + + uint32_t sp = getRegU32(ctx, 29); + uint32_t destExtra = 0; + uint32_t sizeExtra = 0; + readStackU32(rdram, sp, 0x10, destExtra); + readStackU32(rdram, sp, 0x14, sizeExtra); + + if (sizeExtra > 0 && srcExtra && destExtra) + { + rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra); + } + + static int logCount = 0; + if (logCount < 5) + { + RUNTIME_LOG("[sceSifSendCmd] cid=0x" << std::hex << cid + << " packet=0x" << packetAddr + << " psize=0x" << packetSize + << " extra=0x" << destExtra << std::dec << std::endl); + ++logCount; + } + + // Return non-zero on success. + setReturnS32(ctx, 1); +} + +void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t queuePtr = getRegU32(ctx, 4); + setReturnS32(ctx, static_cast(queuePtr)); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.h b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.h new file mode 100644 index 00000000..a968a754 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.h @@ -0,0 +1,29 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void noteDtxSifDmaTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes); + bool handleSoundDriverRpcService(uint8_t *rdram, PS2Runtime *runtime, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBuf, uint32_t sendSize, + uint32_t recvBuf, uint32_t recvSize, + uint32_t &resultPtr, + bool &signalNowaitCompletion); + void prepareSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t size); + void finalizeSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t size); + void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp similarity index 94% rename from ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp index 56bb2447..5c1ebf1d 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp @@ -1,3 +1,8 @@ +#include "Common.h" +#include "Sync.h" + +namespace ps2_syscalls +{ static bool looksLikeGuestPointerOrNull(uint32_t value) { if (value == 0u) @@ -186,7 +191,7 @@ void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) g_semas.emplace(id, info); } - std::cout << "[CreateSema] id=" << id << " init=" << init << " max=" << max << std::endl; + RUNTIME_LOG("[CreateSema] id=" << id << " init=" << init << " max=" << max); setReturnS32(ctx, id); } @@ -253,11 +258,11 @@ void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t sigLog = s_signalSemaLogs.fetch_add(1, std::memory_order_relaxed); if (sigLog < 256u) { - std::cout << "[SignalSema] tid=" << g_currentThreadId + RUNTIME_LOG("[SignalSema] tid=" << g_currentThreadId << " sid=" << sid << " count=" << beforeCount << "->" << afterCount << " ret=" << ret - << std::endl; + << std::endl); } setReturnS32(ctx, ret); @@ -289,12 +294,12 @@ void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t blockLog = s_waitSemaBlockLogs.fetch_add(1, std::memory_order_relaxed); if (blockLog < 256u) { - std::cout << "[WaitSema:block] tid=" << g_currentThreadId + RUNTIME_LOG("[WaitSema:block] tid=" << g_currentThreadId << " sid=" << sid << " pc=0x" << std::hex << ctx->pc << " ra=0x" << getRegU32(ctx, 31) << std::dec - << std::endl; + << std::endl); } if (info) @@ -350,11 +355,11 @@ void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t wakeLog = s_waitSemaWakeLogs.fetch_add(1, std::memory_order_relaxed); if (wakeLog < 256u) { - std::cout << "[WaitSema:wake] tid=" << g_currentThreadId + RUNTIME_LOG("[WaitSema:wake] tid=" << g_currentThreadId << " sid=" << sid << " ret=" << ret << " count=" << sema->count - << std::endl; + << std::endl); } lock.unlock(); waitWhileSuspended(info, runtime); @@ -508,11 +513,11 @@ void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t setLog = s_setEventFlagLogs.fetch_add(1, std::memory_order_relaxed); if (setLog < 256u) { - std::cout << "[SetEventFlag] tid=" << g_currentThreadId + RUNTIME_LOG("[SetEventFlag] tid=" << g_currentThreadId << " eid=" << eid << " bits=0x" << std::hex << bits << " newBits=0x" << newBits - << std::dec << std::endl; + << std::dec << std::endl); } info->cv.notify_all(); setReturnS32(ctx, 0); @@ -609,7 +614,7 @@ void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t evBlockLog = s_waitEventBlockLogs.fetch_add(1, std::memory_order_relaxed); if (evBlockLog < 256u) { - std::cout << "[WaitEventFlag:block] tid=" << g_currentThreadId + RUNTIME_LOG("[WaitEventFlag:block] tid=" << g_currentThreadId << " eid=" << eid << " waitBits=0x" << std::hex << waitBits << " mode=0x" << mode @@ -617,13 +622,13 @@ void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) << " pc=0x" << ctx->pc << " ra=0x" << getRegU32(ctx, 31) << std::dec - << std::endl; + << std::endl); } if (tInfo) { std::lock_guard tLock(tInfo->m); - tInfo->status = THS_WAIT; + tInfo->status = (tInfo->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT; tInfo->waitType = TSW_EVENT; tInfo->waitId = eid; tInfo->forceRelease = false; @@ -639,7 +644,7 @@ void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) if (tInfo) { std::lock_guard tLock(tInfo->m); - tInfo->status = THS_RUN; + tInfo->status = (tInfo->suspendCount > 0) ? THS_SUSPEND : THS_RUN; tInfo->waitType = TSW_NONE; tInfo->waitId = 0; if (tInfo->forceRelease) @@ -686,12 +691,12 @@ void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t evWakeLog = s_waitEventWakeLogs.fetch_add(1, std::memory_order_relaxed); if (evWakeLog < 256u) { - std::cout << "[WaitEventFlag:wake] tid=" << g_currentThreadId + RUNTIME_LOG("[WaitEventFlag:wake] tid=" << g_currentThreadId << " eid=" << eid << " ret=" << ret << " bits=0x" << std::hex << info->bits << std::dec - << std::endl; + << std::endl); } lock.unlock(); @@ -827,9 +832,9 @@ void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) static int logCount = 0; if (logCount < 5) { - std::cout << "[SetAlarm] ticks=" << ticks + RUNTIME_LOG("[SetAlarm] ticks=" << ticks << " handler=0x" << std::hex << handler - << " arg=0x" << arg << std::dec << std::endl; + << " arg=0x" << arg << std::dec << std::endl); ++logCount; } @@ -866,6 +871,11 @@ void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) setReturnS32(ctx, alarmId); } +void InitAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { SetAlarm(rdram, ctx, runtime); @@ -900,3 +910,14 @@ void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { CancelAlarm(rdram, ctx, runtime); } + +void ReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + CancelAlarm(rdram, ctx, runtime); +} + +void iReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + iCancelAlarm(rdram, ctx, runtime); +} +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.h new file mode 100644 index 00000000..0d66da1b --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.h @@ -0,0 +1,35 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void InitAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp similarity index 96% rename from ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp index 30359322..af8110c0 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp @@ -1,12 +1,35 @@ +#include "Common.h" +#include "System.h" + +namespace ps2_syscalls +{ void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int interlaced = getRegU32(ctx, 4); // $a0 - 0=non-interlaced, 1=interlaced int videoMode = getRegU32(ctx, 5); // $a1 - 0=NTSC, 1=PAL, 2=VESA, 3=HiVision int frameMode = getRegU32(ctx, 6); // $a2 - 0=field, 1=frame - std::cout << "PS2 GsSetCrt: interlaced=" << interlaced + if (runtime) + { + auto &gs = runtime->memory().gs(); + const uint64_t smode2 = + (static_cast(interlaced) & 0x1ull) | + ((static_cast(frameMode) & 0x1ull) << 1); + + gs.smode2 = smode2; + + // Keep CRT1 enabled after the BIOS syscall selects a display mode. + if ((gs.pmode & 0x3ull) == 0ull) + { + gs.pmode |= 0x1ull; + } + } + + RUNTIME_LOG("PS2 GsSetCrt: interlaced=" << interlaced << ", videoMode=" << videoMode - << ", frameMode=" << frameMode << std::endl; + << ", frameMode=" << frameMode << std::endl); + + setReturnS32(ctx, 0); } void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -22,7 +45,7 @@ void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) imr = runtime->memory().gs().imr; } - std::cout << "PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec << std::endl; + RUNTIME_LOG("PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec); setReturnU64(ctx, imr); // Return in $v0/$v1 } @@ -41,7 +64,7 @@ void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) oldImr = runtime->memory().gs().imr; runtime->memory().gs().imr = newImr; } - std::cout << "PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec << std::endl; + RUNTIME_LOG("PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec); setReturnU64(ctx, oldImr); } @@ -54,7 +77,7 @@ void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags) - std::cout << "PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec << std::endl; + RUNTIME_LOG("PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec); // Do nothing for now. } @@ -269,7 +292,7 @@ static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram, uint32_t originalEnd, uint32_t target); -static bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t handler = 0u; { @@ -933,3 +956,4 @@ void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) setReturnS32(ctx, 0); } +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/System.h b/ps2xRuntime/src/lib/Kernel/Syscalls/System.h new file mode 100644 index 00000000..b668620e --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.h @@ -0,0 +1,35 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iGsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iGsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + 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 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); + void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId); + void initializeGuestKernelState(uint8_t *rdram); + void SetSyscall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + 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 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 RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp similarity index 91% rename from ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl rename to ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp index 05352594..cf410b81 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp @@ -1,3 +1,8 @@ +#include "Common.h" +#include "Thread.h" + +namespace ps2_syscalls +{ static void applySuspendStatusLocked(ThreadInfo &info) { if (info.waitType != TSW_NONE) @@ -53,6 +58,16 @@ void iFlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) FlushCache(rdram, ctx, runtime); } +void EnableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, KE_OK); +} + +void DisableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, KE_OK); +} + void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { std::cerr << "Syscall: ResetEE - requesting runtime stop" << std::endl; @@ -176,12 +191,12 @@ void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) g_threads[id] = info; } - std::cout << "[CreateThread] id=" << id + RUNTIME_LOG("[CreateThread] id=" << id << " entry=0x" << std::hex << info->entry << " stack=0x" << info->stack << " size=0x" << info->stackSize << " gp=0x" << info->gp - << " prio=" << std::dec << info->priority << std::endl; + << " prio=" << std::dec << info->priority << std::endl); setReturnS32(ctx, id); } @@ -297,9 +312,9 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { info->stack = autoStack; info->ownsStack = true; - std::cout << "[StartThread] id=" << tid + RUNTIME_LOG("[StartThread] id=" << tid << " auto-stack=0x" << std::hex << autoStack - << " size=0x" << info->stackSize << std::dec << std::endl; + << " size=0x" << info->stackSize << std::dec << std::endl); } } @@ -349,11 +364,11 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) g_currentThreadId = tid; - std::cout << "[StartThread] id=" << tid + RUNTIME_LOG("[StartThread] id=" << tid << " entry=0x" << std::hex << info->entry << " sp=0x" << GPR_U32(threadCtx, 29) << " gp=0x" << GPR_U32(threadCtx, 28) - << " arg=0x" << info->arg << std::dec << std::endl; + << " arg=0x" << info->arg << std::dec << std::endl); bool exited = false; try @@ -382,12 +397,12 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) if ((stepCount & 0x1FFFFFu) == 0u) { - std::cout << "[StartThread] id=" << tid + RUNTIME_LOG("[StartThread] id=" << tid << " heartbeat pc=0x" << std::hex << pc << " ra=0x" << GPR_U32(threadCtx, 31) << " sp=0x" << GPR_U32(threadCtx, 29) << " gp=0x" << GPR_U32(threadCtx, 28) - << std::dec << std::endl; + << std::dec << std::endl); } if (pc == lastPc) @@ -399,10 +414,10 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) } if ((samePcCount % kSamePcWarnInterval) == 0u) { - std::cout << "[StartThread] id=" << tid + RUNTIME_LOG("[StartThread] id=" << tid << " spinning at pc=0x" << std::hex << pc << " ra=0x" << GPR_U32(threadCtx, 31) - << std::dec << std::endl; + << std::dec << std::endl); } } else @@ -411,33 +426,6 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) lastPc = pc; } - thread_local uint32_t s_adxProbeLogs = 0u; - if (s_adxProbeLogs < 256u) - { - const uint32_t raProbe = GPR_U32(threadCtx, 31); - const bool probeAdxSetCmd = (pc == 0x2F22E0u) && - ((raProbe < 0x00100000u) || (raProbe == 0x2F45B0u)); - const bool probeAdxUnlock = (pc == 0x2F45B0u) && - (raProbe < 0x00100000u); - const bool probeLowPc = (pc < 0x00100000u); - if (probeAdxSetCmd || probeAdxUnlock || probeLowPc) - { - auto flags = std::cerr.flags(); - std::cerr << "[StartThread:adx-probe] tid=" << tid - << " pc=0x" << std::hex << pc - << " ra=0x" << raProbe - << " sp=0x" << GPR_U32(threadCtx, 29) - << " gp=0x" << GPR_U32(threadCtx, 28) - << " a0=0x" << GPR_U32(threadCtx, 4) - << " a1=0x" << GPR_U32(threadCtx, 5) - << " a2=0x" << GPR_U32(threadCtx, 6) - << " a3=0x" << GPR_U32(threadCtx, 7) - << std::dec << std::endl; - std::cerr.flags(flags); - ++s_adxProbeLogs; - } - } - PS2Runtime::RecompiledFunction step = runtime->lookupFunction(pc); if (!step) { @@ -462,8 +450,8 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) if (!exited) { - std::cout << "[StartThread] id=" << tid << " returned (pc=0x" - << std::hex << threadCtx->pc << std::dec << ")" << std::endl; + RUNTIME_LOG("[StartThread] id=" << tid << " returned (pc=0x" + << std::hex << threadCtx->pc << std::dec << ")" << std::endl); } runExitHandlersForThread(tid, rdram, threadCtx, runtime); @@ -782,10 +770,10 @@ void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t sleepBlockLog = s_sleepBlockLogs.fetch_add(1, std::memory_order_relaxed); if (sleepBlockLog < 256u) { - std::cout << "[SleepThread:block] tid=" << g_currentThreadId + RUNTIME_LOG("[SleepThread:block] tid=" << g_currentThreadId << " pc=0x" << std::hex << ctx->pc << " ra=0x" << getRegU32(ctx, 31) - << std::dec << std::endl; + << std::dec << std::endl); } info->status = THS_WAIT; @@ -825,10 +813,10 @@ void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t sleepWakeLog = s_sleepWakeLogs.fetch_add(1, std::memory_order_relaxed); if (sleepWakeLog < 256u) { - std::cout << "[SleepThread:wake] tid=" << g_currentThreadId + RUNTIME_LOG("[SleepThread:wake] tid=" << g_currentThreadId << " ret=" << ret << " wakeupCount=" << info->wakeupCount - << std::endl; + << std::endl); } lock.unlock(); @@ -893,11 +881,11 @@ void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) const uint32_t wakeupLog = s_wakeupLogs.fetch_add(1, std::memory_order_relaxed); if (wakeupLog < 256u) { - std::cout << "[WakeupThread] tid=" << g_currentThreadId + RUNTIME_LOG("[WakeupThread] tid=" << g_currentThreadId << " target=" << tid << " status=" << statusAfter << " wakeupCount=" << newWakeupCount - << std::endl; + << std::endl); } setReturnS32(ctx, KE_OK); } @@ -1013,7 +1001,7 @@ void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runti } if (logCount < 16) { - std::cout << "[RotateThreadReadyQueue] prio=" << prio << std::endl; + RUNTIME_LOG("[RotateThreadReadyQueue] prio=" << prio); ++logCount; } if (prio <= 0 || prio >= 128) @@ -1104,3 +1092,4 @@ void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { ReleaseWaitThread(rdram, ctx, runtime); } +} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.h new file mode 100644 index 00000000..e34aa052 --- /dev/null +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + void FlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iFlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void EnableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DisableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SetMemoryMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void InitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void GetThreadId(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void CancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iCancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iRotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +} diff --git a/ps2xRuntime/src/lib/ps2_audio.cpp b/ps2xRuntime/src/lib/ps2_audio.cpp index 720d2c96..117d016c 100644 --- a/ps2xRuntime/src/lib/ps2_audio.cpp +++ b/ps2xRuntime/src/lib/ps2_audio.cpp @@ -1,5 +1,5 @@ -#include "ps2_audio.h" -#include "ps2_memory.h" +#include "runtime/ps2_audio.h" +#include "runtime/ps2_memory.h" #include "raylib.h" #include #include @@ -106,9 +106,13 @@ void PS2AudioBackend::onVagTransferFromBuffer(const uint8_t *data, uint32_t size m_sampleBank[physAddr] = sample; m_mostRecentSampleKey = physAddr; m_loadOrderSamples.push_back(std::move(sample)); + m_loadOrderSampleKeys.push_back(physAddr); constexpr size_t kMaxLoadOrderSamples = 32; if (m_loadOrderSamples.size() > kMaxLoadOrderSamples) + { m_loadOrderSamples.erase(m_loadOrderSamples.begin()); + m_loadOrderSampleKeys.erase(m_loadOrderSampleKeys.begin()); + } } namespace @@ -177,10 +181,12 @@ void PS2AudioBackend::play(uint32_t sampleAddr, float pitch, float volume, uint3 sampleToPlay = &it->second; sampleKey = it->first; } - else if (voiceIndex != 0xFFFFFFFFu && voiceIndex < m_loadOrderSamples.size()) + else if (voiceIndex != 0xFFFFFFFFu && + voiceIndex < m_loadOrderSamples.size() && + voiceIndex < m_loadOrderSampleKeys.size()) { sampleToPlay = &m_loadOrderSamples[voiceIndex]; - sampleKey = 0x1719740u + voiceIndex; + sampleKey = m_loadOrderSampleKeys[voiceIndex]; } else { diff --git a/ps2xRuntime/src/lib/ps2_audio_vag.cpp b/ps2xRuntime/src/lib/ps2_audio_vag.cpp index 12ba0ed9..9b187f67 100644 --- a/ps2xRuntime/src/lib/ps2_audio_vag.cpp +++ b/ps2xRuntime/src/lib/ps2_audio_vag.cpp @@ -1,4 +1,4 @@ -#include "ps2_memory.h" +#include "runtime/ps2_memory.h" #include #include #include diff --git a/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp b/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp index 12f169a3..29d69d62 100644 --- a/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp +++ b/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp @@ -1,4 +1,5 @@ -#include "ps2_gif_arbiter.h" +#include "runtime/ps2_gif_arbiter.h" +#include "ps2_log.h" #include #include #include @@ -56,14 +57,14 @@ void GifArbiter::submit(GifPathId pathId, const uint8_t *data, uint32_t sizeByte uint32_t nreg = static_cast((tagLo >> 60) & 0xFu); if (nreg == 0u) nreg = 16u; - std::cout << "[gif:submit] idx=" << debugIndex + RUNTIME_LOG("[gif:submit] idx=" << debugIndex << " path=" << pathName(pathId) << " size=" << sizeBytes << " nloop=" << nloop << " flg=" << static_cast(flg) << " nreg=" << nreg << " directhl=" << static_cast(path2DirectHl ? 1u : 0u) - << std::endl; + << std::endl); } GifArbiterPacket pkt; @@ -109,7 +110,7 @@ void GifArbiter::drain() uint32_t nreg = static_cast((tagLo >> 60) & 0xFu); if (nreg == 0u) nreg = 16u; - std::cout << "[gif:drain] idx=" << debugIndex + RUNTIME_LOG("[gif:drain] idx=" << debugIndex << " path=" << pathName(pkt.pathId) << " size=" << pkt.data.size() << " nloop=" << nloop @@ -117,7 +118,7 @@ void GifArbiter::drain() << " nreg=" << nreg << " directhl=" << static_cast(pkt.path2DirectHl ? 1u : 0u) << " path3image=" << static_cast(pkt.path3Image ? 1u : 0u) - << std::endl; + << std::endl); } m_processFn(pkt.data.data(), static_cast(pkt.data.size())); } diff --git a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp index 56a2fdb9..12566cf4 100644 --- a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp @@ -1,8 +1,12 @@ -#include "ps2_gs_gpu.h" -#include "ps2_gs_common.h" -#include "ps2_gs_psmt4.h" -#include "ps2_gs_psmt8.h" -#include "ps2_memory.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_gs_common.h" +#include "runtime/ps2_gs_psmct16.h" +#include "runtime/ps2_gs_psmct32.h" +#include "runtime/ps2_gs_psmt4.h" +#include "runtime/ps2_gs_psmt8.h" +#include "ps2_log.h" +#include "ps2_syscalls.h" +#include "runtime/ps2_memory.h" #include #include #include @@ -13,6 +17,36 @@ namespace { + static constexpr uint32_t kDefaultDisplayWidth = 640u; + static constexpr uint32_t kDefaultDisplayHeight = 448u; + static constexpr uint32_t kHostFrameWidth = 640u; + static constexpr uint32_t kHostFrameHeight = 512u; + + uint16_t encodeFramePixelPSMCT16(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + return static_cast(((r >> 3) & 0x1Fu) | + (((g >> 3) & 0x1Fu) << 5) | + (((b >> 3) & 0x1Fu) << 10) | + ((a >= 0x40u) ? 0x8000u : 0u)); + } + + uint32_t addrPSMCT16Family(uint32_t basePtr, uint32_t width, uint8_t psm, uint32_t x, uint32_t y) + { + switch (psm) + { + case GS_PSM_CT16: + return GSPSMCT16::addrPSMCT16(basePtr, width, x, y); + case GS_PSM_CT16S: + return GSPSMCT16::addrPSMCT16S(basePtr, width, x, y); + case GS_PSM_Z16: + return GSPSMCT16::addrPSMZ16(basePtr, width, x, y); + case GS_PSM_Z16S: + return GSPSMCT16::addrPSMZ16S(basePtr, width, x, y); + default: + return 0u; + } + } + static inline uint64_t loadLE64(const uint8_t *p) { uint64_t v; @@ -20,6 +54,299 @@ namespace return v; } + void decodeDisplaySize(uint64_t display64, uint32_t &outWidth, uint32_t &outHeight) + { + const uint32_t dx = static_cast((display64 >> 0) & 0x0FFFu); + const uint32_t dy = static_cast((display64 >> 12) & 0x07FFu); + const uint32_t dw = static_cast((display64 >> 32) & 0x0FFFu); + const uint32_t dh = static_cast((display64 >> 44) & 0x07FFu); + const uint32_t magh = static_cast((display64 >> 23) & 0x0Fu); + + outWidth = (dw + 1u) / (magh + 1u); + outHeight = dh + 1u; + + if (outWidth < 64u || outHeight < 64u) + { + outWidth = kDefaultDisplayWidth; + outHeight = kDefaultDisplayHeight; + } + + outWidth = std::min(outWidth, kHostFrameWidth); + outHeight = std::min(outHeight, kHostFrameHeight); + } + + GSFrameReg decodeDisplayFrame(uint64_t dispfb64) + { + GSFrameReg frame{}; + frame.fbp = static_cast(dispfb64 & 0x1FFu); + frame.fbw = static_cast((dispfb64 >> 9) & 0x3Fu); + frame.psm = static_cast((dispfb64 >> 15) & 0x1Fu); + return frame; + } + + struct GSDisplayReadOrigin + { + uint32_t x = 0u; + uint32_t y = 0u; + }; + + GSDisplayReadOrigin decodeDisplayReadOrigin(uint64_t dispfb64) + { + GSDisplayReadOrigin origin{}; + origin.x = static_cast((dispfb64 >> 32) & 0x7FFu); + origin.y = static_cast((dispfb64 >> 43) & 0x7FFu); + return origin; + } + + bool hasDisplaySetup(uint64_t display64, const GSFrameReg &frame) + { + const uint32_t dw = static_cast((display64 >> 32) & 0x0FFFu); + const uint32_t dh = static_cast((display64 >> 44) & 0x07FFu); + const uint32_t magh = static_cast((display64 >> 23) & 0x0Fu); + return frame.fbw != 0u || dw != 0u || dh != 0u || magh != 0u; + } + + struct GSTransferTraversal + { + bool reverseX = false; + bool reverseY = false; + }; + + GSTransferTraversal decodeTransferTraversal(uint8_t dir) + { + GSTransferTraversal traversal{}; + switch (dir & 0x3u) + { + case 1u: + traversal.reverseY = true; + break; + case 2u: + traversal.reverseX = true; + break; + case 3u: + traversal.reverseX = true; + traversal.reverseY = true; + break; + default: + break; + } + return traversal; + } + + uint32_t transferCoord(uint32_t start, uint32_t extent, uint32_t index, bool reverse) + { + if (reverse && extent != 0u) + { + return start + (extent - 1u - index); + } + return start + index; + } + + struct GSPmodeState + { + bool enableCrt1 = false; + bool enableCrt2 = false; + bool mmod = false; + bool amod = false; + bool slbg = false; + uint8_t alp = 0u; + }; + + GSPmodeState decodePmode(uint64_t pmode64) + { + GSPmodeState pmode{}; + pmode.enableCrt1 = (pmode64 & 0x1ull) != 0ull; + pmode.enableCrt2 = (pmode64 & 0x2ull) != 0ull; + pmode.mmod = ((pmode64 >> 5) & 0x1ull) != 0ull; + pmode.amod = ((pmode64 >> 6) & 0x1ull) != 0ull; + pmode.slbg = ((pmode64 >> 7) & 0x1ull) != 0ull; + pmode.alp = static_cast((pmode64 >> 8) & 0xFFu); + return pmode; + } + + struct GSSmode2State + { + bool interlaced = false; + bool frameMode = true; + }; + + GSSmode2State decodeSMode2(uint64_t smode264) + { + GSSmode2State smode2{}; + smode2.interlaced = (smode264 & 0x1ull) != 0ull; + smode2.frameMode = ((smode264 >> 1) & 0x1ull) != 0ull; + return smode2; + } + + void applyFieldPresentation(std::vector &pixels, uint32_t width, uint32_t height, bool oddField) + { + if (pixels.empty() || width == 0u || height < 2u) + { + return; + } + + const std::vector source = pixels; + for (uint32_t y = 0; y < height; ++y) + { + uint32_t sourceY = ((y >> 1u) << 1u) + (oddField ? 1u : 0u); + if (sourceY >= height) + { + sourceY = height - 1u; + } + + const uint8_t *srcRow = source.data() + (sourceY * kHostFrameWidth * 4u); + uint8_t *dstRow = pixels.data() + (y * kHostFrameWidth * 4u); + std::memcpy(dstRow, srcRow, width * 4u); + } + } + + void normalizePresentationAlpha(std::vector &pixels, uint32_t width, uint32_t height) + { + if (pixels.empty() || width == 0u || height == 0u) + { + return; + } + + for (uint32_t y = 0; y < height; ++y) + { + uint8_t *row = pixels.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width; ++x) + { + row[x * 4u + 3u] = 255u; + } + } + } + + uint8_t blendPresentationChannel(uint8_t src, uint8_t dst, uint32_t factor) + { + const int delta = static_cast(src) - static_cast(dst); + return GSInternal::clampU8(static_cast(dst) + ((delta * static_cast(factor)) / 255)); + } + + uint32_t countNonBlackPixels(const std::vector &pixels, uint32_t width, uint32_t height) + { + uint32_t count = 0u; + for (uint32_t y = 0; y < height; ++y) + { + const uint8_t *row = pixels.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width; ++x) + { + const uint8_t r = row[x * 4u + 0u]; + const uint8_t g = row[x * 4u + 1u]; + const uint8_t b = row[x * 4u + 2u]; + if (r != 0u || g != 0u || b != 0u) + { + ++count; + } + } + } + return count; + } + + bool clearFramebufferRect(uint8_t *vram, + uint32_t vramSize, + const GSContext &ctx, + uint32_t rgba) + { + if (!vram || vramSize == 0u || ctx.frame.fbw == 0u) + { + return false; + } + + const uint32_t stride = GSInternal::fbStride(ctx.frame.fbw, ctx.frame.psm); + if (stride == 0u) + { + return false; + } + + const int x0 = std::max(0, ctx.scissor.x0); + const int x1 = std::max(x0, ctx.scissor.x1); + const int y0 = std::max(0, ctx.scissor.y0); + const int y1 = std::max(y0, ctx.scissor.y1); + const uint32_t base = ctx.frame.fbp * 8192u; + + uint8_t r = static_cast(rgba & 0xFFu); + uint8_t g = static_cast((rgba >> 8) & 0xFFu); + uint8_t b = static_cast((rgba >> 16) & 0xFFu); + uint8_t a = static_cast((rgba >> 24) & 0xFFu); + if ((ctx.fba & 0x1ull) != 0ull && ctx.frame.psm != GS_PSM_CT24) + { + a = static_cast(a | 0x80u); + } + + if (ctx.frame.psm == GS_PSM_CT32 || ctx.frame.psm == GS_PSM_CT24) + { + const uint32_t srcPixel = + static_cast(r) | + (static_cast(g) << 8) | + (static_cast(b) << 16) | + (static_cast(a) << 24); + const uint32_t widthBlocks = (ctx.frame.fbw != 0u) ? ctx.frame.fbw : 1u; + + for (int y = y0; y <= y1; ++y) + { + for (int x = x0; x <= x1; ++x) + { + const uint32_t off = + GSPSMCT32::addrPSMCT32(GSInternal::framePageBaseToBlock(ctx.frame.fbp), + widthBlocks, + static_cast(x), + static_cast(y)); + if (off + 4u > vramSize) + { + return true; + } + + uint32_t pixel = srcPixel; + if (ctx.frame.fbmsk != 0u) + { + uint32_t existing = 0u; + std::memcpy(&existing, vram + off, sizeof(existing)); + pixel = (pixel & ~ctx.frame.fbmsk) | (existing & ctx.frame.fbmsk); + } + std::memcpy(vram + off, &pixel, sizeof(pixel)); + } + } + return true; + } + + if (ctx.frame.psm == GS_PSM_CT16 || ctx.frame.psm == GS_PSM_CT16S) + { + const uint16_t srcPixel = encodeFramePixelPSMCT16(r, g, b, a); + const uint16_t mask = static_cast(ctx.frame.fbmsk & 0xFFFFu); + const uint32_t widthBlocks = (ctx.frame.fbw != 0u) ? ctx.frame.fbw : 1u; + const uint32_t basePtr = GSInternal::framePageBaseToBlock(ctx.frame.fbp); + + for (int y = y0; y <= y1; ++y) + { + for (int x = x0; x <= x1; ++x) + { + const uint32_t off = addrPSMCT16Family(basePtr, + widthBlocks, + ctx.frame.psm, + static_cast(x), + static_cast(y)); + if (off + 2u > vramSize) + { + return true; + } + + uint16_t pixel = srcPixel; + if (mask != 0u) + { + uint16_t existing = 0u; + std::memcpy(&existing, vram + off, sizeof(existing)); + pixel = static_cast((pixel & ~mask) | (existing & mask)); + } + std::memcpy(vram + off, &pixel, sizeof(pixel)); + } + } + return true; + } + + return false; + } + std::atomic s_debugGifPacketCount{0}; std::atomic s_debugGsRegisterCount{0}; std::atomic s_debugGsPackedVertexCount{0}; @@ -65,7 +392,7 @@ namespace case GS_PSM_CT32: case GS_PSM_Z32: { - const uint32_t off = base + ((y * width * 64u) + x) * 4u; + const uint32_t off = GSPSMCT32::addrPSMCT32(basePtr, width, x, y); if (off + 4u > vramSize) return 0u; uint32_t value = 0u; @@ -75,7 +402,7 @@ namespace case GS_PSM_CT24: case GS_PSM_Z24: { - const uint32_t off = base + ((y * width * 64u) + x) * 4u; + const uint32_t off = GSPSMCT32::addrPSMCT32(basePtr, width, x, y); if (off + 3u > vramSize) return 0u; return static_cast(vram[off + 0u]) | @@ -87,7 +414,7 @@ namespace case GS_PSM_Z16: case GS_PSM_Z16S: { - const uint32_t off = base + ((y * width * 64u) + x) * 2u; + const uint32_t off = addrPSMCT16Family(basePtr, width, psm, x, y); if (off + 2u > vramSize) return 0u; uint16_t value = 0u; @@ -130,7 +457,7 @@ namespace case GS_PSM_CT32: case GS_PSM_Z32: { - const uint32_t off = base + ((y * width * 64u) + x) * 4u; + const uint32_t off = GSPSMCT32::addrPSMCT32(basePtr, width, x, y); if (off + 4u > vramSize) return; std::memcpy(vram + off, &value, sizeof(value)); @@ -139,7 +466,7 @@ namespace case GS_PSM_CT24: case GS_PSM_Z24: { - const uint32_t off = base + ((y * width * 64u) + x) * 4u; + const uint32_t off = GSPSMCT32::addrPSMCT32(basePtr, width, x, y); if (off + 3u > vramSize) return; vram[off + 0u] = static_cast(value & 0xFFu); @@ -152,7 +479,7 @@ namespace case GS_PSM_Z16: case GS_PSM_Z16S: { - const uint32_t off = base + ((y * width * 64u) + x) * 2u; + const uint32_t off = addrPSMCT16Family(basePtr, width, psm, x, y); if (off + 2u > vramSize) return; const uint16_t value16 = static_cast(value & 0xFFFFu); @@ -203,6 +530,7 @@ void GS::init(uint8_t *vram, uint32_t vramSize, GSRegisters *privRegs) void GS::reset() { + std::lock_guard lock(m_stateMutex); std::memset(m_ctx, 0, sizeof(m_ctx)); m_prim = {}; m_curR = 0x80; @@ -216,6 +544,7 @@ void GS::reset() m_curV = 0; m_curFog = 0; m_prmodecont = true; + m_pabe = false; m_bitbltbuf = {}; m_trxpos = {}; m_trxreg = {}; @@ -226,6 +555,16 @@ void GS::reset() m_vtxIndex = 0; m_localToHostBuffer.clear(); m_localToHostReadPos = 0; + m_preferredDisplaySourceFrame = {}; + m_preferredDisplayDestFbp = 0; + m_hasPreferredDisplaySource = false; + m_hostPresentationFrame.clear(); + m_hostPresentationWidth = 0u; + m_hostPresentationHeight = 0u; + m_hostPresentationDisplayFbp = 0u; + m_hostPresentationSourceFbp = 0u; + m_hostPresentationUsedPreferred = false; + m_hasHostPresentationFrame = false; for (int i = 0; i < 2; ++i) { @@ -242,6 +581,7 @@ GSContext &GS::activeContext() void GS::snapshotVRAM() { + std::lock_guard stateLock(m_stateMutex); if (!m_vram || m_vramSize == 0) return; std::lock_guard lock(m_snapshotMutex); @@ -257,10 +597,26 @@ const uint8_t *GS::lockDisplaySnapshot(uint32_t &outSize) outSize = 0; return nullptr; } + outSize = static_cast(m_displaySnapshot.size()); return m_displaySnapshot.data(); } +bool GS::getPreferredDisplaySource(GSFrameReg &outSource, uint32_t &outDestFbp) const +{ + std::lock_guard lock(m_stateMutex); + if (!m_hasPreferredDisplaySource) + { + outSource = {}; + outDestFbp = 0u; + return false; + } + + outSource = m_preferredDisplaySourceFrame; + outDestFbp = m_preferredDisplayDestFbp; + return true; +} + void GS::unlockDisplaySnapshot() { m_snapshotMutex.unlock(); @@ -276,30 +632,476 @@ void GS::refreshDisplaySnapshot() snapshotVRAM(); } -void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes) +bool GS::copyFrameToHostRgbaUnlocked(const GSFrameReg &frame, + uint32_t width, + uint32_t height, + std::vector &outPixels, + bool preserveAlpha, + bool useLocalMemoryLayout, + bool frameBaseIsPages, + uint32_t sourceOriginX, + uint32_t sourceOriginY) const { - if (!data || sizeBytes < 16 || !m_vram) + if (!m_vram || m_vramSize == 0u) + { + return false; + } + + outPixels.assign(kHostFrameWidth * kHostFrameHeight * 4u, 0u); + + const uint32_t baseBytes = frameBaseIsPages ? (frame.fbp * 8192u) : (frame.fbp * 256u); + const uint32_t basePtr = frameBaseIsPages ? GSInternal::framePageBaseToBlock(frame.fbp) : frame.fbp; + const uint32_t fbwBlocks = frame.fbw ? frame.fbw : (kHostFrameWidth / 64u); + const uint32_t bytesPerPixel = (frame.psm == GS_PSM_CT16 || frame.psm == GS_PSM_CT16S) ? 2u : 4u; + const uint32_t strideBytes = fbwBlocks * 64u * bytesPerPixel; + + if (frame.psm == GS_PSM_CT32 || frame.psm == GS_PSM_CT24) + { + const uint32_t srcPixelBytes = (frame.psm == GS_PSM_CT24) ? 3u : 4u; + if (useLocalMemoryLayout) + { + for (uint32_t y = 0; y < height; ++y) + { + uint8_t *dstRow = outPixels.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width; ++x) + { + const uint32_t srcX = sourceOriginX + x; + const uint32_t srcY = sourceOriginY + y; + const uint32_t srcOff = GSPSMCT32::addrPSMCT32(basePtr, fbwBlocks, srcX, srcY); + if (srcOff + srcPixelBytes > m_vramSize) + { + return false; + } + + dstRow[x * 4u + 0u] = m_vram[srcOff + 0u]; + dstRow[x * 4u + 1u] = m_vram[srcOff + 1u]; + dstRow[x * 4u + 2u] = m_vram[srcOff + 2u]; + dstRow[x * 4u + 3u] = + (preserveAlpha && frame.psm != GS_PSM_CT24) ? m_vram[srcOff + 3u] : 255u; + } + } + return true; + } + + for (uint32_t y = 0; y < height; ++y) + { + const uint32_t dstOff = y * kHostFrameWidth * 4u; + uint8_t *dstRow = outPixels.data() + dstOff; + for (uint32_t x = 0; x < width; ++x) + { + const uint32_t srcX = sourceOriginX + x; + const uint32_t srcY = sourceOriginY + y; + const uint32_t srcOff = baseBytes + (srcY * strideBytes) + (srcX * srcPixelBytes); + if (srcOff + srcPixelBytes > m_vramSize) + { + return false; + } + + dstRow[x * 4u + 0u] = m_vram[srcOff + 0u]; + dstRow[x * 4u + 1u] = m_vram[srcOff + 1u]; + dstRow[x * 4u + 2u] = m_vram[srcOff + 2u]; + dstRow[x * 4u + 3u] = + (preserveAlpha && frame.psm != GS_PSM_CT24) ? m_vram[srcOff + 3u] : 255u; + } + } + return true; + } + + if (frame.psm == GS_PSM_CT16 || frame.psm == GS_PSM_CT16S) + { + if (useLocalMemoryLayout) + { + for (uint32_t y = 0; y < height; ++y) + { + const uint32_t dstOff = y * kHostFrameWidth * 4u; + uint8_t *dst = outPixels.data() + dstOff; + for (uint32_t x = 0; x < width; ++x) + { + const uint32_t srcX = sourceOriginX + x; + const uint32_t srcY = sourceOriginY + y; + const uint32_t srcOff = addrPSMCT16Family(basePtr, fbwBlocks, frame.psm, srcX, srcY); + if (srcOff + sizeof(uint16_t) > m_vramSize) + { + return false; + } + + uint16_t pixel = 0u; + std::memcpy(&pixel, m_vram + srcOff, sizeof(pixel)); + const uint32_t r = pixel & 31u; + const uint32_t g = (pixel >> 5) & 31u; + const uint32_t b = (pixel >> 10) & 31u; + dst[x * 4u + 0u] = static_cast((r << 3) | (r >> 2)); + dst[x * 4u + 1u] = static_cast((g << 3) | (g >> 2)); + dst[x * 4u + 2u] = static_cast((b << 3) | (b >> 2)); + dst[x * 4u + 3u] = preserveAlpha ? ((pixel & 0x8000u) ? 0x80u : 0x00u) : 255u; + } + } + return true; + } + + for (uint32_t y = 0; y < height; ++y) + { + const uint32_t dstOff = y * kHostFrameWidth * 4u; + uint8_t *dst = outPixels.data() + dstOff; + for (uint32_t x = 0; x < width; ++x) + { + const uint32_t srcX = sourceOriginX + x; + const uint32_t srcY = sourceOriginY + y; + const uint32_t srcOff = baseBytes + (srcY * strideBytes) + (srcX * 2u); + if (srcOff + sizeof(uint16_t) > m_vramSize) + { + return false; + } + + uint16_t pixel = 0u; + std::memcpy(&pixel, m_vram + srcOff, sizeof(pixel)); + const uint32_t r = pixel & 31u; + const uint32_t g = (pixel >> 5) & 31u; + const uint32_t b = (pixel >> 10) & 31u; + dst[x * 4u + 0u] = static_cast((r << 3) | (r >> 2)); + dst[x * 4u + 1u] = static_cast((g << 3) | (g >> 2)); + dst[x * 4u + 2u] = static_cast((b << 3) | (b >> 2)); + dst[x * 4u + 3u] = preserveAlpha ? ((pixel & 0x8000u) ? 0x80u : 0x00u) : 255u; + } + } + return true; + } + + return false; +} + +void GS::latchHostPresentationFrame() +{ + std::lock_guard lock(m_stateMutex); + + if (!m_privRegs || !m_vram || m_vramSize == 0u) + { + m_hostPresentationFrame.clear(); + m_hostPresentationWidth = 0u; + m_hostPresentationHeight = 0u; + m_hostPresentationDisplayFbp = 0u; + m_hostPresentationSourceFbp = 0u; + m_hostPresentationUsedPreferred = false; + m_hasHostPresentationFrame = false; return; + } - const uint32_t packetIndex = s_debugGifPacketCount.fetch_add(1, std::memory_order_relaxed); - if (packetIndex < 48u) + const GSPmodeState pmode = decodePmode(m_privRegs->pmode); + const GSSmode2State smode2 = decodeSMode2(m_privRegs->smode2); + const bool applyFieldMode = smode2.interlaced && !smode2.frameMode; + const bool oddField = (ps2_syscalls::GetCurrentVSyncTick() & 1ull) != 0ull; + const GSFrameReg displayFrame1 = decodeDisplayFrame(m_privRegs->dispfb1); + const GSFrameReg displayFrame2 = decodeDisplayFrame(m_privRegs->dispfb2); + const GSDisplayReadOrigin displayOrigin1 = decodeDisplayReadOrigin(m_privRegs->dispfb1); + const GSDisplayReadOrigin displayOrigin2 = decodeDisplayReadOrigin(m_privRegs->dispfb2); + + uint32_t width1 = 0u; + uint32_t height1 = 0u; + uint32_t width2 = 0u; + uint32_t height2 = 0u; + decodeDisplaySize(m_privRegs->display1, width1, height1); + decodeDisplaySize(m_privRegs->display2, width2, height2); + + const bool validCrt1 = pmode.enableCrt1 && hasDisplaySetup(m_privRegs->display1, displayFrame1); + const bool validCrt2 = pmode.enableCrt2 && hasDisplaySetup(m_privRegs->display2, displayFrame2); + + auto copyDisplaySource = [&](const GSFrameReg &displayFrame, + const GSDisplayReadOrigin &displayOrigin, + uint32_t width, + uint32_t height, + bool allowPreferred, + bool preserveAlpha, + GSFrameReg &selectedFrame, + std::vector &scratch, + bool &usedPreferred) -> bool { - const uint64_t tagLo = loadLE64(data); - const uint32_t nloop = static_cast(tagLo & 0x7FFFu); - const uint8_t flg = static_cast((tagLo >> 58) & 0x3u); - uint32_t nreg = static_cast((tagLo >> 60) & 0xFu); - if (nreg == 0u) - nreg = 16u; - std::cout << "[gs:gif] idx=" << packetIndex - << " size=" << sizeBytes - << " nloop=" << nloop - << " flg=" << static_cast(flg) - << " nreg=" << nreg - << " ctx0fbp=" << m_ctx[0].frame.fbp - << " ctx1fbp=" << m_ctx[1].frame.fbp - << std::endl; + selectedFrame = displayFrame; + scratch.clear(); + usedPreferred = false; + + if (allowPreferred && + m_hasPreferredDisplaySource && + m_preferredDisplayDestFbp == displayFrame.fbp && + (m_preferredDisplaySourceFrame.fbw != 0u || m_preferredDisplaySourceFrame.fbp != displayFrame.fbp)) + { + if (copyFrameToHostRgbaUnlocked(m_preferredDisplaySourceFrame, + width, + height, + scratch, + preserveAlpha, + true, + false, + 0u, + 0u)) + { + selectedFrame = m_preferredDisplaySourceFrame; + usedPreferred = true; + } + } + + if (scratch.empty() && + !copyFrameToHostRgbaUnlocked(displayFrame, + width, + height, + scratch, + preserveAlpha, + true, + true, + displayOrigin.x, + displayOrigin.y)) + { + return false; + } + + if (!usedPreferred && displayFrame.fbp == 0u && countNonBlackPixels(scratch, width, height) == 0u) + { + for (int contextIndex = 0; contextIndex < 2; ++contextIndex) + { + const GSFrameReg &candidate = m_ctx[contextIndex].frame; + if (candidate.fbp == selectedFrame.fbp && + candidate.fbw == selectedFrame.fbw && + candidate.psm == selectedFrame.psm) + { + continue; + } + + std::vector candidatePixels; + if (!copyFrameToHostRgbaUnlocked(candidate, + width, + height, + candidatePixels, + preserveAlpha, + true, + true, + 0u, + 0u)) + { + continue; + } + + if (countNonBlackPixels(candidatePixels, width, height) == 0u) + { + continue; + } + + selectedFrame = candidate; + scratch.swap(candidatePixels); + break; + } + } + + return true; + }; + + if (!validCrt1 && !validCrt2) + { + m_hostPresentationFrame.clear(); + m_hostPresentationWidth = 0u; + m_hostPresentationHeight = 0u; + m_hostPresentationDisplayFbp = 0u; + m_hostPresentationSourceFbp = 0u; + m_hostPresentationUsedPreferred = false; + m_hasHostPresentationFrame = false; + return; } + if (validCrt1 && validCrt2) + { + GSFrameReg selectedFrame1{}; + GSFrameReg selectedFrame2{}; + std::vector rc1; + std::vector rc2; + bool usedPreferred1 = false; + bool usedPreferred2 = false; + + const bool copiedCrt1 = copyDisplaySource(displayFrame1, displayOrigin1, width1, height1, false, true, selectedFrame1, rc1, usedPreferred1); + const bool copiedCrt2 = copyDisplaySource(displayFrame2, displayOrigin2, width2, height2, false, true, selectedFrame2, rc2, usedPreferred2); + + if (copiedCrt1 && copiedCrt2) + { + const uint32_t width = std::max(width1, width2); + const uint32_t height = std::max(height1, height2); + const uint8_t bgR = static_cast(m_privRegs->bgcolor & 0xFFu); + const uint8_t bgG = static_cast((m_privRegs->bgcolor >> 8) & 0xFFu); + const uint8_t bgB = static_cast((m_privRegs->bgcolor >> 16) & 0xFFu); + const uint8_t bgA = pmode.alp; + + std::vector merged(kHostFrameWidth * kHostFrameHeight * 4u, 0u); + for (uint32_t y = 0; y < height; ++y) + { + uint8_t *dstRow = merged.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width; ++x) + { + dstRow[x * 4u + 0u] = bgR; + dstRow[x * 4u + 1u] = bgG; + dstRow[x * 4u + 2u] = bgB; + dstRow[x * 4u + 3u] = bgA; + } + } + + if (!pmode.slbg) + { + for (uint32_t y = 0; y < height2; ++y) + { + const uint8_t *srcRow = rc2.data() + (y * kHostFrameWidth * 4u); + uint8_t *dstRow = merged.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width2; ++x) + { + dstRow[x * 4u + 0u] = srcRow[x * 4u + 0u]; + dstRow[x * 4u + 1u] = srcRow[x * 4u + 1u]; + dstRow[x * 4u + 2u] = srcRow[x * 4u + 2u]; + dstRow[x * 4u + 3u] = srcRow[x * 4u + 3u]; + } + } + } + + for (uint32_t y = 0; y < height1; ++y) + { + const uint8_t *srcRow = rc1.data() + (y * kHostFrameWidth * 4u); + uint8_t *dstRow = merged.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width1; ++x) + { + const uint8_t srcR = srcRow[x * 4u + 0u]; + const uint8_t srcG = srcRow[x * 4u + 1u]; + const uint8_t srcB = srcRow[x * 4u + 2u]; + const uint8_t srcA = srcRow[x * 4u + 3u]; + const uint8_t dstR = dstRow[x * 4u + 0u]; + const uint8_t dstG = dstRow[x * 4u + 1u]; + const uint8_t dstB = dstRow[x * 4u + 2u]; + const uint8_t dstA = dstRow[x * 4u + 3u]; + const uint32_t factor = pmode.mmod + ? static_cast(pmode.alp) + : std::min(255u, static_cast(srcA) * 2u); + + dstRow[x * 4u + 0u] = blendPresentationChannel(srcR, dstR, factor); + dstRow[x * 4u + 1u] = blendPresentationChannel(srcG, dstG, factor); + dstRow[x * 4u + 2u] = blendPresentationChannel(srcB, dstB, factor); + dstRow[x * 4u + 3u] = pmode.amod ? dstA : srcA; + } + } + + for (uint32_t y = 0; y < height; ++y) + { + uint8_t *row = merged.data() + (y * kHostFrameWidth * 4u); + for (uint32_t x = 0; x < width; ++x) + { + row[x * 4u + 3u] = 255u; + } + } + + if (applyFieldMode) + { + applyFieldPresentation(merged, width, height, oddField); + } + + m_hostPresentationFrame.swap(merged); + m_hostPresentationWidth = width; + m_hostPresentationHeight = height; + m_hostPresentationDisplayFbp = displayFrame1.fbp; + m_hostPresentationSourceFbp = selectedFrame1.fbp; + m_hostPresentationUsedPreferred = false; + m_hasHostPresentationFrame = true; + return; + } + } + + const GSFrameReg &displayFrame = validCrt1 ? displayFrame1 : displayFrame2; + const uint32_t width = validCrt1 ? width1 : width2; + const uint32_t height = validCrt1 ? height1 : height2; + + GSFrameReg selectedFrame = displayFrame; + std::vector scratch; + bool usedPreferred = false; + const GSDisplayReadOrigin &displayOrigin = validCrt1 ? displayOrigin1 : displayOrigin2; + if (!copyDisplaySource(displayFrame, displayOrigin, width, height, true, false, selectedFrame, scratch, usedPreferred)) + { + m_hostPresentationFrame.clear(); + m_hostPresentationWidth = 0u; + m_hostPresentationHeight = 0u; + m_hostPresentationDisplayFbp = displayFrame.fbp; + m_hostPresentationSourceFbp = 0u; + m_hostPresentationUsedPreferred = false; + m_hasHostPresentationFrame = false; + return; + } + + if (applyFieldMode) + { + applyFieldPresentation(scratch, width, height, oddField); + } + + normalizePresentationAlpha(scratch, width, height); + + m_hostPresentationFrame.swap(scratch); + m_hostPresentationWidth = width; + m_hostPresentationHeight = height; + m_hostPresentationDisplayFbp = displayFrame.fbp; + m_hostPresentationSourceFbp = selectedFrame.fbp; + m_hostPresentationUsedPreferred = usedPreferred; + m_hasHostPresentationFrame = true; +} + +bool GS::copyLatchedHostPresentationFrame(std::vector &outPixels, + uint32_t &outWidth, + uint32_t &outHeight, + uint32_t *outDisplayFbp, + uint32_t *outSourceFbp, + bool *outUsedPreferred) const +{ + std::lock_guard lock(m_stateMutex); + if (!m_hasHostPresentationFrame || m_hostPresentationFrame.empty()) + { + outPixels.clear(); + outWidth = 0u; + outHeight = 0u; + if (outDisplayFbp) + *outDisplayFbp = 0u; + if (outSourceFbp) + *outSourceFbp = 0u; + if (outUsedPreferred) + *outUsedPreferred = false; + return false; + } + + outPixels = m_hostPresentationFrame; + outWidth = m_hostPresentationWidth; + outHeight = m_hostPresentationHeight; + if (outDisplayFbp) + *outDisplayFbp = m_hostPresentationDisplayFbp; + if (outSourceFbp) + *outSourceFbp = m_hostPresentationSourceFbp; + if (outUsedPreferred) + *outUsedPreferred = m_hostPresentationUsedPreferred; + return true; +} + +void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes) +{ + std::lock_guard lock(m_stateMutex); + if (!data || sizeBytes < 16 || !m_vram) + return; + + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t packetIndex = s_debugGifPacketCount.fetch_add(1, std::memory_order_relaxed); + if (packetIndex < 48u) + { + const uint64_t tagLo = loadLE64(data); + const uint32_t nloop = static_cast(tagLo & 0x7FFFu); + const uint8_t flg = static_cast((tagLo >> 58) & 0x3u); + uint32_t nreg = static_cast((tagLo >> 60) & 0xFu); + if (nreg == 0u) + nreg = 16u; + RUNTIME_LOG("[gs:gif] idx=" << packetIndex + << " size=" << sizeBytes + << " nloop=" << nloop + << " flg=" << static_cast(flg) + << " nreg=" << nreg + << " ctx0fbp=" << m_ctx[0].frame.fbp + << " ctx1fbp=" << m_ctx[1].frame.fbp + << std::endl); + } + }); + if (sizeBytes >= 16) { const uint64_t tagLo = loadLE64(data); @@ -413,19 +1215,21 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) uint32_t z = static_cast((hi >> 4) & 0xFFFFFF); uint8_t f = static_cast((hi >> 36) & 0xFF); bool adk = ((hi >> 47) & 1) != 0; - const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); - if (debugIndex < 64u) - { - std::cout << "[gs:packed-xyzf] idx=" << debugIndex - << " x=" << x - << " y=" << y - << " z=0x" << std::hex << z - << std::dec - << " fog=" << static_cast(f) - << " kick=" << static_cast(!adk ? 1u : 0u) - << " prim=" << static_cast(m_prim.type) - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); + if (debugIndex < 64u) + { + RUNTIME_LOG("[gs:packed-xyzf] idx=" << debugIndex + << " x=" << x + << " y=" << y + << " z=0x" << std::hex << z + << std::dec + << " fog=" << static_cast(f) + << " kick=" << static_cast(!adk ? 1u : 0u) + << " prim=" << static_cast(m_prim.type) + << std::endl); + } + }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; vtx.x = static_cast(x) / 16.0f; vtx.y = static_cast(y) / 16.0f; @@ -449,18 +1253,20 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) uint16_t y = static_cast((lo >> 32) & 0xFFFF); uint32_t z = static_cast(hi & 0xFFFFFFFF); bool adk = ((hi >> 47) & 1) != 0; - const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); - if (debugIndex < 64u) - { - std::cout << "[gs:packed-xyz] idx=" << debugIndex - << " x=" << x - << " y=" << y - << " z=0x" << std::hex << z - << std::dec - << " kick=" << static_cast(!adk ? 1u : 0u) - << " prim=" << static_cast(m_prim.type) - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); + if (debugIndex < 64u) + { + RUNTIME_LOG("[gs:packed-xyz] idx=" << debugIndex + << " x=" << x + << " y=" << y + << " z=0x" << std::hex << z + << std::dec + << " kick=" << static_cast(!adk ? 1u : 0u) + << " prim=" << static_cast(m_prim.type) + << std::endl); + } + }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; vtx.x = static_cast(x) / 16.0f; vtx.y = static_cast(y) / 16.0f; @@ -483,16 +1289,18 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) break; case 0x0C: { - const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); - if (debugIndex < 64u) - { - std::cout << "[gs:packed-xyzf3] idx=" << debugIndex - << " x=" << static_cast(lo & 0xFFFFu) - << " y=" << static_cast((lo >> 32) & 0xFFFFu) - << " kick=0" - << " prim=" << static_cast(m_prim.type) - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); + if (debugIndex < 64u) + { + RUNTIME_LOG("[gs:packed-xyzf3] idx=" << debugIndex + << " x=" << static_cast(lo & 0xFFFFu) + << " y=" << static_cast((lo >> 32) & 0xFFFFu) + << " kick=0" + << " prim=" << static_cast(m_prim.type) + << std::endl); + } + }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; vtx.x = static_cast(lo & 0xFFFF) / 16.0f; vtx.y = static_cast((lo >> 32) & 0xFFFF) / 16.0f; @@ -512,16 +1320,18 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) } case 0x0D: { - const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); - if (debugIndex < 64u) - { - std::cout << "[gs:packed-xyz3] idx=" << debugIndex - << " x=" << static_cast(lo & 0xFFFFu) - << " y=" << static_cast((lo >> 32) & 0xFFFFu) - << " kick=0" - << " prim=" << static_cast(m_prim.type) - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t debugIndex = s_debugGsPackedVertexCount.fetch_add(1, std::memory_order_relaxed); + if (debugIndex < 64u) + { + RUNTIME_LOG("[gs:packed-xyz3] idx=" << debugIndex + << " x=" << static_cast(lo & 0xFFFFu) + << " y=" << static_cast((lo >> 32) & 0xFFFFu) + << " kick=0" + << " prim=" << static_cast(m_prim.type) + << std::endl); + } + }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; vtx.x = static_cast(lo & 0xFFFF) / 16.0f; vtx.y = static_cast((lo >> 32) & 0xFFFF) / 16.0f; @@ -555,6 +1365,7 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) void GS::writeRegister(uint8_t regAddr, uint64_t value) { + std::lock_guard lock(m_stateMutex); const bool interestingReg = regAddr == GS_REG_PRIM || regAddr == GS_REG_RGBAQ || @@ -581,18 +1392,20 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) regAddr == GS_REG_TRXREG || regAddr == GS_REG_TRXDIR; - if (interestingReg) - { - const uint32_t debugIndex = s_debugGsRegisterCount.fetch_add(1, std::memory_order_relaxed); - if (debugIndex < 128u) + PS2_IF_AGRESSIVE_LOGS({ + if (interestingReg) { - std::cout << "[gs:reg] idx=" << debugIndex - << " reg=0x" << std::hex << static_cast(regAddr) - << " value=0x" << value - << std::dec - << std::endl; + const uint32_t debugIndex = s_debugGsRegisterCount.fetch_add(1, std::memory_order_relaxed); + if (debugIndex < 128u) + { + RUNTIME_LOG("[gs:reg] idx=" << debugIndex + << " reg=0x" << std::hex << static_cast(regAddr) + << " value=0x" << value + << std::dec + << std::endl); + } } - } + }); const bool isCopyRelevantReg = regAddr == GS_REG_PRIM || @@ -600,21 +1413,24 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) regAddr == GS_REG_TEX1_2 || regAddr == GS_REG_ALPHA_2 || regAddr == GS_REG_TEST_2 || + regAddr == GS_REG_PABE || regAddr == GS_REG_FRAME_2 || regAddr == GS_REG_XYOFFSET_2 || regAddr == GS_REG_SCISSOR_2; - if (isCopyRelevantReg && - s_debugCopyRegCount.fetch_add(1u, std::memory_order_relaxed) < 64u) - { - std::cout << "[gs:copy-reg] reg=0x" - << std::hex << static_cast(regAddr) - << " value=0x" << value - << std::dec - << " primCtxt=" << static_cast(m_prim.ctxt) - << " ctx0fbp=" << m_ctx[0].frame.fbp - << " ctx1fbp=" << m_ctx[1].frame.fbp - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + if (isCopyRelevantReg && + s_debugCopyRegCount.fetch_add(1u, std::memory_order_relaxed) < 64u) + { + RUNTIME_LOG("[gs:copy-reg] reg=0x" + << std::hex << static_cast(regAddr) + << " value=0x" << value + << std::dec + << " primCtxt=" << static_cast(m_prim.ctxt) + << " ctx0fbp=" << m_ctx[0].frame.fbp + << " ctx1fbp=" << m_ctx[1].frame.fbp + << std::endl); + } + }); switch (regAddr) { @@ -858,6 +1674,9 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) processImageData(buf, 8); break; } + case GS_REG_PABE: + m_pabe = (value & 1u) != 0u; + break; case GS_REG_TEXFLUSH: case GS_REG_TEXCLUT: case GS_REG_SCANMSK: @@ -865,7 +1684,6 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) case GS_REG_DIMX: case GS_REG_DTHE: case GS_REG_COLCLAMP: - case GS_REG_PABE: case GS_REG_MIPTBP1_1: case GS_REG_MIPTBP1_2: case GS_REG_MIPTBP2_1: @@ -873,17 +1691,19 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) break; case GS_REG_TEXA: { - const uint32_t texaIndex = s_debugTexaWriteCount.fetch_add(1u, std::memory_order_relaxed); - if (texaIndex < 24u) - { - std::cout << "[gs:texa] idx=" << texaIndex - << " value=0x" << std::hex << value - << " ta0=0x" << ((value >> 0) & 0xFFu) - << " aem=" << ((value >> 15) & 0x1u) - << " ta1=0x" << ((value >> 32) & 0xFFu) - << std::dec - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t texaIndex = s_debugTexaWriteCount.fetch_add(1u, std::memory_order_relaxed); + if (texaIndex < 24u) + { + RUNTIME_LOG("[gs:texa] idx=" << texaIndex + << " value=0x" << std::hex << value + << " ta0=0x" << ((value >> 0) & 0xFFu) + << " aem=" << ((value >> 15) & 0x1u) + << " ta1=0x" << ((value >> 32) & 0xFFu) + << std::dec + << std::endl); + } + }); break; } case GS_REG_SIGNAL: @@ -925,6 +1745,14 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) if (m_privRegs) m_privRegs->display1 = value; break; + case 0x5b: + if (m_privRegs) + m_privRegs->dispfb2 = value; + break; + case 0x5c: + if (m_privRegs) + m_privRegs->display2 = value; + break; case 0x5f: if (m_privRegs) m_privRegs->bgcolor = value; @@ -957,42 +1785,45 @@ void GS::performLocalToLocalTransfer() const uint32_t ssay = m_trxpos.ssay; const uint32_t dsax = m_trxpos.dsax; const uint32_t dsay = m_trxpos.dsay; + const GSTransferTraversal traversal = decodeTransferTraversal(m_trxpos.dir); const bool formatAware = (spsm == dpsm) && supportsFormatAwareLocalCopy(spsm); - if ((spsm == GS_PSM_T4 || dpsm == GS_PSM_T4) && - s_debugLocalCopyCount.fetch_add(1u, std::memory_order_relaxed) < 96u) + if (rrw == 0u || rrh == 0u) { - std::cout << "[gs:l2l] sbp=" << sbp - << " dbp=" << dbp - << " sbw=" << static_cast(sbw) - << " dbw=" << static_cast(dbw) - << " spsm=0x" << std::hex << static_cast(spsm) - << " dpsm=0x" << static_cast(dpsm) << std::dec - << " ss=(" << ssax << "," << ssay << ")" - << " ds=(" << dsax << "," << dsay << ")" - << " rr=(" << rrw << "," << rrh << ")" - << " formatAware=" << (formatAware ? 1 : 0) << std::endl; + return; } - if (formatAware) - { - std::vector staged; - staged.reserve(static_cast(rrw) * static_cast(rrh)); - - for (uint32_t row = 0; row < rrh; ++row) + PS2_IF_AGRESSIVE_LOGS({ + if ((spsm == GS_PSM_T4 || dpsm == GS_PSM_T4) && + s_debugLocalCopyCount.fetch_add(1u, std::memory_order_relaxed) < 96u) { - for (uint32_t col = 0; col < rrw; ++col) - { - staged.push_back(readTransferPixel(m_vram, m_vramSize, sbp, sbw, spsm, ssax + col, ssay + row)); - } + RUNTIME_LOG("[gs:l2l] sbp=" << sbp + << " dbp=" << dbp + << " sbw=" << static_cast(sbw) + << " dbw=" << static_cast(dbw) + << " spsm=0x" << std::hex << static_cast(spsm) + << " dpsm=0x" << static_cast(dpsm) << std::dec + << " ss=(" << ssax << "," << ssay << ")" + << " ds=(" << dsax << "," << dsay << ")" + << " rr=(" << rrw << "," << rrh << ")" + << " dir=" << static_cast(m_trxpos.dir) + << " formatAware=" << (formatAware ? 1 : 0) << std::endl); } + }); - size_t idx = 0; + if (formatAware) + { for (uint32_t row = 0; row < rrh; ++row) { - for (uint32_t col = 0; col < rrw; ++col, ++idx) + const uint32_t srcY = transferCoord(ssay, rrh, row, traversal.reverseY); + const uint32_t dstY = transferCoord(dsay, rrh, row, traversal.reverseY); + for (uint32_t col = 0; col < rrw; ++col) { - writeTransferPixel(m_vram, m_vramSize, dbp, dbw, dpsm, dsax + col, dsay + row, staged[idx]); + const uint32_t srcX = transferCoord(ssax, rrw, col, traversal.reverseX); + const uint32_t dstX = transferCoord(dsax, rrw, col, traversal.reverseX); + const uint32_t pixel = + readTransferPixel(m_vram, m_vramSize, sbp, sbw, spsm, srcX, srcY); + writeTransferPixel(m_vram, m_vramSize, dbp, dbw, dpsm, dstX, dstY, pixel); } } } @@ -1009,26 +1840,25 @@ void GS::performLocalToLocalTransfer() const uint32_t srcStride = static_cast(sbw) * 64u * srcBpp; const uint32_t dstStride = static_cast(dbw) * 64u * dstBpp; const uint32_t copyBpp = (srcBpp < dstBpp) ? srcBpp : dstBpp; - const uint32_t rowBytes = rrw * copyBpp; - if (dstBase > srcBase) - { - for (int row = static_cast(rrh) - 1; row >= 0; --row) - { - const uint32_t srcOff = srcBase + (ssay + static_cast(row)) * srcStride + ssax * srcBpp; - const uint32_t dstOff = dstBase + (dsay + static_cast(row)) * dstStride + dsax * dstBpp; - if (srcOff + rowBytes <= m_vramSize && dstOff + rowBytes <= m_vramSize) - std::memmove(m_vram + dstOff, m_vram + srcOff, rowBytes); - } - } - else + uint8_t pixelBytes[4] = {}; + for (uint32_t row = 0; row < rrh; ++row) { - for (uint32_t row = 0; row < rrh; ++row) + const uint32_t srcY = transferCoord(ssay, rrh, row, traversal.reverseY); + const uint32_t dstY = transferCoord(dsay, rrh, row, traversal.reverseY); + for (uint32_t col = 0; col < rrw; ++col) { - const uint32_t srcOff = srcBase + (ssay + row) * srcStride + ssax * srcBpp; - const uint32_t dstOff = dstBase + (dsay + row) * dstStride + dsax * dstBpp; - if (srcOff + rowBytes <= m_vramSize && dstOff + rowBytes <= m_vramSize) - std::memmove(m_vram + dstOff, m_vram + srcOff, rowBytes); + const uint32_t srcX = transferCoord(ssax, rrw, col, traversal.reverseX); + const uint32_t dstX = transferCoord(dsax, rrw, col, traversal.reverseX); + const uint32_t srcOff = srcBase + srcY * srcStride + srcX * srcBpp; + const uint32_t dstOff = dstBase + dstY * dstStride + dstX * dstBpp; + if (srcOff + copyBpp > m_vramSize || dstOff + copyBpp > m_vramSize) + { + continue; + } + + std::memcpy(pixelBytes, m_vram + srcOff, copyBpp); + std::memcpy(m_vram + dstOff, pixelBytes, copyBpp); } } } @@ -1045,15 +1875,17 @@ void GS::vertexKick(bool drawing) ++m_vtxCount; ++m_vtxIndex; - const uint32_t debugIndex = s_debugGsVertexKickCount.fetch_add(1, std::memory_order_relaxed); - if (debugIndex < 96u) - { - std::cout << "[gs:kick] idx=" << debugIndex - << " drawing=" << static_cast(drawing ? 1u : 0u) - << " prim=" << static_cast(m_prim.type) - << " vtxCount=" << m_vtxCount - << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t debugIndex = s_debugGsVertexKickCount.fetch_add(1, std::memory_order_relaxed); + if (debugIndex < 96u) + { + RUNTIME_LOG("[gs:kick] idx=" << debugIndex + << " drawing=" << static_cast(drawing ? 1u : 0u) + << " prim=" << static_cast(m_prim.type) + << " vtxCount=" << m_vtxCount + << std::endl); + } + }); if (!drawing) return; @@ -1217,14 +2049,11 @@ void GS::processImageData(const uint8_t *data, uint32_t sizeBytes) } else if (dpsm == GS_PSM_CT24 || dpsm == GS_PSM_Z24) { - uint32_t storageBpp = 4; uint32_t transferBpp = 3; - uint32_t storageStride = stridePixels * storageBpp; uint32_t offset = 0; while (offset < sizeBytes && m_hwregY < rrh) { - uint32_t dstY = dsay + m_hwregY; uint32_t pixelsLeft = rrw - m_hwregX; uint32_t srcBytesLeft = pixelsLeft * transferBpp; uint32_t bytesAvail = sizeBytes - offset; @@ -1235,15 +2064,20 @@ void GS::processImageData(const uint8_t *data, uint32_t sizeBytes) if (pixelsToCopy == 0) break; - uint32_t dstOff = base + dstY * storageStride + (dsax + m_hwregX) * storageBpp; - if (dstOff + pixelsToCopy * storageBpp <= m_vramSize && pixelsToCopy > 0) + if (pixelsToCopy > 0) { for (uint32_t p = 0; p < pixelsToCopy; ++p) { - m_vram[dstOff + p * 4 + 0] = data[offset + p * 3 + 0]; - m_vram[dstOff + p * 4 + 1] = data[offset + p * 3 + 1]; - m_vram[dstOff + p * 4 + 2] = data[offset + p * 3 + 2]; - m_vram[dstOff + p * 4 + 3] = 0x80; + const uint32_t vx = dsax + m_hwregX + p; + const uint32_t vy = dsay + m_hwregY; + const uint32_t dstOff = GSPSMCT32::addrPSMCT32(dbp, dbw, vx, vy); + if (dstOff + 4u <= m_vramSize) + { + m_vram[dstOff + 0u] = data[offset + p * 3u + 0u]; + m_vram[dstOff + 1u] = data[offset + p * 3u + 1u]; + m_vram[dstOff + 2u] = data[offset + p * 3u + 2u]; + m_vram[dstOff + 3u] = 0x80u; + } } } @@ -1274,11 +2108,25 @@ void GS::processImageData(const uint8_t *data, uint32_t sizeBytes) if (bytesLeft > bytesAvail) bytesLeft = (bytesAvail / bytesPerPixel) * bytesPerPixel; - uint32_t dstOff = base + dstY * strideBytes + (dsax + m_hwregX) * bytesPerPixel; - if (dstOff + bytesLeft <= m_vramSize && bytesLeft > 0) - std::memcpy(m_vram + dstOff, data + offset, bytesLeft); - uint32_t pixelsCopied = bytesLeft / bytesPerPixel; + if ((dpsm == GS_PSM_CT32 || dpsm == GS_PSM_Z32) && pixelsCopied > 0) + { + for (uint32_t p = 0; p < pixelsCopied; ++p) + { + const uint32_t vx = dsax + m_hwregX + p; + const uint32_t vy = dstY; + const uint32_t dstOff = GSPSMCT32::addrPSMCT32(dbp, dbw, vx, vy); + if (dstOff + 4u <= m_vramSize) + std::memcpy(m_vram + dstOff, data + offset + p * 4u, 4u); + } + } + else + { + uint32_t dstOff = base + dstY * strideBytes + (dsax + m_hwregX) * bytesPerPixel; + if (dstOff + bytesLeft <= m_vramSize && bytesLeft > 0) + std::memcpy(m_vram + dstOff, data + offset, bytesLeft); + } + offset += bytesLeft; m_hwregX += pixelsCopied; if (m_hwregX >= rrw) @@ -1354,16 +2202,14 @@ void GS::performLocalToHostToBuffer() } else if (spsm == GS_PSM_CT24 || spsm == GS_PSM_Z24) { - uint32_t storageBpp = 4; uint32_t transferBpp = 3; - uint32_t storageStride = stridePixels * storageBpp; m_localToHostBuffer.reserve(rrw * rrh * transferBpp); for (uint32_t y = 0; y < rrh; ++y) { for (uint32_t x = 0; x < rrw; ++x) { - uint32_t srcOff = base + (ssay + y) * storageStride + (ssax + x) * storageBpp; + uint32_t srcOff = GSPSMCT32::addrPSMCT32(sbp, sbw, ssax + x, ssay + y); if (srcOff + 4 <= m_vramSize) { m_localToHostBuffer.push_back(m_vram[srcOff + 0]); @@ -1384,18 +2230,48 @@ void GS::performLocalToHostToBuffer() for (uint32_t y = 0; y < rrh; ++y) { - uint32_t srcOff = base + (ssay + y) * strideBytes + ssax * bytesPerPixel; - if (srcOff + rowBytes <= m_vramSize) + if (spsm == GS_PSM_CT32 || spsm == GS_PSM_Z32) + { + for (uint32_t x = 0; x < rrw; ++x) + { + const uint32_t srcOff = GSPSMCT32::addrPSMCT32(sbp, sbw, ssax + x, ssay + y); + if (srcOff + 4u <= m_vramSize) + { + m_localToHostBuffer.push_back(m_vram[srcOff + 0u]); + m_localToHostBuffer.push_back(m_vram[srcOff + 1u]); + m_localToHostBuffer.push_back(m_vram[srcOff + 2u]); + m_localToHostBuffer.push_back(m_vram[srcOff + 3u]); + } + } + } + else { - for (uint32_t i = 0; i < rowBytes; ++i) - m_localToHostBuffer.push_back(m_vram[srcOff + i]); + uint32_t srcOff = base + (ssay + y) * strideBytes + ssax * bytesPerPixel; + if (srcOff + rowBytes <= m_vramSize) + { + for (uint32_t i = 0; i < rowBytes; ++i) + m_localToHostBuffer.push_back(m_vram[srcOff + i]); + } } } } } +bool GS::clearFramebufferContext(uint32_t contextIndex, uint32_t rgba) +{ + std::lock_guard lock(m_stateMutex); + return clearFramebufferRect(m_vram, m_vramSize, m_ctx[(contextIndex != 0u) ? 1 : 0], rgba); +} + +bool GS::clearActiveFramebuffer(uint32_t rgba) +{ + std::lock_guard lock(m_stateMutex); + return clearFramebufferRect(m_vram, m_vramSize, activeContext(), rgba); +} + uint32_t GS::consumeLocalToHostBytes(uint8_t *dst, uint32_t maxBytes) { + std::lock_guard lock(m_stateMutex); if (!dst || maxBytes == 0) return 0; size_t avail = m_localToHostBuffer.size() - m_localToHostReadPos; diff --git a/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp b/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp index f6658ee9..562345c4 100644 --- a/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp @@ -1,8 +1,11 @@ -#include "ps2_gs_rasterizer.h" -#include "ps2_gs_gpu.h" -#include "ps2_gs_common.h" -#include "ps2_gs_psmt4.h" -#include "ps2_gs_psmt8.h" +#include "runtime/ps2_gs_rasterizer.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_gs_common.h" +#include "runtime/ps2_gs_psmct16.h" +#include "runtime/ps2_gs_psmct32.h" +#include "runtime/ps2_gs_psmt4.h" +#include "runtime/ps2_gs_psmt8.h" +#include "ps2_log.h" #include #include #include @@ -38,11 +41,27 @@ namespace ((a >= 0x40u) ? 0x8000u : 0u)); } + uint32_t addrPSMCT16Family(uint32_t basePtr, uint32_t width, uint8_t psm, uint32_t x, uint32_t y) + { + switch (psm) + { + case GS_PSM_CT16: + return GSPSMCT16::addrPSMCT16(basePtr, width, x, y); + case GS_PSM_CT16S: + return GSPSMCT16::addrPSMCT16S(basePtr, width, x, y); + case GS_PSM_Z16: + return GSPSMCT16::addrPSMZ16(basePtr, width, x, y); + case GS_PSM_Z16S: + return GSPSMCT16::addrPSMZ16S(basePtr, width, x, y); + default: + return 0u; + } + } + std::atomic s_debugPrimitiveCount{0}; std::atomic s_debugPixelCount{0}; std::atomic s_debugContext1PrimitiveCount{0}; std::atomic s_debugFbp150PixelCount{0}; - bool passesAlphaTest(uint64_t testReg, uint8_t alpha) { if ((testReg & 0x1u) == 0u) @@ -187,102 +206,124 @@ namespace return clutIndex; } -} -void GSRasterizer::drawPrimitive(GS *gs) -{ - const uint32_t primitiveIndex = s_debugPrimitiveCount.fetch_add(1, std::memory_order_relaxed); - if (primitiveIndex < 64u) + bool tex1UsesLinearFilter(uint64_t tex1) { - const auto &ctx = gs->activeContext(); - std::cout << "[gs:prim] idx=" << primitiveIndex - << " type=" << static_cast(gs->m_prim.type) - << " tme=" << static_cast(gs->m_prim.tme) - << " abe=" << static_cast(gs->m_prim.abe) - << " fst=" << static_cast(gs->m_prim.fst) - << " ctxt=" << static_cast(gs->m_prim.ctxt) - << " fbp=" << ctx.frame.fbp - << " fbw=" << ctx.frame.fbw - << " psm=0x" << std::hex << static_cast(ctx.frame.psm) << std::dec - << " tex0=(" - << "tbp0=" << ctx.tex0.tbp0 - << " tbw=" << static_cast(ctx.tex0.tbw) - << " psm=0x" << std::hex << static_cast(ctx.tex0.psm) << std::dec - << " tw=" << static_cast(ctx.tex0.tw) - << " th=" << static_cast(ctx.tex0.th) - << " tcc=" << static_cast(ctx.tex0.tcc) - << " tfx=" << static_cast(ctx.tex0.tfx) - << " cbp=" << ctx.tex0.cbp - << " cpsm=0x" << std::hex << static_cast(ctx.tex0.cpsm) << std::dec - << " csm=" << static_cast(ctx.tex0.csm) - << " csa=" << static_cast(ctx.tex0.csa) - << ")" - << " ofx=" << (ctx.xyoffset.ofx >> 4) - << " ofy=" << (ctx.xyoffset.ofy >> 4) - << " scissor=(" << ctx.scissor.x0 - << "," << ctx.scissor.y0 - << ")-(" << ctx.scissor.x1 - << "," << ctx.scissor.y1 << ")" - << " test=0x" << std::hex << ctx.test - << " alpha=0x" << ctx.alpha - << std::dec - << " v0=(" << gs->m_vtxQueue[0].x << "," << gs->m_vtxQueue[0].y << ")" - << " uv0=(" << (gs->m_vtxQueue[0].u >> 4) << "," << (gs->m_vtxQueue[0].v >> 4) << ")" - << " stq0=(" << gs->m_vtxQueue[0].s << "," << gs->m_vtxQueue[0].t << "," << gs->m_vtxQueue[0].q << ")" - << " v1=(" << gs->m_vtxQueue[1].x << "," << gs->m_vtxQueue[1].y << ")" - << " uv1=(" << (gs->m_vtxQueue[1].u >> 4) << "," << (gs->m_vtxQueue[1].v >> 4) << ")" - << " stq1=(" << gs->m_vtxQueue[1].s << "," << gs->m_vtxQueue[1].t << "," << gs->m_vtxQueue[1].q << ")" - << " v2=(" << gs->m_vtxQueue[2].x << "," << gs->m_vtxQueue[2].y << ")" - << " uv2=(" << (gs->m_vtxQueue[2].u >> 4) << "," << (gs->m_vtxQueue[2].v >> 4) << ")" - << " stq2=(" << gs->m_vtxQueue[2].s << "," << gs->m_vtxQueue[2].t << "," << gs->m_vtxQueue[2].q << ")" - << " rgba0=(" << static_cast(gs->m_vtxQueue[0].r) << "," - << static_cast(gs->m_vtxQueue[0].g) << "," - << static_cast(gs->m_vtxQueue[0].b) << "," - << static_cast(gs->m_vtxQueue[0].a) << ")" - << " rgba1=(" << static_cast(gs->m_vtxQueue[1].r) << "," - << static_cast(gs->m_vtxQueue[1].g) << "," - << static_cast(gs->m_vtxQueue[1].b) << "," - << static_cast(gs->m_vtxQueue[1].a) << ")" - << " rgba2=(" << static_cast(gs->m_vtxQueue[2].r) << "," - << static_cast(gs->m_vtxQueue[2].g) << "," - << static_cast(gs->m_vtxQueue[2].b) << "," - << static_cast(gs->m_vtxQueue[2].a) << ")" - << std::endl; + const uint8_t mmag = static_cast((tex1 >> 5) & 0x1u); + const uint8_t mmin = static_cast((tex1 >> 6) & 0x7u); + return mmag != 0u || mmin == 1u || (mmin & 0x4u) != 0u; + } + + uint8_t lerpChannel(uint8_t c00, uint8_t c10, uint8_t c01, uint8_t c11, float fx, float fy) + { + const float top = static_cast(c00) + (static_cast(c10) - static_cast(c00)) * fx; + const float bottom = static_cast(c01) + (static_cast(c11) - static_cast(c01)) * fx; + return clampU8(static_cast(std::lround(top + (bottom - top) * fy))); } +} +void GSRasterizer::drawPrimitive(GS *gs) +{ const auto &ctx = gs->activeContext(); - if ((gs->m_prim.ctxt != 0u || ctx.frame.fbp == 150u) && - s_debugContext1PrimitiveCount.fetch_add(1u, std::memory_order_relaxed) < 32u) - { - std::cout << "[gs:copy-prim]" - << " type=" << static_cast(gs->m_prim.type) - << " tme=" << static_cast(gs->m_prim.tme) - << " abe=" << static_cast(gs->m_prim.abe) - << " fst=" << static_cast(gs->m_prim.fst) - << " ctxt=" << static_cast(gs->m_prim.ctxt) - << " fbp=" << ctx.frame.fbp - << " fbw=" << ctx.frame.fbw - << " psm=0x" << std::hex << static_cast(ctx.frame.psm) << std::dec - << " tex0=(" - << "tbp0=" << ctx.tex0.tbp0 - << " tbw=" << static_cast(ctx.tex0.tbw) - << " psm=0x" << std::hex << static_cast(ctx.tex0.psm) << std::dec - << " tcc=" << static_cast(ctx.tex0.tcc) - << " tfx=" << static_cast(ctx.tex0.tfx) - << " cbp=" << ctx.tex0.cbp - << " cpsm=0x" << std::hex << static_cast(ctx.tex0.cpsm) << std::dec - << " csm=" << static_cast(ctx.tex0.csm) - << " csa=" << static_cast(ctx.tex0.csa) - << ")" - << " ofx=" << (ctx.xyoffset.ofx >> 4) - << " ofy=" << (ctx.xyoffset.ofy >> 4) - << " scissor=(" << ctx.scissor.x0 - << "," << ctx.scissor.y0 - << ")-(" << ctx.scissor.x1 - << "," << ctx.scissor.y1 << ")" - << " test=0x" << std::hex << ctx.test - << " alpha=0x" << ctx.alpha - << std::dec << std::endl; + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t primitiveIndex = s_debugPrimitiveCount.fetch_add(1, std::memory_order_relaxed); + if (primitiveIndex < 64u) + { + std::cout << "[gs:prim] idx=" << primitiveIndex + << " type=" << static_cast(gs->m_prim.type) + << " tme=" << static_cast(gs->m_prim.tme) + << " abe=" << static_cast(gs->m_prim.abe) + << " fst=" << static_cast(gs->m_prim.fst) + << " ctxt=" << static_cast(gs->m_prim.ctxt) + << " fbp=" << ctx.frame.fbp + << " fbw=" << ctx.frame.fbw + << " psm=0x" << std::hex << static_cast(ctx.frame.psm) << std::dec + << " tex0=(" + << "tbp0=" << ctx.tex0.tbp0 + << " tbw=" << static_cast(ctx.tex0.tbw) + << " psm=0x" << std::hex << static_cast(ctx.tex0.psm) << std::dec + << " tw=" << static_cast(ctx.tex0.tw) + << " th=" << static_cast(ctx.tex0.th) + << " tcc=" << static_cast(ctx.tex0.tcc) + << " tfx=" << static_cast(ctx.tex0.tfx) + << " cbp=" << ctx.tex0.cbp + << " cpsm=0x" << std::hex << static_cast(ctx.tex0.cpsm) << std::dec + << " csm=" << static_cast(ctx.tex0.csm) + << " csa=" << static_cast(ctx.tex0.csa) + << ")" + << " ofx=" << (ctx.xyoffset.ofx >> 4) + << " ofy=" << (ctx.xyoffset.ofy >> 4) + << " scissor=(" << ctx.scissor.x0 + << "," << ctx.scissor.y0 + << ")-(" << ctx.scissor.x1 + << "," << ctx.scissor.y1 << ")" + << " test=0x" << std::hex << ctx.test + << " alpha=0x" << ctx.alpha + << std::dec + << " v0=(" << gs->m_vtxQueue[0].x << "," << gs->m_vtxQueue[0].y << ")" + << " uv0=(" << (gs->m_vtxQueue[0].u >> 4) << "," << (gs->m_vtxQueue[0].v >> 4) << ")" + << " stq0=(" << gs->m_vtxQueue[0].s << "," << gs->m_vtxQueue[0].t << "," << gs->m_vtxQueue[0].q << ")" + << " v1=(" << gs->m_vtxQueue[1].x << "," << gs->m_vtxQueue[1].y << ")" + << " uv1=(" << (gs->m_vtxQueue[1].u >> 4) << "," << (gs->m_vtxQueue[1].v >> 4) << ")" + << " stq1=(" << gs->m_vtxQueue[1].s << "," << gs->m_vtxQueue[1].t << "," << gs->m_vtxQueue[1].q << ")" + << " v2=(" << gs->m_vtxQueue[2].x << "," << gs->m_vtxQueue[2].y << ")" + << " uv2=(" << (gs->m_vtxQueue[2].u >> 4) << "," << (gs->m_vtxQueue[2].v >> 4) << ")" + << " stq2=(" << gs->m_vtxQueue[2].s << "," << gs->m_vtxQueue[2].t << "," << gs->m_vtxQueue[2].q << ")" + << " rgba0=(" << static_cast(gs->m_vtxQueue[0].r) << "," + << static_cast(gs->m_vtxQueue[0].g) << "," + << static_cast(gs->m_vtxQueue[0].b) << "," + << static_cast(gs->m_vtxQueue[0].a) << ")" + << " rgba1=(" << static_cast(gs->m_vtxQueue[1].r) << "," + << static_cast(gs->m_vtxQueue[1].g) << "," + << static_cast(gs->m_vtxQueue[1].b) << "," + << static_cast(gs->m_vtxQueue[1].a) << ")" + << " rgba2=(" << static_cast(gs->m_vtxQueue[2].r) << "," + << static_cast(gs->m_vtxQueue[2].g) << "," + << static_cast(gs->m_vtxQueue[2].b) << "," + << static_cast(gs->m_vtxQueue[2].a) << ")" + << std::endl; + } + }); + + PS2_IF_AGRESSIVE_LOGS({ + if ((gs->m_prim.ctxt != 0u || ctx.frame.fbp == 150u) && + s_debugContext1PrimitiveCount.fetch_add(1u, std::memory_order_relaxed) < 32u) + { + std::cout << "[gs:copy-prim]" + << " type=" << static_cast(gs->m_prim.type) + << " tme=" << static_cast(gs->m_prim.tme) + << " abe=" << static_cast(gs->m_prim.abe) + << " fst=" << static_cast(gs->m_prim.fst) + << " ctxt=" << static_cast(gs->m_prim.ctxt) + << " fbp=" << ctx.frame.fbp + << " fbw=" << ctx.frame.fbw + << " psm=0x" << std::hex << static_cast(ctx.frame.psm) << std::dec + << " tex0=(" + << "tbp0=" << ctx.tex0.tbp0 + << " tbw=" << static_cast(ctx.tex0.tbw) + << " psm=0x" << std::hex << static_cast(ctx.tex0.psm) << std::dec + << " tcc=" << static_cast(ctx.tex0.tcc) + << " tfx=" << static_cast(ctx.tex0.tfx) + << " cbp=" << ctx.tex0.cbp + << " cpsm=0x" << std::hex << static_cast(ctx.tex0.cpsm) << std::dec + << " csm=" << static_cast(ctx.tex0.csm) + << " csa=" << static_cast(ctx.tex0.csa) + << ")" + << " ofx=" << (ctx.xyoffset.ofx >> 4) + << " ofy=" << (ctx.xyoffset.ofy >> 4) + << " scissor=(" << ctx.scissor.x0 + << "," << ctx.scissor.y0 + << ")-(" << ctx.scissor.x1 + << "," << ctx.scissor.y1 << ")" + << " test=0x" << std::hex << ctx.test + << " alpha=0x" << ctx.alpha + << std::dec << std::endl; + } + }); + + if (gs->m_hasPreferredDisplaySource && ctx.frame.fbp == gs->m_preferredDisplayDestFbp) + { + gs->m_hasPreferredDisplaySource = false; } switch (gs->m_prim.type) @@ -324,47 +365,69 @@ void GSRasterizer::writePixel(GS *gs, int x, int y, uint8_t r, uint8_t g, uint8_ if (!alphaTest.writeFramebuffer) return; - uint32_t fbBase = ctx.frame.fbp * 8192u; - uint32_t stride = fbStride(ctx.frame.fbw, ctx.frame.psm); - if (stride == 0) - return; + const uint32_t widthBlocks = (ctx.frame.fbw != 0u) ? ctx.frame.fbw : 1u; + const uint32_t bytesPerPixel = + (ctx.frame.psm == GS_PSM_CT16 || ctx.frame.psm == GS_PSM_CT16S) ? 2u : 4u; + + uint32_t off = 0u; + if (ctx.frame.psm == GS_PSM_CT32 || ctx.frame.psm == GS_PSM_CT24) + { + off = GSPSMCT32::addrPSMCT32(GSInternal::framePageBaseToBlock(ctx.frame.fbp), + widthBlocks, + static_cast(x), + static_cast(y)); + } + else + { + off = addrPSMCT16Family(GSInternal::framePageBaseToBlock(ctx.frame.fbp), + widthBlocks, + ctx.frame.psm, + static_cast(x), + static_cast(y)); + } - const uint32_t bytesPerPixel = std::max(1u, bitsPerPixel(ctx.frame.psm) / 8u); - uint32_t off = fbBase + static_cast(y) * stride + static_cast(x) * bytesPerPixel; if (off + bytesPerPixel > gs->m_vramSize) return; - const uint32_t pixelIndex = s_debugPixelCount.fetch_add(1, std::memory_order_relaxed); - if (pixelIndex < 32u) - { - std::cout << "[gs:pixel] idx=" << pixelIndex - << " xy=(" << x << "," << y << ")" - << " rgba=(" << static_cast(r) << "," - << static_cast(g) << "," - << static_cast(b) << "," - << static_cast(a) << ")" - << " fbp=" << ctx.frame.fbp - << " fbw=" << ctx.frame.fbw - << " psm=0x" << std::hex << static_cast(ctx.frame.psm) << std::dec - << " off=0x" << std::hex << off << std::dec - << std::endl; - } - - if (ctx.frame.fbp == 150u && - s_debugFbp150PixelCount.fetch_add(1u, std::memory_order_relaxed) < 32u) - { - std::cout << "[gs:fbp150-pixel]" - << " xy=(" << x << "," << y << ")" - << " rgba=(" << static_cast(r) << "," - << static_cast(g) << "," - << static_cast(b) << "," - << static_cast(a) << ")" - << " scissor=(" << ctx.scissor.x0 - << "," << ctx.scissor.y0 - << ")-(" << ctx.scissor.x1 - << "," << ctx.scissor.y1 << ")" - << " off=0x" << std::hex << off << std::dec << std::endl; - } + PS2_IF_AGRESSIVE_LOGS({ + const uint32_t pixelIndex = s_debugPixelCount.fetch_add(1, std::memory_order_relaxed); + if (pixelIndex < 32u) + { + std::cout << "[gs:pixel] idx=" << pixelIndex + << " xy=(" << x << "," << y << ")" + << " rgba=(" << static_cast(r) << "," + << static_cast(g) << "," + << static_cast(b) << "," + << static_cast(a) << ")" + << " fbp=" << ctx.frame.fbp + << " fbw=" << ctx.frame.fbw + << " psm=0x" << std::hex << static_cast(ctx.frame.psm) << std::dec + << " off=0x" << std::hex << off << std::dec + << std::endl; + } + }); + + PS2_IF_AGRESSIVE_LOGS({ + if (ctx.frame.fbp == 150u && + s_debugFbp150PixelCount.fetch_add(1u, std::memory_order_relaxed) < 32u) + { + std::cout << "[gs:fbp150-pixel]" + << " xy=(" << x << "," << y << ")" + << " rgba=(" << static_cast(r) << "," + << static_cast(g) << "," + << static_cast(b) << "," + << static_cast(a) << ")" + << " scissor=(" << ctx.scissor.x0 + << "," << ctx.scissor.y0 + << ")-(" << ctx.scissor.x1 + << "," << ctx.scissor.y1 << ")" + << " off=0x" << std::hex << off << std::dec << std::endl; + } + }); + + const uint8_t srcR = r; + const uint8_t srcG = g; + const uint8_t srcB = b; if (gs->m_prim.abe) { @@ -384,30 +447,47 @@ void GSRasterizer::writePixel(GS *gs, int x, int y, uint8_t r, uint8_t g, uint8_ uint8_t db = (existing >> 16) & 0xFF; uint8_t da = (existing >> 24) & 0xFF; - uint64_t alphaReg = ctx.alpha; - uint8_t asel = alphaReg & 3; - uint8_t bsel = (alphaReg >> 2) & 3; - uint8_t csel = (alphaReg >> 4) & 3; - uint8_t dsel = (alphaReg >> 6) & 3; - uint8_t fix = static_cast((alphaReg >> 32) & 0xFF); - - auto pickRGB = [&](uint8_t sel, int cs, int cd) -> int + // PABE disables alpha blending when the source alpha MSB is clear. + if (!(gs->m_pabe && (a & 0x80u) == 0u)) { - if (sel == 0) - return cs; - if (sel == 1) - return cd; - return 0; - }; - int cAlpha = (csel == 0) ? a : (csel == 1) ? da - : fix; - - r = clampU8(((pickRGB(asel, r, dr) - pickRGB(bsel, r, dr)) * cAlpha >> 7) + pickRGB(dsel, r, dr)); - g = clampU8(((pickRGB(asel, g, dg) - pickRGB(bsel, g, dg)) * cAlpha >> 7) + pickRGB(dsel, g, dg)); - b = clampU8(((pickRGB(asel, b, db) - pickRGB(bsel, b, db)) * cAlpha >> 7) + pickRGB(dsel, b, db)); + uint64_t alphaReg = ctx.alpha; + uint8_t asel = alphaReg & 3; + uint8_t bsel = (alphaReg >> 2) & 3; + uint8_t csel = (alphaReg >> 4) & 3; + uint8_t dsel = (alphaReg >> 6) & 3; + uint8_t fix = static_cast((alphaReg >> 32) & 0xFF); + + auto pickRGB = [&](uint8_t sel, int cs, int cd) -> int + { + if (sel == 0) + return cs; + if (sel == 1) + return cd; + return 0; + }; + int cAlpha = (csel == 0) ? a : (csel == 1) ? da + : fix; + + r = clampU8(((pickRGB(asel, r, dr) - pickRGB(bsel, r, dr)) * cAlpha >> 7) + pickRGB(dsel, r, dr)); + g = clampU8(((pickRGB(asel, g, dg) - pickRGB(bsel, g, dg)) * cAlpha >> 7) + pickRGB(dsel, g, dg)); + b = clampU8(((pickRGB(asel, b, db) - pickRGB(bsel, b, db)) * cAlpha >> 7) + pickRGB(dsel, b, db)); + } + else + { + r = srcR; + g = srcG; + b = srcB; + } } uint32_t mask = ctx.frame.fbmsk; + if (!alphaTest.preserveDestinationAlpha && + (ctx.fba & 0x1ull) != 0ull && + ctx.frame.psm != GS_PSM_CT24) + { + a = static_cast(a | 0x80u); + } + if (bytesPerPixel == 2u) { uint16_t pixel = encodePSMCT16(r, g, b, a); @@ -444,9 +524,7 @@ uint32_t GSRasterizer::readTexelPSMCT32(GS *gs, uint32_t tbp0, uint32_t tbw, int { if (tbw == 0) tbw = 1; - uint32_t base = tbp0 * 256u; - uint32_t stride = tbw * 64u * 4u; - uint32_t off = base + static_cast(texV) * stride + static_cast(texU) * 4u; + uint32_t off = GSPSMCT32::addrPSMCT32(tbp0, tbw, static_cast(texU), static_cast(texV)); if (off + 4 > gs->m_vramSize) return 0xFFFF00FFu; uint32_t texel; @@ -458,9 +536,8 @@ uint32_t GSRasterizer::readTexelPSMCT16(GS *gs, uint32_t tbp0, uint32_t tbw, int { if (tbw == 0) tbw = 1; - uint32_t base = tbp0 * 256u; - uint32_t stride = tbw * 64u * 2u; - uint32_t off = base + static_cast(texV) * stride + static_cast(texU) * 2u; + const uint8_t psm = gs->activeContext().tex0.psm; + uint32_t off = addrPSMCT16Family(tbp0, tbw, psm, static_cast(texU), static_cast(texV)); if (off + 2 > gs->m_vramSize) return 0xFFFF00FFu; uint16_t texel; @@ -492,10 +569,12 @@ uint32_t GSRasterizer::lookupCLUT(GS *gs, { uint32_t clutBase = cbp * 256u; const uint32_t clutIndex = resolveClutIndex(index, csm, csa, sourcePsm); + const uint32_t clutX = clutIndex & 0x0Fu; + const uint32_t clutY = clutIndex >> 4; if (cpsm == GS_PSM_CT32 || cpsm == GS_PSM_CT24) { - uint32_t off = clutBase + clutIndex * 4u; + const uint32_t off = GSPSMCT32::addrPSMCT32(cbp, 1u, clutX, clutY); if (off + 4 > gs->m_vramSize) return 0xFFFF00FFu; uint32_t color; @@ -505,7 +584,7 @@ uint32_t GSRasterizer::lookupCLUT(GS *gs, if (cpsm == GS_PSM_CT16 || cpsm == GS_PSM_CT16S) { - uint32_t off = clutBase + clutIndex * 2u; + uint32_t off = addrPSMCT16Family(cbp, 1u, cpsm, clutX, clutY); if (off + 2 > gs->m_vramSize) return 0xFFFF00FFu; uint16_t c16; @@ -528,46 +607,94 @@ uint32_t GSRasterizer::sampleTexture(GS *gs, float s, float t, float q, uint16_t int texW = 1 << tex.tw; int texH = 1 << tex.th; - int texU, texV; + float texUf, texVf; if (gs->m_prim.fst) { - texU = static_cast(u >> 4); - texV = static_cast(v >> 4); + texUf = static_cast(u) / 16.0f; + texVf = static_cast(v) / 16.0f; } else { const float invQ = 1.0f / fabsQ(q); - texU = static_cast(s * invQ * static_cast(texW)); - texV = static_cast(t * invQ * static_cast(texH)); + texUf = s * invQ * static_cast(texW); + texVf = t * invQ * static_cast(texH); } - texU = clampInt(texU, 0, texW - 1); - texV = clampInt(texV, 0, texH - 1); + auto samplePoint = [&](int sampleU, int sampleV) -> uint32_t + { + sampleU = clampInt(sampleU, 0, texW - 1); + sampleV = clampInt(sampleV, 0, texH - 1); - if (tex.psm == GS_PSM_CT32 || tex.psm == GS_PSM_CT24) - return readTexelPSMCT32(gs, tex.tbp0, tex.tbw, texU, texV); + if (tex.psm == GS_PSM_CT32 || tex.psm == GS_PSM_CT24) + return readTexelPSMCT32(gs, tex.tbp0, tex.tbw, sampleU, sampleV); - if (tex.psm == GS_PSM_CT16 || tex.psm == GS_PSM_CT16S) - return readTexelPSMCT16(gs, tex.tbp0, tex.tbw, texU, texV); + if (tex.psm == GS_PSM_CT16 || tex.psm == GS_PSM_CT16S) + return readTexelPSMCT16(gs, tex.tbp0, tex.tbw, sampleU, sampleV); - if (tex.psm == GS_PSM_T4) - { - uint32_t idx = readTexelPSMT4(gs, tex.tbp0, tex.tbw, texU, texV); - return lookupCLUT(gs, static_cast(idx), tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); - } + if (tex.psm == GS_PSM_T4) + { + uint32_t idx = readTexelPSMT4(gs, tex.tbp0, tex.tbw, sampleU, sampleV); + return lookupCLUT(gs, static_cast(idx), tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); + } + + if (tex.psm == GS_PSM_T8) + { + if (tex.tbw == 0) + return 0xFFFF00FFu; + uint32_t off = GSPSMT8::addrPSMT8(tex.tbp0, tex.tbw, static_cast(sampleU), static_cast(sampleV)); + if (off >= gs->m_vramSize) + return 0xFFFF00FFu; + uint8_t idx = gs->m_vram[off]; + return lookupCLUT(gs, idx, tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); + } - if (tex.psm == GS_PSM_T8) + return 0xFFFF00FFu; + }; + + if (!tex1UsesLinearFilter(ctx.tex1)) { - if (tex.tbw == 0) - return 0xFFFF00FFu; - uint32_t off = GSPSMT8::addrPSMT8(tex.tbp0, tex.tbw, static_cast(texU), static_cast(texV)); - if (off >= gs->m_vramSize) - return 0xFFFF00FFu; - uint8_t idx = gs->m_vram[off]; - return lookupCLUT(gs, idx, tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); + return samplePoint(static_cast(texUf), static_cast(texVf)); } - return 0xFFFF00FFu; + const float sampleU = texUf - 0.5f; + const float sampleV = texVf - 0.5f; + const int u0 = static_cast(std::floor(sampleU)); + const int v0 = static_cast(std::floor(sampleV)); + const int u1 = u0 + 1; + const int v1 = v0 + 1; + const float fx = sampleU - static_cast(u0); + const float fy = sampleV - static_cast(v0); + + const uint32_t c00 = samplePoint(u0, v0); + const uint32_t c10 = samplePoint(u1, v0); + const uint32_t c01 = samplePoint(u0, v1); + const uint32_t c11 = samplePoint(u1, v1); + + const uint8_t r = lerpChannel(static_cast(c00 & 0xFFu), + static_cast(c10 & 0xFFu), + static_cast(c01 & 0xFFu), + static_cast(c11 & 0xFFu), + fx, fy); + const uint8_t g = lerpChannel(static_cast((c00 >> 8) & 0xFFu), + static_cast((c10 >> 8) & 0xFFu), + static_cast((c01 >> 8) & 0xFFu), + static_cast((c11 >> 8) & 0xFFu), + fx, fy); + const uint8_t b = lerpChannel(static_cast((c00 >> 16) & 0xFFu), + static_cast((c10 >> 16) & 0xFFu), + static_cast((c01 >> 16) & 0xFFu), + static_cast((c11 >> 16) & 0xFFu), + fx, fy); + const uint8_t a = lerpChannel(static_cast((c00 >> 24) & 0xFFu), + static_cast((c10 >> 24) & 0xFFu), + static_cast((c01 >> 24) & 0xFFu), + static_cast((c11 >> 24) & 0xFFu), + fx, fy); + + return static_cast(r) | + (static_cast(g) << 8) | + (static_cast(b) << 16) | + (static_cast(a) << 24); } void GSRasterizer::drawSprite(GS *gs) @@ -589,18 +716,47 @@ void GSRasterizer::drawSprite(GS *gs) if (y0 > y1) std::swap(y0, y1); + const int unclippedX0 = x0; + const int unclippedY0 = y0; + const int spanX = std::max(1, x1 - x0); + const int spanY = std::max(1, y1 - y0); + const int unclippedX1 = unclippedX0 + spanX - 1; + const int unclippedY1 = unclippedY0 + spanY - 1; + // If the sprite rectangle is fully outside scissor, nothing should render. - if (x1 < ctx.scissor.x0 || x0 > ctx.scissor.x1 || - y1 < ctx.scissor.y0 || y0 > ctx.scissor.y1) + if (unclippedX1 < ctx.scissor.x0 || unclippedX0 > ctx.scissor.x1 || + unclippedY1 < ctx.scissor.y0 || unclippedY0 > ctx.scissor.y1) { // maybe a log here idk ? return; } - x0 = clampInt(x0, ctx.scissor.x0, ctx.scissor.x1); - y0 = clampInt(y0, ctx.scissor.y0, ctx.scissor.y1); - x1 = clampInt(x1, ctx.scissor.x0, ctx.scissor.x1); - y1 = clampInt(y1, ctx.scissor.y0, ctx.scissor.y1); + const int drawX0 = clampInt(unclippedX0, ctx.scissor.x0, ctx.scissor.x1); + const int drawY0 = clampInt(unclippedY0, ctx.scissor.y0, ctx.scissor.y1); + const int drawX1 = clampInt(unclippedX1, ctx.scissor.x0, ctx.scissor.x1); + const int drawY1 = clampInt(unclippedY1, ctx.scissor.y0, ctx.scissor.y1); + + const uint64_t alphaReg = ctx.alpha; + const uint8_t alphaMode = static_cast(alphaReg & 0xFFu); + const uint8_t alphaFix = static_cast((alphaReg >> 32) & 0xFFu); + const bool looksLikeDisplayCopy = + gs->m_prim.tme && + gs->m_prim.abe && + gs->m_prim.fst && + gs->m_prim.ctxt && + ctx.frame.fbp != ctx.tex0.tbp0 && + alphaMode == 0x64u && + (alphaFix == 0x60u || alphaFix == 0x80u) && + unclippedX0 <= 0 && + unclippedY0 <= 0 && + unclippedX1 >= 639 && + unclippedY1 >= 447; + if (looksLikeDisplayCopy) + { + gs->m_preferredDisplaySourceFrame = {ctx.tex0.tbp0, ctx.tex0.tbw, ctx.tex0.psm, 0u}; + gs->m_preferredDisplayDestFbp = ctx.frame.fbp; + gs->m_hasPreferredDisplaySource = true; + } uint8_t r = v1.r, g = v1.g, b = v1.b, a = v1.a; @@ -632,45 +788,36 @@ void GSRasterizer::drawSprite(GS *gs) v1f = (v1.t / q1) * static_cast(texH); } - float spriteW = static_cast(x1 - x0); - float spriteH = static_cast(y1 - y0); + float spriteW = static_cast(spanX); + float spriteH = static_cast(spanY); if (spriteW < 1.0f) spriteW = 1.0f; if (spriteH < 1.0f) spriteH = 1.0f; - for (int y = y0; y <= y1; ++y) + for (int y = drawY0; y <= drawY1; ++y) { - float ty = (static_cast(y - y0) + 0.5f) / spriteH; + float ty = (static_cast(y - unclippedY0) + 0.5f) / spriteH; float texVf = v0f + (v1f - v0f) * ty; - int tv = clampInt(static_cast(texVf), 0, texH - 1); - for (int x = x0; x <= x1; ++x) + for (int x = drawX0; x <= drawX1; ++x) { - float tx = (static_cast(x - x0) + 0.5f) / spriteW; + float tx = (static_cast(x - unclippedX0) + 0.5f) / spriteW; float texUf = u0f + (u1f - u0f) * tx; - int tu = clampInt(static_cast(texUf), 0, texW - 1); - - uint32_t texel; - uint32_t sampleIndexValue = 0u; - if (tex.psm == GS_PSM_CT32 || tex.psm == GS_PSM_CT24) - texel = readTexelPSMCT32(gs, tex.tbp0, tex.tbw, tu, tv); - else if (tex.psm == GS_PSM_CT16 || tex.psm == GS_PSM_CT16S) - texel = readTexelPSMCT16(gs, tex.tbp0, tex.tbw, tu, tv); - else if (tex.psm == GS_PSM_T4) + uint32_t texel = 0xFFFF00FFu; + if (gs->m_prim.fst) { - sampleIndexValue = readTexelPSMT4(gs, tex.tbp0, tex.tbw, tu, tv); - texel = lookupCLUT(gs, static_cast(sampleIndexValue), tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); + const uint16_t sampleU = static_cast(clampInt(static_cast(std::lround(texUf * 16.0f)), 0, 0xFFFF)); + const uint16_t sampleV = static_cast(clampInt(static_cast(std::lround(texVf * 16.0f)), 0, 0xFFFF)); + texel = sampleTexture(gs, 0.0f, 0.0f, 1.0f, sampleU, sampleV); } - else if (tex.psm == GS_PSM_T8) + else { - uint32_t off = GSPSMT8::addrPSMT8(tex.tbp0, tex.tbw ? tex.tbw : 1u, - static_cast(tu), static_cast(tv)); - sampleIndexValue = (off < gs->m_vramSize) ? gs->m_vram[off] : 0u; - texel = lookupCLUT(gs, static_cast(sampleIndexValue), tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); + texel = sampleTexture(gs, + texUf / static_cast(texW), + texVf / static_cast(texH), + 1.0f, 0u, 0u); } - else - texel = 0xFFFF00FFu; uint8_t tr = static_cast(texel & 0xFF); uint8_t tg = static_cast((texel >> 8) & 0xFF); @@ -684,8 +831,8 @@ void GSRasterizer::drawSprite(GS *gs) } else { - for (int y = y0; y <= y1; ++y) - for (int x = x0; x <= x1; ++x) + for (int y = drawY0; y <= drawY1; ++y) + for (int x = drawX0; x <= drawX1; ++x) writePixel(gs, x, y, r, g, b, a); } } @@ -723,6 +870,7 @@ void GSRasterizer::drawTriangle(GS *gs) const float winding = (denom < 0.0f) ? -1.0f : 1.0f; const float invAbsDenom = 1.0f / std::fabs(denom); + constexpr float kEdgeEpsilon = 1.0e-4f; for (int y = minY; y <= maxY; ++y) { @@ -735,7 +883,7 @@ void GSRasterizer::drawTriangle(GS *gs) float w1 = (((fy2 - fy0) * (px - fx2) + (fx0 - fx2) * (py - fy2)) * winding) * invAbsDenom; float w2 = 1.0f - w0 - w1; - if (w0 < 0.0f || w1 < 0.0f || w2 < 0.0f) + if (w0 < -kEdgeEpsilon || w1 < -kEdgeEpsilon || w2 < -kEdgeEpsilon) continue; uint8_t r, g, b, a; diff --git a/ps2xRuntime/src/lib/ps2_iop.cpp b/ps2xRuntime/src/lib/ps2_iop.cpp index 41ce06d6..e2665098 100644 --- a/ps2xRuntime/src/lib/ps2_iop.cpp +++ b/ps2xRuntime/src/lib/ps2_iop.cpp @@ -1,4 +1,8 @@ -#include "ps2_iop.h" +#include "runtime/ps2_iop.h" +#include "runtime/ps2_iop_audio.h" +#include "runtime/ps2_memory.h" +#include "ps2_runtime.h" +#include "Kernel/Syscalls/RPC.h" ps2_iop::ps2_iop() { @@ -14,9 +18,39 @@ void ps2_iop::reset() { } -bool ps2_iop::handleRPC(uint32_t /*sid*/, uint32_t /*rpcNum*/, - uint32_t /*sendBufAddr*/, uint32_t /*sendSize*/, - uint32_t /*recvBufAddr*/, uint32_t /*recvSize*/) +bool ps2_iop::handleRPC(PS2Runtime *runtime, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBufAddr, uint32_t sendSize, + uint32_t recvBufAddr, uint32_t recvSize, + uint32_t &resultPtr, + bool &signalNowaitCompletion) { + resultPtr = 0u; + signalNowaitCompletion = false; + + if (!runtime || !m_rdram) + { + return false; + } + + if (ps2_syscalls::handleSoundDriverRpcService(m_rdram, runtime, + sid, rpcNum, + sendBufAddr, sendSize, + recvBufAddr, recvSize, + resultPtr, + signalNowaitCompletion)) + { + return true; + } + + if (sid == IOP_SID_LIBSD) + { + const uint8_t *sendPtr = sendBufAddr ? getConstMemPtr(m_rdram, sendBufAddr) : nullptr; + uint8_t *recvPtr = recvBufAddr ? getMemPtr(m_rdram, recvBufAddr) : nullptr; + ps2_iop_audio::handleLibSdRpc(runtime, sid, rpcNum, sendPtr, sendSize, recvPtr, recvSize); + resultPtr = recvBufAddr; + return true; + } + return false; } diff --git a/ps2xRuntime/src/lib/ps2_iop_audio.cpp b/ps2xRuntime/src/lib/ps2_iop_audio.cpp index 259209ef..e1209764 100644 --- a/ps2xRuntime/src/lib/ps2_iop_audio.cpp +++ b/ps2xRuntime/src/lib/ps2_iop_audio.cpp @@ -1,4 +1,4 @@ -#include "ps2_iop_audio.h" +#include "runtime/ps2_iop_audio.h" #include "ps2_runtime.h" namespace ps2_iop_audio diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index 67121677..3fdca8af 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -1,4 +1,5 @@ -#include "ps2_memory.h" +#include "runtime/ps2_memory.h" +#include "ps2_log.h" #include #include #include @@ -195,6 +196,8 @@ bool PS2Memory::initialize(size_t ramSize) memset(&gs_regs, 0, sizeof(gs_regs)); gs_regs.dispfb1 = (0ULL << 0) | (10ULL << 9) | (0ULL << 15) | (0ULL << 32) | (0ULL << 43); gs_regs.display1 = (0ULL << 0) | (0ULL << 12) | (0ULL << 23) | (0ULL << 27) | (639ULL << 32) | (447ULL << 44); + gs_regs.dispfb2 = gs_regs.dispfb1; + gs_regs.display2 = gs_regs.display1; // Allocate GS VRAM (4MB) m_gsVRAM = new uint8_t[PS2_GS_VRAM_SIZE]; @@ -702,17 +705,13 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) { if (address == 0x10002010) { + m_ioRegisters[address] = value & ~(1u << 31); if (value & (1u << 30)) { m_ioRegisters[0x10002000] = 0; - m_ioRegisters[0x10002010] = 0; m_ioRegisters[0x10002020] = 0; m_ioRegisters[0x10002030] = 0; } - else - { - m_ioRegisters[address] = value & ~(1u << 31); - } } else { @@ -1367,7 +1366,7 @@ void PS2Memory::registerCodeRegion(uint32_t start, uint32_t end) region.modified.resize(sizeInWords, false); m_codeRegions.push_back(region); - std::cout << "Registered code region: " << std::hex << start << " - " << end << std::dec << std::endl; + RUNTIME_LOG("Registered code region: " << std::hex << start << " - " << end << std::dec); } bool PS2Memory::isAddressInRegion(uint32_t address, const CodeRegion ®ion) @@ -1413,7 +1412,7 @@ void PS2Memory::markModified(uint32_t address, uint32_t size) if (bitIndex < region.modified.size()) { region.modified[bitIndex] = true; - std::cout << "Marked code at " << std::hex << addr << std::dec << " as modified" << std::endl; + RUNTIME_LOG("Marked code at " << std::hex << addr << std::dec << " as modified"); } } } diff --git a/ps2xRuntime/src/lib/ps2_pad.cpp b/ps2xRuntime/src/lib/ps2_pad.cpp index cc99e1c9..0537366e 100644 --- a/ps2xRuntime/src/lib/ps2_pad.cpp +++ b/ps2xRuntime/src/lib/ps2_pad.cpp @@ -1,4 +1,4 @@ -#include "ps2_pad.h" +#include "runtime/ps2_pad.h" #include "raylib.h" #include diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 3b0ed96b..63d46953 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -2,6 +2,7 @@ #include "ps2_syscalls.h" #include "ps2_stubs.h" #include "game_overrides.h" +#include "ps2_log.h" #include "ps2_runtime_macros.h" #include #include @@ -16,7 +17,7 @@ #include #include #include "raylib.h" -#include "ps2_gs_gpu.h" +#include "runtime/ps2_gs_gpu.h" #include namespace ps2_stubs @@ -30,7 +31,8 @@ namespace ps2_stubs #define PT_LOAD 1 // Loadable segment static constexpr int FB_WIDTH = 640; -static constexpr int FB_HEIGHT = 448; +static constexpr int FB_HEIGHT = 512; +static constexpr int DEFAULT_DISPLAY_HEIGHT = 448; static constexpr uint32_t DEFAULT_FB_SIZE = FB_WIDTH * FB_HEIGHT * 4; static constexpr uint32_t DEFAULT_FB_ADDR = (PS2_RAM_SIZE - DEFAULT_FB_SIZE - 0x10000u); struct ElfHeader @@ -84,6 +86,45 @@ namespace constexpr uint32_t EXCEPTION_VECTOR_TLB_REFILL = 0x80000000u; constexpr uint32_t EXCEPTION_VECTOR_BOOT = 0xBFC00200u; + struct HostFrameProbePoint + { + uint32_t x; + uint32_t y; + }; + + constexpr HostFrameProbePoint kGhostProbePoints[] = { + {220u, 176u}, + {260u, 208u}, + {320u, 208u}, + {260u, 240u}, + {320u, 240u}, + {260u, 272u}, + {320u, 272u}, + }; + + uint32_t sampleHostFramePixel(const std::vector &pixels, + uint32_t width, + uint32_t height, + uint32_t x, + uint32_t y) + { + if (x >= width || y >= height) + { + return 0u; + } + + const size_t offset = (static_cast(y) * static_cast(width) + static_cast(x)) * 4u; + if (offset + 4u > pixels.size()) + { + return 0u; + } + + return static_cast(pixels[offset + 0u]) | + (static_cast(pixels[offset + 1u]) << 8) | + (static_cast(pixels[offset + 2u]) << 16) | + (static_cast(pixels[offset + 3u]) << 24); + } + struct DispatchHistory { std::array pcs{}; @@ -355,286 +396,119 @@ PS2Runtime::GuestExecutionReleaseScope::~GuestExecutionReleaseScope() } } -static void UploadFrame(Texture2D &tex, PS2Runtime *rt) +static void UploadFrame(Texture2D &tex, PS2Runtime *rt, uint32_t &outWidth, uint32_t &outHeight) { - // For now lets keep the display snapshot in sync with rasterized VRAM so the host frame - rt->gs().refreshDisplaySnapshot(); - - const GSRegisters &gs = rt->memory().gs(); - - uint32_t dispfb = static_cast(gs.dispfb1 & 0xFFFFFFFFULL); - uint32_t fbp = dispfb & 0x1FF; - uint32_t fbw = (dispfb >> 9) & 0x3F; - uint32_t psm = (dispfb >> 15) & 0x1F; + static uint64_t s_lastPresentationTick = std::numeric_limits::max(); + static bool s_hasLatchedInitialFrame = false; + static uint32_t s_lastDisplayFbp = std::numeric_limits::max(); + static uint32_t s_lastSourceFbp = std::numeric_limits::max(); + static bool s_lastPreferred = false; + static uint32_t s_lastWidth = 0u; + static uint32_t s_lastHeight = 0u; - uint64_t display64 = gs.display1; - uint32_t dw = static_cast((display64 >> 32) & 0xFFF); - uint32_t dh = static_cast((display64 >> 44) & 0x7FF); - - uint32_t width = (dw + 1); - uint32_t height = (dh + 1); - if (width < 64 || height < 64) + const uint64_t currentTick = ps2_syscalls::GetCurrentVSyncTick(); + if (!s_hasLatchedInitialFrame || currentTick != s_lastPresentationTick) { - width = FB_WIDTH; - height = FB_HEIGHT; + rt->gs().latchHostPresentationFrame(); + s_lastPresentationTick = currentTick; + s_hasLatchedInitialFrame = true; } - if (width > FB_WIDTH) - width = FB_WIDTH; - if (height > FB_HEIGHT) - height = FB_HEIGHT; - - uint8_t *rdram = rt->memory().getRDRAM(); - uint8_t *gsvram = rt->memory().getGSVRAM(); - - uint32_t snapSize = 0; - const uint8_t *snapVram = rt->gs().lockDisplaySnapshot(snapSize); - const uint8_t *vramSrc = (snapVram && snapSize > 0) ? snapVram : gsvram; - auto fillScratchFromFrame = [&](uint32_t srcFbp, - uint32_t srcFbw, - uint32_t srcPsm, - std::vector &outScratch) -> bool - { - outScratch.assign(FB_WIDTH * FB_HEIGHT * 4u, 0u); - - const uint32_t baseBytes = srcFbp * 8192u; - const uint32_t bytesPerPixel = (srcPsm == 2u || srcPsm == 0x0Au) ? 2u : 4u; - const uint32_t strideBytes = (srcFbw ? srcFbw : (FB_WIDTH / 64u)) * 64u * bytesPerPixel; - - if (srcPsm == 0u) - { - for (uint32_t y = 0; y < height; ++y) - { - uint32_t srcOff = baseBytes + y * strideBytes; - uint32_t dstOff = y * FB_WIDTH * 4u; - uint32_t copyW = width * 4u; - if (srcOff + copyW <= PS2_GS_VRAM_SIZE && vramSrc) - { - std::memcpy(&outScratch[dstOff], vramSrc + srcOff, copyW); - } - else - { - uint32_t rdramIdx = srcOff & PS2_RAM_MASK; - if (rdramIdx + copyW > PS2_RAM_SIZE) - { - copyW = PS2_RAM_SIZE - rdramIdx; - } - std::memcpy(&outScratch[dstOff], rdram + rdramIdx, copyW); - } - uint8_t *row = outScratch.data() + dstOff; - for (uint32_t x = 0; x < width; ++x) - { - row[x * 4u + 3u] = 255u; - } - } - return true; - } - - if (srcPsm == 2u || srcPsm == 0x0Au) - { - const uint32_t srcLineBytes = width * 2u; - for (uint32_t y = 0; y < height; ++y) - { - uint32_t srcOff = baseBytes + y * strideBytes; - uint32_t dstOff = y * FB_WIDTH * 4u; - const uint8_t *src = nullptr; - if (srcOff + srcLineBytes <= PS2_GS_VRAM_SIZE && vramSrc) - { - src = vramSrc + srcOff; - } - else if ((srcOff & PS2_RAM_MASK) + srcLineBytes <= PS2_RAM_SIZE) - { - src = rdram + (srcOff & PS2_RAM_MASK); - } - if (!src) - { - continue; - } - uint8_t *dst = outScratch.data() + dstOff; - for (uint32_t x = 0; x < width; ++x) - { - uint16_t p = *reinterpret_cast(src + x * 2u); - uint32_t r = (p >> 10) & 31u; - uint32_t g = (p >> 5) & 31u; - uint32_t b = p & 31u; - dst[x * 4u + 0u] = static_cast((r << 3) | (r >> 2)); - dst[x * 4u + 1u] = static_cast((g << 3) | (g >> 2)); - dst[x * 4u + 2u] = static_cast((b << 3) | (b >> 2)); - dst[x * 4u + 3u] = 255u; - } - } - return true; - } - - return false; - }; - - auto analyzeScratch = [&](const std::vector &scratchBuf, - uint32_t &outNonBlack, - uint32_t &outFirstColor, - uint32_t &outFirstX, - uint32_t &outFirstY) - { - outNonBlack = 0u; - outFirstColor = 0u; - outFirstX = 0u; - outFirstY = 0u; - - for (uint32_t y = 0; y < height; ++y) - { - const uint8_t *row = scratchBuf.data() + y * FB_WIDTH * 4u; - for (uint32_t x = 0; x < width; ++x) - { - const uint8_t r = row[x * 4u + 0u]; - const uint8_t g = row[x * 4u + 1u]; - const uint8_t b = row[x * 4u + 2u]; - if (r != 0u || g != 0u || b != 0u) - { - ++outNonBlack; - if (outFirstColor == 0u) - { - outFirstColor = static_cast(r) | - (static_cast(g) << 8) | - (static_cast(b) << 16); - outFirstX = x; - outFirstY = y; - } - } - } - } - }; - - auto countLinearPageNonBlack = [&](uint32_t probeFbp) -> uint32_t - { - if (!vramSrc) - { - return 0u; - } - const uint32_t probeBaseBytes = probeFbp * 8192u; - const uint32_t probeStrideBytes = 10u * 64u * 4u; - uint32_t count = 0u; - for (uint32_t py = 0; py < height; ++py) - { - const uint32_t srcOff = probeBaseBytes + py * probeStrideBytes; - if (srcOff + width * 4u > PS2_GS_VRAM_SIZE) - { - break; - } - const uint8_t *row = vramSrc + srcOff; - for (uint32_t px = 0; px < width; ++px) - { - const uint8_t r = row[px * 4u + 0u]; - const uint8_t g = row[px * 4u + 1u]; - const uint8_t b = row[px * 4u + 2u]; - if (r != 0u || g != 0u || b != 0u) - { - ++count; - } - } - } - return count; - }; std::vector scratch; - if (!fillScratchFromFrame(fbp, fbw, psm, scratch)) + uint32_t width = 0u; + uint32_t height = 0u; + uint32_t displayFbp = 0u; + uint32_t sourceFbp = 0u; + bool usedPreferredDisplaySource = false; + if (!rt->gs().copyLatchedHostPresentationFrame(scratch, + width, + height, + &displayFbp, + &sourceFbp, + &usedPreferredDisplaySource)) { - rt->gs().unlockDisplaySnapshot(); Image blank = GenImageColor(FB_WIDTH, FB_HEIGHT, MAGENTA); UpdateTexture(tex, blank.data); UnloadImage(blank); + outWidth = FB_WIDTH; + outHeight = DEFAULT_DISPLAY_HEIGHT; return; } - uint32_t selectedFbp = fbp; - uint32_t selectedFbw = fbw; - uint32_t selectedPsm = psm; - uint32_t nonBlack = 0u; - uint32_t firstColor = 0u; - uint32_t firstX = 0u; - uint32_t firstY = 0u; - analyzeScratch(scratch, nonBlack, firstColor, firstX, firstY); - - int fallbackContext = -1; - const bool allowFallbackPresentation = (fbp == 0u); - if (allowFallbackPresentation && nonBlack == 0u) - { - for (int contextIndex = 0; contextIndex < 2; ++contextIndex) + PS2_IF_AGRESSIVE_LOGS({ + static uint32_t s_uploadDebugCount = 0u; + if (s_uploadDebugCount < 128u || + displayFbp != s_lastDisplayFbp || + sourceFbp != s_lastSourceFbp || + usedPreferredDisplaySource != s_lastPreferred || + width != s_lastWidth || + height != s_lastHeight) { - const GSFrameReg &candidate = rt->gs().getContextFrame(contextIndex); - if (candidate.fbp == selectedFbp && - candidate.fbw == selectedFbw && - candidate.psm == selectedPsm) - { - continue; - } - - std::vector candidateScratch; - if (!fillScratchFromFrame(candidate.fbp, candidate.fbw, candidate.psm, candidateScratch)) - { - continue; - } - - uint32_t candidateNonBlack = 0u; - uint32_t candidateFirstColor = 0u; - uint32_t candidateFirstX = 0u; - uint32_t candidateFirstY = 0u; - analyzeScratch(candidateScratch, candidateNonBlack, candidateFirstColor, candidateFirstX, candidateFirstY); - if (candidateNonBlack == 0u) - { - continue; - } - - scratch.swap(candidateScratch); - selectedFbp = candidate.fbp; - selectedFbw = candidate.fbw; - selectedPsm = candidate.psm; - nonBlack = candidateNonBlack; - firstColor = candidateFirstColor; - firstX = candidateFirstX; - firstY = candidateFirstY; - fallbackContext = contextIndex; - break; - } - } - - rt->gs().unlockDisplaySnapshot(); - - static uint32_t s_uploadDebugCount = 0u; - static uint32_t s_lastLoggedFbp = std::numeric_limits::max(); - static uint32_t s_lastLoggedNonBlack = std::numeric_limits::max(); - const bool shouldProbe = (s_uploadDebugCount < 96u) || (fbp != s_lastLoggedFbp); - if (shouldProbe || fallbackContext >= 0) - { - if (s_uploadDebugCount < 96u || selectedFbp != s_lastLoggedFbp || nonBlack != s_lastLoggedNonBlack || fallbackContext >= 0) - { - const uint32_t page0NonBlack = countLinearPageNonBlack(0u); - const uint32_t page150NonBlack = countLinearPageNonBlack(150u); std::cout << "[frame:upload] idx=" << s_uploadDebugCount - << " fbp=" << selectedFbp - << " fbw=" << selectedFbw - << " psm=0x" << std::hex << selectedPsm << std::dec + << " tick=" << currentTick + << " displayFbp=" << displayFbp + << " sourceFbp=" << sourceFbp << " size=" << width << "x" << height - << " nonBlack=" << nonBlack - << " page0=" << page0NonBlack - << " page150=" << page150NonBlack - << " allowFallback=" << static_cast(allowFallbackPresentation ? 1u : 0u); - if (fallbackContext >= 0) - { - std::cout << " displayFbp=" << fbp - << " fallbackCtx=" << fallbackContext; - } - if (firstColor != 0u) + << " preferred=" << static_cast(usedPreferredDisplaySource ? 1u : 0u) + << std::endl; + } + static uint32_t s_probeDebugCount = 0u; + if (s_probeDebugCount < 32u || + displayFbp != s_lastDisplayFbp || + sourceFbp != s_lastSourceFbp || + usedPreferredDisplaySource != s_lastPreferred) + { + std::cout << "[frame:probe] idx=" << s_probeDebugCount + << " tick=" << currentTick + << " displayFbp=" << displayFbp + << " sourceFbp=" << sourceFbp + << " preferred=" << static_cast(usedPreferredDisplaySource ? 1u : 0u); + for (const auto &probe : kGhostProbePoints) { - std::cout << " first=(" << firstX << "," << firstY << ")" - << " rgb=0x" << std::hex << firstColor << std::dec; + if (probe.x >= width || probe.y >= height) + { + continue; + } + + const uint32_t pixel = sampleHostFramePixel(scratch, width, height, probe.x, probe.y); + std::cout << " host[" << probe.x << "," << probe.y << "]=0x" + << std::hex << pixel << std::dec; } std::cout << std::endl; + ++s_probeDebugCount; } - s_lastLoggedFbp = selectedFbp; - s_lastLoggedNonBlack = nonBlack; ++s_uploadDebugCount; + }); + s_lastDisplayFbp = displayFbp; + s_lastSourceFbp = sourceFbp; + s_lastPreferred = usedPreferredDisplaySource; + s_lastWidth = width; + s_lastHeight = height; + + std::vector uploadBuffer(DEFAULT_FB_SIZE, 0u); + if (!scratch.empty() && width != 0u && height != 0u) + { + const uint32_t copyWidth = std::min(width, FB_WIDTH); + const uint32_t copyHeight = std::min(height, FB_HEIGHT); + const size_t srcRowBytes = static_cast(width) * 4u; + const size_t dstRowBytes = static_cast(FB_WIDTH) * 4u; + const size_t copyRowBytes = static_cast(copyWidth) * 4u; + for (uint32_t y = 0; y < copyHeight; ++y) + { + const size_t srcOffset = static_cast(y) * srcRowBytes; + const size_t dstOffset = static_cast(y) * dstRowBytes; + if (srcOffset + copyRowBytes > scratch.size() || + dstOffset + copyRowBytes > uploadBuffer.size()) + { + break; + } + std::memcpy(uploadBuffer.data() + dstOffset, scratch.data() + srcOffset, copyRowBytes); + } } - UpdateTexture(tex, scratch.data()); + UpdateTexture(tex, uploadBuffer.data()); + outWidth = width; + outHeight = height; } PS2Runtime::PS2Runtime() @@ -673,16 +547,21 @@ PS2Runtime::~PS2Runtime() m_functionTable.clear(); } -bool PS2Runtime::initialize(const char *title) +bool PS2Runtime::ensureCoreSubsystemsInitialized() { - if (!m_memory.initialize()) + uint8_t *const rdram = m_memory.getRDRAM(); + uint8_t *const gsVram = m_memory.getGSVRAM(); + if (!rdram || !gsVram) { - std::cerr << "Failed to initialize PS2 memory" << std::endl; return false; } - m_gs.init(m_memory.getGSVRAM(), static_cast(PS2_GS_VRAM_SIZE), &m_memory.gs()); - m_gs.reset(); + if (m_boundRdram == rdram && m_boundGSVram == gsVram) + { + return true; + } + + m_gs.init(gsVram, static_cast(PS2_GS_VRAM_SIZE), &m_memory.gs()); m_gifArbiter.setProcessPacketFn([this](const uint8_t *data, uint32_t size) { m_gs.processGIFPacket(data, size); }); m_memory.setGifArbiter(&m_gifArbiter); @@ -694,18 +573,35 @@ bool PS2Runtime::initialize(const char *title) { m_vu1.resume(m_memory.getVU1Code(), PS2_VU1_CODE_SIZE, m_memory.getVU1Data(), PS2_VU1_DATA_SIZE, m_gs, &m_memory, itop, 65536); }); - - m_iop.init(m_memory.getRDRAM()); + m_iop.init(rdram); m_iop.reset(); + m_vu1.reset(); + + m_boundRdram = rdram; + m_boundGSVram = gsVram; + return true; +} + +bool PS2Runtime::initialize(const char *title) +{ + if (!m_memory.initialize()) + { + std::cerr << "Failed to initialize PS2 memory" << std::endl; + return false; + } + + if (!ensureCoreSubsystemsInitialized()) + { + std::cerr << "Failed to bind runtime core subsystems" << std::endl; + return false; + } SetConfigFlags(FLAG_WINDOW_RESIZABLE); - InitWindow(FB_WIDTH, FB_HEIGHT, title); + InitWindow(FB_WIDTH, DEFAULT_DISPLAY_HEIGHT, title); InitAudioDevice(); m_audioBackend.setAudioReady(IsAudioDeviceReady()); SetTargetFPS(60); - m_vu1.reset(); - return true; } @@ -865,11 +761,11 @@ bool PS2Runtime::loadELF(const std::string &elfPath) std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz); } - std::cout << "Loading segment: 0x" << std::hex << ph.vaddr - << " - 0x" << (static_cast(ph.vaddr) + static_cast(ph.memsz)) - << " (filesz: 0x" << ph.filesz - << ", memsz: 0x" << ph.memsz << ")" - << std::dec << std::endl; + RUNTIME_LOG("Loading segment: 0x" << std::hex << ph.vaddr + << " - 0x" << (static_cast(ph.vaddr) + static_cast(ph.memsz)) + << " (filesz: 0x" << ph.filesz + << ", memsz: 0x" << ph.memsz << ")" + << std::dec << std::endl); if (!scratch) { @@ -938,7 +834,7 @@ bool PS2Runtime::loadELF(const std::string &elfPath) ps2_game_overrides::applyMatching(*this, elfPath, m_cpuContext.pc); - std::cout << "ELF file loaded successfully. Entry point: 0x" << std::hex << m_cpuContext.pc << std::dec << std::endl; + RUNTIME_LOG("ELF file loaded successfully. Entry point: 0x" << std::hex << m_cpuContext.pc << std::dec); return true; } @@ -1010,12 +906,6 @@ bool PS2Runtime::hasFunction(uint32_t address) const return true; } - if (address == 0x2913E4u) - { - auto parent = m_functionTable.find(0x2913B0u); - return parent != m_functionTable.end(); - } - return false; } @@ -1029,15 +919,6 @@ PS2Runtime::RecompiledFunction PS2Runtime::lookupFunction(uint32_t address) return it->second; } - if (address == 0x2913E4u) - { - auto parent = m_functionTable.find(0x2913B0u); - if (parent != m_functionTable.end()) - { - return parent->second; - } - } - std::cerr << "Warning: Function at address 0x" << std::hex << address << std::dec << " not found" << std::endl; static RecompiledFunction defaultFunction = [](uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -1186,10 +1067,10 @@ void PS2Runtime::executeVU0Microprogram(uint8_t *rdram, R5900Context *ctx, uint3 int &count = seen[address]; if (count < 3) { - std::cout << "[VU0] microprogram @0x" << std::hex << address - << " pc=0x" << ctx->pc - << " ra=0x" << static_cast(_mm_extract_epi32(ctx->r[31], 0)) - << std::dec << std::endl; + RUNTIME_LOG("[VU0] microprogram @0x" << std::hex << address + << " pc=0x" << ctx->pc + << " ra=0x" << static_cast(_mm_extract_epi32(ctx->r[31], 0)) + << std::dec << std::endl); } ++count; @@ -1781,7 +1662,9 @@ void PS2Runtime::dispatchLoop(uint8_t *rdram, R5900Context *ctx) ++samePcCount; if ((samePcCount % kSamePcYieldInterval) == 0u) { - std::cout << "CPU is doing some work at PC 0x" << std::hex << pc << ". PC not updating." << std::endl; + PS2_IF_AGRESSIVE_LOGS({ + RUNTIME_LOG("CPU is doing some work at PC 0x" << std::hex << pc << ". PC not updating."); + }); std::this_thread::yield(); } } @@ -2048,7 +1931,11 @@ void PS2Runtime::run() { m_stopRequested.store(false, std::memory_order_relaxed); ps2_stubs::resetSifState(); + ps2_syscalls::resetSoundDriverRpcState(); + ps2_stubs::resetAudioStubState(); ps2_stubs::resetGsSyncVCallbackState(); + ps2_stubs::resetMpegStubState(); + ps2_stubs::resetMpegSourceTracking(); ps2_syscalls::initializeGuestKernelState(m_memory.getRDRAM()); m_cpuContext.r[4] = _mm_setzero_si128(); m_cpuContext.r[5] = _mm_setzero_si128(); @@ -2058,7 +1945,7 @@ void PS2Runtime::run() m_debugSp.store(static_cast(_mm_extract_epi32(m_cpuContext.r[29], 0)), std::memory_order_relaxed); m_debugGp.store(static_cast(_mm_extract_epi32(m_cpuContext.r[28], 0)), std::memory_order_relaxed); - std::cout << "Starting execution at address 0x" << std::hex << m_cpuContext.pc << std::dec << std::endl; + RUNTIME_LOG("Starting execution at address 0x" << std::hex << m_cpuContext.pc << std::dec); // A blank image to use as a framebuffer Image blank = GenImageColor(FB_WIDTH, FB_HEIGHT, BLANK); @@ -2075,8 +1962,8 @@ void PS2Runtime::run() { dispatchLoop(m_memory.getRDRAM(), &m_cpuContext); uint32_t pc = m_debugPc.load(std::memory_order_relaxed); - std::cout << "Game thread returned. PC=0x" << std::hex << pc - << " RA=0x" << static_cast(_mm_extract_epi32(m_cpuContext.r[31], 0)) << std::dec << std::endl; + RUNTIME_LOG("Game thread returned. PC=0x" << std::hex << pc + << " RA=0x" << static_cast(_mm_extract_epi32(m_cpuContext.r[31], 0)) << std::dec << std::endl); } catch (const std::exception &e) { @@ -2089,92 +1976,67 @@ void PS2Runtime::run() g_activeThreads.fetch_sub(1, std::memory_order_relaxed); gameThreadFinished.store(true, std::memory_order_release); }); + ps2_syscalls::EnsureVSyncWorkerRunning(m_memory.getRDRAM(), this); + uint64_t tick = 0; while (!isStopRequested() && g_activeThreads.load(std::memory_order_relaxed) > 0) { - tick++; - if ((tick % 120) == 0) - { - uint64_t curDma = m_memory.dmaStartCount(); - uint64_t curGif = m_memory.gifCopyCount(); - uint64_t curGs = m_memory.gsWriteCount(); - uint64_t curVif = m_memory.vifWriteCount(); - const GSRegisters &gs = m_memory.gs(); - const uint32_t dbgPc = m_debugPc.load(std::memory_order_relaxed); - const uint32_t dbgRa = m_debugRa.load(std::memory_order_relaxed); - const uint32_t dbgSp = m_debugSp.load(std::memory_order_relaxed); - const uint32_t dbgGp = m_debugGp.load(std::memory_order_relaxed); - const int activeThreads = g_activeThreads.load(std::memory_order_relaxed); - - constexpr uint32_t kSndTransTypeAddr = 0x01E0E1C0u; - constexpr uint32_t kSndTransBankAddr = 0x01E0E1C8u; - constexpr uint32_t kSndTransLevelAddr = 0x01E0E1B8u; - constexpr uint32_t kSndGetAdrsAddr = 0x01E212D8u; - constexpr uint32_t kSndStatusMirrorAddr = 0x01E213C0u; - constexpr uint32_t kSndSeCheckAddr = 0x01E0EF10u; - constexpr uint32_t kSndMidiCheckAddr = 0x01E0EF20u; - - const uint32_t sndTransType = readGuestU32Wrapped(m_memory.getRDRAM(), kSndTransTypeAddr); - const uint32_t sndTransLevel = readGuestU32Wrapped(m_memory.getRDRAM(), kSndTransLevelAddr); - const uint32_t sndTransBank = readGuestU32Wrapped(m_memory.getRDRAM(), kSndTransBankAddr); - const uint32_t sndGetAdrs = readGuestU32Wrapped(m_memory.getRDRAM(), kSndGetAdrsAddr); - auto readGuestS16 = [&](uint32_t addr) -> int32_t - { - const uint8_t *rdram = m_memory.getRDRAM(); - if (!rdram) - { - return 0; - } - const uint16_t raw = static_cast( - static_cast(rdram[(addr + 0u) & PS2_RAM_MASK]) | - (static_cast(rdram[(addr + 1u) & PS2_RAM_MASK]) << 8)); - return static_cast(raw); - }; - const int32_t sndMirrorMidi0 = readGuestS16(kSndStatusMirrorAddr + 0x1Eu); - const int32_t sndMirrorSe0 = readGuestS16(kSndStatusMirrorAddr + 0x26u); - int32_t sndBankMidiCheck = 0; - int32_t sndBankSeCheck = 0; - if (sndTransBank < 4u) + PS2_IF_AGRESSIVE_LOGS({ + tick++; + if ((tick % 120) == 0) { - sndBankMidiCheck = readGuestS16(kSndMidiCheckAddr + (sndTransBank * 2u)); + uint64_t curDma = m_memory.dmaStartCount(); + uint64_t curGif = m_memory.gifCopyCount(); + uint64_t curGs = m_memory.gsWriteCount(); + uint64_t curVif = m_memory.vifWriteCount(); + const GSRegisters &gs = m_memory.gs(); + const uint32_t dbgPc = m_debugPc.load(std::memory_order_relaxed); + const uint32_t dbgRa = m_debugRa.load(std::memory_order_relaxed); + const uint32_t dbgSp = m_debugSp.load(std::memory_order_relaxed); + const uint32_t dbgGp = m_debugGp.load(std::memory_order_relaxed); + const int activeThreads = g_activeThreads.load(std::memory_order_relaxed); + + std::cout << "[run:tick] tick=" << tick + << " pc=0x" << std::hex << dbgPc + << " ra=0x" << dbgRa + << " sp=0x" << dbgSp + << " gp=0x" << dbgGp + << " dispfb1=0x" << gs.dispfb1 + << " display1=0x" << gs.display1 + << std::dec + << " activeThreads=" << activeThreads + << " dma=" << curDma + << " gif=" << curGif + << " gsw=" << curGs + << " vif=" << curVif + << std::endl; } - if (sndTransBank < 5u) - { - sndBankSeCheck = readGuestS16(kSndSeCheckAddr + (sndTransBank * 2u)); - } - std::cout << "[run:tick] tick=" << tick - << " pc=0x" << std::hex << dbgPc - << " ra=0x" << dbgRa - << " sp=0x" << dbgSp - << " gp=0x" << dbgGp - << " dispfb1=0x" << gs.dispfb1 - << " display1=0x" << gs.display1 - << std::dec - << " activeThreads=" << activeThreads - << " dma=" << curDma - << " gif=" << curGif - << " gsw=" << curGs - << " vif=" << curVif - << " sndType=" << sndTransType - << " sndLvl=" << sndTransLevel - << " sndBank=" << sndTransBank - << " getAdrs=0x" << std::hex << sndGetAdrs << std::dec - << " sndMirrorMidi0=" << sndMirrorMidi0 - << " sndMirrorSe0=" << sndMirrorSe0 - << " sndChkMidi=" << sndBankMidiCheck - << " sndChkSe=" << sndBankSeCheck - << std::endl; - } - UploadFrame(frameTex, this); + }); + uint32_t presentWidth = FB_WIDTH; + uint32_t presentHeight = DEFAULT_DISPLAY_HEIGHT; + UploadFrame(frameTex, this, presentWidth, presentHeight); BeginDrawing(); ClearBackground(BLACK); - DrawTexture(frameTex, 0, 0, WHITE); + const float srcWidth = static_cast(std::max(1u, presentWidth)); + const float srcHeight = static_cast(std::max(1u, presentHeight)); + const float screenWidth = static_cast(GetScreenWidth()); + const float screenHeight = static_cast(GetScreenHeight()); + const float scale = std::min(screenWidth / srcWidth, screenHeight / srcHeight); + const float dstWidth = srcWidth * scale; + const float dstHeight = srcHeight * scale; + const Rectangle srcRect{0.0f, 0.0f, srcWidth, srcHeight}; + const Rectangle dstRect{ + (screenWidth - dstWidth) * 0.5f, + (screenHeight - dstHeight) * 0.5f, + dstWidth, + dstHeight}; + DrawTexturePro(frameTex, srcRect, dstRect, Vector2{0.0f, 0.0f}, 0.0f, WHITE); EndDrawing(); if (WindowShouldClose()) { - std::cout << "[run] window close requested, breaking out of loop" << std::endl; + RUNTIME_LOG("[run] window close requested, breaking out of loop"); requestStop(); break; } @@ -2235,7 +2097,7 @@ void PS2Runtime::run() CloseWindow(); const int remainingThreads = g_activeThreads.load(std::memory_order_relaxed); - std::cout << "[run] exiting loop, activeThreads=" << remainingThreads << std::endl; + RUNTIME_LOG("[run] exiting loop, activeThreads=" << remainingThreads); if (remainingThreads > 0) { std::cerr << "[run] warning: " << remainingThreads diff --git a/ps2xRuntime/src/lib/ps2_stubs.cpp b/ps2xRuntime/src/lib/ps2_stubs.cpp deleted file mode 100644 index d1387e8a..00000000 --- a/ps2xRuntime/src/lib/ps2_stubs.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "ps2_stubs.h" -#include "ps2_runtime.h" -#include "ps2_runtime_macros.h" -#include "ps2_syscalls.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "raylib.h" -#include "stubs/helpers/ps2_stubs_helpers.inl" - -namespace ps2_stubs -{ - namespace - { - constexpr uint8_t kPadModeDualShock = 0x73; - constexpr uint8_t kPadAnalogCenter = 0x80; - - constexpr uint16_t kPadBtnSelect = 1u << 0; - constexpr uint16_t kPadBtnL3 = 1u << 1; - constexpr uint16_t kPadBtnR3 = 1u << 2; - constexpr uint16_t kPadBtnStart = 1u << 3; - constexpr uint16_t kPadBtnUp = 1u << 4; - constexpr uint16_t kPadBtnRight = 1u << 5; - constexpr uint16_t kPadBtnDown = 1u << 6; - constexpr uint16_t kPadBtnLeft = 1u << 7; - constexpr uint16_t kPadBtnL2 = 1u << 8; - constexpr uint16_t kPadBtnR2 = 1u << 9; - constexpr uint16_t kPadBtnL1 = 1u << 10; - constexpr uint16_t kPadBtnR1 = 1u << 11; - constexpr uint16_t kPadBtnTriangle = 1u << 12; - constexpr uint16_t kPadBtnCircle = 1u << 13; - constexpr uint16_t kPadBtnCross = 1u << 14; - constexpr uint16_t kPadBtnSquare = 1u << 15; - - struct PadInputState - { - uint16_t buttons = 0xFFFF; // active-low - uint8_t rx = kPadAnalogCenter; - uint8_t ry = kPadAnalogCenter; - uint8_t lx = kPadAnalogCenter; - uint8_t ly = kPadAnalogCenter; - }; - - std::mutex g_padOverrideMutex; - bool g_padOverrideEnabled = false; - PadInputState g_padOverrideState{}; - bool g_padDebugCached = false; - bool g_padDebugEnabled = false; - - uint8_t axisToByte(float axis) - { - axis = std::clamp(axis, -1.0f, 1.0f); - const float mapped = (axis + 1.0f) * 127.5f; - return static_cast(std::lround(mapped)); - } - - bool padDebugEnabled() - { - if (!g_padDebugCached) - { - const char *env = std::getenv("PS2_PAD_DEBUG"); - g_padDebugEnabled = (env && *env && std::strcmp(env, "0") != 0); - g_padDebugCached = true; - } - return g_padDebugEnabled; - } - - void setButton(PadInputState &state, uint16_t mask, bool pressed) - { - if (pressed) - { - state.buttons = static_cast(state.buttons & ~mask); - } - } - - int findFirstGamepad() - { - for (int i = 0; i < 4; ++i) - { - if (IsGamepadAvailable(i)) - { - return i; - } - } - return -1; - } - - void applyGamepadState(PadInputState &state) - { - if (!IsWindowReady()) - { - return; - } - - const int gamepad = findFirstGamepad(); - if (gamepad < 0) - { - return; - } - - // Raylib mapping (PS2 -> raylib buttons/axes): - // D-Pad -> LEFT_FACE_*, Cross/Circle/Square/Triangle -> RIGHT_FACE_* - // L1/R1 -> TRIGGER_1, L2/R2 -> TRIGGER_2, L3/R3 -> THUMB - // Select/Start -> MIDDLE_LEFT/MIDDLE_RIGHT - state.lx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X)); - state.ly = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y)); - state.rx = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X)); - state.ry = axisToByte(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y)); - - setButton(state, kPadBtnUp, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)); - setButton(state, kPadBtnDown, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)); - setButton(state, kPadBtnLeft, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)); - setButton(state, kPadBtnRight, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)); - - setButton(state, kPadBtnCross, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)); - setButton(state, kPadBtnCircle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)); - setButton(state, kPadBtnSquare, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)); - setButton(state, kPadBtnTriangle, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)); - - setButton(state, kPadBtnL1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)); - setButton(state, kPadBtnR1, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)); - setButton(state, kPadBtnL2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2)); - setButton(state, kPadBtnR2, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)); - - setButton(state, kPadBtnL3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)); - setButton(state, kPadBtnR3, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)); - - setButton(state, kPadBtnSelect, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)); - setButton(state, kPadBtnStart, IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)); - } - - void applyKeyboardState(PadInputState &state, bool allowAnalog) - { - if (!IsWindowReady()) - { - return; - } - - // Keyboard mapping (PS2 -> keys): - // D-Pad: arrows, Square/Cross/Circle/Triangle: Z/X/C/V - // L1/R1: Q/E, L2/R2: 1/3, Start/Select: Enter/RightShift - // L3/R3: LeftCtrl/RightCtrl, Analog left: WASD - setButton(state, kPadBtnUp, IsKeyDown(KEY_UP)); - setButton(state, kPadBtnDown, IsKeyDown(KEY_DOWN)); - setButton(state, kPadBtnLeft, IsKeyDown(KEY_LEFT)); - setButton(state, kPadBtnRight, IsKeyDown(KEY_RIGHT)); - - setButton(state, kPadBtnSquare, IsKeyDown(KEY_Z)); - setButton(state, kPadBtnCross, IsKeyDown(KEY_X)); - setButton(state, kPadBtnCircle, IsKeyDown(KEY_C)); - setButton(state, kPadBtnTriangle, IsKeyDown(KEY_V)); - - setButton(state, kPadBtnL1, IsKeyDown(KEY_Q)); - setButton(state, kPadBtnR1, IsKeyDown(KEY_E)); - setButton(state, kPadBtnL2, IsKeyDown(KEY_ONE)); - setButton(state, kPadBtnR2, IsKeyDown(KEY_THREE)); - - setButton(state, kPadBtnStart, IsKeyDown(KEY_ENTER)); - setButton(state, kPadBtnSelect, IsKeyDown(KEY_RIGHT_SHIFT)); - setButton(state, kPadBtnL3, IsKeyDown(KEY_LEFT_CONTROL)); - setButton(state, kPadBtnR3, IsKeyDown(KEY_RIGHT_CONTROL)); - - if (!allowAnalog) - { - return; - } - - float ax = 0.0f; - float ay = 0.0f; - if (IsKeyDown(KEY_D)) - ax += 1.0f; - if (IsKeyDown(KEY_A)) - ax -= 1.0f; - if (IsKeyDown(KEY_S)) - ay += 1.0f; - if (IsKeyDown(KEY_W)) - ay -= 1.0f; - - if (ax != 0.0f || ay != 0.0f) - { - state.lx = axisToByte(ax); - state.ly = axisToByte(ay); - } - } - - void fillPadStatus(uint8_t *data, const PadInputState &state) - { - std::memset(data, 0, 32); - data[1] = kPadModeDualShock; - data[2] = static_cast(state.buttons & 0xFFu); - data[3] = static_cast((state.buttons >> 8) & 0xFFu); - data[4] = state.rx; - data[5] = state.ry; - data[6] = state.lx; - data[7] = state.ly; - } - } - -#include "stubs/ps2_stubs_libc.inl" -#include "stubs/ps2_stubs_ps2.inl" -#include "stubs/ps2_stubs_misc.inl" - -#include "stubs/ps2_stubs_gs.inl" -#include "stubs/ps2_stubs_residentEvilCV.inl" - - void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("unknown", rdram, ctx, runtime); - } - - void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const std::string stubName = name ? name : "unknown"; - - uint32_t callCount = 0; - { - std::lock_guard lock(g_stubWarningMutex); - callCount = ++g_stubWarningCount[stubName]; - } - - if (callCount > kMaxStubWarningsPerName) - { - if (callCount == (kMaxStubWarningsPerName + 1)) - { - std::cerr << "Warning: Further calls to PS2 stub '" << stubName - << "' are suppressed after " << kMaxStubWarningsPerName << " warnings" << std::endl; - } - setReturnS32(ctx, -1); - return; - } - - uint32_t stub_num = getRegU32(ctx, 2); // $v0 - uint32_t caller_ra = getRegU32(ctx, 31); // $ra - - std::cerr << "Warning: Unimplemented PS2 stub called. name=" << stubName - << " PC=0x" << std::hex << ctx->pc - << ", RA=0x" << caller_ra - << ", Stub# guess (from $v0)=0x" << stub_num << std::dec << std::endl; - - // More context for debugging - std::cerr << " Args: $a0=0x" << std::hex << getRegU32(ctx, 4) - << ", $a1=0x" << getRegU32(ctx, 5) - << ", $a2=0x" << getRegU32(ctx, 6) - << ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl; - - setReturnS32(ctx, -1); // Return error - } - - void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry) - { - std::lock_guard lock(g_padOverrideMutex); - g_padOverrideEnabled = true; - g_padOverrideState.buttons = buttons; - g_padOverrideState.lx = lx; - g_padOverrideState.ly = ly; - g_padOverrideState.rx = rx; - g_padOverrideState.ry = ry; - } - - void clearPadOverrideState() - { - std::lock_guard lock(g_padOverrideMutex); - g_padOverrideEnabled = false; - g_padOverrideState = PadInputState{}; - } - -} diff --git a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp index 115c3ffa..41a32f0d 100644 --- a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp +++ b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp @@ -1,9 +1,8 @@ // Based on Blackline Interactive implementation -#include "ps2_memory.h" +#include "runtime/ps2_memory.h" #include #include -#include -#include +#include "ps2_log.h" enum VIFCmd : uint8_t { @@ -78,13 +77,13 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) const uint32_t opcodeIndex = s_debugVif1OpcodeCount.fetch_add(1, std::memory_order_relaxed); if (opcodeIndex < 160u) { - std::cout << "[vif1:cmd] idx=" << opcodeIndex + RUNTIME_LOG("[vif1:cmd] idx=" << opcodeIndex << " opcode=0x" << std::hex << static_cast(opcode) << " imm=0x" << imm << std::dec << " num=" << static_cast(num) << " irq=" << static_cast(irq ? 1u : 0u) - << std::endl; + << std::endl); } // Track most-recent command for VIFn_CODE emulation. @@ -155,12 +154,12 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) const uint32_t kickIndex = s_debugVu1KickCount.fetch_add(1, std::memory_order_relaxed); if (kickIndex < 48u) { - std::cout << "[vif1:mscal] idx=" << kickIndex + RUNTIME_LOG("[vif1:mscal] idx=" << kickIndex << " opcode=0x" << std::hex << static_cast(opcode) << " imm=0x" << imm << " startPc=0x" << startPC << " itop=0x" << vif1_regs.itop - << std::dec << std::endl; + << std::dec << std::endl); } if (m_vu1MscalCallback) m_vu1MscalCallback(startPC, vif1_regs.itop); @@ -174,10 +173,10 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) const uint32_t kickIndex = s_debugVu1KickCount.fetch_add(1, std::memory_order_relaxed); if (kickIndex < 48u) { - std::cout << "[vif1:mscnt] idx=" << kickIndex + RUNTIME_LOG("[vif1:mscnt] idx=" << kickIndex << " itop=0x" << std::hex << vif1_regs.itop << " pc=resume" - << std::dec << std::endl; + << std::dec << std::endl); } if (m_vu1MscntCallback) m_vu1MscntCallback(vif1_regs.itop); diff --git a/ps2xRuntime/src/lib/ps2_vu1.cpp b/ps2xRuntime/src/lib/ps2_vu1.cpp index ed085209..bfd626e7 100644 --- a/ps2xRuntime/src/lib/ps2_vu1.cpp +++ b/ps2xRuntime/src/lib/ps2_vu1.cpp @@ -1,7 +1,8 @@ -#include "ps2_vu1.h" -#include "ps2_gs_gpu.h" -#include "ps2_gif_arbiter.h" -#include "ps2_memory.h" +#include "runtime/ps2_vu1.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_gif_arbiter.h" +#include "runtime/ps2_memory.h" +#include "ps2_log.h" #include #include #include @@ -1225,12 +1226,12 @@ void VU1Interpreter::execLower(uint32_t instr, uint8_t *vuData, uint32_t dataSiz const uint32_t debugIndex = s_debugVu1XgkickCount.fetch_add(1, std::memory_order_relaxed); if (debugIndex < 64u) { - std::cout << "[vu1:xgkick] idx=" << debugIndex + RUNTIME_LOG("[vu1:xgkick] idx=" << debugIndex << " addr=0x" << std::hex << addr << " totalBytes=0x" << totalBytes << std::dec << " wrap=" << static_cast((addr + totalBytes > dataSize) ? 1u : 0u) - << std::endl; + << std::endl); } if (addr + totalBytes <= dataSize) diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl deleted file mode 100644 index cfaffa07..00000000 --- a/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl +++ /dev/null @@ -1,4430 +0,0 @@ -void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t count = getRegU32(ctx, 5); // $a1 - const uint32_t size = getRegU32(ctx, 6); // $a2 - const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; - setReturnU32(ctx, guestAddr); -} - -void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, 0u); - ctx->pc = getRegU32(ctx, 31); -} - -void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, 1u); - ctx->pc = getRegU32(ctx, 31); -} - -void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 4)); - ctx->pc = getRegU32(ctx, 31); -} - -void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t guestAddr = getRegU32(ctx, 5); // $a1 - if (runtime && guestAddr != 0u) - { - runtime->guestFree(guestAddr); - } -} - -void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t size = getRegU32(ctx, 5); // $a1 - const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; - setReturnU32(ctx, guestAddr); -} - -void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t wcAddr = getRegU32(ctx, 5); // $a1 - const uint32_t strAddr = getRegU32(ctx, 6); // $a2 - const int32_t n = static_cast(getRegU32(ctx, 7)); // $a3 - if (n <= 0 || strAddr == 0u) - { - setReturnS32(ctx, 0); - return; - } - - const uint8_t *src = getConstMemPtr(rdram, strAddr); - if (!src) - { - setReturnS32(ctx, -1); - return; - } - - const uint8_t ch = *src; - if (wcAddr != 0u) - { - if (uint8_t *dst = getMemPtr(rdram, wcAddr)) - { - const uint32_t out = static_cast(ch); - std::memcpy(dst, &out, sizeof(out)); - } - } - setReturnS32(ctx, (ch == 0u) ? 0 : 1); -} - -void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - if (rendered.size() > 2048) - { - rendered.resize(2048); - } - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - std::cout << "PS2 printf: " << logLine; - std::cout << std::flush; - } - else if (count == kMaxPrintfLogs + 1) - { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; - } - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; - } - - setReturnS32(ctx, ret); -} - -void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceCdRI", rdram, ctx, runtime); -} - -void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceCdRM", rdram, ctx, runtime); -} - -void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsDbChk", rdram, ctx, runtime); -} - -void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsIntrSigSema", rdram, ctx, runtime); -} - -void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsSemExit", rdram, ctx, runtime); -} - -void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsSemInit", rdram, ctx, runtime); -} - -void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsSigSema", rdram, ctx, runtime); -} - -void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegFlush", rdram, ctx, runtime); -} - -void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceRpcFreePacket", rdram, ctx, runtime); -} - -void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceRpcGetFPacket", rdram, ctx, runtime); -} - -void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceRpcGetFPacket2", rdram, ctx, runtime); -} - -void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSifCmdIntrHdlr", rdram, ctx, runtime); -} - -void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifLoadModule(rdram, ctx, runtime); -} - -void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t srcAddr = getRegU32(ctx, 7); // $a3 - const uint32_t dstAddr = readStackU32(rdram, ctx, 16); - const uint32_t size = readStackU32(rdram, ctx, 20); - if (size != 0u && srcAddr != 0u && dstAddr != 0u) - { - for (uint32_t i = 0; i < size; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - uint8_t *dst = getMemPtr(rdram, dstAddr + i); - if (!src || !dst) - { - break; - } - *dst = *src; - } - } - - setReturnS32(ctx, 1); -} - -void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime); -} - -void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t value = static_cast(getRegU32(ctx, 4)); - if (value == std::numeric_limits::min()) - { - setReturnS32(ctx, std::numeric_limits::max()); - return; - } - setReturnS32(ctx, value < 0 ? -value : value); -} - -void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float in = ctx ? ctx->f[12] : 0.0f; - if (in == 0.0f) - { - uint32_t raw = getRegU32(ctx, 4); - std::memcpy(&in, &raw, sizeof(in)); - } - const float out = std::atan(in); - if (ctx) - { - ctx->f[0] = out; - } - - uint32_t outRaw = 0u; - std::memcpy(&outRaw, &out, sizeof(outRaw)); - setReturnU32(ctx, outRaw); -} - -void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioClose(rdram, ctx, runtime); -} - -void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 4)); -} - -void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - if (runtime) - { - runtime->requestStop(); - } - setReturnS32(ctx, 0); -} - -void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t statAddr = getRegU32(ctx, 5); - if (uint8_t *statBuf = getMemPtr(rdram, statAddr)) - { - std::memset(statBuf, 0, 128); - setReturnS32(ctx, 0); - return; - } - setReturnS32(ctx, -1); -} - -void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void iopGetArea(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, kIopHeapBase); -} - -void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioLseek(rdram, ctx, runtime); -} - -void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t srcAddr = getRegU32(ctx, 4); - const uint8_t needle = static_cast(getRegU32(ctx, 5) & 0xFFu); - const uint32_t size = getRegU32(ctx, 6); - - for (uint32_t i = 0; i < size; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - if (!src) - { - break; - } - if (*src == needle) - { - setReturnU32(ctx, srcAddr + i); - return; - } - } - - setReturnU32(ctx, 0u); -} - -void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioOpen(rdram, ctx, runtime); -} - -void Pad_init(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void Pad_set(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, std::rand() & 0x7FFF); -} - -namespace -{ - std::mutex g_mcStateMutex; - int32_t g_mcNextFd = 1; - int32_t g_mcLastResult = 0; -} - -void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioRead(rdram, ctx, runtime); -} - -void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 2); -} - -void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - // SCECdPS2DVD - setReturnS32(ctx, 0x14); -} - -void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, g_cdStreamingLbn); -} - -void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t tocAddr = getRegU32(ctx, 4); - if (uint8_t *toc = getMemPtr(rdram, tocAddr)) - { - std::memset(toc, 0, 1024); - } - setReturnS32(ctx, 1); -} - -void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdInitialized = true; - g_lastCdError = 0; - setReturnS32(ctx, 1); -} - -void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t lsn = getRegU32(ctx, 4); - uint32_t posAddr = getRegU32(ctx, 5); - uint8_t *pos = getMemPtr(rdram, posAddr); - if (!pos) - { - setReturnS32(ctx, 0); - return; - } - - uint32_t adjusted = lsn + 150; - const uint32_t minutes = adjusted / (60 * 75); - adjusted %= (60 * 75); - const uint32_t seconds = adjusted / 75; - const uint32_t sectors = adjusted % 75; - - pos[0] = toBcd(minutes); - pos[1] = toBcd(seconds); - pos[2] = toBcd(sectors); - pos[3] = 0; - setReturnS32(ctx, 1); -} - -void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdMode = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 2); -} - -void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t posAddr = getRegU32(ctx, 4); - const uint8_t *pos = getConstMemPtr(rdram, posAddr); - if (!pos) - { - setReturnS32(ctx, -1); - return; - } - - const uint32_t minutes = fromBcd(pos[0]); - const uint32_t seconds = fromBcd(pos[1]); - const uint32_t sectors = fromBcd(pos[2]); - const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; - const int32_t lsn = static_cast(absolute) - 150; - setReturnS32(ctx, lsn); -} - -void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t chainAddr = getRegU32(ctx, 4); - bool ok = true; - - for (int i = 0; i < 64; ++i) - { - uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); - if (!entry) - { - ok = false; - break; - } - - const uint32_t lbn = entry[0]; - const uint32_t sectors = entry[1]; - const uint32_t buf = entry[2]; - if (lbn == 0xFFFFFFFFu || sectors == 0) - { - break; - } - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } - - if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) - { - ok = false; - break; - } - - g_cdStreamingLbn = lbn + sectors; - } - - setReturnS32(ctx, ok ? 1 : 0); -} - -void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clockAddr = getRegU32(ctx, 4); - uint8_t *clockData = getMemPtr(rdram, clockAddr); - if (!clockData) - { - setReturnS32(ctx, 0); - return; - } - - std::time_t now = std::time(nullptr); - std::tm localTm{}; -#ifdef _WIN32 - localtime_s(&localTm, &now); -#else - localtime_r(&now, &localTm); -#endif - - // sceCdCLOCK format (BCD fields). - clockData[0] = 0; - clockData[1] = toBcd(static_cast(localTm.tm_sec)); - clockData[2] = toBcd(static_cast(localTm.tm_min)); - clockData[3] = toBcd(static_cast(localTm.tm_hour)); - clockData[4] = 0; - clockData[5] = toBcd(static_cast(localTm.tm_mday)); - clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); - clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); - setReturnS32(ctx, 1); -} - -void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - sceCdRead(rdram, ctx, runtime); -} - -void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t fileAddr = getRegU32(ctx, 4); - uint32_t pathAddr = getRegU32(ctx, 5); - const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); - const std::string normalizedPath = normalizeCdPathNoPrefix(path); - static uint32_t traceCount = 0; - const uint32_t callerRa = getRegU32(ctx, 31); - const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); - if (shouldTrace) - { - std::cout << "[sceCdSearchFile] pc=0x" << std::hex << ctx->pc - << " ra=0x" << callerRa - << " file=0x" << fileAddr - << " pathAddr=0x" << pathAddr - << " path=\"" << sanitizeForLog(path) << "\"" - << std::dec << std::endl; - } - ++traceCount; - - if (path.empty()) - { - static uint32_t emptyPathCount = 0; - if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) - { - std::ostringstream preview; - preview << std::hex; - for (uint32_t i = 0; i < 16; ++i) - { - const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); - preview << (i == 0 ? "" : " ") << static_cast(byte); - } - std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr - << " preview=" << preview.str() - << " ra=0x" << callerRa << std::dec << std::endl; - } - ++emptyPathCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - if (normalizedPath.empty()) - { - static uint32_t emptyNormalizedCount = 0; - if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) - { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (normalized path is empty, root: " << getCdRootPath().string() << ")" - << std::endl; - } - ++emptyNormalizedCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - CdFileEntry entry; - bool found = registerCdFile(path, entry); - CdFileEntry resolvedEntry = entry; - std::string resolvedPath; - bool usedRemapFallback = false; - - // Remap is fallback-only: if the requested .IDX exists, keep it. - // This avoids feeding AFS payload sectors to code that expects IDX metadata. - if (!found) - { - const CdFileEntry missingEntry{}; - if (tryRemapGdInitSearchToAfs(path, callerRa, missingEntry, resolvedEntry, resolvedPath)) - { - found = true; - usedRemapFallback = true; - } - } - - if (!found) - { - static std::string lastFailedPath; - static uint32_t samePathFailCount = 0; - if (path == lastFailedPath) - { - ++samePathFailCount; - } - else - { - lastFailedPath = path; - samePathFailCount = 1; - } - - if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) - { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (root: " << getCdRootPath().string() - << ", repeat=" << samePathFailCount << ")" << std::endl; - } - setReturnS32(ctx, 0); - return; - } - - if (usedRemapFallback) - { - std::cout << "[sceCdSearchFile] remap gd-init search \"" << sanitizeForLog(path) - << "\" -> \"" << sanitizeForLog(resolvedPath) << "\"" << std::endl; - } - - if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) - { - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - g_cdStreamingLbn = resolvedEntry.baseLbn; - if (shouldTrace) - { - std::cout << "[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) - << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn - << " size=0x" << resolvedEntry.sizeBytes - << " sectors=0x" << resolvedEntry.sectors - << std::dec << std::endl; - } - setReturnS32(ctx, 1); -} - -void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_cdInitialized ? 6 : 0); -} - -void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t sectors = getRegU32(ctx, 4); - uint32_t buf = getRegU32(ctx, 5); - uint32_t errAddr = getRegU32(ctx, 7); - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } - - const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); - if (ok) - { - g_cdStreamingLbn += sectors; - } - - if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) - { - *err = ok ? 0 : g_lastCdError; - } - - setReturnS32(ctx, ok ? static_cast(sectors) : 0); -} - -void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t statusPtr = getRegU32(ctx, 5); - if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) - { - *status = 0; - } - setReturnS32(ctx, 1); -} - -void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioClose(rdram, ctx, runtime); -} - -void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2Close", rdram, ctx, runtime); -} - -void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime); -} - -void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime); -} - -void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime); -} - -void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime); -} - -void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime); -} - -void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2Open", rdram, ctx, runtime); -} - -void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime); -} - -void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime); -} - -void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaCallback", rdram, ctx, runtime); -} - -void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaDebug", rdram, ctx, runtime); -} - -void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t chanArg = getRegU32(ctx, 4); - const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); - setReturnU32(ctx, channelBase); -} - -void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); -} - -void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime); -} - -void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaPause", rdram, ctx, runtime); -} - -void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); -} - -void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); -} - -void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRecv", rdram, ctx, runtime); -} - -void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime); -} - -void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime); -} - -void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRestart", rdram, ctx, runtime); -} - -void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); -} - -void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); -} - -void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); -} - -void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true)); -} - -void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); -} - -void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); -} - -void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaWatch", rdram, ctx, runtime); -} - -void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -static void writeU32AtGp(uint8_t *rdram, uint32_t gp, int32_t offset, uint32_t value) -{ - const uint32_t addr = gp + static_cast(offset); - if (uint8_t *p = getMemPtr(rdram, addr)) - *reinterpret_cast(p) = value; -} - -void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - const uint32_t a0 = getRegU32(ctx, 4); - const uint32_t a1 = getRegU32(ctx, 5); - const uint32_t a2 = getRegU32(ctx, 6); - const uint32_t a3 = getRegU32(ctx, 7); - writeU32AtGp(rdram, gp, -0x7b60, a1); - writeU32AtGp(rdram, gp, -0x7b5c, a2); - writeU32AtGp(rdram, gp, -0x7b64, a0); - writeU32AtGp(rdram, gp, -0x7c98, a3); - writeU32AtGp(rdram, gp, -0x7b4c, 0x7f7f7f7f); - writeU32AtGp(rdram, gp, -0x7b50, 0x3f800000); - writeU32AtGp(rdram, gp, -0x7b54, 0x3f800000); - writeU32AtGp(rdram, gp, -0x7b58, 0); - - if (runtime && a0 != 0u) - { - if ((a0 * 256u) + 64u <= PS2_GS_VRAM_SIZE) - { - uint32_t clutData[16]; - for (uint32_t i = 0; i < 16u; ++i) - { - uint8_t alpha = static_cast((i * 0x80u) / 15u); - clutData[i] = (i == 0) - ? 0x00000000u - : (0x80u | (0x80u << 8) | (0x80u << 16) | (static_cast(alpha) << 24)); - } - constexpr uint32_t kClutQwc = 4u; - constexpr uint32_t kHeaderQwc = 6u; - constexpr uint32_t kTotalQwc = kHeaderQwc + kClutQwc; - uint32_t pktAddr = runtime->guestMalloc(kTotalQwc * 16u, 16u); - if (pktAddr != 0u) - { - uint8_t *pkt = getMemPtr(rdram, pktAddr); - if (pkt) - { - uint64_t *q = reinterpret_cast(pkt); - const uint32_t dbp = a0 & 0x3FFFu; - constexpr uint8_t psm = 0u; - q[0] = (4ULL << 60) | (1ULL << 56) | 1ULL; - q[1] = 0x0E0E0E0E0E0E0E0EULL; - q[2] = (static_cast(dbp) << 32) | (1ULL << 48) | (static_cast(psm) << 56); - q[3] = 0x50ULL; - q[4] = 0ULL; - q[5] = 0x51ULL; - q[6] = 16ULL | (1ULL << 32); - q[7] = 0x52ULL; - q[8] = 0ULL; - q[9] = 0x53ULL; - q[10] = (2ULL << 58) | (kClutQwc & 0x7FFF) | (1ULL << 15); - q[11] = 0ULL; - std::memcpy(pkt + 12u * 8u, clutData, 64u); - constexpr uint32_t GIF_CHANNEL = 0x1000A000; - constexpr uint32_t CHCR_STR_MODE0 = 0x101u; - runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, kTotalQwc & 0xFFFFu); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - runtime->memory().processPendingTransfers(); - } - } - } - } - - setReturnS32(ctx, static_cast(a0 + 4)); -} - -void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static constexpr uint32_t kFontBase = 0x176148u; - static constexpr uint32_t kFontEntrySz = 0x24u; - - const uint32_t fontDataAddr = getRegU32(ctx, 4); - const int fontId = static_cast(getRegU32(ctx, 5)); - const int tbp0 = static_cast(getRegU32(ctx, 7)); - - if (!fontDataAddr || !runtime) - { - setReturnS32(ctx, tbp0); - return; - } - - const uint8_t *fontPtr = getConstMemPtr(rdram, fontDataAddr); - if (!fontPtr) - { - setReturnS32(ctx, tbp0); - return; - } - - int width = static_cast(*reinterpret_cast(fontPtr + 0x00u)); - int height = static_cast(*reinterpret_cast(fontPtr + 0x04u)); - uint32_t raw8 = *reinterpret_cast(fontPtr + 0x08u); - int fontDataSz = static_cast(*reinterpret_cast(fontPtr + 0x0cu)); - - uint32_t pointsize = raw8; - uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); - if (raw8 & 0x40000000u) - { - pointsize = raw8 - 0x40000000u; - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) - *reinterpret_cast(p) = 1u; - } - else - { - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) - *reinterpret_cast(p) = 0u; - } - - int tw = (width >= 0) ? (width >> 6) : ((width + 0x3f) >> 6); - int qwc = (fontDataSz >= 0) ? (fontDataSz >> 4) : ((fontDataSz + 0xf) >> 4); - - uint32_t glyphSrc = fontDataAddr + static_cast(fontDataSz) + 0x10u; - uint32_t glyphAlloc = runtime->guestMalloc(0x2010u, 0x40u); - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) - *reinterpret_cast(p) = glyphAlloc; - - if (glyphAlloc != 0u) - { - uint8_t *dst = getMemPtr(rdram, glyphAlloc); - const uint8_t *src = getConstMemPtr(rdram, glyphSrc); - if (dst && src) - std::memcpy(dst, src, 0x2010u); - } - - uint32_t isDoubleByte = 0; - if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff + 0x20u)) - isDoubleByte = *reinterpret_cast(p); - if (isDoubleByte == 0u) - { - uint32_t kernSrc = glyphSrc + 0x2010u; - uint32_t kernAlloc = runtime->guestMalloc(0xc400u, 0x40u); - if (glyphAlloc != 0u) - *reinterpret_cast(getMemPtr(rdram, glyphAlloc + 0x2000u)) = kernAlloc; - if (kernAlloc != 0u) - { - uint8_t *dst = getMemPtr(rdram, kernAlloc); - const uint8_t *src = getConstMemPtr(rdram, kernSrc); - if (dst && src) - std::memcpy(dst, src, 0xc400u); - } - } - - auto writeFontField = [&](uint32_t off, uint32_t val) - { - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + off)) - *reinterpret_cast(p) = val; - }; - writeFontField(0x18u, pointsize); - writeFontField(0x08u, static_cast(tbp0)); - writeFontField(0x0cu, static_cast(tw)); - - int logW = 0; - for (int w = width; w != 1 && w != 0; w = static_cast(static_cast(w) >> 1)) - logW++; - writeFontField(0x10u, static_cast(logW)); - - int logH = 0; - for (int h = height; h != 1 && h != 0; h = static_cast(static_cast(h) >> 1)) - logH++; - writeFontField(0x14u, static_cast(logH)); - writeFontField(0x04u, 0u); - writeFontField(0x1cu, getRegU32(ctx, 6)); - - if (qwc > 0) - { - const uint32_t imageBytes = static_cast(qwc) * 16u; - const uint8_t psm = 20u; - const uint32_t headerQwc = 12u; - const uint32_t imageQwc = static_cast(qwc); - const uint32_t totalQwc = headerQwc + imageQwc; - uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u); - if (pktAddr != 0u) - { - uint8_t *pkt = getMemPtr(rdram, pktAddr); - const uint8_t *imgSrc = getConstMemPtr(rdram, fontDataAddr + 0x10u); - if (pkt && imgSrc) - { - uint64_t *q = reinterpret_cast(pkt); - const uint32_t dbp = static_cast(tbp0) & 0x3FFFu; - const uint32_t dbw = static_cast(tw > 0 ? tw : 1) & 0x3Fu; - const uint32_t rrw = static_cast(width > 0 ? width : 64); - const uint32_t rrh = static_cast(height > 0 ? height : 1); - - q[0] = (4ULL << 60) | (1ULL << 56) | 1ULL; - q[1] = 0x0E0E0E0E0E0E0E0EULL; - q[2] = (static_cast(psm) << 24) | (1ULL << 16) | - (static_cast(dbp) << 32) | (static_cast(dbw) << 48) | - (static_cast(psm) << 56); - q[3] = 0x50ULL; - q[4] = 0ULL; - q[5] = 0x51ULL; - q[6] = (static_cast(rrh) << 32) | static_cast(rrw); - q[7] = 0x52ULL; - q[8] = 0ULL; - q[9] = 0x53ULL; - q[10] = (2ULL << 58) | (imageQwc & 0x7FFF) | (1ULL << 15); - q[11] = 0ULL; - std::memcpy(pkt + 12 * 8, imgSrc, imageBytes); - - constexpr uint32_t GIF_CHANNEL = 0x1000A000; - constexpr uint32_t CHCR_STR_MODE0 = 0x101u; - runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - } - } - } - - int retTbp = tbp0 + ((fontDataSz >= 0 ? fontDataSz : fontDataSz + 0x7f) >> 7); - setReturnS32(ctx, retTbp); -} - -static constexpr uint32_t kFontBase = 0x176148u; -static constexpr uint32_t kFontEntrySz = 0x24u; - -void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const float sclx = ctx->f[12]; - const float scly = ctx->f[13]; - const uint32_t bufAddr = getRegU32(ctx, 4); - const uint64_t paramX = GPR_U64(ctx, 5); - const int64_t paramY = GPR_S64(ctx, 6); - const int paramW = static_cast(getRegU32(ctx, 7)); - const int paramH = static_cast(getRegU32(ctx, 8)); - const uint32_t colour = getRegU32(ctx, 9); - const int alignCh = static_cast(getRegU32(ctx, 10) & 0xffu); - const int fontId = static_cast(getRegU32(ctx, 11)); - - const uint32_t sp = getRegU32(ctx, 29); - const uint32_t strAddr = FAST_READ32(sp + 0x00u); - const uint32_t param14 = FAST_READ32(sp + 0x18u); - - if (bufAddr == 0u) - { - setReturnS32(ctx, 0); - ctx->pc = getRegU32(ctx, 31); - return; - } - - const uint32_t gp = getRegU32(ctx, 28); - const uint32_t fontModeAdj = FAST_READ32(gp + static_cast(static_cast(-0x7c98))); - const uint32_t shiftAmt = fontModeAdj & 0x1fu; - const int scrHeight = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b5c)))); - const int scrWidth = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b60)))); - const uint32_t fontClut = FAST_READ32(gp + static_cast(static_cast(-0x7b64))); - - const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); - const int lineH = static_cast(FAST_READ32(kFontBase + fontOff + 0x18u)); - - int iVar21 = 0; - int iStack_dc = 0; - uint32_t uStack_d8 = 0; - int iVar15 = 0; - - int16_t sVar8; - { - int yStepRaw = static_cast(static_cast((lineH + 6) * 16) * scly); - sVar8 = static_cast((static_cast(paramY) + 0x700) * 16) + static_cast(yStepRaw >> static_cast(shiftAmt)); - } - - int16_t baseX = static_cast((static_cast(paramX) + 0x6c0) * 16); - - if (param14 != 0u) - { - int64_t clipY1 = static_cast(static_cast(paramY) + paramH); - int64_t clipX1 = static_cast(static_cast(paramX) + paramW); - if (clipY1 > scrHeight - 1) clipY1 = static_cast(scrHeight - 1); - if (clipX1 > scrWidth - 1) clipX1 = static_cast(scrWidth - 1); - int64_t clipY0 = 0; - if (paramY > 0) clipY0 = paramY; - uint64_t clipX0 = 0; - if (static_cast(paramX) > 0) clipX0 = paramX; - - uint64_t scissor = clipX0 | (static_cast(static_cast(clipX1)) << 16) - | (static_cast(static_cast(clipY0)) << 32) | (static_cast(static_cast(clipY1)) << 48); - - FAST_WRITE64(bufAddr + 0x00, 0x1000000000000005ull); - FAST_WRITE64(bufAddr + 0x08, 0x0eull); - FAST_WRITE64(bufAddr + 0x10, scissor); - FAST_WRITE64(bufAddr + 0x18, 0x40ull); - FAST_WRITE64(bufAddr + 0x20, 0x20000ull); - FAST_WRITE64(bufAddr + 0x28, 0x47ull); - FAST_WRITE64(bufAddr + 0x30, 0x44ull); - FAST_WRITE64(bufAddr + 0x38, 0x42ull); - FAST_WRITE64(bufAddr + 0x40, 0x160ull); - FAST_WRITE64(bufAddr + 0x48, 0x14ull); - FAST_WRITE64(bufAddr + 0x50, 0x156ull); - FAST_WRITE64(bufAddr + 0x58, 0ull); - FAST_WRITE64(bufAddr + 0x60, 0x1000000000000001ull); - FAST_WRITE64(bufAddr + 0x68, 0x0eull); - - uint64_t iVar5 = static_cast(FAST_READ32(kFontBase + fontOff + 0x08u)); - uint64_t iVar22 = static_cast(FAST_READ32(kFontBase + fontOff + 0x0cu)); - uint64_t iVar3 = static_cast(FAST_READ32(kFontBase + fontOff + 0x10u)); - uint64_t iVar4 = static_cast(FAST_READ32(kFontBase + fontOff + 0x14u)); - - uint64_t tex0 = iVar5 - | 0x2000000000000000ull - | (iVar22 << 14) - | 0x400000000ull - | (iVar3 << 26) - | 0x1400000ull - | (iVar4 << 30) - | (static_cast(fontClut) << 37); - - FAST_WRITE64(bufAddr + 0x70, tex0); - FAST_WRITE64(bufAddr + 0x78, 6ull); - FAST_WRITE64(bufAddr + 0x80, 0x1000000000000001ull); - FAST_WRITE64(bufAddr + 0x88, 0x0eull); - FAST_WRITE64(bufAddr + 0x90, static_cast(colour)); - FAST_WRITE64(bufAddr + 0x98, 1ull); - - iVar21 = 10; - } - - int iVar22_qw = iVar21 + 1; - uint32_t s2 = bufAddr + static_cast(iVar22_qw * 16); - uint32_t uVar20 = 0; - - size_t sLen = 0; - { - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - if (hostStr) sLen = ::strlen(hostStr); - } - - while (uVar20 < sLen) - { - uint8_t bVar1 = FAST_READ8(strAddr + uVar20); - uint32_t uVar9 = static_cast(bVar1); - int8_t chSigned = static_cast(bVar1); - - if (uStack_d8 < 0x21u) - { - goto label_check_printable; - } - - if (uVar9 > 0x20u) - { - uint32_t dat176168 = FAST_READ32(kFontBase + fontOff + 0x20u); - if (dat176168 == 0u) - { - uint32_t fontPtr0 = FAST_READ32(kFontBase + fontOff); - uint32_t tableAddr = FAST_READ32(fontPtr0 + 0x2000u); - int8_t kern = static_cast(FAST_READ8(tableAddr - 0x1c20u + uStack_d8 * 0xe0u + uVar9)); - iVar15 += static_cast(static_cast(static_cast(kern)) * sclx); - } - goto label_check_printable; - } - - goto label_space; - -label_check_printable: - if (uVar9 < 0x21u) - { - goto label_space; - } - - { - int glyphIdx = static_cast(chSigned); - uint32_t iVar19_off = static_cast(glyphIdx * 0x20); - - if (param14 != 0u) - { - uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); - int16_t sVar7 = baseX + static_cast(iVar15); - - iVar22_qw += 2; - iStack_dc += 1; - - uint16_t wU0 = FAST_READ16(fontPtr + iVar19_off + 0); - uint16_t wV0 = FAST_READ16(fontPtr + iVar19_off + 2); - FAST_WRITE16(s2 + 0x00, wU0); - FAST_WRITE16(s2 + 0x02, wV0); - - int16_t dx0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 8)); - int16_t dy0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 10)); - uint16_t wX0 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx0)) * sclx))); - int yVal0 = static_cast(static_cast(static_cast(dy0)) * scly) >> static_cast(shiftAmt); - uint16_t wY0 = static_cast(sVar8 + static_cast(yVal0)); - FAST_WRITE16(s2 + 0x08, wX0); - FAST_WRITE16(s2 + 0x0a, wY0); - FAST_WRITE32(s2 + 0x0c, 1u); - - s2 += 0x10u; - - uint16_t wU1 = FAST_READ16(fontPtr + iVar19_off + 4); - uint16_t wV1 = FAST_READ16(fontPtr + iVar19_off + 6); - FAST_WRITE16(s2 + 0x00, wU1); - FAST_WRITE16(s2 + 0x02, wV1); - - int16_t dx1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 12)); - int16_t dy1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 14)); - uint16_t wX1 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx1)) * sclx))); - int yVal1 = static_cast(static_cast(static_cast(dy1)) * scly) >> static_cast(shiftAmt); - uint16_t wY1 = static_cast(sVar8 + static_cast(yVal1)); - FAST_WRITE16(s2 + 0x08, wX1); - FAST_WRITE16(s2 + 0x0a, wY1); - FAST_WRITE32(s2 + 0x0c, 1u); - - s2 += 0x10u; - } - - { - uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); - uint32_t advOff = static_cast((glyphIdx * 2 + 1) * 16 + 8); - int16_t advW = static_cast(FAST_READ16(fontPtr + advOff)); - iVar15 += static_cast(static_cast(static_cast(advW)) * sclx); - } - } - goto label_next; - -label_space: - { - int spaceW = static_cast(FAST_READ32(kFontBase + fontOff + 0x1cu)); - iVar15 += static_cast(static_cast(spaceW) * sclx); - } - -label_next: - uStack_d8 = uVar9; - uVar20++; - } - - if (param14 != 0u) - { - if (alignCh != 'L') - { - if (alignCh == 'C' || alignCh == 'R') - { - int shift = paramW * 16 - iVar15; - if (alignCh == 'C') shift >>= 1; - if (iStack_dc > 0) - { - uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; - for (int k = 0; k < iStack_dc; k++) - { - int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); - int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); - FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(shift))); - FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(shift))); - adj += 0x20u; - } - } - } - else if (alignCh == 'J' && sLen > 1) - { - int iVar19_div = static_cast(sLen) - 1; - if (iVar19_div == 0) iVar19_div = 1; - int spacePer = (paramW * 16 - iVar15) / iVar19_div; - uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; - int accum = 0; - for (uint32_t jj = 0; jj < sLen; jj++) - { - int8_t jch = static_cast(FAST_READ8(strAddr + jj)); - if (jch > 0x20) - { - int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); - int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); - FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(accum))); - FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(accum))); - adj += 0x20u; - } - accum += spacePer; - } - } - } - - if (param14 != 0u) - { - uint32_t tagAddr = bufAddr + static_cast(iVar21 * 16); - FAST_WRITE64(tagAddr + 0x00, static_cast(static_cast(iStack_dc)) | 0x4400000000000000ull); - FAST_WRITE64(tagAddr + 0x08, 0x5353ull); - - uint32_t endAddr = bufAddr + static_cast(iVar22_qw * 16); - FAST_WRITE64(endAddr + 0x00, 0x1000000000008001ull); - FAST_WRITE64(endAddr + 0x08, 0x0eull); - - int iVar19_end = iVar22_qw + 1; - uint32_t endAddr2 = bufAddr + static_cast(iVar19_end * 16); - FAST_WRITE64(endAddr2 + 0x00, 0x01ff0000027f0000ull); - FAST_WRITE64(endAddr2 + 0x08, 0x40ull); - - iVar22_qw += 2; - } - } - - int ret = 0; - if (param14 != 0u) ret = iVar22_qw; - setReturnS32(ctx, ret); - ctx->pc = getRegU32(ctx, 31); -} - -void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t oldSp = getRegU32(ctx, 29); - const uint32_t frame = oldSp - 0x900u; - - const uint32_t bufAddr = getRegU32(ctx, 4); - const uint32_t paramX = getRegU32(ctx, 5); - const uint32_t paramY = getRegU32(ctx, 6); - const uint32_t fmtAddr = getRegU32(ctx, 7); - - const uint8_t *callerVa = getConstMemPtr(rdram, oldSp + 16u); - uint8_t *frameVa = getMemPtr(rdram, frame + 0x8f8u); - if (callerVa && frameVa) - std::memcpy(frameVa, callerVa, 64u); - - SET_GPR_U32(ctx, 4, frame + 0x20u); - SET_GPR_U32(ctx, 5, fmtAddr); - SET_GPR_U32(ctx, 6, frame + 0x8f8u); - vsprintf(rdram, ctx, runtime); - - const uint32_t gp = getRegU32(ctx, 28); - uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); - uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); - uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); - uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); - uint32_t scrWidth = FAST_READ32(gp + static_cast(static_cast(-0x7b60))); - uint32_t scrHeight = FAST_READ32(gp + static_cast(static_cast(-0x7b5c))); - - std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); - std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); - - FAST_WRITE32(frame + 0x00u, frame + 0x20u); - FAST_WRITE32(frame + 0x08u, frame + 0x820u); - FAST_WRITE32(frame + 0x10u, frame + 0x824u); - FAST_WRITE32(frame + 0x18u, 1u); - - SET_GPR_U32(ctx, 29, frame); - SET_GPR_U32(ctx, 4, bufAddr); - SET_GPR_U32(ctx, 5, paramX); - SET_GPR_U32(ctx, 6, paramY); - SET_GPR_U32(ctx, 7, scrWidth); - SET_GPR_U32(ctx, 8, scrHeight); - SET_GPR_U32(ctx, 9, defaultColour); - SET_GPR_U32(ctx, 10, 0x4cu); - SET_GPR_U32(ctx, 11, defaultFontId); - - sceeFontGenerateString(rdram, ctx, runtime); - - SET_GPR_U32(ctx, 29, oldSp); - ctx->pc = getRegU32(ctx, 31); -} - -void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t oldSp = getRegU32(ctx, 29); - const uint32_t frame = oldSp - 0x900u; - - const uint32_t bufAddr = getRegU32(ctx, 4); - const uint32_t paramX = getRegU32(ctx, 5); - const uint32_t paramY = getRegU32(ctx, 6); - const uint32_t paramW = getRegU32(ctx, 7); - const uint32_t paramH = getRegU32(ctx, 8); - const uint32_t alignRaw = getRegU32(ctx, 9); - const uint32_t fmtAddr = getRegU32(ctx, 10); - const uint64_t param8 = GPR_U64(ctx, 11); - - int8_t alignChar = static_cast(alignRaw & 0xffu); - - FAST_WRITE64(frame + 0x8f8u, param8); - - SET_GPR_U32(ctx, 4, frame + 0x20u); - SET_GPR_U32(ctx, 5, fmtAddr); - SET_GPR_U32(ctx, 6, frame + 0x8f8u); - vsprintf(rdram, ctx, runtime); - - const uint32_t gp = getRegU32(ctx, 28); - uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); - uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); - uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); - uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); - - std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); - std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); - - FAST_WRITE32(frame + 0x00u, frame + 0x20u); - FAST_WRITE32(frame + 0x08u, frame + 0x820u); - FAST_WRITE32(frame + 0x10u, frame + 0x824u); - FAST_WRITE32(frame + 0x18u, 1u); - - SET_GPR_U32(ctx, 29, frame); - SET_GPR_U32(ctx, 4, bufAddr); - SET_GPR_U32(ctx, 5, paramX); - SET_GPR_U32(ctx, 6, paramY); - SET_GPR_U32(ctx, 7, paramW); - SET_GPR_U32(ctx, 8, paramH); - SET_GPR_U32(ctx, 9, defaultColour); - SET_GPR_U32(ctx, 10, static_cast(static_cast(alignChar))); - SET_GPR_U32(ctx, 11, defaultFontId); - - sceeFontGenerateString(rdram, ctx, runtime); - - SET_GPR_U32(ctx, 29, oldSp); - ctx->pc = getRegU32(ctx, 31); -} - -void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static constexpr uint32_t kFontBase = 0x176148u; - static constexpr uint32_t kFontEntrySz = 0x24u; - const int fontId = static_cast(getRegU32(ctx, 4)); - const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); - uint32_t glyphPtr = 0; - if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff)) - glyphPtr = *reinterpret_cast(p); - if (glyphPtr != 0u) - { - if (runtime) - { - uint32_t kernPtr = 0; - if (const uint8_t *kp = getConstMemPtr(rdram, glyphPtr + 0x2000u)) - kernPtr = *reinterpret_cast(kp); - if (kernPtr != 0u) - runtime->guestFree(kernPtr); - runtime->guestFree(glyphPtr); - } - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) - *reinterpret_cast(p) = 0u; - setReturnS32(ctx, 0); - } - else - { - setReturnS32(ctx, -1); - } -} - -void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - writeU32AtGp(rdram, gp, -0x7b4c, getRegU32(ctx, 4)); -} - -void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - writeU32AtGp(rdram, gp, -0x7c98, getRegU32(ctx, 4)); - setReturnS32(ctx, 0); -} - -void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - writeU32AtGp(rdram, gp, -0x7b58, getRegU32(ctx, 4)); -} - -void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - uint32_t sclx_bits, scly_bits; - std::memcpy(&sclx_bits, &ctx->f[12], sizeof(float)); - std::memcpy(&scly_bits, &ctx->f[13], sizeof(float)); - writeU32AtGp(rdram, gp, -0x7b54, sclx_bits); - writeU32AtGp(rdram, gp, -0x7b50, scly_bits); -} - -void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t cmd = static_cast(getRegU32(ctx, 5)); - const uint32_t argAddr = getRegU32(ctx, 6); - - // HTCI wait paths poll sceIoctl(fd, 1, &state) and expect state to move - // away from 1 once host-side I/O is no longer busy. - if (cmd == 1 && argAddr != 0u) - { - uint8_t *argPtr = getMemPtr(rdram, argAddr); - if (!argPtr) - { - setReturnS32(ctx, -1); - return; - } - - const uint32_t ready = 0u; - std::memcpy(argPtr, &ready, sizeof(ready)); - } - - setReturnS32(ctx, 0); -} - -void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static constexpr uint32_t REG_IPU_CTRL = 0x10002010u; - static constexpr uint32_t REG_IPU_CMD = 0x10002000u; - static constexpr uint32_t REG_IPU_IN_FIFO = 0x10007010u; - static constexpr uint32_t IQVAL_BASE = 0x1721e0u; - static constexpr uint32_t VQVAL_BASE = 0x172230u; - static constexpr uint32_t SETD4_CHCR_ENTRY = 0x126428u; - - if (!runtime) - return; - - PS2Memory &mem = runtime->memory(); - - auto setD4 = runtime->lookupFunction(SETD4_CHCR_ENTRY); - if (setD4) - { - ctx->r[4] = _mm_set_epi64x(0, 1); - { - PS2Runtime::GuestExecutionScope guestExecution(runtime); - setD4(rdram, ctx, runtime); - } - } - - mem.write32(REG_IPU_CTRL, 0x40000000u); - mem.write32(REG_IPU_CMD, 0u); - - __m128i v; - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x00u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x10u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x20u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x30u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x40u); - mem.write128(REG_IPU_IN_FIFO, v); - mem.write128(REG_IPU_IN_FIFO, v); - mem.write128(REG_IPU_IN_FIFO, v); - mem.write128(REG_IPU_IN_FIFO, v); - - mem.write32(REG_IPU_CMD, 0x50000000u); - mem.write32(REG_IPU_CMD, 0x58000000u); - - v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x00u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x10u); - mem.write128(REG_IPU_IN_FIFO, v); - - mem.write32(REG_IPU_CMD, 0x60000000u); - mem.write32(REG_IPU_CMD, 0x90000000u); - - mem.write32(REG_IPU_CTRL, 0x40000000u); - mem.write32(REG_IPU_CMD, 0u); -} - -void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioLseek(rdram, ctx, runtime); -} - -void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1024); -} - -void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t typePtr = getRegU32(ctx, 6); - const uint32_t freePtr = getRegU32(ctx, 7); - const uint32_t formatPtr = readStackU32(rdram, ctx, 16); - - const int32_t cardType = 2; // PS2 memory card. - const int32_t freeBlocks = 0x2000; - const int32_t format = 2; // formatted. - - if (typePtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, typePtr)) - { - std::memcpy(out, &cardType, sizeof(cardType)); - } - } - if (freePtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, freePtr)) - { - std::memcpy(out, &freeBlocks, sizeof(freeBlocks)); - } - } - if (formatPtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, formatPtr)) - { - std::memcpy(out, &format, sizeof(format)); - } - } - - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static uint32_t logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sceMcInit -> 0" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int32_t fd = 0; - { - std::lock_guard lock(g_mcStateMutex); - fd = g_mcNextFd++; - if (g_mcNextFd <= 0) - { - g_mcNextFd = 1; - } - g_mcLastResult = fd; - } - setReturnS32(ctx, 0); -} - -void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t size = static_cast(getRegU32(ctx, 7)); - if (size > 0) - { - const uint32_t dstAddr = readStackU32(rdram, ctx, 16); - if (uint8_t *dst = getMemPtr(rdram, dstAddr)) - { - std::memset(dst, 0, static_cast(size)); - } - } - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = std::max(0, size); - } - setReturnS32(ctx, 0); -} - -void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t offset = static_cast(getRegU32(ctx, 5)); - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = std::max(0, offset); - } - setReturnS32(ctx, 0); -} - -void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cmdPtr = getRegU32(ctx, 5); - const uint32_t resultPtr = getRegU32(ctx, 6); - int32_t result = 0; - { - std::lock_guard lock(g_mcStateMutex); - result = g_mcLastResult; - } - - if (cmdPtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, cmdPtr)) - { - const int32_t cmd = 0; - std::memcpy(out, &cmd, sizeof(cmd)); - } - } - if (resultPtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, resultPtr)) - { - std::memcpy(out, &result, sizeof(result)); - } - } - - // 1 = command finished in this runtime's immediate model. - setReturnS32(ctx, 1); -} - -void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = 0; - } - setReturnS32(ctx, 0); -} - -void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t size = static_cast(getRegU32(ctx, 7)); - { - std::lock_guard lock(g_mcStateMutex); - g_mcLastResult = std::max(0, size); - } - setReturnS32(ctx, 0); -} - -void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime); -} - -void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegAddCallback", rdram, ctx, runtime); -} - -void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnU32(ctx, 0u); -} - -void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)ctx; - (void)runtime; - static const uint32_t kRefGlobalAddrs[] = { - 0x171800u, 0x17180Cu, 0x171818u, 0x171804u, 0x171810u, 0x17181Cu - }; - for (uint32_t addr : kRefGlobalAddrs) - { - uint8_t *p = getMemPtr(rdram, addr); - if (!p) - continue; - uint32_t ptr = *reinterpret_cast(p); - if (ptr != 0u) - { - uint8_t *q = getMemPtr(rdram, ptr + 0x28u); - if (q) - *reinterpret_cast(q) = 0u; - } - } - setReturnU32(ctx, 1u); -} - -static void mpegGuestWrite32(uint8_t *rdram, uint32_t addr, uint32_t value) -{ - if (uint8_t *p = getMemPtr(rdram, addr)) - *reinterpret_cast(p) = value; -} -static void mpegGuestWrite64(uint8_t *rdram, uint32_t addr, uint64_t value) -{ - if (uint8_t *p = getMemPtr(rdram, addr)) - { - *reinterpret_cast(p) = static_cast(value); - *reinterpret_cast(p + 4) = static_cast(value >> 32); - } -} - -void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t param_1 = getRegU32(ctx, 4); // a0 - const uint32_t param_2 = getRegU32(ctx, 5); // a1 - const uint32_t param_3 = getRegU32(ctx, 6); // a2 - - const uint32_t uVar3 = (param_2 + 3u) & 0xFFFFFFFCu; - const int32_t iVar2_signed = static_cast(param_3) - static_cast(uVar3 - param_2); - - if (iVar2_signed <= 0x117) - { - setReturnU32(ctx, 0u); - return; - } - - const uint32_t puVar4 = uVar3 + 0x108u; - const uint32_t innerSize = static_cast(iVar2_signed) - 0x118u; - - mpegGuestWrite32(rdram, param_1 + 0x40, uVar3); - - const uint32_t a1_init = uVar3 + 0x118u; - mpegGuestWrite32(rdram, puVar4 + 0x0, a1_init); - mpegGuestWrite32(rdram, puVar4 + 0x4, innerSize); - mpegGuestWrite32(rdram, puVar4 + 0x8, a1_init); - mpegGuestWrite32(rdram, puVar4 + 0xC, a1_init); - - const uint32_t allocResult = runtime ? runtime->guestMalloc(0x600, 8u) : (uVar3 + 0x200u); - mpegGuestWrite32(rdram, uVar3 + 0x44, allocResult); - - // param_1[0..2] = 0; param_1[4..0xe] = 0xffffffff/0 as per decompilation - mpegGuestWrite32(rdram, param_1 + 0x00, 0); - mpegGuestWrite32(rdram, param_1 + 0x04, 0); - mpegGuestWrite32(rdram, param_1 + 0x08, 0); - mpegGuestWrite64(rdram, param_1 + 0x10, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x18, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x20, 0); - mpegGuestWrite64(rdram, param_1 + 0x28, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x30, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x38, 0); - - static const unsigned s_zeroOffsets[] = { - 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0, 0xE4, 0xE8, 0xF8, - 0x0C, 0x14, 0x2C, 0x34, 0x3C, - 0x48, 0xFC, 0x100, 0x104, 0x70, 0x90, 0xAC - }; - for (unsigned off : s_zeroOffsets) - mpegGuestWrite32(rdram, uVar3 + off, 0u); - mpegGuestWrite64(rdram, uVar3 + 0x78, 0); - mpegGuestWrite64(rdram, uVar3 + 0x88, 0); - - mpegGuestWrite64(rdram, uVar3 + 0xF0, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite32(rdram, uVar3 + 0x1C, 0x1209F8u); - mpegGuestWrite32(rdram, uVar3 + 0x24, 0x120A08u); - mpegGuestWrite32(rdram, uVar3 + 0xB0, 1u); - mpegGuestWrite32(rdram, uVar3 + 0x9C, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, uVar3 + 0x80, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, uVar3 + 0x94, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, uVar3 + 0x98, 0xFFFFFFFFu); - - mpegGuestWrite32(rdram, 0x1717BCu, param_1); - - static const uint32_t s_refValues[] = { - 0x171A50u, 0x171C58u, 0x171CC0u, 0x171D28u, 0x171D90u, - 0x171AB8u, 0x171B20u, 0x171B88u, 0x171BF0u - }; - for (unsigned i = 0; i < 9u; ++i) - mpegGuestWrite32(rdram, 0x171800u + i * 4u, s_refValues[i]); - - uint32_t setDynamicRet = a1_init; - if (uint8_t *p = getMemPtr(rdram, puVar4 + 8)) - setDynamicRet = *reinterpret_cast(p); - mpegGuestWrite32(rdram, puVar4 + 12, setDynamicRet); - - setReturnU32(ctx, setDynamicRet); -} - -void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDelete", rdram, ctx, runtime); -} - -void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime); -} - -void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDemuxPssRing", rdram, ctx, runtime); -} - -void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime); -} - -void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime); -} - -void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime); -} - -void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime); -} - -void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime); -} - -void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t param_1 = getRegU32(ctx, 4); - if (uint8_t *base = getMemPtr(rdram, param_1)) - { - const uint32_t iVar1 = *reinterpret_cast(base + 0x40); - if (uint8_t *inner = getMemPtr(rdram, iVar1)) - { - *reinterpret_cast(inner + 0xb0) = 1; - *reinterpret_cast(inner + 0xd8) = (getRegU32(ctx, 5) & 0x0FFFFFFFu) | 0x20000000u; - *reinterpret_cast(inner + 0xe4) = getRegU32(ctx, 6); - *reinterpret_cast(inner + 0xdc) = 0; - *reinterpret_cast(inner + 0xe0) = 0; - } - } - setReturnU32(ctx, 0u); -} - -void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime); -} - -void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime); -} - -void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegInit", rdram, ctx, runtime); -} - -void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t param_1 = getRegU32(ctx, 4); - uint8_t *base = getMemPtr(rdram, param_1 + 0x40u); - if (base) - { - uint32_t ptrAddr = *reinterpret_cast(base); - if (ptrAddr != 0u) - { - uint8_t *p = getMemPtr(rdram, ptrAddr); - if (p) - *reinterpret_cast(p) = 1u; - } - } - setReturnS32(ctx, 1); -} - -void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime); -} - -void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t param_1 = getRegU32(ctx, 4); - uint8_t *base = getMemPtr(rdram, param_1); - if (!base) - return; - uint32_t inner = *reinterpret_cast(base + 0x40); - if (inner == 0u) - return; - mpegGuestWrite32(rdram, inner + 0x00, 0u); - mpegGuestWrite32(rdram, inner + 0x04, 0u); - mpegGuestWrite32(rdram, inner + 0x08, 0u); - mpegGuestWrite32(rdram, param_1 + 0x08, 0u); - mpegGuestWrite32(rdram, inner + 0x80, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, inner + 0xAC, 0u); - mpegGuestWrite32(rdram, 0x171904u, 0u); -} - -void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime); -} - -void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime); -} - -void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime); -} - -void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime); -} - -void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioOpen(rdram, ctx, runtime); -} - -void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Report all buttons supported. - setReturnS32(ctx, static_cast(0xFFFFu)); -} - -void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnU32(ctx, getRegU32(ctx, 6)); -} - -void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - static std::atomic frameCount{0}; - setReturnU32(ctx, frameCount++); -} - -void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Arbitrary non-zero module version. - setReturnS32(ctx, 0x0200); -} - -void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 2); -} - -void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // 0 = completed/no pending request. - setReturnS32(ctx, 0); -} - -void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Most games use one slot unless multitap is active. - setReturnS32(ctx, 1); -} - -void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Pad state constants used by libpad: 6 means stable and ready. - setReturnS32(ctx, 6); -} - -void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t act = static_cast(getRegU32(ctx, 6)); - if (act < 0) - { - setReturnS32(ctx, 1); // one actuator descriptor - return; - } - setReturnS32(ctx, 0); -} - -void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // No combined modes reported. - setReturnS32(ctx, 0); -} - -void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - - const int32_t infoMode = static_cast(getRegU32(ctx, 6)); // a2 - const int32_t index = static_cast(getRegU32(ctx, 7)); // a3 - - // Minimal DualShock-like capabilities to keep game-side pad setup paths alive. - constexpr int32_t kPadTypeDualShock = 7; - switch (infoMode) - { - case 1: // PAD_MODECURID - setReturnS32(ctx, kPadTypeDualShock); - return; - case 2: // PAD_MODECUREXID - setReturnS32(ctx, kPadTypeDualShock); - return; - case 3: // PAD_MODECUROFFS - setReturnS32(ctx, 0); - return; - case 4: // PAD_MODETABLE - if (index == -1) - { - setReturnS32(ctx, 1); // one available mode - } - else if (index == 0) - { - setReturnS32(ctx, kPadTypeDualShock); - } - else - { - setReturnS32(ctx, 0); - } - return; - default: - setReturnS32(ctx, 0); - return; - } -} - -void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Pressure mode is disabled in this minimal implementation. - setReturnS32(ctx, 0); -} - -void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int port = static_cast(getRegU32(ctx, 4)); - const int slot = static_cast(getRegU32(ctx, 5)); - const uint32_t dataAddr = getRegU32(ctx, 6); - uint8_t *data = getMemPtr(rdram, dataAddr); - if (!data) - { - setReturnS32(ctx, 0); - return; - } - - PadInputState state; - bool useOverride = false; - { - std::lock_guard lock(g_padOverrideMutex); - if (g_padOverrideEnabled) - { - state = g_padOverrideState; - useOverride = true; - } - } - - if (!useOverride) - { - if (runtime && runtime->padBackend().readState(port, slot, data, 32)) - { - setReturnS32(ctx, 1); - return; - } - - applyGamepadState(state); - applyKeyboardState(state, true); - } - - fillPadStatus(data, state); - - if (padDebugEnabled()) - { - static uint32_t logCounter = 0; - if ((logCounter++ % 60u) == 0u) - { - std::cout << "[pad] buttons=0x" << std::hex << state.buttons << std::dec - << " lx=" << static_cast(state.lx) - << " ly=" << static_cast(state.ly) - << " rx=" << static_cast(state.rx) - << " ry=" << static_cast(state.ry) - << (useOverride ? " (override)" : "") << std::endl; - } - } - - setReturnS32(ctx, 1); -} - -void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t state = getRegU32(ctx, 4); - const uint32_t strAddr = getRegU32(ctx, 5); - char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); - if (!buf) - { - setReturnS32(ctx, -1); - return; - } - - const char *text = (state == 0) ? "COMPLETE" : "BUSY"; - std::strncpy(buf, text, 31); - buf[31] = '\0'; - setReturnS32(ctx, 0); -} - -void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} - -void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 0); -} - -void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t state = getRegU32(ctx, 4); - const uint32_t strAddr = getRegU32(ctx, 5); - char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); - if (!buf) - { - setReturnS32(ctx, -1); - return; - } - - const char *text = "UNKNOWN"; - if (state == 6) - { - text = "STABLE"; - } - else if (state == 1) - { - text = "FINDPAD"; - } - else if (state == 0) - { - text = "DISCONNECTED"; - } - - std::strncpy(buf, text, 31); - buf[31] = '\0'; - setReturnS32(ctx, 0); -} - -void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t format_addr = getRegU32(ctx, 4); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - if (format_addr == 0) - return; - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); - if (rendered.size() > 2048) - rendered.resize(2048); - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - std::cout << "PS2 scePrintf: " << logLine; - std::cout << std::flush; - } - else if (count == kMaxPrintfLogs + 1) - { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; - } -} - -void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioRead(rdram, ctx, runtime); -} - -void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceResetttyinit", rdram, ctx, runtime); -} - -void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSdCallBack", rdram, ctx, runtime); -} - -void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSdRemote", rdram, ctx, runtime); -} - -void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSdRemoteInit", rdram, ctx, runtime); -} - -void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime); -} - -void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime); -} - -void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSetPtm", rdram, ctx, runtime); -} - -namespace -{ - struct Ps2SifDmaTransfer - { - uint32_t src = 0; - uint32_t dest = 0; - int32_t size = 0; - int32_t attr = 0; - }; - static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected SIF DMA descriptor size"); - - std::mutex g_sifDmaTransferMutex; - uint32_t g_nextSifDmaTransferId = 1u; - std::mutex g_sifCmdStateMutex; - std::unordered_map g_sifRegs; - std::unordered_map g_sifSregs; - std::unordered_map g_sifCmdHandlers; - uint32_t g_sifCmdBuffer = 0u; - uint32_t g_sifSysCmdBuffer = 0u; - bool g_sifCmdInitialized = false; - uint32_t g_sifGetRegLogCount = 0u; - uint32_t g_sifSetRegLogCount = 0u; - - constexpr uint32_t kSifRegBootStatus = 0x4u; - constexpr uint32_t kSifRegMainAddr = 0x80000000u; - constexpr uint32_t kSifRegSubAddr = 0x80000001u; - constexpr uint32_t kSifRegMsCom = 0x80000002u; - constexpr uint32_t kSifBootReadyMask = 0x00020000u; - - void seedDefaultSifRegsLocked() - { - g_sifRegs.clear(); - g_sifSregs.clear(); - g_sifCmdHandlers.clear(); - g_sifCmdBuffer = 0u; - g_sifSysCmdBuffer = 0u; - g_sifCmdInitialized = false; - g_sifGetRegLogCount = 0u; - g_sifSetRegLogCount = 0u; - - g_sifRegs[kSifRegBootStatus] = kSifBootReadyMask; - g_sifRegs[kSifRegMainAddr] = 0u; - g_sifRegs[kSifRegSubAddr] = 0u; - g_sifRegs[kSifRegMsCom] = 0u; - } - - bool shouldTraceSifReg(uint32_t reg) - { - switch (reg) - { - case 0x2u: - case 0x4u: - case 0x80000000u: - case 0x80000001u: - case 0x80000002u: - return true; - default: - return false; - } - } - - struct SifStateInitializer - { - SifStateInitializer() - { - std::lock_guard lock(g_sifCmdStateMutex); - seedDefaultSifRegsLocked(); - } - } g_sifStateInitializer; - - uint32_t allocateSifDmaTransferId() - { - std::lock_guard lock(g_sifDmaTransferMutex); - uint32_t id = g_nextSifDmaTransferId++; - if (id == 0u) - { - id = g_nextSifDmaTransferId++; - } - return id; - } - - bool isCopyableGuestAddress(uint32_t addr) - { - if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) - { - return true; - } - - if (addr < 0x20000000u) - { - return true; - } - - if (addr >= 0x20000000u && addr < 0x40000000u) - { - return true; - } - - if (addr >= 0x80000000u && addr < 0xC0000000u) - { - return true; - } - - return false; - } - - bool canCopyGuestByteRange(const uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) - { - if (!rdram) - { - return false; - } - - if (sizeBytes == 0u) - { - return true; - } - - for (uint32_t i = 0u; i < sizeBytes; ++i) - { - const uint32_t srcByteAddr = srcAddr + i; - const uint32_t dstByteAddr = dstAddr + i; - - if (!isCopyableGuestAddress(srcByteAddr) || !isCopyableGuestAddress(dstByteAddr)) - { - return false; - } - - const uint8_t *src = getConstMemPtr(rdram, srcByteAddr); - const uint8_t *dst = getConstMemPtr(rdram, dstByteAddr); - if (!src || !dst) - { - return false; - } - } - - return true; - } - - bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) - { - if (!canCopyGuestByteRange(rdram, dstAddr, srcAddr, sizeBytes)) - { - return false; - } - - if (sizeBytes == 0u) - { - return true; - } - - const uint64_t srcBegin = srcAddr; - const uint64_t srcEnd = srcBegin + static_cast(sizeBytes); - const uint64_t dstBegin = dstAddr; - const bool copyBackward = (dstBegin > srcBegin) && (dstBegin < srcEnd); - - if (copyBackward) - { - for (uint32_t i = sizeBytes; i > 0u; --i) - { - const uint32_t index = i - 1u; - const uint8_t *src = getConstMemPtr(rdram, srcAddr + index); - uint8_t *dst = getMemPtr(rdram, dstAddr + index); - if (!src || !dst) - { - return false; - } - *dst = *src; - } - return true; - } - - for (uint32_t i = 0; i < sizeBytes; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - uint8_t *dst = getMemPtr(rdram, dstAddr + i); - if (!src || !dst) - { - return false; - } - *dst = *src; - } - return true; - } -} - -void resetSifState() -{ - std::lock_guard lock(g_sifCmdStateMutex); - seedDefaultSifRegsLocked(); -} - -void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cid = getRegU32(ctx, 4); - const uint32_t handler = getRegU32(ctx, 5); - std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdHandlers[cid] = handler; - setReturnS32(ctx, 0); -} - -void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reqSize = getRegU32(ctx, 4); - const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); - if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) - { - setReturnS32(ctx, 0); - return; - } - - const uint32_t allocAddr = g_iopHeapNext; - g_iopHeapNext += alignedSize; - setReturnS32(ctx, static_cast(allocAddr)); -} - -void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifBindRpc(rdram, ctx, runtime); -} - -void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime); -} - -void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - (void)getRegU32(ctx, 4); // trid - - // Transfers are applied immediately by sceSifSetDma in this runtime. - setReturnS32(ctx, -1); -} - -void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard lock(g_sifCmdStateMutex); - seedDefaultSifRegsLocked(); - setReturnS32(ctx, 0); -} - -void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard lock(g_sifCmdStateMutex); - setReturnU32(ctx, g_sifCmdBuffer); -} - -void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 4)); -} - -void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - - const uint32_t rdAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - const uint32_t dstAddr = getRegU32(ctx, 6); - const int32_t sizeSigned = static_cast(getRegU32(ctx, 7)); - - if (sizeSigned <= 0) - { - setReturnS32(ctx, 0); - return; - } - - const uint32_t size = static_cast(sizeSigned); - if (size > PS2_RAM_SIZE) - { - static uint32_t warnCount = 0; - if (warnCount < 32u) - { - std::cerr << "sceSifGetOtherData rejected oversized transfer size=0x" - << std::hex << size << std::dec << std::endl; - ++warnCount; - } - setReturnS32(ctx, -1); - return; - } - - auto readGuestU32Local = [&](uint32_t addr, uint32_t &out) -> bool - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - out = 0u; - return false; - } - std::memcpy(&out, ptr, sizeof(out)); - return true; - }; - - auto readGuestS16Local = [&](uint32_t addr, int16_t &out) -> bool - { - const uint8_t *b0 = getConstMemPtr(rdram, addr + 0u); - const uint8_t *b1 = getConstMemPtr(rdram, addr + 1u); - if (!b0 || !b1) - { - out = 0; - return false; - } - const uint16_t raw = static_cast(static_cast(*b0) | - (static_cast(*b1) << 8)); - out = static_cast(raw); - return true; - }; - - constexpr uint32_t kSndTransTypeAddr = 0x01E0E1C0u; - constexpr uint32_t kSndTransBankAddr = 0x01E0E1C8u; - constexpr uint32_t kSndTransLevelAddr = 0x01E0E1B8u; - constexpr uint32_t kSndGetAdrsAddr = 0x01E212D8u; - constexpr uint32_t kSndStatusMirrorAddr = 0x01E213C0u; - constexpr uint32_t kSndSeCheckAddr = 0x01E0EF10u; - constexpr uint32_t kSndMidiCheckAddr = 0x01E0EF20u; - - static uint32_t s_sifGetOtherDataStatusLogs = 0u; - const bool isSndStatusTransfer = (size == 0x42u); - const uint32_t statusLogIndex = s_sifGetOtherDataStatusLogs++; - const bool shouldLogStatus = - isSndStatusTransfer && - (statusLogIndex < 96u || (statusLogIndex % 256u) == 0u); - - if (shouldLogStatus) - { - uint32_t transType = 0u; - uint32_t transLevel = 0u; - uint32_t transBank = 0u; - uint32_t getAdrs = 0u; - (void)readGuestU32Local(kSndTransTypeAddr, transType); - (void)readGuestU32Local(kSndTransLevelAddr, transLevel); - (void)readGuestU32Local(kSndTransBankAddr, transBank); - (void)readGuestU32Local(kSndGetAdrsAddr, getAdrs); - std::cout << "[sceSifGetOtherData] src=0x" << std::hex << srcAddr - << " dst=0x" << dstAddr - << " size=0x" << size - << " get_adrs=0x" << getAdrs - << std::dec - << " transType=" << transType - << " transLevel=" << transLevel - << " transBank=" << transBank - << std::endl; - } - - // Keep RECVX SND_STATUS checksums synchronized with EE-side transfer checks. - if (srcAddr == 0x00012000u && size == 0x42u) - { - constexpr uint32_t kPrimarySeAddr = 0x01E0EF10u; - constexpr uint32_t kPrimaryMidiAddr = 0x01E0EF20u; - constexpr uint32_t kFallbackSeAddr = 0x01E1EF10u; - constexpr uint32_t kFallbackMidiAddr = 0x01E1EF20u; - - auto hasAnyNonZero = [](const uint8_t *ptr, size_t bytes) -> bool - { - if (!ptr) - { - return false; - } - for (size_t i = 0; i < bytes; ++i) - { - if (ptr[i] != 0u) - { - return true; - } - } - return false; - }; - - const uint8_t *selectedSe = getConstMemPtr(rdram, kPrimarySeAddr); - const uint8_t *selectedMidi = getConstMemPtr(rdram, kPrimaryMidiAddr); - - const bool primaryLooksLive = - hasAnyNonZero(selectedSe, 5u * sizeof(int16_t)) || - hasAnyNonZero(selectedMidi, 4u * sizeof(int16_t)); - - if ((!selectedSe || !selectedMidi) || !primaryLooksLive) - { - const uint8_t *fallbackSe = getConstMemPtr(rdram, kFallbackSeAddr); - const uint8_t *fallbackMidi = getConstMemPtr(rdram, kFallbackMidiAddr); - const bool fallbackLooksLive = - hasAnyNonZero(fallbackSe, 5u * sizeof(int16_t)) || - hasAnyNonZero(fallbackMidi, 4u * sizeof(int16_t)); - - if (fallbackLooksLive) - { - selectedSe = fallbackSe; - selectedMidi = fallbackMidi; - } - } - - if (selectedSe && selectedMidi) - { - if (uint8_t *status = getMemPtr(rdram, srcAddr)) - { - std::memcpy(status + 0x26u, selectedSe, 5u * sizeof(int16_t)); // se_sum[5] - std::memcpy(status + 0x1Eu, selectedMidi, 4u * sizeof(int16_t)); // midi_sum[4] - } - } - - if (shouldLogStatus) - { - int16_t se0 = 0; - int16_t midi0 = 0; - int16_t seChk0 = 0; - int16_t midiChk0 = 0; - (void)readGuestS16Local(srcAddr + 0x26u, se0); - (void)readGuestS16Local(srcAddr + 0x1Eu, midi0); - (void)readGuestS16Local(kSndSeCheckAddr + 0u, seChk0); - (void)readGuestS16Local(kSndMidiCheckAddr + 0u, midiChk0); - std::cout << "[sceSifGetOtherData:sndstatus] srcSe0=" << se0 - << " srcMidi0=" << midi0 - << " chkSe0=" << seChk0 - << " chkMidi0=" << midiChk0 - << std::endl; - } - } - - if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size)) - { - static uint32_t warnCount = 0; - if (warnCount < 32u) - { - std::cerr << "sceSifGetOtherData copy failed src=0x" << std::hex << srcAddr - << " dst=0x" << dstAddr - << " size=0x" << size - << std::dec << std::endl; - ++warnCount; - } - setReturnS32(ctx, -1); - return; - } - - // SifRpcReceiveData_t keeps src/dest/size at offsets 0x10/0x14/0x18. - if (uint8_t *rd = getMemPtr(rdram, rdAddr)) - { - std::memcpy(rd + 0x10u, &srcAddr, sizeof(srcAddr)); - std::memcpy(rd + 0x14u, &dstAddr, sizeof(dstAddr)); - std::memcpy(rd + 0x18u, &size, sizeof(size)); - } - - if (shouldLogStatus) - { - uint32_t transBank = 0u; - (void)readGuestU32Local(kSndTransBankAddr, transBank); - int16_t dstSe = 0; - int16_t dstMidi = 0; - int16_t mirrorSe = 0; - int16_t mirrorMidi = 0; - int16_t bankSeChk = 0; - int16_t bankMidiChk = 0; - (void)readGuestS16Local(dstAddr + 0x26u, dstSe); - (void)readGuestS16Local(dstAddr + 0x1Eu, dstMidi); - (void)readGuestS16Local(kSndStatusMirrorAddr + 0x26u, mirrorSe); - (void)readGuestS16Local(kSndStatusMirrorAddr + 0x1Eu, mirrorMidi); - if (transBank < 5u) - { - (void)readGuestS16Local(kSndSeCheckAddr + (transBank * 2u), bankSeChk); - } - if (transBank < 4u) - { - (void)readGuestS16Local(kSndMidiCheckAddr + (transBank * 2u), bankMidiChk); - } - std::cout << "[sceSifGetOtherData:post] bank=" << transBank - << " dstSe=" << dstSe - << " dstMidi=" << dstMidi - << " mirrorSe=" << mirrorSe - << " mirrorMidi=" << mirrorMidi - << " bankSeChk=" << bankSeChk - << " bankMidiChk=" << bankMidiChk - << std::endl; - } - - setReturnS32(ctx, 0); -} - -void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - uint32_t value = 0u; - bool shouldLog = false; - { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifRegs.find(reg); - if (it != g_sifRegs.end()) - { - value = it->second; - } - shouldLog = shouldTraceSifReg(reg) && g_sifGetRegLogCount < 128u; - if (shouldLog) - { - ++g_sifGetRegLogCount; - } - } - if (shouldLog) - { - auto flags = std::cerr.flags(); - std::cerr << "[sceSifGetReg] reg=0x" << std::hex << reg - << " value=0x" << value - << " pc=0x" << (ctx ? ctx->pc : 0u) - << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) - << std::dec << std::endl; - std::cerr.flags(flags); - } - setReturnU32(ctx, value); -} - -void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - uint32_t value = 0u; - { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifSregs.find(reg); - if (it != g_sifSregs.end()) - { - value = it->second; - } - } - setReturnU32(ctx, value); -} - -void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdInitialized = true; - setReturnS32(ctx, 0); -} - -void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_iopHeapNext = kIopHeapBase; - setReturnS32(ctx, 0); -} - -void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifInitRpc(rdram, ctx, runtime); -} - -void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime); -} - -void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime); -} - -void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime); -} - -void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime); -} - -void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cid = getRegU32(ctx, 4); - std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdHandlers.erase(cid); - setReturnS32(ctx, 0); -} - -void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime); -} - -void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime); -} - -void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t newBuffer = getRegU32(ctx, 4); - uint32_t prev = 0u; - { - std::lock_guard lock(g_sifCmdStateMutex); - prev = g_sifCmdBuffer; - g_sifCmdBuffer = newBuffer; - } - setReturnU32(ctx, prev); -} - -void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 0); -} - -void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - - const uint32_t dmatAddr = getRegU32(ctx, 4); - const uint32_t count = getRegU32(ctx, 5); - if (!dmatAddr || count == 0u || count > 32u) - { - setReturnS32(ctx, 0); - return; - } - - std::array pending{}; - uint32_t pendingCount = 0u; - bool ok = true; - for (uint32_t i = 0; i < count; ++i) - { - const uint32_t entryAddr = dmatAddr + (i * static_cast(sizeof(Ps2SifDmaTransfer))); - const uint8_t *entry = getConstMemPtr(rdram, entryAddr); - if (!entry) - { - ok = false; - break; - } - - Ps2SifDmaTransfer xfer{}; - std::memcpy(&xfer, entry, sizeof(xfer)); - if (xfer.size <= 0) - { - continue; - } - - const uint32_t sizeBytes = static_cast(xfer.size); - if (sizeBytes > PS2_RAM_SIZE) - { - ok = false; - break; - } - if (!canCopyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes)) - { - ok = false; - break; - } - - pending[pendingCount++] = xfer; - } - - if (ok) - { - for (uint32_t i = 0; i < pendingCount; ++i) - { - const Ps2SifDmaTransfer &xfer = pending[i]; - if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast(xfer.size))) - { - ok = false; - break; - } - } - } - - if (!ok) - { - static uint32_t warnCount = 0; - if (warnCount < 32u) - { - std::cerr << "sceSifSetDma failed dmat=0x" << std::hex << dmatAddr - << " count=0x" << count - << std::dec << std::endl; - ++warnCount; - } - setReturnS32(ctx, 0); - return; - } - - ps2_syscalls::dispatchDmacHandlersForCause(rdram, runtime, 5u); - - setReturnS32(ctx, static_cast(allocateSifDmaTransferId())); -} - -void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 5)); -} - -void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - const uint32_t value = getRegU32(ctx, 5); - uint32_t prev = 0u; - bool shouldLog = false; - { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifRegs.find(reg); - if (it != g_sifRegs.end()) - { - prev = it->second; - } - g_sifRegs[reg] = value; - shouldLog = shouldTraceSifReg(reg) && g_sifSetRegLogCount < 128u; - if (shouldLog) - { - ++g_sifSetRegLogCount; - } - } - if (shouldLog) - { - auto flags = std::cerr.flags(); - std::cerr << "[sceSifSetReg] reg=0x" << std::hex << reg - << " prev=0x" << prev - << " value=0x" << value - << " pc=0x" << (ctx ? ctx->pc : 0u) - << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) - << std::dec << std::endl; - std::cerr.flags(flags); - } - setReturnU32(ctx, prev); -} - -void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime); -} - -void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - const uint32_t value = getRegU32(ctx, 5); - uint32_t prev = 0u; - { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifSregs.find(reg); - if (it != g_sifSregs.end()) - { - prev = it->second; - } - g_sifSregs[reg] = value; - } - setReturnU32(ctx, prev); -} - -void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t newBuffer = getRegU32(ctx, 4); - uint32_t prev = 0u; - { - std::lock_guard lock(g_sifCmdStateMutex); - prev = g_sifSysCmdBuffer; - g_sifSysCmdBuffer = newBuffer; - } - setReturnU32(ctx, prev); -} - -void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime); -} - -void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime); -} - -void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime); -} - -void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime); -} - -void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime); -} - -void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime); -} - -void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime); -} - -void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime); -} - -void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime); -} - -void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime); -} - -void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime); -} - -void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime); -} - -void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime); -} - -void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime); -} - -void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime); -} - -void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime); -} - -void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime); -} - -void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime); -} - -void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime); -} - -void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime); -} - -void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime); -} - -void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime); -} - -void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime); -} - -void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime); -} - -void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime); -} - -void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime); -} - -void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime); -} - -void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime); -} - -void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime); -} - -void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime); -} - -void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime); -} - -void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime); -} - -void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime); -} - -void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime); -} - -void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime); -} - -void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime); -} - -void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime); -} - -void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime); -} - -void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime); -} - -void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime); -} - -void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime); -} - -void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime); -} - -void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime); -} - -void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime); -} - -void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime); -} - -void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime); -} - -void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime); -} - -void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime); -} - -void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime); -} - -void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime); -} - -void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime); -} - -void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime); -} - -void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime); -} - -void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime); -} - -void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime); -} - -void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime); -} - -void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime); -} - -void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime); -} - -void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime); -} - -void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime); -} - -void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime); -} - -void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime); -} - -void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime); -} - -void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime); -} - -void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime); -} - -void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime); -} - -void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime); -} - -void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime); -} - -void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime); -} - -void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime); -} - -void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime); -} - -void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime); -} - -void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime); -} - -void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime); -} - -void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime); -} - -void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime); -} - -void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime); -} - -void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime); -} - -void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime); -} - -void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime); -} - -void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime); -} - -void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime); -} - -void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyHandler", rdram, ctx, runtime); -} - -void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyInit", rdram, ctx, runtime); -} - -void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyRead", rdram, ctx, runtime); -} - -void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyWrite", rdram, ctx, runtime); -} - -namespace -{ - bool readVuVec4f(uint8_t *rdram, uint32_t addr, float (&out)[4]) - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(out, ptr, sizeof(out)); - return true; - } - - bool writeVuVec4f(uint8_t *rdram, uint32_t addr, const float (&in)[4]) - { - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(ptr, in, sizeof(in)); - return true; - } - - bool readVuVec4i(uint8_t *rdram, uint32_t addr, int32_t (&out)[4]) - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(out, ptr, sizeof(out)); - return true; - } - - bool writeVuVec4i(uint8_t *rdram, uint32_t addr, const int32_t (&in)[4]) - { - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(ptr, in, sizeof(in)); - return true; - } -} - -void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t lhsAddr = getRegU32(ctx, 5); - const uint32_t rhsAddr = getRegU32(ctx, 6); - float lhs[4]{}, rhs[4]{}, out[4]{}; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = lhs[i] + rhs[i]; - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); -} - -void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime); -} - -void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime); -} - -void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime); -} - -void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime); -} - -void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime); -} - -void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); -} - -void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); -} - -void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); -} - -void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime); -} - -void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime); -} - -void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime); -} - -void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}; - int32_t out[4]{}; - if (readVuVec4f(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = static_cast(src[i]); - } - (void)writeVuVec4i(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}; - int32_t out[4]{}; - if (readVuVec4f(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = static_cast(src[i] * 16.0f); - } - (void)writeVuVec4i(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t lhsAddr = getRegU32(ctx, 4); - const uint32_t rhsAddr = getRegU32(ctx, 5); - float lhs[4]{}, rhs[4]{}; - float dot = 0.0f; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) - { - dot = (lhs[0] * rhs[0]) + (lhs[1] * rhs[1]) + (lhs[2] * rhs[2]) + (lhs[3] * rhs[3]); - } - - if (ctx) - { - ctx->f[0] = dot; - } - uint32_t raw = 0u; - std::memcpy(&raw, &dot, sizeof(raw)); - setReturnU32(ctx, raw); -} - -void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime); -} - -void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime); -} - -void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime); -} - -void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - int32_t src[4]{}; - float out[4]{}; - if (readVuVec4i(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = static_cast(src[i]); - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - int32_t src[4]{}; - float out[4]{}; - if (readVuVec4i(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = static_cast(src[i]) / 4096.0f; - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - int32_t src[4]{}; - float out[4]{}; - if (readVuVec4i(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = static_cast(src[i]) / 16.0f; - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime); -} - -void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime); -} - -void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime); -} - -void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}, out[4]{}; - if (readVuVec4f(rdram, srcAddr, src)) - { - const float len = std::sqrt((src[0] * src[0]) + (src[1] * src[1]) + (src[2] * src[2]) + (src[3] * src[3])); - if (len > 1.0e-6f) - { - const float invLen = 1.0f / len; - for (int i = 0; i < 4; ++i) - { - out[i] = src[i] * invLen; - } - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime); -} - -void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t lhsAddr = getRegU32(ctx, 5); - const uint32_t rhsAddr = getRegU32(ctx, 6); - float lhs[4]{}, rhs[4]{}, out[4]{}; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) - { - out[0] = (lhs[1] * rhs[2]) - (lhs[2] * rhs[1]); - out[1] = (lhs[2] * rhs[0]) - (lhs[0] * rhs[2]); - out[2] = (lhs[0] * rhs[1]) - (lhs[1] * rhs[0]); - out[3] = 0.0f; - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime); -} - -void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); -} - -void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); -} - -void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); -} - -void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime); -} - -void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime); -} - -void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}, out[4]{}; - float scale = ctx ? ctx->f[12] : 0.0f; - if (scale == 0.0f) - { - uint32_t raw = getRegU32(ctx, 6); - std::memcpy(&scale, &raw, sizeof(scale)); - if (scale == 0.0f) - { - scale = static_cast(getRegU32(ctx, 6)); - } - } - - if (readVuVec4f(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = src[i] * scale; - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime); -} - -void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t lhsAddr = getRegU32(ctx, 5); - const uint32_t rhsAddr = getRegU32(ctx, 6); - float lhs[4]{}, rhs[4]{}, out[4]{}; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) - { - for (int i = 0; i < 4; ++i) - { - out[i] = lhs[i] - rhs[i]; - } - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} - -void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime); -} - -void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); -} - -void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst - alignas(16) const float identity[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - - if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast(identity), sizeof(identity))) - { - static uint32_t warnCount = 0; - if (warnCount < 8) - { - std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x" - << std::hex << dstAddr << std::dec << std::endl; - ++warnCount; - } - } - - setReturnS32(ctx, 0); -} - -void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime); -} - -void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioWrite(rdram, ctx, runtime); -} - -void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::srand(getRegU32(ctx, 4)); - setReturnS32(ctx, 0); -} - -void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t statAddr = getRegU32(ctx, 5); - uint8_t *statBuf = getMemPtr(rdram, statAddr); - if (!statBuf) - { - setReturnS32(ctx, -1); - return; - } - - // Minimal fake stat payload: zeroed structure indicates a valid, readable file. - std::memset(statBuf, 0, 128); - setReturnS32(ctx, 0); -} - -void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t lhsAddr = getRegU32(ctx, 4); - const uint32_t rhsAddr = getRegU32(ctx, 5); - const std::string lhs = readPs2CStringBounded(rdram, runtime, lhsAddr, 1024); - const std::string rhs = readPs2CStringBounded(rdram, runtime, rhsAddr, 1024); - - const size_t n = std::min(lhs.size(), rhs.size()); - for (size_t i = 0; i < n; ++i) - { - const int a = std::tolower(static_cast(lhs[i])); - const int b = std::tolower(static_cast(rhs[i])); - if (a != b) - { - setReturnS32(ctx, a - b); - return; - } - } - - setReturnS32(ctx, static_cast(lhs.size()) - static_cast(rhs.size())); -} - -void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 - FILE *fp = get_file_ptr(file_handle); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (fp && format_addr != 0) - { - std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); - ret = std::fprintf(fp, "%s", rendered.c_str()); - } - else - { - std::cerr << "vfprintf error: Invalid file handle or format address." - << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } - - setReturnS32(ctx, ret); -} - -void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 - constexpr size_t kSafeVsprintfBytes = 256u; // Keep guest stack temporaries from being overwritten. - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); - if (rendered.size() >= kSafeVsprintfBytes) - { - rendered.resize(kSafeVsprintfBytes - 1); - } - if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), rendered.size() + 1u)) - { - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "vsprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; - } - } - else - { - std::cerr << "vsprintf error: Invalid address provided." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } - - setReturnS32(ctx, ret); -} - -void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioWrite(rdram, ctx, runtime); -} diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl deleted file mode 100644 index b7ebf030..00000000 --- a/ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl +++ /dev/null @@ -1,150 +0,0 @@ -void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t a0 = getRegU32(ctx, 4); // usually lbn - const uint32_t a1 = getRegU32(ctx, 5); // usually sector count - const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer - - struct CdReadArgs - { - uint32_t lbn = 0; - uint32_t sectors = 0; - uint32_t buf = 0; - const char *tag = ""; - }; - - auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t - { - const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); - if (requested == 0) - { - return 0; - } - - const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); - const uint64_t clamped = std::min(requested, maxBytes); - return static_cast(clamped); - }; - - auto tryRead = [&](const CdReadArgs &args) -> bool - { - const uint32_t offset = args.buf & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(args.sectors, offset); - if (bytes == 0) - { - return true; - } - - return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); - }; - - CdReadArgs selected{a0, a1, a2, "a0/a1/a2"}; - bool ok = tryRead(selected); - - if (!ok) - { - // Some game-side wrappers use a nonstandard register layout. - // If primary decode does not resolve to a known LBN, try safe alternatives. - constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize; - if (!isResolvableCdLbn(selected.lbn)) - { - const std::array alternatives = { - CdReadArgs{a2, a1, a0, "a2/a1/a0"}, - CdReadArgs{a0, a2, a1, "a0/a2/a1"}, - CdReadArgs{a1, a0, a2, "a1/a0/a2"}, - CdReadArgs{a1, a2, a0, "a1/a2/a0"}, - CdReadArgs{a2, a0, a1, "a2/a0/a1"}}; - - for (const CdReadArgs &candidate : alternatives) - { - if (candidate.sectors > kMaxReasonableSectors) - { - continue; - } - if (!isResolvableCdLbn(candidate.lbn)) - { - continue; - } - - if (tryRead(candidate)) - { - static uint32_t recoverLogCount = 0; - if (recoverLogCount < 16) - { - std::cout << "[sceCdRead] recovered with alternate args " << candidate.tag - << " (pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << ")" << std::endl; - ++recoverLogCount; - } - selected = candidate; - ok = true; - break; - } - } - } - - if (!ok) - { - const uint32_t offset = a2 & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(a1, offset); - if (bytes > 0) - { - std::memset(rdram + offset, 0, bytes); - } - - static uint32_t unresolvedLogCount = 0; - if (unresolvedLogCount < 32) - { - std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << std::endl; - ++unresolvedLogCount; - } - } - } - - if (ok) - { - g_cdStreamingLbn = selected.lbn + selected.sectors; - setReturnS32(ctx, 1); // command accepted/success - return; - } - - setReturnS32(ctx, 0); -} - -void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); // 0 = completed/not busy -} - -void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_lastCdError); -} - -void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub builtin_set_imask" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void InitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitThread" << std::endl; - ++logCount; - } - setReturnS32(ctx, 1); // success -} \ No newline at end of file diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl deleted file mode 100644 index 290ef80a..00000000 --- a/ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl +++ /dev/null @@ -1,670 +0,0 @@ -void syRtcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syRtcInit" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -namespace -{ - constexpr uint32_t kCvSyMallocAddr = 0x002D9A70u; - - constexpr uint32_t kCvMallocMaxSizeAddr = 0x01140B60u; - constexpr uint32_t kCvMallocFreeSizeAddr = 0x01140B68u; - constexpr uint32_t kCvMallocHeadPtrAddr = 0x01140B70u; - constexpr uint32_t kCvMallocPoolAddr = 0x01140B80u; - constexpr uint32_t kCvMallocPoolSize = 0x00CCD000u; - - constexpr uint32_t kCvMallocUseSizeOff = 0x00u; - constexpr uint32_t kCvMallocTotalSizeOff = 0x04u; - constexpr uint32_t kCvMallocNextOff = 0x0Cu; - constexpr uint32_t kCvMallocHeaderSize = 0x40u; - constexpr uint32_t kCvMallocInitialFreeSize = kCvMallocPoolSize - kCvMallocHeaderSize; - - uint32_t cvReadU32(const uint8_t *rdram, uint32_t addr) - { - if (!rdram) - { - return 0u; - } - - const uint32_t offset = addr & PS2_RAM_MASK; - if (offset + sizeof(uint32_t) > PS2_RAM_SIZE) - { - return 0u; - } - - uint32_t value = 0u; - std::memcpy(&value, rdram + offset, sizeof(value)); - return value; - } - - void cvWriteU32(uint8_t *rdram, uint32_t addr, uint32_t value) - { - if (!rdram) - { - return; - } - - const uint32_t offset = addr & PS2_RAM_MASK; - if (offset + sizeof(uint32_t) > PS2_RAM_SIZE) - { - return; - } - - std::memcpy(rdram + offset, &value, sizeof(value)); - } -} - -void syFree(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syFree" << std::endl; - ++logCount; - } - - const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 - bool released = false; - - if (rdram && guestAddr != 0u) - { - uint32_t search = cvReadU32(rdram, kCvMallocHeadPtrAddr); - if (search < PS2_RAM_SIZE) - { - for (uint32_t guard = 0; guard < 0x100000u; ++guard) - { - const uint32_t next = cvReadU32(rdram, search + kCvMallocNextOff); - if (next == 0u) - { - break; - } - - if (guestAddr == (next + kCvMallocHeaderSize)) - { - const uint32_t searchTotal = cvReadU32(rdram, search + kCvMallocTotalSizeOff); - const uint32_t nextTotal = cvReadU32(rdram, next + kCvMallocTotalSizeOff); - const uint32_t nextUsed = cvReadU32(rdram, next + kCvMallocUseSizeOff); - const uint32_t nextNext = cvReadU32(rdram, next + kCvMallocNextOff); - const uint32_t freeSize = cvReadU32(rdram, kCvMallocFreeSizeAddr); - - cvWriteU32(rdram, search + kCvMallocTotalSizeOff, searchTotal + nextTotal + kCvMallocHeaderSize); - cvWriteU32(rdram, search + kCvMallocNextOff, nextNext); - cvWriteU32(rdram, kCvMallocFreeSizeAddr, freeSize + nextUsed + kCvMallocHeaderSize); - - released = true; - break; - } - - search = next; - if (search >= PS2_RAM_SIZE) - { - break; - } - } - } - } - - if (!released && runtime && guestAddr != 0u) - { - runtime->guestFree(guestAddr); - } - - setReturnS32(ctx, 0); -} - -void syMalloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t requestedSize = getRegU32(ctx, 4); // $a0 - uint32_t resultAddr = 0u; - - if (runtime && requestedSize != 0u && runtime->hasFunction(kCvSyMallocAddr) && ctx->pc != kCvSyMallocAddr) - { - const uint32_t returnPc = getRegU32(ctx, 31); - PS2Runtime::RecompiledFunction syMallocFn = runtime->lookupFunction(kCvSyMallocAddr); - ctx->pc = kCvSyMallocAddr; - { - PS2Runtime::GuestExecutionScope guestExecution(runtime); - syMallocFn(rdram, ctx, runtime); - } - - if (ctx->pc == kCvSyMallocAddr || ctx->pc == 0u) - { - ctx->pc = returnPc; - } - - resultAddr = getRegU32(ctx, 2); - } - else if (runtime && requestedSize != 0u) - { - // Match game expectation for allocator alignment while keeping pointers in EE RAM. - resultAddr = runtime->guestMalloc(requestedSize, 64u); - } - - static int logCount = 0; - if (logCount < 16) - { - std::cout << "ps2_stub syMalloc" - << " size=0x" << std::hex << requestedSize - << " -> 0x" << resultAddr - << std::dec << std::endl; - ++logCount; - } - - setReturnU32(ctx, resultAddr); -} - -void InitSdcParameter(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitSdcParameter" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void Ps2_pad_actuater(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub Ps2_pad_actuater" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void syMallocInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t heapBase = getRegU32(ctx, 4); // $a0 (ignored by original CV allocator) - const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (ignored by original CV allocator) - - cvWriteU32(rdram, kCvMallocMaxSizeAddr, 0u); - cvWriteU32(rdram, kCvMallocFreeSizeAddr, kCvMallocInitialFreeSize); - cvWriteU32(rdram, kCvMallocHeadPtrAddr, kCvMallocPoolAddr); - - cvWriteU32(rdram, kCvMallocPoolAddr + kCvMallocUseSizeOff, 0u); - cvWriteU32(rdram, kCvMallocPoolAddr + kCvMallocTotalSizeOff, kCvMallocInitialFreeSize); - cvWriteU32(rdram, kCvMallocPoolAddr + kCvMallocNextOff, 0u); - - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syMallocInit" - << " reqBase=0x" << std::hex << heapBase - << " reqSize=0x" << heapSize - << " pool=0x" << kCvMallocPoolAddr - << " free=0x" << kCvMallocInitialFreeSize - << std::dec << std::endl; - ++logCount; - } - - setReturnS32(ctx, 0); -} - -void syHwInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syHwInit" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void syHwInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syHwInit2" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void InitGdSystemEx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitGdSystemEx" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void pdInitPeripheral(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub pdInitPeripheral" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void pdGetPeripheral(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub pdGetPeripheral" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub Ps2SwapDBuff" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void InitReadKeyEx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitReadKeyEx" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void SetRepeatKeyTimer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub SetRepeatKeyTimer" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void StopFxProgram(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub StopFxProgram" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sndr_trans_func (noop)" << std::endl; - ++logCount; - } - - // small hack for code veronica - constexpr uint32_t kSndBusyAddrCv = 0x01E1E190; - constexpr uint32_t kSndBusyAddrLegacy = 0x01E0E170; - if (rdram) - { - uint32_t offset = kSndBusyAddrCv & PS2_RAM_MASK; - if (offset + sizeof(uint32_t) <= PS2_RAM_SIZE) - { - *reinterpret_cast(rdram + offset) = 0; - } - - offset = kSndBusyAddrLegacy & PS2_RAM_MASK; - if (offset + sizeof(uint32_t) <= PS2_RAM_SIZE) - { - *reinterpret_cast(rdram + offset) = 0; - } - } - - setReturnS32(ctx, 0); -} - -void sdDrvInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - constexpr uint32_t kSdrInitAddr = 0x2E9A20u; - - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sdDrvInit -> SdrInit_0x2e9a20" << std::endl; - ++logCount; - } - - if (!runtime || !ctx || !rdram || !runtime->hasFunction(kSdrInitAddr)) - { - setReturnS32(ctx, 0); - return; - } - - const uint32_t returnPc = getRegU32(ctx, 31); - PS2Runtime::RecompiledFunction sdrInit = runtime->lookupFunction(kSdrInitAddr); - ctx->pc = kSdrInitAddr; - { - PS2Runtime::GuestExecutionScope guestExecution(runtime); - sdrInit(rdram, ctx, runtime); - } - - if (ctx->pc == kSdrInitAddr || ctx->pc == 0u) - { - ctx->pc = returnPc; - } -} - -void ADXF_LoadPartitionNw(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub ADXF_LoadPartitionNw (noop)" << std::endl; - ++logCount; - } - // Return success to keep the ADX partition setup moving. - setReturnS32(ctx, 0); -} - -void sdSndStopAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sdSndStopAll" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void sdSysFinish(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sdSysFinish" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void ADXT_Init(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub ADXT_Init" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void ADXT_SetNumRetry(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub ADXT_SetNumRetry" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub cvFsSetDefDev" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); -} - -namespace -{ - int32_t g_cvMcFileCursor = 0; - constexpr int32_t kCvMcFreeCapacityBytes = 0x01000000; - constexpr int32_t kCvMcSaveCapacityBytes = 0x00080000; - constexpr int32_t kCvMcConfigCapacityBytes = 0x00008000; - constexpr int32_t kCvMcIconCapacityBytes = 0x00004000; -} - -void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcConfigCapacityBytes); -} - -void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_cvMcFileCursor); -} - -void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcFreeCapacityBytes); -} - -void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcIconCapacityBytes); -} - -void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcIconCapacityBytes); -} - -void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcSaveCapacityBytes); -} - -void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t strAddr = getRegU32(ctx, 4); - const std::string value = readPs2CStringBounded(rdram, runtime, strAddr, 1024); - setReturnU32(ctx, strAddr + static_cast(value.size())); -} - -void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t delta = static_cast(getRegU32(ctx, 5)); - g_cvMcFileCursor += delta; - g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); - setReturnS32(ctx, 0); -} - -void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cvMcFileCursor = 0; - setReturnS32(ctx, 1); -} - -void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cvMcFileCursor = static_cast(getRegU32(ctx, 5)); - g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); - setReturnS32(ctx, 0); -} - -void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cvMcFileCursor = 0; - setReturnS32(ctx, 0); -} - -void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl deleted file mode 100644 index 6524667e..00000000 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl +++ /dev/null @@ -1,1320 +0,0 @@ -void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t moduleId = static_cast(getRegU32(ctx, 4)); // $a0 - const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional) - - uint32_t refsLeft = 0; - const bool knownModule = trackSifModuleStop(moduleId, &refsLeft); - const int32_t ret = knownModule ? 0 : -1; - - if (resultAddr != 0) - { - int32_t *hostResult = reinterpret_cast(getMemPtr(rdram, resultAddr)); - if (hostResult) - { - *hostResult = knownModule ? 0 : -1; - } - } - - if (knownModule) - { - std::string modulePath; - { - std::lock_guard lock(g_sif_module_mutex); - auto it = g_sif_modules_by_id.find(moduleId); - if (it != g_sif_modules_by_id.end()) - { - modulePath = it->second.path; - } - } - logSifModuleAction("stop", moduleId, modulePath, refsLeft); - } - - setReturnS32(ctx, ret); -} - -void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes); - if (modulePath.empty()) - { - setReturnS32(ctx, -1); - return; - } - - const int32_t moduleId = trackSifModuleLoad(modulePath); - if (moduleId <= 0) - { - setReturnS32(ctx, -1); - return; - } - - uint32_t refs = 0; - { - std::lock_guard lock(g_sif_module_mutex); - auto it = g_sif_modules_by_id.find(moduleId); - if (it != g_sif_modules_by_id.end()) - { - refs = it->second.refCount; - } - } - logSifModuleAction("load", moduleId, modulePath, refs); - - setReturnS32(ctx, moduleId); -} - -void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); - if (!g_rpc_initialized) - { - g_rpc_servers.clear(); - g_rpc_clients.clear(); - g_rpc_next_id = 1; - g_rpc_packet_index = 0; - g_rpc_server_index = 0; - g_rpc_active_queue = 0; - g_dtx_remote_by_id.clear(); - g_dtx_next_urpc_obj = kDtxUrpcObjBase; - g_rpc_initialized = true; - std::cout << "[SifInitRpc] Initialized" << std::endl; - } - setReturnS32(ctx, 0); -} - -void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clientPtr = getRegU32(ctx, 4); - uint32_t rpcId = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - - t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); - - if (!client) - { - setReturnS32(ctx, -1); - return; - } - - client->command = 0; - client->buf = 0; - client->cbuf = 0; - client->end_function = 0; - client->end_param = 0; - client->server = 0; - client->hdr.pkt_addr = 0; - client->hdr.sema_id = -1; - client->hdr.mode = mode; - - uint32_t serverPtr = 0; - { - std::lock_guard lock(g_rpc_mutex); - client->hdr.rpc_id = g_rpc_next_id++; - auto it = g_rpc_servers.find(rpcId); - if (it != g_rpc_servers.end()) - { - serverPtr = it->second.sd_ptr; - } - g_rpc_clients[clientPtr] = {}; - g_rpc_clients[clientPtr].sid = rpcId; - } - - if (!serverPtr) - { - // Allocate a dummy server so bind loops can proceed. - serverPtr = rpcAllocServerAddr(rdram); - if (serverPtr) - { - t_SifRpcServerData *dummy = reinterpret_cast(getMemPtr(rdram, serverPtr)); - if (dummy) - { - std::memset(dummy, 0, sizeof(*dummy)); - dummy->sid = static_cast(rpcId); - } - std::lock_guard lock(g_rpc_mutex); - g_rpc_servers[rpcId] = {rpcId, serverPtr}; - } - } - - if (serverPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, serverPtr)); - client->server = serverPtr; - client->buf = sd ? sd->buf : 0; - client->cbuf = sd ? sd->cbuf : 0; - } - else - { - client->server = 0; - client->buf = 0; - client->cbuf = 0; - } - - setReturnS32(ctx, 0); -} - -void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard rpcCallLock(g_sif_call_rpc_mutex); - - uint32_t clientPtr = getRegU32(ctx, 4); - uint32_t rpcNum = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t sendBuf = getRegU32(ctx, 7); - uint32_t sendSize = 0; - uint32_t recvBuf = 0; - uint32_t recvSize = 0; - uint32_t endFunc = 0; - uint32_t endParam = 0; - - // Decode both extended-reg convention (EE default) and standard O32 stack convention, - // picking REG whenever plausible, to avoid zero-collision on the stack. - uint32_t sp = getRegU32(ctx, 29); - - uint32_t sendSizeReg = getRegU32(ctx, 8); - uint32_t recvBufReg = getRegU32(ctx, 9); - uint32_t recvSizeReg = getRegU32(ctx, 10); - uint32_t endFuncReg = getRegU32(ctx, 11); - uint32_t endParamReg = 0; - (void)readStackU32(rdram, sp, 0x0, endParamReg); - - uint32_t sendSizeStk = 0; - uint32_t recvBufStk = 0; - uint32_t recvSizeStk = 0; - uint32_t endFuncStk = 0; - uint32_t endParamStk = 0; - (void)readStackU32(rdram, sp, 0x10, sendSizeStk); - (void)readStackU32(rdram, sp, 0x14, recvBufStk); - (void)readStackU32(rdram, sp, 0x18, recvSizeStk); - (void)readStackU32(rdram, sp, 0x1C, endFuncStk); - (void)readStackU32(rdram, sp, 0x20, endParamStk); - - auto looksLikeGuestPtr = [&](uint32_t v) -> bool - { - if (v == 0) - return true; - const uint32_t norm = v & 0x1FFFFFFFu; - return norm >= 0x10000u && norm < PS2_RAM_SIZE; - }; - - auto looksLikeSize = [&](uint32_t v) -> bool - { - return v <= 0x2000000u; - }; - - auto looksLikeFunc = [&](uint32_t v) -> bool - { - return v == 0 || looksLikeGuestPtr(v); - }; - - auto plausiblePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz, uint32_t endFn) -> bool - { - return looksLikeSize(sendSz) && looksLikeGuestPtr(rbuf) && looksLikeSize(rsz) && looksLikeFunc(endFn); - }; - - const bool regPackPlausible = plausiblePack(sendSizeReg, recvBufReg, recvSizeReg, endFuncReg); - const bool stackPackPlausible = plausiblePack(sendSizeStk, recvBufStk, recvSizeStk, endFuncStk); - - uint32_t boundSidHint = 0u; - { - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it != g_rpc_clients.end()) - { - boundSidHint = it->second.sid; - } - } - - auto looksLikeDtxCreatePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz) -> bool - { - return rbuf != 0u && rsz >= 4u && rsz <= 0x40u && - sendSz >= 12u && sendSz <= 0x1000u; - }; - - const bool isDtxCreate34Call = (boundSidHint == kDtxRpcSid) && (rpcNum == 0x422u); - const bool forceStackForDtxCreate34 = - isDtxCreate34Call && - stackPackPlausible && - looksLikeDtxCreatePack(sendSizeStk, recvBufStk, recvSizeStk) && - !looksLikeDtxCreatePack(sendSizeReg, recvBufReg, recvSizeReg); - - bool useRegConvention = true; - if (forceStackForDtxCreate34) - { - useRegConvention = false; - } - else if (!regPackPlausible && stackPackPlausible) - { - const bool regHasValidCallback = (endFuncReg != 0u) && looksLikeFunc(endFuncReg); - const bool stackHasValidCallback = (endFuncStk != 0u) && looksLikeFunc(endFuncStk); - if (!(regHasValidCallback && !stackHasValidCallback)) - { - useRegConvention = false; - } - } - - sendSize = useRegConvention ? sendSizeReg : sendSizeStk; - recvBuf = useRegConvention ? recvBufReg : recvBufStk; - recvSize = useRegConvention ? recvSizeReg : recvSizeStk; - endFunc = useRegConvention ? endFuncReg : endFuncStk; - endParam = useRegConvention ? endParamReg : endParamStk; - - const bool isDtxLikeRpc = (boundSidHint == kDtxRpcSid) || ((rpcNum & 0xFF00u) == 0x0400u); - static uint32_t dtxAbiLogCount = 0u; - if (isDtxLikeRpc && dtxAbiLogCount < 96u) - { - std::cout << "[SifCallRpc:ABI] client=0x" << std::hex << clientPtr - << " rpc=0x" << rpcNum - << " sidHint=0x" << boundSidHint - << " useReg=" << (useRegConvention ? 1 : 0) - << " reg=(" << sendSizeReg << "," << recvBufReg << "," << recvSizeReg << "," << endFuncReg << "," << endParamReg << ")" - << " stk=(" << sendSizeStk << "," << recvBufStk << "," << recvSizeStk << "," << endFuncStk << "," << endParamStk << ")" - << " plausible=(" << (regPackPlausible ? 1 : 0) << "," << (stackPackPlausible ? 1 : 0) << ")" - << " force34=" << (forceStackForDtxCreate34 ? 1 : 0) - << std::dec << std::endl; - ++dtxAbiLogCount; - } - - t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); - - if (!client) - { - setReturnS32(ctx, -1); - return; - } - - client->command = rpcNum; - client->end_function = endFunc; - client->end_param = endParam; - client->hdr.mode = mode; - - { - std::lock_guard lock(g_rpc_mutex); - g_rpc_clients[clientPtr].busy = true; - g_rpc_clients[clientPtr].last_rpc = rpcNum; - uint32_t sid = g_rpc_clients[clientPtr].sid; - if (sid) - { - auto it = g_rpc_servers.find(sid); - if (it != g_rpc_servers.end()) - { - uint32_t mappedServer = it->second.sd_ptr; - if (mappedServer && client->server != mappedServer) - { - client->server = mappedServer; - } - } - } - } - - uint32_t sid = 0; - { - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it != g_rpc_clients.end()) - { - sid = it->second.sid; - } - } - - uint32_t serverPtr = client->server; - t_SifRpcServerData *sd = serverPtr ? reinterpret_cast(getMemPtr(rdram, serverPtr)) : nullptr; - - if (sd) - { - sd->client = clientPtr; - sd->pkt_addr = client->hdr.pkt_addr; - sd->rpc_number = rpcNum; - sd->size = static_cast(sendSize); - sd->recvbuf = recvBuf; - sd->rsize = static_cast(recvSize); - sd->rmode = ((mode & kSifRpcModeNowait) && endFunc == 0) ? 0 : 1; - sd->rid = 0; - } - - if (sd && sd->buf && sendBuf && sendSize > 0) - { - rpcCopyToRdram(rdram, sd->buf, sendBuf, sendSize); - } - - uint32_t resultPtr = 0; - bool handled = false; - - auto readRpcU32 = [&](uint32_t addr, uint32_t &out) -> bool - { - if (!addr) - { - return false; - } - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(&out, ptr, sizeof(out)); - return true; - }; - - auto writeRpcU32 = [&](uint32_t addr, uint32_t value) -> bool - { - if (!addr) - { - return false; - } - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(ptr, &value, sizeof(value)); - return true; - }; - - if (!handled && sid != 0 && runtime) - { - if (!runtime->iop().handleRPC(sid, rpcNum, sendBuf, sendSize, recvBuf, recvSize) && - sid == IOP_SID_LIBSD) - { - const uint8_t *sendPtr = sendBuf ? getConstMemPtr(rdram, sendBuf) : nullptr; - uint8_t *recvPtr = recvBuf ? getMemPtr(rdram, recvBuf) : nullptr; - ps2_iop_audio::handleLibSdRpc(runtime, sid, rpcNum, sendPtr, sendSize, recvPtr, recvSize); - handled = true; - resultPtr = recvBuf; - } - } - - const bool isDtxUrpc = (sid == kDtxRpcSid) && (rpcNum >= 0x400u) && (rpcNum < 0x500u); - uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; - uint32_t dtxUrpcFn = 0; - uint32_t dtxUrpcObj = 0; - uint32_t dtxUrpcSend0 = 0; - bool dtxUrpcDispatchAttempted = false; - bool dtxUrpcFallbackEmulated = false; - bool dtxUrpcFallbackCreate34 = false; - bool hasUrpcHandler = false; - if (isDtxUrpc) - { - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, dtxUrpcSend0); - } - if (dtxUrpcCommand < 64u) - { - (void)readRpcU32(kDtxUrpcFnTableBase + (dtxUrpcCommand * 4u), dtxUrpcFn); - (void)readRpcU32(kDtxUrpcObjTableBase + (dtxUrpcCommand * 4u), dtxUrpcObj); - } - hasUrpcHandler = (dtxUrpcCommand < 64u) && (dtxUrpcFn != 0u); - } - const bool allowServerDispatch = !isDtxUrpc || hasUrpcHandler; - - if (sd && sd->func && (sid != kDtxRpcSid || isDtxUrpc) && allowServerDispatch) - { - dtxUrpcDispatchAttempted = dtxUrpcDispatchAttempted || isDtxUrpc; - handled = rpcInvokeFunction(rdram, ctx, runtime, sd->func, rpcNum, sd->buf, sendSize, 0, &resultPtr); - if (handled && resultPtr == 0 && sd->buf) - { - resultPtr = sd->buf; - } - if (handled && resultPtr == 0 && recvBuf) - { - resultPtr = recvBuf; - } - } - - if (!handled && isDtxUrpc && sendBuf && sendSize > 0) - { - // Only dispatch through dtx_rpc_func when a URPC handler is registered in the table. - // If the slot is empty, defer to the fallback emulation below. - if (hasUrpcHandler) - { - dtxUrpcDispatchAttempted = true; - handled = rpcInvokeFunction(rdram, ctx, runtime, 0x2fabc0u, rpcNum, sendBuf, sendSize, 0, &resultPtr); - if (handled && resultPtr == 0) - { - resultPtr = sendBuf; - } - } - } - - if (!handled && sid == kDtxRpcSid) - { - if (rpcNum == 2 && recvBuf && recvSize >= sizeof(uint32_t)) - { - uint32_t dtxId = 0; - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, dtxId); - } - - uint32_t remoteHandle = 0; - { - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_remote_by_id.find(dtxId); - if (it != g_dtx_remote_by_id.end()) - { - remoteHandle = it->second; - } - if (!remoteHandle) - { - remoteHandle = rpcAllocServerAddr(rdram); - if (!remoteHandle) - { - remoteHandle = rpcAllocPacketAddr(rdram); - } - if (!remoteHandle) - { - remoteHandle = kRpcServerPoolBase + ((dtxId & 0xFFu) * kRpcServerStride); - } - g_dtx_remote_by_id[dtxId] = remoteHandle; - } - } - - (void)writeRpcU32(recvBuf, remoteHandle); - if (recvSize > sizeof(uint32_t)) - { - rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); - } - static uint32_t dtxCreateLogCount = 0; - if (dtxCreateLogCount < 64u) - { - std::cout << "[SifCallRpc:DTX_CREATE] dtxId=0x" << std::hex << dtxId - << " remote=0x" << remoteHandle - << " recvBuf=0x" << recvBuf - << " recvSize=0x" << recvSize - << std::dec << std::endl; - ++dtxCreateLogCount; - } - handled = true; - resultPtr = recvBuf; - } - else if (rpcNum == 3) - { - uint32_t remoteHandle = 0; - if (sendBuf && sendSize >= sizeof(uint32_t) && readRpcU32(sendBuf, remoteHandle) && remoteHandle) - { - std::lock_guard lock(g_dtx_rpc_mutex); - for (auto it = g_dtx_remote_by_id.begin(); it != g_dtx_remote_by_id.end(); ++it) - { - if (it->second == remoteHandle) - { - g_dtx_remote_by_id.erase(it); - break; - } - } - } - if (recvBuf && recvSize > 0) - { - rpcZeroRdram(rdram, recvBuf, recvSize); - } - handled = true; - resultPtr = recvBuf; - } - else if (rpcNum >= 0x400 && rpcNum < 0x500) - { - dtxUrpcFallbackEmulated = true; - const uint32_t urpcCommand = rpcNum & 0xFFu; - uint32_t outWords[4] = {1u, 0u, 0u, 0u}; - uint32_t outWordCount = 1u; - - auto readSendWord = [&](uint32_t index, uint32_t &out) -> bool - { - const uint64_t byteOffset = static_cast(index) * sizeof(uint32_t); - if (!sendBuf || sendSize < (byteOffset + sizeof(uint32_t))) - { - return false; - } - return readRpcU32(sendBuf + static_cast(byteOffset), out); - }; - - switch (urpcCommand) - { - case 32u: // SJRMT_RBF_CREATE - case 33u: // SJRMT_MEM_CREATE - case 34u: // SJRMT_UNI_CREATE - { - uint32_t arg0 = 0; - uint32_t arg1 = 0; - uint32_t arg2 = 0; - (void)readSendWord(0u, arg0); - (void)readSendWord(1u, arg1); - (void)readSendWord(2u, arg2); - - uint32_t mode = 0; - uint32_t wkAddr = 0; - uint32_t wkSize = 0; - if (urpcCommand == 34u) - { - mode = arg0; - wkAddr = arg1; - wkSize = arg2; - dtxUrpcFallbackCreate34 = true; - } - else if (urpcCommand == 33u) - { - wkAddr = arg0; - wkSize = arg1; - } - else - { - wkAddr = arg0; - wkSize = (arg1 != 0u) ? arg1 : arg2; - } - - wkSize = dtxNormalizeSjrmtCapacity(wkSize); - - std::lock_guard lock(g_dtx_rpc_mutex); - const uint32_t handle = dtxAllocUrpcHandleLocked(); - DtxSjrmtState state{}; - state.handle = handle; - state.mode = mode; - state.wkAddr = wkAddr; - state.wkSize = wkSize; - state.readPos = 0u; - state.writePos = 0u; - state.roomBytes = wkSize; - state.dataBytes = 0u; - state.uuid0 = 0x53524D54u; // "SRMT" - state.uuid1 = handle; - state.uuid2 = wkAddr; - state.uuid3 = wkSize; - g_dtx_sjrmt_by_handle[handle] = state; - - outWords[0] = handle ? handle : 1u; - outWordCount = 1u; - break; - } - case 35u: // SJRMT_DESTROY - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - g_dtx_sjrmt_by_handle.erase(handle); - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 36u: // SJRMT_GET_UUID - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - outWords[0] = it->second.uuid0; - outWords[1] = it->second.uuid1; - outWords[2] = it->second.uuid2; - outWords[3] = it->second.uuid3; - } - else - { - outWords[0] = 0u; - outWords[1] = 0u; - outWords[2] = 0u; - outWords[3] = 0u; - } - outWordCount = 4u; - break; - } - case 37u: // SJRMT_RESET - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - const uint32_t cap = (it->second.wkSize == 0u) ? 0x4000u : it->second.wkSize; - it->second.readPos = 0u; - it->second.writePos = 0u; - it->second.roomBytes = cap; - it->second.dataBytes = 0u; - } - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 38u: // SJRMT_GET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t nbyte = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(2u, nbyte); - - uint32_t ptr = 0u; - uint32_t len = 0u; - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - - if (streamId == 0u) - { - len = std::min(nbyte, state.roomBytes); - ptr = state.wkAddr + (cap ? (state.writePos % cap) : 0u); - if (cap != 0u) - { - state.writePos = (state.writePos + len) % cap; - } - state.roomBytes -= len; - } - else if (streamId == 1u) - { - len = std::min(nbyte, state.dataBytes); - ptr = state.wkAddr + (cap ? (state.readPos % cap) : 0u); - if (cap != 0u) - { - state.readPos = (state.readPos + len) % cap; - } - state.dataBytes -= len; - } - } - - outWords[0] = ptr; - outWords[1] = len; - outWordCount = 2u; - break; - } - case 39u: // SJRMT_UNGET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t len = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(3u, len); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (streamId == 0u) - { - const uint32_t delta = (cap == 0u) ? 0u : (len % cap); - if (cap != 0u) - { - state.writePos = (state.writePos + cap - delta) % cap; - } - state.roomBytes = std::min(cap, state.roomBytes + len); - } - else if (streamId == 1u) - { - const uint32_t delta = (cap == 0u) ? 0u : (len % cap); - if (cap != 0u) - { - state.readPos = (state.readPos + cap - delta) % cap; - } - state.dataBytes = std::min(cap, state.dataBytes + len); - } - } - - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 40u: // SJRMT_PUT_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t len = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(3u, len); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (streamId == 0u) - { - state.roomBytes = std::min(cap, state.roomBytes + len); - } - else if (streamId == 1u) - { - state.dataBytes = std::min(cap, state.dataBytes + len); - } - } - - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 41u: // SJRMT_GET_NUM_DATA - { - uint32_t handle = 0; - uint32_t streamId = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - outWords[0] = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; - } - else - { - outWords[0] = 0u; - } - outWordCount = 1u; - break; - } - case 42u: // SJRMT_IS_GET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t nbyte = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(2u, nbyte); - - uint32_t available = 0u; - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - available = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; - } - outWords[0] = (available >= nbyte) ? 1u : 0u; - outWords[1] = available; - outWordCount = 2u; - break; - } - case 43u: // SJRMT_INIT - case 44u: // SJRMT_FINISH - { - outWords[0] = 1u; - outWordCount = 1u; - break; - } - default: - { - uint32_t urpcRet = 1u; - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, urpcRet); - } - if (urpcCommand == 0u) - { - std::lock_guard lock(g_dtx_rpc_mutex); - urpcRet = dtxAllocUrpcHandleLocked(); - } - if (urpcRet == 0u) - { - urpcRet = 1u; - } - outWords[0] = urpcRet; - outWordCount = 1u; - break; - } - } - - if (recvBuf && recvSize > 0u) - { - const uint32_t recvWordCapacity = static_cast(recvSize / sizeof(uint32_t)); - const uint32_t wordsToWrite = std::min(outWordCount, recvWordCapacity); - for (uint32_t i = 0; i < wordsToWrite; ++i) - { - (void)writeRpcU32(recvBuf + (i * sizeof(uint32_t)), outWords[i]); - } - - // SJRMT_IsGetChunk callers read rbuf[1] even when nout==1. - if (urpcCommand == 42u && outWordCount > 1u) - { - (void)writeRpcU32(recvBuf + sizeof(uint32_t), outWords[1]); - } - - if (recvSize > (wordsToWrite * sizeof(uint32_t))) - { - rpcZeroRdram(rdram, recvBuf + (wordsToWrite * sizeof(uint32_t)), - recvSize - (wordsToWrite * sizeof(uint32_t))); - } - } - - handled = true; - resultPtr = recvBuf; - } - } - - auto signalRpcCompletionSema = [&](uint32_t semaId) -> bool - { - if (semaId == 0u || semaId > 0xFFFFu) - { - return false; - } - - auto sema = lookupSemaInfo(static_cast(semaId)); - if (!sema) - { - return false; - } - - bool signaled = false; - { - std::lock_guard lock(sema->m); - if (!sema->deleted && sema->count < sema->maxCount) - { - sema->count++; - signaled = true; - } - } - - if (signaled) - { - sema->cv.notify_one(); - } - return signaled; - }; - - if (sid == 1u && (rpcNum == 0x12u || rpcNum == 0x13u)) - { - // RECVX snddrv expects: - // cmd 0x12 -> SND_STATUS* (get_adrs) - // cmd 0x13 -> int[16]* (iop_data_adr_top) - constexpr uint32_t kSdrStatusAddr = 0x00012000u; - constexpr uint32_t kSdrAddrTableAddr = 0x00012100u; - constexpr uint32_t kSdrHdBaseAddr = 0x00014000u; - constexpr uint32_t kSdrSqBaseAddr = 0x00018000u; - constexpr uint32_t kSdrDataBaseAddr = 0x00030000u; - - rpcZeroRdram(rdram, kSdrStatusAddr, 0x42u); - rpcZeroRdram(rdram, kSdrAddrTableAddr, 16u * sizeof(uint32_t)); - (void)writeRpcU32(kSdrAddrTableAddr + (0u * sizeof(uint32_t)), kSdrHdBaseAddr); - (void)writeRpcU32(kSdrAddrTableAddr + (1u * sizeof(uint32_t)), kSdrSqBaseAddr); - (void)writeRpcU32(kSdrAddrTableAddr + (2u * sizeof(uint32_t)), kSdrDataBaseAddr); - - const uint32_t responseWord = (rpcNum == 0x12u) ? kSdrStatusAddr : kSdrAddrTableAddr; - if (recvBuf && recvSize >= sizeof(uint32_t)) - { - (void)writeRpcU32(recvBuf, responseWord); - if (recvSize > sizeof(uint32_t)) - { - rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); - } - resultPtr = recvBuf; - } - - handled = true; - - if ((mode & kSifRpcModeNowait) != 0u) - { - uint32_t semaId = static_cast(client->hdr.sema_id); - if (semaId == 0xFFFFFFFFu || semaId == 0u) - { - semaId = endParam; - } - (void)signalRpcCompletionSema(semaId); - } - } - - if (recvBuf && recvSize > 0) - { - if (handled && resultPtr && resultPtr != recvBuf) - { - rpcCopyToRdram(rdram, recvBuf, resultPtr, recvSize); - } - else if (!handled && sendBuf && sendSize > 0 && sendBuf != recvBuf) - { - size_t copySize = (sendSize < recvSize) ? sendSize : recvSize; - rpcCopyToRdram(rdram, recvBuf, sendBuf, copySize); - } - else if (!handled) - { - rpcZeroRdram(rdram, recvBuf, recvSize); - } - } - - if (isDtxUrpc) - { - static int dtxUrpcLogCount = 0; - if (dtxUrpcLogCount < 64) - { - uint32_t dtxUrpcRecv0 = 0; - if (recvBuf && recvSize >= sizeof(uint32_t)) - { - (void)readRpcU32(recvBuf, dtxUrpcRecv0); - } - std::cout << "[SifCallRpc:DTX] rpcNum=0x" << std::hex << rpcNum - << " cmd=0x" << dtxUrpcCommand - << " fn=0x" << dtxUrpcFn - << " obj=0x" << dtxUrpcObj - << " send0=0x" << dtxUrpcSend0 - << " recv0=0x" << dtxUrpcRecv0 - << " resultPtr=0x" << resultPtr - << " handled=" << std::dec << (handled ? 1 : 0) - << " dispatch=" << (dtxUrpcDispatchAttempted ? 1 : 0) - << " emu=" << (dtxUrpcFallbackEmulated ? 1 : 0) - << " emu34=" << (dtxUrpcFallbackCreate34 ? 1 : 0) - << std::endl; - ++dtxUrpcLogCount; - } - } - - if (endFunc) - { - bool callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr); - - if (!callbackInvoked && (endFunc == 0x2fac20u || endFunc == 0x2fac30u)) - { - const uint32_t normalizedEndFunc = endFunc - 0x10000u; - callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, normalizedEndFunc, endParam, 0, 0, 0, nullptr); - } - - const bool isSoundRpcCallback = - (endFunc == 0x2eac20u || endFunc == 0x2eac30u || - endFunc == 0x2fac20u || endFunc == 0x2fac30u); - if (isSoundRpcCallback) - { - uint32_t semaId = static_cast(client->hdr.sema_id); - if (semaId == 0xFFFFFFFFu || semaId == 0u) - { - semaId = endParam; - } - (void)signalRpcCompletionSema(semaId); - if (rdram && (endFunc == 0x2eac30u || endFunc == 0x2fac30u)) - { - constexpr uint32_t kSndBusyFlagAddr = 0x01E212C8u; - if (uint32_t *busy = reinterpret_cast(getMemPtr(rdram, kSndBusyFlagAddr))) - { - *busy = 0u; - } - } - } - - if (!callbackInvoked) - { - uint32_t semaId = static_cast(client->hdr.sema_id); - if (semaId == 0xFFFFFFFFu || semaId == 0u) - { - semaId = endParam; - } - const bool fallbackSignaledSema = signalRpcCompletionSema(semaId); - - static uint32_t unresolvedEndFuncWarnCount = 0; - if (unresolvedEndFuncWarnCount < 32u) - { - std::cerr << "[SifCallRpc] unresolved end callback endFunc=0x" << std::hex << endFunc - << " semaId=0x" << semaId - << " fallbackSignal=" << std::dec << (fallbackSignaledSema ? 1 : 0) - << std::endl; - ++unresolvedEndFuncWarnCount; - } - } - } - - static int logCount = 0; - if (logCount < 10) - { - std::cout << "[SifCallRpc] client=0x" << std::hex << clientPtr - << " sid=0x" << sid - << " rpcNum=0x" << rpcNum - << " mode=0x" << mode - << " sendBuf=0x" << sendBuf - << " recvBuf=0x" << recvBuf - << " recvSize=0x" << recvSize - << " size=" << std::dec << sendSize << std::endl; - ++logCount; - } - - { - std::lock_guard lock(g_rpc_mutex); - g_rpc_clients[clientPtr].busy = false; - } - - setReturnS32(ctx, 0); -} - -void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t sdPtr = getRegU32(ctx, 4); - uint32_t sid = getRegU32(ctx, 5); - uint32_t func = getRegU32(ctx, 6); - uint32_t buf = getRegU32(ctx, 7); - // stack args: cfunc, cbuf, qd... - uint32_t sp = getRegU32(ctx, 29); - uint32_t cfunc = 0; - uint32_t cbuf = 0; - uint32_t qd = 0; - readStackU32(rdram, sp, 0x10, cfunc); - readStackU32(rdram, sp, 0x14, cbuf); - readStackU32(rdram, sp, 0x18, qd); - - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - if (!sd) - { - setReturnS32(ctx, -1); - return; - } - - sd->sid = static_cast(sid); - sd->func = func; - sd->buf = buf; - sd->size = 0; - sd->cfunc = cfunc; - sd->cbuf = cbuf; - sd->size2 = 0; - sd->client = 0; - sd->pkt_addr = 0; - sd->rpc_number = 0; - sd->recvbuf = 0; - sd->rsize = 0; - sd->rmode = 0; - sd->rid = 0; - sd->base = qd; - sd->link = 0; - sd->next = 0; - - { - std::lock_guard lock(g_rpc_mutex); - - if (qd) - { - t_SifRpcDataQueue *queue = reinterpret_cast(getMemPtr(rdram, qd)); - if (queue) - { - if (!queue->link) - { - queue->link = sdPtr; - } - else - { - uint32_t curPtr = queue->link; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (!cur->link) - { - cur->link = sdPtr; - break; - } - if (cur->link == sdPtr) - break; - curPtr = cur->link; - } - } - } - } - - g_rpc_servers[sid] = {sid, sdPtr}; - for (auto &entry : g_rpc_clients) - { - if (entry.second.sid == sid) - { - t_SifRpcClientData *cd = reinterpret_cast(getMemPtr(rdram, entry.first)); - if (cd) - { - cd->server = sdPtr; - cd->buf = sd->buf; - cd->cbuf = sd->cbuf; - } - } - } - } - - std::cout << "[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec << std::endl; - setReturnS32(ctx, 0); -} - -void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clientPtr = getRegU32(ctx, 4); - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it == g_rpc_clients.end()) - { - setReturnS32(ctx, 0); - return; - } - setReturnS32(ctx, it->second.busy ? 1 : 0); -} - -void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t qdPtr = getRegU32(ctx, 4); - int threadId = static_cast(getRegU32(ctx, 5)); - - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - if (!qd) - { - setReturnS32(ctx, -1); - return; - } - - qd->thread_id = threadId; - qd->active = 0; - qd->link = 0; - qd->start = 0; - qd->end = 0; - qd->next = 0; - - { - std::lock_guard lock(g_rpc_mutex); - if (!g_rpc_active_queue) - { - g_rpc_active_queue = qdPtr; - } - else - { - uint32_t curPtr = g_rpc_active_queue; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - if (curPtr == qdPtr) - break; - t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (!cur->next) - { - cur->next = qdPtr; - break; - } - curPtr = cur->next; - } - } - } - - setReturnS32(ctx, 0); -} - -void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t qdPtr = getRegU32(ctx, 4); - if (!qdPtr) - { - setReturnU32(ctx, 0); - return; - } - - std::lock_guard lock(g_rpc_mutex); - if (!g_rpc_active_queue) - { - setReturnU32(ctx, 0); - return; - } - - if (g_rpc_active_queue == qdPtr) - { - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - g_rpc_active_queue = qd ? qd->next : 0; - setReturnU32(ctx, qdPtr); - return; - } - - uint32_t curPtr = g_rpc_active_queue; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (cur->next == qdPtr) - { - t_SifRpcDataQueue *rem = reinterpret_cast(getMemPtr(rdram, qdPtr)); - cur->next = rem ? rem->next : 0; - setReturnU32(ctx, qdPtr); - return; - } - curPtr = cur->next; - } - - setReturnU32(ctx, 0); -} - -void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t sdPtr = getRegU32(ctx, 4); - uint32_t qdPtr = getRegU32(ctx, 5); - - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - if (!qd || !sdPtr) - { - setReturnU32(ctx, 0); - return; - } - - std::lock_guard lock(g_rpc_mutex); - - if (qd->link == sdPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - qd->link = sd ? sd->link : 0; - if (sd) - sd->link = 0; - setReturnU32(ctx, sdPtr); - return; - } - - uint32_t curPtr = qd->link; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (cur->link == sdPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - cur->link = sd ? sd->link : 0; - if (sd) - sd->link = 0; - setReturnU32(ctx, sdPtr); - return; - } - curPtr = cur->link; - } - - setReturnU32(ctx, 0); -} - -void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - SifCallRpc(rdram, ctx, runtime); -} - -void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t cid = getRegU32(ctx, 4); - uint32_t packetAddr = getRegU32(ctx, 5); - uint32_t packetSize = getRegU32(ctx, 6); - uint32_t srcExtra = getRegU32(ctx, 7); - - uint32_t sp = getRegU32(ctx, 29); - uint32_t destExtra = 0; - uint32_t sizeExtra = 0; - readStackU32(rdram, sp, 0x10, destExtra); - readStackU32(rdram, sp, 0x14, sizeExtra); - - if (sizeExtra > 0 && srcExtra && destExtra) - { - rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra); - } - - static int logCount = 0; - if (logCount < 5) - { - std::cout << "[sceSifSendCmd] cid=0x" << std::hex << cid - << " packet=0x" << packetAddr - << " psize=0x" << packetSize - << " extra=0x" << destExtra << std::dec << std::endl; - ++logCount; - } - - // Return non-zero on success. - setReturnS32(ctx, 1); -} - -void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t queuePtr = getRegU32(ctx, 4); - setReturnS32(ctx, static_cast(queuePtr)); -} diff --git a/ps2xRuntime/src/runner/games_database.cpp b/ps2xRuntime/src/runner/games_database.cpp deleted file mode 100644 index e6fd1ec3..00000000 --- a/ps2xRuntime/src/runner/games_database.cpp +++ /dev/null @@ -1,804 +0,0 @@ -#include "games_database.h" -#include - -static const std::unordered_map gameDatabase = -{ - { "SLUS-20267", "hack Part 1 - Infection (USA)" }, - { "SLKA-25080", ".hack Vol. 1 - Infection (Korea)" }, - { "SLPS-25143", ".hack Vol. 2 - Mutation (Japan)" }, - { "SLPS-25158", ".hack Vol. 3 - Erosion Pollution (Japan)" }, - { "SLUS-20579", "007 - NightFire (USA)" }, - { "SLES-51258", "007 - Nightfire (Europe)" }, - { "SLES-51260", "007 - Nightfire (Europe)" }, - { "SLES-50214", "18 Wheeler - American Pro Trucker (Europe)" }, - { "SLUS-20210", "18 Wheeler - American Pro Trucker (USA)" }, - { "SLPS-25118", "2002 FIFA World Cup (Japan)" }, - { "SLUS-20404", "2002 FIFA World Cup (USA)" }, - { "SLES-50796", "2002 FIFA World Cup Korea Japan (Europe)" }, - { "SLES-50798", "2002 FIFA World Cup Korea Japan (Germany)" }, - { "SLES-50799", "2002 FIFA World Cup Korea Japan (Italy)" }, - { "SLES-50800", "2002 FIFA World Cup Korea Japan (Spain)" }, - { "SLPS-20214", "3D Fighting School 2 (Japan)" }, - { "SLUS-20091", "4x4 Evo (USA)" }, - { "SCES-50293", "ATV Offroad - All Terrain Vehicle (Europe)" }, - { "SCUS-97104", "ATV Offroad Fury (USA) (v1.00)" }, - { "SCUS-97104", "ATV Offroad Fury (USA) (v3.01)" }, - { "SLUS-20588", "Activision Anthology (USA)" }, - { "SLPM-65150", "Aero Dancing 4 - New Generation (Japan)" }, - { "SLUS-20614", "Aero Elite - Combat Academy (USA)" }, - { "SLPS-25224", "Ai yori Aoshi Limited Edition (Japan)" }, - { "SLES-50953", "Air Ranger - Rescue Helicopter (Europe)" }, - { "SLPM-65486", "AirForce Delta - Blue Wing Knights (Japan)" }, - { "SLUS-20703", "AirForce Delta Strike (USA)" }, - { "SLES-50919", "Akira Psycho Ball (Europe)" }, - { "SLPS-20150", "Akira Psycho Ball (Japan)" }, - { "SLES-50429", "Alex Ferguson’s Player Manager 2001 (Europe)" }, - { "SLES-51792", "Aliens Versus Predator - Extinction (Europe)" }, - { "SLUS-20147", "Aliens vs Predator - Extinction (USA)" }, - { "SLPS-20181", "Alpine Racer 3 (Japan)" }, - { "SLUS-21069", "American Chopper (USA)" }, - { "SLPM-65513", "Angel’s Feather (Japan)" }, - { "SLPM-65027", "Anime Eikaiwa - 15 Shounen Hyouryuuki - Hitomi no Naka no Shounen (Japan)" }, - { "SLPM-65029", "Anime Eikaiwa - Tondemo Nezumi Daikatsuyaku (Japan)" }, - { "SLPM-65028", "Anime Eikaiwa - Tottoi (Japan)" }, - { "SLUS-20217", "Arctic Thunder (USA)" }, - { "SLES-50191", "Army Men - Green Rogue (Europe)" }, - { "SLUS-20087", "Army Men - Green Rogue (USA)" }, - { "SLPM-62501", "Assault Suits Valken (Japan)" }, - { "SLES-51896", "Attheraces Presents Gallop Racer (Europe)" }, - { "SLES-51191", "Auto Modellista (Europe)" }, - { "SLUS-20498", "Auto Modellista (USA) (Volume 1.0) (Beta)" }, - { "SLUS-28031", "Auto Modellista (USA) (Volume 2.0) (Beta)" }, - { "SLUS-20642", "Auto Modellista (USA)" }, - { "SLPS-25140", "Baldur’s Gate - Dark Alliance (Japan)" }, - { "SLPM-62155", "Baseball 2002, The - Battle Ball Park Sengen (Japan)" }, - { "SLPM-65180", "Baseball 2003, The - Battle Ball Park Sengen - Perfect Play Pro Yakyuu (Japan) (v1.05)" }, - { "SLES-51756", "Batman - Rise of Sin Tzu (Europe)" }, - { "SLUS-20709", "Batman - Rise of Sin Tzu (USA)" }, - { "SLES-50355", "Batman - Vengeance (Europe)" }, - { "SLUS-20226", "Batman - Vengeance (USA)" }, - { "SLPM-62052", "Beatmania Da Da Da!! (Japan)" }, - { "SCPS-11004", "Bikkuri Mouse (Japan)" }, - { "SLPM-65059", "Biohazard - Gun Survivor 2 - CODE - Veronica (Japan)" }, - { "SLPS-20187", "Black-Matrix II (Japan)" }, - { "SLES-51013", "Blade II (Europe)" }, - { "SLUS-20360", "Blade II (USA)" }, - { "SLUS-20862", "BloodRayne 2 (USA)" }, - { "SLPM-65262", "Boboboubo Boubobo - Hajike Matsuri (Japan)" }, - { "SLUS-20499", "Breath of Fire - Dragon Quarter (USA)" }, - { "SLPM-65196", "Breath of Fire V - Dragon Quarter (Japan)" }, - { "SLPM-66410", "Brothers in Arms - Meiyo no Daishou (Japan)" }, - { "SLUS-20895", "Bujingai - The Forsaken City (USA)" }, - { "?", "Burnout Dominator SLAJ-25094 SLPM-66739 SLUS-21596 SLES-54627 SLES-54681" }, - { "SLUS-20141", "CART Fury - Championship Racing (USA)" }, - { "SLES-50541", "Capcom vs. SNK 2 - Mark of the Millennium 2001 (Europe)" }, - { "SLUS-20246", "Capcom vs. SNK 2 - Mark of the Millennium 2001 (USA)" }, - { "SLPM-62365", "Cardinal Arc - Konton no Fuusatsu (Japan)" }, - { "SLES-52143", "Carmen Sandiego - The Secret of the Stolen Drums (Europe) (En,Fr,De,Es)" }, - { "SLUS-20849", "Carmen Sandiego - The Secret of the Stolen Drums (USA)" }, - { "SLES-50636", "Centre Court - Hard Hitter (Europe)" }, - { "SLPM-65255", "Chobits - Chii dake no Hito (Japan)" }, - { "SLPS-25015", "Choro Q - High Grade (Japan)" }, - { "SLPS-25014", "Choro Q - High Grade Limited Edition (Japan)" }, - { "SLPS-25073", "Cinema Surfing - Youga Taizen (Japan)" }, - { "SLES-50935", "Circus Maximus - Chariot Wars (Europe)" }, - { "SLES-51619", "Clock Tower 3 (Europe)" }, - { "SLUS-20633", "Clock Tower 3 (USA)" }, - { "SLPS-20056", "Colorio Hagaki Print (Japan)" }, - { "SCUS-97108", "Cool Boarders 2001 (USA)" }, - { "SLUS-20238", "Crash Bandicoot - The Wrath of Cortex (USA) (v1.00)" }, - { "SLES-50215", "Crazy Taxi (Europe)" }, - { "SLUS-20202", "Crazy Taxi (USA)" }, - { "SLPM-65368", "D.N.Angel - TV Animation Series (Japan)" }, - { "PSXC-00203", "DESR-7000-DESR-5000-DESR-7100-DESR-5100 Senyou - PSX Update Disc Ver. 1.31 (Japan)" }, - { "SCES-51190", "Dark Chronicle (Europe)" }, - { "PAPX-90506", "Dark Chronicle (Japan) (Demo)" }, - { "SCES-50295", "Dark Cloud (Europe)" }, - { "SCPS-15004", "Dark Cloud (Japan)" }, - { "SCUS-97111", "Dark Cloud (USA)" }, - { "SCUS-97213", "Dark Cloud 2 (USA) (v2.00)" }, - { "SLES-52874", "Dark Wind (Europe)" }, - { "SLPM-65303", "Dennou Senki - Virtual-On Marz (Japan)" }, - { "SLPS-25321", "Derby Stallion 04 (Japan)" }, - { "SLED-50359", "Devil May Cry (Europe) (Demo)" }, - { "SLPM-61010", "Devil May Cry (Japan) (Demo)" }, - { "SLPM-65023", "Devil May Cry (Japan) (Demo)" }, - { "SLPM-65038", "Devil May Cry (Japan)" }, - { "SLUS-20216", "Devil May Cry (USA)" }, - { "SLES-51347", "Die Hard - Vendetta (Europe)" }, - { "SLES-51348", "Die Hard - Vendetta (Germany)" }, - { "SLES-51095", "Dino Stalker (France)" }, - { "SLES-51096", "Dino Stalker (Germany)" }, - { "SLUS-20485", "Dino Stalker (USA)" }, - { "SLES-55392", "Disney Sing It (Europe)" }, - { "SLES-55542", "Disney Sing It - Pop Hits (Europe)" }, - { "SLES-50042", "Disney’s Dinosaur (Europe)" }, - { "SLES-50043", "Disney’s Dinosaur (Europe)" }, - { "SLES-50048", "Disney’s Donald Duck - Quack Attack (Europe)" }, - { "SLES-50045", "Disney’s Jungle Book - Groove Party (Europe)" }, - { "SCES-50522", "Disney’s Peter Pan - The Legend of Never Land (Europe)" }, - { "SCES-50531", "Disney’s Peter Pan - The Legend of Never Land (Scandinavia)" }, - { "SLES-50350", "Disney’s Tarzan - Freeride (Europe)" }, - { "SCES-51176", "Disney’s Treasure Planet (Europe)" }, - { "SCUS-97146", "Disney’s Treasure Planet (USA)" }, - { "SCES-50600", "Disney-Pixar Die Monster AG - Schreckens-Insel (Germany)" }, - { "SCES-50597", "Disney-Pixar Monsters en Co. - Schrik Eiland (Netherlands)" }, - { "SCES-50595", "Disney-Pixar Monsters, Inc. - Scare Island (Europe)" }, - { "SCES-50604", "Disney-Pixar Monsters, Inc. - Skraemmaroen (Sweden)" }, - { "SCES-50603", "Disney-Pixar Monstruos, S.A. - Isla de los Sustos (Spain)" }, - { "SLPM-65703", "Double Reaction! Plus (Japan)" }, - { "SCPS-56010", "Downhill Racer (Korea)" }, - { "SLPM-62199", "Dragon Quest Characters - Torneko no Daibouken 3 (Japan)" }, - { "SLPM-62490", "Dragon Quest VIII Premium Disc (Japan)" }, - { "SLPS-20016", "Dream Audition (Japan)" }, - { "SLPS-20099", "Dream Audition 3 (Japan)" }, - { "SLPS-20140", "Dream Audition Super Hit Disc 1 (Japan)" }, - { "SLPS-20141", "Dream Audition Super Hit Disc 2 (Japan)" }, - { "SLUS-20239", "Driven (USA)" }, - { "SLUS-20113", "Driving Emotion Type-S (USA)" }, - { "SLES-51303", "Drome Racers (Europe)" }, - { "SLUS-20475", "Dual Hearts (USA)" }, - { "SLES-50057", "Dynasty Warriors 2 (Europe)" }, - { "SLES-50058", "Dynasty Warriors 2 (France)" }, - { "SLPM-69004", "EGBrowser Light for I-O Data Device, Inc. (Japan)" }, - { "SLES-50036", "ESPN International Track & Field (Europe)" }, - { "SLUS-20041", "ESPN International Track & Field (USA)" }, - { "SLUS-20320", "ESPN International Winter Sports 2002 (USA)" }, - { "SLUS-20128", "ESPN MLS ExtraTime (USA)" }, - { "SLUS-20089", "ESPN Winter X Games Snowboarding (USA)" }, - { "SLPM-62103", "EX Okuman Chouja Game - The Money Battle (Japan)" }, - { "SLUS-20169", "Ephemeral Fantasia (USA)" }, - { "SLES-51813", "European Tennis Pro (Europe)" }, - { "SCED-51728", "EverQuest - Online Adventures (Europe) (Demo)" }, - { "SLES-51392", "Evolution Snowboarding (Europe)" }, - { "SLUS-20546", "Evolution Snowboarding (USA)" }, - { "SLPS-25326", "Exciting Pro Wres 5 (Japan) (Limited Edition)" }, - { "SLPS-25083", "Exciting Pro Wrestling 3 (Japan) (Limited Edition)" }, - { "SLPS-25087", "Exciting Pro Wrestling 3 (Japan)" }, - { "SLPS-20223", "Exciting Pro Wrestling 4 (Japan) (Demo)" }, - { "SLPS-25210", "Exciting Pro Wrestling 4 (Japan)" }, - { "SLPS-25326", "Exciting Pro Wrestling 5 (Japan)" }, - { "SCES-51513", "EyeToy - Play (Europe, Australia)" }, - { "SCUS-97319", "EyeToy - Play (USA)" }, - { "SLES-50011", "FIFA 2001 (Europe)" }, - { "SLES-50012", "FIFA 2001 (France)" }, - { "SLES-50013", "FIFA 2001 (Germany)" }, - { "SLES-50015", "FIFA 2001 (Italy)" }, - { "SLES-50016", "FIFA 2001 (Spain)" }, - { "SLUS-20097", "FIFA 2001 (USA)" }, - { "SLPS-20054", "FIFA 2001 - World Championship (Japan)" }, - { "SLPS-25069", "FIFA 2002 - Road to FIFA World Cup (Japan)" }, - { "SLPS-25179", "FIFA 2003 - Europe Soccer (Japan)" }, - { "SLES-50464", "FIFA Football 2002 (Europe)" }, - { "SLES-50466", "FIFA Football 2002 (France)" }, - { "SLES-50467", "FIFA Football 2002 (Germany)" }, - { "SLES-50470", "FIFA Football 2002 (Italy)" }, - { "SLPM-67503", "FIFA Football 2002 (Korea)" }, - { "SLES-50471", "FIFA Football 2002 (Spain)" }, - { "SLES-51197", "FIFA Football 2003 (Europe)" }, - { "SLUS-20280", "FIFA Soccer 2002 (USA)" }, - { "SLUS-20580", "FIFA Soccer 2003 (USA)" }, - { "SLPS-20020", "FIFA Soccer World Championship (Japan)" }, - { "SLPS-25236", "Fantastic Fortune 2 (Japan)" }, - { "SLUS-20388", "Fatal Frame (USA)" }, - { "SLUS-20766", "Fatal Frame II - Crimson Butterfly (USA)" }, - { "SLPS-20298", "Fever 8 - Sankyo Koushiki Pachinko Simulation (Japan)" }, - { "SLUS-20524", "Fighter Maker 2 (USA)" }, - { "SLPM-62135", "Final Fantasy XI - Online (Japan) (Beta)" }, - { "SLPS-25200", "Final Fantasy XI - Online (Japan)" }, - { "SCUS-97271", "Final Fantasy XI - Online (USA) (Beta)" }, - { "SCUS-97266", "Final Fantasy XI - Online (USA)" }, - { "SLPM-65288", "Final Fantasy XI - Zilart no Gen’ei (Japan) (All in One Pack 2003)" }, - { "SLPM-65287", "Final Fantasy XI - Zilart no Gen’ei (Japan)" }, - { "SLES-51418", "Fisherman’s Challenge (Europe)" }, - { "SLUS-20553", "Fisherman’s Challenge (USA)" }, - { "SLES-50259", "Flintstones in Viva Rock Vegas, The (Europe)" }, - { "SLPS-25034", "Flower, Sun and Rain (Japan)" }, - { "SLED-52852", "Forgotten Realms - Demon Stone (Europe) (Demo)" }, - { "SLUS-29061", "Freaky Flyers (USA) (Demo)" }, - { "SLUS-20658", "Freedom Fighters (USA)" }, - { "SLES-50720", "Freestyle Metal X (Europe)" }, - { "SLUS-20494", "Freestyle Metal X (USA)" }, - { "SLES-50788", "Frogger - The Great Quest (Europe)" }, - { "SLUS-20257", "Frogger - The Great Quest (USA)" }, - { "SLPM-60102", "From Software First Previews (Japan)" }, - { "SLUS-20785", "Funkmaster Flex - Digital Hitz Factory (USA)" }, - { "SLUS-20859", "Future Tactics - The Uprising (USA)" }, - { "SLKA-25139", "Fuuun Shinsengumi (Korea)" }, - { "SCED-52094", "G-Con 2 Competition Demo (Germany)" }, - { "SLES-50584", "G1 Jockey (Europe)" }, - { "SLES-51357", "G1 Jockey 3 (Europe)" }, - { "SLUS-20690", "G1 Jockey 3 (USA)" }, - { "SLPM-62020", "GI Jockey 2 (Japan)" }, - { "SLPM-62059", "GI Jockey 2 2001 (Japan) (Super Value Set)" }, - { "SLPM-62061", "GI Jockey 2 2001 (Japan)" }, - { "SLPM-62279", "GI Jockey 3 (Japan) (Premium Pack)" }, - { "SLPM-62277", "GI Jockey 3 (Japan)" }, - { "SLES-50472", "GTC Africa (Europe)" }, - { "SLES-52845", "Gadget & the Gadgetinis (Europe)" }, - { "SLUS-20225", "Gadget Racers (USA)" }, - { "SLPS-25333", "Gallop Racer - Lucky 7 (Japan)" }, - { "SLUS-20255", "Gallop Racer 2001 (USA)" }, - { "SLUS-20662", "Gallop Racer 2003 - A New Breed (USA)" }, - { "SLUS-21031", "Gallop Racer 2004 (USA)" }, - { "SLPS-25036", "Gallop Racer 5 (Japan)" }, - { "SLPS-73415", "Gallop Racer 6 - Revolution (Japan) (PlayStation 2 the Best)" }, - { "SLPS-25177", "Gallop Racer 6 - Revolution (Japan)" }, - { "SLPM-62009", "Ganbare! Nippon! Olympic 2000 (Japan)" }, - { "SLES-50211", "Gauntlet - Dark Legacy (Europe)" }, - { "SLUS-20047", "Gauntlet - Dark Legacy (USA)" }, - { "SLPM-62235", "Get Bass Battle (Japan)" }, - { "?", "Ghost Master - The Gravenville Chronicles (2003 beta) [Emuparadise]" }, - { "SLPS-20052", "Global Folktale (Japan)" }, - { "SLUS-20395", "Global Touring Challenge - Africa (USA)" }, - { "SLES-52117", "Go Go Copter - Remote Control Helicopter (Europe)" }, - { "SLES-51055", "Go Go Golf (Europe)" }, - { "SCED-54680", "God of War II (Europe) (Demo)" }, - { "SLES-50433", "Godai - Elemental Force (Europe)" }, - { "SLUS-20288", "Godai - Elemental Force (USA)" }, - { "SLPM-60107", "Golf Paradise (Japan) (Demo)" }, - { "SLPS-20009", "Golf Paradise (Japan)" }, - { "SLES-51296", "Grand Prix Challenge (Europe)" }, - { "SLES-50793", "Grand Theft Auto III (Australia)" }, - { "SLES-50330", "Grand Theft Auto III (Europe) (v1.40)" }, - { "SLES-50330", "Grand Theft Auto III (Europe) (v1.60)" }, - { "SLUS-20062", "Grand Theft Auto III (USA)" }, - { "SLUS-20466", "Gravenville Ghost Master Chronicles" }, - { "SLUS-20310", "Gravity Games Bike - Street. Vert. Dirt. (USA)" }, - { "SCES-50246", "Gravity Sucks (Europe, Australia)" }, - { "SLES-51999", "Grooverider (Europe)" }, - { "SLPS-20106", "Growlanser II - The Sense of Justice (Japan)" }, - { "SLKA-15007", "Growlanser II - The Sense of Justice (Korea)" }, - { "SLPM-62108", "Growlanser III - The Dual Darkness (Japan)" }, - { "SLPM-65383", "Growlanser IV - Wayfarer of the Time (Japan) (Deluxe Pack)" }, - { "SLPM-65408", "Growlanser IV - Wayfarer of the Time (Japan)" }, - { "SLPM-65139", "Gun Survivor 3 - Dino Crisis (Japan)" }, - { "SLES-52620", "Guncom 2 (Europe)" }, - { "SLPM-65153", "Gungrave (Japan)" }, - { "SLUS-20493", "Gungrave (USA)" }, - { "SLUS-21020", "Gungrave - Overdose (USA)" }, - { "SLPM-65492", "Gungrave OD (Japan)" }, - { "SLES-50559", "Guy Roux Manager 2002 (France)" }, - { "SLPM-62273", "Haishin 3 (Japan)" }, - { "SLPS-20098", "Hard Hitter (Japan)" }, - { "SLES-51057", "Hard Hitter 2 (Europe)" }, - { "SLPS-20173", "Hard Hitter 2 (Japan)" }, - { "SLUS-20568", "Hard Hitter Tennis (USA)" }, - { "SLES-51254", "Herr der Ringe, Der - Die zwei Tuerme (Germany)" }, - { "SLES-50260", "Hidden Invasion (Europe)" }, - { "SLUS-20301", "Hidden Invasion (USA)" }, - { "SLPS-25111", "Higanbana (Japan)" }, - { "SLPS-20213", "Hissatsu Pachinko Station V4 - Drumtic Mahjong (Japan)" }, - { "SLES-53028", "Hitman - Blood Money (Europe)" }, - { "SLPS-25269", "Hitman 2 - Silent Assassin (Japan)" }, - { "SLUS-20374", "Hitman 2 - Silent Assassin (USA) (v1.01)" }, - { "SLPM-62072", "Horse Breaker (Japan)" }, - { "SLES-51063", "Hot Wheels - Velocity X - Maximum Justice (Europe)" }, - { "SLUS-20412", "Hot Wheels - Velocity X - Maximum Justice (USA)" }, - { "SLPM-65083", "Houshin Engi 2 (Japan)" }, - { "SLES-52102", "Hugo - Bukkazoom! (Europe)" }, - { "SLPM-62067", "Hunter x Hunter - Ryumyaku no Saidan (Japan)" }, - { "SLES-50266", "Hype - The Time Quest (Europe)" }, - { "SLES-50265", "Hype - The Time Quest (Germany)" }, - { "SLPM-65405", "Hyper Dimension Fortress Macross (Japan)" }, - { "SLPM-62126", "Hyper Sports 2002 Winter (Japan)" }, - { "SLUS-20586", "IHRA Drag Racing 2 (USA)" }, - { "SCES-50760", "Ico (Europe)" }, - { "SLPS-25182", "Idol Janshi R - Jan Guru Project (Japan)" }, - { "SLES-51255", "Il Signore degli Anelli - Le Due Torri (Italy)" }, - { "SLES-51397", "IndyCar Series (Europe)" }, - { "SLUS-20641", "IndyCar Series featuring The Indianapolis 500 (USA)" }, - { "SLUS-20830", "Intellivision Lives! (USA)" }, - { "SLES-51629", "International Pool Championship (Europe)" }, - { "SLES-50039", "International Superstar Soccer (Europe)" }, - { "SLPM-62075", "International Superstar Soccer 2 (Europe) (Beta)" }, - { "SLUS-20913", "Inuyasha - The Secret of the Cursed Mask (USA)" }, - { "SLPM-65530", "J. League Pro Soccer Club o Tsukurou! ‘04 (Japan)" }, - { "SLPM-62217", "J. League Winning Eleven 6 (Japan)" }, - { "SLES-50735", "Jade Cocoon 2 (Europe)" }, - { "SCED-52952", "Jak 3 (Europe) (Demo)" }, - { "SCKA-20010", "Jak II (Korea) (En,Ja,Fr,De,Es,It,Ko)" }, - { "SCUS-97273", "Jak II (USA) (Demo)" }, - { "SCUS-97265", "Jak II (USA) (En,Ja,Fr,De,Es,It,Ko) (v1.00)" }, - { "SCUS-97265", "Jak II (USA) (En,Ja,Fr,De,Es,It,Ko) (v2.01)" }, - { "SCPS-15057", "Jak II - Jak x Daxter 2 (Japan)" }, - { "SCED-51700", "Jak II - Renegade (Europe) (Demo)" }, - { "SCES-51608", "Jak II Renegade (Europe) (Preview)" }, - { "SCES-50361", "Jak and Daxter - The Precursor Legacy (Europe)" }, - { "SCUS-97124", "Jak and Daxter - The Precursor Legacy (USA) (Cingular Wireless Demo)" }, - { "SCUS-97124", "Jak and Daxter - The Precursor Legacy (USA) (En,Fr,De,Es,It) (Rev 1)" }, - { "SCUS-97124", "Jak and Daxter - The Precursor Legacy (USA) (En,Fr,De,Es,It)" }, - { "PAPX-90222", "Jak x Daxter - Kyuu Sekai no Isan (Japan) (Demo)" }, - { "SCPS-15021", "Jak x Daxter - Kyuu Sekai no Isan (Japan)" }, - { "SLES-50209", "Jeremy McGrath Supercross World (Europe)" }, - { "SLUS-20245", "Jeremy McGrath Supercross World (USA)" }, - { "SCUS-97239", "Jet X2O (USA) (Demo)" }, - { "SCUS-97173", "Jet X2O (USA)" }, - { "SLPM-62011", "Jikkyou GI Stable (Japan)" }, - { "SLPM-62075", "Jikkyou World Soccer 2001 (Japan)" }, - { "SLPM-65140", "Jojo no Kimyou na Bouken - Ougon no Kaze (Japan)" }, - { "SLPM-65336", "K-1 World Grand Prix - The Beast Attack! (Japan)" }, - { "SLPM-65075", "K-1 World Grand Prix 2001 (Japan)" }, - { "SLPM-65202", "K-1 World Grand Prix 2002 (Japan)" }, - { "SLPM-65433", "K-1 World Grand Prix 2003 (Japan)" }, - { "SLPS-25386", "KOF - Maximum Impact (Japan)" }, - { "SLUS-20923", "KOF - Maximum Impact (USA)" }, - { "SCPS-11009", "Ka (Japan)" }, - { "SCPS-15045", "Ka 2 - Let’s Go Hawaii (Japan)" }, - { "SLPM-62383", "Karaoke Revolution - Night Selection 2003 (Japan)" }, - { "SLPM-62528", "Karaoke Revolution Family Pack (Japan)" }, - { "SLES-52308", "Karaoke Stage (Europe)" }, - { "SLES-51200", "Kelly Slater’s Pro Surfer (Europe)" }, - { "SLES-51201", "Kelly Slater’s Pro Surfer (Europe)" }, - { "SLUS-20334", "Kelly Slater’s Pro Surfer (USA)" }, - { "SLES-50114", "Kengo - Master of Bushido (Europe)" }, - { "SLUS-20021", "Kengo - Master of Bushido (USA)" }, - { "SLPM-60177", "Kengou 2 (Japan) (Taikenban)" }, - { "SLPS-25107", "Kengou 2 (Japan)" }, - { "SLPS-25020", "Kidou Senshi Gundam (Japan)" }, - { "SLPS-25120", "Kidou Senshi Gundam - Gihren no Yabou - Zeon Dokuritsu Sensouki (Japan)" }, - { "SLPS-25212", "Kidou Senshi Gundam - Gihren no Yabou - Zeon Dokuritsu Sensouki - Kouryaku Shireisho (Japan)" }, - { "SLPM-65076", "Kidou Senshi Gundam - Renpou vs. Zeon DX (Japan)" }, - { "SLPS-25061", "Kidou Senshi Gundam - Ver. 1.5 (Japan)" }, - { "SLPS-25389", "Kidou Senshi Gundam Seed - Owaranai Ashita e (Japan)" }, - { "SLPS-25123", "Kidou Senshi Gundam Senki - Lost War Chronicles (Japan)" }, - { "SLPM-65033", "Kikou Heidan J-Phoenix (Japan)" }, - { "SLPM-65123", "Kikou Heidan J-Phoenix - Burst Tactics (Japan)" }, - { "SLPM-65199", "Kikou Heidan J-Phoenix - Cobalt Shoutai-hen (Japan)" }, - { "SLPS-20075", "Kikou Heidan J-Phoenix - Joshou-hen (Japan)" }, - { "SLPM-65343", "Kikou Heidan J-Phoenix 2 (Japan)" }, - { "SLUS-20834", "King of Fighters 2000, The (USA)" }, - { "SLPS-25266", "King of Fighters 2001, The (Japan)" }, - { "SLUS-20839", "King of Fighters 2001, The (USA)" }, - { "?", "Kingdom Hearts - Re Chain of Memories (Preview)" }, - { "SLPS-25248", "Kino no Tabi - The Beautiful World (Japan)" }, - { "SLPM-65491", "Kishin Houkou Demonbane (Japan)" }, - { "SLPM-65404", "Kita e. - Diamond Dust (Japan)" }, - { "SLPM-65569", "Kita e. - Diamond Dust+ - Kiss is Beginning. (Japan)" }, - { "SLES-50128", "Knockout Kings 2001 (Europe)" }, - { "SLES-50129", "Knockout Kings 2001 (France)" }, - { "SLES-50130", "Knockout Kings 2001 (Germany)" }, - { "SLUS-20150", "Knockout Kings 2001 (USA)" }, - { "SLPM-65554", "Korokke! Ban Ou no Kiki o Sukue (Japan)" }, - { "SLPM-65447", "Kunoichi (Japan)" }, - { "SLPS-25136", "Kuon no Kizuna - Sairinshou (Japan)" }, - { "SLPM-60127", "Kuri Kuri Mix (Japan) (Taikenban)" }, - { "SLES-50443", "LEGO Racers 2 (Europe)" }, - { "SLUS-20042", "LEGO Racers 2 (USA)" }, - { "SLPS-20165", "La Pucelle - Hikari no Seijo Densetsu (Japan)" }, - { "SLES-50709", "Le Maillon Faible (France)" }, - { "SLES-50131", "Le Mans 24 Hours (Europe)" }, - { "SLUS-20207", "Le Mans 24 Hours (USA) (En,Fr,Es)" }, - { "SLES-51415", "Legacy of Kain Defiance" }, - { "SLUS-20045", "Legend of Alon D’ar, The (USA)" }, - { "SLES-51045", "Legends of Wrestling II (Europe)" }, - { "SLUS-20507", "Legends of Wrestling II (USA)" }, - { "SLES-50892", "Lethal Skies - Elite Pilot - Team SW (Europe)" }, - { "SLUS-20386", "Lethal Skies - Elite Pilot - Team SW (USA)" }, - { "SLES-51886", "Lethal Skies II (Europe)" }, - { "SLUS-20735", "Lethal Skies II (USA)" }, - { "SLPS-29004", "Lord of the Rings, The - Futatsu no Tou (Japan)" }, - { "SLPM-65212", "Lord of the Rings, The - The Two Towers (Asia) (En,Zh)" }, - { "SLES-51252", "Lord of the Rings, The - The Two Towers (Europe)" }, - { "SLPM-67546", "Lord of the Rings, The - The Two Towers (Korea)" }, - { "SLUS-20578", "Lord of the Rings, The - The Two Towers (USA)" }, - { "SLES-50230", "Lotus Challenge (Europe)" }, - { "SLPM-60101", "Love Story (Japan) (Demo)" }, - { "SLPS-20245", "LowRider - Round the World (Japan)" }, - { "SLES-50248", "MDK2 - Armageddon (Europe)" }, - { "SLUS-20105", "MDK2 - Armageddon (USA)" }, - { "SLES-50182", "MTV Music Generator 2 (Europe)" }, - { "SLUS-20222", "MTV Music Generator 2 (USA)" }, - { "SLES-50428", "MX 2002 featuring Ricky Carmichael (Europe)" }, - { "SLUS-20072", "MX 2002 featuring Ricky Carmichael (USA)" }, - { "SLES-50132", "MX Rider (Europe)" }, - { "SLUS-20234", "MX Rider (USA)" }, - { "SLES-51038", "MX SuperFly (Europe)" }, - { "SLUS-20381", "MX SuperFly (USA)" }, - { "SLES-51653", "Mace Griffin - Bounty Hunter (Europe)" }, - { "SLES-51654", "Mace Griffin - Bounty Hunter (Germany)" }, - { "SLUS-20505", "Mace Griffin - Bounty Hunter (USA)" }, - { "SLPM-62077", "Maestromusic II, The (Japan) (Doukonban)" }, - { "SLPM-62078", "Maestromusic II, The (Japan)" }, - { "SLUS-20671", "Mafia (USA)" }, - { "SLPS-20037", "Magical Sports Go Go Golf (Japan)" }, - { "SLPS-20310", "Mahjong Hiryuu Densetsu - Tenpai (Japan)" }, - { "SLPM-65367", "Makai Eiyuuki Maximo - Machine Monster no Yabou (Japan)" }, - { "SLPS-25042", "Maken Shao (Japan)" }, - { "SLES-51058", "Maken Shao - Demon Sword (Europe)" }, - { "SLUS-20358", "Malice (USA)" }, - { "SCED-51406", "Mark of Kri, The (Europe) (Demo)" }, - { "SCES-51164", "Mark of Kri, The (Europe)" }, - { "SCUS-97140", "Mark of Kri, The (USA)" }, - { "SLUS-20722", "Maximo vs Army of Zin (USA)" }, - { "SCPS-11014", "McDonald’s Original Happy Disc (Japan)" }, - { "SLPS-20031", "MechSmith, The - Run=Dim (Japan)" }, - { "SLES-51873", "Medal of Honor - Rising Sun (Europe, Australia)" }, - { "SLES-51875", "Medal of Honor - Rising Sun (Germany)" }, - { "SLPM-65469", "Medal of Honor - Rising Sun (Japan)" }, - { "SLES-51876", "Medal of Honor - Rising Sun (Spain)" }, - { "SLUS-20753", "Medal of Honor - Rising Sun (USA)" }, - { "SLES-51874", "Medal of Honor - Soleil Levant (France)" }, - { "SLES-50903", "MegaRace 3 - Nanotech Disaster (Europe)" }, - { "SLPM-67535", "Memories Off (Korea) (Ja,Ko)" }, - { "SLES-50789", "Men in Black II - Alien Escape (Europe)" }, - { "SLUS-20373", "Men in Black II - Alien Escape (USA)" }, - { "SLES-52599", "Metal Slug 3 (Europe)" }, - { "SLPS-25209", "Metal Slug 3 (Japan)" }, - { "SLES-53383", "Metal Slug 5 (Europe)" }, - { "SLPM-65480", "Michigan (Japan)" }, - { "SLES-52001", "Mission - Impossible - Operation Surma (Europe)" }, - { "SLUS-20400", "Mission - Impossible - Operation Surma (USA)" }, - { "SLES-51271", "Mobile Suit Gundam - Federation vs. Zeon (Europe)" }, - { "SLUS-20382", "Mobile Suit Gundam - Federation vs. Zeon (USA)" }, - { "SLUS-20175", "Mobile Suit Gundam - Journey to Jaburo (USA)" }, - { "SLUS-20741", "Mojo! (USA)" }, - { "SLPS-20381", "Monkey Turn V (Japan)" }, - { "SCPS-12345", "Monster House (Europe)" }, - { "SCPS-12345", "Monster House (Italy)" }, - { "SLPM-65495", "Monster Hunter (Japan)" }, - { "SLES-50908", "Monster Jam - Maximum Destruction (Europe)" }, - { "SLUS-20186", "Monster Jam - Maximum Destruction (USA)" }, - { "SLES-50717", "Mortal Kombat - Deadly Alliance (Europe, Australia)" }, - { "SLES-51439", "Mortal Kombat - Deadly Alliance (Germany)" }, - { "SLPS-25242", "Motion Gravure Series - Kitagawa Tomomi (Japan)" }, - { "SLES-51605", "Motorsiege - Warriors of Primetime (Europe)" }, - { "SLES-51363", "Music 3000 (Europe)" }, - { "SCUS-97263", "My Street (USA) (Demo)" }, - { "SCUS-97212", "My Street (USA)" }, - { "SLES-50726", "Myst III - Exile (Europe)" }, - { "SLUS-20434", "Myst III - Exile (USA)" }, - { "SLES-50080", "NBA Hoopz (Europe)" }, - { "SLUS-20050", "NBA Hoopz (USA)" }, - { "SCUS-97114", "NBA ShootOut 2001 (USA)" }, - { "SLES-50219", "NBA Street (Europe)" }, - { "SLUS-20187", "NBA Street (USA)" }, - { "SCUS-97109", "NCAA Final Four 2001 (USA)" }, - { "SCUS-97136", "NCAA Final Four 2002 (USA)" }, - { "SCUS-97204", "NCAA Final Four 2003 (USA)" }, - { "SCUS-97278", "NCAA Final Four 2004 (USA)" }, - { "SCUS-97107", "NCAA GameBreaker 2001 (USA)" }, - { "SCUS-97106", "NFL GameDay 2001 (USA)" }, - { "SLUS-20308", "NFL Prime Time 2002 (USA)" }, - { "SLES-50213", "NFL QB Club 2002 (Europe)" }, - { "SLUS-20154", "NFL QB Club 2002 (USA)" }, - { "SLES-51341", "NHL 2K3 (Europe)" }, - { "SLUS-20477", "NHL 2K3 (USA)" }, - { "SLES-50451", "NHL Hitz 2002 (Europe)" }, - { "SLUS-20140", "NHL Hitz 2002 (USA) (v2.00)" }, - { "SLES-50712", "NHL Hitz 2003 (Europe)" }, - { "SLUS-20438", "NHL Hitz 2003 (USA)" }, - { "SLUS-20691", "NHL Hitz Pro (USA)" }, - { "SLPS-25276", "Natsu Yume Ya Wa - The Tale of a Midsummer Night’s Dream (Japan)" }, - { "SLPS-25314", "Nebula - Echo Night (Japan)" }, - { "?", "Need for Speed Most Wanted" }, - { "SLUS-20537", "Nickelodeon Jimmy Neutron - Boy Genius (USA)" }, - { "SLUS-20473", "Nickelodeon Rocket Power - Beach Bandits (USA)" }, - { "SLUS-20810", "Nightshade (USA)" }, - { "SLPM-65130", "Nihon Daihyou Senshu ni Narou! (Japan)" }, - { "SLPM-62082", "Nihon Pro Yakyuu Kikou Kounin - Pro Yakyuu Japan 2001 (Japan)" }, - { "SLPS-25324", "Nishikaze no Kyoushikyoku - The Rhapsody of Zephyr (Japan)" }, - { "SLES-50232", "Off-Road - Wide Open (Europe)" }, - { "SLPM-65010", "Onimusha (Japan)" }, - { "SLES-51913", "Onimusha - Blade Warriors (Europe)" }, - { "SLES-50247", "Onimusha - Warlords (Europe)" }, - { "SLUS-20018", "Onimusha - Warlords (USA) (En,Ja)" }, - { "SCPS-15038", "Operator’s Side (Japan)" }, - { "SLPM-65524", "Orange Pocket - Root (Japan)" }, - { "SLPM-65005", "Ore ga Kantoku da! Gekitou Pennant Race (Japan)" }, - { "SCPS-15017", "PaRappa the Rapper 2 (Japan) (En,Ja)" }, - { "SCES-50888", "Pac-Man World 2 (Europe)" }, - { "SLPS-25141", "Pac-Man World 2 (Japan)" }, - { "SLUS-20224", "Pac-Man World 2 (USA) (v1.00)" }, - { "SLUS-20224", "Pac-Man World 2 (USA) (v2.00)" }, - { "SLPS-20186", "Pachinko de Asobou! Fever Dodeka Saurus (Japan)" }, - { "SLES-50212", "Paris-Dakar Rally (Europe)" }, - { "SLUS-20324", "Paris-Dakar Rally (USA)" }, - { "SLES-50252", "Penny Racers (Europe)" }, - { "SLPS-25222", "Pia Carrot e Youkoso!! 3 - Round Summer (Japan)" }, - { "SCPS-11014", "Piposaru 2001 (Japan)" }, - { "SLPM-65611", "Pizzicato Polka - Ensa Gen’ya (Japan)" }, - { "SCPS-15063", "PoPoLoCrois - Tsuki no Okite no Bouken (Japan)" }, - { "SLPS-20323", "Pochi to Nyaa (Japan)" }, - { "SCES-51135", "Primal" }, - { "SLES-50637", "Pro Rally 2002 (Europe)" }, - { "SLPM-65543", "Pro Yakyuu Spirits 2004 (Japan)" }, - { "SLPM-65721", "Pro Yakyuu Spirits 2004 Climax (Japan)" }, - { "SLPM-65426", "Pro Yakyuu Team o Tsukurou! 2003 (Japan)" }, - { "SLES-50821", "Project Zero (Europe)" }, - { "SLPM-66235", "Psychic Force Complete (Japan)" }, - { "SLPM-64534", "Psyvariar - Complete Edition (Korea)" }, - { "SLPM-65532", "Puyo Puyo Fever (Japan) (En,Ja,Fr,De,Es,It)" }, - { "SLES-50126", "Quake III - Revolution (Europe)" }, - { "SLES-50127", "Quake III - Revolution (Germany)" }, - { "SLUS-20167", "Quake III - Revolution (USA)" }, - { "SLPM-62424", "Quiz & Variety - Suku Suku Inufuku (Japan)" }, - { "SLES-50981", "R-C Sports Copter Challenge (Europe)" }, - { "SLES-50077", "RC Revenge Pro (Europe)" }, - { "SLUS-20153", "RC Revenge Pro (USA)" }, - { "SLUS-20340", "RPG Maker II (USA)" }, - { "SLPS-20143", "RPG Tkool 5 (Japan)" }, - { "SLES-51391", "RTL Skispringen 2003 (Germany)" }, - { "SLES-51633", "Racing Simulation 3 (Europe)" }, - { "SLPS-20307", "Rakushou! Pachi-Slot Sengen (Japan)" }, - { "SLES-50763", "Rally Championship (Europe)" }, - { "SLPS-20305", "Real Sports Pro Yakyuu (Japan)" }, - { "SLPM-65004", "Reiselied - Ephemeral Fantasia (Japan) (v1.00)" }, - { "SLPM-65004", "Reiselied - Ephemeral Fantasia (Japan) (v2.01)" }, - { "SLES-50306", "Resident Evil - Code - Veronica X (Europe)" }, - { "SLUS-20184", "Resident Evil - Code - Veronica X (USA)" }, - { "SLES-50650", "Resident Evil - Survivor 2 - Code - Veronica (Europe)" }, - { "SLUS-21134", "Resident Evil 4" }, - { "SLPS-25094", "Reveal Fantasia - Mariel to Yousei Monogatari (Japan)" }, - { "SLES-50113", "Ring of Red (Europe)" }, - { "SLPM-60122", "Ring of Red (Japan) (Taikenban)" }, - { "SLPM-62013", "Ring of Red (Japan)" }, - { "SLUS-20145", "Ring of Red (USA)" }, - { "SLES-51374", "RoboCop (Europe)" }, - { "SLES-50136", "Robot Warlords (Europe)" }, - { "SLES-50137", "Robot Warlords (France)" }, - { "SLES-50138", "Robot Warlords (Germany)" }, - { "SLES-50572", "Robot Wars - Arenas of Destruction (UK)" }, - { "SLPS-25005", "Rock’n Megastage (Japan)" }, - { "SLPM-99999", "Rockman X - Command Mission (Japan)" }, - { "SLPM-65463", "Rocky (Japan)" }, - { "SLES-52002", "Rogue Ops (Europe)" }, - { "SLPM-65534", "Rogue Ops (Japan)" }, - { "SLUS-20746", "Rogue Ops (USA)" }, - { "SLES-52100", "Rugby League (Australia)" }, - { "SLUS-20174", "Rumble Racing" }, - { "SLES-50335", "Rune - Viking Warlord (Europe)" }, - { "SLES-50337", "Rune - Viking Warlord (France)" }, - { "SLES-50336", "Rune - Viking Warlord (Germany)" }, - { "SLES-50338", "Rune - Viking Warlord (Italy)" }, - { "SLES-50339", "Rune - Viking Warlord (Spain)" }, - { "SLUS-20109", "Rune - Viking Warlord (USA)" }, - { "SLPS-25316", "SNK vs. Capcom - SVC Chaos (Japan)" }, - { "SLUS-20433", "SWAT - Global Strike Team (USA)" }, - { "SLUS-20600", "SX Superstar" }, - { "SCPS-11005", "Sagashi ni Ikouyo (Japan)" }, - { "SLPS-20365", "Saikyou Ginsei Shougi 4 (Japan)" }, - { "SLPS-25081", "Saishuu Densha (Japan)" }, - { "SLPM-65275", "Saishuu Heiki Kanojo (Japan)" }, - { "SLPS-20391", "Saiyuuki Reload Gunlock (Japan)" }, - { "SLPM-65109", "Saka Tsuku 2002 - J. League Pro Soccer Club wo Tsukurou! (Japan)" }, - { "SLPM-65515", "Sakura Taisen Monogatari - Mysterious Paris (Japan)" }, - { "SLPS-25559", "Samurai Spirits - Tenkaichi Kenkakuden (Japan)" }, - { "SLPS-20203", "Sanyo Pachinko Paradise 7 - Edokko Gen-san (Japan)" }, - { "SLES-51883", "Scooby-Doo! Mystery Mayhem (Europe)" }, - { "SLUS-20701", "Scooby-Doo! Mystery Mayhem (USA) (En,Fr)" }, - { "SLUS-20424", "Scorpion King, The - Rise of the Akkadian (USA)" }, - { "SLPM-62079", "Se-Pa 2001 (Japan)" }, - { "SLUS-20606", "Seek and Destroy (USA)" }, - { "SLPM-62400", "Sega Ages 2500 Series Vol. 12 - Puyo Puyo Tsuu - Perfect Set (Japan)" }, - { "SLPM-62547", "Sega Ages 2500 Series Vol. 16 - Virtua Fighter 2 (Japan)" }, - { "SLPM-62366", "Sega Ages 2500 Series Vol. 3 - Fantasy Zone (Japan)" }, - { "SLPM-62385", "Sega Ages 2500 Series Vol. 5 - Golden Axe (Japan)" }, - { "SLES-51388", "Sega Bass Fishing Duel (Europe)" }, - { "SLUS-20339", "Sega Bass Fishing Duel (USA)" }, - { "SLES-53461", "Sega Classics Collection (Europe, Australia)" }, - { "SLES-51125", "Sega Soccer Slam (Europe)" }, - { "SLUS-20509", "Sega Soccer Slam (USA)" }, - { "SLES-51253", "Seigneur des Anneaux, Le - Les Deux Tours (France)" }, - { "SLES-51256", "Senor de los Anillos, El - Las Dos Torres (Spain)" }, - { "SLES-50822", "Shadow Hearts (Europe)" }, - { "SLPS-25041", "Shadow Hearts (Japan)" }, - { "SLUS-20347", "Shadow Hearts (USA)" }, - { "SLES-50446", "Shadow Man - 2econd Coming (Europe)" }, - { "SLES-50608", "Shadow Man - 2econd Coming (Germany)" }, - { "SLUS-20413", "Shadow Man - 2econd Coming (USA)" }, - { "?", "Shadow of the Colossus (Europe)" }, - { "SLES-50400", "Shaun Palmer’s Pro Snowboarder (Europe)" }, - { "SLES-50401", "Shaun Palmer’s Pro Snowboarder (France)" }, - { "SLES-50402", "Shaun Palmer’s Pro Snowboarder (Germany)" }, - { "SLUS-20199", "Shaun Palmer’s Pro Snowboarder (USA)" }, - { "SLPM-65334", "Shin Seiki Evangelion - Ayanami Ikusei Keikaku with Asuka Hokan Keikaku (Japan)" }, - { "SLPM-65867", "Shin Seiki Evangelion - Koutetsu no Girlfriend 2nd (Japan)" }, - { "SLPM-65391", "Shinki Gensou - Spectral Souls (Japan)" }, - { "SLPM-65200", "Shinobi (Japan)" }, - { "SLUS-20459", "Shinobi (USA) (En,Ja)" }, - { "SLPM-65328", "Shirachuu Tankenbu (Japan)" }, - { "SLES-52382", "Shrek 2 (Spain)" }, - { "SLPS-25076", "Sidewinder F (Japan)" }, - { "SLPS-25018", "Sidewinder Max (Japan)" }, - { "SLPS-25255", "Sidewinder V (Japan)" }, - { "SLES-51157", "Silent Scope 3 (Europe)" }, - { "SLUS-20514", "Silent Scope 3 (USA) (En,Ja,Es)" }, - { "SLUS-20624", "Simpsons Hit and Run" }, - { "SLES-50754", "Simpsons Skateboarding, The (Europe)" }, - { "SLES-50755", "Simpsons Skateboarding, The (France)" }, - { "SLES-51362", "Simpsons Skateboarding, The (Germany)" }, - { "SLES-51360", "Simpsons Skateboarding, The (Italy)" }, - { "SLES-51361", "Simpsons Skateboarding, The (Spain)" }, - { "SLUS-20114", "Simpsons Skateboarding, The (USA)" }, - { "SLES-51257", "Sims, The (Europe)" }, - { "SLUS-20573", "Sims, The (USA)" }, - { "SLES-50261", "Sky Surfer (Europe)" }, - { "SLPS-20012", "Sky Surfer (Japan)" }, - { "SLPS-20262", "Slot! Pro DX - Fujiko 2 (Japan)" }, - { "SLPS-20285", "Slotter Up Core - Enda! Kyojin no Hoshi (Japan)" }, - { "SLPS-20370", "Slotter Up Core 3 - Yuda! Doronjo ni Omakase (Japan)" }, - { "SLPS-20337", "Slotter Up Core Alpha - Shukko! Yuushou Panel! Shinka! Kyojin no Hoshi (Japan)" }, - { "SLPS-20278", "Slotter Up Mania - Chou Oki-Slot! Pioneer Special (Japan)" }, - { "SLPM-62615", "Slotter Up Mania 6 - Oki no Neppuu! Pioneer Special II (Japan)" }, - { "SLES-51800", "Smash Cars (Europe)" }, - { "SLUS-20620", "Smash Cars (USA)" }, - { "SLPM-65431", "Sonic Heroes (Japan) (En,Ja,Fr,De,Es,It)" }, - { "SLUS-20718", "Sonic Heroes (USA) (En,Ja,Fr,De,Es,It)" }, - { "SLPM-62310", "Soutenryuu - The Arcade (Japan)" }, - { "SLPM-62275", "Space Raiders (Japan)" }, - { "SLES-50486", "Splashdown (Europe)" }, - { "SLES-50268", "SpyHunter (Europe)" }, - { "SLUS-20056", "SpyHunter (USA)" }, - { "SLES-51043", "Spyro - Enter the Dragonfly (Europe)" }, - { "SLUS-20315", "Spyro - Enter the Dragonfly (USA)" }, - { "SLES-52545", "Star Wars - Battlefront (Europe)" }, - { "SLES-52546", "Star Wars - Battlefront (France)" }, - { "SLES-53503", "Star Wars - Battlefront (Germany)" }, - { "SLUS-29164", "Star Wars - Battlefront II (USA) (Beta)" }, - { "SLPS-25252", "Star Wars - Jango Fett (Japan)" }, - { "SLES-50204", "Star Wars - Super Bombad Racing (Europe)" }, - { "SLES-50205", "Star Wars - Super Bombad Racing (France)" }, - { "SLES-50206", "Star Wars - Super Bombad Racing (Germany)" }, - { "SLES-50207", "Star Wars - Super Bombad Racing (Italy)" }, - { "SLES-50208", "Star Wars - Super Bombad Racing (Spain)" }, - { "SLUS-20043", "Star Wars - Super Bombad Racing (USA) (En,Fr,De,Es,It)" }, - { "SLPS-20018", "Stepping Selection (Japan) (Disc 1)" }, - { "SLPS-20019", "Stepping Selection (Japan) (Disc 2)" }, - { "SLES-50072", "Street Fighter EX3 (Europe)" }, - { "SLPM-60105", "Street Fighter EX3 (Japan) (Taikenban)" }, - { "SLPS-20003", "Street Fighter EX3 (Japan)" }, - { "SLUS-20130", "Street Fighter EX3 (USA)" }, - { "SLES-50064", "Stunt GP (Europe)" }, - { "SLPS-20152", "Stunt GP (Japan)" }, - { "SLUS-20218", "Stunt GP (USA)" }, - { "SLES-51160", "Sub Rebellion (Europe)" }, - { "SLUS-20548", "Sub Rebellion (USA)" }, - { "SLPM-65751", "Suigetsu - Mayoi Gokoro (Japan)" }, - { "SLUS-20074", "Summoner (USA)" }, - { "SLES-50533", "Sunny Garcia Surfing (Europe)" }, - { "SLUS-20208", "Sunny Garcia Surfing (USA)" }, - { "SLPS-25070", "Sunrise Eiyuutan 2 (Japan)" }, - { "SLPS-25270", "Sunrise World War (Japan)" }, - { "SLPS-25104", "Super Robot Taisen Impact (Japan)" }, - { "SLES-50897", "Super Trucks (Europe)" }, - { "SLUS-20748", "Super Trucks Racing (USA)" }, - { "SLPM-62423", "SuperLite 2000 Vol. 13 - Tetris - Kiwame Michi (Japan) (v1.02)" }, - { "SLPM-65689", "SuperLite 2000 Vol. 23 - Never 7 - The End of Infinity (Japan)" }, - { "SLES-50419", "Supercar Street Challenge (Europe)" }, - { "SLES-50421", "Supercar Street Challenge (Germany)" }, - { "SLUS-20012", "Supercar Street Challenge (USA)" }, - { "SLES-50852", "Sven-Goeran Eriksson’s World Challenge (Europe)" }, - { "SLES-50794", "Sven-Goeran Eriksson’s World Manager 2002 (Europe)" }, - { "SLES-50033", "Swing Away Golf (Europe)" }, - { "SLUS-20096", "Swing Away Golf (USA)" }, - { "SLPM-65121", "Switch (Japan)" }, - { "SLES-51290", "Sword of the Samurai (Europe)" }, - { "SLPM-65261", "TBS All Star Kanshasai Vol. 1 - Chou Gouka! Quiz Ketteiban (Japan)" }, - { "SLES-50778", "TD Overdrive - The Brotherhood of Speed (Europe)" }, - { "SLPM-62105", "Taikou Risshiden IV (Japan)" }, - { "SLPM-65450", "Tantei Gakuen Q - Kioukan no Satsui (Japan)" }, - { "SLPM-60134", "Technictix (Japan) (Taikenban)" }, - { "SLPS-20055", "Technictix (Japan)" }, - { "SLUS-20981", "Teenage Mutant Ninja Turtles 2 - Battle Nexus (USA)" }, - { "SCAJ-20100", "Tenchu Kurenai (Asia)" }, - { "SLPS-25384", "Tenchu Kurenai (Japan)" }, - { "SLPM-65401", "Tengai Makyou II - Manji Maru (Japan) (Shokai Gentei Picture Label Shiyou)" }, - { "SLPM-65401", "Tengai Makyou II - Manji Maru (Japan)" }, - { "SLPM-65398", "Tennis no Oujisama - Kiss of Prince Flame (Japan)" }, - { "SLPM-65397", "Tennis no Oujisama - Kiss of Prince Ice (Japan)" }, - { "SLPM-65323", "Tennis no Oujisama - Smash Hit! (Japan)" }, - { "SLPM-62359", "Tennis no Oujisama - Smash Hit! Original Anime Game (Japan)" }, - { "SLPM-65371", "Tennis no Oujisama - Sweat & Tears 2 - Seishun Gakuen Teikyuusai ‘03 - Perfect Live (Japan)" }, - { "SLPS-20053", "Tenshi no Present - Marl Oukoku Monogatari (Japan) (Genteiban)" }, - { "SLPS-20066", "Tenshi no Present - Marl Oukoku Monogatari (Japan)" }, - { "SLPM-65598", "Tenshou Gakuen Gensouroku (Japan)" }, - { "SLPS-25298", "Tentama - 1st Sunny Side (Japan)" }, - { "SLUS-20213", "Test Drive (USA)" }, - { "SLUS-20177", "Test Drive Off-Road - Wide Open (USA)" }, - { "SLES-50551", "Tetris Worlds (Europe)" }, - { "SLUS-20099", "Theme Park Roller Coaster (USA)" }, - { "SLES-50078", "TimeSplitters (Europe)" }, - { "SLUS-20090", "TimeSplitters (USA) (v1.10)" }, - { "SLUS-20090", "TimeSplitters (USA) (v2.00)" }, - { "SLES-51181", "Tom Clancy’s Ghost Recon (Europe)" }, - { "SLES-51182", "Tom Clancy’s Ghost Recon (Germany)" }, - { "SLUS-20613", "Tom Clancy’s Ghost Recon (USA)" }, - { "SLED-51472", "Tom Clancy’s Splinter Cell (Europe) (Demo)" }, - { "SLUS-20652", "Tom Clancy’s Splinter Cell (USA)" }, - { "SLES-50400", "Tony Hawk’s Pro Skater 3 (Europe)" }, - { "SLES-50401", "Tony Hawk’s Pro Skater 3 (France)" }, - { "SLES-50402", "Tony Hawk’s Pro Skater 3 (Germany)" }, - { "SLUS-20199", "Tony Hawk’s Pro Skater 3 (USA) (Rev 1)" }, - { "SLUS-20199", "Tony Hawk’s Pro Skater 3 (USA)" }, - { "SLPS-99999", "Tony Hawk’s Pro Skater 4 (USA) (v1.02)" }, - { "SLPS-99999", "Tony Hawk’s Pro Skater 4 (USA) (v2.01)" }, - { "SCED-52441", "Transformers (Europe) (Demo)" }, - { "SLUS-20149", "Tribes - Aerial Assault (USA)" }, - { "SLUS-20931", "Trigger Man (USA)" }, - { "SLUS-20168", "Triple Play Baseball (USA)" }, - { "SLPS-20196", "Tsuki no Hikari - Shizumeru Kane no Satsujin (Japan)" }, - { "SCES-50360", "Twisted Metal - Black (Europe)" }, - { "SCUS-97101", "Twisted Metal - Black (USA)" }, - { "SLPS-20080", "Typing Namidabashi Ashita no Joe Touda (Japan) (USB Keyboard Doukonban)" }, - { "SLPS-20194", "U - Underwater Unit (Japan)" }, - { "SLES-50195", "UEFA Challenge (Europe)" }, - { "SLPS-25294", "Uchuu no Stellvia (Japan)" }, - { "SLPS-25364", "Ultraman (Japan)" }, - { "SLES-51606", "Unlimited Saga (Europe)" }, - { "SLPS-25185", "Unlimited Saga (Japan) (Limited Edition)" }, - { "SLPS-25199", "Unlimited Saga (Japan)" }, - { "SLUS-20678", "Unlimited Saga (USA)" }, - { "SLES-50725", "V-Rally 3 (Europe)" }, - { "SLPM-65191", "V-Rally 3 (Japan) (En,Ja)" }, - { "SCES-50411", "Vampire Night (Europe, Australia)" }, - { "SLPS-25077", "Vampire Night (Japan)" }, - { "SLUS-20221", "Vampire Night (USA)" }, - { "SLPS-20034", "Velvet File (Japan)" }, - { "SLPS-25012", "Victorious Boxers (Japan)" }, - { "SLPS-25129", "Victorious Boxers - Championship Version (Japan)" }, - { "SLES-50280", "Victorious Boxers - Ippo’s Road to Glory (Europe)" }, - { "SLUS-20282", "Victorious Boxers - Ippo’s Road to Glory (USA)" }, - { "SLPS-25287", "Victorious Boxers 2 - Ioop’s Road to Glory (Japan)" }, - { "SLUS-20951", "Viewtiful Joe (USA)" }, - { "SLES-51699", "Virtua Fighter - 10th Anniversary Edition (Europe)" }, - { "SLES-51616", "Virtua Fighter 4 - Evolution (Europe)" }, - { "SLPM-65270", "Virtua Fighter 4 - Evolution (Japan)" }, - { "SLKA-00000", "Virtua Fighter 4 - Evolution (Korea)" }, - { "SLUS-00000", "Virtua Fighter 4 - Evolution (USA)" }, - { "SLES-51600", "WWE Crush Hour (Europe)" }, - { "SLUS-20385", "WWE Crush Hour (USA)" }, - { "SLES-52036", "WWE SmackDown! Here Comes the Pain (Europe)" }, - { "SLUS-20787", "WWE SmackDown! Here Comes the Pain (USA)" }, - { "SLES-51283", "WWE SmackDown! Shut Your Mouth (Europe)" }, - { "SLUS-20483", "WWE SmackDown! Shut Your Mouth (USA)" }, - { "SLES-50183", "Wacky Races Starring Dastardly & Muttley (Europe)" }, - { "SLES-51272", "Wakeboarding Unleashed featuring Shaun Murray (Europe)" }, - { "SLES-51273", "Wakeboarding Unleashed featuring Shaun Murray (France)" }, - { "SLUS-20418", "Wakeboarding Unleashed featuring Shaun Murray (USA)" }, - { "SLUS-20075", "Walt Disney’s The Jungle Book - Rhythm n’ Groove (USA)" }, - { "SLES-51973", "War Chess (Europe)" }, - { "SCUS-97197", "War of the Monsters (USA)" }, - { "SLES-50503", "Weakest Link, The (Europe)" }, - { "SLPM-62019", "Winning Post 4 Maximum (Japan)" }, - { "SLPM-62058", "Winning Post 4 Maximum 2001 (Japan) (Super Value Set)" }, - { "SLPM-62123", "Winning Post 5 (Japan)" }, - { "SLPM-62280", "Winning Post 5 Maximum 2002 (Japan) (Premium Pack)" }, - { "SLPM-62221", "Winning Post 5 Maximum 2002 (Japan)" }, - { "SLES-50035", "Winter X Games Snowboarding (Europe)" }, - { "SLES-50670", "Winter X Games Snowboarding 2 (Europe)" }, - { "SLUS-20321", "Winter X Games Snowboarding 2002 (USA)" }, - { "SLES-50170", "World Destruction League - Thunder Tanks (Europe)" }, - { "SLUS-20005", "World Destruction League - Thunder Tanks (USA)" }, - { "SLES-50262", "World Destruction League - WarJetz (Europe)" }, - { "SLUS-20007", "World Destruction League - WarJetz (USA)" }, - { "SLUS-20611", "World Series Baseball 2K3 (USA)" }, - { "SLPM-62268", "World Soccer Winning Eleven 6 - Final Evolution (Japan)" }, - { "SLES-51843", "Worms 3D (Europe)" }, - { "SLES-51202", "Wreckless - The Yakuza Missions (Europe)" }, - { "SLUS-20431", "Wreckless - The Yakuza Missions (USA)" }, - { "SLES-50430", "X Games Skateboarding (Europe)" }, - { "SLES-50031", "X Squad (Europe)" }, - { "SLUS-20094", "X Squad (USA)" }, - { "SLES-50210", "XGIII - Extreme G Racing (Europe) (v1.02)" }, - { "SLES-50210", "XGIII - Extreme G Racing (Europe) (v2.00)" }, - { "SLUS-20302", "XGIII - Extreme G Racing (USA)" }, - { "SLPS-29002", "Xenosaga Episode I - Der Wille zur Macht (Japan) (Premium Box)" }, - { "SLPS-29002", "Xenosaga Episode I - Der Wille zur Macht (Japan)" }, - { "SLUS-20469", "Xenosaga Episode I - Der Wille zur Macht (USA)" }, - { "SLPS-25048", "Zeonic Front - Kidou Senshi Gundam 0079 (Japan)" }, - { "SLPS-25074", "Zero (Japan)" }, - { "SLPS-25303", "Zero - Akai Chou (Japan)" }, - { "SLPM-65019", "Zone of the Enders - Z.O.E (Japan)" }, - { "SLES-50933", "eJay ClubWorld - The Music Making Experience (Europe)" }, - { "SLUS-20525", "eJay Clubworld - The Music Making Experience (USA)" } -}; - -const char* getGameName(const std::string& gameId) -{ - auto it = gameDatabase.find(gameId); - if (it != gameDatabase.end()) - return it->second.c_str(); - - return nullptr; -} \ No newline at end of file diff --git a/ps2xRuntime/src/runner/register_functions.cpp b/ps2xRuntime/src/runner/register_functions.cpp deleted file mode 100644 index 42a234b6..00000000 --- a/ps2xRuntime/src/runner/register_functions.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "register_functions.h" - -// Replace this file with the actual implementation of the functions that was generated by the compiler -void registerAllFunctions(PS2Runtime &runtime) -{ -} \ No newline at end of file diff --git a/ps2xTest/src/pad_input_tests.cpp b/ps2xTest/src/pad_input_tests.cpp index b3a18f07..bbff0caf 100644 --- a/ps2xTest/src/pad_input_tests.cpp +++ b/ps2xTest/src/pad_input_tests.cpp @@ -31,8 +31,25 @@ namespace ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); } + void openPadPort(R5900Context &ctx, std::vector &rdram, uint32_t port = 0, uint32_t slot = 0) + { + setRegU32(ctx, 4, port); + setRegU32(ctx, 5, slot); + setRegU32(ctx, 6, kPadDataAddr + 0x200u); + ps2_stubs::scePadPortOpen(rdram.data(), &ctx, nullptr); + } + + void closePadPort(R5900Context &ctx, std::vector &rdram, uint32_t port = 0, uint32_t slot = 0) + { + setRegU32(ctx, 4, port); + setRegU32(ctx, 5, slot); + ps2_stubs::scePadPortClose(rdram.data(), &ctx, nullptr); + } + void runPadRead(R5900Context &ctx, std::vector &rdram) { + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); setRegU32(ctx, 6, kPadDataAddr); // a2 ps2_stubs::scePadRead(rdram.data(), &ctx, nullptr); } @@ -53,6 +70,9 @@ void register_pad_input_tests() std::vector rdram(PS2_RAM_SIZE, 0); R5900Context ctx; + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); + openPadPort(ctx, rdram); + const uint16_t buttons = static_cast(0xFFFFu & ~kPadBtnCross & ~kPadBtnStart); ps2_stubs::setPadOverrideState(buttons, 0x00, 0xFF, 0x10, 0xEE); @@ -67,6 +87,7 @@ void register_pad_input_tests() t.Equals(data[7], static_cast(0xFF), "ly should match override"); ps2_stubs::clearPadOverrideState(); + closePadPort(ctx, rdram); }); tc.Run("scePadRead button bits are active-low", [](TestCase &t) @@ -74,6 +95,9 @@ void register_pad_input_tests() std::vector rdram(PS2_RAM_SIZE, 0); R5900Context ctx; + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); + openPadPort(ctx, rdram); + struct ButtonCase { uint16_t mask; @@ -104,11 +128,13 @@ void register_pad_input_tests() ps2_stubs::setPadOverrideState(buttons, 0x80, 0x80, 0x80, 0x80); runPadRead(ctx, rdram); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadRead should succeed for opened ports"); const uint16_t mask = readButtons(rdram); t.IsTrue((mask & entry.mask) == 0, std::string("button should be active-low: ").append(entry.name)); } ps2_stubs::clearPadOverrideState(); + closePadPort(ctx, rdram); }); tc.Run("scePadGetButtonMask returns all buttons", [](TestCase &t) @@ -120,34 +146,50 @@ void register_pad_input_tests() tc.Run("basic pad init/port/state functions return expected values", [](TestCase &t) { + std::vector rdram(PS2_RAM_SIZE, 0); R5900Context ctx; - ps2_stubs::scePadInit(nullptr, &ctx, nullptr); + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadInit should succeed"); - ps2_stubs::scePadInit2(nullptr, &ctx, nullptr); + ps2_stubs::scePadInit2(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadInit2 should succeed"); - ps2_stubs::scePadPortOpen(nullptr, &ctx, nullptr); - t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadPortOpen should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadGetState(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "closed port should report DISCONNECTED"); - ps2_stubs::scePadPortClose(nullptr, &ctx, nullptr); - t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadPortClose should succeed"); + openPadPort(ctx, rdram); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadPortOpen should succeed"); - ps2_stubs::scePadGetState(nullptr, &ctx, nullptr); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadGetState(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(6), "scePadGetState should return STABLE"); - ps2_stubs::scePadGetReqState(nullptr, &ctx, nullptr); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadGetReqState(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "scePadGetReqState should return completed"); - ps2_stubs::scePadGetPortMax(nullptr, &ctx, nullptr); + ps2_stubs::scePadGetPortMax(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(2), "scePadGetPortMax should be 2"); - ps2_stubs::scePadGetSlotMax(nullptr, &ctx, nullptr); + setRegU32(ctx, 4, 0u); + ps2_stubs::scePadGetSlotMax(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadGetSlotMax should be 1"); - ps2_stubs::scePadGetModVersion(nullptr, &ctx, nullptr); + ps2_stubs::scePadGetModVersion(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0x0200), "scePadGetModVersion should be 0x0200"); + + closePadPort(ctx, rdram); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadPortClose should succeed"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadGetState(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "closed port should return DISCONNECTED after close"); }); tc.Run("pad info and mode helpers return consistent values", [](TestCase &t) @@ -155,24 +197,64 @@ void register_pad_input_tests() std::vector rdram(PS2_RAM_SIZE, 0); R5900Context ctx; + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); + openPadPort(ctx, rdram); + + setRegU32(ctx, 6, static_cast(-1)); ps2_stubs::scePadInfoAct(rdram.data(), &ctx, nullptr); - t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "scePadInfoAct should return 0"); + t.IsTrue(static_cast(getRegU32(&ctx, 2)) >= 1u, "scePadInfoAct should report at least one actuator descriptor"); ps2_stubs::scePadInfoComb(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "scePadInfoComb should return 0"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); setRegU32(ctx, 6, 1); setRegU32(ctx, 7, 0); ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(7), "scePadInfoMode CURID should return DualShock"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); setRegU32(ctx, 6, 4); setRegU32(ctx, 7, static_cast(-1)); ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); - t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadInfoMode table count should be 1"); + t.IsTrue(static_cast(getRegU32(&ctx, 2)) >= 1u, "scePadInfoMode table count should be non-zero"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadInfoPressMode(rdram.data(), &ctx, nullptr); - t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "scePadInfoPressMode should be 0"); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadInfoPressMode should report pressure support"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 0u); + setRegU32(ctx, 7, 3u); + ps2_stubs::scePadSetMainMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetMainMode should accept digital mode"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 1u); + setRegU32(ctx, 7, 0u); + ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(4), "CURID should switch to digital mode"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 1u); + setRegU32(ctx, 7, 3u); + ps2_stubs::scePadSetMainMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetMainMode should accept analog mode"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 4); + setRegU32(ctx, 7, 0u); + ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(7), "mode table entry should return DualShock in analog mode"); + + closePadPort(ctx, rdram); }); tc.Run("pad setters return success", [](TestCase &t) @@ -180,35 +262,124 @@ void register_pad_input_tests() std::vector rdram(PS2_RAM_SIZE, 0); R5900Context ctx; + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); + openPadPort(ctx, rdram); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadSetActAlign(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetActAlign should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadSetActDirect(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetActDirect should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 0xFFFFu); ps2_stubs::scePadSetButtonInfo(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetButtonInfo should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 1u); + setRegU32(ctx, 7, 3u); ps2_stubs::scePadSetMainMode(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetMainMode should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadSetReqState(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetReqState should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadSetVrefParam(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetVrefParam should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadSetWarningLevel(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(0), "scePadSetWarningLevel should return 0"); ps2_stubs::scePadEnd(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadEnd should succeed"); + openPadPort(ctx, rdram); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadEnterPressMode(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadEnterPressMode should succeed"); + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); ps2_stubs::scePadExitPressMode(rdram.data(), &ctx, nullptr); t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadExitPressMode should succeed"); + + closePadPort(ctx, rdram); + }); + + tc.Run("scePadRead fills pressure bytes and honors button info mask", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0); + R5900Context ctx; + + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); + openPadPort(ctx, rdram); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 0xFFFFu); + ps2_stubs::scePadSetButtonInfo(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetButtonInfo should accept all buttons"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadEnterPressMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadEnterPressMode should enable pressure data"); + + const uint16_t pressedButtons = static_cast(0xFFFFu & + ~kPadBtnLeft & + ~kPadBtnUp & + ~kPadBtnTriangle & + ~kPadBtnCross & + ~kPadBtnL1 & + ~kPadBtnR2); + ps2_stubs::setPadOverrideState(pressedButtons, 0x80, 0x80, 0x80, 0x80); + runPadRead(ctx, rdram); + + const uint8_t *data = rdram.data() + kPadDataAddr; + t.Equals(data[8], static_cast(0x00), "right pressure should be clear when not pressed"); + t.Equals(data[9], static_cast(0xFF), "left pressure should be populated when pressed"); + t.Equals(data[10], static_cast(0xFF), "up pressure should be populated when pressed"); + t.Equals(data[11], static_cast(0x00), "down pressure should be clear when not pressed"); + t.Equals(data[12], static_cast(0xFF), "triangle pressure should be populated when pressed"); + t.Equals(data[13], static_cast(0x00), "circle pressure should be clear when not pressed"); + t.Equals(data[14], static_cast(0xFF), "cross pressure should be populated when pressed"); + t.Equals(data[15], static_cast(0x00), "square pressure should be clear when not pressed"); + t.Equals(data[16], static_cast(0xFF), "L1 pressure should be populated when pressed"); + t.Equals(data[17], static_cast(0x00), "L2 pressure should be clear when not pressed"); + t.Equals(data[18], static_cast(0x00), "R1 pressure should be clear when not pressed"); + t.Equals(data[19], static_cast(0xFF), "R2 pressure should be populated when pressed"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, static_cast(kPadBtnL1 | kPadBtnR2)); + ps2_stubs::scePadSetButtonInfo(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetButtonInfo should narrow the enabled pressure mask"); + + runPadRead(ctx, rdram); + + t.Equals(data[9], static_cast(0x00), "masked-out direction pressure should clear"); + t.Equals(data[10], static_cast(0x00), "masked-out direction pressure should clear"); + t.Equals(data[12], static_cast(0x00), "masked-out face-button pressure should clear"); + t.Equals(data[14], static_cast(0x00), "masked-out face-button pressure should clear"); + t.Equals(data[16], static_cast(0xFF), "enabled L1 pressure should remain populated"); + t.Equals(data[19], static_cast(0xFF), "enabled R2 pressure should remain populated"); + + ps2_stubs::clearPadOverrideState(); + closePadPort(ctx, rdram); }); tc.Run("pad string helpers map state codes", [](TestCase &t) diff --git a/ps2xTest/src/ps2_gs_tests.cpp b/ps2xTest/src/ps2_gs_tests.cpp index d5c00f06..e6c29384 100644 --- a/ps2xTest/src/ps2_gs_tests.cpp +++ b/ps2xTest/src/ps2_gs_tests.cpp @@ -1,10 +1,12 @@ #include "MiniTest.h" -#include "ps2_memory.h" +#include "runtime/ps2_memory.h" #include "ps2_runtime.h" #include "ps2_stubs.h" #include "ps2_syscalls.h" -#include "ps2_gs_gpu.h" -#include "ps2_gs_psmt4.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_gs_psmct32.h" +#include "runtime/ps2_gs_psmt4.h" +#include "runtime/ps2_gs_psmt8.h" #include #include @@ -20,6 +22,19 @@ namespace std::atomic g_gsSyncCallbackHits{0u}; std::atomic g_gsSyncCallbackLastTick{0u}; + struct GsImageMem + { + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; + uint16_t vramAddr; + uint8_t vramWidth; + uint8_t psm; + }; + + static_assert(sizeof(GsImageMem) == 12, "GsImageMem size mismatch"); + void setRegU32(R5900Context &ctx, int reg, uint32_t value) { ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); @@ -79,6 +94,206 @@ namespace g_gsSyncCallbackHits.fetch_add(1u, std::memory_order_relaxed); ctx->pc = 0u; } + + void writeGsImage(uint8_t *rdram, uint32_t addr, const GsImageMem &image) + { + std::memcpy(rdram + addr, &image, sizeof(image)); + } + + void writeGsImage(std::vector &rdram, uint32_t addr, const GsImageMem &image) + { + writeGsImage(rdram.data(), addr, image); + } + + void writePSMT4Texel(std::vector &vram, uint32_t tbp, uint32_t tbw, uint32_t x, uint32_t y, uint8_t index) + { + const uint32_t nibbleAddr = GSPSMT4::addrPSMT4(tbp, tbw, x, y); + const uint32_t byteOff = nibbleAddr >> 1; + uint8_t &packed = vram[byteOff]; + if ((nibbleAddr & 1u) != 0u) + { + packed = static_cast((packed & 0x0Fu) | ((index & 0x0Fu) << 4)); + } + else + { + packed = static_cast((packed & 0xF0u) | (index & 0x0Fu)); + } + } + + uint32_t referenceAddrPSMT4(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + static constexpr uint8_t kBlockTable4[8][4] = { + {0, 2, 8, 10}, + {1, 3, 9, 11}, + {4, 6, 12, 14}, + {5, 7, 13, 15}, + {16, 18, 24, 26}, + {17, 19, 25, 27}, + {20, 22, 28, 30}, + {21, 23, 29, 31}, + }; + + static constexpr uint16_t kColumnTable4[16][32] = { + {0, 8, 32, 40, 64, 72, 96, 104, 2, 10, 34, 42, 66, 74, 98, 106, 4, 12, 36, 44, 68, 76, 100, 108, 6, 14, 38, 46, 70, 78, 102, 110}, + {16, 24, 48, 56, 80, 88, 112, 120, 18, 26, 50, 58, 82, 90, 114, 122, 20, 28, 52, 60, 84, 92, 116, 124, 22, 30, 54, 62, 86, 94, 118, 126}, + {65, 73, 97, 105, 1, 9, 33, 41, 67, 75, 99, 107, 3, 11, 35, 43, 69, 77, 101, 109, 5, 13, 37, 45, 71, 79, 103, 111, 7, 15, 39, 47}, + {81, 89, 113, 121, 17, 25, 49, 57, 83, 91, 115, 123, 19, 27, 51, 59, 85, 93, 117, 125, 21, 29, 53, 61, 87, 95, 119, 127, 23, 31, 55, 63}, + {192, 200, 224, 232, 128, 136, 160, 168, 194, 202, 226, 234, 130, 138, 162, 170, 196, 204, 228, 236, 132, 140, 164, 172, 198, 206, 230, 238, 134, 142, 166, 174}, + {208, 216, 240, 248, 144, 152, 176, 184, 210, 218, 242, 250, 146, 154, 178, 186, 212, 220, 244, 252, 148, 156, 180, 188, 214, 222, 246, 254, 150, 158, 182, 190}, + {129, 137, 161, 169, 193, 201, 225, 233, 131, 139, 163, 171, 195, 203, 227, 235, 133, 141, 165, 173, 197, 205, 229, 237, 135, 143, 167, 175, 199, 207, 231, 239}, + {145, 153, 177, 185, 209, 217, 241, 249, 147, 155, 179, 187, 211, 219, 243, 251, 149, 157, 181, 189, 213, 221, 245, 253, 151, 159, 183, 191, 215, 223, 247, 255}, + {256, 264, 288, 296, 320, 328, 352, 360, 258, 266, 290, 298, 322, 330, 354, 362, 260, 268, 292, 300, 324, 332, 356, 364, 262, 270, 294, 302, 326, 334, 358, 366}, + {272, 280, 304, 312, 336, 344, 368, 376, 274, 282, 306, 314, 338, 346, 370, 378, 276, 284, 308, 316, 340, 348, 372, 380, 278, 286, 310, 318, 342, 350, 374, 382}, + {321, 329, 353, 361, 257, 265, 289, 297, 323, 331, 355, 363, 259, 267, 291, 299, 325, 333, 357, 365, 261, 269, 293, 301, 327, 335, 359, 367, 263, 271, 295, 303}, + {337, 345, 369, 377, 273, 281, 305, 313, 339, 347, 371, 379, 275, 283, 307, 315, 341, 349, 373, 381, 277, 285, 309, 317, 343, 351, 375, 383, 279, 287, 311, 319}, + {448, 456, 480, 488, 384, 392, 416, 424, 450, 458, 482, 490, 386, 394, 418, 426, 452, 460, 484, 492, 388, 396, 420, 428, 454, 462, 486, 494, 390, 398, 422, 430}, + {464, 472, 496, 504, 400, 408, 432, 440, 466, 474, 498, 506, 402, 410, 434, 442, 468, 476, 500, 508, 404, 412, 436, 444, 470, 478, 502, 510, 406, 414, 438, 446}, + {385, 393, 417, 425, 449, 457, 481, 489, 387, 395, 419, 427, 451, 459, 483, 491, 389, 397, 421, 429, 453, 461, 485, 493, 391, 399, 423, 431, 455, 463, 487, 495}, + {401, 409, 433, 441, 465, 473, 497, 505, 403, 411, 435, 443, 467, 475, 499, 507, 405, 413, 437, 445, 469, 477, 501, 509, 407, 415, 439, 447, 471, 479, 503, 511}, + }; + + const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u; + const uint32_t page = (block >> 5u) + (y >> 7u) * pagesPerRow + (x >> 7u); + const uint32_t blockId = (block & 0x1Fu) + kBlockTable4[(y >> 4u) & 7u][(x >> 5u) & 3u]; + const uint32_t pageOffset = (blockId >> 5u) << 14u; + const uint32_t localBlock = blockId & 0x1Fu; + return (page << 14u) + pageOffset + localBlock * 512u + kColumnTable4[y & 0x0Fu][x & 0x1Fu]; + } + + void writeReferencePSMT4Texel(std::vector &vram, uint32_t tbp, uint32_t tbw, uint32_t x, uint32_t y, uint8_t index) + { + const uint32_t nibbleAddr = referenceAddrPSMT4(tbp, tbw, x, y); + const uint32_t byteOff = nibbleAddr >> 1; + uint8_t &packed = vram[byteOff]; + if ((nibbleAddr & 1u) != 0u) + { + packed = static_cast((packed & 0x0Fu) | ((index & 0x0Fu) << 4)); + } + else + { + packed = static_cast((packed & 0xF0u) | (index & 0x0Fu)); + } + } + + uint32_t referenceAddrPSMT8(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + static constexpr uint8_t kBlockTable8[4][8] = { + {0, 1, 4, 5, 16, 17, 20, 21}, + {2, 3, 6, 7, 18, 19, 22, 23}, + {8, 9, 12, 13, 24, 25, 28, 29}, + {10, 11, 14, 15, 26, 27, 30, 31}, + }; + + static constexpr uint8_t kColumnTable8[16][16] = { + {0, 4, 16, 20, 32, 36, 48, 52, 2, 6, 18, 22, 34, 38, 50, 54}, + {8, 12, 24, 28, 40, 44, 56, 60, 10, 14, 26, 30, 42, 46, 58, 62}, + {33, 37, 49, 53, 1, 5, 17, 21, 35, 39, 51, 55, 3, 7, 19, 23}, + {41, 45, 57, 61, 9, 13, 25, 29, 43, 47, 59, 63, 11, 15, 27, 31}, + {96, 100, 112, 116, 64, 68, 80, 84, 98, 102, 114, 118, 66, 70, 82, 86}, + {104, 108, 120, 124, 72, 76, 88, 92, 106, 110, 122, 126, 74, 78, 90, 94}, + {65, 69, 81, 85, 97, 101, 113, 117, 67, 71, 83, 87, 99, 103, 115, 119}, + {73, 77, 89, 93, 105, 109, 121, 125, 75, 79, 91, 95, 107, 111, 123, 127}, + {128, 132, 144, 148, 160, 164, 176, 180, 130, 134, 146, 150, 162, 166, 178, 182}, + {136, 140, 152, 156, 168, 172, 184, 188, 138, 142, 154, 158, 170, 174, 186, 190}, + {161, 165, 177, 181, 129, 133, 145, 149, 163, 167, 179, 183, 131, 135, 147, 151}, + {169, 173, 185, 189, 137, 141, 153, 157, 171, 175, 187, 191, 139, 143, 155, 159}, + {224, 228, 240, 244, 192, 196, 208, 212, 226, 230, 242, 246, 194, 198, 210, 214}, + {232, 236, 248, 252, 200, 204, 216, 220, 234, 238, 250, 254, 202, 206, 218, 222}, + {193, 197, 209, 213, 225, 229, 241, 245, 195, 199, 211, 215, 227, 231, 243, 247}, + {201, 205, 217, 221, 233, 237, 249, 253, 203, 207, 219, 223, 235, 239, 251, 255}, + }; + + const uint32_t pagesPerRow = ((width >> 1u) != 0u) ? (width >> 1u) : 1u; + const uint32_t page = (block >> 5u) + (y >> 6u) * pagesPerRow + (x >> 7u); + const uint32_t blockId = (block & 0x1Fu) + kBlockTable8[(y >> 4u) & 3u][(x >> 4u) & 7u]; + const uint32_t pageOffset = (blockId >> 5u) << 13u; + const uint32_t localBlock = blockId & 0x1Fu; + return (page << 13u) + pageOffset + localBlock * 256u + kColumnTable8[y & 0x0Fu][x & 0x0Fu]; + } + + uint32_t referenceAddrPSMCT32(uint32_t block, uint32_t width, uint32_t x, uint32_t y) + { + static constexpr uint8_t kBlockTable32[4][8] = { + {0, 1, 4, 5, 16, 17, 20, 21}, + {2, 3, 6, 7, 18, 19, 22, 23}, + {8, 9, 12, 13, 24, 25, 28, 29}, + {10, 11, 14, 15, 26, 27, 30, 31}, + }; + + static constexpr uint8_t kColumnTable32[8][8] = { + {0, 1, 4, 5, 8, 9, 12, 13}, + {2, 3, 6, 7, 10, 11, 14, 15}, + {16, 17, 20, 21, 24, 25, 28, 29}, + {18, 19, 22, 23, 26, 27, 30, 31}, + {32, 33, 36, 37, 40, 41, 44, 45}, + {34, 35, 38, 39, 42, 43, 46, 47}, + {48, 49, 52, 53, 56, 57, 60, 61}, + {50, 51, 54, 55, 58, 59, 62, 63}, + }; + + const uint32_t pagesPerRow = (width != 0u) ? width : 1u; + const uint32_t page = (block >> 5u) + (y >> 5u) * pagesPerRow + (x >> 6u); + const uint32_t blockId = (block & 0x1Fu) + kBlockTable32[(y >> 3u) & 3u][(x >> 3u) & 7u]; + const uint32_t pageOffset = (blockId >> 5u) << 13u; + const uint32_t localBlock = blockId & 0x1Fu; + return (page << 13u) + pageOffset + localBlock * 256u + + static_cast(kColumnTable32[y & 0x7u][x & 0x7u]) * 4u; + } + + void writeReferencePSMCT32Pixel(std::vector &vram, + uint32_t fbp, + uint32_t fbw, + uint32_t x, + uint32_t y, + uint32_t pixel) + { + const uint32_t off = referenceAddrPSMCT32(fbp, (fbw != 0u) ? fbw : 1u, x, y); + std::memcpy(vram.data() + off, &pixel, sizeof(pixel)); + } + + uint32_t readReferencePSMCT32Pixel(const std::vector &vram, + uint32_t fbp, + uint32_t fbw, + uint32_t x, + uint32_t y) + { + const uint32_t off = referenceAddrPSMCT32(fbp, (fbw != 0u) ? fbw : 1u, x, y); + uint32_t pixel = 0u; + std::memcpy(&pixel, vram.data() + off, sizeof(pixel)); + return pixel; + } + + uint32_t frameBaseToBlock(uint32_t fbp) + { + return fbp << 5u; + } + + void writeReferenceFramePSMCT32Pixel(std::vector &vram, + uint32_t fbp, + uint32_t fbw, + uint32_t x, + uint32_t y, + uint32_t pixel) + { + writeReferencePSMCT32Pixel(vram, frameBaseToBlock(fbp), fbw, x, y, pixel); + } + + uint32_t readReferenceFramePSMCT32Pixel(const std::vector &vram, + uint32_t fbp, + uint32_t fbw, + uint32_t x, + uint32_t y) + { + return readReferencePSMCT32Pixel(vram, frameBaseToBlock(fbp), fbw, x, y); + } + + void expectGuestHeapReusable(TestCase &t, PS2Runtime &runtime, const char *message) + { + const uint32_t expectedBase = runtime.guestHeapBase(); + const uint32_t probe = runtime.guestMalloc(16u, 16u); + t.Equals(probe, expectedBase, message); + runtime.guestFree(probe); + } } void register_ps2_gs_tests() @@ -167,6 +382,29 @@ void register_ps2_gs_tests() t.Equals(currentImr, 0x3333444411112222ull, "GsGetIMR should return current GS IMR"); }); + tc.Run("GsSetCrt updates SMODE2 for host presentation mode", [](TestCase &t) + { + PS2Runtime runtime; + t.IsTrue(runtime.memory().initialize(), "runtime memory initialize should succeed"); + + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + setRegU32(ctx, 4, 1u); // interlaced + setRegU32(ctx, 5, 0u); // NTSC + setRegU32(ctx, 6, 0u); // field mode + + runtime.memory().gs().pmode = 0u; + runtime.memory().gs().smode2 = 0u; + GsSetCrt(rdram.data(), &ctx, &runtime); + + t.Equals(runtime.memory().gs().smode2, 0x1ull, + "GsSetCrt should publish interlaced field mode through SMODE2"); + t.Equals(runtime.memory().gs().pmode & 0x3ull, 0x1ull, + "GsSetCrt should leave CRT1 enabled for presentation"); + t.Equals(getRegU32Test(ctx, 2), 0u, + "GsSetCrt should return success"); + }); + tc.Run("sceGsSetDefDBuffDc seeds display envs and swap applies the selected page", [](TestCase &t) { PS2Runtime runtime; @@ -214,22 +452,993 @@ void register_ps2_gs_tests() t.Equals((xyoffset10 >> 32) & 0xFFFFull, 0x7200ull, "dbuff draw env should seed OFY in 12.4 fixed point"); t.Equals(xyoffset10Addr, 0x18ull, "dbuff draw env should seed XYOFFSET_1 register id"); - dispfb0 = (dispfb0 & ~0x1FFull) | 150ull; - std::memcpy(rdram.data() + kEnvAddr + kDispFbOffset, &dispfb0, sizeof(dispfb0)); + dispfb0 = (dispfb0 & ~0x1FFull) | 150ull; + std::memcpy(rdram.data() + kEnvAddr + kDispFbOffset, &dispfb0, sizeof(dispfb0)); + + uint64_t dispfb1 = dispfb0; + dispfb1 = (dispfb1 & ~0x1FFull) | 151ull; + std::memcpy(rdram.data() + kEnvAddr + kDispEnvSize + kDispFbOffset, &dispfb1, sizeof(dispfb1)); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kEnvAddr); + setRegU32(ctx, 5, 1u); + ps2_stubs::sceGsSwapDBuffDc(rdram.data(), &ctx, &runtime); + + t.Equals(runtime.memory().gs().dispfb1 & 0x1FFull, 151ull, + "sceGsSwapDBuffDc should program GS to the selected display page"); + t.Equals((runtime.memory().gs().display1 >> 32) & 0x0FFFull, 639ull, + "sceGsSwapDBuffDc should preserve the display width from the seeded env"); + }); + + tc.Run("sceGsSetDefDBuffDc seeds a clear packet and swap clears the draw buffer", [](TestCase &t) + { + PS2Runtime runtime; + t.IsTrue(runtime.memory().initialize(), "runtime memory initialize should succeed"); + + std::vector rdram(PS2_RAM_SIZE, 0u); + constexpr uint32_t kEnvAddr = 0x5000u; + constexpr uint32_t kDBuffSize = 0x330u; + constexpr uint32_t kClear0Offset = 0x160u; + constexpr uint32_t kTestAAddrOffset = kClear0Offset + 0x08u; + constexpr uint32_t kPrimAddrOffset = kClear0Offset + 0x18u; + constexpr uint32_t kRgbaqOffset = kClear0Offset + 0x20u; + constexpr uint32_t kRgbaqAddrOffset = kClear0Offset + 0x28u; + constexpr uint32_t kXyz2AAddrOffset = kClear0Offset + 0x38u; + constexpr uint32_t kXyz2BAddrOffset = kClear0Offset + 0x48u; + constexpr uint32_t kTestBAddrOffset = kClear0Offset + 0x58u; + constexpr uint32_t kClearColor = 0x80402010u; + constexpr uint32_t kStackAddr = 0x900u; + const uint32_t kZTest = 2u; + const uint32_t kEnableClear = 1u; + + R5900Context ctx{}; + setRegU32(ctx, 4, kEnvAddr); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 640u); + setRegU32(ctx, 7, 448u); + setRegU32(ctx, 29, kStackAddr); + std::memset(rdram.data() + kEnvAddr, 0xCD, kDBuffSize); + std::memcpy(rdram.data() + kStackAddr + 16u, &kZTest, sizeof(kZTest)); + std::memcpy(rdram.data() + kStackAddr + 24u, &kEnableClear, sizeof(kEnableClear)); + std::memset(runtime.memory().getGSVRAM(), 0xAB, 16u); + ps2_stubs::sceGsSetDefDBuffDc(rdram.data(), &ctx, &runtime); + + uint64_t testAAddr = 0u; + uint64_t primAddr = 0u; + uint64_t rgbaqAddr = 0u; + uint64_t xyz2AAddr = 0u; + uint64_t xyz2BAddr = 0u; + uint64_t testBAddr = 0u; + std::memcpy(&testAAddr, rdram.data() + kEnvAddr + kTestAAddrOffset, sizeof(testAAddr)); + std::memcpy(&primAddr, rdram.data() + kEnvAddr + kPrimAddrOffset, sizeof(primAddr)); + std::memcpy(&rgbaqAddr, rdram.data() + kEnvAddr + kRgbaqAddrOffset, sizeof(rgbaqAddr)); + std::memcpy(&xyz2AAddr, rdram.data() + kEnvAddr + kXyz2AAddrOffset, sizeof(xyz2AAddr)); + std::memcpy(&xyz2BAddr, rdram.data() + kEnvAddr + kXyz2BAddrOffset, sizeof(xyz2BAddr)); + std::memcpy(&testBAddr, rdram.data() + kEnvAddr + kTestBAddrOffset, sizeof(testBAddr)); + + t.Equals(testAAddr, 0x47ull, "dbuff clear packet should program TEST_1 before clearing"); + t.Equals(primAddr, 0x00ull, "dbuff clear packet should program PRIM before clearing"); + t.Equals(rgbaqAddr, 0x01ull, "dbuff clear packet should expose RGBAQ for runtime color updates"); + t.Equals(xyz2AAddr, 0x05ull, "dbuff clear packet should seed the first clear vertex as XYZ2"); + t.Equals(xyz2BAddr, 0x05ull, "dbuff clear packet should seed the second clear vertex as XYZ2"); + t.Equals(testBAddr, 0x47ull, "dbuff clear packet should restore TEST_1 after clearing"); + + uint64_t rgbaq = static_cast(kClearColor); + std::memcpy(rdram.data() + kEnvAddr + kRgbaqOffset, &rgbaq, sizeof(rgbaq)); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kEnvAddr); + setRegU32(ctx, 5, 0u); + ps2_stubs::sceGsSwapDBuffDc(rdram.data(), &ctx, &runtime); + + uint32_t clearedPixel = 0u; + std::memcpy(&clearedPixel, runtime.memory().getGSVRAM(), sizeof(clearedPixel)); + t.Equals(clearedPixel, kClearColor, + "sceGsSwapDBuffDc should execute the seeded clear packet against the active draw buffer"); + + constexpr uint32_t kMidX = 320u; + constexpr uint32_t kMidY = 200u; + const uint32_t kMidOffset = ((kMidY * 640u) + kMidX) * 4u; + uint32_t clearedMidPixel = 0u; + std::memcpy(&clearedMidPixel, runtime.memory().getGSVRAM() + kMidOffset, sizeof(clearedMidPixel)); + t.Equals(clearedMidPixel, kClearColor, + "sceGsSwapDBuffDc should clear the interior of the active draw buffer, not just the first pixel"); + }); + + tc.Run("sceGsSetDefDBuffDc accepts trailing args from the recompiler register ABI", [](TestCase &t) + { + PS2Runtime runtime; + t.IsTrue(runtime.memory().initialize(), "runtime memory initialize should succeed"); + + std::vector rdram(PS2_RAM_SIZE, 0u); + constexpr uint32_t kEnvAddr = 0x5400u; + constexpr uint32_t kDBuffSize = 0x330u; + constexpr uint32_t kClear0Offset = 0x160u; + constexpr uint32_t kRgbaqOffset = kClear0Offset + 0x20u; + constexpr uint32_t kRgbaqAddrOffset = kClear0Offset + 0x28u; + constexpr uint32_t kStackAddr = 0xA00u; + constexpr uint32_t kClearColor = 0x40201008u; + + R5900Context ctx{}; + setRegU32(ctx, 4, kEnvAddr); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 640u); + setRegU32(ctx, 7, 448u); + setRegU32(ctx, 8, 2u); + setRegU32(ctx, 9, 58u); + setRegU32(ctx, 10, 1u); + setRegU32(ctx, 29, kStackAddr); + std::memset(rdram.data() + kEnvAddr, 0xCD, kDBuffSize); + std::memset(runtime.memory().getGSVRAM(), 0xAB, 16u); + + ps2_stubs::sceGsSetDefDBuffDc(rdram.data(), &ctx, &runtime); + + uint64_t rgbaqAddr = 0u; + std::memcpy(&rgbaqAddr, rdram.data() + kEnvAddr + kRgbaqAddrOffset, sizeof(rgbaqAddr)); + t.Equals(rgbaqAddr, 0x01ull, + "sceGsSetDefDBuffDc should seed the clear packet when trailing args arrive in t0-t2"); + + const uint64_t rgbaq = static_cast(kClearColor); + std::memcpy(rdram.data() + kEnvAddr + kRgbaqOffset, &rgbaq, sizeof(rgbaq)); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kEnvAddr); + setRegU32(ctx, 5, 0u); + ps2_stubs::sceGsSwapDBuffDc(rdram.data(), &ctx, &runtime); + + uint32_t clearedPixel = 0u; + std::memcpy(&clearedPixel, runtime.memory().getGSVRAM(), sizeof(clearedPixel)); + t.Equals(clearedPixel, kClearColor, + "sceGsSwapDBuffDc should honor a clear packet seeded from register-based trailing args"); + }); + + tc.Run("clearFramebufferContext clears the requested context even if another context is active", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kCtx0Color = 0x11223344u; + constexpr uint32_t kCtx1Sentinel = 0xAABBCCDDu; + + gs.writeRegister(GS_REG_FRAME_1, (1ull << 16)); // FBP=0, FBW=1, PSMCT32 + gs.writeRegister(GS_REG_SCISSOR_1, (0ull << 0) | (0ull << 16) | (1ull << 32) | (1ull << 48)); + gs.writeRegister(GS_REG_FRAME_2, 150ull | (1ull << 16)); + gs.writeRegister(GS_REG_SCISSOR_2, (0ull << 0) | (0ull << 16) | (1ull << 32) | (1ull << 48)); + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT) | (1ull << 9)); + + writeReferenceFramePSMCT32Pixel(vram, 150u, 1u, 0u, 1u, kCtx1Sentinel); + + t.IsTrue(gs.clearFramebufferContext(0u, kCtx0Color), + "context-targeted clear should succeed for a configured CT32 framebuffer"); + + const uint32_t ctx0Pixel = readReferenceFramePSMCT32Pixel(vram, 0u, 1u, 0u, 1u); + t.Equals(ctx0Pixel, kCtx0Color, + "context-targeted clear should write the requested context even when PRIM.ctxt points elsewhere"); + + const uint32_t ctx1Pixel = readReferenceFramePSMCT32Pixel(vram, 150u, 1u, 0u, 1u); + t.Equals(ctx1Pixel, kCtx1Sentinel, + "context-targeted clear should leave the other context framebuffer untouched"); + }); + + tc.Run("PABE bypasses alpha blend for low-alpha source pixels", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + gs.writeRegister(GS_REG_FRAME_1, (1ull << 16)); // FBW=1, PSMCT32, FBP=0 + gs.writeRegister(GS_REG_SCISSOR_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0x6000000064ull); + gs.writeRegister(GS_REG_TEST_1, 0x30000ull); + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT) | (1ull << 6)); + + const uint32_t dstWhite = 0xFFFFFFFFu; + std::memcpy(vram.data(), &dstWhite, sizeof(dstWhite)); + + gs.writeRegister(GS_REG_PABE, 0ull); + gs.writeRegister(GS_REG_RGBAQ, 0x01000000ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + uint32_t blendedPixel = 0u; + std::memcpy(&blendedPixel, vram.data(), sizeof(blendedPixel)); + t.Equals(blendedPixel, 0x013F3F3Fu, + "without PABE, low-alpha fullscreen copies should still apply ALPHA blending"); + + std::memcpy(vram.data(), &dstWhite, sizeof(dstWhite)); + + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT) | (1ull << 6)); + gs.writeRegister(GS_REG_PABE, 1ull); + gs.writeRegister(GS_REG_RGBAQ, 0x01000000ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + uint32_t pabeBypassedPixel = 0u; + std::memcpy(&pabeBypassedPixel, vram.data(), sizeof(pabeBypassedPixel)); + t.Equals(pabeBypassedPixel, 0x01000000u, + "with PABE enabled, low-alpha source pixels should bypass ALPHA blending and overwrite the destination"); + + std::memcpy(vram.data(), &dstWhite, sizeof(dstWhite)); + + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT) | (1ull << 6)); + gs.writeRegister(GS_REG_PABE, 1ull); + gs.writeRegister(GS_REG_RGBAQ, 0x80000000ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + uint32_t highAlphaPixel = 0u; + std::memcpy(&highAlphaPixel, vram.data(), sizeof(highAlphaPixel)); + t.Equals(highAlphaPixel, 0x803F3F3Fu, + "with PABE enabled, high-alpha source pixels should still use the configured ALPHA blend"); + }); + + tc.Run("FBA forces the framebuffer alpha high bit on CT32 writes", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + gs.writeRegister(GS_REG_FRAME_1, (1ull << 16)); + gs.writeRegister(GS_REG_SCISSOR_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT)); + gs.writeRegister(GS_REG_FBA_1, 0ull); + gs.writeRegister(GS_REG_RGBAQ, 0x01112233ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + uint32_t pixelWithoutFba = 0u; + std::memcpy(&pixelWithoutFba, vram.data(), sizeof(pixelWithoutFba)); + t.Equals(pixelWithoutFba, 0x01112233u, + "without FBA, CT32 writes should preserve the source alpha byte"); + + std::memset(vram.data(), 0, sizeof(uint32_t)); + + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT)); + gs.writeRegister(GS_REG_FBA_1, 1ull); + gs.writeRegister(GS_REG_RGBAQ, 0x01112233ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + uint32_t pixelWithFba = 0u; + std::memcpy(&pixelWithFba, vram.data(), sizeof(pixelWithFba)); + t.Equals(pixelWithFba, 0x81112233u, + "with FBA enabled, CT32 writes should force the framebuffer alpha high bit"); + }); + + tc.Run("CT32 raster writes alias cleanly into later CT32 texture sampling", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint64_t kFrame1 = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor1 = + (0ull << 0) | + (1ull << 16) | + (0ull << 32) | + (1ull << 48); + constexpr uint64_t kFrame2 = + (150ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor2 = 0ull; + constexpr uint64_t kTex0_2 = + (0ull << 0) | + (1ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (0ull << 26) | + (1ull << 30) | + (1ull << 34) | + (1ull << 35); + constexpr uint64_t kPointPrim = static_cast(GS_PRIM_POINT); + constexpr uint64_t kCopyPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8) | + (1ull << 9); + constexpr uint64_t kSourceColor = 0x80665544ull; + constexpr uint64_t kPointXyz = + (0ull << 0) | + (16ull << 16); + constexpr uint64_t kUvRow1 = + (0ull << 0) | + (16ull << 16); + + gs.writeRegister(GS_REG_FRAME_1, kFrame1); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor1); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_PRIM, kPointPrim); + gs.writeRegister(GS_REG_RGBAQ, kSourceColor); + gs.writeRegister(GS_REG_XYZ2, kPointXyz); + + gs.writeRegister(GS_REG_FRAME_2, kFrame2); + gs.writeRegister(GS_REG_SCISSOR_2, kScissor2); + gs.writeRegister(GS_REG_XYOFFSET_2, 0ull); + gs.writeRegister(GS_REG_TEST_2, 0ull); + gs.writeRegister(GS_REG_TEX0_2, kTex0_2); + gs.writeRegister(GS_REG_PRIM, kCopyPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, kUvRow1); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, kUvRow1); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + const uint32_t dstPixel = readReferenceFramePSMCT32Pixel(vram, 150u, 1u, 0u, 0u); + t.Equals(dstPixel, static_cast(kSourceColor), + "CT32 primitives should land in the same local-memory layout that later CT32 texture sampling expects"); + }); + + tc.Run("FST sprite 1:1 CT32 copies preserve source texels at the right and bottom edges", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint64_t kFrame = + (150ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (3ull << 16) | + (0ull << 32) | + (3ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (2ull << 26) | + (2ull << 30) | + (1ull << 34); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint64_t kXyz0 = 0ull; + constexpr uint64_t kXyz1 = + (static_cast(4u << 4) << 0) | + (static_cast(4u << 4) << 16); + constexpr uint64_t kUv0 = 0ull; + constexpr uint64_t kUv1 = + ((4ull * 16ull) << 0) | + ((4ull * 16ull) << 16); + constexpr uint32_t kSourcePixels[4] = { + 0x800000FFu, + 0x8000FF00u, + 0x80FF0000u, + 0x80FFFFFFu, + }; + + for (uint32_t y = 0u; y < 4u; ++y) + { + for (uint32_t x = 0u; x < 4u; ++x) + { + writeReferencePSMCT32Pixel(vram, kTexTbp, 1u, x, y, kSourcePixels[x]); + } + } + + gs.writeRegister(GS_REG_FRAME_1, kFrame); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_TEX1_1, 0ull); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, kUv0); + gs.writeRegister(GS_REG_XYZ2, kXyz0); + gs.writeRegister(GS_REG_UV, kUv1); + gs.writeRegister(GS_REG_XYZ2, kXyz1); + + for (uint32_t y = 0u; y < 4u; ++y) + { + for (uint32_t x = 0u; x < 4u; ++x) + { + const uint32_t pixel = readReferenceFramePSMCT32Pixel(vram, 150u, 1u, x, y); + t.Equals(pixel, kSourcePixels[x], + "1:1 FST sprite copies should preserve each source texel without off-by-one edge skew"); + } + } + }); + + tc.Run("fullscreen display copy tracks the preferred presentation source frame", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint64_t kFrame2 = + 150ull | + (10ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor2 = + (0ull << 0) | + (639ull << 16) | + (0ull << 32) | + (479ull << 48); + constexpr uint64_t kXYOffset2 = + (static_cast(1728u << 4) << 0) | + (static_cast(1808u << 4) << 32); + constexpr uint64_t kAlpha2 = 0x6000000064ull; + constexpr uint64_t kTest2 = 0x30000ull; + constexpr uint64_t kTex0_2 = + (0ull << 0) | + (10ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (10ull << 26) | + (9ull << 30) | + (1ull << 34); + constexpr uint64_t kPrimCopy = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 6) | + (1ull << 8) | + (1ull << 9); + constexpr uint64_t kXyz0 = + (static_cast(1728u << 4) << 0) | + (static_cast(1808u << 4) << 16) | + (256ull << 32); + constexpr uint64_t kXyz1 = + (static_cast(2368u << 4) << 0) | + (static_cast(2288u << 4) << 16) | + (256ull << 32); + constexpr uint64_t kUv0 = + (8ull << 0) | + (8ull << 16); + constexpr uint64_t kUv1 = + ((8ull + (640ull * 16ull)) << 0) | + ((8ull + (480ull * 16ull)) << 16); + + gs.writeRegister(GS_REG_FRAME_2, kFrame2); + gs.writeRegister(GS_REG_SCISSOR_2, kScissor2); + gs.writeRegister(GS_REG_XYOFFSET_2, kXYOffset2); + gs.writeRegister(GS_REG_ALPHA_2, kAlpha2); + gs.writeRegister(GS_REG_TEST_2, kTest2); + gs.writeRegister(GS_REG_TEX0_2, kTex0_2); + gs.writeRegister(GS_REG_PRIM, kPrimCopy); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, kUv0); + gs.writeRegister(GS_REG_XYZ2, kXyz0); + gs.writeRegister(GS_REG_UV, kUv1); + gs.writeRegister(GS_REG_XYZ2, kXyz1); + + GSFrameReg preferredSource{}; + uint32_t preferredDestFbp = 0u; + t.IsTrue(gs.getPreferredDisplaySource(preferredSource, preferredDestFbp), + "fullscreen display-copy sprites should record their source frame for host presentation"); + t.Equals(preferredDestFbp, 150u, + "preferred presentation tracking should target the copied display page"); + t.Equals(preferredSource.fbp, 0u, + "preferred presentation tracking should expose the copy source frame base"); + t.Equals(preferredSource.fbw, 10u, + "preferred presentation tracking should expose the copy source width"); + t.Equals(static_cast(preferredSource.psm), static_cast(GS_PSM_CT32), + "preferred presentation tracking should expose the copy source format"); + + gs.writeRegister(GS_REG_PRIM, static_cast(GS_PRIM_POINT) | (1ull << 9)); + gs.writeRegister(GS_REG_RGBAQ, 0xFFFFFFFFull); + gs.writeRegister(GS_REG_XYZ2, kXyz0); + + t.IsFalse(gs.getPreferredDisplaySource(preferredSource, preferredDestFbp), + "non-copy primitives that touch the display target should invalidate the preferred presentation source"); + }); + + tc.Run("latched host presentation frame stays stable until the next latch", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 1ull; + regs.dispfb1 = + 150ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (447ull << 44); + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kDisplayPixel = 0x00332211u; + constexpr uint32_t kSourcePixel = 0x00665544u; + constexpr uint32_t kUpdatedSourcePixel = 0x00998877u; + writeReferenceFramePSMCT32Pixel(vram, 150u, 10u, 0u, 0u, kDisplayPixel); + std::memcpy(vram.data() + 0u, &kSourcePixel, sizeof(kSourcePixel)); + + constexpr uint64_t kFrame2 = + 150ull | + (10ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor2 = + (0ull << 0) | + (639ull << 16) | + (0ull << 32) | + (479ull << 48); + constexpr uint64_t kXYOffset2 = + (static_cast(1728u << 4) << 0) | + (static_cast(1808u << 4) << 32); + constexpr uint64_t kAlpha2 = 0x6000000064ull; + constexpr uint64_t kTest2 = 0x30000ull; + constexpr uint64_t kTex0_2 = + (0ull << 0) | + (10ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (10ull << 26) | + (9ull << 30) | + (1ull << 34); + constexpr uint64_t kPrimCopy = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 6) | + (1ull << 8) | + (1ull << 9); + constexpr uint64_t kXyz0 = + (static_cast(1728u << 4) << 0) | + (static_cast(1808u << 4) << 16) | + (256ull << 32); + constexpr uint64_t kXyz1 = + (static_cast(2368u << 4) << 0) | + (static_cast(2288u << 4) << 16) | + (256ull << 32); + constexpr uint64_t kUv0 = + (8ull << 0) | + (8ull << 16); + constexpr uint64_t kUv1 = + ((8ull + (640ull * 16ull)) << 0) | + ((8ull + (480ull * 16ull)) << 16); + + gs.writeRegister(GS_REG_FRAME_2, kFrame2); + gs.writeRegister(GS_REG_SCISSOR_2, kScissor2); + gs.writeRegister(GS_REG_XYOFFSET_2, kXYOffset2); + gs.writeRegister(GS_REG_ALPHA_2, kAlpha2); + gs.writeRegister(GS_REG_TEST_2, kTest2); + gs.writeRegister(GS_REG_TEX0_2, kTex0_2); + gs.writeRegister(GS_REG_PRIM, kPrimCopy); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, kUv0); + gs.writeRegister(GS_REG_XYZ2, kXyz0); + gs.writeRegister(GS_REG_UV, kUv1); + gs.writeRegister(GS_REG_XYZ2, kXyz1); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + uint32_t displayFbp = 0u; + uint32_t sourceFbp = 0u; + bool usedPreferred = false; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, + latchedWidth, + latchedHeight, + &displayFbp, + &sourceFbp, + &usedPreferred), + "host presentation latch should produce a readable frame"); + t.Equals(displayFbp, 150u, + "latched host presentation should remember the selected display page"); + t.Equals(sourceFbp, 0u, + "latched host presentation should switch to the fullscreen copy source"); + t.IsTrue(usedPreferred, + "latched host presentation should record when it used the preferred copy source"); + t.Equals(latchedWidth, 640u, + "latched host presentation should preserve display width"); + t.Equals(latchedHeight, 448u, + "latched host presentation should preserve display height"); + t.Equals(static_cast(latchedFrame[0]), 0x44u, + "latched host presentation should expose the source frame RGB data"); + t.Equals(static_cast(latchedFrame[1]), 0x55u, + "latched host presentation should preserve the source frame green channel"); + t.Equals(static_cast(latchedFrame[2]), 0x66u, + "latched host presentation should preserve the source frame blue channel"); + t.Equals(static_cast(latchedFrame[3]), 0xFFu, + "latched host presentation should normalize framebuffer alpha for host upload"); + + std::memcpy(vram.data() + 0u, &kUpdatedSourcePixel, sizeof(kUpdatedSourcePixel)); + + std::vector staleFrame; + uint32_t staleWidth = 0u; + uint32_t staleHeight = 0u; + t.IsTrue(gs.copyLatchedHostPresentationFrame(staleFrame, staleWidth, staleHeight), + "latched host presentation should remain readable without relatching"); + t.Equals(static_cast(staleFrame[0]), 0x44u, + "latched host presentation should stay stable until the next latch"); + + gs.latchHostPresentationFrame(); + + std::vector refreshedFrame; + uint32_t refreshedWidth = 0u; + uint32_t refreshedHeight = 0u; + t.IsTrue(gs.copyLatchedHostPresentationFrame(refreshedFrame, refreshedWidth, refreshedHeight), + "latched host presentation should refresh after a new latch"); + t.Equals(static_cast(refreshedFrame[0]), 0x77u, + "relatching should pick up the updated source frame contents"); + }); + + tc.Run("latched host presentation reads preferred CT32 source with GS swizzle", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 1ull; + regs.dispfb1 = + 150ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (447ull << 44); + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kSourceTbp0 = 64u; + constexpr uint32_t kSourcePixelRow1 = 0x00665544u; + constexpr uint32_t kDisplayPixelRow1 = 0x00CCBBAAu; + const uint32_t swizzledSourceOff = GSPSMCT32::addrPSMCT32(kSourceTbp0, 10u, 0u, 1u); + std::memcpy(vram.data() + swizzledSourceOff, &kSourcePixelRow1, sizeof(kSourcePixelRow1)); + writeReferenceFramePSMCT32Pixel(vram, 150u, 10u, 0u, 1u, kDisplayPixelRow1); + + constexpr uint64_t kFrame2 = + 150ull | + (10ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor2 = + (0ull << 0) | + (639ull << 16) | + (0ull << 32) | + (479ull << 48); + constexpr uint64_t kXYOffset2 = + (static_cast(1728u << 4) << 0) | + (static_cast(1808u << 4) << 32); + constexpr uint64_t kAlpha2 = 0x6000000064ull; + constexpr uint64_t kTest2 = 0x30000ull; + constexpr uint64_t kTex0_2 = + (static_cast(kSourceTbp0) << 0) | + (10ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (10ull << 26) | + (9ull << 30) | + (1ull << 34); + constexpr uint64_t kPrimCopy = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 6) | + (1ull << 8) | + (1ull << 9); + constexpr uint64_t kXyz0 = + (static_cast(1728u << 4) << 0) | + (static_cast(1808u << 4) << 16) | + (256ull << 32); + constexpr uint64_t kXyz1 = + (static_cast(2368u << 4) << 0) | + (static_cast(2288u << 4) << 16) | + (256ull << 32); + constexpr uint64_t kUv0 = + (8ull << 0) | + (8ull << 16); + constexpr uint64_t kUv1 = + ((8ull + (640ull * 16ull)) << 0) | + ((8ull + (480ull * 16ull)) << 16); + + gs.writeRegister(GS_REG_FRAME_2, kFrame2); + gs.writeRegister(GS_REG_SCISSOR_2, kScissor2); + gs.writeRegister(GS_REG_XYOFFSET_2, kXYOffset2); + gs.writeRegister(GS_REG_ALPHA_2, kAlpha2); + gs.writeRegister(GS_REG_TEST_2, kTest2); + gs.writeRegister(GS_REG_TEX0_2, kTex0_2); + gs.writeRegister(GS_REG_PRIM, kPrimCopy); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, kUv0); + gs.writeRegister(GS_REG_XYZ2, kXyz0); + gs.writeRegister(GS_REG_UV, kUv1); + gs.writeRegister(GS_REG_XYZ2, kXyz1); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + uint32_t displayFbp = 0u; + uint32_t sourceFbp = 0u; + bool usedPreferred = false; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, + latchedWidth, + latchedHeight, + &displayFbp, + &sourceFbp, + &usedPreferred), + "preferred-source presentation should produce a host frame"); + t.IsTrue(usedPreferred, + "preferred-source presentation should use the fullscreen copy source"); + t.Equals(displayFbp, 150u, + "preferred-source presentation should still target the display page"); + t.Equals(sourceFbp, kSourceTbp0, + "preferred-source presentation should report the CT32 source frame"); + + const size_t row1Off = 640u * 4u; + t.Equals(static_cast(latchedFrame[row1Off + 0u]), 0x44u, + "preferred-source presentation should read row 1 red from the swizzled CT32 source"); + t.Equals(static_cast(latchedFrame[row1Off + 1u]), 0x55u, + "preferred-source presentation should read row 1 green from the swizzled CT32 source"); + t.Equals(static_cast(latchedFrame[row1Off + 2u]), 0x66u, + "preferred-source presentation should read row 1 blue from the swizzled CT32 source"); + t.Equals(static_cast(latchedFrame[row1Off + 3u]), 0xFFu, + "preferred-source presentation should normalize row 1 alpha for the host frame"); + }); + + tc.Run("latched host presentation reads direct CT32 display frames with GS swizzle", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 0x0001ull; + regs.dispfb1 = + 150ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (447ull << 44); + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kRow1Pixel = + 0x44u | + (0x55u << 8) | + (0x66u << 16) | + (0x77u << 24); + constexpr uint32_t kLinearGarbageRow1 = + 0xAAu | + (0xBBu << 8) | + (0xCCu << 16) | + (0xDDu << 24); + constexpr size_t kHostRow1Off = 640u * 4u; + + writeReferenceFramePSMCT32Pixel(vram, 150u, 10u, 0u, 1u, kRow1Pixel); + std::memcpy(vram.data() + (150u * 8192u) + kHostRow1Off, &kLinearGarbageRow1, sizeof(kLinearGarbageRow1)); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + uint32_t displayFbp = 0u; + bool usedPreferred = false; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, + latchedWidth, + latchedHeight, + &displayFbp, + nullptr, + &usedPreferred), + "direct CT32 display presentation should produce a host frame"); + t.Equals(displayFbp, 150u, + "direct CT32 presentation should report the active display page"); + t.IsFalse(usedPreferred, + "direct CT32 presentation should not claim it used a preferred copy source"); + t.Equals(static_cast(latchedFrame[kHostRow1Off + 0u]), 0x44u, + "direct CT32 presentation should read row 1 red from the GS-swizzled display page"); + t.Equals(static_cast(latchedFrame[kHostRow1Off + 1u]), 0x55u, + "direct CT32 presentation should read row 1 green from the GS-swizzled display page"); + t.Equals(static_cast(latchedFrame[kHostRow1Off + 2u]), 0x66u, + "direct CT32 presentation should read row 1 blue from the GS-swizzled display page"); + t.Equals(static_cast(latchedFrame[kHostRow1Off + 3u]), 0xFFu, + "direct CT32 presentation should normalize row 1 alpha for the host frame"); + }); + + tc.Run("latched host presentation merges both enabled PMODE circuits", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 0x8007ull; + regs.dispfb1 = + 150ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (447ull << 44); + regs.dispfb2 = + 0ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display2 = regs.display1; + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kCircuit1Pixel = + 200u | + (0u << 8) | + (0u << 16) | + (64u << 24); + constexpr uint32_t kCircuit2Pixel = + 0u | + (0u << 8) | + (200u << 16) | + (255u << 24); + + writeReferenceFramePSMCT32Pixel(vram, 150u, 10u, 0u, 0u, kCircuit1Pixel); + std::memcpy(vram.data(), &kCircuit2Pixel, sizeof(kCircuit2Pixel)); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + uint32_t displayFbp = 0u; + bool usedPreferred = false; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, + latchedWidth, + latchedHeight, + &displayFbp, + nullptr, + &usedPreferred), + "dual-circuit PMODE presentation should produce a host frame"); + t.Equals(displayFbp, 150u, + "dual-circuit presentation should still report the primary display page"); + t.IsFalse(usedPreferred, + "dual-circuit PMODE presentation should not bypass the first circuit with the preferred-copy shortcut"); + t.Equals(latchedWidth, 640u, + "dual-circuit presentation should preserve the display width"); + t.Equals(latchedHeight, 448u, + "dual-circuit presentation should preserve the display height"); + t.Equals(static_cast(latchedFrame[0]), 100u, + "dual-circuit presentation should blend the first circuit red channel over the second circuit"); + t.Equals(static_cast(latchedFrame[1]), 0u, + "dual-circuit presentation should preserve a zero green channel"); + t.Equals(static_cast(latchedFrame[2]), 100u, + "dual-circuit presentation should blend the second circuit blue channel under the first circuit"); + t.Equals(static_cast(latchedFrame[3]), 0xFFu, + "dual-circuit presentation should normalize the final host alpha"); + }); + + tc.Run("latched host presentation normalizes alpha for single-circuit display", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 0x0001ull; + regs.dispfb1 = + 150ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (447ull << 44); + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kPixel = + 0x22u | + (0x44u << 8) | + (0x66u << 16) | + (0x01u << 24); + writeReferenceFramePSMCT32Pixel(vram, 150u, 10u, 0u, 0u, kPixel); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, latchedWidth, latchedHeight), + "single-circuit presentation should produce a host frame"); + t.Equals(static_cast(latchedFrame[0]), 0x22u, + "single-circuit presentation should preserve the red channel"); + t.Equals(static_cast(latchedFrame[1]), 0x44u, + "single-circuit presentation should preserve the green channel"); + t.Equals(static_cast(latchedFrame[2]), 0x66u, + "single-circuit presentation should preserve the blue channel"); + t.Equals(static_cast(latchedFrame[3]), 0xFFu, + "single-circuit presentation should upload an opaque host alpha"); + }); + + tc.Run("latched host presentation preserves 480-line display height", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 0x0001ull; + regs.dispfb1 = + 150ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (479ull << 44); + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kLastRowPixel = + 0x12u | + (0x34u << 8) | + (0x56u << 16) | + (0x78u << 24); + writeReferenceFramePSMCT32Pixel(vram, 150u, 10u, 0u, 479u, kLastRowPixel); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, latchedWidth, latchedHeight), + "480-line single-circuit presentation should produce a host frame"); + t.Equals(latchedWidth, 640u, + "480-line presentation should preserve the display width"); + t.Equals(latchedHeight, 480u, + "480-line presentation should preserve the full display height"); + + const size_t lastRowOffset = static_cast(479u) * 640u * 4u; + t.Equals(static_cast(latchedFrame[lastRowOffset + 0u]), 0x12u, + "480-line presentation should keep the last row red channel"); + t.Equals(static_cast(latchedFrame[lastRowOffset + 1u]), 0x34u, + "480-line presentation should keep the last row green channel"); + t.Equals(static_cast(latchedFrame[lastRowOffset + 2u]), 0x56u, + "480-line presentation should keep the last row blue channel"); + t.Equals(static_cast(latchedFrame[lastRowOffset + 3u]), 0xFFu, + "single-circuit presentation should normalize the last row alpha"); + }); - uint64_t dispfb1 = dispfb0; - dispfb1 = (dispfb1 & ~0x1FFull) | 151ull; - std::memcpy(rdram.data() + kEnvAddr + kDispEnvSize + kDispFbOffset, &dispfb1, sizeof(dispfb1)); + tc.Run("latched host presentation line-doubles interlaced field output", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 0x0001ull; + regs.smode2 = 0x0001ull; // interlaced, field mode + regs.dispfb1 = + 0ull | + (10ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (639ull << 32) | + (447ull << 44); - std::memset(&ctx, 0, sizeof(ctx)); - setRegU32(ctx, 4, kEnvAddr); - setRegU32(ctx, 5, 1u); - ps2_stubs::sceGsSwapDBuffDc(rdram.data(), &ctx, &runtime); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); - t.Equals(runtime.memory().gs().dispfb1 & 0x1FFull, 151ull, - "sceGsSwapDBuffDc should program GS to the selected display page"); - t.Equals((runtime.memory().gs().display1 >> 32) & 0x0FFFull, 639ull, - "sceGsSwapDBuffDc should preserve the display width from the seeded env"); + constexpr uint32_t kLine0 = 0x000000FFu; + constexpr uint32_t kLine1 = 0x0000FF00u; + constexpr uint32_t kLine2 = 0x00FF0000u; + constexpr uint32_t kLine3 = 0x00FFFF00u; + writeReferencePSMCT32Pixel(vram, 0u, 10u, 0u, 0u, kLine0); + writeReferencePSMCT32Pixel(vram, 0u, 10u, 0u, 1u, kLine1); + writeReferencePSMCT32Pixel(vram, 0u, 10u, 0u, 2u, kLine2); + writeReferencePSMCT32Pixel(vram, 0u, 10u, 0u, 3u, kLine3); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, latchedWidth, latchedHeight), + "interlaced field presentation should produce a host frame"); + t.Equals(latchedWidth, 640u, + "field presentation should preserve display width"); + t.Equals(latchedHeight, 448u, + "field presentation should preserve display height"); + + auto pixelAtRow = [&](uint32_t row) -> uint32_t + { + const size_t off = static_cast(row) * 640u * 4u; + return static_cast(latchedFrame[off + 0u]) | + (static_cast(latchedFrame[off + 1u]) << 8) | + (static_cast(latchedFrame[off + 2u]) << 16); + }; + + const uint32_t row0 = pixelAtRow(0u); + const uint32_t row1 = pixelAtRow(1u); + const uint32_t row2 = pixelAtRow(2u); + const uint32_t row3 = pixelAtRow(3u); + + t.Equals(row0, row1, + "field presentation should duplicate the active field into the next scanline"); + t.Equals(row2, row3, + "field presentation should duplicate later field scanlines as well"); + t.IsTrue(row0 != row2, + "field presentation should still preserve different source content across field rows"); }); tc.Run("GIF PACKED A+D writes DISPFB1 and DISPLAY1 privileged registers", [](TestCase &t) @@ -256,7 +1465,31 @@ void register_ps2_gs_tests() t.Equals(regs.display1, display1, "A+D should write GS DISPLAY1"); }); - tc.Run("PSMT4 address mapping matches Veronica Conv4to32 layout", [](TestCase &t) + tc.Run("GIF PACKED A+D writes DISPFB2 and DISPLAY2 privileged registers", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + std::vector packet; + appendU64(packet, makeGifTag(2u, GIF_FMT_PACKED, 1u, true)); + appendU64(packet, 0x0Eull); // REGS[0] = A+D + + const uint64_t dispfb2 = 0x2222333344445555ull; + const uint64_t display2 = 0x6666777788889999ull; + appendU64(packet, dispfb2); + appendU64(packet, 0x5Bull); // DISPFB2 + appendU64(packet, display2); + appendU64(packet, 0x5Cull); // DISPLAY2 + + gs.processGIFPacket(packet.data(), static_cast(packet.size())); + + t.Equals(regs.dispfb2, dispfb2, "A+D should write GS DISPFB2"); + t.Equals(regs.display2, display2, "A+D should write GS DISPLAY2"); + }); + + tc.Run("PSMT4 address mapping matches GS manual layout", [](TestCase &t) { constexpr uint32_t kBaseBlock = 0u; constexpr uint32_t kWidth = 2u; // One 128x128 PSMT4 page. @@ -265,24 +1498,175 @@ void register_ps2_gs_tests() "PSMT4 origin should map to nibble offset 0"); t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 1u, 0u), 8u, "PSMT4 x=1 should advance to the next packed nibble group"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 0u, 1u), 512u, - "PSMT4 second source row should land on the next CT32 row stride"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 0u, 2u), 33u, - "PSMT4 third source row should keep Veronica's interleaved ordering"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 0u, 3u), 545u, - "PSMT4 fourth source row should include both interleave and CT32 row stride"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 31u, 15u), 3647u, - "PSMT4 final texel in the first 32x16 block should match Veronica's block layout"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 32u, 0u), 4096u, - "PSMT4 x=32 should advance to the next CT32 block column"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 32u, 16u), 4160u, - "PSMT4 x=32,y=16 should include both block-column and block-row offsets"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 64u, 0u), 8192u, - "PSMT4 x=64 should advance to the third block column in the page"); - t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 96u, 112u), 12736u, - "PSMT4 bottom-right block origin should match Veronica's page permutation"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 0u, 1u), 16u, + "PSMT4 second source row should follow the manual's row packing"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 0u, 2u), 65u, + "PSMT4 third source row should include the manual's odd-row permutation"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 0u, 3u), 81u, + "PSMT4 fourth source row should stay in the first block's manual column layout"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 31u, 15u), 511u, + "PSMT4 final texel in the first 32x16 block should land at the end of the block"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 32u, 0u), 1024u, + "PSMT4 x=32 should advance to the next swizzled block in the page"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 32u, 16u), 1536u, + "PSMT4 x=32,y=16 should follow the manual's second block-row permutation"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 64u, 0u), 4096u, + "PSMT4 x=64 should advance to the third swizzled block column in the page"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 96u, 112u), 15872u, + "PSMT4 bottom-right block origin should match the manual's page permutation"); t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 127u, 127u), 16383u, "PSMT4 final texel in a 128x128 page should land at the end of the page"); + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, 128u, 0u), 16384u, + "PSMT4 x=128 should advance to the next page of nibble addresses"); + }); + + tc.Run("PSMT4 large atlases keep manual page layout across 512x512 textures", [](TestCase &t) + { + constexpr uint32_t kBaseBlock = 64u; + constexpr uint32_t kWidth = 8u; // 512 pixel-wide T4 atlas, like Veronica font pages. + constexpr uint32_t kCoords[][2] = { + {0u, 0u}, + {31u, 15u}, + {32u, 0u}, + {95u, 31u}, + {127u, 127u}, + {128u, 0u}, + {255u, 127u}, + {256u, 0u}, + {383u, 127u}, + {384u, 128u}, + {511u, 511u}, + }; + + for (const auto &coord : kCoords) + { + const uint32_t x = coord[0]; + const uint32_t y = coord[1]; + t.Equals(GSPSMT4::addrPSMT4(kBaseBlock, kWidth, x, y), + referenceAddrPSMT4(kBaseBlock, kWidth, x, y), + "PSMT4 512x512 atlas mapping should match the GS manual for every sampled page boundary"); + } + }); + + tc.Run("GS T4 triangle sampling reads manual-layout texels from a 512x512 atlas", [](TestCase &t) + { + constexpr uint32_t kTexTbp = 64u; + constexpr uint32_t kClutCbp = 128u; + constexpr uint64_t kFrame = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (4ull << 16) | + (0ull << 32) | + (4ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (8ull << 14) | + (static_cast(GS_PSM_T4) << 20) | + (9ull << 26) | + (9ull << 30) | + (1ull << 34) | + (1ull << 35) | + (static_cast(kClutCbp) << 37) | + (static_cast(GS_PSM_CT32) << 51) | + (1ull << 55); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_TRIANGLE) | + (1ull << 4); + constexpr uint64_t kRgbaq = 0x3F80000080808080ull; + + auto packFloat = [](float value) -> uint32_t + { + uint32_t bits = 0u; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; + }; + + auto packSt = [&](float s, float tVal) -> uint64_t + { + return static_cast(packFloat(s)) | + (static_cast(packFloat(tVal)) << 32); + }; + + const struct SampleCase + { + uint32_t x; + uint32_t y; + uint8_t index; + uint32_t color; + } cases[] = { + {5u, 5u, 1u, 0xFF0000FFu}, + {129u, 5u, 2u, 0xFF00FF00u}, + {257u, 5u, 3u, 0xFFFF0000u}, + {385u, 129u, 4u, 0xFFFFFFFFu}, + }; + + for (const auto &sample : cases) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + writeReferencePSMT4Texel(vram, kTexTbp, 8u, sample.x, sample.y, sample.index); + const uint32_t clutOff = referenceAddrPSMCT32(kClutCbp, 1u, sample.index, 0u); + std::memcpy(vram.data() + clutOff, &sample.color, sizeof(sample.color)); + + gs.writeRegister(GS_REG_FRAME_1, kFrame); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_TEX1_1, 0ull); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, kRgbaq); + + const float s = (static_cast(sample.x) + 0.25f) / 512.0f; + const float tVal = (static_cast(sample.y) + 0.25f) / 512.0f; + gs.writeRegister(GS_REG_ST, packSt(s, tVal)); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_ST, packSt(s, tVal)); + gs.writeRegister(GS_REG_XYZ2, (64ull << 0) | (0ull << 16)); + gs.writeRegister(GS_REG_ST, packSt(s, tVal)); + gs.writeRegister(GS_REG_XYZ2, (0ull << 0) | (64ull << 16)); + + const uint32_t pixel = readReferencePSMCT32Pixel(vram, 0u, 1u, 1u, 1u); + t.Equals(pixel, sample.color, + "T4 triangle sampling should fetch the manual-layout atlas texel from the correct 128x128 page"); + } + }); + + tc.Run("PSMT8 address mapping matches Veronica Conv8to32 layout", [](TestCase &t) + { + constexpr uint32_t kBaseBlock = 0u; + constexpr uint32_t kWidth = 2u; // One 128x64 PSMT8 page. + + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 0u, 0u), 0u, + "PSMT8 origin should map to byte offset 0"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 1u, 0u), 4u, + "PSMT8 x=1 should follow Veronica's Conv8to32 byte interleave"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 0u, 1u), 8u, + "PSMT8 second source row should land on the next Conv8to32 row stride"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 0u, 2u), 33u, + "PSMT8 third source row should preserve Veronica's odd-row shuffle"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 0u, 3u), 41u, + "PSMT8 fourth source row should preserve Veronica's alternating block rows"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 15u, 15u), 255u, + "PSMT8 final texel in the first 16x16 block should end at byte 255"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 16u, 0u), 256u, + "PSMT8 x=16 should advance to the next 16x16 block"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 16u, 16u), 768u, + "PSMT8 x=16,y=16 should include both block-column and block-row offsets"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 32u, 0u), 1024u, + "PSMT8 x=32 should advance to the third block column in the page"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 64u, 0u), 4096u, + "PSMT8 x=64 should advance to the second page half"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 96u, 48u), 7680u, + "PSMT8 lower-right interior block should follow Veronica's page permutation"); + t.Equals(GSPSMT8::addrPSMT8(kBaseBlock, kWidth, 127u, 63u), 8191u, + "PSMT8 final texel in a 128x64 page should land at the final byte"); }); tc.Run("GIF REGLIST with odd register count consumes 128-bit padding before next tag", [](TestCase &t) @@ -322,12 +1706,16 @@ void register_ps2_gs_tests() gs.processGIFPacket(packet.data(), static_cast(packet.size())); bool imageOk = true; - for (uint32_t i = 0; i < 16u; ++i) + for (uint32_t x = 0; x < 4u && imageOk; ++x) { - if (vram[i] != payload[i]) + const uint32_t off = referenceAddrPSMCT32(0u, 1u, x, 0u); + for (uint32_t c = 0; c < 4u; ++c) { - imageOk = false; - break; + if (vram[off + c] != payload[x * 4u + c]) + { + imageOk = false; + break; + } } } t.IsTrue(imageOk, "odd REGLIST payload should not corrupt alignment of the following IMAGE tag"); @@ -372,12 +1760,16 @@ void register_ps2_gs_tests() gs.processGIFPacket(packet.data(), static_cast(packet.size())); bool imageOk = true; - for (uint32_t i = 0; i < 16u; ++i) + for (uint32_t x = 0; x < 4u && imageOk; ++x) { - if (vram[i] != payload[i]) + const uint32_t off = referenceAddrPSMCT32(0u, 1u, x, 0u); + for (uint32_t c = 0; c < 4u; ++c) { - imageOk = false; - break; + if (vram[off + c] != payload[x * 4u + c]) + { + imageOk = false; + break; + } } } t.IsTrue(imageOk, "NREG=0 REGLIST should consume 16 data words and keep following tag aligned"); @@ -441,12 +1833,22 @@ void register_ps2_gs_tests() gs.processGIFPacket(packet.data(), static_cast(packet.size())); bool same = true; - for (size_t i = 0; i < 8u; ++i) + for (uint32_t y = 0; y < 2u && same; ++y) { - if (vram[i] != payload[i] || vram[256u + i] != payload[8u + i]) + for (uint32_t x = 0; x < 2u; ++x) { - same = false; - break; + const uint32_t pixelIndex = y * 2u + x; + const uint32_t off = referenceAddrPSMCT32(0u, 1u, x, y); + for (uint32_t c = 0; c < 4u; ++c) + { + if (vram[off + c] != payload[pixelIndex * 4u + c]) + { + same = false; + break; + } + } + if (!same) + break; } } t.IsTrue(same, "GIF IMAGE transfer should write payload bytes into GS VRAM"); @@ -458,9 +1860,13 @@ void register_ps2_gs_tests() GS gs; gs.init(vram.data(), static_cast(vram.size()), nullptr); - for (uint32_t i = 0; i < 16u; ++i) + for (uint32_t x = 0; x < 4u; ++x) { - vram[i] = static_cast(0xA0u + i); + const uint32_t off = referenceAddrPSMCT32(0u, 1u, x, 0u); + for (uint32_t c = 0; c < 4u; ++c) + { + vram[off + c] = static_cast(0xA0u + x * 4u + c); + } } const uint64_t bitblt = @@ -625,6 +2031,114 @@ void register_ps2_gs_tests() } }); + tc.Run("GS CT32 upload aliases cleanly into later PSMT8 sampling", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexWidth = 128u; + constexpr uint32_t kTexHeight = 64u; + constexpr uint32_t kUploadWidth = 64u; + constexpr uint32_t kUploadHeight = 32u; + constexpr uint32_t kTexTbp = 0u; + constexpr uint32_t kTexTbw = 2u; + + std::vector source(kTexWidth * kTexHeight, 0u); + for (uint32_t i = 0; i < source.size(); ++i) + { + source[i] = static_cast((i * 37u + 11u) & 0xFFu); + } + + std::vector rawToUpload(8192u, 0xFFFFu); + for (uint32_t y = 0; y < kUploadHeight; ++y) + { + for (uint32_t x = 0; x < kUploadWidth; ++x) + { + const uint32_t rawBase = referenceAddrPSMCT32(kTexTbp, 1u, x, y); + const uint32_t uploadBase = ((y * kUploadWidth) + x) * 4u; + for (uint32_t c = 0; c < 4u; ++c) + { + rawToUpload[rawBase + c] = static_cast(uploadBase + c); + } + } + } + + bool inverseComplete = true; + for (uint16_t byteOff : rawToUpload) + { + if (byteOff == 0xFFFFu) + { + inverseComplete = false; + break; + } + } + t.IsTrue(inverseComplete, + "reference CT32 raw-to-upload map should cover every byte in a 64x32 CT32 page"); + + std::vector upload(kUploadWidth * kUploadHeight * 4u, 0u); + for (uint32_t y = 0; y < kTexHeight; ++y) + { + for (uint32_t x = 0; x < kTexWidth; ++x) + { + const uint32_t texelIndex = y * kTexWidth + x; + const uint32_t rawOff = referenceAddrPSMT8(kTexTbp, kTexTbw, x, y); + const uint32_t uploadOff = rawToUpload[rawOff]; + upload[uploadOff] = source[texelIndex]; + } + } + + const uint64_t bitblt = + (static_cast(0u) << 0) | + (static_cast(1u) << 16) | + (static_cast(GS_PSM_CT32) << 24) | + (static_cast(kTexTbp) << 32) | + (static_cast(1u) << 48) | + (static_cast(GS_PSM_CT32) << 56); + gs.writeRegister(GS_REG_BITBLTBUF, bitblt); + gs.writeRegister(GS_REG_TRXPOS, 0ull); + gs.writeRegister(GS_REG_TRXREG, (static_cast(kUploadWidth) << 0) | + (static_cast(kUploadHeight) << 32)); + gs.writeRegister(GS_REG_TRXDIR, 0ull); + + std::vector packet; + appendU64(packet, makeGifTag(static_cast(upload.size() / 16u), GIF_FMT_IMAGE, 0u, true)); + appendU64(packet, 0ull); + packet.insert(packet.end(), upload.begin(), upload.end()); + gs.processGIFPacket(packet.data(), static_cast(packet.size())); + + bool aliasOk = true; + uint32_t badX = 0u; + uint32_t badY = 0u; + uint32_t got = 0u; + uint32_t expected = 0u; + for (uint32_t y = 0; y < kTexHeight && aliasOk; ++y) + { + for (uint32_t x = 0; x < kTexWidth; ++x) + { + const uint32_t texelOff = GSPSMT8::addrPSMT8(kTexTbp, kTexTbw, x, y); + got = vram[texelOff]; + expected = source[y * kTexWidth + x]; + if (got != expected) + { + aliasOk = false; + badX = x; + badY = y; + break; + } + } + } + + if (!aliasOk) + { + t.Fail("CT32 image upload should preserve Veronica's later PSMT8 sampling layout " + "(first mismatch at x=" + std::to_string(badX) + + ", y=" + std::to_string(badY) + + ", got " + std::to_string(got) + + ", expected " + std::to_string(expected) + ")"); + } + }); + tc.Run("GS PSMT4 local-local copy respects swizzled page layout", [](TestCase &t) { std::vector vram(PS2_GS_VRAM_SIZE, 0u); @@ -733,9 +2247,92 @@ void register_ps2_gs_tests() const uint32_t texByteOff = texNibbleAddr >> 1; vram[texByteOff] = static_cast((vram[texByteOff] & 0xF0u) | 0x08u); - // Veronica's ClutCopy stores logical entries 8..15 into physical slots 16..23. - std::memcpy(vram.data() + kClutCbp * 256u + 8u * 4u, &kWrongColor, sizeof(kWrongColor)); - std::memcpy(vram.data() + kClutCbp * 256u + 16u * 4u, &kExpectedColor, sizeof(kExpectedColor)); + // Veronica uploads CSM1 CLUT rows with a 64-pixel GS stride, so logical entry 8 + // resolves to row 1, column 0 after the CSM1 swizzle. + const uint32_t wrongClutOff = GSPSMCT32::addrPSMCT32(kClutCbp, 1u, 8u, 0u); + const uint32_t expectedClutOff = GSPSMCT32::addrPSMCT32(kClutCbp, 1u, 0u, 1u); + std::memcpy(vram.data() + wrongClutOff, &kWrongColor, sizeof(kWrongColor)); + std::memcpy(vram.data() + expectedClutOff, &kExpectedColor, sizeof(kExpectedColor)); + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, 0ull); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + + uint32_t pixel = 0u; + std::memcpy(&pixel, vram.data(), sizeof(pixel)); + t.Equals(pixel, kExpectedColor, + "T4 CSM1 lookup should follow Veronica's swizzled CLUT row layout for logical index 8"); + }); + + tc.Run("GS T8 CT32-uploaded CSM1 CLUT follows swizzled palette layout", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint32_t kClutCbp = 128u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_T8) << 20) | + (0ull << 26) | + (0ull << 30) | + (1ull << 34) | + (1ull << 35) | + (static_cast(kClutCbp) << 37) | + (static_cast(GS_PSM_CT32) << 51); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | // TME + (1ull << 8); // FST + constexpr uint64_t kClutBitblt = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24) | + (static_cast(kClutCbp) << 32) | + (1ull << 48) | + (static_cast(GS_PSM_CT32) << 56); + constexpr uint64_t kClutRect = + (16ull << 0) | + (2ull << 32); + constexpr uint32_t kExpectedColor = 0x80FFFFFFu; + + const uint32_t texOff = GSPSMT8::addrPSMT8(kTexTbp, 1u, 0u, 0u); + vram[texOff] = 8u; + + std::vector clut(32u, 0u); + clut[16] = kExpectedColor; + + gs.writeRegister(GS_REG_BITBLTBUF, kClutBitblt); + gs.writeRegister(GS_REG_TRXPOS, 0ull); + gs.writeRegister(GS_REG_TRXREG, kClutRect); + gs.writeRegister(GS_REG_TRXDIR, 0ull); + + std::vector packet; + appendU64(packet, + makeGifTag(static_cast((clut.size() * sizeof(uint32_t)) / 16u), + GIF_FMT_IMAGE, + 0u, + true)); + appendU64(packet, 0ull); + const size_t payloadOffset = packet.size(); + packet.resize(payloadOffset + clut.size() * sizeof(uint32_t)); + std::memcpy(packet.data() + payloadOffset, clut.data(), clut.size() * sizeof(uint32_t)); + gs.processGIFPacket(packet.data(), static_cast(packet.size())); gs.writeRegister(GS_REG_FRAME_1, kFrameReg); gs.writeRegister(GS_REG_SCISSOR_1, 0ull); @@ -753,7 +2350,99 @@ void register_ps2_gs_tests() uint32_t pixel = 0u; std::memcpy(&pixel, vram.data(), sizeof(pixel)); t.Equals(pixel, kExpectedColor, - "T4 CSM1 lookup should follow Veronica's swizzled CLUT layout for logical index 8"); + "T8 CSM1 CLUT sampling should read CT32-uploaded palette entries through GS swizzled addressing"); + }); + + tc.Run("GS TEX1 linear filter blends T4 STQ triangle samples", [](TestCase &t) + { + auto renderSamplePixel = [](uint64_t tex1Reg) -> uint32_t + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint32_t kClutCbp = 128u; + constexpr uint64_t kFrame = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (4ull << 16) | + (0ull << 32) | + (4ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_T4) << 20) | + (1ull << 26) | + (0ull << 30) | + (1ull << 34) | + (1ull << 35) | + (static_cast(kClutCbp) << 37) | + (static_cast(GS_PSM_CT32) << 51); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_TRIANGLE) | + (1ull << 4) | + (0ull << 8); + constexpr uint64_t kRgbaq = 0x3F80000080808080ull; + constexpr uint32_t kBlack = 0x80000000u; + constexpr uint32_t kWhite = 0x80FFFFFFu; + + writePSMT4Texel(vram, kTexTbp, 1u, 0u, 0u, 0u); + writePSMT4Texel(vram, kTexTbp, 1u, 1u, 0u, 1u); + std::memcpy(vram.data() + kClutCbp * 256u + 0u * 4u, &kBlack, sizeof(kBlack)); + std::memcpy(vram.data() + kClutCbp * 256u + 1u * 4u, &kWhite, sizeof(kWhite)); + + auto packFloat = [](float value) -> uint32_t + { + uint32_t bits = 0u; + std::memcpy(&bits, &value, sizeof(bits)); + return bits; + }; + + auto packSt = [&](float s, float tVal) -> uint64_t + { + return static_cast(packFloat(s)) | + (static_cast(packFloat(tVal)) << 32); + }; + + gs.writeRegister(GS_REG_FRAME_1, kFrame); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_TEX1_1, tex1Reg); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, kRgbaq); + gs.writeRegister(GS_REG_ST, packSt(0.0f, 0.0f)); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_ST, packSt(1.0f, 0.0f)); + gs.writeRegister(GS_REG_XYZ2, (64ull << 0) | (0ull << 16)); + gs.writeRegister(GS_REG_ST, packSt(0.0f, 0.0f)); + gs.writeRegister(GS_REG_XYZ2, (0ull << 0) | (64ull << 16)); + + return readReferencePSMCT32Pixel(vram, 0u, 1u, 1u, 1u); + }; + + constexpr uint64_t kTex1Linear = + (1ull << 5) | + (1ull << 6); + + const uint32_t nearestPixel = renderSamplePixel(0ull); + const uint32_t linearPixel = renderSamplePixel(kTex1Linear); + + t.Equals(nearestPixel, 0x80000000u, + "point sampling should keep the sampled STQ triangle pixel on texel 0"); + + const uint8_t linearR = static_cast(linearPixel & 0xFFu); + const uint8_t linearA = static_cast((linearPixel >> 24) & 0xFFu); + t.IsTrue(linearR > 0x10u && linearR < 0x70u, + "linear filtering should blend the STQ triangle sample between black and white T4 texels"); + t.Equals(linearA, static_cast(0x80u), + "linear filtering should preserve the shared opaque alpha from the CLUT entries"); }); tc.Run("GS alpha test AFAIL framebuffer-only still writes the pixel", [](TestCase &t) @@ -843,6 +2532,198 @@ void register_ps2_gs_tests() "AFAIL=RGB_ONLY should update RGB while preserving destination alpha"); }); + tc.Run("GS triangle fan subpixel quad fills rows without interior holes", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint64_t kFrame = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (31ull << 16) | + (0ull << 32) | + (31ull << 48); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_TRIFAN); + constexpr uint64_t kRgbaq = + 0xFFull | + (0xFFull << 8) | + (0xFFull << 16) | + (0x80ull << 24) | + (0x3F800000ull << 32); // q = 1.0f + auto makeXyzf = [](uint16_t x, uint16_t y) -> uint64_t + { + return static_cast(x) | + (static_cast(y) << 16); + }; + + gs.writeRegister(GS_REG_FRAME_1, kFrame); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, kRgbaq); + gs.writeRegister(GS_REG_XYZF2, makeXyzf(102u, 102u)); + gs.writeRegister(GS_REG_XYZF2, makeXyzf(420u, 102u)); + gs.writeRegister(GS_REG_XYZF2, makeXyzf(420u, 420u)); + gs.writeRegister(GS_REG_XYZF2, makeXyzf(102u, 420u)); + + bool sawFilledRow = false; + for (uint32_t y = 6u; y <= 26u; ++y) + { + int first = -1; + int last = -1; + for (uint32_t x = 6u; x <= 26u; ++x) + { + const size_t offset = (static_cast(y) * 64u + static_cast(x)) * 4u; + uint32_t pixel = 0u; + std::memcpy(&pixel, vram.data() + offset, sizeof(pixel)); + if ((pixel & 0x00FFFFFFu) != 0u) + { + if (first < 0) + { + first = static_cast(x); + } + last = static_cast(x); + } + } + + if (first < 0 || last < 0) + { + continue; + } + + sawFilledRow = true; + for (int x = first; x <= last; ++x) + { + const size_t offset = (static_cast(y) * 64u + static_cast(x)) * 4u; + uint32_t pixel = 0u; + std::memcpy(&pixel, vram.data() + offset, sizeof(pixel)); + if ((pixel & 0x00FFFFFFu) == 0u) + { + t.Fail("triangle fan quad should not leave interior holes within a covered row"); + break; + } + } + } + + t.IsTrue(sawFilledRow, + "triangle fan quad should light at least one framebuffer row"); + }); + + tc.Run("sceGsExecLoadImage and sceGsExecStoreImage roundtrip and free guest packets", [](TestCase &t) + { + PS2Runtime runtime; + t.IsTrue(runtime.memory().initialize(), "runtime memory initialize should succeed"); + uint8_t *const rdram = runtime.memory().getRDRAM(); + constexpr uint32_t kImageAddr = 0x4000u; + constexpr uint32_t kSrcAddr = 0x5000u; + constexpr uint32_t kDstAddr = 0x6000u; + + const GsImageMem image{0u, 0u, 2u, 2u, 0u, 1u, 0u}; + const uint8_t pixels[16] = { + 0x10u, 0x20u, 0x30u, 0x40u, + 0x50u, 0x60u, 0x70u, 0x80u, + 0x90u, 0xA0u, 0xB0u, 0xC0u, + 0xD0u, 0xE0u, 0xF0u, 0xFFu, + }; + + writeGsImage(rdram, kImageAddr, image); + std::memcpy(rdram + kSrcAddr, pixels, sizeof(pixels)); + + R5900Context loadCtx{}; + setRegU32(loadCtx, 4, kImageAddr); + setRegU32(loadCtx, 5, kSrcAddr); + ps2_stubs::sceGsExecLoadImage(rdram, &loadCtx, &runtime); + t.Equals(static_cast(getRegU32Test(loadCtx, 2)), 0, + "sceGsExecLoadImage should succeed for a simple CT32 upload"); + uint64_t loadTag = 0u; + std::memcpy(&loadTag, rdram + runtime.guestHeapBase(), sizeof(loadTag)); + t.Equals(loadTag, 0x1000000000008004ull, + "sceGsExecLoadImage should populate the packed A+D GIF tag in guest RAM"); + uint64_t loadReg1 = 0u; + uint64_t loadReg2 = 0u; + uint64_t loadReg3 = 0u; + uint64_t loadReg4 = 0u; + std::memcpy(&loadReg1, rdram + runtime.guestHeapBase() + 24u, sizeof(loadReg1)); + std::memcpy(&loadReg2, rdram + runtime.guestHeapBase() + 40u, sizeof(loadReg2)); + std::memcpy(&loadReg3, rdram + runtime.guestHeapBase() + 56u, sizeof(loadReg3)); + std::memcpy(&loadReg4, rdram + runtime.guestHeapBase() + 72u, sizeof(loadReg4)); + t.Equals(loadReg1, 0x50ull, "sceGsExecLoadImage should encode BITBLTBUF as A+D register 0x50"); + t.Equals(loadReg2, 0x51ull, "sceGsExecLoadImage should encode TRXPOS as A+D register 0x51"); + t.Equals(loadReg3, 0x52ull, "sceGsExecLoadImage should encode TRXREG as A+D register 0x52"); + t.Equals(loadReg4, 0x53ull, "sceGsExecLoadImage should encode TRXDIR as A+D register 0x53"); + expectGuestHeapReusable(t, runtime, + "sceGsExecLoadImage should free its temporary GIF packet"); + + R5900Context storeCtx{}; + setRegU32(storeCtx, 4, kImageAddr); + setRegU32(storeCtx, 5, kDstAddr); + ps2_stubs::sceGsExecStoreImage(rdram, &storeCtx, &runtime); + t.Equals(static_cast(getRegU32Test(storeCtx, 2)), 0, + "sceGsExecStoreImage should succeed for a matching CT32 readback"); + uint64_t storeTag = 0u; + std::memcpy(&storeTag, rdram + runtime.guestHeapBase(), sizeof(storeTag)); + t.Equals(storeTag, 0x1000000000008004ull, + "sceGsExecStoreImage should populate the packed A+D GIF tag in guest RAM"); + uint64_t storeReg1 = 0u; + uint64_t storeReg2 = 0u; + uint64_t storeReg3 = 0u; + uint64_t storeReg4 = 0u; + std::memcpy(&storeReg1, rdram + runtime.guestHeapBase() + 24u, sizeof(storeReg1)); + std::memcpy(&storeReg2, rdram + runtime.guestHeapBase() + 40u, sizeof(storeReg2)); + std::memcpy(&storeReg3, rdram + runtime.guestHeapBase() + 56u, sizeof(storeReg3)); + std::memcpy(&storeReg4, rdram + runtime.guestHeapBase() + 72u, sizeof(storeReg4)); + t.Equals(storeReg1, 0x50ull, "sceGsExecStoreImage should encode BITBLTBUF as A+D register 0x50"); + t.Equals(storeReg2, 0x51ull, "sceGsExecStoreImage should encode TRXPOS as A+D register 0x51"); + t.Equals(storeReg3, 0x52ull, "sceGsExecStoreImage should encode TRXREG as A+D register 0x52"); + t.Equals(storeReg4, 0x53ull, "sceGsExecStoreImage should encode TRXDIR as A+D register 0x53"); + expectGuestHeapReusable(t, runtime, + "sceGsExecStoreImage should free its temporary GIF packet"); + + bool roundtripOk = true; + size_t mismatchIndex = 0u; + for (size_t i = 0; i < sizeof(pixels); ++i) + { + if (rdram[kDstAddr + i] != pixels[i]) + { + roundtripOk = false; + mismatchIndex = i; + break; + } + } + if (!roundtripOk) + { + t.Fail("sceGsExecLoadImage/sceGsExecStoreImage should roundtrip CT32 pixel data " + "(first mismatch at byte " + std::to_string(mismatchIndex) + + ", got " + std::to_string(rdram[kDstAddr + mismatchIndex]) + + ", expected " + std::to_string(pixels[mismatchIndex]) + ")"); + } + }); + + tc.Run("sceGsResetGraph frees its temporary GIF packet", [](TestCase &t) + { + PS2Runtime runtime; + t.IsTrue(runtime.memory().initialize(), "runtime memory initialize should succeed"); + + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 1u); + setRegU32(ctx, 6, 2u); + setRegU32(ctx, 7, 1u); + ps2_stubs::sceGsResetGraph(rdram.data(), &ctx, &runtime); + + t.Equals(static_cast(getRegU32Test(ctx, 2)), 0, + "sceGsResetGraph should succeed in reset mode"); + expectGuestHeapReusable(t, runtime, + "sceGsResetGraph should free its temporary GIF packet"); + }); + tc.Run("sceGsSyncV waits on VBlank and reports interlaced field parity", [](TestCase &t) { notifyRuntimeStop(); diff --git a/ps2xTest/src/ps2_memory_tests.cpp b/ps2xTest/src/ps2_memory_tests.cpp index 91ce9f62..286f8d5b 100644 --- a/ps2xTest/src/ps2_memory_tests.cpp +++ b/ps2xTest/src/ps2_memory_tests.cpp @@ -1,7 +1,8 @@ #include "MiniTest.h" -#include "ps2_memory.h" -#include "ps2_gs_gpu.h" -#include "ps2_vu1.h" +#include "runtime/ps2_memory.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_gs_psmct32.h" +#include "runtime/ps2_vu1.h" #include "ps2_runtime_macros.h" #include @@ -1257,12 +1258,16 @@ void register_ps2_memory_tests() const uint8_t *vramOut = mem.getGSVRAM(); bool imageOk = true; - for (uint32_t i = 0; i < 16u; ++i) + for (uint32_t x = 0; x < 4u && imageOk; ++x) { - if (vramOut[i] != static_cast(0x70u + i)) + const uint32_t off = GSPSMCT32::addrPSMCT32(0u, 1u, x, 0u); + for (uint32_t c = 0; c < 4u; ++c) { - imageOk = false; - break; + if (vramOut[off + c] != static_cast(0x70u + x * 4u + c)) + { + imageOk = false; + break; + } } } t.IsTrue(imageOk, "VIF1 DIRECT image should update GS VRAM through GIF path2"); @@ -1331,12 +1336,16 @@ void register_ps2_memory_tests() const uint8_t *vramOut = mem.getGSVRAM(); bool imageOk = true; - for (uint32_t i = 0; i < 16u; ++i) + for (uint32_t x = 0; x < 4u && imageOk; ++x) { - if (vramOut[i] != static_cast(0x90u + i)) + const uint32_t off = GSPSMCT32::addrPSMCT32(0u, 1u, x, 0u); + for (uint32_t c = 0; c < 4u; ++c) { - imageOk = false; - break; + if (vramOut[off + c] != static_cast(0x90u + x * 4u + c)) + { + imageOk = false; + break; + } } } t.IsTrue(imageOk, "MSCAL-triggered XGKICK should route PATH1 packet into GS VRAM"); diff --git a/ps2xTest/src/ps2_recompiler_tests.cpp b/ps2xTest/src/ps2_recompiler_tests.cpp index bbaaacd4..de6c29bc 100644 --- a/ps2xTest/src/ps2_recompiler_tests.cpp +++ b/ps2xTest/src/ps2_recompiler_tests.cpp @@ -4,6 +4,7 @@ #include "ps2recomp/elf_parser.h" #include "ps2recomp/instructions.h" #include "ps2recomp/types.h" +#include "ps2_runtime_calls.h" #include #include #include @@ -158,6 +159,43 @@ void register_ps2_recompiler_tests() { MiniTest::Case("PS2Recompiler", [](TestCase &tc) { + tc.Run("game helpers are not classified as runtime stubs", [](TestCase &t) { + t.IsFalse(ps2_runtime_calls::isStubName("Pad_init"), + "Pad_init should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("Pad_set"), + "Pad_set should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("pdInitPeripheral"), + "pdInitPeripheral should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("pdGetPeripheral"), + "pdGetPeripheral should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("InitThread"), + "InitThread should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("syFree"), + "syFree should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("syMallocInit"), + "syMallocInit should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("syHwInit"), + "syHwInit should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("syHwInit2"), + "syHwInit2 should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("syRtcInit"), + "syRtcInit should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("sdDrvInit"), + "sdDrvInit should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("sdSndStopAll"), + "sdSndStopAll should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("sdSysFinish"), + "sdSysFinish should be recompiled as game code"); + t.IsFalse(ps2_runtime_calls::isStubName("iopGetArea"), + "iopGetArea should be recompiled as game code"); + t.IsTrue(ps2_runtime_calls::isStubName("builtin_set_imask"), + "builtin_set_imask should remain a runtime helper"); + t.IsTrue(ps2_runtime_calls::isStubName("getpid"), + "getpid should remain a runtime helper"); + t.IsTrue(ps2_runtime_calls::isStubName("scePadRead"), + "scePadRead should remain a runtime pad stub"); + }); + tc.Run("additional entries split at nearest discovered boundary", [](TestCase &t) { std::vector
sections = { {".text", 0x1000u, 0x3000u, 0u, true, false, false, true, nullptr} @@ -187,8 +225,8 @@ void register_ps2_recompiler_tests() size_t discovered = PS2Recompiler::DiscoverAdditionalEntryPoints( functions, decodedFunctions, sections); - t.Equals(discovered, static_cast(2), - "expected two additional entries to be discovered"); + t.Equals(discovered, static_cast(3), + "expected two mid-function targets plus the JAL return entry to be discovered"); auto findByStart = [&](uint32_t start) -> const Function* { auto it = std::find_if(functions.begin(), functions.end(), @@ -202,8 +240,10 @@ void register_ps2_recompiler_tests() const Function *entry1008 = findByStart(0x1008u); const Function *entry100C = findByStart(0x100Cu); + const Function *entry2008 = findByStart(0x2008u); t.IsNotNull(entry1008, "entry at 0x1008 should exist"); t.IsNotNull(entry100C, "entry at 0x100C should exist"); + t.IsNotNull(entry2008, "JAL return address entry at 0x2008 should exist"); if (entry1008 && entry100C) { t.Equals(entry1008->end, 0x100Cu, @@ -211,11 +251,18 @@ void register_ps2_recompiler_tests() t.Equals(entry100C->end, 0x1018u, "entry 0x100C should end at containing function end"); } + if (entry2008) + { + t.Equals(entry2008->end, 0x2010u, + "return entry 0x2008 should slice through the caller tail"); + } auto decoded1008It = decodedFunctions.find(0x1008u); auto decoded100CIt = decodedFunctions.find(0x100Cu); + auto decoded2008It = decodedFunctions.find(0x2008u); t.IsTrue(decoded1008It != decodedFunctions.end(), "decoded slice for 0x1008 should exist"); t.IsTrue(decoded100CIt != decodedFunctions.end(), "decoded slice for 0x100C should exist"); + t.IsTrue(decoded2008It != decodedFunctions.end(), "decoded slice for 0x2008 should exist"); if (decoded1008It != decodedFunctions.end()) { t.Equals(decoded1008It->second.size(), static_cast(1), @@ -231,6 +278,16 @@ void register_ps2_recompiler_tests() t.Equals(decoded100CIt->second.front().address, 0x100Cu, "entry 0x100C slice should begin at 0x100C"); } + if (decoded2008It != decodedFunctions.end()) + { + t.Equals(decoded2008It->second.size(), static_cast(2), + "return entry 0x2008 slice should keep the jump and its delay slot"); + if (!decoded2008It->second.empty()) + { + t.Equals(decoded2008It->second.front().address, 0x2008u, + "return entry 0x2008 slice should begin at the JAL fallthrough"); + } + } }); tc.Run("entry reslice trims earlier entries after late discovery", [](TestCase &t) { @@ -311,7 +368,7 @@ void register_ps2_recompiler_tests() } }); - tc.Run("same-function JAL targets get entry wrappers but J targets stay labels", [](TestCase &t) { + tc.Run("same-function JAL return addresses get entry wrappers but targets stay labels", [](TestCase &t) { std::vector
sections = { {".text", 0x1000u, 0x40u, 0u, true, false, false, true, nullptr} }; @@ -335,8 +392,11 @@ void register_ps2_recompiler_tests() functions, decodedFunctions, sections); t.Equals(discovered, static_cast(1), - "same-function JAL should create one entry while plain J stays internal"); + "same-function JAL should create only the resume entry while plain J stays internal"); + const bool hasResumeEntry = std::any_of( + functions.begin(), functions.end(), + [](const Function &fn) { return fn.start == 0x1008u; }); const bool hasCallEntry = std::any_of( functions.begin(), functions.end(), [](const Function &fn) { return fn.start == 0x100Cu; }); @@ -344,10 +404,142 @@ void register_ps2_recompiler_tests() functions.begin(), functions.end(), [](const Function &fn) { return fn.start == 0x1014u && fn.name.rfind("entry_", 0) == 0; }); - t.IsTrue(hasCallEntry, "same-function JAL target should be promoted to an entry wrapper"); + t.IsTrue(hasResumeEntry, "same-function JAL return address should be promoted to a resumable entry"); + t.IsFalse(hasCallEntry, "same-function JAL target should remain an internal label"); t.IsFalse(hasJumpEntry, "same-function J target should remain an internal label only"); }); + tc.Run("JAL return addresses get resumable entry wrappers", [](TestCase &t) { + std::vector
sections = { + {".text", 0x1000u, 0x2000u, 0u, true, false, false, true, nullptr} + }; + + std::vector functions = { + makeFunction("caller", 0x1000u, 0x1018u), + makeFunction("callee", 0x2000u, 0x2008u) + }; + + std::unordered_map> decodedFunctions; + decodedFunctions[0x1000u] = { + makeAbsJump(0x1000u, 0x2000u, OPCODE_JAL), + makeNopLike(0x1004u), + makeNopLike(0x1008u), + makeNopLike(0x100Cu), + makeJrRa(0x1010u), + makeNopLike(0x1014u) + }; + decodedFunctions[0x2000u] = { + makeJrRa(0x2000u), + makeNopLike(0x2004u) + }; + + size_t discovered = PS2Recompiler::DiscoverAdditionalEntryPoints( + functions, decodedFunctions, sections); + t.Equals(discovered, static_cast(1), + "external JAL should create one resumable entry at the caller return address"); + + auto entryIt = std::find_if(functions.begin(), functions.end(), + [](const Function &fn) { return fn.start == 0x1008u; }); + t.IsTrue(entryIt != functions.end(), "return address 0x1008 should be promoted to an entry wrapper"); + if (entryIt != functions.end()) + { + t.Equals(entryIt->end, 0x1018u, + "return-address entry should slice through the remainder of the caller"); + } + + auto decodedEntryIt = decodedFunctions.find(0x1008u); + t.IsTrue(decodedEntryIt != decodedFunctions.end(), + "decoded entry slice for the caller return address should exist"); + if (decodedEntryIt != decodedFunctions.end()) + { + t.Equals(decodedEntryIt->second.size(), static_cast(4), + "return-address entry slice should keep the caller tail"); + if (!decodedEntryIt->second.empty()) + { + t.Equals(decodedEntryIt->second.front().address, 0x1008u, + "return-address entry slice should begin at the JAL fallthrough"); + } + } + }); + + tc.Run("JAL to an already-known function still discovers the return entry", [](TestCase &t) { + std::vector
sections = { + {".text", 0x1000u, 0x2000u, 0u, true, false, false, true, nullptr} + }; + + std::vector functions = { + makeFunction("caller", 0x1000u, 0x1020u), + makeFunction("callee", 0x1100u, 0x1108u) + }; + + std::unordered_map> decodedFunctions; + decodedFunctions[0x1000u] = { + makeNopLike(0x1000u), + makeNopLike(0x1004u), + makeAbsJump(0x1008u, 0x1100u, OPCODE_JAL), + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u), + makeJrRa(0x1018u), + makeNopLike(0x101Cu) + }; + decodedFunctions[0x1100u] = { + makeJrRa(0x1100u), + makeNopLike(0x1104u) + }; + + size_t discovered = PS2Recompiler::DiscoverAdditionalEntryPoints( + functions, decodedFunctions, sections); + t.Equals(discovered, static_cast(1), + "return entry should still be discovered even when the JAL target is already registered"); + + auto entryIt = std::find_if(functions.begin(), functions.end(), + [](const Function &fn) { return fn.start == 0x1010u; }); + t.IsTrue(entryIt != functions.end(), + "return address 0x1010 should be emitted as a resumable entry"); + if (entryIt != functions.end()) + { + t.Equals(entryIt->end, 0x1020u, + "return entry should cover the remaining caller tail"); + } + }); + + tc.Run("discovery ignores synthetic entry wrappers", [](TestCase &t) { + std::vector
sections = { + {".text", 0x1000u, 0x2000u, 0u, true, false, false, true, nullptr} + }; + + std::vector functions = { + makeFunction("entry_1008", 0x1008u, 0x1020u), + makeFunction("callee", 0x1100u, 0x1108u) + }; + + std::unordered_map> decodedFunctions; + decodedFunctions[0x1008u] = { + makeAbsJump(0x1008u, 0x1100u, OPCODE_JAL), + makeNopLike(0x100Cu), + makeNopLike(0x1010u), + makeNopLike(0x1014u), + makeJrRa(0x1018u), + makeNopLike(0x101Cu) + }; + decodedFunctions[0x1100u] = { + makeJrRa(0x1100u), + makeNopLike(0x1104u) + }; + + size_t discovered = PS2Recompiler::DiscoverAdditionalEntryPoints( + functions, decodedFunctions, sections); + t.Equals(discovered, static_cast(0), + "synthetic entry wrappers should not recursively produce more entries"); + + const bool hasRecursiveResumeEntry = std::any_of( + functions.begin(), functions.end(), + [](const Function &fn) { return fn.start == 0x1010u; }); + t.IsFalse(hasRecursiveResumeEntry, + "discovery should not promote a return entry out of an existing entry wrapper"); + }); + tc.Run("entry reslice handles entries without containing function", [](TestCase &t) { std::vector functions = { makeFunction("entry_1008", 0x1008u, 0x1018u), @@ -659,6 +851,21 @@ void register_ps2_recompiler_tests() std::filesystem::remove(mapPath, removeError); }); + tc.Run("runtime call resolution includes Veronica compatibility aliases", [](TestCase &t) { + t.Equals(ps2_runtime_calls::resolveSyscallName("ReleaseAlarm"), std::string_view{"ReleaseAlarm"}, + "ReleaseAlarm should resolve as a syscall name"); + t.Equals(ps2_runtime_calls::resolveSyscallName("_ReleaseAlarm"), std::string_view{"ReleaseAlarm"}, + "underscore ReleaseAlarm alias should resolve to ReleaseAlarm"); + t.Equals(ps2_runtime_calls::resolveSyscallName("EnableCache"), std::string_view{"EnableCache"}, + "EnableCache should resolve as a syscall name"); + t.Equals(ps2_runtime_calls::resolveSyscallName("DisableCache"), std::string_view{"DisableCache"}, + "DisableCache should resolve as a syscall name"); + t.Equals(ps2_runtime_calls::resolveStubName("isceSifSetDma"), std::string_view{"isceSifSetDma"}, + "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"); + }); + tc.Run("respect max length for .cpp filenames", [](TestCase& t) { t.IsTrue(PS2Recompiler::ClampFilenameLength("ReallyLongFunctionNameReallyLongFunctionNameReallyLongFunctionName_0x12345678",".cpp",50).length() <= 50,"Function name must be max 50 characters"); diff --git a/ps2xTest/src/ps2_runtime_expansion_tests.cpp b/ps2xTest/src/ps2_runtime_expansion_tests.cpp index 6632d668..e0008e25 100644 --- a/ps2xTest/src/ps2_runtime_expansion_tests.cpp +++ b/ps2xTest/src/ps2_runtime_expansion_tests.cpp @@ -4,10 +4,11 @@ #include "ps2recomp/r5900_decoder.h" #include "ps2recomp/types.h" #include "ps2_runtime.h" -#include "ps2_memory.h" +#include "runtime/ps2_memory.h" #include "ps2_syscalls.h" #include "ps2_stubs.h" -#include "ps2_gs_gpu.h" +#include "runtime/ps2_gs_gpu.h" +#include "runtime/ps2_gs_psmct32.h" #include "ps2_runtime_macros.h" #include @@ -78,8 +79,7 @@ namespace uint32_t frameOffsetBytes(uint32_t x, uint32_t y, uint32_t fbw) { - const uint32_t stride = fbw * 64u * 4u; // CT32 - return y * stride + x * 4u; + return GSPSMCT32::addrPSMCT32(0u, (fbw != 0u) ? fbw : 1u, x, y); } void testRuntimeWorkerLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -211,6 +211,7 @@ namespace gAsyncCallbackObservedGp.store(::getRegU32(ctx, 28), std::memory_order_release); ctx->pc = 0u; } + } void register_ps2_runtime_expansion_tests() @@ -459,6 +460,227 @@ void register_ps2_runtime_expansion_tests() notifyRuntimeStop(); }); + tc.Run("MPEG init and callback stubs return success instead of TODO errors", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0u); + ps2_stubs::resetMpegStubState(); + + R5900Context initCtx{}; + ps2_stubs::sceMpegInit(rdram.data(), &initCtx, nullptr); + t.Equals(getRegS32(initCtx, 2), 0, + "sceMpegInit should succeed so games can continue through movie setup"); + + R5900Context addCtx0{}; + setRegU32(addCtx0, 4, 0x00123000u); + setRegU32(addCtx0, 5, 1u); + setRegU32(addCtx0, 6, 0x00124000u); + setRegU32(addCtx0, 7, 0u); + ps2_stubs::sceMpegAddCallback(rdram.data(), &addCtx0, nullptr); + t.Equals(getRegS32(addCtx0, 2), 1, + "first sceMpegAddCallback should hand back a non-error callback handle"); + + R5900Context addCtx1{}; + setRegU32(addCtx1, 4, 0x00123000u); + setRegU32(addCtx1, 5, 2u); + setRegU32(addCtx1, 6, 0x00124010u); + setRegU32(addCtx1, 7, 0u); + ps2_stubs::sceMpegAddCallback(rdram.data(), &addCtx1, nullptr); + t.Equals(getRegS32(addCtx1, 2), 2, + "subsequent sceMpegAddCallback calls should keep succeeding"); + + R5900Context reinitCtx{}; + ps2_stubs::sceMpegInit(rdram.data(), &reinitCtx, nullptr); + + R5900Context addAfterReinit{}; + setRegU32(addAfterReinit, 4, 0x00123000u); + setRegU32(addAfterReinit, 5, 3u); + setRegU32(addAfterReinit, 6, 0x00124020u); + setRegU32(addAfterReinit, 7, 0u); + ps2_stubs::sceMpegAddCallback(rdram.data(), &addAfterReinit, nullptr); + t.Equals(getRegS32(addAfterReinit, 2), 1, + "sceMpegInit should reset MPEG callback bookkeeping between runs"); + }); + + tc.Run("movie startup MPEG and audio stubs return safe progress values", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0u); + ps2_stubs::clearMpegCompatLayout(); + ps2_stubs::resetMpegStubState(); + ps2_stubs::resetAudioStubState(); + + R5900Context firstIsEndCtx{}; + setRegU32(firstIsEndCtx, 4, 0x00123000u); + ps2_stubs::sceMpegIsEnd(rdram.data(), &firstIsEndCtx, nullptr); + t.Equals(getRegS32(firstIsEndCtx, 2), 0, + "sceMpegIsEnd should allow one synthetic frame before reporting end"); + + R5900Context demuxCtx{}; + setRegU32(demuxCtx, 4, 0x00123000u); + setRegU32(demuxCtx, 5, 0x00400000u); + setRegU32(demuxCtx, 6, 0x00004000u); + setRegU32(demuxCtx, 7, 0x00410000u); + ps2_stubs::sceMpegDemuxPssRing(rdram.data(), &demuxCtx, nullptr); + t.Equals(getRegS32(demuxCtx, 2), 0x4000, + "sceMpegDemuxPssRing should consume the provided input instead of trapping"); + + R5900Context getPictureCtx{}; + setRegU32(getPictureCtx, 4, 0x00123000u); + setRegU32(getPictureCtx, 5, 0x00124000u); + setRegU32(getPictureCtx, 6, 440u); + ps2_stubs::sceMpegGetPicture(rdram.data(), &getPictureCtx, nullptr); + t.Equals(Ps2FastRead32(rdram.data(), 0x00123000u + 0x00u), 320u, + "sceMpegGetPicture should seed a safe movie width"); + t.Equals(Ps2FastRead32(rdram.data(), 0x00123000u + 0x04u), 240u, + "sceMpegGetPicture should seed a safe movie height"); + t.Equals(Ps2FastRead32(rdram.data(), 0x00123000u + 0x08u), 0u, + "first synthetic picture should preserve frameCount==0 for guest setup"); + + R5900Context secondIsEndCtx{}; + setRegU32(secondIsEndCtx, 4, 0x00123000u); + ps2_stubs::sceMpegIsEnd(rdram.data(), &secondIsEndCtx, nullptr); + t.Equals(getRegS32(secondIsEndCtx, 2), 0, + "sceMpegIsEnd should keep the decode thread alive and let the guest stop playback"); + + R5900Context remoteInitCtx{}; + ps2_stubs::sceSdRemoteInit(rdram.data(), &remoteInitCtx, nullptr); + t.Equals(getRegS32(remoteInitCtx, 2), 0, + "sceSdRemoteInit should succeed so Veronica can set up movie audio"); + + R5900Context blockTransCtx{}; + const uint32_t blockTransSp = 0x00100000u; + setRegU32(blockTransCtx, 29, blockTransSp); + setRegU32(blockTransCtx, 4, 1u); + setRegU32(blockTransCtx, 5, 0x80E0u); + setRegU32(blockTransCtx, 6, 1u); + setRegU32(blockTransCtx, 7, 2u); + std::memcpy(rdram.data() + blockTransSp + 0x10u, "\x40\x23\x01\x00", 4u); + std::memcpy(rdram.data() + blockTransSp + 0x14u, "\x00\x30\x00\x00", 4u); + std::memcpy(rdram.data() + blockTransSp + 0x18u, "\x40\x27\x01\x00", 4u); + ps2_stubs::sceSdRemote(rdram.data(), &blockTransCtx, nullptr); + t.Equals(getRegU32(&blockTransCtx, 2), 0x00012340u, + "sceSdRemote block transfer should publish the current IOP ring position"); + + R5900Context statusCtx{}; + setRegU32(statusCtx, 29, blockTransSp); + setRegU32(statusCtx, 4, 1u); + setRegU32(statusCtx, 5, 0x80F0u); + setRegU32(statusCtx, 6, 1u); + setRegU32(statusCtx, 7, 0u); + std::memset(rdram.data() + blockTransSp + 0x10u, 0, 12u); + ps2_stubs::sceSdRemote(rdram.data(), &statusCtx, nullptr); + t.Equals(getRegU32(&statusCtx, 2), 0x00012340u, + "sceSdRemote status polling should reuse the last configured transfer base"); + + R5900Context setParamCtx{}; + setRegU32(setParamCtx, 29, blockTransSp); + setRegU32(setParamCtx, 4, 1u); + setRegU32(setParamCtx, 5, 0x8010u); + setRegU32(setParamCtx, 6, 0x0F81u); + setRegU32(setParamCtx, 7, 0u); + ps2_stubs::sceSdRemote(rdram.data(), &setParamCtx, nullptr); + t.Equals(getRegU32(&setParamCtx, 2), 0x00012340u, + "sceSdRemote set-param calls should not trap or disturb the movie audio state"); + }); + + tc.Run("MPEG compat layout enters playing state and defers end to guest state", [](TestCase &t) + { + constexpr uint32_t kCodeVeronicaMpegAddr = 0x01E27140u; + constexpr uint32_t kCodeVeronicaVideoStateAddr = 0x01E271E8u; + constexpr uint32_t kCodeVeronicaMovieStateAddr = 0x01E21914u; + + std::vector rdram(PS2_RAM_SIZE, 0u); + PS2MpegCompatLayout compat{}; + compat.mpegObjectAddr = kCodeVeronicaMpegAddr; + compat.videoStateAddr = kCodeVeronicaVideoStateAddr; + compat.movieStateAddr = kCodeVeronicaMovieStateAddr; + compat.syntheticFramesBeforeEnd = 0u; + compat.playingVideoStateValue = 0u; + compat.playingMovieStateValue = 2u; + compat.finishedVideoStateValue = 3u; + compat.finishedMovieStateValue = 3u; + ps2_stubs::setMpegCompatLayout(compat); + ps2_stubs::resetMpegStubState(); + + R5900Context firstIsEndCtx{}; + setRegU32(firstIsEndCtx, 4, kCodeVeronicaMpegAddr); + ps2_stubs::sceMpegIsEnd(rdram.data(), &firstIsEndCtx, nullptr); + t.Equals(getRegS32(firstIsEndCtx, 2), 0, + "compat movie path should get one synthetic frame before the MPEG stub reports end"); + + R5900Context getPictureCtx{}; + setRegU32(getPictureCtx, 4, kCodeVeronicaMpegAddr); + setRegU32(getPictureCtx, 5, 0x00124000u); + setRegU32(getPictureCtx, 6, 440u); + ps2_stubs::sceMpegGetPicture(rdram.data(), &getPictureCtx, nullptr); + t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaMpegAddr + 0x00u), 320u, + "compat movie fallback should seed a movie width"); + t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaMpegAddr + 0x04u), 240u, + "compat movie fallback should seed a movie height"); + t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaVideoStateAddr), 0u, + "compat movie fallback should leave videoDec.state in the active state while playback is running"); + t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaMovieStateAddr), 2u, + "compat movie fallback should promote the movie state to playing after the first synthetic frame"); + + R5900Context secondIsEndCtx{}; + setRegU32(secondIsEndCtx, 4, kCodeVeronicaMpegAddr); + ps2_stubs::sceMpegIsEnd(rdram.data(), &secondIsEndCtx, nullptr); + t.Equals(getRegS32(secondIsEndCtx, 2), 0, + "compat movie fallback should keep the decode thread alive until the guest marks playback finished"); + + Ps2FastWrite32(rdram.data(), kCodeVeronicaMovieStateAddr, 3u); + + R5900Context guestFinishedIsEndCtx{}; + setRegU32(guestFinishedIsEndCtx, 4, kCodeVeronicaMpegAddr); + ps2_stubs::sceMpegIsEnd(rdram.data(), &guestFinishedIsEndCtx, nullptr); + t.Equals(getRegS32(guestFinishedIsEndCtx, 2), 1, + "compat movie fallback should report end once the guest movie state reaches the finished value"); + + ps2_stubs::clearMpegCompatLayout(); + }); + + tc.Run("IPU init skips missing optional helper instead of dispatching the default trap", [](TestCase &t) + { + PS2Runtime runtime; + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + ctx.pc = 0x0010B470u; + + ps2_stubs::sceIpuInit(rdram.data(), &ctx, &runtime); + + t.IsFalse(runtime.isStopRequested(), + "sceIpuInit should tolerate the missing optional SetD4 helper"); + t.Equals(runtime.memory().read32(0x10002010u), 0x40000000u, + "sceIpuInit should still program IPU_CTRL"); + t.Equals(runtime.memory().read32(0x10002000u), 0u, + "sceIpuInit should leave IPU_CMD reset after initialization"); + }); + + tc.Run("sprintf consumes EE varargs from a2 a3 t0 and preserves width formatting", [](TestCase &t) + { + PS2Runtime runtime; + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + + constexpr uint32_t kDestAddr = 0x00002000u; + constexpr uint32_t kFormatAddr = 0x00002100u; + constexpr char kFormat[] = "rm_%1d%02d%1d.rdx"; + + std::memcpy(rdram.data() + kFormatAddr, kFormat, sizeof(kFormat)); + setRegU32(ctx, 4, kDestAddr); + setRegU32(ctx, 5, kFormatAddr); + setRegU32(ctx, 6, 0u); // a2 + setRegU32(ctx, 7, 3u); // a3 + setRegU32(ctx, 8, 1u); // t0 + + ps2_stubs::sprintf(rdram.data(), &ctx, &runtime); + + const std::string rendered(reinterpret_cast(rdram.data() + kDestAddr)); + t.Equals(rendered, std::string("rm_0031.rdx"), + "sprintf should read the third variadic integer from t0 and honor %02d"); + t.Equals(getRegS32(ctx, 2), static_cast(rendered.size()), + "sprintf should return the rendered length"); + }); + tc.Run("multiply-add matrix writes rd only when R5900 requires it", [](TestCase &t) { R5900Decoder decoder; diff --git a/ps2xTest/src/ps2_runtime_io_tests.cpp b/ps2xTest/src/ps2_runtime_io_tests.cpp index ecadc544..817e5f70 100644 --- a/ps2xTest/src/ps2_runtime_io_tests.cpp +++ b/ps2xTest/src/ps2_runtime_io_tests.cpp @@ -16,11 +16,40 @@ namespace // Guest memory address ranges for test data constexpr uint32_t GUEST_STRING_AREA_START = 0x1000; constexpr uint32_t GUEST_BUFFER_AREA_START = 0x2000; + constexpr uint32_t GUEST_STACK_AREA_START = 0x6000; + constexpr uint32_t GUEST_MC_SYNC_CMD_ADDR = GUEST_BUFFER_AREA_START + 0x1C00; + constexpr uint32_t GUEST_MC_SYNC_RESULT_ADDR = GUEST_BUFFER_AREA_START + 0x1C04; + constexpr uint32_t GUEST_MC_TABLE_ADDR = GUEST_BUFFER_AREA_START + 0x2000; // Common file I/O flag combinations constexpr uint32_t PS2_FIO_WRITE_CREATE_TRUNC = PS2_FIO_O_WRONLY | PS2_FIO_O_CREAT | PS2_FIO_O_TRUNC; + struct SceMcStDateTime + { + uint8_t resv2; + uint8_t sec; + uint8_t min; + uint8_t hour; + uint8_t day; + uint8_t month; + uint16_t year; + }; + + struct SceMcTblGetDir + { + SceMcStDateTime create; + SceMcStDateTime modify; + uint32_t fileSizeByte; + uint16_t attrFile; + uint16_t reserve1; + uint32_t reserve2; + uint32_t pdaAplNo; + char entryName[32]; + }; + + static_assert(sizeof(SceMcTblGetDir) == 64, "sceMcTblGetDir size mismatch"); + void setRegU32(R5900Context &ctx, int reg, uint32_t value) { ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); @@ -36,6 +65,51 @@ namespace std::memcpy(rdram + addr, value.c_str(), value.size() + 1); } + void writeGuestU32(uint8_t *rdram, uint32_t addr, uint32_t value) + { + std::memcpy(rdram + addr, &value, sizeof(value)); + } + + int32_t readGuestS32(const uint8_t *rdram, uint32_t addr) + { + int32_t value = 0; + std::memcpy(&value, rdram + addr, sizeof(value)); + return value; + } + + uint32_t readGuestU32(const uint8_t *rdram, uint32_t addr) + { + uint32_t value = 0; + std::memcpy(&value, rdram + addr, sizeof(value)); + return value; + } + + void clearContext(R5900Context &ctx) + { + std::memset(&ctx, 0, sizeof(ctx)); + } + + void writeStackArg(std::vector &rdram, R5900Context &ctx, uint32_t slotIndex, uint32_t value) + { + const uint32_t sp = ::getRegU32(&ctx, 29); + writeGuestU32(rdram.data(), sp + 16u + slotIndex * sizeof(uint32_t), value); + } + + int32_t syncMc(std::vector &rdram, int32_t *cmdOut = nullptr) + { + R5900Context syncCtx{}; + setRegU32(syncCtx, 4, 0u); + setRegU32(syncCtx, 5, GUEST_MC_SYNC_CMD_ADDR); + setRegU32(syncCtx, 6, GUEST_MC_SYNC_RESULT_ADDR); + ps2_stubs::sceMcSync(rdram.data(), &syncCtx, nullptr); + + if (cmdOut) + { + *cmdOut = readGuestS32(rdram.data(), GUEST_MC_SYNC_CMD_ADDR); + } + return readGuestS32(rdram.data(), GUEST_MC_SYNC_RESULT_ADDR); + } + struct TempPaths { std::filesystem::path base; @@ -276,6 +350,165 @@ void register_ps2_runtime_io_tests() "mc0: directory should NOT exist under cdRoot"); }); + tc.Run("sceMc open write read and close roundtrip through sync", [](TestCase &t) + { + TestContext test; + + const uint32_t dirAddr = GUEST_STRING_AREA_START + 0x400; + const uint32_t fileAddr = GUEST_STRING_AREA_START + 0x500; + const uint32_t writeBufAddr = GUEST_BUFFER_AREA_START + 0x300; + const uint32_t readBufAddr = GUEST_BUFFER_AREA_START + 0x500; + const std::string payload = "libmc roundtrip"; + + writeGuestString(test.rdram.data(), dirAddr, "/SAVEDATA"); + writeGuestString(test.rdram.data(), fileAddr, "/SAVEDATA/test.bin"); + std::memcpy(test.rdram.data() + writeBufAddr, payload.data(), payload.size()); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, dirAddr); + ps2_stubs::sceMcMkdir(test.rdram.data(), &test.ctx, nullptr); + t.Equals(getRegS32(&test.ctx, 2), 0, "sceMcMkdir should dispatch successfully"); + + int32_t cmd = 0; + t.Equals(syncMc(test.rdram, &cmd), 0, "sceMcMkdir should finish successfully"); + t.Equals(cmd, 0x0B, "sceMcSync should report MKDIR as the last command"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, fileAddr); + setRegU32(test.ctx, 7, PS2_FIO_O_RDWR | PS2_FIO_O_CREAT | PS2_FIO_O_TRUNC); + ps2_stubs::sceMcOpen(test.rdram.data(), &test.ctx, nullptr); + t.Equals(getRegS32(&test.ctx, 2), 0, "sceMcOpen should dispatch successfully"); + + const int32_t fd = syncMc(test.rdram, &cmd); + t.IsTrue(fd > 0, "sceMcOpen should produce a positive descriptor in sceMcSync"); + t.Equals(cmd, 0x02, "sceMcSync should report OPEN as the last command"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, static_cast(fd)); + setRegU32(test.ctx, 5, writeBufAddr); + setRegU32(test.ctx, 6, static_cast(payload.size())); + ps2_stubs::sceMcWrite(test.rdram.data(), &test.ctx, nullptr); + t.Equals(syncMc(test.rdram, &cmd), static_cast(payload.size()), + "sceMcWrite should report the full byte count via sceMcSync"); + t.Equals(cmd, 0x06, "sceMcSync should report WRITE as the last command"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, static_cast(fd)); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, PS2_FIO_SEEK_SET); + ps2_stubs::sceMcSeek(test.rdram.data(), &test.ctx, nullptr); + t.Equals(syncMc(test.rdram, &cmd), 0, "sceMcSeek should rewind to offset zero"); + t.Equals(cmd, 0x04, "sceMcSync should report SEEK as the last command"); + + std::memset(test.rdram.data() + readBufAddr, 0, payload.size()); + clearContext(test.ctx); + setRegU32(test.ctx, 4, static_cast(fd)); + setRegU32(test.ctx, 5, readBufAddr); + setRegU32(test.ctx, 6, static_cast(payload.size())); + ps2_stubs::sceMcRead(test.rdram.data(), &test.ctx, nullptr); + t.Equals(syncMc(test.rdram, &cmd), static_cast(payload.size()), + "sceMcRead should report the full byte count via sceMcSync"); + t.Equals(cmd, 0x05, "sceMcSync should report READ as the last command"); + + std::string readback(reinterpret_cast(test.rdram.data() + readBufAddr), payload.size()); + t.Equals(readback, payload, "sceMcRead should fill the guest buffer with the written payload"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, static_cast(fd)); + ps2_stubs::sceMcClose(test.rdram.data(), &test.ctx, nullptr); + t.Equals(syncMc(test.rdram, &cmd), 0, "sceMcClose should finish successfully"); + t.Equals(cmd, 0x03, "sceMcSync should report CLOSE as the last command"); + + const std::filesystem::path hostPath = test.paths.mcRoot / "SAVEDATA" / "test.bin"; + t.IsTrue(std::filesystem::exists(hostPath), "sceMcOpen/sceMcWrite should create the host file under mcRoot"); + }); + + tc.Run("sceMcGetDir includes dot entries and file metadata", [](TestCase &t) + { + TestContext test; + + std::filesystem::create_directories(test.paths.mcRoot / "SAVEDATA"); + const std::string hostPayload = "abc123"; + { + std::ofstream out(test.paths.mcRoot / "SAVEDATA" / "game.dat", std::ios::binary); + out.write(hostPayload.data(), static_cast(hostPayload.size())); + } + + const uint32_t patternAddr = GUEST_STRING_AREA_START + 0x700; + writeGuestString(test.rdram.data(), patternAddr, "/SAVEDATA/*"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, patternAddr); + setRegU32(test.ctx, 7, 0u); + setRegU32(test.ctx, 29, GUEST_STACK_AREA_START); + writeStackArg(test.rdram, test.ctx, 0u, 8u); + writeStackArg(test.rdram, test.ctx, 1u, GUEST_MC_TABLE_ADDR); + + ps2_stubs::sceMcGetDir(test.rdram.data(), &test.ctx, nullptr); + + int32_t cmd = 0; + const int32_t entryCount = syncMc(test.rdram, &cmd); + t.Equals(cmd, 0x0D, "sceMcSync should report GETDIR as the last command"); + t.Equals(entryCount, 3, "sceMcGetDir should return '.', '..', and the matching file"); + + const auto *entries = reinterpret_cast(test.rdram.data() + GUEST_MC_TABLE_ADDR); + t.Equals(std::string(entries[0].entryName), std::string("."), "sceMcGetDir should return '.' first"); + t.Equals(std::string(entries[1].entryName), std::string(".."), "sceMcGetDir should return '..' second"); + t.Equals(std::string(entries[2].entryName), std::string("game.dat"), "sceMcGetDir should include the matching file entry"); + t.Equals(entries[2].fileSizeByte, static_cast(hostPayload.size()), + "sceMcGetDir should report the host file size"); + t.IsTrue((entries[2].attrFile & 0x0080u) != 0u, + "sceMcGetDir file entries should carry the closed-file attribute"); + }); + + tc.Run("sceMcGetInfo reports formatted and unformatted states", [](TestCase &t) + { + TestContext test; + + constexpr uint32_t typeAddr = GUEST_BUFFER_AREA_START + 0x900; + constexpr uint32_t freeAddr = GUEST_BUFFER_AREA_START + 0x904; + constexpr uint32_t formatAddr = GUEST_BUFFER_AREA_START + 0x908; + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, typeAddr); + setRegU32(test.ctx, 7, freeAddr); + setRegU32(test.ctx, 29, GUEST_STACK_AREA_START); + writeStackArg(test.rdram, test.ctx, 0u, formatAddr); + ps2_stubs::sceMcGetInfo(test.rdram.data(), &test.ctx, nullptr); + + int32_t cmd = 0; + t.Equals(syncMc(test.rdram, &cmd), 0, "formatted cards should report success through sceMcSync"); + t.Equals(cmd, 0x01, "sceMcSync should report GETINFO as the last command"); + t.Equals(readGuestS32(test.rdram.data(), typeAddr), 2, "sceMcGetInfo should report a PS2 memory card"); + t.Equals(readGuestS32(test.rdram.data(), formatAddr), 1, "sceMcGetInfo should report a formatted card"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + ps2_stubs::sceMcUnformat(test.rdram.data(), &test.ctx, nullptr); + t.Equals(syncMc(test.rdram, &cmd), 0, "sceMcUnformat should complete successfully"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, typeAddr); + setRegU32(test.ctx, 7, freeAddr); + setRegU32(test.ctx, 29, GUEST_STACK_AREA_START); + writeStackArg(test.rdram, test.ctx, 0u, formatAddr); + ps2_stubs::sceMcGetInfo(test.rdram.data(), &test.ctx, nullptr); + + t.Equals(syncMc(test.rdram, &cmd), -2, "unformatted cards should report sceMcResNoFormat through sceMcSync"); + t.Equals(readGuestS32(test.rdram.data(), formatAddr), 0, "sceMcGetInfo should report an unformatted card after sceMcUnformat"); + }); + tc.Run("sceIoctl cmd1 updates wait flag state", [](TestCase &t) { TestContext test; @@ -296,5 +529,68 @@ void register_ps2_runtime_io_tests() std::memcpy(&state, test.rdram.data() + statusAddr, sizeof(state)); t.Equals(state, 0u, "sceIoctl cmd1 should clear wait state from busy to ready"); }); + + tc.Run("sceCdSearchFile resolves movie filenames with zero-padded host leaf", [](TestCase &t) + { + TestContext test; + + std::filesystem::create_directories(test.paths.cdRoot / "movie"); + { + std::ofstream out(test.paths.cdRoot / "movie" / "mv_016.pss", std::ios::binary); + const std::string payload = "pss"; + out.write(payload.data(), static_cast(payload.size())); + } + + constexpr uint32_t fileAddr = GUEST_BUFFER_AREA_START + 0x1A00; + constexpr uint32_t pathAddr = GUEST_STRING_AREA_START + 0xA00; + writeGuestString(test.rdram.data(), pathAddr, "\\MOVIE\\MV_16.PSS;1"); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, fileAddr); + setRegU32(test.ctx, 5, pathAddr); + ps2_stubs::sceCdSearchFile(test.rdram.data(), &test.ctx, nullptr); + + t.Equals(getRegS32(&test.ctx, 2), 1, "sceCdSearchFile should resolve the extracted movie file"); + t.Equals(readGuestU32(test.rdram.data(), fileAddr + 4), 3u, + "sceCdSearchFile should report the host file size"); + t.IsTrue(readGuestU32(test.rdram.data(), fileAddr + 0) >= 0x00100000u, + "sceCdSearchFile should assign a pseudo LSN for the resolved host file"); + }); + + tc.Run("sceCdRead reads from explicit cdImage path", [](TestCase &t) + { + TestContext test; + + constexpr uint32_t kSectorSize = 2048u; + constexpr uint32_t bufAddr = GUEST_BUFFER_AREA_START + 0x1C80; + const std::filesystem::path imagePath = test.paths.base / "disc.iso"; + { + std::vector sector(kSectorSize, 0); + const char payload[] = "cd-image"; + std::memcpy(sector.data(), payload, sizeof(payload) - 1); + + std::ofstream out(imagePath, std::ios::binary); + out.write(reinterpret_cast(sector.data()), + static_cast(sector.size())); + } + + PS2Runtime::IoPaths ioPaths; + ioPaths.elfDirectory = test.paths.cdRoot; + ioPaths.hostRoot = test.paths.cdRoot; + ioPaths.cdRoot = test.paths.cdRoot; + ioPaths.mcRoot = test.paths.mcRoot; + ioPaths.cdImage = imagePath; + PS2Runtime::setIoPaths(ioPaths); + + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 1u); + setRegU32(test.ctx, 6, bufAddr); + ps2_stubs::sceCdRead(test.rdram.data(), &test.ctx, nullptr); + + t.Equals(getRegS32(&test.ctx, 2), 1, "sceCdRead should succeed when cdImage is configured"); + t.Equals(std::memcmp(test.rdram.data() + bufAddr, "cd-image", 8), 0, + "sceCdRead should copy sector data from the configured image"); + }); }); } diff --git a/ps2xTest/src/ps2_runtime_kernel_tests.cpp b/ps2xTest/src/ps2_runtime_kernel_tests.cpp index a3d13c50..7dbe6725 100644 --- a/ps2xTest/src/ps2_runtime_kernel_tests.cpp +++ b/ps2xTest/src/ps2_runtime_kernel_tests.cpp @@ -3,9 +3,11 @@ #include "ps2_runtime_macros.h" #include "ps2_syscalls.h" +#include #include #include #include +#include #include using namespace ps2_syscalls; @@ -24,7 +26,13 @@ namespace constexpr int KE_SEMA_ZERO = -419; constexpr int KE_SEMA_OVF = -420; + constexpr int THS_SUSPEND = 0x08; + constexpr int THS_WAITSUSPEND = 0x0C; constexpr int THS_DORMANT = 0x10; + constexpr uint32_t TSW_EVENT = 3u; + + constexpr uint32_t K_EVENT_WAIT_READY_ADDR = 0x1800u; + constexpr uint32_t K_EVENT_WAIT_GATE_ADDR = 0x1804u; struct EeThreadStatus { @@ -78,6 +86,28 @@ namespace } } + uint32_t readGuestU32(const uint8_t *rdram, uint32_t addr) + { + uint32_t value = 0; + std::memcpy(&value, rdram + addr, sizeof(value)); + return value; + } + + template + bool waitUntil(Predicate pred, std::chrono::milliseconds timeout) + { + const auto deadline = std::chrono::steady_clock::now() + timeout; + while (std::chrono::steady_clock::now() < deadline) + { + if (pred()) + { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + return pred(); + } + bool callSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { return dispatchNumericSyscall(syscallNumber, rdram, ctx, runtime); @@ -138,6 +168,38 @@ namespace ctx->pc = ::getRegU32(ctx, 31); } + void waitEventAfterSuspendHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + if (!rdram || !ctx) + { + return; + } + + writeGuestU32(rdram, K_EVENT_WAIT_READY_ADDR, 1u); + while (readGuestU32(rdram, K_EVENT_WAIT_GATE_ADDR) == 0u) + { + if (runtime && runtime->isStopRequested()) + { + ctx->pc = 0u; + return; + } + std::this_thread::yield(); + } + + const uint32_t eid = ::getRegU32(ctx, 4); + setRegU32(*ctx, 4, eid); + setRegU32(*ctx, 5, 0x4u); + setRegU32(*ctx, 6, 1u); + setRegU32(*ctx, 7, 0u); + WaitEventFlag(rdram, ctx, runtime); + ctx->pc = 0u; + } + + void alarmNoopHandler(uint8_t *, R5900Context *ctx, PS2Runtime *) + { + ctx->pc = 0u; + } + struct TestEnv { std::vector rdram; @@ -364,6 +426,138 @@ void register_ps2_runtime_kernel_tests() t.Equals(getRegS32(env.ctx, 2), KE_OK, "DeleteSema should clean up legacy-decoded semaphore"); }); + tc.Run("WaitEventFlag preserves waitsuspend state when a suspended thread blocks", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kEventParamAddr = 0x1600u; + constexpr uint32_t kWaitThreadEntry = 0x00260000u; + + const uint32_t eventParam[3] = { + 0u, + 0u, + 0u + }; + std::memcpy(env.rdram.data() + kEventParamAddr, eventParam, sizeof(eventParam)); + writeGuestU32(env.rdram.data(), K_EVENT_WAIT_READY_ADDR, 0u); + writeGuestU32(env.rdram.data(), K_EVENT_WAIT_GATE_ADDR, 0u); + + R5900Context createEventCtx{}; + setRegU32(createEventCtx, 4, kEventParamAddr); + CreateEventFlag(env.rdram.data(), &createEventCtx, &env.runtime); + const int32_t eid = getRegS32(createEventCtx, 2); + t.IsTrue(eid > 0, "CreateEventFlag should return a valid event id"); + + env.runtime.registerFunction(kWaitThreadEntry, &waitEventAfterSuspendHandler); + + const uint32_t threadParam[7] = { + 0u, + kWaitThreadEntry, + 0x00310000u, + 0x00000800u, + 0x00120000u, + 6u, + 0u + }; + + writeGuestWords(env.rdram.data(), K_PARAM_ADDR, threadParam, std::size(threadParam)); + setRegU32(env.ctx, 4, K_PARAM_ADDR); + CreateThread(env.rdram.data(), &env.ctx, &env.runtime); + const int32_t tid = getRegS32(env.ctx, 2); + t.IsTrue(tid >= 2, "CreateThread should return a valid worker thread id"); + + setRegU32(env.ctx, 4, static_cast(tid)); + setRegU32(env.ctx, 5, static_cast(eid)); + StartThread(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "StartThread should launch the event waiter"); + + const bool ready = waitUntil([&]() + { + return readGuestU32(env.rdram.data(), K_EVENT_WAIT_READY_ADDR) == 1u; + }, std::chrono::milliseconds(200)); + t.IsTrue(ready, "waiter thread should reach the suspend gate before blocking"); + + setRegU32(env.ctx, 4, static_cast(tid)); + SuspendThread(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SuspendThread should succeed for the running waiter"); + + writeGuestU32(env.rdram.data(), K_EVENT_WAIT_GATE_ADDR, 1u); + + const bool waiting = waitUntil([&]() + { + R5900Context statusCtx{}; + setRegU32(statusCtx, 4, static_cast(tid)); + setRegU32(statusCtx, 5, K_STATUS_ADDR); + ReferThreadStatus(env.rdram.data(), &statusCtx, &env.runtime); + if (getRegS32(statusCtx, 2) != KE_OK) + { + return false; + } + + EeThreadStatus status{}; + std::memcpy(&status, env.rdram.data() + K_STATUS_ADDR, sizeof(status)); + return status.waitType == TSW_EVENT; + }, std::chrono::milliseconds(200)); + t.IsTrue(waiting, "waiter thread should block on the event flag"); + + EeThreadStatus waitingStatus{}; + std::memcpy(&waitingStatus, env.rdram.data() + K_STATUS_ADDR, sizeof(waitingStatus)); + t.Equals(waitingStatus.status, THS_WAITSUSPEND, + "event-flag wait should report THS_WAITSUSPEND when the thread is already suspended"); + + R5900Context signalCtx{}; + setRegU32(signalCtx, 4, static_cast(eid)); + setRegU32(signalCtx, 5, 0x4u); + SetEventFlag(env.rdram.data(), &signalCtx, &env.runtime); + t.Equals(getRegS32(signalCtx, 2), KE_OK, "SetEventFlag should wake the waiting thread"); + + const bool suspended = waitUntil([&]() + { + R5900Context statusCtx{}; + setRegU32(statusCtx, 4, static_cast(tid)); + setRegU32(statusCtx, 5, K_STATUS_ADDR); + ReferThreadStatus(env.rdram.data(), &statusCtx, &env.runtime); + if (getRegS32(statusCtx, 2) != KE_OK) + { + return false; + } + + EeThreadStatus status{}; + std::memcpy(&status, env.rdram.data() + K_STATUS_ADDR, sizeof(status)); + return status.status == THS_SUSPEND && status.waitType == 0u; + }, std::chrono::milliseconds(200)); + t.IsTrue(suspended, "after wake, a still-suspended waiter should move to THS_SUSPEND"); + + setRegU32(env.ctx, 4, static_cast(tid)); + ResumeThread(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "ResumeThread should release the waiter after the event is set"); + + const bool dormant = waitUntil([&]() + { + R5900Context statusCtx{}; + setRegU32(statusCtx, 4, static_cast(tid)); + setRegU32(statusCtx, 5, K_STATUS_ADDR); + ReferThreadStatus(env.rdram.data(), &statusCtx, &env.runtime); + if (getRegS32(statusCtx, 2) != KE_OK) + { + return false; + } + + EeThreadStatus status{}; + std::memcpy(&status, env.rdram.data() + K_STATUS_ADDR, sizeof(status)); + return status.status == THS_DORMANT; + }, std::chrono::milliseconds(200)); + t.IsTrue(dormant, "waiter thread should return to dormant after the event is signaled and resumed"); + + setRegU32(env.ctx, 4, static_cast(eid)); + DeleteEventFlag(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "DeleteEventFlag should clean up the test event flag"); + + setRegU32(env.ctx, 4, static_cast(tid)); + DeleteThread(env.rdram.data(), &env.ctx, &env.runtime); + 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) { TestEnv env; @@ -401,6 +595,36 @@ void register_ps2_runtime_kernel_tests() t.Equals(reused, heapBase, "guestFree should make the head block reusable"); }); + tc.Run("ReleaseAlarm aliases CancelAlarm and cache toggles succeed", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kAlarmHandlerAddr = 0x00270000u; + env.runtime.registerFunction(kAlarmHandlerAddr, &alarmNoopHandler); + + setRegU32(env.ctx, 4, 0xFFFFu); + setRegU32(env.ctx, 5, kAlarmHandlerAddr); + setRegU32(env.ctx, 6, 0u); + SetAlarm(env.rdram.data(), &env.ctx, &env.runtime); + const int32_t alarmId = getRegS32(env.ctx, 2); + t.IsTrue(alarmId > 0, "SetAlarm should create a cancellable alarm"); + + setRegU32(env.ctx, 4, static_cast(alarmId)); + ReleaseAlarm(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "ReleaseAlarm should cancel active alarms"); + + setRegU32(env.ctx, 4, static_cast(alarmId)); + CancelAlarm(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_ERROR, + "CancelAlarm should report missing alarms after ReleaseAlarm consumes them"); + + EnableCache(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "EnableCache should succeed as a no-op"); + + DisableCache(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "DisableCache should succeed as a no-op"); + }); + tc.Run("setup heap and thread invalid ids use documented kernel errors", [](TestCase &t) { TestEnv env; diff --git a/ps2xTest/src/ps2_sif_dma_tests.cpp b/ps2xTest/src/ps2_sif_dma_tests.cpp index a1735904..32f86a84 100644 --- a/ps2xTest/src/ps2_sif_dma_tests.cpp +++ b/ps2xTest/src/ps2_sif_dma_tests.cpp @@ -15,6 +15,8 @@ namespace ps2_stubs namespace { + constexpr int KE_OK = 0; + struct TestEnv { std::vector rdram; @@ -24,6 +26,8 @@ namespace TestEnv() : rdram(PS2_RAM_SIZE, 0u) { ps2_stubs::resetSifState(); + ps2_syscalls::resetSoundDriverRpcState(); + ps2_syscalls::clearSoundDriverCompatLayout(); std::memset(&ctx, 0, sizeof(ctx)); } }; @@ -79,6 +83,18 @@ namespace return value; } + void writeGuestS16(uint8_t *rdram, uint32_t addr, int16_t value) + { + std::memcpy(rdram + addr, &value, sizeof(value)); + } + + int16_t readGuestS16(const uint8_t *rdram, uint32_t addr) + { + int16_t value = 0; + std::memcpy(&value, rdram + addr, sizeof(value)); + return value; + } + uint32_t g_dmacHandlerWriteAddr = 0u; uint32_t g_dmacHandlerValue = 0u; uint32_t g_dmacHandlerLastCause = 0u; @@ -138,6 +154,40 @@ void register_ps2_sif_dma_tests() t.IsTrue(getRegS32(env.ctx, 2) < 0, "sceSifDmaStat should be negative when transfer is complete"); }); + tc.Run("isceSifSetDma and isceSifSetDChain alias the SIF DMA helpers", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kDescAddr = 0x00020240u; + constexpr uint32_t kSrcAddr = 0x00020340u; + constexpr uint32_t kDstAddr = 0x00020440u; + + std::array payload{}; + for (size_t i = 0; i < payload.size(); ++i) + { + payload[i] = static_cast(0x50u + i); + } + std::memcpy(env.rdram.data() + kSrcAddr, payload.data(), payload.size()); + std::memset(env.rdram.data() + kDstAddr, 0, payload.size()); + + const Ps2SifDmaTransfer desc{ + kSrcAddr, + kDstAddr, + static_cast(payload.size()), + 0}; + std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc)); + + setRegU32(env.ctx, 4, kDescAddr); + setRegU32(env.ctx, 5, 1u); + ps2_stubs::isceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime); + t.IsTrue(getRegS32(env.ctx, 2) > 0, "isceSifSetDma should report a successful transfer id"); + t.IsTrue(std::memcmp(env.rdram.data() + kDstAddr, payload.data(), payload.size()) == 0, + "isceSifSetDma should copy transfer payload like sceSifSetDma"); + + ps2_stubs::isceSifSetDChain(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), 0, "isceSifSetDChain should mirror sceSifSetDChain"); + }); + tc.Run("sceSifSetDma dispatches enabled DMAC handlers for cause 5", [](TestCase &t) { TestEnv env; @@ -192,6 +242,374 @@ void register_ps2_sif_dma_tests() t.Equals(g_dmacHandlerLastArg, kHandlerArg, "DMAC handler should receive registered argument"); }); + tc.Run("sceSifSetDma acknowledges DTX work-buffer transfers by advancing the EE footer ticket", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kClientAddr = 0x0002D000u; + constexpr uint32_t kDtxSid = 0x7D000000u; + constexpr uint32_t kSendAddr = 0x0002D100u; + constexpr uint32_t kRecvAddr = 0x0002D200u; + constexpr uint32_t kDescAddr = 0x0002D300u; + constexpr uint32_t kEeWorkAddr = 0x0002D400u; + constexpr uint32_t kIopWorkAddr = 0x0002D800u; + constexpr uint32_t kDtxId = 3u; + constexpr uint32_t kWorkLen = 0x100u; + constexpr uint32_t kFooterTicketAddr = kEeWorkAddr + kWorkLen - sizeof(uint32_t); + + ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, kDtxSid); + setRegU32(env.ctx, 6, 0u); + ps2_syscalls::SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for the DTX sid"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, kDtxId); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWorkAddr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWorkAddr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen); + writeGuestU32(env.rdram.data(), kRecvAddr + 0x00u, 0u); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 2u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifCallRpc should create the DTX transport"); + t.IsTrue(readGuestU32(env.rdram.data(), kRecvAddr) != 0u, "DTX create should return a remote handle"); + + std::memset(env.rdram.data() + kEeWorkAddr, 0x44, kWorkLen); + std::memset(env.rdram.data() + kIopWorkAddr, 0x00, kWorkLen); + writeGuestU32(env.rdram.data(), kFooterTicketAddr, 1u); + + const Ps2SifDmaTransfer desc{ + kEeWorkAddr, + kIopWorkAddr, + static_cast(kWorkLen), + 0}; + std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc)); + + setRegU32(env.ctx, 4, kDescAddr); + setRegU32(env.ctx, 5, 1u); + ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime); + t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the DTX transfer"); + + t.Equals(readGuestU32(env.rdram.data(), kFooterTicketAddr), 2u, + "sceSifSetDma should advance the EE footer ticket so DTX clears wait_flag"); + }); + + tc.Run("sceSifSetDma applies SJX DTX payloads into the emulated SJRMT data ring", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kClientAddr = 0x0002E000u; + constexpr uint32_t kDtxSid = 0x7D000000u; + constexpr uint32_t kRecvAddr = 0x0002E100u; + constexpr uint32_t kSendAddr = 0x0002E200u; + constexpr uint32_t kDescAddr = 0x0002E300u; + constexpr uint32_t kEeWorkAddr = 0x0002E400u; + constexpr uint32_t kIopWorkAddr = 0x0002E800u; + constexpr uint32_t kRingAddr = 0x0002EC00u; + constexpr uint32_t kChunkDataAddr = 0x0002ED00u; + constexpr uint32_t kWorkLen = 0x100u; + constexpr uint32_t kChunkLen = 8u; + + ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, kDtxSid); + setRegU32(env.ctx, 6, 0u); + ps2_syscalls::SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should bind the DTX sid"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kRingAddr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kWorkLen); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x422u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 12u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t sjrmtHandle = readGuestU32(env.rdram.data(), kRecvAddr); + t.IsTrue(sjrmtHandle != 0u, "SJRMT_UNI_CREATE should return a handle"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, sjrmtHandle); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 1u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0x12345678u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x400u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t sjxHandle = readGuestU32(env.rdram.data(), kRecvAddr); + t.IsTrue(sjxHandle != 0u, "SJX_CREATE should return a handle"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWorkAddr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWorkAddr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 2u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed"); + + std::memset(env.rdram.data() + kEeWorkAddr, 0, kWorkLen); + std::memset(env.rdram.data() + kIopWorkAddr, 0, kWorkLen); + std::memset(env.rdram.data() + kRingAddr, 0, kWorkLen); + for (uint32_t i = 0; i < kChunkLen; ++i) + { + env.rdram[kChunkDataAddr + i] = static_cast(0xA0u + i); + } + + writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x00u, 1u); + env.rdram[kEeWorkAddr + 0x10u] = 0u; + env.rdram[kEeWorkAddr + 0x11u] = 1u; + std::memcpy(env.rdram.data() + kEeWorkAddr + 0x12u, "\0\0", 2u); + writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x14u, sjxHandle); + writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x18u, kChunkDataAddr); + writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x1Cu, kChunkLen); + writeGuestU32(env.rdram.data(), kEeWorkAddr + kWorkLen - sizeof(uint32_t), 1u); + + const Ps2SifDmaTransfer desc{ + kEeWorkAddr, + kIopWorkAddr, + static_cast(kWorkLen), + 0}; + std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc)); + + setRegU32(env.ctx, 4, kDescAddr); + setRegU32(env.ctx, 5, 1u); + ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime); + t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the SJX transport"); + t.Equals(env.rdram[kEeWorkAddr + 0x11u], static_cast(0u), + "SJX DMA ack should rewrite the response line to room so EE recycles the chunk"); + t.Equals(readGuestU32(env.rdram.data(), kEeWorkAddr + kWorkLen - sizeof(uint32_t)), 2u, + "SJX DMA ack should still advance the EE footer ticket"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 1u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x429u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 8u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), kChunkLen, + "SJX DMA should make SJRMT report available data"); + t.IsTrue(std::memcmp(env.rdram.data() + kRingAddr, env.rdram.data() + kChunkDataAddr, kChunkLen) == 0, + "SJX DMA should copy the chunk payload into the emulated SJRMT ring"); + }); + + tc.Run("sceSifSetDma lets active PS2RNA playback drain emulated SJRMT data", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kClientAddr = 0x0002F000u; + constexpr uint32_t kDtxSid = 0x7D000000u; + constexpr uint32_t kRecvAddr = 0x0002F100u; + constexpr uint32_t kSendAddr = 0x0002F200u; + constexpr uint32_t kDesc0Addr = 0x0002F300u; + constexpr uint32_t kDesc1Addr = 0x0002F320u; + constexpr uint32_t kEeWork0Addr = 0x0002F400u; + constexpr uint32_t kIopWork0Addr = 0x0002F800u; + constexpr uint32_t kEeWork1Addr = 0x0002FC00u; + constexpr uint32_t kIopWork1Addr = 0x00030000u; + constexpr uint32_t kRingAddr = 0x00030400u; + constexpr uint32_t kChunkDataAddr = 0x00030500u; + constexpr uint32_t kWorkLen = 0x100u; + constexpr uint32_t kChunkLen = 8u; + + ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, kDtxSid); + setRegU32(env.ctx, 6, 0u); + ps2_syscalls::SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should bind the DTX sid"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kRingAddr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kWorkLen); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x422u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 12u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t sjrmtHandle = readGuestU32(env.rdram.data(), kRecvAddr); + t.IsTrue(sjrmtHandle != 0u, "SJRMT_UNI_CREATE should return a handle"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, sjrmtHandle); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 1u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0xCAFEBABEu); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x400u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t sjxHandle = readGuestU32(env.rdram.data(), kRecvAddr); + t.IsTrue(sjxHandle != 0u, "SJX_CREATE should return a handle"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 0u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, sjrmtHandle); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x408u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t ps2RnaHandle = readGuestU32(env.rdram.data(), kRecvAddr); + t.IsTrue(ps2RnaHandle != 0u, "PS2RNA_CREATE should return a handle"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWork0Addr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWork0Addr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 2u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed for SJX transport"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWork1Addr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWork1Addr); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 2u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 16u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed for PS2RNA transport"); + + std::memset(env.rdram.data() + kEeWork0Addr, 0, kWorkLen); + std::memset(env.rdram.data() + kIopWork0Addr, 0, kWorkLen); + std::memset(env.rdram.data() + kEeWork1Addr, 0, kWorkLen); + std::memset(env.rdram.data() + kIopWork1Addr, 0, kWorkLen); + std::memset(env.rdram.data() + kRingAddr, 0, kWorkLen); + for (uint32_t i = 0; i < kChunkLen; ++i) + { + env.rdram[kChunkDataAddr + i] = static_cast(0xB0u + i); + } + + writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x00u, 1u); + writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x10u, 2u); + writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x14u, ps2RnaHandle); + writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x18u, 1u); + writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x1Cu, 0u); + writeGuestU32(env.rdram.data(), kEeWork1Addr + kWorkLen - sizeof(uint32_t), 1u); + + const Ps2SifDmaTransfer desc1{ + kEeWork1Addr, + kIopWork1Addr, + static_cast(kWorkLen), + 0}; + std::memcpy(env.rdram.data() + kDesc1Addr, &desc1, sizeof(desc1)); + + setRegU32(env.ctx, 4, kDesc1Addr); + setRegU32(env.ctx, 5, 1u); + ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime); + t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the PS2RNA control transport"); + t.Equals(readGuestU32(env.rdram.data(), kEeWork1Addr + kWorkLen - sizeof(uint32_t)), 2u, + "PS2RNA control DMA should advance the EE footer ticket"); + + writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x00u, 1u); + env.rdram[kEeWork0Addr + 0x10u] = 0u; + env.rdram[kEeWork0Addr + 0x11u] = 1u; + std::memcpy(env.rdram.data() + kEeWork0Addr + 0x12u, "\0\0", 2u); + writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x14u, sjxHandle); + writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x18u, kChunkDataAddr); + writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x1Cu, kChunkLen); + writeGuestU32(env.rdram.data(), kEeWork0Addr + kWorkLen - sizeof(uint32_t), 1u); + + const Ps2SifDmaTransfer desc0{ + kEeWork0Addr, + kIopWork0Addr, + static_cast(kWorkLen), + 0}; + std::memcpy(env.rdram.data() + kDesc0Addr, &desc0, sizeof(desc0)); + + setRegU32(env.ctx, 4, kDesc0Addr); + setRegU32(env.ctx, 5, 1u); + ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime); + t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the SJX transport"); + t.Equals(env.rdram[kEeWork0Addr + 0x11u], static_cast(0u), + "SJX DMA ack should still rewrite the response line to room"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 1u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x429u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 8u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), 0u, + "active PS2RNA playback should drain remote SJRMT data instead of leaving it queued forever"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 0u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x429u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 8u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), kWorkLen, + "drained PS2RNA playback should return remote SJRMT room to full capacity"); + }); + tc.Run("resetSifState seeds boot-ready SIF registers", [](TestCase &t) { TestEnv env; @@ -321,6 +739,142 @@ void register_ps2_sif_dma_tests() t.Equals(static_cast(rd.size), kSize, "receive metadata size should be populated"); }); + tc.Run("sceSifGetOtherData preserves live sound-status sums when compat backfill is enabled", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kRdAddr = 0x00023300u; + constexpr uint32_t kDstAddr = 0x00023400u; + constexpr uint32_t kSize = 0x42u; + constexpr uint32_t kPrimarySeCheckAddr = 0x01E0EF10u; + constexpr uint32_t kPrimaryMidiCheckAddr = 0x01E0EF20u; + constexpr uint32_t kMidiSumOffset = 0x1Eu; + constexpr uint32_t kSeSumOffset = 0x26u; + constexpr uint32_t kBank = 1u; + + PS2SoundDriverCompatLayout compat{}; + compat.primarySeCheckAddr = kPrimarySeCheckAddr; + compat.primaryMidiCheckAddr = kPrimaryMidiCheckAddr; + ps2_syscalls::setSoundDriverCompatLayout(compat); + + constexpr uint32_t kClientAddr = 0x00023500u; + constexpr uint32_t kRecvAddr = 0x00023600u; + constexpr uint32_t kSid = 1u; + + ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, kSid); + setRegU32(env.ctx, 6, 0u); + ps2_syscalls::SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for sound-driver sid"); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x12u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, 0u); + setRegU32(env.ctx, 8, 0u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t kSrcAddr = readGuestU32(env.rdram.data(), kRecvAddr); + + std::memset(env.rdram.data() + kDstAddr, 0, kSize); + std::memset(env.rdram.data() + kRdAddr, 0, sizeof(SifRpcReceiveData)); + + writeGuestS16(env.rdram.data(), kSrcAddr + kSeSumOffset + (kBank * 2u), static_cast(0x1357)); + writeGuestS16(env.rdram.data(), kSrcAddr + kMidiSumOffset + (kBank * 2u), static_cast(0x2468)); + + writeGuestS16(env.rdram.data(), kPrimarySeCheckAddr + (kBank * 2u), static_cast(0x7B7B)); + writeGuestS16(env.rdram.data(), kPrimaryMidiCheckAddr + (kBank * 2u), static_cast(0x6A6A)); + + setRegU32(env.ctx, 4, kRdAddr); + setRegU32(env.ctx, 5, kSrcAddr); + setRegU32(env.ctx, 6, kDstAddr); + setRegU32(env.ctx, 7, kSize); + ps2_stubs::sceSifGetOtherData(env.rdram.data(), &env.ctx, &env.runtime); + + t.Equals(getRegS32(env.ctx, 2), 0, + "sceSifGetOtherData should succeed for sound-status transfer"); + t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kSeSumOffset + (kBank * 2u)), + static_cast(0x1357), + "live se_sum for the active bank should not be clobbered by compat check arrays"); + t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kBank * 2u)), + static_cast(0x2468), + "live midi_sum for the active bank should not be clobbered by compat check arrays"); + }); + + tc.Run("sceSifGetOtherData backfills zero sound-status sums for later banks", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kRdAddr = 0x00023700u; + constexpr uint32_t kDstAddr = 0x00023800u; + constexpr uint32_t kSize = 0x42u; + constexpr uint32_t kPrimarySeCheckAddr = 0x01E0EF10u; + constexpr uint32_t kPrimaryMidiCheckAddr = 0x01E0EF20u; + constexpr uint32_t kMidiSumOffset = 0x1Eu; + constexpr uint32_t kSeSumOffset = 0x26u; + constexpr uint32_t kLiveBank = 0u; + constexpr uint32_t kPendingBank = 1u; + + PS2SoundDriverCompatLayout compat{}; + compat.primarySeCheckAddr = kPrimarySeCheckAddr; + compat.primaryMidiCheckAddr = kPrimaryMidiCheckAddr; + ps2_syscalls::setSoundDriverCompatLayout(compat); + + constexpr uint32_t kClientAddr = 0x00023900u; + constexpr uint32_t kRecvAddr = 0x00023A00u; + + ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 1u); + setRegU32(env.ctx, 6, 0u); + ps2_syscalls::SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for sound-driver sid"); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x12u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, 0u); + setRegU32(env.ctx, 8, 0u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 4u); + setRegU32(env.ctx, 11, 0u); + ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t kSrcAddr = readGuestU32(env.rdram.data(), kRecvAddr); + + std::memset(env.rdram.data() + kDstAddr, 0, kSize); + std::memset(env.rdram.data() + kRdAddr, 0, sizeof(SifRpcReceiveData)); + + writeGuestS16(env.rdram.data(), kSrcAddr + kSeSumOffset + (kLiveBank * 2u), static_cast(0x1111)); + writeGuestS16(env.rdram.data(), kSrcAddr + kMidiSumOffset + (kLiveBank * 2u), static_cast(0x2222)); + + writeGuestS16(env.rdram.data(), kPrimarySeCheckAddr + (kPendingBank * 2u), static_cast(0x3333)); + writeGuestS16(env.rdram.data(), kPrimaryMidiCheckAddr + (kPendingBank * 2u), static_cast(0x4444)); + + setRegU32(env.ctx, 4, kRdAddr); + setRegU32(env.ctx, 5, kSrcAddr); + setRegU32(env.ctx, 6, kDstAddr); + setRegU32(env.ctx, 7, kSize); + ps2_stubs::sceSifGetOtherData(env.rdram.data(), &env.ctx, &env.runtime); + + t.Equals(getRegS32(env.ctx, 2), 0, + "sceSifGetOtherData should succeed for later-bank sound-status transfer"); + t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kSeSumOffset + (kLiveBank * 2u)), + static_cast(0x1111), + "existing live se_sum values should remain intact"); + t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kLiveBank * 2u)), + static_cast(0x2222), + "existing live midi_sum values should remain intact"); + t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kSeSumOffset + (kPendingBank * 2u)), + static_cast(0x3333), + "zero se_sum slots should backfill from compat tables for later banks"); + t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kPendingBank * 2u)), + static_cast(0x4444), + "zero midi_sum slots should backfill from compat tables for later banks"); + }); + tc.Run("sceSifGetOtherData rejects unsupported guest segments", [](TestCase &t) { TestEnv env; diff --git a/ps2xTest/src/ps2_sif_rpc_tests.cpp b/ps2xTest/src/ps2_sif_rpc_tests.cpp index 3aede2f4..f5904723 100644 --- a/ps2xTest/src/ps2_sif_rpc_tests.cpp +++ b/ps2xTest/src/ps2_sif_rpc_tests.cpp @@ -89,6 +89,8 @@ namespace TestEnv() : rdram(PS2_RAM_SIZE, 0) { ps2_stubs::resetSifState(); + ps2_syscalls::resetSoundDriverRpcState(); + ps2_syscalls::clearSoundDriverCompatLayout(); std::memset(&ctx, 0, sizeof(ctx)); } }; @@ -309,14 +311,14 @@ void register_ps2_sif_rpc_tests() t.Equals(getRegU32Result(env.ctx, 2), 0u, "removing the same queue twice should return 0"); }); - tc.Run("sid1 nowait RPC 0x12/0x13 returns expected pointers and signals sema", [](TestCase &t) + tc.Run("snddrv state RPC returns stable buffers and signals sema", [](TestCase &t) { TestEnv env; constexpr uint32_t kClientAddr = 0x00028000u; constexpr uint32_t kSemaParamAddr = 0x00028100u; constexpr uint32_t kRecvAddr = 0x00028200u; - constexpr uint32_t kSid = 1u; + constexpr uint32_t kSid = IOP_SID_SNDDRV_STATE; SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); @@ -339,11 +341,7 @@ void register_ps2_sif_rpc_tests() setRegU32(env.ctx, 5, kSid); setRegU32(env.ctx, 6, 0u); SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for sid 1"); - - SifRpcClientData client = readGuestStruct(env.rdram.data(), kClientAddr); - client.hdr.sema_id = semaId; - writeGuestStruct(env.rdram.data(), kClientAddr, client); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for snddrv state sid"); setRegU32(env.ctx, 4, static_cast(semaId)); PollSema(env.rdram.data(), &env.ctx, &env.runtime); @@ -351,7 +349,7 @@ void register_ps2_sif_rpc_tests() std::memset(env.rdram.data() + kRecvAddr, 0, 16u); setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x12u); + setRegU32(env.ctx, 5, IOP_RPC_SNDDRV_GET_STATUS_ADDR); setRegU32(env.ctx, 6, K_SIF_RPC_MODE_NOWAIT); setRegU32(env.ctx, 7, 0u); setRegU32(env.ctx, 8, 0u); @@ -359,10 +357,11 @@ void register_ps2_sif_rpc_tests() setRegU32(env.ctx, 10, 16u); setRegU32(env.ctx, 11, 0u); setRegU32(env.ctx, 29, K_STACK_ADDR); - writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, 0u); + writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, static_cast(semaId)); SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifCallRpc(0x12) should succeed"); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr), 0x00012000u, "rpc 0x12 should return SND_STATUS pointer"); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifCallRpc(sound status) should succeed"); + const uint32_t statusAddr = readGuestStruct(env.rdram.data(), kRecvAddr); + t.IsTrue(statusAddr != 0u, "rpc 0x12 should return a nonzero sound-status pointer"); setRegU32(env.ctx, 4, static_cast(semaId)); PollSema(env.rdram.data(), &env.ctx, &env.runtime); @@ -370,20 +369,105 @@ void register_ps2_sif_rpc_tests() std::memset(env.rdram.data() + kRecvAddr, 0, 16u); setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x13u); + setRegU32(env.ctx, 5, IOP_RPC_SNDDRV_GET_ADDR_TABLE); setRegU32(env.ctx, 6, K_SIF_RPC_MODE_NOWAIT); setRegU32(env.ctx, 7, 0u); setRegU32(env.ctx, 8, 0u); setRegU32(env.ctx, 9, kRecvAddr); setRegU32(env.ctx, 10, 16u); setRegU32(env.ctx, 11, 0u); + writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, static_cast(semaId)); SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifCallRpc(0x13) should succeed"); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr), 0x00012100u, "rpc 0x13 should return address-table pointer"); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifCallRpc(sound addr table) should succeed"); + const uint32_t addrTableAddr = readGuestStruct(env.rdram.data(), kRecvAddr); + t.IsTrue(addrTableAddr != 0u, "rpc 0x13 should return a nonzero address-table pointer"); + t.IsTrue(addrTableAddr != statusAddr, "sound-status and address-table pointers should be distinct"); setRegU32(env.ctx, 4, static_cast(semaId)); PollSema(env.rdram.data(), &env.ctx, &env.runtime); t.Equals(getRegS32(env.ctx, 2), KE_OK, "each nowait rpc should signal completion sema"); + + std::memset(env.rdram.data() + kRecvAddr, 0, 16u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, IOP_RPC_SNDDRV_GET_STATUS_ADDR); + setRegU32(env.ctx, 6, K_SIF_RPC_MODE_NOWAIT); + setRegU32(env.ctx, 7, 0u); + setRegU32(env.ctx, 8, 0u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 16u); + setRegU32(env.ctx, 11, 0u); + writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, static_cast(semaId)); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr), statusAddr, + "sound-status pointer should remain stable across repeated rpc 0x12 calls"); + }); + + tc.Run("snddrv state RPC returns low guest sound-driver addresses", [](TestCase &t) + { + TestEnv env; + + constexpr uint32_t kClientAddr = 0x00028300u; + constexpr uint32_t kSemaParamAddr = 0x00028400u; + constexpr uint32_t kRecvAddr = 0x00028500u; + constexpr uint32_t kSid = IOP_SID_SNDDRV_STATE; + + SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + + const uint32_t semaParam[6] = {0u, 1u, 0u, 0u, 0u, 0u}; + std::memcpy(env.rdram.data() + kSemaParamAddr, semaParam, sizeof(semaParam)); + + setRegU32(env.ctx, 4, kSemaParamAddr); + CreateSema(env.rdram.data(), &env.ctx, &env.runtime); + const int32_t semaId = getRegS32(env.ctx, 2); + t.IsTrue(semaId > 0, "CreateSema should return a positive semaphore id"); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, kSid); + setRegU32(env.ctx, 6, 0u); + SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for snddrv state sid"); + + SifRpcClientData client = readGuestStruct(env.rdram.data(), kClientAddr); + client.hdr.sema_id = semaId; + writeGuestStruct(env.rdram.data(), kClientAddr, client); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, IOP_RPC_SNDDRV_GET_STATUS_ADDR); + setRegU32(env.ctx, 6, K_SIF_RPC_MODE_NOWAIT); + setRegU32(env.ctx, 7, 0u); + setRegU32(env.ctx, 8, 0u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 16u); + setRegU32(env.ctx, 11, 0u); + setRegU32(env.ctx, 29, K_STACK_ADDR); + writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, static_cast(semaId)); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t statusAddr = readGuestStruct(env.rdram.data(), kRecvAddr); + t.IsTrue(statusAddr > 0u && statusAddr < 0x00200000u, + "rpc 0x12 should return a low guest address like an IOP pointer"); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, IOP_RPC_SNDDRV_GET_ADDR_TABLE); + setRegU32(env.ctx, 6, K_SIF_RPC_MODE_NOWAIT); + setRegU32(env.ctx, 7, 0u); + setRegU32(env.ctx, 8, 0u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 16u); + setRegU32(env.ctx, 11, 0u); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + const uint32_t addrTableAddr = readGuestStruct(env.rdram.data(), kRecvAddr); + t.IsTrue(addrTableAddr > 0u && addrTableAddr < 0x00200000u, + "rpc 0x13 should return a low guest address like an IOP pointer"); + + const uint32_t hdBaseAddr = readGuestStruct(env.rdram.data(), addrTableAddr + 0u); + const uint32_t sqBaseAddr = readGuestStruct(env.rdram.data(), addrTableAddr + 4u); + const uint32_t dataBaseAddr = readGuestStruct(env.rdram.data(), addrTableAddr + 8u); + t.IsTrue(hdBaseAddr > 0u && hdBaseAddr < 0x00200000u, + "sound-driver hd base should stay in low guest address space"); + t.IsTrue(sqBaseAddr > hdBaseAddr && sqBaseAddr < 0x00200000u, + "sound-driver sq base should be a later low guest address"); + t.IsTrue(dataBaseAddr > sqBaseAddr && dataBaseAddr < 0x00200000u, + "sound-driver data base should be a later low guest address"); }); tc.Run("SifCallRpc falls back to stack ABI when register pack is implausible", [](TestCase &t) From 945df438aede5e2f16f4a60d635de2dc5d1554b5 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Tue, 31 Mar 2026 23:37:10 -0300 Subject: [PATCH 08/30] feat: added game override for code veronica --- ps2xRuntime/src/lib/game_overrides.cpp | 71 +++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/ps2xRuntime/src/lib/game_overrides.cpp b/ps2xRuntime/src/lib/game_overrides.cpp index 262f9738..285388ef 100644 --- a/ps2xRuntime/src/lib/game_overrides.cpp +++ b/ps2xRuntime/src/lib/game_overrides.cpp @@ -3,6 +3,7 @@ #include "ps2_runtime_calls.h" #include "ps2_stubs.h" #include "ps2_syscalls.h" +#include "ps2_log.h" #include #include #include @@ -175,6 +176,10 @@ namespace ps2_game_overrides void applyMatching(PS2Runtime &runtime, const std::string &elfPath, uint32_t entry) { + ps2_syscalls::clearSoundDriverCompatLayout(); + ps2_syscalls::clearDtxCompatLayout(); + ps2_stubs::clearMpegCompatLayout(); + std::vector descriptors; { std::lock_guard lock(registryMutex()); @@ -233,14 +238,76 @@ namespace ps2_game_overrides const char *name = (descriptor.name && descriptor.name[0] != '\0') ? descriptor.name : "unnamed"; - std::cout << "[game_overrides] applying '" << name << "'" << std::endl; + RUNTIME_LOG("[game_overrides] applying '" << name << "'"); descriptor.apply(runtime); ++appliedCount; } if (appliedCount > 0) { - std::cout << "[game_overrides] applied " << appliedCount << " matching override(s)." << std::endl; + RUNTIME_LOG("[game_overrides] applied " << appliedCount << " matching override(s)."); } } } + +namespace +{ + void applyRecvxSoundDriverCompat(PS2Runtime &runtime) + { + (void)runtime; + + // Trying to explain a bit of Resident Evil Code: Veronica X sound-driver guest globals. + // Update these guest addresses/callback PCs when porting the override to another build: + // - checksum tables back the SE/MIDI status values mirrored through the snddrv RPC stubs + // - busyFlagAddr is the guest-side "work in progress" word cleared on completion + // - completion/clearBusy callbacks are guest PCs reached when async snddrv work finishes + PS2SoundDriverCompatLayout layout{}; + layout.primarySeCheckAddr = 0x01E0EF10u; + layout.primaryMidiCheckAddr = 0x01E0EF20u; + layout.fallbackSeCheckAddr = 0x01E1EF10u; + layout.fallbackMidiCheckAddr = 0x01E1EF20u; + layout.busyFlagAddr = 0x01E212C8u; + layout.completionCallbacks = {0x002EAC20u, 0x002EAC30u, 0x002FAC20u, 0x002FAC30u}; + layout.clearBusyCallbacks = {0x002EAC30u, 0x002FAC30u}; + ps2_syscalls::setSoundDriverCompatLayout(layout); + } + + void applyRecvxDtxCompat(PS2Runtime &runtime) + { + (void)runtime; + + // Trying to explain abit of Resident Evil Code: Veronica X DTX guest layout. + // Update these guest values when porting the middleware override to another build: + // - rpcSid identifies the DTX RPC service the guest binds/registers + // - urpc object/table addresses back the SJX/PS2RNA/SJRMT command tables + // - dispatcherFuncAddr is the guest-side DTX RPC handler used for URPC dispatch + PS2DtxCompatLayout layout{}; + layout.rpcSid = 0x7D000000u; + layout.urpcObjBase = 0x01F18000u; + layout.urpcObjLimit = 0x01F1FF00u; + layout.urpcObjStride = 0x20u; + layout.urpcFnTableBase = 0x0034FED0u; + layout.urpcObjTableBase = 0x0034FFD0u; + layout.dispatcherFuncAddr = 0x002FABC0u; + ps2_syscalls::setDtxCompatLayout(layout); + } + + void applyRecvxMpegCompat(PS2Runtime &runtime) + { + (void)runtime; + + // this is temporary so ignore for now + PS2MpegCompatLayout layout{}; + layout.mpegObjectAddr = 0x01E27140u; + layout.videoStateAddr = 0x01E271E8u; + layout.movieStateAddr = 0x01E21914u; + layout.syntheticFramesBeforeEnd = 1u; + layout.finishedVideoStateValue = 3u; + layout.finishedMovieStateValue = 3u; + ps2_stubs::setMpegCompatLayout(layout); + } + + PS2_REGISTER_GAME_OVERRIDE("RECVX sound-driver compat", "slus_201.84", 0u, 0u, &applyRecvxSoundDriverCompat); + PS2_REGISTER_GAME_OVERRIDE("RECVX DTX compat", "slus_201.84", 0u, 0u, &applyRecvxDtxCompat); + PS2_REGISTER_GAME_OVERRIDE("RECVX MPEG compat", "slus_201.84", 0u, 0u, &applyRecvxMpegCompat); +} From 7f97d1a1e2d541785ec7825621009496aba94b1a Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 00:00:47 -0300 Subject: [PATCH 09/30] feat: apply vita patch --- ps2xRuntime/CMakeLists.txt | 23 +++++ ps2xRuntime/src/lib/ps2_runtime.cpp | 138 +++++++++++++++++++++----- ps2xRuntime/src/main.cpp | 148 +++++++++++++++++++--------- 3 files changed, 239 insertions(+), 70 deletions(-) diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index c6c04f75..cde6be76 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -5,6 +5,17 @@ project(PS2Runtime VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(PS2X_IS_VITA OFF) +if((CMAKE_C_COMPILER MATCHES "arm-vita-eabi") OR + (CMAKE_CXX_COMPILER MATCHES "arm-vita-eabi")) + set(PS2X_IS_VITA ON) +endif() + +option(PS2X_VITA_CREATE_PACKAGE "Create Vita eboot/vpk targets" OFF) +set(PS2X_VITA_APP_NAME "PS2 Retro X" CACHE STRING "Display name for the Vita bubble") +set(PS2X_VITA_TITLEID "RANJ00001" CACHE STRING "9-character Vita title id") +set(PS2X_VITA_VERSION "01.00" CACHE STRING "Vita app version") + include(FetchContent) set(FETCHCONTENT_QUIET FALSE) set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) @@ -76,6 +87,18 @@ target_link_libraries(ps2EntryRunner raylib ) +if(PS2X_IS_VITA) + set(VITA_MKSFOEX_FLAGS "${VITA_MKSFOEX_FLAGS} -d PARENTAL_LEVEL=1") + + if(PS2X_VITA_CREATE_PACKAGE) + vita_create_self(eboot.bin ps2EntryRunner UNSAFE) + vita_create_vpk(ps2EntryRunner.vpk ${PS2X_VITA_TITLEID} eboot.bin + VERSION ${PS2X_VITA_VERSION} + NAME ${PS2X_VITA_APP_NAME} + ) + endif() +endif() + if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") EnableFastReleaseMode(ps2_runtime) EnableFastReleaseMode(ps2EntryRunner) diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 63d46953..34721482 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -1,9 +1,16 @@ #include "ps2_runtime.h" -#include "ps2_syscalls.h" +#include "ps2_log.h" #include "ps2_stubs.h" +#include "ps2_syscalls.h" #include "game_overrides.h" -#include "ps2_log.h" #include "ps2_runtime_macros.h" +#include "runtime/ps2_gs_gpu.h" +#include "ThreadNaming.h" + +#include +#if defined(PLATFORM_VITA) +#include +#endif #include #include #include @@ -16,9 +23,6 @@ #include #include #include -#include "raylib.h" -#include "runtime/ps2_gs_gpu.h" -#include namespace ps2_stubs { @@ -135,6 +139,11 @@ namespace thread_local DispatchHistory g_dispatchHistory; thread_local std::unordered_map g_guestExecutionDepths; +#if defined(PLATFORM_VITA) + std::mutex g_pibInitMutex; + std::atomic g_pibInitialized{false}; +#endif + void pushDispatchPc(uint32_t pc) { DispatchHistory &h = g_dispatchHistory; @@ -242,6 +251,19 @@ namespace return {}; } +#if defined(PLATFORM_VITA) + const std::string generic = path.generic_string(); + const std::size_t colon = generic.find(':'); + if (colon != std::string::npos && colon != 0u) + { + const std::size_t slash = generic.find_first_of("/\\"); + if (slash == std::string::npos || colon < slash) + { + return path.lexically_normal(); + } + } +#endif + std::error_code ec; const std::filesystem::path absolute = std::filesystem::absolute(path, ec); if (ec) @@ -251,6 +273,39 @@ namespace return absolute.lexically_normal(); } +#if defined(PLATFORM_VITA) + bool ensurePibInitialized() + { + if (g_pibInitialized.load(std::memory_order_acquire)) + { + return true; + } + + std::lock_guard lock(g_pibInitMutex); + if (g_pibInitialized.load(std::memory_order_relaxed)) + { + return true; + } + + try + { + pibInit(static_cast(PIB_SHACCCG | PIB_GET_PROC_ADDR_CORE)); + g_pibInitialized.store(true, std::memory_order_release); + return true; + } + catch (const std::exception &e) + { + std::cerr << "[vita] pibInit failed: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "[vita] pibInit failed: unknown exception" << std::endl; + } + + return false; + } +#endif + PS2Runtime::IoPaths &runtimeIoPaths() { static PS2Runtime::IoPaths paths = []() @@ -535,16 +590,27 @@ PS2Runtime::PS2Runtime() PS2Runtime::~PS2Runtime() { - requestStop(); - ps2_syscalls::detachAllGuestHostThreads(); - if (IsWindowReady()) + try { - CloseWindow(); - } + requestStop(); + ps2_syscalls::detachAllGuestHostThreads(); + if (IsWindowReady()) + { + CloseWindow(); + } - m_loadedModules.clear(); + m_loadedModules.clear(); - m_functionTable.clear(); + m_functionTable.clear(); + } + catch (const std::exception &e) + { + std::cerr << "[~PS2Runtime] cleanup exception: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "[~PS2Runtime] cleanup exception: unknown" << std::endl; + } } bool PS2Runtime::ensureCoreSubsystemsInitialized() @@ -584,25 +650,45 @@ bool PS2Runtime::ensureCoreSubsystemsInitialized() bool PS2Runtime::initialize(const char *title) { - if (!m_memory.initialize()) + try { - std::cerr << "Failed to initialize PS2 memory" << std::endl; - return false; - } + if (!m_memory.initialize()) + { + std::cerr << "Failed to initialize PS2 memory" << std::endl; + return false; + } - if (!ensureCoreSubsystemsInitialized()) + if (!ensureCoreSubsystemsInitialized()) + { + std::cerr << "Failed to bind runtime core subsystems" << std::endl; + return false; + } + +#if defined(PLATFORM_VITA) + if (!ensurePibInitialized()) + { + return false; + } +#endif + + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(FB_WIDTH, DEFAULT_DISPLAY_HEIGHT, title); + InitAudioDevice(); + m_audioBackend.setAudioReady(IsAudioDeviceReady()); + SetTargetFPS(60); + + return true; + } + catch (const std::exception &e) { - std::cerr << "Failed to bind runtime core subsystems" << std::endl; - return false; + std::cerr << "Failed to initialize PS2 runtime: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "Failed to initialize PS2 runtime: unknown exception" << std::endl; } - SetConfigFlags(FLAG_WINDOW_RESIZABLE); - InitWindow(FB_WIDTH, DEFAULT_DISPLAY_HEIGHT, title); - InitAudioDevice(); - m_audioBackend.setAudioReady(IsAudioDeviceReady()); - SetTargetFPS(60); - - return true; + return false; } bool PS2Runtime::loadELF(const std::string &elfPath) diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index dc3425b9..adb145d7 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -1,6 +1,7 @@ #include "ps2_runtime.h" #include "register_functions.h" #include "games_database.h" + #ifdef _DEBUG #include "ps2_log.h" #endif @@ -8,66 +9,125 @@ #include #include #include +#include -std::string normalizeGameId(const std::string& folderName) -{ - std::string result = folderName; - - size_t underscore = result.find('_'); - if (underscore != std::string::npos) - result[underscore] = '-'; - - size_t dot = result.find('.'); - if (dot != std::string::npos) - result.erase(dot, 1); - - return result; -} - -int main(int argc, char* argv[]) +namespace { - if (argc < 2) + void setupTerminateLogger() // to help on release build crashs { - std::cout << "Usage: " << argv[0] << " " << std::endl; - return 1; + std::set_terminate([]() + { + std::cerr << "[terminate] unhandled exception" << std::endl; + const std::exception_ptr ep = std::current_exception(); + if (ep) + { + try + { + std::rethrow_exception(ep); + } + catch (const std::system_error &e) + { + std::cerr << "[terminate] std::system_error code=" << e.code().value() + << " category=" << e.code().category().name() + << " message=" << e.what() << std::endl; + } + catch (const std::exception &e) + { + std::cerr << "[terminate] std::exception: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "[terminate] non-std exception" << std::endl; + } + } + std::abort(); }); } - std::string elfPath = argv[1]; - std::filesystem::path pathObj(elfPath); - std::string folderName = pathObj.filename().string(); - std::string normalizedId = normalizeGameId(folderName); + std::string normalizeGameId(const std::string &folderName) + { + std::string result = folderName; - std::string windowTitle = "PS2-Recomp | "; - const char* gameName = getGameName(normalizedId); + size_t underscore = result.find('_'); + if (underscore != std::string::npos) + result[underscore] = '-'; - if (gameName) - { - windowTitle += std::string(gameName) + " | " + folderName; - } - else - { - windowTitle += folderName; + size_t dot = result.find('.'); + if (dot != std::string::npos) + result.erase(dot, 1); + + return result; } - PS2Runtime runtime; - if (!runtime.initialize(windowTitle.c_str())) + std::filesystem::path getExecutablePath(int argc, char *argv[]) { - std::cerr << "Failed to initialize PS2 runtime" << std::endl; - return 1; +#if defined(PS2X_DEFAULT_BOOT_ELF) + std::cout << "Using default boot file" << std::endl; + return std::filesystem::current_path() + PS2X_DEFAULT_BOOT_ELF; +#else + std::cout << "Using agrs" << std::endl; + if (argc > 0) + { + return std::filesystem::path(argv[0]).parent_path(); + } + + throw std::runtime_error("Unable to determine executable path"); +#endif } +} - registerAllFunctions(runtime); +int main(int argc, char *argv[]) +{ + setupTerminateLogger(); - if (!runtime.loadELF(elfPath)) + try { - std::cerr << "Failed to load ELF file: " << elfPath << std::endl; - return 1; - } + std::filesystem::path exePath = getExecutablePath(argc, argv); + + std::string elfPath = exePath.filename().string(); + std::filesystem::path pathObj(elfPath); + std::string folderName = pathObj.filename().string(); + std::string normalizedId = normalizeGameId(folderName); + + std::string windowTitle = "PS2-Recomp | "; + const char *gameName = getGameName(normalizedId); + + if (gameName) + { + windowTitle += std::string(gameName) + " | " + folderName; + } + else + { + windowTitle += folderName; + } + + PS2Runtime runtime; + if (!runtime.initialize(windowTitle.c_str())) + { + std::cerr << "Failed to initialize PS2 runtime" << std::endl; + return 1; + } + + registerAllFunctions(runtime); - runtime.run(); + if (!runtime.loadELF(elfPath)) + { + std::cerr << "Failed to load ELF file: " << elfPath << std::endl; + return 1; + } + + runtime.run(); #ifdef _DEBUG - ps2_log::print_saved_location(); + ps2_log::print_saved_location(); #endif - return 0; + return 0; + } + catch (const std::exception &e) + { + std::cerr << "[main] fatal exception: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "[main] fatal exception: unknown" << std::endl; + } } \ No newline at end of file From f278ad04d37894d1cd1ff507fef9eba87de29e25 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 00:15:21 -0300 Subject: [PATCH 10/30] feat: flags to disable build --- CMakeLists.txt | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fafc59aa..df7b01b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,11 @@ project("PS2 Retro X") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -option(PS2X_RUNTIME OFF) +option(PS2X_BUILD_RECOMP "Build ps2xRecomp" ON) +option(PS2X_BUILD_RUNTIME "Build ps2xRuntime" ON) +option(PS2X_BUILD_ANALYZER "Build ps2xAnalyzer" ON) +option(PS2X_BUILD_TEST "Build ps2xTest" ON) +option(PS2X_BUILD_STUDIO "Build ps2xStudio" ON) # ARM64 support using sse2neon if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64") @@ -39,14 +43,28 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64") if(COMPILER_SUPPORTS_CRYPTO_CRC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+fp+simd+crypto+crc") message(STATUS "Crypto and CRC extensions enabled") + else() + add_compile_options(-march=armv8-a+fp+simd) endif() endif() endif() -add_subdirectory("ps2xRecomp") +if(PS2X_BUILD_RECOMP) + add_subdirectory("ps2xRecomp") +endif() + +if(PS2X_BUILD_RUNTIME) + add_subdirectory("ps2xRuntime") +endif() -add_subdirectory("ps2xRuntime") +if(PS2X_BUILD_ANALYZER) + add_subdirectory("ps2xAnalyzer") +endif() + +if(PS2X_BUILD_TEST) + add_subdirectory("ps2xTest") +endif() -add_subdirectory("ps2xAnalyzer") -add_subdirectory("ps2xTest") -add_subdirectory("ps2xStudio") +if(PS2X_BUILD_STUDIO) + add_subdirectory("ps2xStudio") +endif() \ No newline at end of file From 76194f9f0443e64c3da113edbed68b407bc9f3ae Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 02:21:55 -0300 Subject: [PATCH 11/30] feat: fix merges feat: break a lot of tests --- ps2xRuntime/CMakeLists.txt | 1 + ps2xRuntime/include/ps2_stubs.h | 575 +----------- ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp | 884 +++++++++--------- ps2xRuntime/src/lib/Kernel/Stubs/Common.h | 4 + .../src/lib/Kernel/Syscalls/Interrupt.cpp | 1 + ps2xRuntime/src/lib/ps2_runtime.cpp | 5 +- ps2xRuntime/src/runner/games_database.cpp | 804 ++++++++++++++++ ps2xTest/src/pad_input_tests.cpp | 3 + ps2xTest/src/ps2_gs_tests.cpp | 1 + ps2xTest/src/ps2_runtime_expansion_tests.cpp | 61 +- 10 files changed, 1285 insertions(+), 1054 deletions(-) create mode 100644 ps2xRuntime/src/runner/games_database.cpp diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index cde6be76..3f358517 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -78,6 +78,7 @@ endif() target_include_directories(ps2_runtime PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel ) target_link_libraries(ps2_runtime PRIVATE raylib) diff --git a/ps2xRuntime/include/ps2_stubs.h b/ps2xRuntime/include/ps2_stubs.h index 11e6419f..7ad07b0a 100644 --- a/ps2xRuntime/include/ps2_stubs.h +++ b/ps2xRuntime/include/ps2_stubs.h @@ -1,564 +1,43 @@ -#include "Common.h" -#include "CD.h" +#pragma once -namespace ps2_stubs -{ -void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t a0 = getRegU32(ctx, 4); // usually lbn - const uint32_t a1 = getRegU32(ctx, 5); // usually sector count - const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer - - struct CdReadArgs - { - uint32_t lbn = 0; - uint32_t sectors = 0; - uint32_t buf = 0; - const char *tag = ""; - }; - - auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t - { - const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); - if (requested == 0) - { - return 0; - } - - const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); - const uint64_t clamped = std::min(requested, maxBytes); - return static_cast(clamped); - }; - - auto tryRead = [&](const CdReadArgs &args) -> bool - { - const uint32_t offset = args.buf & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(args.sectors, offset); - if (bytes == 0) - { - return true; - } - - return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); - }; - - CdReadArgs selected{a0, a1, a2, "a0/a1/a2"}; - bool ok = tryRead(selected); - - if (!ok) - { - // Some game-side wrappers use a nonstandard register layout. - // If primary decode does not resolve to a known LBN, try safe alternatives. - constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize; - if (!isResolvableCdLbn(selected.lbn)) - { - const std::array alternatives = { - CdReadArgs{a2, a1, a0, "a2/a1/a0"}, - CdReadArgs{a0, a2, a1, "a0/a2/a1"}, - CdReadArgs{a1, a0, a2, "a1/a0/a2"}, - CdReadArgs{a1, a2, a0, "a1/a2/a0"}, - CdReadArgs{a2, a0, a1, "a2/a0/a1"}}; - - for (const CdReadArgs &candidate : alternatives) - { - if (candidate.sectors > kMaxReasonableSectors) - { - continue; - } - if (!isResolvableCdLbn(candidate.lbn)) - { - continue; - } - - if (tryRead(candidate)) - { - static uint32_t recoverLogCount = 0; - if (recoverLogCount < 16) - { - RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag - << " (pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << ")" << std::endl); - ++recoverLogCount; - } - selected = candidate; - ok = true; - break; - } - } - } - - if (!ok) - { - const uint32_t offset = a2 & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(a1, offset); - if (bytes > 0) - { - std::memset(rdram + offset, 0, bytes); - } - - static uint32_t unresolvedLogCount = 0; - if (unresolvedLogCount < 32) - { - std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << std::endl; - ++unresolvedLogCount; - } - } - } - - if (ok) - { - g_cdStreamingLbn = selected.lbn + selected.sectors; - setReturnS32(ctx, 1); // command accepted/success - return; - } - - setReturnS32(ctx, 0); -} - - -void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); // 0 = completed/not busy -} - - -void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_lastCdError); -} - - -void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceCdRI", rdram, ctx, runtime); -} - - -void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceCdRM", rdram, ctx, runtime); -} - - -void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 2); -} - -void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - // SCECdPS2DVD - setReturnS32(ctx, 0x14); -} - -void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, g_cdStreamingLbn); -} - -void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t tocAddr = getRegU32(ctx, 4); - if (uint8_t *toc = getMemPtr(rdram, tocAddr)) - { - std::memset(toc, 0, 1024); - } - setReturnS32(ctx, 1); -} - -void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdInitialized = true; - g_lastCdError = 0; - setReturnS32(ctx, 1); -} - -void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t lsn = getRegU32(ctx, 4); - uint32_t posAddr = getRegU32(ctx, 5); - uint8_t *pos = getMemPtr(rdram, posAddr); - if (!pos) - { - setReturnS32(ctx, 0); - return; - } - - uint32_t adjusted = lsn + 150; - const uint32_t minutes = adjusted / (60 * 75); - adjusted %= (60 * 75); - const uint32_t seconds = adjusted / 75; - const uint32_t sectors = adjusted % 75; - - pos[0] = toBcd(minutes); - pos[1] = toBcd(seconds); - pos[2] = toBcd(sectors); - pos[3] = 0; - setReturnS32(ctx, 1); -} - -void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdMode = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 2); -} - -void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t posAddr = getRegU32(ctx, 4); - const uint8_t *pos = getConstMemPtr(rdram, posAddr); - if (!pos) - { - setReturnS32(ctx, -1); - return; - } - - const uint32_t minutes = fromBcd(pos[0]); - const uint32_t seconds = fromBcd(pos[1]); - const uint32_t sectors = fromBcd(pos[2]); - const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; - const int32_t lsn = static_cast(absolute) - 150; - setReturnS32(ctx, lsn); -} - -void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t chainAddr = getRegU32(ctx, 4); - bool ok = true; - - for (int i = 0; i < 64; ++i) - { - uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); - if (!entry) - { - ok = false; - break; - } - - const uint32_t lbn = entry[0]; - const uint32_t sectors = entry[1]; - const uint32_t buf = entry[2]; - if (lbn == 0xFFFFFFFFu || sectors == 0) - { - break; - } - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } - - if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) - { - ok = false; - break; - } - - g_cdStreamingLbn = lbn + sectors; - } - - setReturnS32(ctx, ok ? 1 : 0); -} - -void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clockAddr = getRegU32(ctx, 4); - uint8_t *clockData = getMemPtr(rdram, clockAddr); - if (!clockData) - { - setReturnS32(ctx, 0); - return; - } - - std::time_t now = std::time(nullptr); - std::tm localTm{}; -#ifdef _WIN32 - localtime_s(&localTm, &now); -#else - localtime_r(&now, &localTm); -#endif - - // sceCdCLOCK format (BCD fields). - clockData[0] = 0; - clockData[1] = toBcd(static_cast(localTm.tm_sec)); - clockData[2] = toBcd(static_cast(localTm.tm_min)); - clockData[3] = toBcd(static_cast(localTm.tm_hour)); - clockData[4] = 0; - clockData[5] = toBcd(static_cast(localTm.tm_mday)); - clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); - clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); - setReturnS32(ctx, 1); -} - -void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - sceCdRead(rdram, ctx, runtime); -} - -void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t fileAddr = getRegU32(ctx, 4); - uint32_t pathAddr = getRegU32(ctx, 5); - const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); - const std::string normalizedPath = normalizeCdPathNoPrefix(path); - static uint32_t traceCount = 0; - const uint32_t callerRa = getRegU32(ctx, 31); - const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); - if (shouldTrace) - { - RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc - << " ra=0x" << callerRa - << " file=0x" << fileAddr - << " pathAddr=0x" << pathAddr - << " path=\"" << sanitizeForLog(path) << "\"" - << std::dec << std::endl); - } - ++traceCount; - - if (path.empty()) - { - static uint32_t emptyPathCount = 0; - if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) - { - std::ostringstream preview; - preview << std::hex; - for (uint32_t i = 0; i < 16; ++i) - { - const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); - preview << (i == 0 ? "" : " ") << static_cast(byte); - } - std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr - << " preview=" << preview.str() - << " ra=0x" << callerRa << std::dec << std::endl; - } - ++emptyPathCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - if (normalizedPath.empty()) - { - static uint32_t emptyNormalizedCount = 0; - if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) - { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (normalized path is empty, root: " << getCdRootPath().string() << ")" - << std::endl; - } - ++emptyNormalizedCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - CdFileEntry entry; - bool found = registerCdFile(path, entry); - CdFileEntry resolvedEntry = entry; - std::string resolvedPath; - - if (!found) - { - static std::string lastFailedPath; - static uint32_t samePathFailCount = 0; - if (path == lastFailedPath) - { - ++samePathFailCount; - } - else - { - lastFailedPath = path; - samePathFailCount = 1; - } - - if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) - { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (root: " << getCdRootPath().string() - << ", repeat=" << samePathFailCount << ")" << std::endl; - } - setReturnS32(ctx, 0); - return; - } - - if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) - { - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - g_cdStreamingLbn = resolvedEntry.baseLbn; - if (shouldTrace) - { - RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) - << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn - << " size=0x" << resolvedEntry.sizeBytes - << " sectors=0x" << resolvedEntry.sectors - << std::dec << std::endl); - } - setReturnS32(ctx, 1); -} +#include +#include "ps2_call_list.h" +#include "runtime/ps2_memory.h" -void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_cdInitialized ? 6 : 0); -} - -void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} +struct R5900Context; +class PS2Runtime; -void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +struct PS2MpegCompatLayout { - uint32_t sectors = getRegU32(ctx, 4); - uint32_t buf = getRegU32(ctx, 5); - uint32_t errAddr = getRegU32(ctx, 7); - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } + uint32_t mpegObjectAddr = 0; + uint32_t videoStateAddr = 0; + uint32_t movieStateAddr = 0; + uint32_t syntheticFramesBeforeEnd = 1u; + uint32_t playingVideoStateValue = 0u; + uint32_t playingMovieStateValue = 2u; + uint32_t finishedVideoStateValue = 3u; + uint32_t finishedMovieStateValue = 3u; - const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); - if (ok) + [[nodiscard]] bool matchesMpegObject(uint32_t addr) const { - g_cdStreamingLbn += sectors; + return mpegObjectAddr != 0u && ((addr & PS2_RAM_MASK) == (mpegObjectAddr & PS2_RAM_MASK)); } - if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + [[nodiscard]] bool hasFinishTargets() const { - *err = ok ? 0 : g_lastCdError; + return videoStateAddr != 0u || movieStateAddr != 0u; } +}; - setReturnS32(ctx, ok ? static_cast(sectors) : 0); -} - -void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} -void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} - -void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +namespace ps2_stubs { - setReturnS32(ctx, 0); -} +#define PS2_DECLARE_STUB(name) void name(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + PS2_STUB_LIST(PS2_DECLARE_STUB) +#undef PS2_DECLARE_STUB -void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void resetSifState(); -void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t statusPtr = getRegU32(ctx, 5); - if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) - { - *status = 0; - } - setReturnS32(ctx, 1); -} + void setMpegCompatLayout(const PS2MpegCompatLayout &layout); + void clearMpegCompatLayout(); } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp index 579e0fa9..a8263316 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp @@ -1,576 +1,564 @@ #include "Common.h" #include "CD.h" -#include "MpegSource.h" namespace ps2_stubs { -void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t a0 = getRegU32(ctx, 4); // usually lbn - const uint32_t a1 = getRegU32(ctx, 5); // usually sector count - const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer - - struct CdReadArgs + void sceCdRead(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - uint32_t lbn = 0; - uint32_t sectors = 0; - uint32_t buf = 0; - const char *tag = ""; - }; + const uint32_t a0 = getRegU32(ctx, 4); // usually lbn + const uint32_t a1 = getRegU32(ctx, 5); // usually sector count + const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer - auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t - { - const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); - if (requested == 0) + struct CdReadArgs { - return 0; - } + uint32_t lbn = 0; + uint32_t sectors = 0; + uint32_t buf = 0; + const char* tag = ""; + }; - const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); - const uint64_t clamped = std::min(requested, maxBytes); - return static_cast(clamped); - }; + auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t + { + const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); + if (requested == 0) + { + return 0; + } - auto tryRead = [&](const CdReadArgs &args) -> bool - { - const uint32_t offset = args.buf & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(args.sectors, offset); - if (bytes == 0) - { - return true; - } + const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); + const uint64_t clamped = std::min(requested, maxBytes); + return static_cast(clamped); + }; + + auto tryRead = [&](const CdReadArgs& args) -> bool + { + const uint32_t offset = args.buf & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(args.sectors, offset); + if (bytes == 0) + { + return true; + } - return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); - }; + return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); + }; - CdReadArgs selected{a0, a1, a2, "a0/a1/a2"}; - bool ok = tryRead(selected); + CdReadArgs selected{ a0, a1, a2, "a0/a1/a2" }; + bool ok = tryRead(selected); - if (!ok) - { - // Some game-side wrappers use a nonstandard register layout. - // If primary decode does not resolve to a known LBN, try safe alternatives. - constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize; - if (!isResolvableCdLbn(selected.lbn)) + if (!ok) { - const std::array alternatives = { - CdReadArgs{a2, a1, a0, "a2/a1/a0"}, - CdReadArgs{a0, a2, a1, "a0/a2/a1"}, - CdReadArgs{a1, a0, a2, "a1/a0/a2"}, - CdReadArgs{a1, a2, a0, "a1/a2/a0"}, - CdReadArgs{a2, a0, a1, "a2/a0/a1"}}; - - for (const CdReadArgs &candidate : alternatives) + // Some game-side wrappers use a nonstandard register layout. + // If primary decode does not resolve to a known LBN, try safe alternatives. + constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize; + if (!isResolvableCdLbn(selected.lbn)) { - if (candidate.sectors > kMaxReasonableSectors) + const std::array alternatives = { + CdReadArgs{a2, a1, a0, "a2/a1/a0"}, + CdReadArgs{a0, a2, a1, "a0/a2/a1"}, + CdReadArgs{a1, a0, a2, "a1/a0/a2"}, + CdReadArgs{a1, a2, a0, "a1/a2/a0"}, + CdReadArgs{a2, a0, a1, "a2/a0/a1"} }; + + for (const CdReadArgs& candidate : alternatives) { - continue; + if (candidate.sectors > kMaxReasonableSectors) + { + continue; + } + if (!isResolvableCdLbn(candidate.lbn)) + { + continue; + } + + if (tryRead(candidate)) + { + static uint32_t recoverLogCount = 0; + if (recoverLogCount < 16) + { + RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag + << " (pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << ")" << std::endl); + ++recoverLogCount; + } + selected = candidate; + ok = true; + break; + } } - if (!isResolvableCdLbn(candidate.lbn)) + } + + if (!ok) + { + const uint32_t offset = a2 & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(a1, offset); + if (bytes > 0) { - continue; + std::memset(rdram + offset, 0, bytes); } - if (tryRead(candidate)) + static uint32_t unresolvedLogCount = 0; + if (unresolvedLogCount < 32) { - static uint32_t recoverLogCount = 0; - if (recoverLogCount < 16) - { - RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag - << " (pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << ")" << std::endl); - ++recoverLogCount; - } - selected = candidate; - ok = true; - break; + std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << std::endl; + ++unresolvedLogCount; } } } - if (!ok) + if (ok) { - const uint32_t offset = a2 & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(a1, offset); - if (bytes > 0) - { - std::memset(rdram + offset, 0, bytes); - } - - static uint32_t unresolvedLogCount = 0; - if (unresolvedLogCount < 32) - { - std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << std::endl; - ++unresolvedLogCount; - } + g_cdStreamingLbn = selected.lbn + selected.sectors; + setReturnS32(ctx, 1); // command accepted/success + return; } - } - if (ok) - { - g_cdStreamingLbn = selected.lbn + selected.sectors; - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); - setReturnS32(ctx, 1); // command accepted/success - return; + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - - -void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); // 0 = completed/not busy -} - - -void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_lastCdError); -} + void sceCdSync(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 0); // 0 = completed/not busy + } -void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceCdRI", rdram, ctx, runtime); -} + void sceCdGetError(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, g_lastCdError); + } -void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceCdRM", rdram, ctx, runtime); -} + void sceCdRI(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + TODO_NAMED("sceCdRI", rdram, ctx, runtime); + } -void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} -void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceCdRM(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + TODO_NAMED("sceCdRM", rdram, ctx, runtime); + } -void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} -void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceCdApplyNCmd(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 2); -} + void sceCdBreak(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - // SCECdPS2DVD - setReturnS32(ctx, 0x14); -} + void sceCdCallback(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 0); + } -void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, g_cdStreamingLbn); -} + void sceCdChangeThreadPriority(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t tocAddr = getRegU32(ctx, 4); - if (uint8_t *toc = getMemPtr(rdram, tocAddr)) + void sceCdDelayThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - std::memset(toc, 0, 1024); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 1); -} -void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdInitialized = true; - g_lastCdError = 0; - setReturnS32(ctx, 1); -} + void sceCdDiskReady(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 2); + } -void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceCdGetDiskType(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + // SCECdPS2DVD + setReturnS32(ctx, 0x14); + } -void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t lsn = getRegU32(ctx, 4); - uint32_t posAddr = getRegU32(ctx, 5); - uint8_t *pos = getMemPtr(rdram, posAddr); - if (!pos) + void sceCdGetReadPos(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - setReturnS32(ctx, 0); - return; + setReturnU32(ctx, g_cdStreamingLbn); } - uint32_t adjusted = lsn + 150; - const uint32_t minutes = adjusted / (60 * 75); - adjusted %= (60 * 75); - const uint32_t seconds = adjusted / 75; - const uint32_t sectors = adjusted % 75; + void sceCdGetToc(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + uint32_t tocAddr = getRegU32(ctx, 4); + if (uint8_t* toc = getMemPtr(rdram, tocAddr)) + { + std::memset(toc, 0, 1024); + } + setReturnS32(ctx, 1); + } - pos[0] = toBcd(minutes); - pos[1] = toBcd(seconds); - pos[2] = toBcd(sectors); - pos[3] = 0; - setReturnS32(ctx, 1); -} + void sceCdInit(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + g_cdInitialized = true; + g_lastCdError = 0; + setReturnS32(ctx, 1); + } -void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdMode = getRegU32(ctx, 4); - setReturnS32(ctx, 1); -} + void sceCdInitEeCB(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 2); -} + void sceCdIntToPos(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + uint32_t lsn = getRegU32(ctx, 4); + uint32_t posAddr = getRegU32(ctx, 5); + uint8_t* pos = getMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, 0); + return; + } -void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + uint32_t adjusted = lsn + 150; + const uint32_t minutes = adjusted / (60 * 75); + adjusted %= (60 * 75); + const uint32_t seconds = adjusted / 75; + const uint32_t sectors = adjusted % 75; + + pos[0] = toBcd(minutes); + pos[1] = toBcd(seconds); + pos[2] = toBcd(sectors); + pos[3] = 0; + setReturnS32(ctx, 1); + } -void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t posAddr = getRegU32(ctx, 4); - const uint8_t *pos = getConstMemPtr(rdram, posAddr); - if (!pos) + void sceCdMmode(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - setReturnS32(ctx, -1); - return; + g_cdMode = getRegU32(ctx, 4); + setReturnS32(ctx, 1); } - const uint32_t minutes = fromBcd(pos[0]); - const uint32_t seconds = fromBcd(pos[1]); - const uint32_t sectors = fromBcd(pos[2]); - const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; - const int32_t lsn = static_cast(absolute) - 150; - setReturnS32(ctx, lsn); -} + void sceCdNcmdDiskReady(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 2); + } -void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t chainAddr = getRegU32(ctx, 4); - bool ok = true; + void sceCdPause(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } - for (int i = 0; i < 64; ++i) + void sceCdPosToInt(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); - if (!entry) + uint32_t posAddr = getRegU32(ctx, 4); + const uint8_t* pos = getConstMemPtr(rdram, posAddr); + if (!pos) { - ok = false; - break; + setReturnS32(ctx, -1); + return; } - const uint32_t lbn = entry[0]; - const uint32_t sectors = entry[1]; - const uint32_t buf = entry[2]; - if (lbn == 0xFFFFFFFFu || sectors == 0) - { - break; - } + const uint32_t minutes = fromBcd(pos[0]); + const uint32_t seconds = fromBcd(pos[1]); + const uint32_t sectors = fromBcd(pos[2]); + const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; + const int32_t lsn = static_cast(absolute) - 150; + setReturnS32(ctx, lsn); + } - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } + void sceCdReadChain(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + uint32_t chainAddr = getRegU32(ctx, 4); + bool ok = true; - if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) + for (int i = 0; i < 64; ++i) { - ok = false; - break; + uint32_t* entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); + if (!entry) + { + ok = false; + break; + } + + const uint32_t lbn = entry[0]; + const uint32_t sectors = entry[1]; + const uint32_t buf = entry[2]; + if (lbn == 0xFFFFFFFFu || sectors == 0) + { + break; + } + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) + { + ok = false; + break; + } + + g_cdStreamingLbn = lbn + sectors; } - g_cdStreamingLbn = lbn + sectors; - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, ok ? 1 : 0); } - setReturnS32(ctx, ok ? 1 : 0); -} - -void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clockAddr = getRegU32(ctx, 4); - uint8_t *clockData = getMemPtr(rdram, clockAddr); - if (!clockData) + void sceCdReadClock(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - setReturnS32(ctx, 0); - return; - } + uint32_t clockAddr = getRegU32(ctx, 4); + uint8_t* clockData = getMemPtr(rdram, clockAddr); + if (!clockData) + { + setReturnS32(ctx, 0); + return; + } - std::time_t now = std::time(nullptr); - std::tm localTm{}; + std::time_t now = std::time(nullptr); + std::tm localTm{}; #ifdef _WIN32 - localtime_s(&localTm, &now); + localtime_s(&localTm, &now); #else - localtime_r(&now, &localTm); + localtime_r(&now, &localTm); #endif - // sceCdCLOCK format (BCD fields). - clockData[0] = 0; - clockData[1] = toBcd(static_cast(localTm.tm_sec)); - clockData[2] = toBcd(static_cast(localTm.tm_min)); - clockData[3] = toBcd(static_cast(localTm.tm_hour)); - clockData[4] = 0; - clockData[5] = toBcd(static_cast(localTm.tm_mday)); - clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); - clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); - setReturnS32(ctx, 1); -} + // sceCdCLOCK format (BCD fields). + clockData[0] = 0; + clockData[1] = toBcd(static_cast(localTm.tm_sec)); + clockData[2] = toBcd(static_cast(localTm.tm_min)); + clockData[3] = toBcd(static_cast(localTm.tm_hour)); + clockData[4] = 0; + clockData[5] = toBcd(static_cast(localTm.tm_mday)); + clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); + clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); + setReturnS32(ctx, 1); + } -void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - sceCdRead(rdram, ctx, runtime); -} + void sceCdReadIOPm(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + sceCdRead(rdram, ctx, runtime); + } -void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t fileAddr = getRegU32(ctx, 4); - uint32_t pathAddr = getRegU32(ctx, 5); - const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); - const std::string normalizedPath = normalizeCdPathNoPrefix(path); - static uint32_t traceCount = 0; - const uint32_t callerRa = getRegU32(ctx, 31); - const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); - if (shouldTrace) - { - RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc - << " ra=0x" << callerRa - << " file=0x" << fileAddr - << " pathAddr=0x" << pathAddr - << " path=\"" << sanitizeForLog(path) << "\"" - << std::dec << std::endl); - } - ++traceCount; - - if (path.empty()) - { - static uint32_t emptyPathCount = 0; - if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) + void sceCdSearchFile(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + uint32_t fileAddr = getRegU32(ctx, 4); + uint32_t pathAddr = getRegU32(ctx, 5); + const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); + const std::string normalizedPath = normalizeCdPathNoPrefix(path); + static uint32_t traceCount = 0; + const uint32_t callerRa = getRegU32(ctx, 31); + const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); + if (shouldTrace) { - std::ostringstream preview; - preview << std::hex; - for (uint32_t i = 0; i < 16; ++i) + RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc + << " ra=0x" << callerRa + << " file=0x" << fileAddr + << " pathAddr=0x" << pathAddr + << " path=\"" << sanitizeForLog(path) << "\"" + << std::dec << std::endl); + } + ++traceCount; + + if (path.empty()) + { + static uint32_t emptyPathCount = 0; + if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) { - const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); - preview << (i == 0 ? "" : " ") << static_cast(byte); + std::ostringstream preview; + preview << std::hex; + for (uint32_t i = 0; i < 16; ++i) + { + const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); + preview << (i == 0 ? "" : " ") << static_cast(byte); + } + std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr + << " preview=" << preview.str() + << " ra=0x" << callerRa << std::dec << std::endl; } - std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr - << " preview=" << preview.str() - << " ra=0x" << callerRa << std::dec << std::endl; + ++emptyPathCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; } - ++emptyPathCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - if (normalizedPath.empty()) - { - static uint32_t emptyNormalizedCount = 0; - if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) + if (normalizedPath.empty()) { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (normalized path is empty, root: " << getCdRootPath().string() << ")" - << std::endl; + static uint32_t emptyNormalizedCount = 0; + if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (normalized path is empty, root: " << getCdRootPath().string() << ")" + << std::endl; + } + ++emptyNormalizedCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; } - ++emptyNormalizedCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - CdFileEntry entry; - bool found = registerCdFile(path, entry); - CdFileEntry resolvedEntry = entry; - std::string resolvedPath; + CdFileEntry entry; + bool found = registerCdFile(path, entry); + CdFileEntry resolvedEntry = entry; + std::string resolvedPath; - if (!found) - { - static std::string lastFailedPath; - static uint32_t samePathFailCount = 0; - if (path == lastFailedPath) + if (!found) { - ++samePathFailCount; + static std::string lastFailedPath; + static uint32_t samePathFailCount = 0; + if (path == lastFailedPath) + { + ++samePathFailCount; + } + else + { + lastFailedPath = path; + samePathFailCount = 1; + } + + if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (root: " << getCdRootPath().string() + << ", repeat=" << samePathFailCount << ")" << std::endl; + } + setReturnS32(ctx, 0); + return; } - else + + if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) { - lastFailedPath = path; - samePathFailCount = 1; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; } - if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) + g_cdStreamingLbn = resolvedEntry.baseLbn; + if (shouldTrace) { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (root: " << getCdRootPath().string() - << ", repeat=" << samePathFailCount << ")" << std::endl; + RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) + << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn + << " size=0x" << resolvedEntry.sizeBytes + << " sectors=0x" << resolvedEntry.sectors + << std::dec << std::endl); } - setReturnS32(ctx, 0); - return; + setReturnS32(ctx, 1); } - if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) + void sceCdSeek(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); } - g_cdStreamingLbn = resolvedEntry.baseLbn; - detail::trackCdFileForMpeg(resolvedEntry.baseLbn, - resolvedEntry.sizeBytes, - resolvedEntry.hostPath); - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); - if (shouldTrace) + void sceCdStandby(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) - << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn - << " size=0x" << resolvedEntry.sizeBytes - << " sectors=0x" << resolvedEntry.sectors - << std::dec << std::endl); + setReturnS32(ctx, 1); } - setReturnS32(ctx, 1); -} -void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); - setReturnS32(ctx, 1); -} + void sceCdStatus(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, g_cdInitialized ? 6 : 0); + } -void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceCdStInit(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_cdInitialized ? 6 : 0); -} + void sceCdStop(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceCdStPause(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceCdStRead(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + uint32_t sectors = getRegU32(ctx, 4); + uint32_t buf = getRegU32(ctx, 5); + uint32_t errAddr = getRegU32(ctx, 7); -void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } -void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t sectors = getRegU32(ctx, 4); - uint32_t buf = getRegU32(ctx, 5); - uint32_t errAddr = getRegU32(ctx, 7); + const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); + if (ok) + { + g_cdStreamingLbn += sectors; + } - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; + if (int32_t* err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + { + *err = ok ? 0 : g_lastCdError; + } + + setReturnS32(ctx, ok ? static_cast(sectors) : 0); } - const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); - if (ok) + void sceCdStream(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - g_cdStreamingLbn += sectors; - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); + setReturnS32(ctx, 1); } - if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + void sceCdStResume(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - *err = ok ? 0 : g_lastCdError; + setReturnS32(ctx, 1); } - setReturnS32(ctx, ok ? static_cast(sectors) : 0); -} - -void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - -void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); - setReturnS32(ctx, 1); -} + void sceCdStSeek(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); + } -void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); - setReturnS32(ctx, 1); -} + void sceCdStSeekF(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); + } -void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cdStreamingLbn = getRegU32(ctx, 4); - detail::setCurrentCdStreamLbnForMpeg(g_cdStreamingLbn); - setReturnS32(ctx, 1); -} + void sceCdStStart(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); + } -void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceCdStStat(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 0); + } -void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceCdStStop(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 1); + } -void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceCdSyncS(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + { + setReturnS32(ctx, 0); + } -void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t statusPtr = getRegU32(ctx, 5); - if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) + void sceCdTrayReq(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) { - *status = 0; + uint32_t statusPtr = getRegU32(ctx, 5); + if (uint32_t* status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) + { + *status = 0; + } + setReturnS32(ctx, 1); } - setReturnS32(ctx, 1); -} } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Common.h b/ps2xRuntime/src/lib/Kernel/Stubs/Common.h index 6f2e3b82..9d12cee2 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Common.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Common.h @@ -1,7 +1,11 @@ +#pragma once + #include "ps2_stubs.h" #include "ps2_runtime.h" #include "ps2_runtime_macros.h" #include "ps2_syscalls.h" +#include "Unimplemented.h" + #include #include #include diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp index 1b9021f2..8d7e2ecc 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp @@ -1,6 +1,7 @@ #include "Common.h" #include "Interrupt.h" #include "ps2_log.h" +#include "Stubs/GS.h" namespace ps2_syscalls { diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 34721482..462c1625 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -6,6 +6,9 @@ #include "ps2_runtime_macros.h" #include "runtime/ps2_gs_gpu.h" #include "ThreadNaming.h" +#include "Kernel/Stubs/Audio.h" +#include "Kernel/Stubs/GS.h" +#include "Kernel/Stubs/MPEG.h" #include #if defined(PLATFORM_VITA) @@ -24,6 +27,7 @@ #include #include + namespace ps2_stubs { void resetSifState(); @@ -2021,7 +2025,6 @@ void PS2Runtime::run() ps2_stubs::resetAudioStubState(); ps2_stubs::resetGsSyncVCallbackState(); ps2_stubs::resetMpegStubState(); - ps2_stubs::resetMpegSourceTracking(); ps2_syscalls::initializeGuestKernelState(m_memory.getRDRAM()); m_cpuContext.r[4] = _mm_setzero_si128(); m_cpuContext.r[5] = _mm_setzero_si128(); diff --git a/ps2xRuntime/src/runner/games_database.cpp b/ps2xRuntime/src/runner/games_database.cpp new file mode 100644 index 00000000..2e7755b5 --- /dev/null +++ b/ps2xRuntime/src/runner/games_database.cpp @@ -0,0 +1,804 @@ +#include "games_database.h" +#include + +static const std::unordered_map gameDatabase = +{ + { "SLUS-20267", "hack Part 1 - Infection (USA)" }, + { "SLKA-25080", ".hack Vol. 1 - Infection (Korea)" }, + { "SLPS-25143", ".hack Vol. 2 - Mutation (Japan)" }, + { "SLPS-25158", ".hack Vol. 3 - Erosion Pollution (Japan)" }, + { "SLUS-20579", "007 - NightFire (USA)" }, + { "SLES-51258", "007 - Nightfire (Europe)" }, + { "SLES-51260", "007 - Nightfire (Europe)" }, + { "SLES-50214", "18 Wheeler - American Pro Trucker (Europe)" }, + { "SLUS-20210", "18 Wheeler - American Pro Trucker (USA)" }, + { "SLPS-25118", "2002 FIFA World Cup (Japan)" }, + { "SLUS-20404", "2002 FIFA World Cup (USA)" }, + { "SLES-50796", "2002 FIFA World Cup Korea Japan (Europe)" }, + { "SLES-50798", "2002 FIFA World Cup Korea Japan (Germany)" }, + { "SLES-50799", "2002 FIFA World Cup Korea Japan (Italy)" }, + { "SLES-50800", "2002 FIFA World Cup Korea Japan (Spain)" }, + { "SLPS-20214", "3D Fighting School 2 (Japan)" }, + { "SLUS-20091", "4x4 Evo (USA)" }, + { "SCES-50293", "ATV Offroad - All Terrain Vehicle (Europe)" }, + { "SCUS-97104", "ATV Offroad Fury (USA) (v1.00)" }, + { "SCUS-97104", "ATV Offroad Fury (USA) (v3.01)" }, + { "SLUS-20588", "Activision Anthology (USA)" }, + { "SLPM-65150", "Aero Dancing 4 - New Generation (Japan)" }, + { "SLUS-20614", "Aero Elite - Combat Academy (USA)" }, + { "SLPS-25224", "Ai yori Aoshi Limited Edition (Japan)" }, + { "SLES-50953", "Air Ranger - Rescue Helicopter (Europe)" }, + { "SLPM-65486", "AirForce Delta - Blue Wing Knights (Japan)" }, + { "SLUS-20703", "AirForce Delta Strike (USA)" }, + { "SLES-50919", "Akira Psycho Ball (Europe)" }, + { "SLPS-20150", "Akira Psycho Ball (Japan)" }, + { "SLES-50429", "Alex Ferguson�s Player Manager 2001 (Europe)" }, + { "SLES-51792", "Aliens Versus Predator - Extinction (Europe)" }, + { "SLUS-20147", "Aliens vs Predator - Extinction (USA)" }, + { "SLPS-20181", "Alpine Racer 3 (Japan)" }, + { "SLUS-21069", "American Chopper (USA)" }, + { "SLPM-65513", "Angel�s Feather (Japan)" }, + { "SLPM-65027", "Anime Eikaiwa - 15 Shounen Hyouryuuki - Hitomi no Naka no Shounen (Japan)" }, + { "SLPM-65029", "Anime Eikaiwa - Tondemo Nezumi Daikatsuyaku (Japan)" }, + { "SLPM-65028", "Anime Eikaiwa - Tottoi (Japan)" }, + { "SLUS-20217", "Arctic Thunder (USA)" }, + { "SLES-50191", "Army Men - Green Rogue (Europe)" }, + { "SLUS-20087", "Army Men - Green Rogue (USA)" }, + { "SLPM-62501", "Assault Suits Valken (Japan)" }, + { "SLES-51896", "Attheraces Presents Gallop Racer (Europe)" }, + { "SLES-51191", "Auto Modellista (Europe)" }, + { "SLUS-20498", "Auto Modellista (USA) (Volume 1.0) (Beta)" }, + { "SLUS-28031", "Auto Modellista (USA) (Volume 2.0) (Beta)" }, + { "SLUS-20642", "Auto Modellista (USA)" }, + { "SLPS-25140", "Baldur�s Gate - Dark Alliance (Japan)" }, + { "SLPM-62155", "Baseball 2002, The - Battle Ball Park Sengen (Japan)" }, + { "SLPM-65180", "Baseball 2003, The - Battle Ball Park Sengen - Perfect Play Pro Yakyuu (Japan) (v1.05)" }, + { "SLES-51756", "Batman - Rise of Sin Tzu (Europe)" }, + { "SLUS-20709", "Batman - Rise of Sin Tzu (USA)" }, + { "SLES-50355", "Batman - Vengeance (Europe)" }, + { "SLUS-20226", "Batman - Vengeance (USA)" }, + { "SLPM-62052", "Beatmania Da Da Da!! (Japan)" }, + { "SCPS-11004", "Bikkuri Mouse (Japan)" }, + { "SLPM-65059", "Biohazard - Gun Survivor 2 - CODE - Veronica (Japan)" }, + { "SLPS-20187", "Black-Matrix II (Japan)" }, + { "SLES-51013", "Blade II (Europe)" }, + { "SLUS-20360", "Blade II (USA)" }, + { "SLUS-20862", "BloodRayne 2 (USA)" }, + { "SLPM-65262", "Boboboubo Boubobo - Hajike Matsuri (Japan)" }, + { "SLUS-20499", "Breath of Fire - Dragon Quarter (USA)" }, + { "SLPM-65196", "Breath of Fire V - Dragon Quarter (Japan)" }, + { "SLPM-66410", "Brothers in Arms - Meiyo no Daishou (Japan)" }, + { "SLUS-20895", "Bujingai - The Forsaken City (USA)" }, + { "?", "Burnout Dominator SLAJ-25094 SLPM-66739 SLUS-21596 SLES-54627 SLES-54681" }, + { "SLUS-20141", "CART Fury - Championship Racing (USA)" }, + { "SLES-50541", "Capcom vs. SNK 2 - Mark of the Millennium 2001 (Europe)" }, + { "SLUS-20246", "Capcom vs. SNK 2 - Mark of the Millennium 2001 (USA)" }, + { "SLPM-62365", "Cardinal Arc - Konton no Fuusatsu (Japan)" }, + { "SLES-52143", "Carmen Sandiego - The Secret of the Stolen Drums (Europe) (En,Fr,De,Es)" }, + { "SLUS-20849", "Carmen Sandiego - The Secret of the Stolen Drums (USA)" }, + { "SLES-50636", "Centre Court - Hard Hitter (Europe)" }, + { "SLPM-65255", "Chobits - Chii dake no Hito (Japan)" }, + { "SLPS-25015", "Choro Q - High Grade (Japan)" }, + { "SLPS-25014", "Choro Q - High Grade Limited Edition (Japan)" }, + { "SLPS-25073", "Cinema Surfing - Youga Taizen (Japan)" }, + { "SLES-50935", "Circus Maximus - Chariot Wars (Europe)" }, + { "SLES-51619", "Clock Tower 3 (Europe)" }, + { "SLUS-20633", "Clock Tower 3 (USA)" }, + { "SLPS-20056", "Colorio Hagaki Print (Japan)" }, + { "SCUS-97108", "Cool Boarders 2001 (USA)" }, + { "SLUS-20238", "Crash Bandicoot - The Wrath of Cortex (USA) (v1.00)" }, + { "SLES-50215", "Crazy Taxi (Europe)" }, + { "SLUS-20202", "Crazy Taxi (USA)" }, + { "SLPM-65368", "D.N.Angel - TV Animation Series (Japan)" }, + { "PSXC-00203", "DESR-7000-DESR-5000-DESR-7100-DESR-5100 Senyou - PSX Update Disc Ver. 1.31 (Japan)" }, + { "SCES-51190", "Dark Chronicle (Europe)" }, + { "PAPX-90506", "Dark Chronicle (Japan) (Demo)" }, + { "SCES-50295", "Dark Cloud (Europe)" }, + { "SCPS-15004", "Dark Cloud (Japan)" }, + { "SCUS-97111", "Dark Cloud (USA)" }, + { "SCUS-97213", "Dark Cloud 2 (USA) (v2.00)" }, + { "SLES-52874", "Dark Wind (Europe)" }, + { "SLPM-65303", "Dennou Senki - Virtual-On Marz (Japan)" }, + { "SLPS-25321", "Derby Stallion 04 (Japan)" }, + { "SLED-50359", "Devil May Cry (Europe) (Demo)" }, + { "SLPM-61010", "Devil May Cry (Japan) (Demo)" }, + { "SLPM-65023", "Devil May Cry (Japan) (Demo)" }, + { "SLPM-65038", "Devil May Cry (Japan)" }, + { "SLUS-20216", "Devil May Cry (USA)" }, + { "SLES-51347", "Die Hard - Vendetta (Europe)" }, + { "SLES-51348", "Die Hard - Vendetta (Germany)" }, + { "SLES-51095", "Dino Stalker (France)" }, + { "SLES-51096", "Dino Stalker (Germany)" }, + { "SLUS-20485", "Dino Stalker (USA)" }, + { "SLES-55392", "Disney Sing It (Europe)" }, + { "SLES-55542", "Disney Sing It - Pop Hits (Europe)" }, + { "SLES-50042", "Disney�s Dinosaur (Europe)" }, + { "SLES-50043", "Disney�s Dinosaur (Europe)" }, + { "SLES-50048", "Disney�s Donald Duck - Quack Attack (Europe)" }, + { "SLES-50045", "Disney�s Jungle Book - Groove Party (Europe)" }, + { "SCES-50522", "Disney�s Peter Pan - The Legend of Never Land (Europe)" }, + { "SCES-50531", "Disney�s Peter Pan - The Legend of Never Land (Scandinavia)" }, + { "SLES-50350", "Disney�s Tarzan - Freeride (Europe)" }, + { "SCES-51176", "Disney�s Treasure Planet (Europe)" }, + { "SCUS-97146", "Disney�s Treasure Planet (USA)" }, + { "SCES-50600", "Disney-Pixar Die Monster AG - Schreckens-Insel (Germany)" }, + { "SCES-50597", "Disney-Pixar Monsters en Co. - Schrik Eiland (Netherlands)" }, + { "SCES-50595", "Disney-Pixar Monsters, Inc. - Scare Island (Europe)" }, + { "SCES-50604", "Disney-Pixar Monsters, Inc. - Skraemmaroen (Sweden)" }, + { "SCES-50603", "Disney-Pixar Monstruos, S.A. - Isla de los Sustos (Spain)" }, + { "SLPM-65703", "Double Reaction! Plus (Japan)" }, + { "SCPS-56010", "Downhill Racer (Korea)" }, + { "SLPM-62199", "Dragon Quest Characters - Torneko no Daibouken 3 (Japan)" }, + { "SLPM-62490", "Dragon Quest VIII Premium Disc (Japan)" }, + { "SLPS-20016", "Dream Audition (Japan)" }, + { "SLPS-20099", "Dream Audition 3 (Japan)" }, + { "SLPS-20140", "Dream Audition Super Hit Disc 1 (Japan)" }, + { "SLPS-20141", "Dream Audition Super Hit Disc 2 (Japan)" }, + { "SLUS-20239", "Driven (USA)" }, + { "SLUS-20113", "Driving Emotion Type-S (USA)" }, + { "SLES-51303", "Drome Racers (Europe)" }, + { "SLUS-20475", "Dual Hearts (USA)" }, + { "SLES-50057", "Dynasty Warriors 2 (Europe)" }, + { "SLES-50058", "Dynasty Warriors 2 (France)" }, + { "SLPM-69004", "EGBrowser Light for I-O Data Device, Inc. (Japan)" }, + { "SLES-50036", "ESPN International Track & Field (Europe)" }, + { "SLUS-20041", "ESPN International Track & Field (USA)" }, + { "SLUS-20320", "ESPN International Winter Sports 2002 (USA)" }, + { "SLUS-20128", "ESPN MLS ExtraTime (USA)" }, + { "SLUS-20089", "ESPN Winter X Games Snowboarding (USA)" }, + { "SLPM-62103", "EX Okuman Chouja Game - The Money Battle (Japan)" }, + { "SLUS-20169", "Ephemeral Fantasia (USA)" }, + { "SLES-51813", "European Tennis Pro (Europe)" }, + { "SCED-51728", "EverQuest - Online Adventures (Europe) (Demo)" }, + { "SLES-51392", "Evolution Snowboarding (Europe)" }, + { "SLUS-20546", "Evolution Snowboarding (USA)" }, + { "SLPS-25326", "Exciting Pro Wres 5 (Japan) (Limited Edition)" }, + { "SLPS-25083", "Exciting Pro Wrestling 3 (Japan) (Limited Edition)" }, + { "SLPS-25087", "Exciting Pro Wrestling 3 (Japan)" }, + { "SLPS-20223", "Exciting Pro Wrestling 4 (Japan) (Demo)" }, + { "SLPS-25210", "Exciting Pro Wrestling 4 (Japan)" }, + { "SLPS-25326", "Exciting Pro Wrestling 5 (Japan)" }, + { "SCES-51513", "EyeToy - Play (Europe, Australia)" }, + { "SCUS-97319", "EyeToy - Play (USA)" }, + { "SLES-50011", "FIFA 2001 (Europe)" }, + { "SLES-50012", "FIFA 2001 (France)" }, + { "SLES-50013", "FIFA 2001 (Germany)" }, + { "SLES-50015", "FIFA 2001 (Italy)" }, + { "SLES-50016", "FIFA 2001 (Spain)" }, + { "SLUS-20097", "FIFA 2001 (USA)" }, + { "SLPS-20054", "FIFA 2001 - World Championship (Japan)" }, + { "SLPS-25069", "FIFA 2002 - Road to FIFA World Cup (Japan)" }, + { "SLPS-25179", "FIFA 2003 - Europe Soccer (Japan)" }, + { "SLES-50464", "FIFA Football 2002 (Europe)" }, + { "SLES-50466", "FIFA Football 2002 (France)" }, + { "SLES-50467", "FIFA Football 2002 (Germany)" }, + { "SLES-50470", "FIFA Football 2002 (Italy)" }, + { "SLPM-67503", "FIFA Football 2002 (Korea)" }, + { "SLES-50471", "FIFA Football 2002 (Spain)" }, + { "SLES-51197", "FIFA Football 2003 (Europe)" }, + { "SLUS-20280", "FIFA Soccer 2002 (USA)" }, + { "SLUS-20580", "FIFA Soccer 2003 (USA)" }, + { "SLPS-20020", "FIFA Soccer World Championship (Japan)" }, + { "SLPS-25236", "Fantastic Fortune 2 (Japan)" }, + { "SLUS-20388", "Fatal Frame (USA)" }, + { "SLUS-20766", "Fatal Frame II - Crimson Butterfly (USA)" }, + { "SLPS-20298", "Fever 8 - Sankyo Koushiki Pachinko Simulation (Japan)" }, + { "SLUS-20524", "Fighter Maker 2 (USA)" }, + { "SLPM-62135", "Final Fantasy XI - Online (Japan) (Beta)" }, + { "SLPS-25200", "Final Fantasy XI - Online (Japan)" }, + { "SCUS-97271", "Final Fantasy XI - Online (USA) (Beta)" }, + { "SCUS-97266", "Final Fantasy XI - Online (USA)" }, + { "SLPM-65288", "Final Fantasy XI - Zilart no Gen�ei (Japan) (All in One Pack 2003)" }, + { "SLPM-65287", "Final Fantasy XI - Zilart no Gen�ei (Japan)" }, + { "SLES-51418", "Fisherman�s Challenge (Europe)" }, + { "SLUS-20553", "Fisherman�s Challenge (USA)" }, + { "SLES-50259", "Flintstones in Viva Rock Vegas, The (Europe)" }, + { "SLPS-25034", "Flower, Sun and Rain (Japan)" }, + { "SLED-52852", "Forgotten Realms - Demon Stone (Europe) (Demo)" }, + { "SLUS-29061", "Freaky Flyers (USA) (Demo)" }, + { "SLUS-20658", "Freedom Fighters (USA)" }, + { "SLES-50720", "Freestyle Metal X (Europe)" }, + { "SLUS-20494", "Freestyle Metal X (USA)" }, + { "SLES-50788", "Frogger - The Great Quest (Europe)" }, + { "SLUS-20257", "Frogger - The Great Quest (USA)" }, + { "SLPM-60102", "From Software First Previews (Japan)" }, + { "SLUS-20785", "Funkmaster Flex - Digital Hitz Factory (USA)" }, + { "SLUS-20859", "Future Tactics - The Uprising (USA)" }, + { "SLKA-25139", "Fuuun Shinsengumi (Korea)" }, + { "SCED-52094", "G-Con 2 Competition Demo (Germany)" }, + { "SLES-50584", "G1 Jockey (Europe)" }, + { "SLES-51357", "G1 Jockey 3 (Europe)" }, + { "SLUS-20690", "G1 Jockey 3 (USA)" }, + { "SLPM-62020", "GI Jockey 2 (Japan)" }, + { "SLPM-62059", "GI Jockey 2 2001 (Japan) (Super Value Set)" }, + { "SLPM-62061", "GI Jockey 2 2001 (Japan)" }, + { "SLPM-62279", "GI Jockey 3 (Japan) (Premium Pack)" }, + { "SLPM-62277", "GI Jockey 3 (Japan)" }, + { "SLES-50472", "GTC Africa (Europe)" }, + { "SLES-52845", "Gadget & the Gadgetinis (Europe)" }, + { "SLUS-20225", "Gadget Racers (USA)" }, + { "SLPS-25333", "Gallop Racer - Lucky 7 (Japan)" }, + { "SLUS-20255", "Gallop Racer 2001 (USA)" }, + { "SLUS-20662", "Gallop Racer 2003 - A New Breed (USA)" }, + { "SLUS-21031", "Gallop Racer 2004 (USA)" }, + { "SLPS-25036", "Gallop Racer 5 (Japan)" }, + { "SLPS-73415", "Gallop Racer 6 - Revolution (Japan) (PlayStation 2 the Best)" }, + { "SLPS-25177", "Gallop Racer 6 - Revolution (Japan)" }, + { "SLPM-62009", "Ganbare! Nippon! Olympic 2000 (Japan)" }, + { "SLES-50211", "Gauntlet - Dark Legacy (Europe)" }, + { "SLUS-20047", "Gauntlet - Dark Legacy (USA)" }, + { "SLPM-62235", "Get Bass Battle (Japan)" }, + { "?", "Ghost Master - The Gravenville Chronicles (2003 beta) [Emuparadise]" }, + { "SLPS-20052", "Global Folktale (Japan)" }, + { "SLUS-20395", "Global Touring Challenge - Africa (USA)" }, + { "SLES-52117", "Go Go Copter - Remote Control Helicopter (Europe)" }, + { "SLES-51055", "Go Go Golf (Europe)" }, + { "SCED-54680", "God of War II (Europe) (Demo)" }, + { "SLES-50433", "Godai - Elemental Force (Europe)" }, + { "SLUS-20288", "Godai - Elemental Force (USA)" }, + { "SLPM-60107", "Golf Paradise (Japan) (Demo)" }, + { "SLPS-20009", "Golf Paradise (Japan)" }, + { "SLES-51296", "Grand Prix Challenge (Europe)" }, + { "SLES-50793", "Grand Theft Auto III (Australia)" }, + { "SLES-50330", "Grand Theft Auto III (Europe) (v1.40)" }, + { "SLES-50330", "Grand Theft Auto III (Europe) (v1.60)" }, + { "SLUS-20062", "Grand Theft Auto III (USA)" }, + { "SLUS-20466", "Gravenville Ghost Master Chronicles" }, + { "SLUS-20310", "Gravity Games Bike - Street. Vert. Dirt. (USA)" }, + { "SCES-50246", "Gravity Sucks (Europe, Australia)" }, + { "SLES-51999", "Grooverider (Europe)" }, + { "SLPS-20106", "Growlanser II - The Sense of Justice (Japan)" }, + { "SLKA-15007", "Growlanser II - The Sense of Justice (Korea)" }, + { "SLPM-62108", "Growlanser III - The Dual Darkness (Japan)" }, + { "SLPM-65383", "Growlanser IV - Wayfarer of the Time (Japan) (Deluxe Pack)" }, + { "SLPM-65408", "Growlanser IV - Wayfarer of the Time (Japan)" }, + { "SLPM-65139", "Gun Survivor 3 - Dino Crisis (Japan)" }, + { "SLES-52620", "Guncom 2 (Europe)" }, + { "SLPM-65153", "Gungrave (Japan)" }, + { "SLUS-20493", "Gungrave (USA)" }, + { "SLUS-21020", "Gungrave - Overdose (USA)" }, + { "SLPM-65492", "Gungrave OD (Japan)" }, + { "SLES-50559", "Guy Roux Manager 2002 (France)" }, + { "SLPM-62273", "Haishin 3 (Japan)" }, + { "SLPS-20098", "Hard Hitter (Japan)" }, + { "SLES-51057", "Hard Hitter 2 (Europe)" }, + { "SLPS-20173", "Hard Hitter 2 (Japan)" }, + { "SLUS-20568", "Hard Hitter Tennis (USA)" }, + { "SLES-51254", "Herr der Ringe, Der - Die zwei Tuerme (Germany)" }, + { "SLES-50260", "Hidden Invasion (Europe)" }, + { "SLUS-20301", "Hidden Invasion (USA)" }, + { "SLPS-25111", "Higanbana (Japan)" }, + { "SLPS-20213", "Hissatsu Pachinko Station V4 - Drumtic Mahjong (Japan)" }, + { "SLES-53028", "Hitman - Blood Money (Europe)" }, + { "SLPS-25269", "Hitman 2 - Silent Assassin (Japan)" }, + { "SLUS-20374", "Hitman 2 - Silent Assassin (USA) (v1.01)" }, + { "SLPM-62072", "Horse Breaker (Japan)" }, + { "SLES-51063", "Hot Wheels - Velocity X - Maximum Justice (Europe)" }, + { "SLUS-20412", "Hot Wheels - Velocity X - Maximum Justice (USA)" }, + { "SLPM-65083", "Houshin Engi 2 (Japan)" }, + { "SLES-52102", "Hugo - Bukkazoom! (Europe)" }, + { "SLPM-62067", "Hunter x Hunter - Ryumyaku no Saidan (Japan)" }, + { "SLES-50266", "Hype - The Time Quest (Europe)" }, + { "SLES-50265", "Hype - The Time Quest (Germany)" }, + { "SLPM-65405", "Hyper Dimension Fortress Macross (Japan)" }, + { "SLPM-62126", "Hyper Sports 2002 Winter (Japan)" }, + { "SLUS-20586", "IHRA Drag Racing 2 (USA)" }, + { "SCES-50760", "Ico (Europe)" }, + { "SLPS-25182", "Idol Janshi R - Jan Guru Project (Japan)" }, + { "SLES-51255", "Il Signore degli Anelli - Le Due Torri (Italy)" }, + { "SLES-51397", "IndyCar Series (Europe)" }, + { "SLUS-20641", "IndyCar Series featuring The Indianapolis 500 (USA)" }, + { "SLUS-20830", "Intellivision Lives! (USA)" }, + { "SLES-51629", "International Pool Championship (Europe)" }, + { "SLES-50039", "International Superstar Soccer (Europe)" }, + { "SLPM-62075", "International Superstar Soccer 2 (Europe) (Beta)" }, + { "SLUS-20913", "Inuyasha - The Secret of the Cursed Mask (USA)" }, + { "SLPM-65530", "J. League Pro Soccer Club o Tsukurou! �04 (Japan)" }, + { "SLPM-62217", "J. League Winning Eleven 6 (Japan)" }, + { "SLES-50735", "Jade Cocoon 2 (Europe)" }, + { "SCED-52952", "Jak 3 (Europe) (Demo)" }, + { "SCKA-20010", "Jak II (Korea) (En,Ja,Fr,De,Es,It,Ko)" }, + { "SCUS-97273", "Jak II (USA) (Demo)" }, + { "SCUS-97265", "Jak II (USA) (En,Ja,Fr,De,Es,It,Ko) (v1.00)" }, + { "SCUS-97265", "Jak II (USA) (En,Ja,Fr,De,Es,It,Ko) (v2.01)" }, + { "SCPS-15057", "Jak II - Jak x Daxter 2 (Japan)" }, + { "SCED-51700", "Jak II - Renegade (Europe) (Demo)" }, + { "SCES-51608", "Jak II Renegade (Europe) (Preview)" }, + { "SCES-50361", "Jak and Daxter - The Precursor Legacy (Europe)" }, + { "SCUS-97124", "Jak and Daxter - The Precursor Legacy (USA) (Cingular Wireless Demo)" }, + { "SCUS-97124", "Jak and Daxter - The Precursor Legacy (USA) (En,Fr,De,Es,It) (Rev 1)" }, + { "SCUS-97124", "Jak and Daxter - The Precursor Legacy (USA) (En,Fr,De,Es,It)" }, + { "PAPX-90222", "Jak x Daxter - Kyuu Sekai no Isan (Japan) (Demo)" }, + { "SCPS-15021", "Jak x Daxter - Kyuu Sekai no Isan (Japan)" }, + { "SLES-50209", "Jeremy McGrath Supercross World (Europe)" }, + { "SLUS-20245", "Jeremy McGrath Supercross World (USA)" }, + { "SCUS-97239", "Jet X2O (USA) (Demo)" }, + { "SCUS-97173", "Jet X2O (USA)" }, + { "SLPM-62011", "Jikkyou GI Stable (Japan)" }, + { "SLPM-62075", "Jikkyou World Soccer 2001 (Japan)" }, + { "SLPM-65140", "Jojo no Kimyou na Bouken - Ougon no Kaze (Japan)" }, + { "SLPM-65336", "K-1 World Grand Prix - The Beast Attack! (Japan)" }, + { "SLPM-65075", "K-1 World Grand Prix 2001 (Japan)" }, + { "SLPM-65202", "K-1 World Grand Prix 2002 (Japan)" }, + { "SLPM-65433", "K-1 World Grand Prix 2003 (Japan)" }, + { "SLPS-25386", "KOF - Maximum Impact (Japan)" }, + { "SLUS-20923", "KOF - Maximum Impact (USA)" }, + { "SCPS-11009", "Ka (Japan)" }, + { "SCPS-15045", "Ka 2 - Let�s Go Hawaii (Japan)" }, + { "SLPM-62383", "Karaoke Revolution - Night Selection 2003 (Japan)" }, + { "SLPM-62528", "Karaoke Revolution Family Pack (Japan)" }, + { "SLES-52308", "Karaoke Stage (Europe)" }, + { "SLES-51200", "Kelly Slater�s Pro Surfer (Europe)" }, + { "SLES-51201", "Kelly Slater�s Pro Surfer (Europe)" }, + { "SLUS-20334", "Kelly Slater�s Pro Surfer (USA)" }, + { "SLES-50114", "Kengo - Master of Bushido (Europe)" }, + { "SLUS-20021", "Kengo - Master of Bushido (USA)" }, + { "SLPM-60177", "Kengou 2 (Japan) (Taikenban)" }, + { "SLPS-25107", "Kengou 2 (Japan)" }, + { "SLPS-25020", "Kidou Senshi Gundam (Japan)" }, + { "SLPS-25120", "Kidou Senshi Gundam - Gihren no Yabou - Zeon Dokuritsu Sensouki (Japan)" }, + { "SLPS-25212", "Kidou Senshi Gundam - Gihren no Yabou - Zeon Dokuritsu Sensouki - Kouryaku Shireisho (Japan)" }, + { "SLPM-65076", "Kidou Senshi Gundam - Renpou vs. Zeon DX (Japan)" }, + { "SLPS-25061", "Kidou Senshi Gundam - Ver. 1.5 (Japan)" }, + { "SLPS-25389", "Kidou Senshi Gundam Seed - Owaranai Ashita e (Japan)" }, + { "SLPS-25123", "Kidou Senshi Gundam Senki - Lost War Chronicles (Japan)" }, + { "SLPM-65033", "Kikou Heidan J-Phoenix (Japan)" }, + { "SLPM-65123", "Kikou Heidan J-Phoenix - Burst Tactics (Japan)" }, + { "SLPM-65199", "Kikou Heidan J-Phoenix - Cobalt Shoutai-hen (Japan)" }, + { "SLPS-20075", "Kikou Heidan J-Phoenix - Joshou-hen (Japan)" }, + { "SLPM-65343", "Kikou Heidan J-Phoenix 2 (Japan)" }, + { "SLUS-20834", "King of Fighters 2000, The (USA)" }, + { "SLPS-25266", "King of Fighters 2001, The (Japan)" }, + { "SLUS-20839", "King of Fighters 2001, The (USA)" }, + { "?", "Kingdom Hearts - Re Chain of Memories (Preview)" }, + { "SLPS-25248", "Kino no Tabi - The Beautiful World (Japan)" }, + { "SLPM-65491", "Kishin Houkou Demonbane (Japan)" }, + { "SLPM-65404", "Kita e. - Diamond Dust (Japan)" }, + { "SLPM-65569", "Kita e. - Diamond Dust+ - Kiss is Beginning. (Japan)" }, + { "SLES-50128", "Knockout Kings 2001 (Europe)" }, + { "SLES-50129", "Knockout Kings 2001 (France)" }, + { "SLES-50130", "Knockout Kings 2001 (Germany)" }, + { "SLUS-20150", "Knockout Kings 2001 (USA)" }, + { "SLPM-65554", "Korokke! Ban Ou no Kiki o Sukue (Japan)" }, + { "SLPM-65447", "Kunoichi (Japan)" }, + { "SLPS-25136", "Kuon no Kizuna - Sairinshou (Japan)" }, + { "SLPM-60127", "Kuri Kuri Mix (Japan) (Taikenban)" }, + { "SLES-50443", "LEGO Racers 2 (Europe)" }, + { "SLUS-20042", "LEGO Racers 2 (USA)" }, + { "SLPS-20165", "La Pucelle - Hikari no Seijo Densetsu (Japan)" }, + { "SLES-50709", "Le Maillon Faible (France)" }, + { "SLES-50131", "Le Mans 24 Hours (Europe)" }, + { "SLUS-20207", "Le Mans 24 Hours (USA) (En,Fr,Es)" }, + { "SLES-51415", "Legacy of Kain Defiance" }, + { "SLUS-20045", "Legend of Alon D�ar, The (USA)" }, + { "SLES-51045", "Legends of Wrestling II (Europe)" }, + { "SLUS-20507", "Legends of Wrestling II (USA)" }, + { "SLES-50892", "Lethal Skies - Elite Pilot - Team SW (Europe)" }, + { "SLUS-20386", "Lethal Skies - Elite Pilot - Team SW (USA)" }, + { "SLES-51886", "Lethal Skies II (Europe)" }, + { "SLUS-20735", "Lethal Skies II (USA)" }, + { "SLPS-29004", "Lord of the Rings, The - Futatsu no Tou (Japan)" }, + { "SLPM-65212", "Lord of the Rings, The - The Two Towers (Asia) (En,Zh)" }, + { "SLES-51252", "Lord of the Rings, The - The Two Towers (Europe)" }, + { "SLPM-67546", "Lord of the Rings, The - The Two Towers (Korea)" }, + { "SLUS-20578", "Lord of the Rings, The - The Two Towers (USA)" }, + { "SLES-50230", "Lotus Challenge (Europe)" }, + { "SLPM-60101", "Love Story (Japan) (Demo)" }, + { "SLPS-20245", "LowRider - Round the World (Japan)" }, + { "SLES-50248", "MDK2 - Armageddon (Europe)" }, + { "SLUS-20105", "MDK2 - Armageddon (USA)" }, + { "SLES-50182", "MTV Music Generator 2 (Europe)" }, + { "SLUS-20222", "MTV Music Generator 2 (USA)" }, + { "SLES-50428", "MX 2002 featuring Ricky Carmichael (Europe)" }, + { "SLUS-20072", "MX 2002 featuring Ricky Carmichael (USA)" }, + { "SLES-50132", "MX Rider (Europe)" }, + { "SLUS-20234", "MX Rider (USA)" }, + { "SLES-51038", "MX SuperFly (Europe)" }, + { "SLUS-20381", "MX SuperFly (USA)" }, + { "SLES-51653", "Mace Griffin - Bounty Hunter (Europe)" }, + { "SLES-51654", "Mace Griffin - Bounty Hunter (Germany)" }, + { "SLUS-20505", "Mace Griffin - Bounty Hunter (USA)" }, + { "SLPM-62077", "Maestromusic II, The (Japan) (Doukonban)" }, + { "SLPM-62078", "Maestromusic II, The (Japan)" }, + { "SLUS-20671", "Mafia (USA)" }, + { "SLPS-20037", "Magical Sports Go Go Golf (Japan)" }, + { "SLPS-20310", "Mahjong Hiryuu Densetsu - Tenpai (Japan)" }, + { "SLPM-65367", "Makai Eiyuuki Maximo - Machine Monster no Yabou (Japan)" }, + { "SLPS-25042", "Maken Shao (Japan)" }, + { "SLES-51058", "Maken Shao - Demon Sword (Europe)" }, + { "SLUS-20358", "Malice (USA)" }, + { "SCED-51406", "Mark of Kri, The (Europe) (Demo)" }, + { "SCES-51164", "Mark of Kri, The (Europe)" }, + { "SCUS-97140", "Mark of Kri, The (USA)" }, + { "SLUS-20722", "Maximo vs Army of Zin (USA)" }, + { "SCPS-11014", "McDonald�s Original Happy Disc (Japan)" }, + { "SLPS-20031", "MechSmith, The - Run=Dim (Japan)" }, + { "SLES-51873", "Medal of Honor - Rising Sun (Europe, Australia)" }, + { "SLES-51875", "Medal of Honor - Rising Sun (Germany)" }, + { "SLPM-65469", "Medal of Honor - Rising Sun (Japan)" }, + { "SLES-51876", "Medal of Honor - Rising Sun (Spain)" }, + { "SLUS-20753", "Medal of Honor - Rising Sun (USA)" }, + { "SLES-51874", "Medal of Honor - Soleil Levant (France)" }, + { "SLES-50903", "MegaRace 3 - Nanotech Disaster (Europe)" }, + { "SLPM-67535", "Memories Off (Korea) (Ja,Ko)" }, + { "SLES-50789", "Men in Black II - Alien Escape (Europe)" }, + { "SLUS-20373", "Men in Black II - Alien Escape (USA)" }, + { "SLES-52599", "Metal Slug 3 (Europe)" }, + { "SLPS-25209", "Metal Slug 3 (Japan)" }, + { "SLES-53383", "Metal Slug 5 (Europe)" }, + { "SLPM-65480", "Michigan (Japan)" }, + { "SLES-52001", "Mission - Impossible - Operation Surma (Europe)" }, + { "SLUS-20400", "Mission - Impossible - Operation Surma (USA)" }, + { "SLES-51271", "Mobile Suit Gundam - Federation vs. Zeon (Europe)" }, + { "SLUS-20382", "Mobile Suit Gundam - Federation vs. Zeon (USA)" }, + { "SLUS-20175", "Mobile Suit Gundam - Journey to Jaburo (USA)" }, + { "SLUS-20741", "Mojo! (USA)" }, + { "SLPS-20381", "Monkey Turn V (Japan)" }, + { "SCPS-12345", "Monster House (Europe)" }, + { "SCPS-12345", "Monster House (Italy)" }, + { "SLPM-65495", "Monster Hunter (Japan)" }, + { "SLES-50908", "Monster Jam - Maximum Destruction (Europe)" }, + { "SLUS-20186", "Monster Jam - Maximum Destruction (USA)" }, + { "SLES-50717", "Mortal Kombat - Deadly Alliance (Europe, Australia)" }, + { "SLES-51439", "Mortal Kombat - Deadly Alliance (Germany)" }, + { "SLPS-25242", "Motion Gravure Series - Kitagawa Tomomi (Japan)" }, + { "SLES-51605", "Motorsiege - Warriors of Primetime (Europe)" }, + { "SLES-51363", "Music 3000 (Europe)" }, + { "SCUS-97263", "My Street (USA) (Demo)" }, + { "SCUS-97212", "My Street (USA)" }, + { "SLES-50726", "Myst III - Exile (Europe)" }, + { "SLUS-20434", "Myst III - Exile (USA)" }, + { "SLES-50080", "NBA Hoopz (Europe)" }, + { "SLUS-20050", "NBA Hoopz (USA)" }, + { "SCUS-97114", "NBA ShootOut 2001 (USA)" }, + { "SLES-50219", "NBA Street (Europe)" }, + { "SLUS-20187", "NBA Street (USA)" }, + { "SCUS-97109", "NCAA Final Four 2001 (USA)" }, + { "SCUS-97136", "NCAA Final Four 2002 (USA)" }, + { "SCUS-97204", "NCAA Final Four 2003 (USA)" }, + { "SCUS-97278", "NCAA Final Four 2004 (USA)" }, + { "SCUS-97107", "NCAA GameBreaker 2001 (USA)" }, + { "SCUS-97106", "NFL GameDay 2001 (USA)" }, + { "SLUS-20308", "NFL Prime Time 2002 (USA)" }, + { "SLES-50213", "NFL QB Club 2002 (Europe)" }, + { "SLUS-20154", "NFL QB Club 2002 (USA)" }, + { "SLES-51341", "NHL 2K3 (Europe)" }, + { "SLUS-20477", "NHL 2K3 (USA)" }, + { "SLES-50451", "NHL Hitz 2002 (Europe)" }, + { "SLUS-20140", "NHL Hitz 2002 (USA) (v2.00)" }, + { "SLES-50712", "NHL Hitz 2003 (Europe)" }, + { "SLUS-20438", "NHL Hitz 2003 (USA)" }, + { "SLUS-20691", "NHL Hitz Pro (USA)" }, + { "SLPS-25276", "Natsu Yume Ya Wa - The Tale of a Midsummer Night�s Dream (Japan)" }, + { "SLPS-25314", "Nebula - Echo Night (Japan)" }, + { "?", "Need for Speed Most Wanted" }, + { "SLUS-20537", "Nickelodeon Jimmy Neutron - Boy Genius (USA)" }, + { "SLUS-20473", "Nickelodeon Rocket Power - Beach Bandits (USA)" }, + { "SLUS-20810", "Nightshade (USA)" }, + { "SLPM-65130", "Nihon Daihyou Senshu ni Narou! (Japan)" }, + { "SLPM-62082", "Nihon Pro Yakyuu Kikou Kounin - Pro Yakyuu Japan 2001 (Japan)" }, + { "SLPS-25324", "Nishikaze no Kyoushikyoku - The Rhapsody of Zephyr (Japan)" }, + { "SLES-50232", "Off-Road - Wide Open (Europe)" }, + { "SLPM-65010", "Onimusha (Japan)" }, + { "SLES-51913", "Onimusha - Blade Warriors (Europe)" }, + { "SLES-50247", "Onimusha - Warlords (Europe)" }, + { "SLUS-20018", "Onimusha - Warlords (USA) (En,Ja)" }, + { "SCPS-15038", "Operator�s Side (Japan)" }, + { "SLPM-65524", "Orange Pocket - Root (Japan)" }, + { "SLPM-65005", "Ore ga Kantoku da! Gekitou Pennant Race (Japan)" }, + { "SCPS-15017", "PaRappa the Rapper 2 (Japan) (En,Ja)" }, + { "SCES-50888", "Pac-Man World 2 (Europe)" }, + { "SLPS-25141", "Pac-Man World 2 (Japan)" }, + { "SLUS-20224", "Pac-Man World 2 (USA) (v1.00)" }, + { "SLUS-20224", "Pac-Man World 2 (USA) (v2.00)" }, + { "SLPS-20186", "Pachinko de Asobou! Fever Dodeka Saurus (Japan)" }, + { "SLES-50212", "Paris-Dakar Rally (Europe)" }, + { "SLUS-20324", "Paris-Dakar Rally (USA)" }, + { "SLES-50252", "Penny Racers (Europe)" }, + { "SLPS-25222", "Pia Carrot e Youkoso!! 3 - Round Summer (Japan)" }, + { "SCPS-11014", "Piposaru 2001 (Japan)" }, + { "SLPM-65611", "Pizzicato Polka - Ensa Gen�ya (Japan)" }, + { "SCPS-15063", "PoPoLoCrois - Tsuki no Okite no Bouken (Japan)" }, + { "SLPS-20323", "Pochi to Nyaa (Japan)" }, + { "SCES-51135", "Primal" }, + { "SLES-50637", "Pro Rally 2002 (Europe)" }, + { "SLPM-65543", "Pro Yakyuu Spirits 2004 (Japan)" }, + { "SLPM-65721", "Pro Yakyuu Spirits 2004 Climax (Japan)" }, + { "SLPM-65426", "Pro Yakyuu Team o Tsukurou! 2003 (Japan)" }, + { "SLES-50821", "Project Zero (Europe)" }, + { "SLPM-66235", "Psychic Force Complete (Japan)" }, + { "SLPM-64534", "Psyvariar - Complete Edition (Korea)" }, + { "SLPM-65532", "Puyo Puyo Fever (Japan) (En,Ja,Fr,De,Es,It)" }, + { "SLES-50126", "Quake III - Revolution (Europe)" }, + { "SLES-50127", "Quake III - Revolution (Germany)" }, + { "SLUS-20167", "Quake III - Revolution (USA)" }, + { "SLPM-62424", "Quiz & Variety - Suku Suku Inufuku (Japan)" }, + { "SLES-50981", "R-C Sports Copter Challenge (Europe)" }, + { "SLES-50077", "RC Revenge Pro (Europe)" }, + { "SLUS-20153", "RC Revenge Pro (USA)" }, + { "SLUS-20340", "RPG Maker II (USA)" }, + { "SLPS-20143", "RPG Tkool 5 (Japan)" }, + { "SLES-51391", "RTL Skispringen 2003 (Germany)" }, + { "SLES-51633", "Racing Simulation 3 (Europe)" }, + { "SLPS-20307", "Rakushou! Pachi-Slot Sengen (Japan)" }, + { "SLES-50763", "Rally Championship (Europe)" }, + { "SLPS-20305", "Real Sports Pro Yakyuu (Japan)" }, + { "SLPM-65004", "Reiselied - Ephemeral Fantasia (Japan) (v1.00)" }, + { "SLPM-65004", "Reiselied - Ephemeral Fantasia (Japan) (v2.01)" }, + { "SLES-50306", "Resident Evil - Code - Veronica X (Europe)" }, + { "SLUS-20184", "Resident Evil - Code - Veronica X (USA)" }, + { "SLES-50650", "Resident Evil - Survivor 2 - Code - Veronica (Europe)" }, + { "SLUS-21134", "Resident Evil 4" }, + { "SLPS-25094", "Reveal Fantasia - Mariel to Yousei Monogatari (Japan)" }, + { "SLES-50113", "Ring of Red (Europe)" }, + { "SLPM-60122", "Ring of Red (Japan) (Taikenban)" }, + { "SLPM-62013", "Ring of Red (Japan)" }, + { "SLUS-20145", "Ring of Red (USA)" }, + { "SLES-51374", "RoboCop (Europe)" }, + { "SLES-50136", "Robot Warlords (Europe)" }, + { "SLES-50137", "Robot Warlords (France)" }, + { "SLES-50138", "Robot Warlords (Germany)" }, + { "SLES-50572", "Robot Wars - Arenas of Destruction (UK)" }, + { "SLPS-25005", "Rock�n Megastage (Japan)" }, + { "SLPM-99999", "Rockman X - Command Mission (Japan)" }, + { "SLPM-65463", "Rocky (Japan)" }, + { "SLES-52002", "Rogue Ops (Europe)" }, + { "SLPM-65534", "Rogue Ops (Japan)" }, + { "SLUS-20746", "Rogue Ops (USA)" }, + { "SLES-52100", "Rugby League (Australia)" }, + { "SLUS-20174", "Rumble Racing" }, + { "SLES-50335", "Rune - Viking Warlord (Europe)" }, + { "SLES-50337", "Rune - Viking Warlord (France)" }, + { "SLES-50336", "Rune - Viking Warlord (Germany)" }, + { "SLES-50338", "Rune - Viking Warlord (Italy)" }, + { "SLES-50339", "Rune - Viking Warlord (Spain)" }, + { "SLUS-20109", "Rune - Viking Warlord (USA)" }, + { "SLPS-25316", "SNK vs. Capcom - SVC Chaos (Japan)" }, + { "SLUS-20433", "SWAT - Global Strike Team (USA)" }, + { "SLUS-20600", "SX Superstar" }, + { "SCPS-11005", "Sagashi ni Ikouyo (Japan)" }, + { "SLPS-20365", "Saikyou Ginsei Shougi 4 (Japan)" }, + { "SLPS-25081", "Saishuu Densha (Japan)" }, + { "SLPM-65275", "Saishuu Heiki Kanojo (Japan)" }, + { "SLPS-20391", "Saiyuuki Reload Gunlock (Japan)" }, + { "SLPM-65109", "Saka Tsuku 2002 - J. League Pro Soccer Club wo Tsukurou! (Japan)" }, + { "SLPM-65515", "Sakura Taisen Monogatari - Mysterious Paris (Japan)" }, + { "SLPS-25559", "Samurai Spirits - Tenkaichi Kenkakuden (Japan)" }, + { "SLPS-20203", "Sanyo Pachinko Paradise 7 - Edokko Gen-san (Japan)" }, + { "SLES-51883", "Scooby-Doo! Mystery Mayhem (Europe)" }, + { "SLUS-20701", "Scooby-Doo! Mystery Mayhem (USA) (En,Fr)" }, + { "SLUS-20424", "Scorpion King, The - Rise of the Akkadian (USA)" }, + { "SLPM-62079", "Se-Pa 2001 (Japan)" }, + { "SLUS-20606", "Seek and Destroy (USA)" }, + { "SLPM-62400", "Sega Ages 2500 Series Vol. 12 - Puyo Puyo Tsuu - Perfect Set (Japan)" }, + { "SLPM-62547", "Sega Ages 2500 Series Vol. 16 - Virtua Fighter 2 (Japan)" }, + { "SLPM-62366", "Sega Ages 2500 Series Vol. 3 - Fantasy Zone (Japan)" }, + { "SLPM-62385", "Sega Ages 2500 Series Vol. 5 - Golden Axe (Japan)" }, + { "SLES-51388", "Sega Bass Fishing Duel (Europe)" }, + { "SLUS-20339", "Sega Bass Fishing Duel (USA)" }, + { "SLES-53461", "Sega Classics Collection (Europe, Australia)" }, + { "SLES-51125", "Sega Soccer Slam (Europe)" }, + { "SLUS-20509", "Sega Soccer Slam (USA)" }, + { "SLES-51253", "Seigneur des Anneaux, Le - Les Deux Tours (France)" }, + { "SLES-51256", "Senor de los Anillos, El - Las Dos Torres (Spain)" }, + { "SLES-50822", "Shadow Hearts (Europe)" }, + { "SLPS-25041", "Shadow Hearts (Japan)" }, + { "SLUS-20347", "Shadow Hearts (USA)" }, + { "SLES-50446", "Shadow Man - 2econd Coming (Europe)" }, + { "SLES-50608", "Shadow Man - 2econd Coming (Germany)" }, + { "SLUS-20413", "Shadow Man - 2econd Coming (USA)" }, + { "?", "Shadow of the Colossus (Europe)" }, + { "SLES-50400", "Shaun Palmer�s Pro Snowboarder (Europe)" }, + { "SLES-50401", "Shaun Palmer�s Pro Snowboarder (France)" }, + { "SLES-50402", "Shaun Palmer�s Pro Snowboarder (Germany)" }, + { "SLUS-20199", "Shaun Palmer�s Pro Snowboarder (USA)" }, + { "SLPM-65334", "Shin Seiki Evangelion - Ayanami Ikusei Keikaku with Asuka Hokan Keikaku (Japan)" }, + { "SLPM-65867", "Shin Seiki Evangelion - Koutetsu no Girlfriend 2nd (Japan)" }, + { "SLPM-65391", "Shinki Gensou - Spectral Souls (Japan)" }, + { "SLPM-65200", "Shinobi (Japan)" }, + { "SLUS-20459", "Shinobi (USA) (En,Ja)" }, + { "SLPM-65328", "Shirachuu Tankenbu (Japan)" }, + { "SLES-52382", "Shrek 2 (Spain)" }, + { "SLPS-25076", "Sidewinder F (Japan)" }, + { "SLPS-25018", "Sidewinder Max (Japan)" }, + { "SLPS-25255", "Sidewinder V (Japan)" }, + { "SLES-51157", "Silent Scope 3 (Europe)" }, + { "SLUS-20514", "Silent Scope 3 (USA) (En,Ja,Es)" }, + { "SLUS-20624", "Simpsons Hit and Run" }, + { "SLES-50754", "Simpsons Skateboarding, The (Europe)" }, + { "SLES-50755", "Simpsons Skateboarding, The (France)" }, + { "SLES-51362", "Simpsons Skateboarding, The (Germany)" }, + { "SLES-51360", "Simpsons Skateboarding, The (Italy)" }, + { "SLES-51361", "Simpsons Skateboarding, The (Spain)" }, + { "SLUS-20114", "Simpsons Skateboarding, The (USA)" }, + { "SLES-51257", "Sims, The (Europe)" }, + { "SLUS-20573", "Sims, The (USA)" }, + { "SLES-50261", "Sky Surfer (Europe)" }, + { "SLPS-20012", "Sky Surfer (Japan)" }, + { "SLPS-20262", "Slot! Pro DX - Fujiko 2 (Japan)" }, + { "SLPS-20285", "Slotter Up Core - Enda! Kyojin no Hoshi (Japan)" }, + { "SLPS-20370", "Slotter Up Core 3 - Yuda! Doronjo ni Omakase (Japan)" }, + { "SLPS-20337", "Slotter Up Core Alpha - Shukko! Yuushou Panel! Shinka! Kyojin no Hoshi (Japan)" }, + { "SLPS-20278", "Slotter Up Mania - Chou Oki-Slot! Pioneer Special (Japan)" }, + { "SLPM-62615", "Slotter Up Mania 6 - Oki no Neppuu! Pioneer Special II (Japan)" }, + { "SLES-51800", "Smash Cars (Europe)" }, + { "SLUS-20620", "Smash Cars (USA)" }, + { "SLPM-65431", "Sonic Heroes (Japan) (En,Ja,Fr,De,Es,It)" }, + { "SLUS-20718", "Sonic Heroes (USA) (En,Ja,Fr,De,Es,It)" }, + { "SLPM-62310", "Soutenryuu - The Arcade (Japan)" }, + { "SLPM-62275", "Space Raiders (Japan)" }, + { "SLES-50486", "Splashdown (Europe)" }, + { "SLES-50268", "SpyHunter (Europe)" }, + { "SLUS-20056", "SpyHunter (USA)" }, + { "SLES-51043", "Spyro - Enter the Dragonfly (Europe)" }, + { "SLUS-20315", "Spyro - Enter the Dragonfly (USA)" }, + { "SLES-52545", "Star Wars - Battlefront (Europe)" }, + { "SLES-52546", "Star Wars - Battlefront (France)" }, + { "SLES-53503", "Star Wars - Battlefront (Germany)" }, + { "SLUS-29164", "Star Wars - Battlefront II (USA) (Beta)" }, + { "SLPS-25252", "Star Wars - Jango Fett (Japan)" }, + { "SLES-50204", "Star Wars - Super Bombad Racing (Europe)" }, + { "SLES-50205", "Star Wars - Super Bombad Racing (France)" }, + { "SLES-50206", "Star Wars - Super Bombad Racing (Germany)" }, + { "SLES-50207", "Star Wars - Super Bombad Racing (Italy)" }, + { "SLES-50208", "Star Wars - Super Bombad Racing (Spain)" }, + { "SLUS-20043", "Star Wars - Super Bombad Racing (USA) (En,Fr,De,Es,It)" }, + { "SLPS-20018", "Stepping Selection (Japan) (Disc 1)" }, + { "SLPS-20019", "Stepping Selection (Japan) (Disc 2)" }, + { "SLES-50072", "Street Fighter EX3 (Europe)" }, + { "SLPM-60105", "Street Fighter EX3 (Japan) (Taikenban)" }, + { "SLPS-20003", "Street Fighter EX3 (Japan)" }, + { "SLUS-20130", "Street Fighter EX3 (USA)" }, + { "SLES-50064", "Stunt GP (Europe)" }, + { "SLPS-20152", "Stunt GP (Japan)" }, + { "SLUS-20218", "Stunt GP (USA)" }, + { "SLES-51160", "Sub Rebellion (Europe)" }, + { "SLUS-20548", "Sub Rebellion (USA)" }, + { "SLPM-65751", "Suigetsu - Mayoi Gokoro (Japan)" }, + { "SLUS-20074", "Summoner (USA)" }, + { "SLES-50533", "Sunny Garcia Surfing (Europe)" }, + { "SLUS-20208", "Sunny Garcia Surfing (USA)" }, + { "SLPS-25070", "Sunrise Eiyuutan 2 (Japan)" }, + { "SLPS-25270", "Sunrise World War (Japan)" }, + { "SLPS-25104", "Super Robot Taisen Impact (Japan)" }, + { "SLES-50897", "Super Trucks (Europe)" }, + { "SLUS-20748", "Super Trucks Racing (USA)" }, + { "SLPM-62423", "SuperLite 2000 Vol. 13 - Tetris - Kiwame Michi (Japan) (v1.02)" }, + { "SLPM-65689", "SuperLite 2000 Vol. 23 - Never 7 - The End of Infinity (Japan)" }, + { "SLES-50419", "Supercar Street Challenge (Europe)" }, + { "SLES-50421", "Supercar Street Challenge (Germany)" }, + { "SLUS-20012", "Supercar Street Challenge (USA)" }, + { "SLES-50852", "Sven-Goeran Eriksson�s World Challenge (Europe)" }, + { "SLES-50794", "Sven-Goeran Eriksson�s World Manager 2002 (Europe)" }, + { "SLES-50033", "Swing Away Golf (Europe)" }, + { "SLUS-20096", "Swing Away Golf (USA)" }, + { "SLPM-65121", "Switch (Japan)" }, + { "SLES-51290", "Sword of the Samurai (Europe)" }, + { "SLPM-65261", "TBS All Star Kanshasai Vol. 1 - Chou Gouka! Quiz Ketteiban (Japan)" }, + { "SLES-50778", "TD Overdrive - The Brotherhood of Speed (Europe)" }, + { "SLPM-62105", "Taikou Risshiden IV (Japan)" }, + { "SLPM-65450", "Tantei Gakuen Q - Kioukan no Satsui (Japan)" }, + { "SLPM-60134", "Technictix (Japan) (Taikenban)" }, + { "SLPS-20055", "Technictix (Japan)" }, + { "SLUS-20981", "Teenage Mutant Ninja Turtles 2 - Battle Nexus (USA)" }, + { "SCAJ-20100", "Tenchu Kurenai (Asia)" }, + { "SLPS-25384", "Tenchu Kurenai (Japan)" }, + { "SLPM-65401", "Tengai Makyou II - Manji Maru (Japan) (Shokai Gentei Picture Label Shiyou)" }, + { "SLPM-65401", "Tengai Makyou II - Manji Maru (Japan)" }, + { "SLPM-65398", "Tennis no Oujisama - Kiss of Prince Flame (Japan)" }, + { "SLPM-65397", "Tennis no Oujisama - Kiss of Prince Ice (Japan)" }, + { "SLPM-65323", "Tennis no Oujisama - Smash Hit! (Japan)" }, + { "SLPM-62359", "Tennis no Oujisama - Smash Hit! Original Anime Game (Japan)" }, + { "SLPM-65371", "Tennis no Oujisama - Sweat & Tears 2 - Seishun Gakuen Teikyuusai �03 - Perfect Live (Japan)" }, + { "SLPS-20053", "Tenshi no Present - Marl Oukoku Monogatari (Japan) (Genteiban)" }, + { "SLPS-20066", "Tenshi no Present - Marl Oukoku Monogatari (Japan)" }, + { "SLPM-65598", "Tenshou Gakuen Gensouroku (Japan)" }, + { "SLPS-25298", "Tentama - 1st Sunny Side (Japan)" }, + { "SLUS-20213", "Test Drive (USA)" }, + { "SLUS-20177", "Test Drive Off-Road - Wide Open (USA)" }, + { "SLES-50551", "Tetris Worlds (Europe)" }, + { "SLUS-20099", "Theme Park Roller Coaster (USA)" }, + { "SLES-50078", "TimeSplitters (Europe)" }, + { "SLUS-20090", "TimeSplitters (USA) (v1.10)" }, + { "SLUS-20090", "TimeSplitters (USA) (v2.00)" }, + { "SLES-51181", "Tom Clancy�s Ghost Recon (Europe)" }, + { "SLES-51182", "Tom Clancy�s Ghost Recon (Germany)" }, + { "SLUS-20613", "Tom Clancy�s Ghost Recon (USA)" }, + { "SLED-51472", "Tom Clancy�s Splinter Cell (Europe) (Demo)" }, + { "SLUS-20652", "Tom Clancy�s Splinter Cell (USA)" }, + { "SLES-50400", "Tony Hawk�s Pro Skater 3 (Europe)" }, + { "SLES-50401", "Tony Hawk�s Pro Skater 3 (France)" }, + { "SLES-50402", "Tony Hawk�s Pro Skater 3 (Germany)" }, + { "SLUS-20199", "Tony Hawk�s Pro Skater 3 (USA) (Rev 1)" }, + { "SLUS-20199", "Tony Hawk�s Pro Skater 3 (USA)" }, + { "SLPS-99999", "Tony Hawk�s Pro Skater 4 (USA) (v1.02)" }, + { "SLPS-99999", "Tony Hawk�s Pro Skater 4 (USA) (v2.01)" }, + { "SCED-52441", "Transformers (Europe) (Demo)" }, + { "SLUS-20149", "Tribes - Aerial Assault (USA)" }, + { "SLUS-20931", "Trigger Man (USA)" }, + { "SLUS-20168", "Triple Play Baseball (USA)" }, + { "SLPS-20196", "Tsuki no Hikari - Shizumeru Kane no Satsujin (Japan)" }, + { "SCES-50360", "Twisted Metal - Black (Europe)" }, + { "SCUS-97101", "Twisted Metal - Black (USA)" }, + { "SLPS-20080", "Typing Namidabashi Ashita no Joe Touda (Japan) (USB Keyboard Doukonban)" }, + { "SLPS-20194", "U - Underwater Unit (Japan)" }, + { "SLES-50195", "UEFA Challenge (Europe)" }, + { "SLPS-25294", "Uchuu no Stellvia (Japan)" }, + { "SLPS-25364", "Ultraman (Japan)" }, + { "SLES-51606", "Unlimited Saga (Europe)" }, + { "SLPS-25185", "Unlimited Saga (Japan) (Limited Edition)" }, + { "SLPS-25199", "Unlimited Saga (Japan)" }, + { "SLUS-20678", "Unlimited Saga (USA)" }, + { "SLES-50725", "V-Rally 3 (Europe)" }, + { "SLPM-65191", "V-Rally 3 (Japan) (En,Ja)" }, + { "SCES-50411", "Vampire Night (Europe, Australia)" }, + { "SLPS-25077", "Vampire Night (Japan)" }, + { "SLUS-20221", "Vampire Night (USA)" }, + { "SLPS-20034", "Velvet File (Japan)" }, + { "SLPS-25012", "Victorious Boxers (Japan)" }, + { "SLPS-25129", "Victorious Boxers - Championship Version (Japan)" }, + { "SLES-50280", "Victorious Boxers - Ippo�s Road to Glory (Europe)" }, + { "SLUS-20282", "Victorious Boxers - Ippo�s Road to Glory (USA)" }, + { "SLPS-25287", "Victorious Boxers 2 - Ioop�s Road to Glory (Japan)" }, + { "SLUS-20951", "Viewtiful Joe (USA)" }, + { "SLES-51699", "Virtua Fighter - 10th Anniversary Edition (Europe)" }, + { "SLES-51616", "Virtua Fighter 4 - Evolution (Europe)" }, + { "SLPM-65270", "Virtua Fighter 4 - Evolution (Japan)" }, + { "SLKA-00000", "Virtua Fighter 4 - Evolution (Korea)" }, + { "SLUS-00000", "Virtua Fighter 4 - Evolution (USA)" }, + { "SLES-51600", "WWE Crush Hour (Europe)" }, + { "SLUS-20385", "WWE Crush Hour (USA)" }, + { "SLES-52036", "WWE SmackDown! Here Comes the Pain (Europe)" }, + { "SLUS-20787", "WWE SmackDown! Here Comes the Pain (USA)" }, + { "SLES-51283", "WWE SmackDown! Shut Your Mouth (Europe)" }, + { "SLUS-20483", "WWE SmackDown! Shut Your Mouth (USA)" }, + { "SLES-50183", "Wacky Races Starring Dastardly & Muttley (Europe)" }, + { "SLES-51272", "Wakeboarding Unleashed featuring Shaun Murray (Europe)" }, + { "SLES-51273", "Wakeboarding Unleashed featuring Shaun Murray (France)" }, + { "SLUS-20418", "Wakeboarding Unleashed featuring Shaun Murray (USA)" }, + { "SLUS-20075", "Walt Disney�s The Jungle Book - Rhythm n� Groove (USA)" }, + { "SLES-51973", "War Chess (Europe)" }, + { "SCUS-97197", "War of the Monsters (USA)" }, + { "SLES-50503", "Weakest Link, The (Europe)" }, + { "SLPM-62019", "Winning Post 4 Maximum (Japan)" }, + { "SLPM-62058", "Winning Post 4 Maximum 2001 (Japan) (Super Value Set)" }, + { "SLPM-62123", "Winning Post 5 (Japan)" }, + { "SLPM-62280", "Winning Post 5 Maximum 2002 (Japan) (Premium Pack)" }, + { "SLPM-62221", "Winning Post 5 Maximum 2002 (Japan)" }, + { "SLES-50035", "Winter X Games Snowboarding (Europe)" }, + { "SLES-50670", "Winter X Games Snowboarding 2 (Europe)" }, + { "SLUS-20321", "Winter X Games Snowboarding 2002 (USA)" }, + { "SLES-50170", "World Destruction League - Thunder Tanks (Europe)" }, + { "SLUS-20005", "World Destruction League - Thunder Tanks (USA)" }, + { "SLES-50262", "World Destruction League - WarJetz (Europe)" }, + { "SLUS-20007", "World Destruction League - WarJetz (USA)" }, + { "SLUS-20611", "World Series Baseball 2K3 (USA)" }, + { "SLPM-62268", "World Soccer Winning Eleven 6 - Final Evolution (Japan)" }, + { "SLES-51843", "Worms 3D (Europe)" }, + { "SLES-51202", "Wreckless - The Yakuza Missions (Europe)" }, + { "SLUS-20431", "Wreckless - The Yakuza Missions (USA)" }, + { "SLES-50430", "X Games Skateboarding (Europe)" }, + { "SLES-50031", "X Squad (Europe)" }, + { "SLUS-20094", "X Squad (USA)" }, + { "SLES-50210", "XGIII - Extreme G Racing (Europe) (v1.02)" }, + { "SLES-50210", "XGIII - Extreme G Racing (Europe) (v2.00)" }, + { "SLUS-20302", "XGIII - Extreme G Racing (USA)" }, + { "SLPS-29002", "Xenosaga Episode I - Der Wille zur Macht (Japan) (Premium Box)" }, + { "SLPS-29002", "Xenosaga Episode I - Der Wille zur Macht (Japan)" }, + { "SLUS-20469", "Xenosaga Episode I - Der Wille zur Macht (USA)" }, + { "SLPS-25048", "Zeonic Front - Kidou Senshi Gundam 0079 (Japan)" }, + { "SLPS-25074", "Zero (Japan)" }, + { "SLPS-25303", "Zero - Akai Chou (Japan)" }, + { "SLPM-65019", "Zone of the Enders - Z.O.E (Japan)" }, + { "SLES-50933", "eJay ClubWorld - The Music Making Experience (Europe)" }, + { "SLUS-20525", "eJay Clubworld - The Music Making Experience (USA)" } +}; + +const char* getGameName(const std::string& gameId) +{ + auto it = gameDatabase.find(gameId); + if (it != gameDatabase.end()) + return it->second.c_str(); + + return nullptr; +} \ No newline at end of file diff --git a/ps2xTest/src/pad_input_tests.cpp b/ps2xTest/src/pad_input_tests.cpp index bbff0caf..4d973527 100644 --- a/ps2xTest/src/pad_input_tests.cpp +++ b/ps2xTest/src/pad_input_tests.cpp @@ -1,10 +1,13 @@ #include "MiniTest.h" #include "ps2_stubs.h" +#include "ps2_syscalls.h" +#include "Stubs/Pad.h" #include #include #include + namespace { constexpr uint32_t kPadDataAddr = 0x1000; diff --git a/ps2xTest/src/ps2_gs_tests.cpp b/ps2xTest/src/ps2_gs_tests.cpp index e6c29384..3f4728a4 100644 --- a/ps2xTest/src/ps2_gs_tests.cpp +++ b/ps2xTest/src/ps2_gs_tests.cpp @@ -7,6 +7,7 @@ #include "runtime/ps2_gs_psmct32.h" #include "runtime/ps2_gs_psmt4.h" #include "runtime/ps2_gs_psmt8.h" +#include "Stubs/GS.h" #include #include diff --git a/ps2xTest/src/ps2_runtime_expansion_tests.cpp b/ps2xTest/src/ps2_runtime_expansion_tests.cpp index e0008e25..ec7c3981 100644 --- a/ps2xTest/src/ps2_runtime_expansion_tests.cpp +++ b/ps2xTest/src/ps2_runtime_expansion_tests.cpp @@ -10,6 +10,9 @@ #include "runtime/ps2_gs_gpu.h" #include "runtime/ps2_gs_psmct32.h" #include "ps2_runtime_macros.h" +#include "Stubs/MPEG.h" +#include "Stubs/Audio.h" +#include "Stubs/GS.h" #include #include @@ -581,63 +584,7 @@ void register_ps2_runtime_expansion_tests() t.Equals(getRegU32(&setParamCtx, 2), 0x00012340u, "sceSdRemote set-param calls should not trap or disturb the movie audio state"); }); - - tc.Run("MPEG compat layout enters playing state and defers end to guest state", [](TestCase &t) - { - constexpr uint32_t kCodeVeronicaMpegAddr = 0x01E27140u; - constexpr uint32_t kCodeVeronicaVideoStateAddr = 0x01E271E8u; - constexpr uint32_t kCodeVeronicaMovieStateAddr = 0x01E21914u; - - std::vector rdram(PS2_RAM_SIZE, 0u); - PS2MpegCompatLayout compat{}; - compat.mpegObjectAddr = kCodeVeronicaMpegAddr; - compat.videoStateAddr = kCodeVeronicaVideoStateAddr; - compat.movieStateAddr = kCodeVeronicaMovieStateAddr; - compat.syntheticFramesBeforeEnd = 0u; - compat.playingVideoStateValue = 0u; - compat.playingMovieStateValue = 2u; - compat.finishedVideoStateValue = 3u; - compat.finishedMovieStateValue = 3u; - ps2_stubs::setMpegCompatLayout(compat); - ps2_stubs::resetMpegStubState(); - - R5900Context firstIsEndCtx{}; - setRegU32(firstIsEndCtx, 4, kCodeVeronicaMpegAddr); - ps2_stubs::sceMpegIsEnd(rdram.data(), &firstIsEndCtx, nullptr); - t.Equals(getRegS32(firstIsEndCtx, 2), 0, - "compat movie path should get one synthetic frame before the MPEG stub reports end"); - - R5900Context getPictureCtx{}; - setRegU32(getPictureCtx, 4, kCodeVeronicaMpegAddr); - setRegU32(getPictureCtx, 5, 0x00124000u); - setRegU32(getPictureCtx, 6, 440u); - ps2_stubs::sceMpegGetPicture(rdram.data(), &getPictureCtx, nullptr); - t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaMpegAddr + 0x00u), 320u, - "compat movie fallback should seed a movie width"); - t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaMpegAddr + 0x04u), 240u, - "compat movie fallback should seed a movie height"); - t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaVideoStateAddr), 0u, - "compat movie fallback should leave videoDec.state in the active state while playback is running"); - t.Equals(Ps2FastRead32(rdram.data(), kCodeVeronicaMovieStateAddr), 2u, - "compat movie fallback should promote the movie state to playing after the first synthetic frame"); - - R5900Context secondIsEndCtx{}; - setRegU32(secondIsEndCtx, 4, kCodeVeronicaMpegAddr); - ps2_stubs::sceMpegIsEnd(rdram.data(), &secondIsEndCtx, nullptr); - t.Equals(getRegS32(secondIsEndCtx, 2), 0, - "compat movie fallback should keep the decode thread alive until the guest marks playback finished"); - - Ps2FastWrite32(rdram.data(), kCodeVeronicaMovieStateAddr, 3u); - - R5900Context guestFinishedIsEndCtx{}; - setRegU32(guestFinishedIsEndCtx, 4, kCodeVeronicaMpegAddr); - ps2_stubs::sceMpegIsEnd(rdram.data(), &guestFinishedIsEndCtx, nullptr); - t.Equals(getRegS32(guestFinishedIsEndCtx, 2), 1, - "compat movie fallback should report end once the guest movie state reaches the finished value"); - - ps2_stubs::clearMpegCompatLayout(); - }); - + tc.Run("IPU init skips missing optional helper instead of dispatching the default trap", [](TestCase &t) { PS2Runtime runtime; From 6f3b299c45889552a507f65571ceb5eebfcbeaff Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 02:50:18 -0300 Subject: [PATCH 12/30] feat: better throw error on empty cd path feat: remove recompiler unusde function feat: apply missing patch --- ps2xRuntime/include/ps2_runtime.h | 2 +- ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp | 864 ++-- ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp | 193 +- .../src/lib/Kernel/Stubs/Compatibility.cpp | 184 +- ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp | 213 +- ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp | 96 +- ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp | 238 +- ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp | 1134 ++--- ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp | 1907 ++++---- .../src/lib/Kernel/Stubs/Helpers/Support.h | 6 +- ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp | 151 +- ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp | 1765 ++++--- ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp | 692 ++- .../src/lib/Kernel/Stubs/MemoryCard.cpp | 2221 +++++---- ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp | 703 ++- ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp | 26 +- ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp | 1067 +++-- ps2xRuntime/src/lib/Kernel/Stubs/System.cpp | 90 +- ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp | 106 +- .../src/lib/Kernel/Stubs/Unimplemented.cpp | 4 +- ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp | 648 +-- .../src/lib/Kernel/Syscalls/FileIO.cpp | 830 ++-- .../src/lib/Kernel/Syscalls/Interrupt.cpp | 1003 ++-- .../src/lib/Kernel/Syscalls/Lifecycle.cpp | 2 - ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp | 4090 +++++++++-------- ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp | 1414 +++--- .../src/lib/Kernel/Syscalls/System.cpp | 1530 +++--- .../src/lib/Kernel/Syscalls/Thread.cpp | 1418 +++--- ps2xRuntime/src/lib/game_overrides.cpp | 16 +- ps2xRuntime/src/lib/ps2_audio.cpp | 101 +- ps2xRuntime/src/lib/ps2_audio_vag.cpp | 174 +- ps2xRuntime/src/lib/ps2_gif_arbiter.cpp | 30 +- ps2xRuntime/src/lib/ps2_gs_gpu.cpp | 160 +- ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp | 4 +- ps2xRuntime/src/lib/ps2_iop_audio.cpp | 16 +- ps2xRuntime/src/lib/ps2_memory.cpp | 2 +- ps2xRuntime/src/lib/ps2_pad.cpp | 129 +- ps2xRuntime/src/lib/ps2_runtime.cpp | 19 +- ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp | 28 +- ps2xRuntime/src/lib/ps2_vu1.cpp | 10 +- ps2xRuntime/src/main.cpp | 2 +- 41 files changed, 11648 insertions(+), 11640 deletions(-) diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 2a75ff00..62853918 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -447,7 +447,7 @@ class PS2Runtime ~PS2Runtime(); bool initialize(const char *title = "PS2 Game"); - bool ensureCoreSubsystemsInitialized(); + bool syncCoreSubsystems(); bool loadELF(const std::string &elfPath); void run(); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp index 99fe1c29..0b37516c 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp @@ -3,544 +3,540 @@ namespace ps2_stubs { -namespace -{ -constexpr uint32_t kLibSdCmdSetParam = 0x8010u; -constexpr uint32_t kLibSdCmdBlockTrans = 0x80E0u; -constexpr uint32_t kAudioPositionMask = 0x00FFFFFFu; + namespace + { + constexpr uint32_t kLibSdCmdSetParam = 0x8010u; + constexpr uint32_t kLibSdCmdBlockTrans = 0x80E0u; + constexpr uint32_t kAudioPositionMask = 0x00FFFFFFu; -struct AudioStubState -{ - bool initialized = false; - uint32_t currentBlockBase = 0u; - uint32_t currentBlockSize = 0u; - uint32_t currentPauseBase = 0u; -}; + struct AudioStubState + { + bool initialized = false; + uint32_t currentBlockBase = 0u; + uint32_t currentBlockSize = 0u; + uint32_t currentPauseBase = 0u; + }; -std::mutex g_audio_stub_mutex; -AudioStubState g_audio_stub_state; + std::mutex g_audio_stub_mutex; + AudioStubState g_audio_stub_state; -void resetAudioStubStateUnlocked() -{ - g_audio_stub_state = {}; -} -} - -void resetAudioStubState() -{ - std::lock_guard lock(g_audio_stub_mutex); - resetAudioStubStateUnlocked(); -} + void resetAudioStubStateUnlocked() + { + g_audio_stub_state = {}; + } + } -void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSdCallBack", rdram, ctx, runtime); -} + void resetAudioStubState() + { + std::lock_guard lock(g_audio_stub_mutex); + resetAudioStubStateUnlocked(); + } + void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSdCallBack", rdram, ctx, runtime); + } -void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; + void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)runtime; - const uint32_t cmd = getRegU32(ctx, 5); - const uint32_t cmdArg0 = getRegU32(ctx, 6); - const uint32_t cmdArg1 = getRegU32(ctx, 7); - const uint32_t sp = getRegU32(ctx, 29); - const uint32_t arg4 = FAST_READ32(sp + 0x10u); - const uint32_t arg5 = FAST_READ32(sp + 0x14u); - const uint32_t arg6 = FAST_READ32(sp + 0x18u); + const uint32_t cmd = getRegU32(ctx, 5); + const uint32_t cmdArg0 = getRegU32(ctx, 6); + const uint32_t cmdArg1 = getRegU32(ctx, 7); + const uint32_t sp = getRegU32(ctx, 29); + const uint32_t arg4 = FAST_READ32(sp + 0x10u); + const uint32_t arg5 = FAST_READ32(sp + 0x14u); + const uint32_t arg6 = FAST_READ32(sp + 0x18u); - std::lock_guard lock(g_audio_stub_mutex); - g_audio_stub_state.initialized = true; + std::lock_guard lock(g_audio_stub_mutex); + g_audio_stub_state.initialized = true; - if (cmd == kLibSdCmdBlockTrans) - { - if (arg4 != 0u) + if (cmd == kLibSdCmdBlockTrans) { - g_audio_stub_state.currentBlockBase = arg4 & kAudioPositionMask; + if (arg4 != 0u) + { + g_audio_stub_state.currentBlockBase = arg4 & kAudioPositionMask; + } + if (arg5 != 0u) + { + g_audio_stub_state.currentBlockSize = arg5; + } + if (arg6 != 0u) + { + g_audio_stub_state.currentPauseBase = arg6 & kAudioPositionMask; + } } - if (arg5 != 0u) + else if (cmd == kLibSdCmdSetParam) { - g_audio_stub_state.currentBlockSize = arg5; + (void)cmdArg0; + (void)cmdArg1; } - if (arg6 != 0u) - { - g_audio_stub_state.currentPauseBase = arg6 & kAudioPositionMask; - } - } - else if (cmd == kLibSdCmdSetParam) - { - (void)cmdArg0; - (void)cmdArg1; - } - - // Some games only sample the low 24 bits of the reported SPU transfer head. - // Returning the last configured transfer base keeps the ring-buffer math - // stable without emulating SPU DMA progress. - setReturnU32(ctx, g_audio_stub_state.currentBlockBase & kAudioPositionMask); -} - - -void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_audio_stub_mutex); - resetAudioStubStateUnlocked(); - g_audio_stub_state.initialized = true; - setReturnS32(ctx, 0); -} + // Some games only sample the low 24 bits of the reported SPU transfer head. + // Returning the last configured transfer base keeps the ring-buffer math + // stable without emulating SPU DMA progress. + setReturnU32(ctx, g_audio_stub_state.currentBlockBase & kAudioPositionMask); + } + void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; -void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime); -} + std::lock_guard lock(g_audio_stub_mutex); + resetAudioStubStateUnlocked(); + g_audio_stub_state.initialized = true; + setReturnS32(ctx, 0); + } + void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime); + } -void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime); -} + void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime); + } -void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime); -} + void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime); + } -void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime); -} + void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime); + } -void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime); -} + void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime); + } -void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime); -} + void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime); + } -void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime); -} + void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime); + } -void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime); -} + void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime); + } -void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime); -} + void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime); + } -void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime); -} + void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime); + } -void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime); -} + void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime); + } -void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime); -} + void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime); + } -void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime); -} + void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime); + } -void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime); -} + void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime); + } -void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime); -} + void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime); + } -void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime); -} + void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime); + } -void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime); -} + void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime); + } -void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime); -} + void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime); + } -void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime); -} + void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime); + } -void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime); -} + void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime); + } -void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime); -} + void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime); + } -void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime); -} + void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime); + } -void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime); -} + void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime); + } -void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime); -} + void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime); + } -void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime); -} + void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime); + } -void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime); -} + void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime); + } -void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime); -} + void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime); + } -void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime); -} + void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime); + } -void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime); -} + void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime); + } -void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime); -} + void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime); + } -void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime); -} + void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime); + } -void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime); -} + void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime); + } -void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime); -} + void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime); + } -void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime); -} + void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime); + } -void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime); -} + void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime); + } -void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime); -} + void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime); + } -void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime); -} + void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime); + } -void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime); -} + void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime); + } -void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime); -} + void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime); + } -void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime); -} + void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime); + } -void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime); -} + void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime); + } -void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime); -} + void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime); + } -void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime); -} + void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime); + } -void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime); -} + void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime); + } -void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime); -} + void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime); + } -void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime); -} + void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime); + } -void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime); -} + void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime); + } -void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime); -} + void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime); + } -void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime); -} + void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime); + } -void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime); -} + void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime); + } -void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime); -} + void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime); + } -void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime); -} + void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime); + } -void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime); -} + void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime); + } -void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime); -} + void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime); + } -void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime); -} + void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime); + } -void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime); -} + void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime); + } -void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime); -} + void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime); + } -void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime); -} + void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime); + } -void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime); -} + void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime); + } -void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime); -} + void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime); + } -void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime); -} + void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime); + } -void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime); -} + void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime); + } -void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime); -} + void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime); + } -void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime); -} + void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime); + } -void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime); -} + void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime); + } -void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime); -} + void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime); + } -void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime); -} + void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime); + } -void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime); -} + void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime); + } -void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime); -} + void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime); + } -void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime); -} + void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime); + } -void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime); -} + void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime); + } -void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime); -} + void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime); + } -void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime); -} + void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime); + } -void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime); -} + void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime); + } -void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime); -} + void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime); + } -void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime); -} + void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime); + } -void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime); -} + void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime); + } -void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime); -} + void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime); + } -void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime); -} + void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime); + } -void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime); -} + void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime); + } -void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime); -} + void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime); + } -void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime); -} + void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime); + } -void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime); -} + void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime); + } -void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime); -} + void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime); + } -void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime); -} + void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime); + } -void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime); -} + void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime); + } -void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime); -} + void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime); + } -void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime); -} + void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime); + } -void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime); -} + void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp index a8263316..286886f3 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/CD.cpp @@ -3,7 +3,7 @@ namespace ps2_stubs { - void sceCdRead(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t a0 = getRegU32(ctx, 4); // usually lbn const uint32_t a1 = getRegU32(ctx, 5); // usually sector count @@ -14,35 +14,35 @@ namespace ps2_stubs uint32_t lbn = 0; uint32_t sectors = 0; uint32_t buf = 0; - const char* tag = ""; + const char *tag = ""; }; auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t + { + const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); + if (requested == 0) { - const uint64_t requested = static_cast(sectors) * static_cast(kCdSectorSize); - if (requested == 0) - { - return 0; - } + return 0; + } - const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); - const uint64_t clamped = std::min(requested, maxBytes); - return static_cast(clamped); - }; + const uint64_t maxBytes = static_cast(PS2_RAM_SIZE - offset); + const uint64_t clamped = std::min(requested, maxBytes); + return static_cast(clamped); + }; - auto tryRead = [&](const CdReadArgs& args) -> bool + auto tryRead = [&](const CdReadArgs &args) -> bool + { + const uint32_t offset = args.buf & PS2_RAM_MASK; + const size_t bytes = clampReadBytes(args.sectors, offset); + if (bytes == 0) { - const uint32_t offset = args.buf & PS2_RAM_MASK; - const size_t bytes = clampReadBytes(args.sectors, offset); - if (bytes == 0) - { - return true; - } + return true; + } - return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); - }; + return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes); + }; - CdReadArgs selected{ a0, a1, a2, "a0/a1/a2" }; + CdReadArgs selected{a0, a1, a2, "a0/a1/a2"}; bool ok = tryRead(selected); if (!ok) @@ -57,9 +57,9 @@ namespace ps2_stubs CdReadArgs{a0, a2, a1, "a0/a2/a1"}, CdReadArgs{a1, a0, a2, "a1/a0/a2"}, CdReadArgs{a1, a2, a0, "a1/a2/a0"}, - CdReadArgs{a2, a0, a1, "a2/a0/a1"} }; + CdReadArgs{a2, a0, a1, "a2/a0/a1"}}; - for (const CdReadArgs& candidate : alternatives) + for (const CdReadArgs &candidate : alternatives) { if (candidate.sectors > kMaxReasonableSectors) { @@ -76,11 +76,11 @@ namespace ps2_stubs if (recoverLogCount < 16) { RUNTIME_LOG("[sceCdRead] recovered with alternate args " << candidate.tag - << " (pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << ")" << std::endl); + << " (pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << ")" << std::endl); ++recoverLogCount; } selected = candidate; @@ -103,10 +103,10 @@ namespace ps2_stubs if (unresolvedLogCount < 32) { std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << " a0=0x" << a0 - << " a1=0x" << a1 - << " a2=0x" << a2 << std::dec << std::endl; + << " ra=0x" << getRegU32(ctx, 31) + << " a0=0x" << a0 + << " a1=0x" << a1 + << " a2=0x" << a2 << std::dec << std::endl; ++unresolvedLogCount; } } @@ -122,99 +122,94 @@ namespace ps2_stubs setReturnS32(ctx, 0); } - - void sceCdSync(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); // 0 = completed/not busy } - - void sceCdGetError(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, g_lastCdError); } - - void sceCdRI(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { TODO_NAMED("sceCdRI", rdram, ctx, runtime); } - - void sceCdRM(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { TODO_NAMED("sceCdRM", rdram, ctx, runtime); } - - void sceCdApplyNCmd(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdBreak(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdCallback(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); } - void sceCdChangeThreadPriority(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdDelayThread(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); } - void sceCdDiskReady(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 2); } - void sceCdGetDiskType(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { // SCECdPS2DVD setReturnS32(ctx, 0x14); } - void sceCdGetReadPos(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnU32(ctx, g_cdStreamingLbn); } - void sceCdGetToc(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t tocAddr = getRegU32(ctx, 4); - if (uint8_t* toc = getMemPtr(rdram, tocAddr)) + if (uint8_t *toc = getMemPtr(rdram, tocAddr)) { std::memset(toc, 0, 1024); } setReturnS32(ctx, 1); } - void sceCdInit(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { g_cdInitialized = true; g_lastCdError = 0; setReturnS32(ctx, 1); } - void sceCdInitEeCB(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdIntToPos(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t lsn = getRegU32(ctx, 4); uint32_t posAddr = getRegU32(ctx, 5); - uint8_t* pos = getMemPtr(rdram, posAddr); + uint8_t *pos = getMemPtr(rdram, posAddr); if (!pos) { setReturnS32(ctx, 0); @@ -234,26 +229,26 @@ namespace ps2_stubs setReturnS32(ctx, 1); } - void sceCdMmode(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { g_cdMode = getRegU32(ctx, 4); setReturnS32(ctx, 1); } - void sceCdNcmdDiskReady(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 2); } - void sceCdPause(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdPosToInt(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t posAddr = getRegU32(ctx, 4); - const uint8_t* pos = getConstMemPtr(rdram, posAddr); + const uint8_t *pos = getConstMemPtr(rdram, posAddr); if (!pos) { setReturnS32(ctx, -1); @@ -268,14 +263,14 @@ namespace ps2_stubs setReturnS32(ctx, lsn); } - void sceCdReadChain(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t chainAddr = getRegU32(ctx, 4); bool ok = true; for (int i = 0; i < 64; ++i) { - uint32_t* entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); + uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); if (!entry) { ok = false; @@ -310,10 +305,10 @@ namespace ps2_stubs setReturnS32(ctx, ok ? 1 : 0); } - void sceCdReadClock(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t clockAddr = getRegU32(ctx, 4); - uint8_t* clockData = getMemPtr(rdram, clockAddr); + uint8_t *clockData = getMemPtr(rdram, clockAddr); if (!clockData) { setReturnS32(ctx, 0); @@ -340,12 +335,12 @@ namespace ps2_stubs setReturnS32(ctx, 1); } - void sceCdReadIOPm(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { sceCdRead(rdram, ctx, runtime); } - void sceCdSearchFile(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t fileAddr = getRegU32(ctx, 4); uint32_t pathAddr = getRegU32(ctx, 5); @@ -357,11 +352,11 @@ namespace ps2_stubs if (shouldTrace) { RUNTIME_LOG("[sceCdSearchFile] pc=0x" << std::hex << ctx->pc - << " ra=0x" << callerRa - << " file=0x" << fileAddr - << " pathAddr=0x" << pathAddr - << " path=\"" << sanitizeForLog(path) << "\"" - << std::dec << std::endl); + << " ra=0x" << callerRa + << " file=0x" << fileAddr + << " pathAddr=0x" << pathAddr + << " path=\"" << sanitizeForLog(path) << "\"" + << std::dec << std::endl); } ++traceCount; @@ -378,8 +373,8 @@ namespace ps2_stubs preview << (i == 0 ? "" : " ") << static_cast(byte); } std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr - << " preview=" << preview.str() - << " ra=0x" << callerRa << std::dec << std::endl; + << " preview=" << preview.str() + << " ra=0x" << callerRa << std::dec << std::endl; } ++emptyPathCount; g_lastCdError = -1; @@ -393,8 +388,8 @@ namespace ps2_stubs if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) { std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (normalized path is empty, root: " << getCdRootPath().string() << ")" - << std::endl; + << " (normalized path is empty, root: " << getCdRootPath().string() << ")" + << std::endl; } ++emptyNormalizedCount; g_lastCdError = -1; @@ -424,8 +419,8 @@ namespace ps2_stubs if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) { std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (root: " << getCdRootPath().string() - << ", repeat=" << samePathFailCount << ")" << std::endl; + << " (root: " << getCdRootPath().string() + << ", repeat=" << samePathFailCount << ")" << std::endl; } setReturnS32(ctx, 0); return; @@ -442,46 +437,46 @@ namespace ps2_stubs if (shouldTrace) { RUNTIME_LOG("[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) - << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn - << " size=0x" << resolvedEntry.sizeBytes - << " sectors=0x" << resolvedEntry.sectors - << std::dec << std::endl); + << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn + << " size=0x" << resolvedEntry.sizeBytes + << " sectors=0x" << resolvedEntry.sectors + << std::dec << std::endl); } setReturnS32(ctx, 1); } - void sceCdSeek(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { g_cdStreamingLbn = getRegU32(ctx, 4); setReturnS32(ctx, 1); } - void sceCdStandby(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdStatus(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, g_cdInitialized ? 6 : 0); } - void sceCdStInit(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdStop(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdStPause(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdStRead(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t sectors = getRegU32(ctx, 4); uint32_t buf = getRegU32(ctx, 5); @@ -501,7 +496,7 @@ namespace ps2_stubs g_cdStreamingLbn += sectors; } - if (int32_t* err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) { *err = ok ? 0 : g_lastCdError; } @@ -509,53 +504,53 @@ namespace ps2_stubs setReturnS32(ctx, ok ? static_cast(sectors) : 0); } - void sceCdStream(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdStResume(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdStSeek(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { g_cdStreamingLbn = getRegU32(ctx, 4); setReturnS32(ctx, 1); } - void sceCdStSeekF(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { g_cdStreamingLbn = getRegU32(ctx, 4); setReturnS32(ctx, 1); } - void sceCdStStart(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { g_cdStreamingLbn = getRegU32(ctx, 4); setReturnS32(ctx, 1); } - void sceCdStStat(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); } - void sceCdStStop(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 1); } - void sceCdSyncS(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); } - void sceCdTrayReq(uint8_t* rdram, R5900Context* ctx, PS2Runtime* runtime) + void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t statusPtr = getRegU32(ctx, 5); - if (uint32_t* status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) + if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) { *status = 0; } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp index f3808bf0..f548b640 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Compatibility.cpp @@ -4,120 +4,120 @@ namespace ps2_stubs { -void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t count = getRegU32(ctx, 5); // $a1 - const uint32_t size = getRegU32(ctx, 6); // $a2 - const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; - setReturnU32(ctx, guestAddr); -} - -void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, 0u); - ctx->pc = getRegU32(ctx, 31); -} - -void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, 1u); - ctx->pc = getRegU32(ctx, 31); -} - -void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 4)); - ctx->pc = getRegU32(ctx, 31); -} - -void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t guestAddr = getRegU32(ctx, 5); // $a1 - if (runtime && guestAddr != 0u) + void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - runtime->guestFree(guestAddr); + const uint32_t count = getRegU32(ctx, 5); // $a1 + const uint32_t size = getRegU32(ctx, 6); // $a2 + const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; + setReturnU32(ctx, guestAddr); } -} - -void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t size = getRegU32(ctx, 5); // $a1 - const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; - setReturnU32(ctx, guestAddr); -} -void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void ret0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, 0u); + ctx->pc = getRegU32(ctx, 31); + } -void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t wcAddr = getRegU32(ctx, 5); // $a1 - const uint32_t strAddr = getRegU32(ctx, 6); // $a2 - const int32_t n = static_cast(getRegU32(ctx, 7)); // $a3 - if (n <= 0 || strAddr == 0u) + void ret1(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; + setReturnU32(ctx, 1u); + ctx->pc = getRegU32(ctx, 31); } - const uint8_t *src = getConstMemPtr(rdram, strAddr); - if (!src) + void reta0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + setReturnU32(ctx, getRegU32(ctx, 4)); + ctx->pc = getRegU32(ctx, 31); } - const uint8_t ch = *src; - if (wcAddr != 0u) + void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (uint8_t *dst = getMemPtr(rdram, wcAddr)) + const uint32_t guestAddr = getRegU32(ctx, 5); // $a1 + if (runtime && guestAddr != 0u) { - const uint32_t out = static_cast(ch); - std::memcpy(dst, &out, sizeof(out)); + runtime->guestFree(guestAddr); } } - setReturnS32(ctx, (ch == 0u) ? 0 : 1); -} -void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; + void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t size = getRegU32(ctx, 5); // $a1 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; + setReturnU32(ctx, guestAddr); + } - if (format_addr != 0) + void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - if (rendered.size() > 2048) + setReturnS32(ctx, 0); + } + + void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t wcAddr = getRegU32(ctx, 5); // $a1 + const uint32_t strAddr = getRegU32(ctx, 6); // $a2 + const int32_t n = static_cast(getRegU32(ctx, 7)); // $a3 + if (n <= 0 || strAddr == 0u) { - rendered.resize(2048); + setReturnS32(ctx, 0); + return; } - PS2_IF_AGRESSIVE_LOGS({ - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - RUNTIME_LOG("PS2 printf: " << logLine); - RUNTIME_LOG(std::flush); - } - else if (count == kMaxPrintfLogs + 1) + + const uint8_t *src = getConstMemPtr(rdram, strAddr); + if (!src) + { + setReturnS32(ctx, -1); + return; + } + + const uint8_t ch = *src; + if (wcAddr != 0u) + { + if (uint8_t *dst = getMemPtr(rdram, wcAddr)) { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + const uint32_t out = static_cast(ch); + std::memcpy(dst, &out, sizeof(out)); } - }); - ret = static_cast(rendered.size()); + } + setReturnS32(ctx, (ch == 0u) ? 0 : 1); } - else + + void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; - } + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; - setReturnS32(ctx, ret); -} + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + if (rendered.size() > 2048) + { + rendered.resize(2048); + } + PS2_IF_AGRESSIVE_LOGS({ + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + RUNTIME_LOG("PS2 printf: " << logLine); + RUNTIME_LOG(std::flush); + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + }); + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; + } + + setReturnS32(ctx, ret); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp index 6533aa09..49106dbb 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp @@ -3,111 +3,110 @@ namespace ps2_stubs { -void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 4)); -} - - -void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaCallback", rdram, ctx, runtime); -} - -void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaDebug", rdram, ctx, runtime); -} - -void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t chanArg = getRegU32(ctx, 4); - const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); - setReturnU32(ctx, channelBase); -} - -void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); -} - -void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime); -} - -void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaPause", rdram, ctx, runtime); -} - -void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); -} - -void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); -} - -void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRecv", rdram, ctx, runtime); -} - -void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime); -} - -void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime); -} - -void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaRestart", rdram, ctx, runtime); -} - -void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); -} - -void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); -} - -void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); -} - -void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true)); -} - -void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); -} - -void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); -} - -void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDmaWatch", rdram, ctx, runtime); -} + void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, getRegU32(ctx, 4)); + } + + void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaCallback", rdram, ctx, runtime); + } + + void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaDebug", rdram, ctx, runtime); + } + + void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t chanArg = getRegU32(ctx, 4); + const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); + setReturnU32(ctx, channelBase); + } + + void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); + } + + void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime); + } + + void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaPause", rdram, ctx, runtime); + } + + void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); + } + + void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); + } + + void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaRecv", rdram, ctx, runtime); + } + + void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime); + } + + void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime); + } + + void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } + + void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaRestart", rdram, ctx, runtime); + } + + void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); + } + + void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); + } + + void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); + } + + void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true)); + } + + void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); + } + + void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); + } + + void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDmaWatch", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp index 16f34d4d..1309d3cf 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Deci2.cpp @@ -3,56 +3,48 @@ namespace ps2_stubs { -void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2Close", rdram, ctx, runtime); -} - - -void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime); -} - - -void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime); -} - - -void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime); -} - - -void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime); -} - - -void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime); -} - - -void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2Open", rdram, ctx, runtime); -} - - -void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime); -} - - -void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime); -} + void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2Close", rdram, ctx, runtime); + } + + void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime); + } + + void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime); + } + + void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime); + } + + void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime); + } + + void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime); + } + + void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2Open", rdram, ctx, runtime); + } + + void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime); + } + + void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp index 76bc35dc..b1083af7 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/FileIO.cpp @@ -3,169 +3,149 @@ namespace ps2_stubs { -void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsDbChk", rdram, ctx, runtime); -} - + void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceFsDbChk", rdram, ctx, runtime); + } -void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsIntrSigSema", rdram, ctx, runtime); -} + void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceFsIntrSigSema", rdram, ctx, runtime); + } + void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceFsSemExit", rdram, ctx, runtime); + } -void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsSemExit", rdram, ctx, runtime); -} + void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceFsSemInit", rdram, ctx, runtime); + } + void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceFsSigSema", rdram, ctx, runtime); + } -void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsSemInit", rdram, ctx, runtime); -} + void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioClose(rdram, ctx, runtime); + } + void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t statAddr = getRegU32(ctx, 5); + if (uint8_t *statBuf = getMemPtr(rdram, statAddr)) + { + std::memset(statBuf, 0, 128); + setReturnS32(ctx, 0); + return; + } + setReturnS32(ctx, -1); + } -void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceFsSigSema", rdram, ctx, runtime); -} + void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioLseek(rdram, ctx, runtime); + } + void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioOpen(rdram, ctx, runtime); + } -void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioClose(rdram, ctx, runtime); -} + void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioRead(rdram, ctx, runtime); + } + void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioClose(rdram, ctx, runtime); + } -void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t statAddr = getRegU32(ctx, 5); - if (uint8_t *statBuf = getMemPtr(rdram, statAddr)) + void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::memset(statBuf, 0, 128); setReturnS32(ctx, 0); - return; } - setReturnS32(ctx, -1); -} - - -void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioLseek(rdram, ctx, runtime); -} - - -void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioOpen(rdram, ctx, runtime); -} - - -void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioRead(rdram, ctx, runtime); -} + void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioClose(rdram, ctx, runtime); -} + void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int32_t cmd = static_cast(getRegU32(ctx, 5)); + const uint32_t argAddr = getRegU32(ctx, 6); + // HTCI wait paths poll sceIoctl(fd, 1, &state) and expect state to move + // away from 1 once host-side I/O is no longer busy. + if (cmd == 1 && argAddr != 0u) + { + uint8_t *argPtr = getMemPtr(rdram, argAddr); + if (!argPtr) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t ready = 0u; + std::memcpy(argPtr, &ready, sizeof(ready)); + } -void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + setReturnS32(ctx, 0); + } + void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioLseek(rdram, ctx, runtime); + } -void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioOpen(rdram, ctx, runtime); + } + void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioRead(rdram, ctx, runtime); + } -void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t cmd = static_cast(getRegU32(ctx, 5)); - const uint32_t argAddr = getRegU32(ctx, 6); + void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::fioWrite(rdram, ctx, runtime); + } - // HTCI wait paths poll sceIoctl(fd, 1, &state) and expect state to move - // away from 1 once host-side I/O is no longer busy. - if (cmd == 1 && argAddr != 0u) + void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint8_t *argPtr = getMemPtr(rdram, argAddr); - if (!argPtr) + const uint32_t statAddr = getRegU32(ctx, 5); + uint8_t *statBuf = getMemPtr(rdram, statAddr); + if (!statBuf) { setReturnS32(ctx, -1); return; } - const uint32_t ready = 0u; - std::memcpy(argPtr, &ready, sizeof(ready)); + // Minimal fake stat payload: zeroed structure indicates a valid, readable file. + std::memset(statBuf, 0, 128); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - - -void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioLseek(rdram, ctx, runtime); -} - - -void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioOpen(rdram, ctx, runtime); -} - - -void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioRead(rdram, ctx, runtime); -} - - -void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioWrite(rdram, ctx, runtime); -} - - -void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t statAddr = getRegU32(ctx, 5); - uint8_t *statBuf = getMemPtr(rdram, statAddr); - if (!statBuf) + void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + ps2_syscalls::fioWrite(rdram, ctx, runtime); } - // Minimal fake stat payload: zeroed structure indicates a valid, readable file. - std::memset(statBuf, 0, 128); - setReturnS32(ctx, 0); -} - - -void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::fioWrite(rdram, ctx, runtime); -} - - -void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) + void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("ps2_stub cvFsSetDefDev"); - ++logCount; + static int logCount = 0; + if (logCount < 8) + { + RUNTIME_LOG("ps2_stub cvFsSetDefDev"); + ++logCount; + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp index 877e3aa5..41e43874 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Font.cpp @@ -3,665 +3,665 @@ namespace ps2_stubs { -static void writeU32AtGp(uint8_t *rdram, uint32_t gp, int32_t offset, uint32_t value) -{ - const uint32_t addr = gp + static_cast(offset); - if (uint8_t *p = getMemPtr(rdram, addr)) - *reinterpret_cast(p) = value; -} + static void writeU32AtGp(uint8_t *rdram, uint32_t gp, int32_t offset, uint32_t value) + { + const uint32_t addr = gp + static_cast(offset); + if (uint8_t *p = getMemPtr(rdram, addr)) + *reinterpret_cast(p) = value; + } -void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - const uint32_t a0 = getRegU32(ctx, 4); - const uint32_t a1 = getRegU32(ctx, 5); - const uint32_t a2 = getRegU32(ctx, 6); - const uint32_t a3 = getRegU32(ctx, 7); - writeU32AtGp(rdram, gp, -0x7b60, a1); - writeU32AtGp(rdram, gp, -0x7b5c, a2); - writeU32AtGp(rdram, gp, -0x7b64, a0); - writeU32AtGp(rdram, gp, -0x7c98, a3); - writeU32AtGp(rdram, gp, -0x7b4c, 0x7f7f7f7f); - writeU32AtGp(rdram, gp, -0x7b50, 0x3f800000); - writeU32AtGp(rdram, gp, -0x7b54, 0x3f800000); - writeU32AtGp(rdram, gp, -0x7b58, 0); - - if (runtime && a0 != 0u) + void sceeFontInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if ((a0 * 256u) + 64u <= PS2_GS_VRAM_SIZE) + const uint32_t gp = getRegU32(ctx, 28); + const uint32_t a0 = getRegU32(ctx, 4); + const uint32_t a1 = getRegU32(ctx, 5); + const uint32_t a2 = getRegU32(ctx, 6); + const uint32_t a3 = getRegU32(ctx, 7); + writeU32AtGp(rdram, gp, -0x7b60, a1); + writeU32AtGp(rdram, gp, -0x7b5c, a2); + writeU32AtGp(rdram, gp, -0x7b64, a0); + writeU32AtGp(rdram, gp, -0x7c98, a3); + writeU32AtGp(rdram, gp, -0x7b4c, 0x7f7f7f7f); + writeU32AtGp(rdram, gp, -0x7b50, 0x3f800000); + writeU32AtGp(rdram, gp, -0x7b54, 0x3f800000); + writeU32AtGp(rdram, gp, -0x7b58, 0); + + if (runtime && a0 != 0u) { - uint32_t clutData[16]; - for (uint32_t i = 0; i < 16u; ++i) + if ((a0 * 256u) + 64u <= PS2_GS_VRAM_SIZE) { - uint8_t alpha = static_cast((i * 0x80u) / 15u); - clutData[i] = (i == 0) - ? 0x00000000u - : (0x80u | (0x80u << 8) | (0x80u << 16) | (static_cast(alpha) << 24)); + uint32_t clutData[16]; + for (uint32_t i = 0; i < 16u; ++i) + { + uint8_t alpha = static_cast((i * 0x80u) / 15u); + clutData[i] = (i == 0) + ? 0x00000000u + : (0x80u | (0x80u << 8) | (0x80u << 16) | (static_cast(alpha) << 24)); + } + constexpr uint32_t kClutQwc = 4u; + constexpr uint32_t kHeaderQwc = 6u; + constexpr uint32_t kTotalQwc = kHeaderQwc + kClutQwc; + uint32_t pktAddr = runtime->guestMalloc(kTotalQwc * 16u, 16u); + if (pktAddr != 0u) + { + uint8_t *pkt = getMemPtr(rdram, pktAddr); + if (pkt) + { + uint64_t *q = reinterpret_cast(pkt); + const uint32_t dbp = a0 & 0x3FFFu; + constexpr uint8_t psm = 0u; + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; + q[2] = (static_cast(dbp) << 32) | (1ULL << 48) | (static_cast(psm) << 56); + q[3] = 0x50ULL; + q[4] = 0ULL; + q[5] = 0x51ULL; + q[6] = 16ULL | (1ULL << 32); + q[7] = 0x52ULL; + q[8] = 0ULL; + q[9] = 0x53ULL; + q[10] = (2ULL << 58) | (kClutQwc & 0x7FFF) | (1ULL << 15); + q[11] = 0ULL; + std::memcpy(pkt + 12u * 8u, clutData, 64u); + constexpr uint32_t GIF_CHANNEL = 0x1000A000; + constexpr uint32_t CHCR_STR_MODE0 = 0x101u; + runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, kTotalQwc & 0xFFFFu); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + runtime->memory().processPendingTransfers(); + } + } } - constexpr uint32_t kClutQwc = 4u; - constexpr uint32_t kHeaderQwc = 6u; - constexpr uint32_t kTotalQwc = kHeaderQwc + kClutQwc; - uint32_t pktAddr = runtime->guestMalloc(kTotalQwc * 16u, 16u); + } + + setReturnS32(ctx, static_cast(a0 + 4)); + } + + void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + static constexpr uint32_t kFontBase = 0x176148u; + static constexpr uint32_t kFontEntrySz = 0x24u; + + const uint32_t fontDataAddr = getRegU32(ctx, 4); + const int fontId = static_cast(getRegU32(ctx, 5)); + const int tbp0 = static_cast(getRegU32(ctx, 7)); + + if (!fontDataAddr || !runtime) + { + setReturnS32(ctx, tbp0); + return; + } + + const uint8_t *fontPtr = getConstMemPtr(rdram, fontDataAddr); + if (!fontPtr) + { + setReturnS32(ctx, tbp0); + return; + } + + int width = static_cast(*reinterpret_cast(fontPtr + 0x00u)); + int height = static_cast(*reinterpret_cast(fontPtr + 0x04u)); + uint32_t raw8 = *reinterpret_cast(fontPtr + 0x08u); + int fontDataSz = static_cast(*reinterpret_cast(fontPtr + 0x0cu)); + + uint32_t pointsize = raw8; + uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); + if (raw8 & 0x40000000u) + { + pointsize = raw8 - 0x40000000u; + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) + *reinterpret_cast(p) = 1u; + } + else + { + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) + *reinterpret_cast(p) = 0u; + } + + int tw = (width >= 0) ? (width >> 6) : ((width + 0x3f) >> 6); + int qwc = (fontDataSz >= 0) ? (fontDataSz >> 4) : ((fontDataSz + 0xf) >> 4); + + uint32_t glyphSrc = fontDataAddr + static_cast(fontDataSz) + 0x10u; + uint32_t glyphAlloc = runtime->guestMalloc(0x2010u, 0x40u); + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) + *reinterpret_cast(p) = glyphAlloc; + + if (glyphAlloc != 0u) + { + uint8_t *dst = getMemPtr(rdram, glyphAlloc); + const uint8_t *src = getConstMemPtr(rdram, glyphSrc); + if (dst && src) + std::memcpy(dst, src, 0x2010u); + } + + uint32_t isDoubleByte = 0; + if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff + 0x20u)) + isDoubleByte = *reinterpret_cast(p); + if (isDoubleByte == 0u) + { + uint32_t kernSrc = glyphSrc + 0x2010u; + uint32_t kernAlloc = runtime->guestMalloc(0xc400u, 0x40u); + if (glyphAlloc != 0u) + *reinterpret_cast(getMemPtr(rdram, glyphAlloc + 0x2000u)) = kernAlloc; + if (kernAlloc != 0u) + { + uint8_t *dst = getMemPtr(rdram, kernAlloc); + const uint8_t *src = getConstMemPtr(rdram, kernSrc); + if (dst && src) + std::memcpy(dst, src, 0xc400u); + } + } + + auto writeFontField = [&](uint32_t off, uint32_t val) + { + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + off)) + *reinterpret_cast(p) = val; + }; + writeFontField(0x18u, pointsize); + writeFontField(0x08u, static_cast(tbp0)); + writeFontField(0x0cu, static_cast(tw)); + + int logW = 0; + for (int w = width; w != 1 && w != 0; w = static_cast(static_cast(w) >> 1)) + logW++; + writeFontField(0x10u, static_cast(logW)); + + int logH = 0; + for (int h = height; h != 1 && h != 0; h = static_cast(static_cast(h) >> 1)) + logH++; + writeFontField(0x14u, static_cast(logH)); + writeFontField(0x04u, 0u); + writeFontField(0x1cu, getRegU32(ctx, 6)); + + if (qwc > 0) + { + const uint32_t imageBytes = static_cast(qwc) * 16u; + const uint8_t psm = 20u; + const uint32_t headerQwc = 12u; + const uint32_t imageQwc = static_cast(qwc); + const uint32_t totalQwc = headerQwc + imageQwc; + uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u); if (pktAddr != 0u) { uint8_t *pkt = getMemPtr(rdram, pktAddr); - if (pkt) + const uint8_t *imgSrc = getConstMemPtr(rdram, fontDataAddr + 0x10u); + if (pkt && imgSrc) { uint64_t *q = reinterpret_cast(pkt); - const uint32_t dbp = a0 & 0x3FFFu; - constexpr uint8_t psm = 0u; + const uint32_t dbp = static_cast(tbp0) & 0x3FFFu; + const uint32_t dbw = static_cast(tw > 0 ? tw : 1) & 0x3Fu; + const uint32_t rrw = static_cast(width > 0 ? width : 64); + const uint32_t rrh = static_cast(height > 0 ? height : 1); + q[0] = makeGiftagAplusD(4u); q[1] = 0xEULL; - q[2] = (static_cast(dbp) << 32) | (1ULL << 48) | (static_cast(psm) << 56); + q[2] = (static_cast(psm) << 24) | (1ULL << 16) | + (static_cast(dbp) << 32) | (static_cast(dbw) << 48) | + (static_cast(psm) << 56); q[3] = 0x50ULL; q[4] = 0ULL; q[5] = 0x51ULL; - q[6] = 16ULL | (1ULL << 32); + q[6] = (static_cast(rrh) << 32) | static_cast(rrw); q[7] = 0x52ULL; q[8] = 0ULL; q[9] = 0x53ULL; - q[10] = (2ULL << 58) | (kClutQwc & 0x7FFF) | (1ULL << 15); + q[10] = (2ULL << 58) | (imageQwc & 0x7FFF) | (1ULL << 15); q[11] = 0ULL; - std::memcpy(pkt + 12u * 8u, clutData, 64u); + std::memcpy(pkt + 12 * 8, imgSrc, imageBytes); + constexpr uint32_t GIF_CHANNEL = 0x1000A000; constexpr uint32_t CHCR_STR_MODE0 = 0x101u; runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, kTotalQwc & 0xFFFFu); + runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - runtime->memory().processPendingTransfers(); } } } - } - setReturnS32(ctx, static_cast(a0 + 4)); -} + int retTbp = tbp0 + ((fontDataSz >= 0 ? fontDataSz : fontDataSz + 0x7f) >> 7); + setReturnS32(ctx, retTbp); + } -void sceeFontLoadFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ static constexpr uint32_t kFontBase = 0x176148u; static constexpr uint32_t kFontEntrySz = 0x24u; - const uint32_t fontDataAddr = getRegU32(ctx, 4); - const int fontId = static_cast(getRegU32(ctx, 5)); - const int tbp0 = static_cast(getRegU32(ctx, 7)); - - if (!fontDataAddr || !runtime) - { - setReturnS32(ctx, tbp0); - return; - } - - const uint8_t *fontPtr = getConstMemPtr(rdram, fontDataAddr); - if (!fontPtr) - { - setReturnS32(ctx, tbp0); - return; - } - - int width = static_cast(*reinterpret_cast(fontPtr + 0x00u)); - int height = static_cast(*reinterpret_cast(fontPtr + 0x04u)); - uint32_t raw8 = *reinterpret_cast(fontPtr + 0x08u); - int fontDataSz = static_cast(*reinterpret_cast(fontPtr + 0x0cu)); - - uint32_t pointsize = raw8; - uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); - if (raw8 & 0x40000000u) + void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - pointsize = raw8 - 0x40000000u; - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) - *reinterpret_cast(p) = 1u; - } - else - { - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + 0x20u)) - *reinterpret_cast(p) = 0u; - } + const float sclx = ctx->f[12]; + const float scly = ctx->f[13]; + const uint32_t bufAddr = getRegU32(ctx, 4); + const uint64_t paramX = GPR_U64(ctx, 5); + const int64_t paramY = GPR_S64(ctx, 6); + const int paramW = static_cast(getRegU32(ctx, 7)); + const int paramH = static_cast(getRegU32(ctx, 8)); + const uint32_t colour = getRegU32(ctx, 9); + const int alignCh = static_cast(getRegU32(ctx, 10) & 0xffu); + const int fontId = static_cast(getRegU32(ctx, 11)); + + const uint32_t sp = getRegU32(ctx, 29); + const uint32_t strAddr = FAST_READ32(sp + 0x00u); + const uint32_t param14 = FAST_READ32(sp + 0x18u); + + if (bufAddr == 0u) + { + setReturnS32(ctx, 0); + ctx->pc = getRegU32(ctx, 31); + return; + } - int tw = (width >= 0) ? (width >> 6) : ((width + 0x3f) >> 6); - int qwc = (fontDataSz >= 0) ? (fontDataSz >> 4) : ((fontDataSz + 0xf) >> 4); + const uint32_t gp = getRegU32(ctx, 28); + const uint32_t fontModeAdj = FAST_READ32(gp + static_cast(static_cast(-0x7c98))); + const uint32_t shiftAmt = fontModeAdj & 0x1fu; + const int scrHeight = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b5c)))); + const int scrWidth = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b60)))); + const uint32_t fontClut = FAST_READ32(gp + static_cast(static_cast(-0x7b64))); - uint32_t glyphSrc = fontDataAddr + static_cast(fontDataSz) + 0x10u; - uint32_t glyphAlloc = runtime->guestMalloc(0x2010u, 0x40u); - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) - *reinterpret_cast(p) = glyphAlloc; + const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); + const int lineH = static_cast(FAST_READ32(kFontBase + fontOff + 0x18u)); - if (glyphAlloc != 0u) - { - uint8_t *dst = getMemPtr(rdram, glyphAlloc); - const uint8_t *src = getConstMemPtr(rdram, glyphSrc); - if (dst && src) - std::memcpy(dst, src, 0x2010u); - } + int iVar21 = 0; + int iStack_dc = 0; + uint32_t uStack_d8 = 0; + int iVar15 = 0; - uint32_t isDoubleByte = 0; - if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff + 0x20u)) - isDoubleByte = *reinterpret_cast(p); - if (isDoubleByte == 0u) - { - uint32_t kernSrc = glyphSrc + 0x2010u; - uint32_t kernAlloc = runtime->guestMalloc(0xc400u, 0x40u); - if (glyphAlloc != 0u) - *reinterpret_cast(getMemPtr(rdram, glyphAlloc + 0x2000u)) = kernAlloc; - if (kernAlloc != 0u) + int16_t sVar8; { - uint8_t *dst = getMemPtr(rdram, kernAlloc); - const uint8_t *src = getConstMemPtr(rdram, kernSrc); - if (dst && src) - std::memcpy(dst, src, 0xc400u); + int yStepRaw = static_cast(static_cast((lineH + 6) * 16) * scly); + sVar8 = static_cast((static_cast(paramY) + 0x700) * 16) + static_cast(yStepRaw >> static_cast(shiftAmt)); } - } - auto writeFontField = [&](uint32_t off, uint32_t val) - { - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff + off)) - *reinterpret_cast(p) = val; - }; - writeFontField(0x18u, pointsize); - writeFontField(0x08u, static_cast(tbp0)); - writeFontField(0x0cu, static_cast(tw)); - - int logW = 0; - for (int w = width; w != 1 && w != 0; w = static_cast(static_cast(w) >> 1)) - logW++; - writeFontField(0x10u, static_cast(logW)); - - int logH = 0; - for (int h = height; h != 1 && h != 0; h = static_cast(static_cast(h) >> 1)) - logH++; - writeFontField(0x14u, static_cast(logH)); - writeFontField(0x04u, 0u); - writeFontField(0x1cu, getRegU32(ctx, 6)); - - if (qwc > 0) - { - const uint32_t imageBytes = static_cast(qwc) * 16u; - const uint8_t psm = 20u; - const uint32_t headerQwc = 12u; - const uint32_t imageQwc = static_cast(qwc); - const uint32_t totalQwc = headerQwc + imageQwc; - uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u); - if (pktAddr != 0u) + int16_t baseX = static_cast((static_cast(paramX) + 0x6c0) * 16); + + if (param14 != 0u) { - uint8_t *pkt = getMemPtr(rdram, pktAddr); - const uint8_t *imgSrc = getConstMemPtr(rdram, fontDataAddr + 0x10u); - if (pkt && imgSrc) - { - uint64_t *q = reinterpret_cast(pkt); - const uint32_t dbp = static_cast(tbp0) & 0x3FFFu; - const uint32_t dbw = static_cast(tw > 0 ? tw : 1) & 0x3Fu; - const uint32_t rrw = static_cast(width > 0 ? width : 64); - const uint32_t rrh = static_cast(height > 0 ? height : 1); - - q[0] = makeGiftagAplusD(4u); - q[1] = 0xEULL; - q[2] = (static_cast(psm) << 24) | (1ULL << 16) | - (static_cast(dbp) << 32) | (static_cast(dbw) << 48) | - (static_cast(psm) << 56); - q[3] = 0x50ULL; - q[4] = 0ULL; - q[5] = 0x51ULL; - q[6] = (static_cast(rrh) << 32) | static_cast(rrw); - q[7] = 0x52ULL; - q[8] = 0ULL; - q[9] = 0x53ULL; - q[10] = (2ULL << 58) | (imageQwc & 0x7FFF) | (1ULL << 15); - q[11] = 0ULL; - std::memcpy(pkt + 12 * 8, imgSrc, imageBytes); - - constexpr uint32_t GIF_CHANNEL = 0x1000A000; - constexpr uint32_t CHCR_STR_MODE0 = 0x101u; - runtime->memory().writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); - runtime->memory().writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - } + int64_t clipY1 = static_cast(static_cast(paramY) + paramH); + int64_t clipX1 = static_cast(static_cast(paramX) + paramW); + if (clipY1 > scrHeight - 1) + clipY1 = static_cast(scrHeight - 1); + if (clipX1 > scrWidth - 1) + clipX1 = static_cast(scrWidth - 1); + int64_t clipY0 = 0; + if (paramY > 0) + clipY0 = paramY; + uint64_t clipX0 = 0; + if (static_cast(paramX) > 0) + clipX0 = paramX; + + uint64_t scissor = clipX0 | (static_cast(static_cast(clipX1)) << 16) | (static_cast(static_cast(clipY0)) << 32) | (static_cast(static_cast(clipY1)) << 48); + + FAST_WRITE64(bufAddr + 0x00, 0x1000000000000005ull); + FAST_WRITE64(bufAddr + 0x08, 0x0eull); + FAST_WRITE64(bufAddr + 0x10, scissor); + FAST_WRITE64(bufAddr + 0x18, 0x40ull); + FAST_WRITE64(bufAddr + 0x20, 0x20000ull); + FAST_WRITE64(bufAddr + 0x28, 0x47ull); + FAST_WRITE64(bufAddr + 0x30, 0x44ull); + FAST_WRITE64(bufAddr + 0x38, 0x42ull); + FAST_WRITE64(bufAddr + 0x40, 0x160ull); + FAST_WRITE64(bufAddr + 0x48, 0x14ull); + FAST_WRITE64(bufAddr + 0x50, 0x156ull); + FAST_WRITE64(bufAddr + 0x58, 0ull); + FAST_WRITE64(bufAddr + 0x60, 0x1000000000000001ull); + FAST_WRITE64(bufAddr + 0x68, 0x0eull); + + uint64_t iVar5 = static_cast(FAST_READ32(kFontBase + fontOff + 0x08u)); + uint64_t iVar22 = static_cast(FAST_READ32(kFontBase + fontOff + 0x0cu)); + uint64_t iVar3 = static_cast(FAST_READ32(kFontBase + fontOff + 0x10u)); + uint64_t iVar4 = static_cast(FAST_READ32(kFontBase + fontOff + 0x14u)); + + uint64_t tex0 = iVar5 | 0x2000000000000000ull | (iVar22 << 14) | 0x400000000ull | (iVar3 << 26) | 0x1400000ull | (iVar4 << 30) | (static_cast(fontClut) << 37); + + FAST_WRITE64(bufAddr + 0x70, tex0); + FAST_WRITE64(bufAddr + 0x78, 6ull); + FAST_WRITE64(bufAddr + 0x80, 0x1000000000000001ull); + FAST_WRITE64(bufAddr + 0x88, 0x0eull); + FAST_WRITE64(bufAddr + 0x90, static_cast(colour)); + FAST_WRITE64(bufAddr + 0x98, 1ull); + + iVar21 = 10; } - } - - int retTbp = tbp0 + ((fontDataSz >= 0 ? fontDataSz : fontDataSz + 0x7f) >> 7); - setReturnS32(ctx, retTbp); -} - -static constexpr uint32_t kFontBase = 0x176148u; -static constexpr uint32_t kFontEntrySz = 0x24u; - -void sceeFontGenerateString(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const float sclx = ctx->f[12]; - const float scly = ctx->f[13]; - const uint32_t bufAddr = getRegU32(ctx, 4); - const uint64_t paramX = GPR_U64(ctx, 5); - const int64_t paramY = GPR_S64(ctx, 6); - const int paramW = static_cast(getRegU32(ctx, 7)); - const int paramH = static_cast(getRegU32(ctx, 8)); - const uint32_t colour = getRegU32(ctx, 9); - const int alignCh = static_cast(getRegU32(ctx, 10) & 0xffu); - const int fontId = static_cast(getRegU32(ctx, 11)); - - const uint32_t sp = getRegU32(ctx, 29); - const uint32_t strAddr = FAST_READ32(sp + 0x00u); - const uint32_t param14 = FAST_READ32(sp + 0x18u); - - if (bufAddr == 0u) - { - setReturnS32(ctx, 0); - ctx->pc = getRegU32(ctx, 31); - return; - } - - const uint32_t gp = getRegU32(ctx, 28); - const uint32_t fontModeAdj = FAST_READ32(gp + static_cast(static_cast(-0x7c98))); - const uint32_t shiftAmt = fontModeAdj & 0x1fu; - const int scrHeight = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b5c)))); - const int scrWidth = static_cast(FAST_READ32(gp + static_cast(static_cast(-0x7b60)))); - const uint32_t fontClut = FAST_READ32(gp + static_cast(static_cast(-0x7b64))); - - const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); - const int lineH = static_cast(FAST_READ32(kFontBase + fontOff + 0x18u)); - - int iVar21 = 0; - int iStack_dc = 0; - uint32_t uStack_d8 = 0; - int iVar15 = 0; - int16_t sVar8; - { - int yStepRaw = static_cast(static_cast((lineH + 6) * 16) * scly); - sVar8 = static_cast((static_cast(paramY) + 0x700) * 16) + static_cast(yStepRaw >> static_cast(shiftAmt)); - } - - int16_t baseX = static_cast((static_cast(paramX) + 0x6c0) * 16); - - if (param14 != 0u) - { - int64_t clipY1 = static_cast(static_cast(paramY) + paramH); - int64_t clipX1 = static_cast(static_cast(paramX) + paramW); - if (clipY1 > scrHeight - 1) clipY1 = static_cast(scrHeight - 1); - if (clipX1 > scrWidth - 1) clipX1 = static_cast(scrWidth - 1); - int64_t clipY0 = 0; - if (paramY > 0) clipY0 = paramY; - uint64_t clipX0 = 0; - if (static_cast(paramX) > 0) clipX0 = paramX; - - uint64_t scissor = clipX0 | (static_cast(static_cast(clipX1)) << 16) - | (static_cast(static_cast(clipY0)) << 32) | (static_cast(static_cast(clipY1)) << 48); - - FAST_WRITE64(bufAddr + 0x00, 0x1000000000000005ull); - FAST_WRITE64(bufAddr + 0x08, 0x0eull); - FAST_WRITE64(bufAddr + 0x10, scissor); - FAST_WRITE64(bufAddr + 0x18, 0x40ull); - FAST_WRITE64(bufAddr + 0x20, 0x20000ull); - FAST_WRITE64(bufAddr + 0x28, 0x47ull); - FAST_WRITE64(bufAddr + 0x30, 0x44ull); - FAST_WRITE64(bufAddr + 0x38, 0x42ull); - FAST_WRITE64(bufAddr + 0x40, 0x160ull); - FAST_WRITE64(bufAddr + 0x48, 0x14ull); - FAST_WRITE64(bufAddr + 0x50, 0x156ull); - FAST_WRITE64(bufAddr + 0x58, 0ull); - FAST_WRITE64(bufAddr + 0x60, 0x1000000000000001ull); - FAST_WRITE64(bufAddr + 0x68, 0x0eull); - - uint64_t iVar5 = static_cast(FAST_READ32(kFontBase + fontOff + 0x08u)); - uint64_t iVar22 = static_cast(FAST_READ32(kFontBase + fontOff + 0x0cu)); - uint64_t iVar3 = static_cast(FAST_READ32(kFontBase + fontOff + 0x10u)); - uint64_t iVar4 = static_cast(FAST_READ32(kFontBase + fontOff + 0x14u)); - - uint64_t tex0 = iVar5 - | 0x2000000000000000ull - | (iVar22 << 14) - | 0x400000000ull - | (iVar3 << 26) - | 0x1400000ull - | (iVar4 << 30) - | (static_cast(fontClut) << 37); - - FAST_WRITE64(bufAddr + 0x70, tex0); - FAST_WRITE64(bufAddr + 0x78, 6ull); - FAST_WRITE64(bufAddr + 0x80, 0x1000000000000001ull); - FAST_WRITE64(bufAddr + 0x88, 0x0eull); - FAST_WRITE64(bufAddr + 0x90, static_cast(colour)); - FAST_WRITE64(bufAddr + 0x98, 1ull); - - iVar21 = 10; - } - - int iVar22_qw = iVar21 + 1; - uint32_t s2 = bufAddr + static_cast(iVar22_qw * 16); - uint32_t uVar20 = 0; - - size_t sLen = 0; - { - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - if (hostStr) sLen = ::strlen(hostStr); - } - - while (uVar20 < sLen) - { - uint8_t bVar1 = FAST_READ8(strAddr + uVar20); - uint32_t uVar9 = static_cast(bVar1); - int8_t chSigned = static_cast(bVar1); + int iVar22_qw = iVar21 + 1; + uint32_t s2 = bufAddr + static_cast(iVar22_qw * 16); + uint32_t uVar20 = 0; - if (uStack_d8 < 0x21u) + size_t sLen = 0; { - goto label_check_printable; + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + if (hostStr) + sLen = ::strlen(hostStr); } - if (uVar9 > 0x20u) + while (uVar20 < sLen) { - uint32_t dat176168 = FAST_READ32(kFontBase + fontOff + 0x20u); - if (dat176168 == 0u) + uint8_t bVar1 = FAST_READ8(strAddr + uVar20); + uint32_t uVar9 = static_cast(bVar1); + int8_t chSigned = static_cast(bVar1); + + if (uStack_d8 < 0x21u) { - uint32_t fontPtr0 = FAST_READ32(kFontBase + fontOff); - uint32_t tableAddr = FAST_READ32(fontPtr0 + 0x2000u); - int8_t kern = static_cast(FAST_READ8(tableAddr - 0x1c20u + uStack_d8 * 0xe0u + uVar9)); - iVar15 += static_cast(static_cast(static_cast(kern)) * sclx); + goto label_check_printable; } - goto label_check_printable; - } - goto label_space; + if (uVar9 > 0x20u) + { + uint32_t dat176168 = FAST_READ32(kFontBase + fontOff + 0x20u); + if (dat176168 == 0u) + { + uint32_t fontPtr0 = FAST_READ32(kFontBase + fontOff); + uint32_t tableAddr = FAST_READ32(fontPtr0 + 0x2000u); + int8_t kern = static_cast(FAST_READ8(tableAddr - 0x1c20u + uStack_d8 * 0xe0u + uVar9)); + iVar15 += static_cast(static_cast(static_cast(kern)) * sclx); + } + goto label_check_printable; + } -label_check_printable: - if (uVar9 < 0x21u) - { goto label_space; - } - { - int glyphIdx = static_cast(chSigned); - uint32_t iVar19_off = static_cast(glyphIdx * 0x20); - - if (param14 != 0u) + label_check_printable: + if (uVar9 < 0x21u) { - uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); - int16_t sVar7 = baseX + static_cast(iVar15); - - iVar22_qw += 2; - iStack_dc += 1; - - uint16_t wU0 = FAST_READ16(fontPtr + iVar19_off + 0); - uint16_t wV0 = FAST_READ16(fontPtr + iVar19_off + 2); - FAST_WRITE16(s2 + 0x00, wU0); - FAST_WRITE16(s2 + 0x02, wV0); - - int16_t dx0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 8)); - int16_t dy0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 10)); - uint16_t wX0 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx0)) * sclx))); - int yVal0 = static_cast(static_cast(static_cast(dy0)) * scly) >> static_cast(shiftAmt); - uint16_t wY0 = static_cast(sVar8 + static_cast(yVal0)); - FAST_WRITE16(s2 + 0x08, wX0); - FAST_WRITE16(s2 + 0x0a, wY0); - FAST_WRITE32(s2 + 0x0c, 1u); - - s2 += 0x10u; - - uint16_t wU1 = FAST_READ16(fontPtr + iVar19_off + 4); - uint16_t wV1 = FAST_READ16(fontPtr + iVar19_off + 6); - FAST_WRITE16(s2 + 0x00, wU1); - FAST_WRITE16(s2 + 0x02, wV1); - - int16_t dx1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 12)); - int16_t dy1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 14)); - uint16_t wX1 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx1)) * sclx))); - int yVal1 = static_cast(static_cast(static_cast(dy1)) * scly) >> static_cast(shiftAmt); - uint16_t wY1 = static_cast(sVar8 + static_cast(yVal1)); - FAST_WRITE16(s2 + 0x08, wX1); - FAST_WRITE16(s2 + 0x0a, wY1); - FAST_WRITE32(s2 + 0x0c, 1u); - - s2 += 0x10u; + goto label_space; } { - uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); - uint32_t advOff = static_cast((glyphIdx * 2 + 1) * 16 + 8); - int16_t advW = static_cast(FAST_READ16(fontPtr + advOff)); - iVar15 += static_cast(static_cast(static_cast(advW)) * sclx); + int glyphIdx = static_cast(chSigned); + uint32_t iVar19_off = static_cast(glyphIdx * 0x20); + + if (param14 != 0u) + { + uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); + int16_t sVar7 = baseX + static_cast(iVar15); + + iVar22_qw += 2; + iStack_dc += 1; + + uint16_t wU0 = FAST_READ16(fontPtr + iVar19_off + 0); + uint16_t wV0 = FAST_READ16(fontPtr + iVar19_off + 2); + FAST_WRITE16(s2 + 0x00, wU0); + FAST_WRITE16(s2 + 0x02, wV0); + + int16_t dx0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 8)); + int16_t dy0 = static_cast(FAST_READ16(fontPtr + iVar19_off + 10)); + uint16_t wX0 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx0)) * sclx))); + int yVal0 = static_cast(static_cast(static_cast(dy0)) * scly) >> static_cast(shiftAmt); + uint16_t wY0 = static_cast(sVar8 + static_cast(yVal0)); + FAST_WRITE16(s2 + 0x08, wX0); + FAST_WRITE16(s2 + 0x0a, wY0); + FAST_WRITE32(s2 + 0x0c, 1u); + + s2 += 0x10u; + + uint16_t wU1 = FAST_READ16(fontPtr + iVar19_off + 4); + uint16_t wV1 = FAST_READ16(fontPtr + iVar19_off + 6); + FAST_WRITE16(s2 + 0x00, wU1); + FAST_WRITE16(s2 + 0x02, wV1); + + int16_t dx1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 12)); + int16_t dy1 = static_cast(FAST_READ16(fontPtr + iVar19_off + 14)); + uint16_t wX1 = static_cast(sVar7 + static_cast(static_cast(static_cast(static_cast(dx1)) * sclx))); + int yVal1 = static_cast(static_cast(static_cast(dy1)) * scly) >> static_cast(shiftAmt); + uint16_t wY1 = static_cast(sVar8 + static_cast(yVal1)); + FAST_WRITE16(s2 + 0x08, wX1); + FAST_WRITE16(s2 + 0x0a, wY1); + FAST_WRITE32(s2 + 0x0c, 1u); + + s2 += 0x10u; + } + + { + uint32_t fontPtr = FAST_READ32(kFontBase + fontOff); + uint32_t advOff = static_cast((glyphIdx * 2 + 1) * 16 + 8); + int16_t advW = static_cast(FAST_READ16(fontPtr + advOff)); + iVar15 += static_cast(static_cast(static_cast(advW)) * sclx); + } } - } - goto label_next; + goto label_next; -label_space: + label_space: { int spaceW = static_cast(FAST_READ32(kFontBase + fontOff + 0x1cu)); iVar15 += static_cast(static_cast(spaceW) * sclx); } -label_next: - uStack_d8 = uVar9; - uVar20++; - } + label_next: + uStack_d8 = uVar9; + uVar20++; + } - if (param14 != 0u) - { - if (alignCh != 'L') + if (param14 != 0u) { - if (alignCh == 'C' || alignCh == 'R') + if (alignCh != 'L') { - int shift = paramW * 16 - iVar15; - if (alignCh == 'C') shift >>= 1; - if (iStack_dc > 0) + if (alignCh == 'C' || alignCh == 'R') { - uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; - for (int k = 0; k < iStack_dc; k++) + int shift = paramW * 16 - iVar15; + if (alignCh == 'C') + shift >>= 1; + if (iStack_dc > 0) { - int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); - int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); - FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(shift))); - FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(shift))); - adj += 0x20u; + uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; + for (int k = 0; k < iStack_dc; k++) + { + int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); + int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); + FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(shift))); + FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(shift))); + adj += 0x20u; + } } } - } - else if (alignCh == 'J' && sLen > 1) - { - int iVar19_div = static_cast(sLen) - 1; - if (iVar19_div == 0) iVar19_div = 1; - int spacePer = (paramW * 16 - iVar15) / iVar19_div; - uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; - int accum = 0; - for (uint32_t jj = 0; jj < sLen; jj++) + else if (alignCh == 'J' && sLen > 1) { - int8_t jch = static_cast(FAST_READ8(strAddr + jj)); - if (jch > 0x20) + int iVar19_div = static_cast(sLen) - 1; + if (iVar19_div == 0) + iVar19_div = 1; + int spacePer = (paramW * 16 - iVar15) / iVar19_div; + uint32_t adj = bufAddr + static_cast(iVar21 * 16) + 0x20u; + int accum = 0; + for (uint32_t jj = 0; jj < sLen; jj++) { - int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); - int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); - FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(accum))); - FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(accum))); - adj += 0x20u; + int8_t jch = static_cast(FAST_READ8(strAddr + jj)); + if (jch > 0x20) + { + int16_t oldX0 = static_cast(FAST_READ16(adj - 8u)); + int16_t oldX1 = static_cast(FAST_READ16(adj + 8u)); + FAST_WRITE16(adj - 8u, static_cast(oldX0 + static_cast(accum))); + FAST_WRITE16(adj + 8u, static_cast(oldX1 + static_cast(accum))); + adj += 0x20u; + } + accum += spacePer; } - accum += spacePer; } } - } - if (param14 != 0u) - { - uint32_t tagAddr = bufAddr + static_cast(iVar21 * 16); - FAST_WRITE64(tagAddr + 0x00, static_cast(static_cast(iStack_dc)) | 0x4400000000000000ull); - FAST_WRITE64(tagAddr + 0x08, 0x5353ull); + if (param14 != 0u) + { + uint32_t tagAddr = bufAddr + static_cast(iVar21 * 16); + FAST_WRITE64(tagAddr + 0x00, static_cast(static_cast(iStack_dc)) | 0x4400000000000000ull); + FAST_WRITE64(tagAddr + 0x08, 0x5353ull); - uint32_t endAddr = bufAddr + static_cast(iVar22_qw * 16); - FAST_WRITE64(endAddr + 0x00, 0x1000000000008001ull); - FAST_WRITE64(endAddr + 0x08, 0x0eull); + uint32_t endAddr = bufAddr + static_cast(iVar22_qw * 16); + FAST_WRITE64(endAddr + 0x00, 0x1000000000008001ull); + FAST_WRITE64(endAddr + 0x08, 0x0eull); - int iVar19_end = iVar22_qw + 1; - uint32_t endAddr2 = bufAddr + static_cast(iVar19_end * 16); - FAST_WRITE64(endAddr2 + 0x00, 0x01ff0000027f0000ull); - FAST_WRITE64(endAddr2 + 0x08, 0x40ull); + int iVar19_end = iVar22_qw + 1; + uint32_t endAddr2 = bufAddr + static_cast(iVar19_end * 16); + FAST_WRITE64(endAddr2 + 0x00, 0x01ff0000027f0000ull); + FAST_WRITE64(endAddr2 + 0x08, 0x40ull); - iVar22_qw += 2; + iVar22_qw += 2; + } } - } - int ret = 0; - if (param14 != 0u) ret = iVar22_qw; - setReturnS32(ctx, ret); - ctx->pc = getRegU32(ctx, 31); -} + int ret = 0; + if (param14 != 0u) + ret = iVar22_qw; + setReturnS32(ctx, ret); + ctx->pc = getRegU32(ctx, 31); + } -void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t oldSp = getRegU32(ctx, 29); - const uint32_t frame = oldSp - 0x900u; - - const uint32_t bufAddr = getRegU32(ctx, 4); - const uint32_t paramX = getRegU32(ctx, 5); - const uint32_t paramY = getRegU32(ctx, 6); - const uint32_t fmtAddr = getRegU32(ctx, 7); - - const uint8_t *callerVa = getConstMemPtr(rdram, oldSp + 16u); - uint8_t *frameVa = getMemPtr(rdram, frame + 0x8f8u); - if (callerVa && frameVa) - std::memcpy(frameVa, callerVa, 64u); - - SET_GPR_U32(ctx, 4, frame + 0x20u); - SET_GPR_U32(ctx, 5, fmtAddr); - SET_GPR_U32(ctx, 6, frame + 0x8f8u); - vsprintf(rdram, ctx, runtime); - - const uint32_t gp = getRegU32(ctx, 28); - uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); - uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); - uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); - uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); - uint32_t scrWidth = FAST_READ32(gp + static_cast(static_cast(-0x7b60))); - uint32_t scrHeight = FAST_READ32(gp + static_cast(static_cast(-0x7b5c))); - - std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); - std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); - - FAST_WRITE32(frame + 0x00u, frame + 0x20u); - FAST_WRITE32(frame + 0x08u, frame + 0x820u); - FAST_WRITE32(frame + 0x10u, frame + 0x824u); - FAST_WRITE32(frame + 0x18u, 1u); - - SET_GPR_U32(ctx, 29, frame); - SET_GPR_U32(ctx, 4, bufAddr); - SET_GPR_U32(ctx, 5, paramX); - SET_GPR_U32(ctx, 6, paramY); - SET_GPR_U32(ctx, 7, scrWidth); - SET_GPR_U32(ctx, 8, scrHeight); - SET_GPR_U32(ctx, 9, defaultColour); - SET_GPR_U32(ctx, 10, 0x4cu); - SET_GPR_U32(ctx, 11, defaultFontId); - - sceeFontGenerateString(rdram, ctx, runtime); - - SET_GPR_U32(ctx, 29, oldSp); - ctx->pc = getRegU32(ctx, 31); -} + void sceeFontPrintfAt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t oldSp = getRegU32(ctx, 29); + const uint32_t frame = oldSp - 0x900u; + + const uint32_t bufAddr = getRegU32(ctx, 4); + const uint32_t paramX = getRegU32(ctx, 5); + const uint32_t paramY = getRegU32(ctx, 6); + const uint32_t fmtAddr = getRegU32(ctx, 7); + + const uint8_t *callerVa = getConstMemPtr(rdram, oldSp + 16u); + uint8_t *frameVa = getMemPtr(rdram, frame + 0x8f8u); + if (callerVa && frameVa) + std::memcpy(frameVa, callerVa, 64u); + + SET_GPR_U32(ctx, 4, frame + 0x20u); + SET_GPR_U32(ctx, 5, fmtAddr); + SET_GPR_U32(ctx, 6, frame + 0x8f8u); + vsprintf(rdram, ctx, runtime); + + const uint32_t gp = getRegU32(ctx, 28); + uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); + uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); + uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); + uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); + uint32_t scrWidth = FAST_READ32(gp + static_cast(static_cast(-0x7b60))); + uint32_t scrHeight = FAST_READ32(gp + static_cast(static_cast(-0x7b5c))); + + std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); + std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); + + FAST_WRITE32(frame + 0x00u, frame + 0x20u); + FAST_WRITE32(frame + 0x08u, frame + 0x820u); + FAST_WRITE32(frame + 0x10u, frame + 0x824u); + FAST_WRITE32(frame + 0x18u, 1u); + + SET_GPR_U32(ctx, 29, frame); + SET_GPR_U32(ctx, 4, bufAddr); + SET_GPR_U32(ctx, 5, paramX); + SET_GPR_U32(ctx, 6, paramY); + SET_GPR_U32(ctx, 7, scrWidth); + SET_GPR_U32(ctx, 8, scrHeight); + SET_GPR_U32(ctx, 9, defaultColour); + SET_GPR_U32(ctx, 10, 0x4cu); + SET_GPR_U32(ctx, 11, defaultFontId); + + sceeFontGenerateString(rdram, ctx, runtime); + + SET_GPR_U32(ctx, 29, oldSp); + ctx->pc = getRegU32(ctx, 31); + } -void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t oldSp = getRegU32(ctx, 29); - const uint32_t frame = oldSp - 0x900u; - - const uint32_t bufAddr = getRegU32(ctx, 4); - const uint32_t paramX = getRegU32(ctx, 5); - const uint32_t paramY = getRegU32(ctx, 6); - const uint32_t paramW = getRegU32(ctx, 7); - const uint32_t paramH = getRegU32(ctx, 8); - const uint32_t alignRaw = getRegU32(ctx, 9); - const uint32_t fmtAddr = getRegU32(ctx, 10); - const uint64_t param8 = GPR_U64(ctx, 11); - - int8_t alignChar = static_cast(alignRaw & 0xffu); - - FAST_WRITE64(frame + 0x8f8u, param8); - - SET_GPR_U32(ctx, 4, frame + 0x20u); - SET_GPR_U32(ctx, 5, fmtAddr); - SET_GPR_U32(ctx, 6, frame + 0x8f8u); - vsprintf(rdram, ctx, runtime); - - const uint32_t gp = getRegU32(ctx, 28); - uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); - uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); - uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); - uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); - - std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); - std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); - - FAST_WRITE32(frame + 0x00u, frame + 0x20u); - FAST_WRITE32(frame + 0x08u, frame + 0x820u); - FAST_WRITE32(frame + 0x10u, frame + 0x824u); - FAST_WRITE32(frame + 0x18u, 1u); - - SET_GPR_U32(ctx, 29, frame); - SET_GPR_U32(ctx, 4, bufAddr); - SET_GPR_U32(ctx, 5, paramX); - SET_GPR_U32(ctx, 6, paramY); - SET_GPR_U32(ctx, 7, paramW); - SET_GPR_U32(ctx, 8, paramH); - SET_GPR_U32(ctx, 9, defaultColour); - SET_GPR_U32(ctx, 10, static_cast(static_cast(alignChar))); - SET_GPR_U32(ctx, 11, defaultFontId); - - sceeFontGenerateString(rdram, ctx, runtime); - - SET_GPR_U32(ctx, 29, oldSp); - ctx->pc = getRegU32(ctx, 31); -} + void sceeFontPrintfAt2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t oldSp = getRegU32(ctx, 29); + const uint32_t frame = oldSp - 0x900u; + + const uint32_t bufAddr = getRegU32(ctx, 4); + const uint32_t paramX = getRegU32(ctx, 5); + const uint32_t paramY = getRegU32(ctx, 6); + const uint32_t paramW = getRegU32(ctx, 7); + const uint32_t paramH = getRegU32(ctx, 8); + const uint32_t alignRaw = getRegU32(ctx, 9); + const uint32_t fmtAddr = getRegU32(ctx, 10); + const uint64_t param8 = GPR_U64(ctx, 11); + + int8_t alignChar = static_cast(alignRaw & 0xffu); + + FAST_WRITE64(frame + 0x8f8u, param8); + + SET_GPR_U32(ctx, 4, frame + 0x20u); + SET_GPR_U32(ctx, 5, fmtAddr); + SET_GPR_U32(ctx, 6, frame + 0x8f8u); + vsprintf(rdram, ctx, runtime); + + const uint32_t gp = getRegU32(ctx, 28); + uint32_t defaultSclxBits = FAST_READ32(gp + static_cast(static_cast(-0x7b54))); + uint32_t defaultSclyBits = FAST_READ32(gp + static_cast(static_cast(-0x7b50))); + uint32_t defaultColour = FAST_READ32(gp + static_cast(static_cast(-0x7b4c))); + uint32_t defaultFontId = FAST_READ32(gp + static_cast(static_cast(-0x7b58))); + + std::memcpy(&ctx->f[12], &defaultSclxBits, sizeof(float)); + std::memcpy(&ctx->f[13], &defaultSclyBits, sizeof(float)); + + FAST_WRITE32(frame + 0x00u, frame + 0x20u); + FAST_WRITE32(frame + 0x08u, frame + 0x820u); + FAST_WRITE32(frame + 0x10u, frame + 0x824u); + FAST_WRITE32(frame + 0x18u, 1u); + + SET_GPR_U32(ctx, 29, frame); + SET_GPR_U32(ctx, 4, bufAddr); + SET_GPR_U32(ctx, 5, paramX); + SET_GPR_U32(ctx, 6, paramY); + SET_GPR_U32(ctx, 7, paramW); + SET_GPR_U32(ctx, 8, paramH); + SET_GPR_U32(ctx, 9, defaultColour); + SET_GPR_U32(ctx, 10, static_cast(static_cast(alignChar))); + SET_GPR_U32(ctx, 11, defaultFontId); + + sceeFontGenerateString(rdram, ctx, runtime); + + SET_GPR_U32(ctx, 29, oldSp); + ctx->pc = getRegU32(ctx, 31); + } -void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static constexpr uint32_t kFontBase = 0x176148u; - static constexpr uint32_t kFontEntrySz = 0x24u; - const int fontId = static_cast(getRegU32(ctx, 4)); - const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); - uint32_t glyphPtr = 0; - if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff)) - glyphPtr = *reinterpret_cast(p); - if (glyphPtr != 0u) + void sceeFontClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (runtime) + static constexpr uint32_t kFontBase = 0x176148u; + static constexpr uint32_t kFontEntrySz = 0x24u; + const int fontId = static_cast(getRegU32(ctx, 4)); + const uint32_t fontOff = static_cast(fontId * static_cast(kFontEntrySz)); + uint32_t glyphPtr = 0; + if (const uint8_t *p = getConstMemPtr(rdram, kFontBase + fontOff)) + glyphPtr = *reinterpret_cast(p); + if (glyphPtr != 0u) + { + if (runtime) + { + uint32_t kernPtr = 0; + if (const uint8_t *kp = getConstMemPtr(rdram, glyphPtr + 0x2000u)) + kernPtr = *reinterpret_cast(kp); + if (kernPtr != 0u) + runtime->guestFree(kernPtr); + runtime->guestFree(glyphPtr); + } + if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) + *reinterpret_cast(p) = 0u; + setReturnS32(ctx, 0); + } + else { - uint32_t kernPtr = 0; - if (const uint8_t *kp = getConstMemPtr(rdram, glyphPtr + 0x2000u)) - kernPtr = *reinterpret_cast(kp); - if (kernPtr != 0u) - runtime->guestFree(kernPtr); - runtime->guestFree(glyphPtr); + setReturnS32(ctx, -1); } - if (uint8_t *p = getMemPtr(rdram, kFontBase + fontOff)) - *reinterpret_cast(p) = 0u; - setReturnS32(ctx, 0); } - else + + void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); + const uint32_t gp = getRegU32(ctx, 28); + writeU32AtGp(rdram, gp, -0x7b4c, getRegU32(ctx, 4)); } -} -void sceeFontSetColour(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - writeU32AtGp(rdram, gp, -0x7b4c, getRegU32(ctx, 4)); -} - -void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - writeU32AtGp(rdram, gp, -0x7c98, getRegU32(ctx, 4)); - setReturnS32(ctx, 0); -} + void sceeFontSetMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t gp = getRegU32(ctx, 28); + writeU32AtGp(rdram, gp, -0x7c98, getRegU32(ctx, 4)); + setReturnS32(ctx, 0); + } -void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - writeU32AtGp(rdram, gp, -0x7b58, getRegU32(ctx, 4)); -} + void sceeFontSetFont(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t gp = getRegU32(ctx, 28); + writeU32AtGp(rdram, gp, -0x7b58, getRegU32(ctx, 4)); + } -void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 28); - uint32_t sclx_bits, scly_bits; - std::memcpy(&sclx_bits, &ctx->f[12], sizeof(float)); - std::memcpy(&scly_bits, &ctx->f[13], sizeof(float)); - writeU32AtGp(rdram, gp, -0x7b54, sclx_bits); - writeU32AtGp(rdram, gp, -0x7b50, scly_bits); -} + void sceeFontSetScale(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t gp = getRegU32(ctx, 28); + uint32_t sclx_bits, scly_bits; + std::memcpy(&sclx_bits, &ctx->f[12], sizeof(float)); + std::memcpy(&scly_bits, &ctx->f[13], sizeof(float)); + writeU32AtGp(rdram, gp, -0x7b54, sclx_bits); + writeU32AtGp(rdram, gp, -0x7b50, scly_bits); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp index 47100835..948c76e3 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp @@ -6,1152 +6,1151 @@ namespace ps2_stubs { -namespace -{ - std::mutex g_gs_sync_v_mutex; - uint64_t g_gs_sync_v_base_tick = 0u; - std::mutex g_gs_sync_v_callback_mutex; - uint32_t g_gs_sync_v_callback_func = 0u; - uint32_t g_gs_sync_v_callback_gp = 0u; - uint32_t g_gs_sync_v_callback_sp = 0u; - uint32_t g_gs_sync_v_callback_stack_base = 0u; - uint32_t g_gs_sync_v_callback_stack_top = 0u; - uint32_t g_gs_sync_v_callback_bad_pc_logs = 0u; - struct GsDebugProbePoint - { - uint32_t x; - uint32_t y; - }; - - constexpr GsDebugProbePoint kGhostProbePoints[] = { - {220u, 176u}, - {260u, 208u}, - {320u, 208u}, - {260u, 240u}, - {320u, 240u}, - {260u, 272u}, - {320u, 272u}, - }; - - uint64_t makeClearPrim(bool useContext2) + namespace { - return static_cast(GS_PRIM_SPRITE) | - (static_cast(useContext2 ? 1u : 0u) << 9); - } - - uint64_t makeClearRgbaq(uint32_t rgba) - { - return static_cast(rgba); - } - - uint64_t makeClearXyz(int32_t x, int32_t y) - { - return static_cast(static_cast(x << 4)) | - (static_cast(static_cast(y << 4)) << 16); - } - - void seedGsClearPacket(GsClearMem &clear, - int32_t width, - int32_t height, - uint32_t rgba, - uint32_t ztest, - bool useContext2) - { - const int32_t offX = 0x800 - (width >> 1); - const int32_t offY = 0x800 - (height >> 1); - const uint64_t clearTest = makeTest(0u); - const uint64_t restoreTest = makeTest(ztest); - const uint64_t prim = makeClearPrim(useContext2); - const uint64_t rgbaq = makeClearRgbaq(rgba); - const uint64_t xyz0 = makeClearXyz(offX, offY); - const uint64_t xyz1 = makeClearXyz(offX + width, offY + height); - const uint64_t testReg = useContext2 ? GS_REG_TEST_2 : GS_REG_TEST_1; - - clear.testa = {clearTest, testReg}; - clear.prim = {prim, GS_REG_PRIM}; - clear.rgbaq = {rgbaq, GS_REG_RGBAQ}; - clear.xyz2a = {xyz0, GS_REG_XYZ2}; - clear.xyz2b = {xyz1, GS_REG_XYZ2}; - clear.testb = {restoreTest, testReg}; - } - - bool hasSeededGsClearPacket(const GsClearMem &clear) - { - return clear.rgbaq.reg == GS_REG_RGBAQ && - clear.xyz2a.reg == GS_REG_XYZ2 && - clear.xyz2b.reg == GS_REG_XYZ2; - } - - struct GsTrailingArgs2 - { - uint32_t arg0 = 0u; - uint32_t arg1 = 0u; - }; - - struct GsTrailingArgs3 - { - uint32_t arg0 = 0u; - uint32_t arg1 = 0u; - uint32_t arg2 = 0u; - }; - - GsTrailingArgs2 decodeGsTrailingArgs2(uint8_t *rdram, R5900Context *ctx) - { - const uint32_t reg8 = getRegU32(ctx, 8); - const uint32_t reg9 = getRegU32(ctx, 9); - const uint32_t stack0 = readStackU32(rdram, ctx, 16); - const uint32_t stack1 = readStackU32(rdram, ctx, 20); - - const bool hasRegArgs = (reg8 != 0u || reg9 != 0u); - const bool hasStackArgs = (stack0 != 0u || stack1 != 0u); - if (hasRegArgs || !hasStackArgs) + std::mutex g_gs_sync_v_mutex; + uint64_t g_gs_sync_v_base_tick = 0u; + std::mutex g_gs_sync_v_callback_mutex; + uint32_t g_gs_sync_v_callback_func = 0u; + uint32_t g_gs_sync_v_callback_gp = 0u; + uint32_t g_gs_sync_v_callback_sp = 0u; + uint32_t g_gs_sync_v_callback_stack_base = 0u; + uint32_t g_gs_sync_v_callback_stack_top = 0u; + uint32_t g_gs_sync_v_callback_bad_pc_logs = 0u; + struct GsDebugProbePoint + { + uint32_t x; + uint32_t y; + }; + + constexpr GsDebugProbePoint kGhostProbePoints[] = { + {220u, 176u}, + {260u, 208u}, + {320u, 208u}, + {260u, 240u}, + {320u, 240u}, + {260u, 272u}, + {320u, 272u}, + }; + + uint64_t makeClearPrim(bool useContext2) { - return {reg8, reg9}; + return static_cast(GS_PRIM_SPRITE) | + (static_cast(useContext2 ? 1u : 0u) << 9); } - return {stack0, stack1}; - } - - GsTrailingArgs3 decodeGsTrailingArgs3(uint8_t *rdram, R5900Context *ctx) - { - const uint32_t reg8 = getRegU32(ctx, 8); - const uint32_t reg9 = getRegU32(ctx, 9); - const uint32_t reg10 = getRegU32(ctx, 10); - const uint32_t stack0 = readStackU32(rdram, ctx, 16); - const uint32_t stack1 = readStackU32(rdram, ctx, 20); - const uint32_t stack2 = readStackU32(rdram, ctx, 24); - - const bool hasRegArgs = (reg8 != 0u || reg9 != 0u || reg10 != 0u); - const bool hasStackArgs = (stack0 != 0u || stack1 != 0u || stack2 != 0u); - if (hasRegArgs || !hasStackArgs) + uint64_t makeClearRgbaq(uint32_t rgba) { - return {reg8, reg9, reg10}; + return static_cast(rgba); } - return {stack0, stack1, stack2}; - } - - bool sampleFramebufferPixel(uint8_t *vram, - uint32_t vramSize, - uint32_t fbp, - uint32_t fbw, - uint32_t psm, - uint32_t x, - uint32_t y, - uint32_t &outPixel) - { - if (!vram || fbw == 0u) + uint64_t makeClearXyz(int32_t x, int32_t y) { - return false; + return static_cast(static_cast(x << 4)) | + (static_cast(static_cast(y << 4)) << 16); } - const uint32_t bytesPerPixel = - (psm == GS_PSM_CT16 || psm == GS_PSM_CT16S) ? 2u : - (psm == GS_PSM_CT32 || psm == GS_PSM_CT24) ? 4u : - 0u; - if (bytesPerPixel == 0u) + void seedGsClearPacket(GsClearMem &clear, + int32_t width, + int32_t height, + uint32_t rgba, + uint32_t ztest, + bool useContext2) { - return false; + const int32_t offX = 0x800 - (width >> 1); + const int32_t offY = 0x800 - (height >> 1); + const uint64_t clearTest = makeTest(0u); + const uint64_t restoreTest = makeTest(ztest); + const uint64_t prim = makeClearPrim(useContext2); + const uint64_t rgbaq = makeClearRgbaq(rgba); + const uint64_t xyz0 = makeClearXyz(offX, offY); + const uint64_t xyz1 = makeClearXyz(offX + width, offY + height); + const uint64_t testReg = useContext2 ? GS_REG_TEST_2 : GS_REG_TEST_1; + + clear.testa = {clearTest, testReg}; + clear.prim = {prim, GS_REG_PRIM}; + clear.rgbaq = {rgbaq, GS_REG_RGBAQ}; + clear.xyz2a = {xyz0, GS_REG_XYZ2}; + clear.xyz2b = {xyz1, GS_REG_XYZ2}; + clear.testb = {restoreTest, testReg}; } - const uint32_t widthBlocks = (fbw != 0u) ? fbw : 1u; - const uint32_t strideBytes = fbw * 64u * bytesPerPixel; - const uint32_t baseBytes = fbp * 8192u; - uint32_t offset = baseBytes + (y * strideBytes) + (x * bytesPerPixel); - if (psm == GS_PSM_CT16) + bool hasSeededGsClearPacket(const GsClearMem &clear) { - offset = GSPSMCT16::addrPSMCT16(GSInternal::framePageBaseToBlock(fbp), widthBlocks, x, y); + return clear.rgbaq.reg == GS_REG_RGBAQ && + clear.xyz2a.reg == GS_REG_XYZ2 && + clear.xyz2b.reg == GS_REG_XYZ2; } - else if (psm == GS_PSM_CT16S) + + struct GsTrailingArgs2 { - offset = GSPSMCT16::addrPSMCT16S(GSInternal::framePageBaseToBlock(fbp), widthBlocks, x, y); - } - if (offset + bytesPerPixel > vramSize) + uint32_t arg0 = 0u; + uint32_t arg1 = 0u; + }; + + struct GsTrailingArgs3 { - return false; - } + uint32_t arg0 = 0u; + uint32_t arg1 = 0u; + uint32_t arg2 = 0u; + }; - if (bytesPerPixel == 4u) + GsTrailingArgs2 decodeGsTrailingArgs2(uint8_t *rdram, R5900Context *ctx) { - std::memcpy(&outPixel, vram + offset, sizeof(outPixel)); - if (psm == GS_PSM_CT24) + const uint32_t reg8 = getRegU32(ctx, 8); + const uint32_t reg9 = getRegU32(ctx, 9); + const uint32_t stack0 = readStackU32(rdram, ctx, 16); + const uint32_t stack1 = readStackU32(rdram, ctx, 20); + + const bool hasRegArgs = (reg8 != 0u || reg9 != 0u); + const bool hasStackArgs = (stack0 != 0u || stack1 != 0u); + if (hasRegArgs || !hasStackArgs) { - outPixel |= 0xFF000000u; + return {reg8, reg9}; } - return true; - } - uint16_t packed = 0u; - std::memcpy(&packed, vram + offset, sizeof(packed)); - const uint32_t r = ((packed >> 0) & 0x1Fu) << 3; - const uint32_t g = ((packed >> 5) & 0x1Fu) << 3; - const uint32_t b = ((packed >> 10) & 0x1Fu) << 3; - const uint32_t a = (packed & 0x8000u) ? 0x80u : 0x00u; - outPixel = r | (g << 8) | (b << 16) | (a << 24); - return true; - } - - bool sampleFrameRegPixel(PS2Runtime *runtime, - uint64_t frameReg, - uint32_t x, - uint32_t y, - uint32_t &outPixel) - { - if (!runtime) - { - return false; + return {stack0, stack1}; } - const uint32_t fbp = static_cast(frameReg & 0x1FFu); - const uint32_t fbw = static_cast((frameReg >> 16) & 0x3Fu); - const uint32_t psm = static_cast((frameReg >> 24) & 0x3Fu); - return sampleFramebufferPixel(runtime->memory().getGSVRAM(), PS2_GS_VRAM_SIZE, fbp, fbw, psm, x, y, outPixel); - } - - bool sampleDispFbPixel(PS2Runtime *runtime, - uint64_t dispfb, - uint32_t x, - uint32_t y, - uint32_t &outPixel) - { - if (!runtime) + GsTrailingArgs3 decodeGsTrailingArgs3(uint8_t *rdram, R5900Context *ctx) { - return false; - } + const uint32_t reg8 = getRegU32(ctx, 8); + const uint32_t reg9 = getRegU32(ctx, 9); + const uint32_t reg10 = getRegU32(ctx, 10); + const uint32_t stack0 = readStackU32(rdram, ctx, 16); + const uint32_t stack1 = readStackU32(rdram, ctx, 20); + const uint32_t stack2 = readStackU32(rdram, ctx, 24); + + const bool hasRegArgs = (reg8 != 0u || reg9 != 0u || reg10 != 0u); + const bool hasStackArgs = (stack0 != 0u || stack1 != 0u || stack2 != 0u); + if (hasRegArgs || !hasStackArgs) + { + return {reg8, reg9, reg10}; + } - const uint32_t fbp = static_cast(dispfb & 0x1FFu); - const uint32_t fbw = static_cast((dispfb >> 9) & 0x3Fu); - const uint32_t psm = static_cast((dispfb >> 15) & 0x1Fu); - return sampleFramebufferPixel(runtime->memory().getGSVRAM(), PS2_GS_VRAM_SIZE, fbp, fbw, psm, x, y, outPixel); - } + return {stack0, stack1, stack2}; + } - void logSwapProbeStage(PS2Runtime *runtime, - const char *stage, - uint32_t which, - uint64_t drawFrameReg, - uint64_t dispfb, - bool hasClearPacket) - { - static uint32_t s_swapProbeCount = 0u; - if (!runtime || s_swapProbeCount >= 24u) + bool sampleFramebufferPixel(uint8_t *vram, + uint32_t vramSize, + uint32_t fbp, + uint32_t fbw, + uint32_t psm, + uint32_t x, + uint32_t y, + uint32_t &outPixel) { - return; - } + if (!vram || fbw == 0u) + { + return false; + } - PS2_IF_AGRESSIVE_LOGS({ - RUNTIME_LOG("[gs:probe] stage=" << stage - << " which=" << which - << " clear=" << static_cast(hasClearPacket ? 1u : 0u)); + const uint32_t bytesPerPixel = + (psm == GS_PSM_CT16 || psm == GS_PSM_CT16S) ? 2u : (psm == GS_PSM_CT32 || psm == GS_PSM_CT24) ? 4u + : 0u; + if (bytesPerPixel == 0u) + { + return false; + } - for (const auto& probe : kGhostProbePoints) + const uint32_t widthBlocks = (fbw != 0u) ? fbw : 1u; + const uint32_t strideBytes = fbw * 64u * bytesPerPixel; + const uint32_t baseBytes = fbp * 8192u; + uint32_t offset = baseBytes + (y * strideBytes) + (x * bytesPerPixel); + if (psm == GS_PSM_CT16) { - uint32_t page0Pixel = 0u; - uint32_t page150Pixel = 0u; - const bool havePage0 = sampleFrameRegPixel(runtime, drawFrameReg, probe.x, probe.y, page0Pixel); - const bool havePage150 = sampleDispFbPixel(runtime, dispfb, probe.x, probe.y, page150Pixel); - if (havePage0) - { - RUNTIME_LOG(" p0[" << probe.x << "," << probe.y << "]=0x" - << std::hex << page0Pixel << std::dec); - } - if (havePage150) + offset = GSPSMCT16::addrPSMCT16(GSInternal::framePageBaseToBlock(fbp), widthBlocks, x, y); + } + else if (psm == GS_PSM_CT16S) + { + offset = GSPSMCT16::addrPSMCT16S(GSInternal::framePageBaseToBlock(fbp), widthBlocks, x, y); + } + if (offset + bytesPerPixel > vramSize) + { + return false; + } + + if (bytesPerPixel == 4u) + { + std::memcpy(&outPixel, vram + offset, sizeof(outPixel)); + if (psm == GS_PSM_CT24) { - RUNTIME_LOG(" p150[" << probe.x << "," << probe.y << "]=0x" - << std::hex << page150Pixel << std::dec); + outPixel |= 0xFF000000u; } + return true; } - RUNTIME_LOG(std::endl); - ++s_swapProbeCount; - }); - } - - void applyGsClearPacket(PS2Runtime *runtime, const GsClearMem &clear) - { - if (!runtime || !runtime->ensureCoreSubsystemsInitialized() || !hasSeededGsClearPacket(clear)) + + uint16_t packed = 0u; + std::memcpy(&packed, vram + offset, sizeof(packed)); + const uint32_t r = ((packed >> 0) & 0x1Fu) << 3; + const uint32_t g = ((packed >> 5) & 0x1Fu) << 3; + const uint32_t b = ((packed >> 10) & 0x1Fu) << 3; + const uint32_t a = (packed & 0x8000u) ? 0x80u : 0x00u; + outPixel = r | (g << 8) | (b << 16) | (a << 24); + return true; + } + + bool sampleFrameRegPixel(PS2Runtime *runtime, + uint64_t frameReg, + uint32_t x, + uint32_t y, + uint32_t &outPixel) { - return; + if (!runtime) + { + return false; + } + + const uint32_t fbp = static_cast(frameReg & 0x1FFu); + const uint32_t fbw = static_cast((frameReg >> 16) & 0x3Fu); + const uint32_t psm = static_cast((frameReg >> 24) & 0x3Fu); + return sampleFramebufferPixel(runtime->memory().getGSVRAM(), PS2_GS_VRAM_SIZE, fbp, fbw, psm, x, y, outPixel); } - runtime->gs().writeRegister(static_cast(clear.testa.reg & 0xFFu), clear.testa.value); - runtime->gs().writeRegister(static_cast(clear.prim.reg & 0xFFu), clear.prim.value); - runtime->gs().writeRegister(static_cast(clear.rgbaq.reg & 0xFFu), clear.rgbaq.value); - runtime->gs().writeRegister(static_cast(clear.xyz2a.reg & 0xFFu), clear.xyz2a.value); - runtime->gs().writeRegister(static_cast(clear.xyz2b.reg & 0xFFu), clear.xyz2b.value); - runtime->gs().writeRegister(static_cast(clear.testb.reg & 0xFFu), clear.testb.value); - } -} + bool sampleDispFbPixel(PS2Runtime *runtime, + uint64_t dispfb, + uint32_t x, + uint32_t y, + uint32_t &outPixel) + { + if (!runtime) + { + return false; + } -static void resetGsSyncVState() -{ - std::lock_guard lock(g_gs_sync_v_mutex); - g_gs_sync_v_base_tick = ps2_syscalls::GetCurrentVSyncTick(); -} + const uint32_t fbp = static_cast(dispfb & 0x1FFu); + const uint32_t fbw = static_cast((dispfb >> 9) & 0x3Fu); + const uint32_t psm = static_cast((dispfb >> 15) & 0x1Fu); + return sampleFramebufferPixel(runtime->memory().getGSVRAM(), PS2_GS_VRAM_SIZE, fbp, fbw, psm, x, y, outPixel); + } -static int32_t getGsSyncVFieldForTick(uint64_t tick) -{ - std::lock_guard lock(g_gs_sync_v_mutex); - if (tick <= g_gs_sync_v_base_tick) - { - return 0; - } + void logSwapProbeStage(PS2Runtime *runtime, + const char *stage, + uint32_t which, + uint64_t drawFrameReg, + uint64_t dispfb, + bool hasClearPacket) + { + static uint32_t s_swapProbeCount = 0u; + if (!runtime || s_swapProbeCount >= 24u) + { + return; + } - return static_cast((tick - g_gs_sync_v_base_tick - 1u) & 1u); -} + PS2_IF_AGRESSIVE_LOGS({ + RUNTIME_LOG("[gs:probe] stage=" << stage + << " which=" << which + << " clear=" << static_cast(hasClearPacket ? 1u : 0u)); -void resetGsSyncVCallbackState() -{ - { - std::lock_guard lock(g_gs_sync_v_callback_mutex); - g_gs_sync_v_callback_func = 0u; - g_gs_sync_v_callback_gp = 0u; - g_gs_sync_v_callback_sp = 0u; - g_gs_sync_v_callback_stack_base = 0u; - g_gs_sync_v_callback_stack_top = 0u; - g_gs_sync_v_callback_bad_pc_logs = 0u; + for (const auto &probe : kGhostProbePoints) + { + uint32_t page0Pixel = 0u; + uint32_t page150Pixel = 0u; + const bool havePage0 = sampleFrameRegPixel(runtime, drawFrameReg, probe.x, probe.y, page0Pixel); + const bool havePage150 = sampleDispFbPixel(runtime, dispfb, probe.x, probe.y, page150Pixel); + if (havePage0) + { + RUNTIME_LOG(" p0[" << probe.x << "," << probe.y << "]=0x" + << std::hex << page0Pixel << std::dec); + } + if (havePage150) + { + RUNTIME_LOG(" p150[" << probe.x << "," << probe.y << "]=0x" + << std::hex << page150Pixel << std::dec); + } + } + RUNTIME_LOG(std::endl); + ++s_swapProbeCount; + }); + } + + void applyGsClearPacket(PS2Runtime *runtime, const GsClearMem &clear) + { + if (!runtime || !runtime->syncCoreSubsystems() || !hasSeededGsClearPacket(clear)) + { + return; + } + + runtime->gs().writeRegister(static_cast(clear.testa.reg & 0xFFu), clear.testa.value); + runtime->gs().writeRegister(static_cast(clear.prim.reg & 0xFFu), clear.prim.value); + runtime->gs().writeRegister(static_cast(clear.rgbaq.reg & 0xFFu), clear.rgbaq.value); + runtime->gs().writeRegister(static_cast(clear.xyz2a.reg & 0xFFu), clear.xyz2a.value); + runtime->gs().writeRegister(static_cast(clear.xyz2b.reg & 0xFFu), clear.xyz2b.value); + runtime->gs().writeRegister(static_cast(clear.testb.reg & 0xFFu), clear.testb.value); + } } - resetGsSyncVState(); -} -void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick) -{ - if (!rdram || !runtime) + static void resetGsSyncVState() { - return; + std::lock_guard lock(g_gs_sync_v_mutex); + g_gs_sync_v_base_tick = ps2_syscalls::GetCurrentVSyncTick(); } - uint32_t callback = 0u; - uint32_t gp = 0u; - uint32_t callbackStackTop = 0u; - const uint64_t callbackTick = (tick != 0u) ? tick : ps2_syscalls::GetCurrentVSyncTick(); + static int32_t getGsSyncVFieldForTick(uint64_t tick) { - std::lock_guard lock(g_gs_sync_v_callback_mutex); - callback = g_gs_sync_v_callback_func; - gp = g_gs_sync_v_callback_gp; - callbackStackTop = g_gs_sync_v_callback_stack_top; - if (callback == 0u) + std::lock_guard lock(g_gs_sync_v_mutex); + if (tick <= g_gs_sync_v_base_tick) { - return; + return 0; } + + return static_cast((tick - g_gs_sync_v_base_tick - 1u) & 1u); } - if (!runtime->hasFunction(callback)) + void resetGsSyncVCallbackState() { - static uint32_t s_missingCallbackLogCount = 0u; - if (s_missingCallbackLogCount < 32u) { - std::cerr << "[sceGsSyncVCallback:missing] cb=0x" << std::hex << callback - << " gp=0x" << gp - << " tick=0x" << callbackTick - << std::dec << std::endl; - ++s_missingCallbackLogCount; + std::lock_guard lock(g_gs_sync_v_callback_mutex); + g_gs_sync_v_callback_func = 0u; + g_gs_sync_v_callback_gp = 0u; + g_gs_sync_v_callback_sp = 0u; + g_gs_sync_v_callback_stack_base = 0u; + g_gs_sync_v_callback_stack_top = 0u; + g_gs_sync_v_callback_bad_pc_logs = 0u; } - return; + resetGsSyncVState(); } - if (callbackStackTop == 0u) + void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick) { - constexpr uint32_t kCallbackStackSize = 0x4000u; - const uint32_t stackTop = runtime->reserveAsyncCallbackStack(kCallbackStackSize, 16u); - if (stackTop != 0u) + if (!rdram || !runtime) + { + return; + } + + uint32_t callback = 0u; + uint32_t gp = 0u; + uint32_t callbackStackTop = 0u; + const uint64_t callbackTick = (tick != 0u) ? tick : ps2_syscalls::GetCurrentVSyncTick(); { std::lock_guard lock(g_gs_sync_v_callback_mutex); - if (g_gs_sync_v_callback_stack_top == 0u) + callback = g_gs_sync_v_callback_func; + gp = g_gs_sync_v_callback_gp; + callbackStackTop = g_gs_sync_v_callback_stack_top; + if (callback == 0u) { - g_gs_sync_v_callback_stack_base = stackTop - (kCallbackStackSize - 0x10u); - g_gs_sync_v_callback_stack_top = stackTop; + return; } - callbackStackTop = g_gs_sync_v_callback_stack_top; } - } - try - { - R5900Context callbackCtx{}; - SET_GPR_U32(&callbackCtx, 28, gp); - SET_GPR_U32(&callbackCtx, 29, (callbackStackTop != 0u) ? callbackStackTop : (PS2_RAM_SIZE - 0x10u)); - SET_GPR_U32(&callbackCtx, 31, 0u); - SET_GPR_U32(&callbackCtx, 4, static_cast(callbackTick)); - callbackCtx.pc = callback; - - static uint32_t s_dispatchLogCount = 0u; - const bool shouldLogDispatch = (s_dispatchLogCount < 64u); - if (shouldLogDispatch) + if (!runtime->hasFunction(callback)) { - RUNTIME_LOG("[sceGsSyncVCallback:dispatch] cb=0x" << std::hex << callback - << " gp=0x" << gp - << " sp=0x" << getRegU32(&callbackCtx, 29) - << " tick=0x" << callbackTick - << std::dec << std::endl); + static uint32_t s_missingCallbackLogCount = 0u; + if (s_missingCallbackLogCount < 32u) + { + std::cerr << "[sceGsSyncVCallback:missing] cb=0x" << std::hex << callback + << " gp=0x" << gp + << " tick=0x" << callbackTick + << std::dec << std::endl; + ++s_missingCallbackLogCount; + } + return; } - uint32_t steps = 0u; - while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < 1024u) + if (callbackStackTop == 0u) { - if (!runtime->hasFunction(callbackCtx.pc)) + constexpr uint32_t kCallbackStackSize = 0x4000u; + const uint32_t stackTop = runtime->reserveAsyncCallbackStack(kCallbackStackSize, 16u); + if (stackTop != 0u) { - if (g_gs_sync_v_callback_bad_pc_logs < 16u) + std::lock_guard lock(g_gs_sync_v_callback_mutex); + if (g_gs_sync_v_callback_stack_top == 0u) { - std::cerr << "[sceGsSyncVCallback:bad-pc] pc=0x" << std::hex << callbackCtx.pc - << " ra=0x" << getRegU32(&callbackCtx, 31) - << " sp=0x" << getRegU32(&callbackCtx, 29) - << " gp=0x" << getRegU32(&callbackCtx, 28) - << std::dec << std::endl; - ++g_gs_sync_v_callback_bad_pc_logs; + g_gs_sync_v_callback_stack_base = stackTop - (kCallbackStackSize - 0x10u); + g_gs_sync_v_callback_stack_top = stackTop; } - callbackCtx.pc = 0u; - break; + callbackStackTop = g_gs_sync_v_callback_stack_top; } + } - auto step = runtime->lookupFunction(callbackCtx.pc); - if (!step) + try + { + R5900Context callbackCtx{}; + SET_GPR_U32(&callbackCtx, 28, gp); + SET_GPR_U32(&callbackCtx, 29, (callbackStackTop != 0u) ? callbackStackTop : (PS2_RAM_SIZE - 0x10u)); + SET_GPR_U32(&callbackCtx, 31, 0u); + SET_GPR_U32(&callbackCtx, 4, static_cast(callbackTick)); + callbackCtx.pc = callback; + + static uint32_t s_dispatchLogCount = 0u; + const bool shouldLogDispatch = (s_dispatchLogCount < 64u); + if (shouldLogDispatch) { - break; + RUNTIME_LOG("[sceGsSyncVCallback:dispatch] cb=0x" << std::hex << callback + << " gp=0x" << gp + << " sp=0x" << getRegU32(&callbackCtx, 29) + << " tick=0x" << callbackTick + << std::dec << std::endl); } - ++steps; - step(rdram, &callbackCtx, runtime); - } - if (shouldLogDispatch) + uint32_t steps = 0u; + while (callbackCtx.pc != 0u && !runtime->isStopRequested() && steps < 1024u) + { + if (!runtime->hasFunction(callbackCtx.pc)) + { + if (g_gs_sync_v_callback_bad_pc_logs < 16u) + { + std::cerr << "[sceGsSyncVCallback:bad-pc] pc=0x" << std::hex << callbackCtx.pc + << " ra=0x" << getRegU32(&callbackCtx, 31) + << " sp=0x" << getRegU32(&callbackCtx, 29) + << " gp=0x" << getRegU32(&callbackCtx, 28) + << std::dec << std::endl; + ++g_gs_sync_v_callback_bad_pc_logs; + } + callbackCtx.pc = 0u; + break; + } + + auto step = runtime->lookupFunction(callbackCtx.pc); + if (!step) + { + break; + } + ++steps; + step(rdram, &callbackCtx, runtime); + } + + if (shouldLogDispatch) + { + RUNTIME_LOG("[sceGsSyncVCallback:return] cb=0x" << std::hex << callback + << " finalPc=0x" << callbackCtx.pc + << " ra=0x" << getRegU32(&callbackCtx, 31) + << " steps=0x" << steps + << std::dec << std::endl); + ++s_dispatchLogCount; + } + } + catch (const std::exception &e) { - RUNTIME_LOG("[sceGsSyncVCallback:return] cb=0x" << std::hex << callback - << " finalPc=0x" << callbackCtx.pc - << " ra=0x" << getRegU32(&callbackCtx, 31) - << " steps=0x" << steps - << std::dec << std::endl); - ++s_dispatchLogCount; + static uint32_t warnCount = 0u; + if (warnCount < 8u) + { + std::cerr << "[sceGsSyncVCallback] callback exception: " << e.what() << std::endl; + ++warnCount; + } } } - catch (const std::exception &e) + + void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - static uint32_t warnCount = 0u; - if (warnCount < 8u) + uint32_t imgAddr = getRegU32(ctx, 4); + uint32_t srcAddr = getRegU32(ctx, 5); + + GsImageMem img{}; + if (!runtime || !runtime->syncCoreSubsystems() || !readGsImage(rdram, imgAddr, img)) { - std::cerr << "[sceGsSyncVCallback] callback exception: " << e.what() << std::endl; - ++warnCount; + setReturnS32(ctx, -1); + return; } - } -} -void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t imgAddr = getRegU32(ctx, 4); - uint32_t srcAddr = getRegU32(ctx, 5); + const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); + if (rowBytes == 0) + { + setReturnS32(ctx, -1); + return; + } - GsImageMem img{}; - if (!runtime || !runtime->ensureCoreSubsystemsInitialized() || !readGsImage(rdram, imgAddr, img)) - { - setReturnS32(ctx, -1); - return; - } + uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); + const uint32_t totalImageBytes = rowBytes * static_cast(img.height); + const uint32_t headerQwc = 6u; + const uint32_t imageQwc = (totalImageBytes + 15u) / 16u; + const uint32_t totalQwc = headerQwc + imageQwc; - const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); - if (rowBytes == 0) - { - setReturnS32(ctx, -1); - return; - } + uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u); + if (pktAddr == 0) + { + setReturnS32(ctx, -1); + return; + } - uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); - const uint32_t totalImageBytes = rowBytes * static_cast(img.height); - const uint32_t headerQwc = 6u; - const uint32_t imageQwc = (totalImageBytes + 15u) / 16u; - const uint32_t totalQwc = headerQwc + imageQwc; + uint8_t *pkt = getMemPtr(rdram, pktAddr); + const uint8_t *src = getConstMemPtr(rdram, srcAddr); + if (!pkt || !src) + { + runtime->guestFree(pktAddr); + setReturnS32(ctx, -1); + return; + } - uint32_t pktAddr = runtime->guestMalloc(totalQwc * 16u, 16u); - if (pktAddr == 0) - { - setReturnS32(ctx, -1); - return; + uint32_t dbp = (static_cast(img.vram_addr) * 2048u) / 256u; + uint32_t dsax = static_cast(img.x); + uint32_t dsay = static_cast(img.y); + + // Full messy + uint64_t *q = reinterpret_cast(pkt); + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; + q[2] = (static_cast(img.psm & 0x3Fu) << 24) | (static_cast(1u) << 16) | + (static_cast(dbp & 0x3FFFu) << 32) | (static_cast(fbw & 0x3Fu) << 48) | + (static_cast(img.psm & 0x3Fu) << 56); + q[3] = 0x50ULL; + q[4] = (static_cast(dsay & 0x7FFu) << 48) | (static_cast(dsax & 0x7FFu) << 32); + q[5] = 0x51ULL; + q[6] = (static_cast(img.height) << 32) | static_cast(img.width); + q[7] = 0x52ULL; + q[8] = 0ULL; + q[9] = 0x53ULL; + q[10] = (static_cast(2) << 58) | (static_cast(imageQwc) & 0x7FFF) | + (1ULL << 15); + q[11] = 0ULL; + + std::memcpy(pkt + headerQwc * 16u, src, totalImageBytes); + + constexpr uint32_t GIF_CHANNEL = 0x1000A000; + constexpr uint32_t CHCR_STR_MODE0 = 0x101u; + auto &mem = runtime->memory(); + mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); + mem.writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); + mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + mem.processPendingTransfers(); + runtime->guestFree(pktAddr); + + setReturnS32(ctx, 0); } - uint8_t *pkt = getMemPtr(rdram, pktAddr); - const uint8_t *src = getConstMemPtr(rdram, srcAddr); - if (!pkt || !src) + void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - runtime->guestFree(pktAddr); - setReturnS32(ctx, -1); - return; - } + uint32_t imgAddr = getRegU32(ctx, 4); + uint32_t dstAddr = getRegU32(ctx, 5); - uint32_t dbp = (static_cast(img.vram_addr) * 2048u) / 256u; - uint32_t dsax = static_cast(img.x); - uint32_t dsay = static_cast(img.y); - - // Full messy - uint64_t *q = reinterpret_cast(pkt); - q[0] = makeGiftagAplusD(4u); - q[1] = 0xEULL; - q[2] = (static_cast(img.psm & 0x3Fu) << 24) | (static_cast(1u) << 16) | - (static_cast(dbp & 0x3FFFu) << 32) | (static_cast(fbw & 0x3Fu) << 48) | - (static_cast(img.psm & 0x3Fu) << 56); - q[3] = 0x50ULL; - q[4] = (static_cast(dsay & 0x7FFu) << 48) | (static_cast(dsax & 0x7FFu) << 32); - q[5] = 0x51ULL; - q[6] = (static_cast(img.height) << 32) | static_cast(img.width); - q[7] = 0x52ULL; - q[8] = 0ULL; - q[9] = 0x53ULL; - q[10] = (static_cast(2) << 58) | (static_cast(imageQwc) & 0x7FFF) | - (1ULL << 15); - q[11] = 0ULL; - - std::memcpy(pkt + headerQwc * 16u, src, totalImageBytes); - - constexpr uint32_t GIF_CHANNEL = 0x1000A000; - constexpr uint32_t CHCR_STR_MODE0 = 0x101u; - auto &mem = runtime->memory(); - mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - mem.writeIORegister(GIF_CHANNEL + 0x20u, totalQwc & 0xFFFFu); - mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - mem.processPendingTransfers(); - runtime->guestFree(pktAddr); - - setReturnS32(ctx, 0); -} + GsImageMem img{}; + if (!runtime || !runtime->syncCoreSubsystems() || !readGsImage(rdram, imgAddr, img)) + { + setReturnS32(ctx, -1); + return; + } -void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t imgAddr = getRegU32(ctx, 4); - uint32_t dstAddr = getRegU32(ctx, 5); + const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); + if (rowBytes == 0) + { + setReturnS32(ctx, -1); + return; + } - GsImageMem img{}; - if (!runtime || !runtime->ensureCoreSubsystemsInitialized() || !readGsImage(rdram, imgAddr, img)) - { - setReturnS32(ctx, -1); - return; - } + uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); + const uint32_t totalImageBytes = rowBytes * static_cast(img.height); - const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); - if (rowBytes == 0) - { - setReturnS32(ctx, -1); - return; - } + uint8_t *dst = getMemPtr(rdram, dstAddr); + if (!dst) + { + setReturnS32(ctx, -1); + return; + } - uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); - const uint32_t totalImageBytes = rowBytes * static_cast(img.height); + uint32_t sbp = (static_cast(img.vram_addr) * 2048u) / 256u; + uint64_t bitbltbuf = (static_cast(sbp & 0x3FFFu) << 0) | + (static_cast(fbw & 0x3Fu) << 16) | + (static_cast(img.psm & 0x3Fu) << 24) | + (static_cast(0u) << 32) | + (static_cast(1u) << 48) | + (static_cast(0u) << 56); + uint64_t trxpos = (static_cast(img.x & 0x7FFu) << 0) | + (static_cast(img.y & 0x7FFu) << 16) | + (static_cast(0u) << 32) | + (static_cast(0u) << 48); + uint64_t trxreg = static_cast(img.height) << 32 | static_cast(img.width); + + uint32_t pktAddr = runtime->guestMalloc(80u, 16u); + if (pktAddr == 0) + { + setReturnS32(ctx, -1); + return; + } - uint8_t *dst = getMemPtr(rdram, dstAddr); - if (!dst) - { - setReturnS32(ctx, -1); - return; - } + uint8_t *pkt = getMemPtr(rdram, pktAddr); + if (!pkt) + { + runtime->guestFree(pktAddr); + setReturnS32(ctx, -1); + return; + } - uint32_t sbp = (static_cast(img.vram_addr) * 2048u) / 256u; - uint64_t bitbltbuf = (static_cast(sbp & 0x3FFFu) << 0) | - (static_cast(fbw & 0x3Fu) << 16) | - (static_cast(img.psm & 0x3Fu) << 24) | - (static_cast(0u) << 32) | - (static_cast(1u) << 48) | - (static_cast(0u) << 56); - uint64_t trxpos = (static_cast(img.x & 0x7FFu) << 0) | - (static_cast(img.y & 0x7FFu) << 16) | - (static_cast(0u) << 32) | - (static_cast(0u) << 48); - uint64_t trxreg = static_cast(img.height) << 32 | static_cast(img.width); - - uint32_t pktAddr = runtime->guestMalloc(80u, 16u); - if (pktAddr == 0) - { - setReturnS32(ctx, -1); - return; - } + uint64_t *q = reinterpret_cast(pkt); + q[0] = makeGiftagAplusD(4u); + q[1] = 0xEULL; + q[2] = bitbltbuf; + q[3] = 0x50ULL; + q[4] = trxpos; + q[5] = 0x51ULL; + q[6] = trxreg; + q[7] = 0x52ULL; + q[8] = 1ULL; + q[9] = 0x53ULL; + + constexpr uint32_t GIF_CHANNEL = 0x1000A000; + constexpr uint32_t CHCR_STR_MODE0 = 0x101u; + auto &mem = runtime->memory(); + mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); + mem.writeIORegister(GIF_CHANNEL + 0x20u, 5u); + mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + mem.processPendingTransfers(); - uint8_t *pkt = getMemPtr(rdram, pktAddr); - if (!pkt) - { + runtime->gs().consumeLocalToHostBytes(dst, totalImageBytes); runtime->guestFree(pktAddr); - setReturnS32(ctx, -1); - return; - } - uint64_t *q = reinterpret_cast(pkt); - q[0] = makeGiftagAplusD(4u); - q[1] = 0xEULL; - q[2] = bitbltbuf; - q[3] = 0x50ULL; - q[4] = trxpos; - q[5] = 0x51ULL; - q[6] = trxreg; - q[7] = 0x52ULL; - q[8] = 1ULL; - q[9] = 0x53ULL; - - constexpr uint32_t GIF_CHANNEL = 0x1000A000; - constexpr uint32_t CHCR_STR_MODE0 = 0x101u; - auto &mem = runtime->memory(); - mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - mem.writeIORegister(GIF_CHANNEL + 0x20u, 5u); - mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - mem.processPendingTransfers(); - - runtime->gs().consumeLocalToHostBytes(dst, totalImageBytes); - runtime->guestFree(pktAddr); - - setReturnS32(ctx, 0); -} - -void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t addr = writeGsGParamToScratch(runtime); - setReturnU32(ctx, addr); -} + setReturnS32(ctx, 0); + } -void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t envAddr = getRegU32(ctx, 4); - GsDispEnvMem env{}; - if (!readGsDispEnv(rdram, envAddr, env)) + void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + uint32_t addr = writeGsGParamToScratch(runtime); + setReturnU32(ctx, addr); } - applyGsDispEnv(runtime, env); - setReturnS32(ctx, 0); -} -void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t envAddr = getRegU32(ctx, 4); - GsRegPairMem pairs[8]{}; - if (!readGsRegPairs(rdram, envAddr, pairs, 8u)) + void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + uint32_t envAddr = getRegU32(ctx, 4); + GsDispEnvMem env{}; + if (!readGsDispEnv(rdram, envAddr, env)) + { + setReturnS32(ctx, -1); + return; + } + applyGsDispEnv(runtime, env); + setReturnS32(ctx, 0); } - applyGsRegPairs(runtime, pairs, 8u); - setReturnS32(ctx, 0); -} - -void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t mode = getRegU32(ctx, 4); - uint32_t interlace = getRegU32(ctx, 5); - uint32_t omode = getRegU32(ctx, 6); - uint32_t ffmode = getRegU32(ctx, 7); - if (mode == 0) + void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (runtime && !runtime->ensureCoreSubsystemsInitialized()) + uint32_t envAddr = getRegU32(ctx, 4); + GsRegPairMem pairs[8]{}; + if (!readGsRegPairs(rdram, envAddr, pairs, 8u)) { setReturnS32(ctx, -1); return; } + applyGsRegPairs(runtime, pairs, 8u); + setReturnS32(ctx, 0); + } - g_gparam.interlace = static_cast(interlace & 0x1); - g_gparam.omode = static_cast(omode & 0xFF); - g_gparam.ffmode = static_cast(ffmode & 0x1); - writeGsGParamToScratch(runtime); - resetGsSyncVState(); - - uint64_t pmode = makePmode(1, 0, 0, 0, 0, 0x80); - uint64_t smode2 = (interlace & 0x1) | ((ffmode & 0x1) << 1); - uint64_t dispfb = makeDispFb(0, 10, 0, 0, 0); - uint64_t display = makeDisplay(0, 0, 0, 0, 639, 447); - uint64_t bgcolor = 0ULL; + void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t mode = getRegU32(ctx, 4); + uint32_t interlace = getRegU32(ctx, 5); + uint32_t omode = getRegU32(ctx, 6); + uint32_t ffmode = getRegU32(ctx, 7); - if (runtime) + if (mode == 0) { - uint32_t pktAddr = runtime->guestMalloc(128u, 16u); - if (pktAddr != 0u) + if (runtime && !runtime->syncCoreSubsystems()) { - uint8_t *pkt = getMemPtr(rdram, pktAddr); - if (pkt) - { - uint64_t *q = reinterpret_cast(pkt); - q[0] = makeGiftagAplusD(7u); - q[1] = 0xEULL; - q[2] = pmode; - q[3] = 0x41ULL; - q[4] = smode2; - q[5] = 0x42ULL; - q[6] = dispfb; - q[7] = 0x59ULL; - q[8] = display; - q[9] = 0x5aULL; - q[10] = dispfb; - q[11] = 0x5bULL; - q[12] = display; - q[13] = 0x5cULL; - q[14] = bgcolor; - q[15] = 0x5fULL; - constexpr uint32_t GIF_CHANNEL = 0x1000A000; - constexpr uint32_t CHCR_STR_MODE0 = 0x101u; - auto &mem = runtime->memory(); - mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); - mem.writeIORegister(GIF_CHANNEL + 0x20u, 8u); - mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); - mem.processPendingTransfers(); - runtime->guestFree(pktAddr); - } - else + setReturnS32(ctx, -1); + return; + } + + g_gparam.interlace = static_cast(interlace & 0x1); + g_gparam.omode = static_cast(omode & 0xFF); + g_gparam.ffmode = static_cast(ffmode & 0x1); + writeGsGParamToScratch(runtime); + resetGsSyncVState(); + + uint64_t pmode = makePmode(1, 0, 0, 0, 0, 0x80); + uint64_t smode2 = (interlace & 0x1) | ((ffmode & 0x1) << 1); + uint64_t dispfb = makeDispFb(0, 10, 0, 0, 0); + uint64_t display = makeDisplay(0, 0, 0, 0, 639, 447); + uint64_t bgcolor = 0ULL; + + if (runtime) + { + uint32_t pktAddr = runtime->guestMalloc(128u, 16u); + if (pktAddr != 0u) { - runtime->guestFree(pktAddr); + uint8_t *pkt = getMemPtr(rdram, pktAddr); + if (pkt) + { + uint64_t *q = reinterpret_cast(pkt); + q[0] = makeGiftagAplusD(7u); + q[1] = 0xEULL; + q[2] = pmode; + q[3] = 0x41ULL; + q[4] = smode2; + q[5] = 0x42ULL; + q[6] = dispfb; + q[7] = 0x59ULL; + q[8] = display; + q[9] = 0x5aULL; + q[10] = dispfb; + q[11] = 0x5bULL; + q[12] = display; + q[13] = 0x5cULL; + q[14] = bgcolor; + q[15] = 0x5fULL; + constexpr uint32_t GIF_CHANNEL = 0x1000A000; + constexpr uint32_t CHCR_STR_MODE0 = 0x101u; + auto &mem = runtime->memory(); + mem.writeIORegister(GIF_CHANNEL + 0x10u, pktAddr); + mem.writeIORegister(GIF_CHANNEL + 0x20u, 8u); + mem.writeIORegister(GIF_CHANNEL + 0x00u, CHCR_STR_MODE0); + mem.processPendingTransfers(); + runtime->guestFree(pktAddr); + } + else + { + runtime->guestFree(pktAddr); + } } } } - } - - setReturnS32(ctx, 0); -} - -void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} -void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)ctx; - (void)runtime; - setReturnS32(ctx, 0); -} + setReturnS32(ctx, 0); + } -void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t envAddr = getRegU32(ctx, 4); - uint32_t psm = getRegU32(ctx, 5); - uint32_t w = getRegU32(ctx, 6); - uint32_t h = getRegU32(ctx, 7); - const GsTrailingArgs3 trailing = decodeGsTrailingArgs3(rdram, ctx); - const uint32_t ztest = trailing.arg0; - const uint32_t zpsm = trailing.arg1; - const uint32_t clear = trailing.arg2; - - if (w == 0u) + void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - w = 640u; + setReturnS32(ctx, 0); } - if (h == 0u) + + void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - h = 448u; + (void)rdram; + (void)ctx; + (void)runtime; + setReturnS32(ctx, 0); } - const uint32_t fbw = std::max(1u, (w + 63u) / 64u); - const uint64_t pmode = makePmode(1u, 1u, 0u, 0u, 0u, 0x80u); - const uint64_t smode2 = - (static_cast(g_gparam.interlace & 0x1u) << 0) | - (static_cast(g_gparam.ffmode & 0x1u) << 1); - const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u); + void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t envAddr = getRegU32(ctx, 4); + uint32_t psm = getRegU32(ctx, 5); + uint32_t w = getRegU32(ctx, 6); + uint32_t h = getRegU32(ctx, 7); + const GsTrailingArgs3 trailing = decodeGsTrailingArgs3(rdram, ctx); + const uint32_t ztest = trailing.arg0; + const uint32_t zpsm = trailing.arg1; + const uint32_t clear = trailing.arg2; + + if (w == 0u) + { + w = 640u; + } + if (h == 0u) + { + h = 448u; + } - const int32_t drawWidth = static_cast(w); - const int32_t drawHeight = static_cast(h); + const uint32_t fbw = std::max(1u, (w + 63u) / 64u); + const uint64_t pmode = makePmode(1u, 1u, 0u, 0u, 0u, 0x80u); + const uint64_t smode2 = + (static_cast(g_gparam.interlace & 0x1u) << 0) | + (static_cast(g_gparam.ffmode & 0x1u) << 1); + const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u); - uint32_t zbufAddr = 0u; - { - R5900Context temp = *ctx; - sceGszbufaddr(rdram, &temp, runtime); - zbufAddr = getRegU32(&temp, 2); - } + const int32_t drawWidth = static_cast(w); + const int32_t drawHeight = static_cast(h); - const uint32_t fbp1 = zbufAddr; - const uint64_t dispfb0 = makeDispFb(fbp1, fbw, psm, 0u, 0u); - const uint64_t dispfb1 = makeDispFb(0u, fbw, psm, 0u, 0u); - - - GsDBuffDcMem db{}; - db.disp[0].pmode = pmode; - db.disp[0].smode2 = smode2; - db.disp[0].dispfb = dispfb0; - db.disp[0].display = display; - db.disp[0].bgcolor = 0u; - db.disp[1] = db.disp[0]; - db.disp[1].dispfb = dispfb1; - - const bool seedClear = clear != 0u; - db.giftag0 = {makeGiftagAplusD(seedClear ? 22u : 16u), 0xEULL}; - seedGsDrawEnv1(db.draw01, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); - seedGsDrawEnv2(db.draw02, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); - db.giftag1 = db.giftag0; - seedGsDrawEnv1(db.draw11, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false); - seedGsDrawEnv2(db.draw12, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false); - if (seedClear) - { - seedGsClearPacket(db.clear0, drawWidth, drawHeight, 0u, ztest, false); - seedGsClearPacket(db.clear1, drawWidth, drawHeight, 0u, ztest, true); + uint32_t zbufAddr = 0u; + { + R5900Context temp = *ctx; + sceGszbufaddr(rdram, &temp, runtime); + zbufAddr = getRegU32(&temp, 2); + } + + const uint32_t fbp1 = zbufAddr; + const uint64_t dispfb0 = makeDispFb(fbp1, fbw, psm, 0u, 0u); + const uint64_t dispfb1 = makeDispFb(0u, fbw, psm, 0u, 0u); + + GsDBuffDcMem db{}; + db.disp[0].pmode = pmode; + db.disp[0].smode2 = smode2; + db.disp[0].dispfb = dispfb0; + db.disp[0].display = display; + db.disp[0].bgcolor = 0u; + db.disp[1] = db.disp[0]; + db.disp[1].dispfb = dispfb1; + + const bool seedClear = clear != 0u; + db.giftag0 = {makeGiftagAplusD(seedClear ? 22u : 16u), 0xEULL}; + seedGsDrawEnv1(db.draw01, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); + seedGsDrawEnv2(db.draw02, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); + db.giftag1 = db.giftag0; + seedGsDrawEnv1(db.draw11, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false); + seedGsDrawEnv2(db.draw12, drawWidth, drawHeight, fbp1, fbw, psm, zbufAddr, zpsm, ztest, false); + if (seedClear) + { + seedGsClearPacket(db.clear0, drawWidth, drawHeight, 0u, ztest, false); + seedGsClearPacket(db.clear1, drawWidth, drawHeight, 0u, ztest, true); + } + + if (!writeGsDBuffDc(rdram, envAddr, db)) + { + setReturnS32(ctx, -1); + return; + } + setReturnS32(ctx, 0); } - if (!writeGsDBuffDc(rdram, envAddr, db)) + void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + uint32_t envAddr = getRegU32(ctx, 4); + uint32_t psm = getRegU32(ctx, 5); + uint32_t w = getRegU32(ctx, 6); + uint32_t h = getRegU32(ctx, 7); + const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); + uint32_t dx = trailing.arg0; + uint32_t dy = trailing.arg1; + + if (w == 0) + w = 640; + if (h == 0) + h = 448; + + uint32_t fbw = (w + 63) / 64; + uint64_t dispfb = makeDispFb(0, fbw, psm, 0, 0); + uint64_t display = makeDisplay(dx, dy, 0, 0, w - 1, h - 1); + + writeGsDispEnv(rdram, envAddr, display, dispfb); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - -void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t envAddr = getRegU32(ctx, 4); - uint32_t psm = getRegU32(ctx, 5); - uint32_t w = getRegU32(ctx, 6); - uint32_t h = getRegU32(ctx, 7); - const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); - uint32_t dx = trailing.arg0; - uint32_t dy = trailing.arg1; - - if (w == 0) - w = 640; - if (h == 0) - h = 448; - - uint32_t fbw = (w + 63) / 64; - uint64_t dispfb = makeDispFb(0, fbw, psm, 0, 0); - uint64_t display = makeDisplay(dx, dy, 0, 0, w - 1, h - 1); - - writeGsDispEnv(rdram, envAddr, display, dispfb); - setReturnS32(ctx, 0); -} -void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t envAddr = getRegU32(ctx, 4); - uint32_t param_2 = getRegU32(ctx, 5); - int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); - int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); - const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); - uint32_t param_5 = trailing.arg0; - uint32_t param_6 = trailing.arg1; - - if (w <= 0) - w = 640; - if (h <= 0) - h = 448; - - uint32_t psm = param_2 & 0xFU; - uint32_t fbw = ((static_cast(w) + 63u) >> 6) & 0x3FU; - sceGszbufaddr(rdram, ctx, runtime); - int32_t zbuf = static_cast(static_cast(getRegU32(ctx, 2) & 0xFFFF)); - - GsDrawEnv1Mem env{}; - seedGsDrawEnv1(env, - w, - h, - 0u, - fbw, - psm, - static_cast(zbuf), - param_6 & 0xFu, - param_5 & 0x3u, - (param_2 & 2u) != 0u); - - uint8_t *const ptr = getMemPtr(rdram, envAddr); - if (!ptr) + void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + uint32_t envAddr = getRegU32(ctx, 4); + uint32_t param_2 = getRegU32(ctx, 5); + int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); + int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); + const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); + uint32_t param_5 = trailing.arg0; + uint32_t param_6 = trailing.arg1; + + if (w <= 0) + w = 640; + if (h <= 0) + h = 448; + + uint32_t psm = param_2 & 0xFU; + uint32_t fbw = ((static_cast(w) + 63u) >> 6) & 0x3FU; + sceGszbufaddr(rdram, ctx, runtime); + int32_t zbuf = static_cast(static_cast(getRegU32(ctx, 2) & 0xFFFF)); + + GsDrawEnv1Mem env{}; + seedGsDrawEnv1(env, + w, + h, + 0u, + fbw, + psm, + static_cast(zbuf), + param_6 & 0xFu, + param_5 & 0x3u, + (param_2 & 2u) != 0u); + + uint8_t *const ptr = getMemPtr(rdram, envAddr); + if (!ptr) + { + setReturnS32(ctx, 8); + return; + } + std::memcpy(ptr, &env, sizeof(env)); + setReturnS32(ctx, 8); - return; } - std::memcpy(ptr, &env, sizeof(env)); - - setReturnS32(ctx, 8); -} -void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t envAddr = getRegU32(ctx, 4); - uint32_t param_2 = getRegU32(ctx, 5); - int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); - int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); - const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); - uint32_t param_5 = trailing.arg0; - uint32_t param_6 = trailing.arg1; - - if (w <= 0) - w = 640; - if (h <= 0) - h = 448; - - uint32_t psm = param_2 & 0xFU; - uint32_t fbw = ((static_cast(w) + 63u) >> 6) & 0x3FU; - sceGszbufaddr(rdram, ctx, runtime); - int32_t zbuf = static_cast(static_cast(getRegU32(ctx, 2) & 0xFFFF)); - - GsDrawEnv2Mem env{}; - seedGsDrawEnv2(env, - w, - h, - 0u, - fbw, - psm, - static_cast(zbuf), - param_6 & 0xFu, - param_5 & 0x3u, - (param_2 & 2u) != 0u); - - uint8_t *const ptr = getMemPtr(rdram, envAddr); - if (!ptr) + void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + uint32_t envAddr = getRegU32(ctx, 4); + uint32_t param_2 = getRegU32(ctx, 5); + int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); + int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); + const GsTrailingArgs2 trailing = decodeGsTrailingArgs2(rdram, ctx); + uint32_t param_5 = trailing.arg0; + uint32_t param_6 = trailing.arg1; + + if (w <= 0) + w = 640; + if (h <= 0) + h = 448; + + uint32_t psm = param_2 & 0xFU; + uint32_t fbw = ((static_cast(w) + 63u) >> 6) & 0x3FU; + sceGszbufaddr(rdram, ctx, runtime); + int32_t zbuf = static_cast(static_cast(getRegU32(ctx, 2) & 0xFFFF)); + + GsDrawEnv2Mem env{}; + seedGsDrawEnv2(env, + w, + h, + 0u, + fbw, + psm, + static_cast(zbuf), + param_6 & 0xFu, + param_5 & 0x3u, + (param_2 & 2u) != 0u); + + uint8_t *const ptr = getMemPtr(rdram, envAddr); + if (!ptr) + { + setReturnS32(ctx, 8); + return; + } + + std::memcpy(ptr, &env, sizeof(env)); setReturnS32(ctx, 8); - return; } - std::memcpy(ptr, &env, sizeof(env)); - setReturnS32(ctx, 8); -} - -void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t imgAddr = getRegU32(ctx, 4); - const GsSetDefImageArgs args = decodeGsSetDefImageArgs(rdram, ctx); - - GsImageMem img{}; - img.x = static_cast(args.x); - img.y = static_cast(args.y); - img.width = static_cast(args.width); - img.height = static_cast(args.height); - img.vram_addr = static_cast(args.vramAddr); - img.vram_width = static_cast(args.vramWidth); - img.psm = static_cast(args.psm); - - writeGsImage(rdram, imgAddr, img); - setReturnS32(ctx, 0); -} - -void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - sceGsSetDefLoadImage(rdram, ctx, runtime); -} - -void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t envAddr = getRegU32(ctx, 4); - const uint32_t which = getRegU32(ctx, 5) & 1u; - - GsDBuffDcMem db{}; - if (!runtime || !readGsDBuffDc(rdram, envAddr, db)) + void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + uint32_t imgAddr = getRegU32(ctx, 4); + const GsSetDefImageArgs args = decodeGsSetDefImageArgs(rdram, ctx); + + GsImageMem img{}; + img.x = static_cast(args.x); + img.y = static_cast(args.y); + img.width = static_cast(args.width); + img.height = static_cast(args.height); + img.vram_addr = static_cast(args.vramAddr); + img.vram_width = static_cast(args.vramWidth); + img.psm = static_cast(args.psm); + + writeGsImage(rdram, imgAddr, img); + setReturnS32(ctx, 0); } - const bool hasClearPacket = (which == 0u) ? hasSeededGsClearPacket(db.clear0) - : hasSeededGsClearPacket(db.clear1); - const uint64_t debugDrawFrameReg = (which == 0u) ? db.draw01.frame1.value - : db.draw11.frame1.value; - - applyGsDispEnv(runtime, db.disp[which]); - static uint32_t s_swapDbuffLogCount = 0u; - if (s_swapDbuffLogCount < 32u) + void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const uint32_t dispFbp = static_cast(db.disp[which].dispfb & 0x1FFu); - const uint32_t clearContext = (which == 0u) - ? static_cast((db.clear0.prim.value >> 9) & 0x1u) - : static_cast((db.clear1.prim.value >> 9) & 0x1u); - RUNTIME_LOG("[gs:swapdbuff] which=" << which - << " env=0x" << std::hex << envAddr - << " dispfb=0x" << db.disp[which].dispfb - << " display=0x" << db.disp[which].display - << " pmode=0x" << db.disp[which].pmode - << " dispFbp=" << dispFbp - << " clearCtxt=" << clearContext - << std::dec << std::endl); - ++s_swapDbuffLogCount; + sceGsSetDefLoadImage(rdram, ctx, runtime); } - logSwapProbeStage(runtime, "swap-pre", which, debugDrawFrameReg, db.disp[which].dispfb, hasClearPacket); - if (which == 0u) + + void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - applyGsRegPairs(runtime, reinterpret_cast(&db.draw01), 8u); - applyGsRegPairs(runtime, reinterpret_cast(&db.draw02), 8u); - if (hasSeededGsClearPacket(db.clear0)) + const uint32_t envAddr = getRegU32(ctx, 4); + const uint32_t which = getRegU32(ctx, 5) & 1u; + + GsDBuffDcMem db{}; + if (!runtime || !readGsDBuffDc(rdram, envAddr, db)) { - const uint32_t clearContext = static_cast((db.clear0.prim.value >> 9) & 0x1u); - runtime->gs().clearFramebufferContext(clearContext, static_cast(db.clear0.rgbaq.value)); - logSwapProbeStage(runtime, "swap-post-clear", which, db.draw01.frame1.value, db.disp[which].dispfb, true); + setReturnS32(ctx, -1); + return; } - applyGsClearPacket(runtime, db.clear0); - logSwapProbeStage(runtime, "swap-post", which, db.draw01.frame1.value, db.disp[which].dispfb, hasClearPacket); - } - else - { - applyGsRegPairs(runtime, reinterpret_cast(&db.draw11), 8u); - applyGsRegPairs(runtime, reinterpret_cast(&db.draw12), 8u); - if (hasSeededGsClearPacket(db.clear1)) + + const bool hasClearPacket = (which == 0u) ? hasSeededGsClearPacket(db.clear0) + : hasSeededGsClearPacket(db.clear1); + const uint64_t debugDrawFrameReg = (which == 0u) ? db.draw01.frame1.value + : db.draw11.frame1.value; + + applyGsDispEnv(runtime, db.disp[which]); + static uint32_t s_swapDbuffLogCount = 0u; + if (s_swapDbuffLogCount < 32u) { - const uint32_t clearContext = static_cast((db.clear1.prim.value >> 9) & 0x1u); - runtime->gs().clearFramebufferContext(clearContext, static_cast(db.clear1.rgbaq.value)); - logSwapProbeStage(runtime, "swap-post-clear", which, db.draw11.frame1.value, db.disp[which].dispfb, true); + const uint32_t dispFbp = static_cast(db.disp[which].dispfb & 0x1FFu); + const uint32_t clearContext = (which == 0u) + ? static_cast((db.clear0.prim.value >> 9) & 0x1u) + : static_cast((db.clear1.prim.value >> 9) & 0x1u); + RUNTIME_LOG("[gs:swapdbuff] which=" << which + << " env=0x" << std::hex << envAddr + << " dispfb=0x" << db.disp[which].dispfb + << " display=0x" << db.disp[which].display + << " pmode=0x" << db.disp[which].pmode + << " dispFbp=" << dispFbp + << " clearCtxt=" << clearContext + << std::dec << std::endl); + ++s_swapDbuffLogCount; + } + logSwapProbeStage(runtime, "swap-pre", which, debugDrawFrameReg, db.disp[which].dispfb, hasClearPacket); + if (which == 0u) + { + applyGsRegPairs(runtime, reinterpret_cast(&db.draw01), 8u); + applyGsRegPairs(runtime, reinterpret_cast(&db.draw02), 8u); + if (hasSeededGsClearPacket(db.clear0)) + { + const uint32_t clearContext = static_cast((db.clear0.prim.value >> 9) & 0x1u); + runtime->gs().clearFramebufferContext(clearContext, static_cast(db.clear0.rgbaq.value)); + logSwapProbeStage(runtime, "swap-post-clear", which, db.draw01.frame1.value, db.disp[which].dispfb, true); + } + applyGsClearPacket(runtime, db.clear0); + logSwapProbeStage(runtime, "swap-post", which, db.draw01.frame1.value, db.disp[which].dispfb, hasClearPacket); + } + else + { + applyGsRegPairs(runtime, reinterpret_cast(&db.draw11), 8u); + applyGsRegPairs(runtime, reinterpret_cast(&db.draw12), 8u); + if (hasSeededGsClearPacket(db.clear1)) + { + const uint32_t clearContext = static_cast((db.clear1.prim.value >> 9) & 0x1u); + runtime->gs().clearFramebufferContext(clearContext, static_cast(db.clear1.rgbaq.value)); + logSwapProbeStage(runtime, "swap-post-clear", which, db.draw11.frame1.value, db.disp[which].dispfb, true); + } + applyGsClearPacket(runtime, db.clear1); + logSwapProbeStage(runtime, "swap-post", which, db.draw11.frame1.value, db.disp[which].dispfb, hasClearPacket); } - applyGsClearPacket(runtime, db.clear1); - logSwapProbeStage(runtime, "swap-post", which, db.draw11.frame1.value, db.disp[which].dispfb, hasClearPacket); - } - - setReturnS32(ctx, static_cast(which ^ 1u)); -} -void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int32_t mode = static_cast(getRegU32(ctx, 4)); - auto &mem = runtime->memory(); + setReturnS32(ctx, static_cast(which ^ 1u)); + } - if (mode == 0) + void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - mem.processPendingTransfers(); - - uint32_t count = 0; - constexpr uint32_t kTimeout = 0x1000000; + int32_t mode = static_cast(getRegU32(ctx, 4)); + auto &mem = runtime->memory(); - while ((mem.readIORegister(0x10009000) & 0x100) != 0) + if (mode == 0) { - if (++count > kTimeout) + mem.processPendingTransfers(); + + uint32_t count = 0; + constexpr uint32_t kTimeout = 0x1000000; + + while ((mem.readIORegister(0x10009000) & 0x100) != 0) { - setReturnS32(ctx, -1); - return; + if (++count > kTimeout) + { + setReturnS32(ctx, -1); + return; + } } - } - while ((mem.readIORegister(0x1000A000) & 0x100) != 0) - { - if (++count > kTimeout) + while ((mem.readIORegister(0x1000A000) & 0x100) != 0) { - setReturnS32(ctx, -1); - return; + if (++count > kTimeout) + { + setReturnS32(ctx, -1); + return; + } } - } - while ((mem.readIORegister(0x10003C00) & 0x1F000003) != 0) - { - if (++count > kTimeout) + while ((mem.readIORegister(0x10003C00) & 0x1F000003) != 0) { - setReturnS32(ctx, -1); - return; + if (++count > kTimeout) + { + setReturnS32(ctx, -1); + return; + } } - } - while ((mem.readIORegister(0x10003020) & 0xC00) != 0) - { - if (++count > kTimeout) + while ((mem.readIORegister(0x10003020) & 0xC00) != 0) { - setReturnS32(ctx, -1); - return; + if (++count > kTimeout) + { + setReturnS32(ctx, -1); + return; + } } - } - setReturnS32(ctx, 0); + setReturnS32(ctx, 0); + } + else + { + uint32_t result = 0; + + if ((mem.readIORegister(0x10009000) & 0x100) != 0) + result |= 1; + if ((mem.readIORegister(0x1000A000) & 0x100) != 0) + result |= 2; + if ((mem.readIORegister(0x10003C00) & 0x1F000003) != 0) + result |= 4; + if ((mem.readIORegister(0x10003020) & 0xC00) != 0) + result |= 0x10; + + setReturnS32(ctx, result); + } } - else + + void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint32_t result = 0; - - if ((mem.readIORegister(0x10009000) & 0x100) != 0) - result |= 1; - if ((mem.readIORegister(0x1000A000) & 0x100) != 0) - result |= 2; - if ((mem.readIORegister(0x10003C00) & 0x1F000003) != 0) - result |= 4; - if ((mem.readIORegister(0x10003020) & 0xC00) != 0) - result |= 0x10; - - setReturnS32(ctx, result); + const uint64_t tick = ps2_syscalls::WaitForNextVSyncTick(rdram, runtime); + if (g_gparam.interlace != 0u) + { + setReturnS32(ctx, getGsSyncVFieldForTick(tick)); + return; + } + + setReturnS32(ctx, 1); } -} -void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint64_t tick = ps2_syscalls::WaitForNextVSyncTick(rdram, runtime); - if (g_gparam.interlace != 0u) + void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, getGsSyncVFieldForTick(tick)); - return; - } + const uint32_t newCallback = getRegU32(ctx, 4); + const uint32_t callerPc = ctx ? ctx->pc : 0u; + const uint32_t callerRa = ctx ? getRegU32(ctx, 31) : 0u; + const uint32_t gp = getRegU32(ctx, 28); + const uint32_t sp = getRegU32(ctx, 29); - setReturnS32(ctx, 1); -} + uint32_t oldCallback = 0u; + { + std::lock_guard lock(g_gs_sync_v_callback_mutex); + oldCallback = g_gs_sync_v_callback_func; + g_gs_sync_v_callback_func = newCallback; + if (newCallback != 0u) + { + g_gs_sync_v_callback_gp = gp; + g_gs_sync_v_callback_sp = sp; + } + } -void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t newCallback = getRegU32(ctx, 4); - const uint32_t callerPc = ctx ? ctx->pc : 0u; - const uint32_t callerRa = ctx ? getRegU32(ctx, 31) : 0u; - const uint32_t gp = getRegU32(ctx, 28); - const uint32_t sp = getRegU32(ctx, 29); + static uint32_t s_syncVCallbackLogCount = 0u; + if (s_syncVCallbackLogCount < 128u) + { + RUNTIME_LOG("[sceGsSyncVCallback:set] new=0x" << std::hex << newCallback + << " old=0x" << oldCallback + << " callerPc=0x" << callerPc + << " callerRa=0x" << callerRa + << " gp=0x" << gp + << " sp=0x" << sp + << std::dec << std::endl); + ++s_syncVCallbackLogCount; + } - uint32_t oldCallback = 0u; - { - std::lock_guard lock(g_gs_sync_v_callback_mutex); - oldCallback = g_gs_sync_v_callback_func; - g_gs_sync_v_callback_func = newCallback; if (newCallback != 0u) { - g_gs_sync_v_callback_gp = gp; - g_gs_sync_v_callback_sp = sp; + ps2_syscalls::EnsureVSyncWorkerRunning(rdram, runtime); } - } - static uint32_t s_syncVCallbackLogCount = 0u; - if (s_syncVCallbackLogCount < 128u) - { - RUNTIME_LOG("[sceGsSyncVCallback:set] new=0x" << std::hex << newCallback - << " old=0x" << oldCallback - << " callerPc=0x" << callerPc - << " callerRa=0x" << callerRa - << " gp=0x" << gp - << " sp=0x" << sp - << std::dec << std::endl); - ++s_syncVCallbackLogCount; + setReturnU32(ctx, oldCallback); } - if (newCallback != 0u) + void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ps2_syscalls::EnsureVSyncWorkerRunning(rdram, runtime); - } - - setReturnU32(ctx, oldCallback); -} + (void)rdram; + uint32_t param_1 = getRegU32(ctx, 4); + int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); + int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); -void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - uint32_t param_1 = getRegU32(ctx, 4); - int32_t w = static_cast(static_cast(getRegU32(ctx, 6) & 0xFFFF)); - int32_t h = static_cast(static_cast(getRegU32(ctx, 7) & 0xFFFF)); - - int32_t width_blocks = (w + 63) >> 6; - if (w + 63 < 0) - width_blocks = (w + 126) >> 6; + int32_t width_blocks = (w + 63) >> 6; + if (w + 63 < 0) + width_blocks = (w + 126) >> 6; - int32_t height_blocks; - if ((param_1 & 2) != 0) - { - int32_t v = (h + 63) >> 6; - if (h + 63 < 0) - v = (h + 126) >> 6; - height_blocks = v; - } - else - { - int32_t v = (h + 31) >> 5; - if (h + 31 < 0) - v = (h + 62) >> 5; - height_blocks = v; - } + int32_t height_blocks; + if ((param_1 & 2) != 0) + { + int32_t v = (h + 63) >> 6; + if (h + 63 < 0) + v = (h + 126) >> 6; + height_blocks = v; + } + else + { + int32_t v = (h + 31) >> 5; + if (h + 31 < 0) + v = (h + 62) >> 5; + height_blocks = v; + } - int32_t product = width_blocks * height_blocks; + int32_t product = width_blocks * height_blocks; - uint64_t gparam_val = 0; - if (runtime) - { - uint8_t *scratch = runtime->memory().getScratchpad(); - if (scratch) + uint64_t gparam_val = 0; + if (runtime) { - std::memcpy(&gparam_val, scratch + 0x100, sizeof(gparam_val)); + uint8_t *scratch = runtime->memory().getScratchpad(); + if (scratch) + { + std::memcpy(&gparam_val, scratch + 0x100, sizeof(gparam_val)); + } } + if ((gparam_val & 0xFFFF0000FFFFULL) == 1ULL) + product = (product * 0x10000) >> 16; + else + product = (product * 0x20000) >> 16; + + setReturnS32(ctx, product); } - if ((gparam_val & 0xFFFF0000FFFFULL) == 1ULL) - product = (product * 0x10000) >> 16; - else - product = (product * 0x20000) >> 16; - setReturnS32(ctx, product); -} -void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) + void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("ps2_stub Ps2SwapDBuff"); - ++logCount; + static int logCount = 0; + if (logCount < 8) + { + RUNTIME_LOG("ps2_stub Ps2SwapDBuff"); + ++logCount; + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h index 831c1f83..9de08082 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h @@ -141,7 +141,7 @@ namespace const std::string normalized = normalizeCdPathNoPrefix(relative.generic_string()); if (normalized.empty()) { - return {}; + throw std::runtime_error("cdLoosePathKeyFromRelative: normalized path is empty"); } const std::filesystem::path relPath(normalized); @@ -1768,7 +1768,7 @@ namespace static void applyGsDispEnv(PS2Runtime *runtime, const GsDispEnvMem &env) { - if (!runtime || !runtime->ensureCoreSubsystemsInitialized()) + if (!runtime || !runtime->syncCoreSubsystems()) return; auto ®s = runtime->memory().gs(); regs.pmode = env.pmode; @@ -1782,7 +1782,7 @@ namespace static void applyGsRegPairs(PS2Runtime *runtime, const GsRegPairMem *pairs, size_t pairCount) { - if (!runtime || !pairs || !runtime->ensureCoreSubsystemsInitialized()) + if (!runtime || !pairs || !runtime->syncCoreSubsystems()) return; for (size_t i = 0; i < pairCount; ++i) { diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp index 35761848..a589216e 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/IPU.cpp @@ -3,95 +3,92 @@ namespace ps2_stubs { -void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static constexpr uint32_t REG_IPU_CTRL = 0x10002010u; - static constexpr uint32_t REG_IPU_CMD = 0x10002000u; - static constexpr uint32_t REG_IPU_IN_FIFO = 0x10007010u; - static constexpr uint32_t IQVAL_BASE = 0x1721e0u; - static constexpr uint32_t VQVAL_BASE = 0x172230u; - static constexpr uint32_t SETD4_CHCR_ENTRY = 0x126428u; + void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + static constexpr uint32_t REG_IPU_CTRL = 0x10002010u; + static constexpr uint32_t REG_IPU_CMD = 0x10002000u; + static constexpr uint32_t REG_IPU_IN_FIFO = 0x10007010u; + static constexpr uint32_t IQVAL_BASE = 0x1721e0u; + static constexpr uint32_t VQVAL_BASE = 0x172230u; + static constexpr uint32_t SETD4_CHCR_ENTRY = 0x126428u; + + if (!runtime) + return; - if (!runtime) - return; + if (!runtime->memory().getRDRAM()) + { + if (!runtime->memory().initialize()) + { + setReturnS32(ctx, -1); + return; + } + } - if (!runtime->memory().getRDRAM()) - { - if (!runtime->memory().initialize()) + if (!runtime->syncCoreSubsystems()) { setReturnS32(ctx, -1); return; } - } - if (!runtime->ensureCoreSubsystemsInitialized()) - { - setReturnS32(ctx, -1); - return; - } - - PS2Memory &mem = runtime->memory(); + PS2Memory &mem = runtime->memory(); - if (runtime->hasFunction(SETD4_CHCR_ENTRY)) - { - auto setD4 = runtime->lookupFunction(SETD4_CHCR_ENTRY); - ctx->r[4] = _mm_set_epi64x(0, 1); + if (runtime->hasFunction(SETD4_CHCR_ENTRY)) { - PS2Runtime::GuestExecutionScope guestExecution(runtime); - setD4(rdram, ctx, runtime); + auto setD4 = runtime->lookupFunction(SETD4_CHCR_ENTRY); + ctx->r[4] = _mm_set_epi64x(0, 1); + { + PS2Runtime::GuestExecutionScope guestExecution(runtime); + setD4(rdram, ctx, runtime); + } } - } - - mem.write32(REG_IPU_CTRL, 0x40000000u); - mem.write32(REG_IPU_CMD, 0u); - - __m128i v; - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x00u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x10u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x20u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x30u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x40u); - mem.write128(REG_IPU_IN_FIFO, v); - mem.write128(REG_IPU_IN_FIFO, v); - mem.write128(REG_IPU_IN_FIFO, v); - mem.write128(REG_IPU_IN_FIFO, v); - - mem.write32(REG_IPU_CMD, 0x50000000u); - mem.write32(REG_IPU_CMD, 0x58000000u); - - v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x00u); - mem.write128(REG_IPU_IN_FIFO, v); - v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x10u); - mem.write128(REG_IPU_IN_FIFO, v); - - mem.write32(REG_IPU_CMD, 0x60000000u); - mem.write32(REG_IPU_CMD, 0x90000000u); - - mem.write32(REG_IPU_CTRL, 0x40000000u); - mem.write32(REG_IPU_CMD, 0u); - - setReturnS32(ctx, 0); -} - - -void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + mem.write32(REG_IPU_CTRL, 0x40000000u); + mem.write32(REG_IPU_CMD, 0u); + + __m128i v; + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x00u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x10u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x20u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x30u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, IQVAL_BASE + 0x40u); + mem.write128(REG_IPU_IN_FIFO, v); + mem.write128(REG_IPU_IN_FIFO, v); + mem.write128(REG_IPU_IN_FIFO, v); + mem.write128(REG_IPU_IN_FIFO, v); + + mem.write32(REG_IPU_CMD, 0x50000000u); + mem.write32(REG_IPU_CMD, 0x58000000u); + + v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x00u); + mem.write128(REG_IPU_IN_FIFO, v); + v = runtime->Load128(rdram, ctx, VQVAL_BASE + 0x10u); + mem.write128(REG_IPU_IN_FIFO, v); + + mem.write32(REG_IPU_CMD, 0x60000000u); + mem.write32(REG_IPU_CMD, 0x90000000u); + + mem.write32(REG_IPU_CTRL, 0x40000000u); + mem.write32(REG_IPU_CMD, 0u); + + setReturnS32(ctx, 0); + } -void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } + void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp index f7cbdb2f..253540e0 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/LibC.cpp @@ -4,1136 +4,1129 @@ namespace ps2_stubs { -namespace -{ - uint32_t sanitizeMemTransferSize(uint32_t size, const char *op) + namespace { - constexpr uint32_t kMaxTransfer = PS2_RAM_SIZE; - if (size <= kMaxTransfer) + uint32_t sanitizeMemTransferSize(uint32_t size, const char *op) { - return size; - } + constexpr uint32_t kMaxTransfer = PS2_RAM_SIZE; + if (size <= kMaxTransfer) + { + return size; + } - static std::mutex s_warnMutex; - static std::unordered_map s_warnCounts; - uint32_t warnCount = 0u; - { - std::lock_guard lock(s_warnMutex); - warnCount = ++s_warnCounts[op ? op : "memop"]; + static std::mutex s_warnMutex; + static std::unordered_map s_warnCounts; + uint32_t warnCount = 0u; + { + std::lock_guard lock(s_warnMutex); + warnCount = ++s_warnCounts[op ? op : "memop"]; + } + if (warnCount <= 16u) + { + std::cerr << "[" << (op ? op : "memop") << "] size clamp from 0x" + << std::hex << size << " to 0x" << kMaxTransfer + << std::dec << std::endl; + } + return kMaxTransfer; } - if (warnCount <= 16u) + + uint32_t guestContiguousBytes(uint32_t guestAddr) { - std::cerr << "[" << (op ? op : "memop") << "] size clamp from 0x" - << std::hex << size << " to 0x" << kMaxTransfer - << std::dec << std::endl; + uint32_t offset = 0u; + bool scratch = false; + if (!ps2ResolveGuestPointer(guestAddr, offset, scratch)) + { + return 0u; + } + if (scratch) + { + return (offset < PS2_SCRATCHPAD_SIZE) ? (PS2_SCRATCHPAD_SIZE - offset) : 0u; + } + return (offset < PS2_RAM_SIZE) ? (PS2_RAM_SIZE - offset) : 0u; } - return kMaxTransfer; } - uint32_t guestContiguousBytes(uint32_t guestAddr) + void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint32_t offset = 0u; - bool scratch = false; - if (!ps2ResolveGuestPointer(guestAddr, offset, scratch)) - { - return 0u; - } - if (scratch) + const uint32_t size = getRegU32(ctx, 4); // $a0 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; + setReturnU32(ctx, guestAddr); + } + + void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 + if (runtime && guestAddr != 0u) { - return (offset < PS2_SCRATCHPAD_SIZE) ? (PS2_SCRATCHPAD_SIZE - offset) : 0u; + runtime->guestFree(guestAddr); } - return (offset < PS2_RAM_SIZE) ? (PS2_RAM_SIZE - offset) : 0u; } -} - -void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t size = getRegU32(ctx, 4); // $a0 - const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; - setReturnU32(ctx, guestAddr); -} -void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 - if (runtime && guestAddr != 0u) + void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - runtime->guestFree(guestAddr); + const uint32_t count = getRegU32(ctx, 4); // $a0 + const uint32_t size = getRegU32(ctx, 5); // $a1 + const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; + setReturnU32(ctx, guestAddr); } -} -void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t count = getRegU32(ctx, 4); // $a0 - const uint32_t size = getRegU32(ctx, 5); // $a1 - const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; - setReturnU32(ctx, guestAddr); -} - -void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t oldGuestAddr = getRegU32(ctx, 4); // $a0 - const uint32_t newSize = getRegU32(ctx, 5); // $a1 - const uint32_t newGuestAddr = runtime ? runtime->guestRealloc(oldGuestAddr, newSize) : 0u; - setReturnU32(ctx, newGuestAddr); -} + void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t oldGuestAddr = getRegU32(ctx, 4); // $a0 + const uint32_t newSize = getRegU32(ctx, 5); // $a1 + const uint32_t newGuestAddr = runtime ? runtime->guestRealloc(oldGuestAddr, newSize) : 0u; + setReturnU32(ctx, newGuestAddr); + } -void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - size = sanitizeMemTransferSize(size, "memcpy"); - - uint32_t copied = 0u; - uint32_t curDst = destAddr; - uint32_t curSrc = srcAddr; - while (copied < size) + void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint8_t *hostDest = getMemPtr(rdram, curDst); - const uint8_t *hostSrc = getConstMemPtr(rdram, curSrc); - if (!hostDest || !hostSrc) + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + size = sanitizeMemTransferSize(size, "memcpy"); + + uint32_t copied = 0u; + uint32_t curDst = destAddr; + uint32_t curSrc = srcAddr; + while (copied < size) { - break; + uint8_t *hostDest = getMemPtr(rdram, curDst); + const uint8_t *hostSrc = getConstMemPtr(rdram, curSrc); + if (!hostDest || !hostSrc) + { + break; + } + + uint32_t chunk = size - copied; + chunk = std::min(chunk, guestContiguousBytes(curDst)); + chunk = std::min(chunk, guestContiguousBytes(curSrc)); + if (chunk == 0u) + { + break; + } + + ::memcpy(hostDest, hostSrc, chunk); + copied += chunk; + curDst += chunk; + curSrc += chunk; } - uint32_t chunk = size - copied; - chunk = std::min(chunk, guestContiguousBytes(curDst)); - chunk = std::min(chunk, guestContiguousBytes(curSrc)); - if (chunk == 0u) + if (copied != 0u) { - break; + ps2TraceGuestRangeWrite(rdram, destAddr, copied, "memcpy", ctx); } - ::memcpy(hostDest, hostSrc, chunk); - copied += chunk; - curDst += chunk; - curSrc += chunk; + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - if (copied != 0u) + void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ps2TraceGuestRangeWrite(rdram, destAddr, copied, "memcpy", ctx); - } + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + int value = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + uint32_t size = getRegU32(ctx, 6); // $a2 + size = sanitizeMemTransferSize(size, "memset"); - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} - -void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - int value = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) - uint32_t size = getRegU32(ctx, 6); // $a2 - size = sanitizeMemTransferSize(size, "memset"); - - uint32_t written = 0u; - uint32_t curDst = destAddr; - while (written < size) - { - uint8_t *hostDest = getMemPtr(rdram, curDst); - if (!hostDest) + uint32_t written = 0u; + uint32_t curDst = destAddr; + while (written < size) { - break; + 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, value, chunk); + written += chunk; + curDst += chunk; } - uint32_t chunk = size - written; - chunk = std::min(chunk, guestContiguousBytes(curDst)); - if (chunk == 0u) + if (written != 0u) { - break; + ps2TraceGuestRangeWrite(rdram, destAddr, written, "memset", ctx); } - ::memset(hostDest, value, chunk); - written += chunk; - curDst += chunk; + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - if (written != 0u) + void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ps2TraceGuestRangeWrite(rdram, destAddr, written, "memset", ctx); - } + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + size = sanitizeMemTransferSize(size, "memmove"); - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} + uint32_t copied = 0u; + std::vector tmp; + tmp.reserve(size); + for (uint32_t i = 0u; i < size; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + if (!src) + { + break; + } + tmp.push_back(*src); + } -void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - size = sanitizeMemTransferSize(size, "memmove"); - - uint32_t copied = 0u; - std::vector tmp; - tmp.reserve(size); - for (uint32_t i = 0u; i < size; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - if (!src) + for (uint32_t i = 0u; i < static_cast(tmp.size()); ++i) { - break; + uint8_t *dst = getMemPtr(rdram, destAddr + i); + if (!dst) + { + break; + } + *dst = tmp[i]; + ++copied; } - tmp.push_back(*src); - } - for (uint32_t i = 0u; i < static_cast(tmp.size()); ++i) - { - uint8_t *dst = getMemPtr(rdram, destAddr + i); - if (!dst) + if (copied != 0u) { - break; + ps2TraceGuestRangeWrite(rdram, destAddr, copied, "memmove", ctx); } - *dst = tmp[i]; - ++copied; + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - if (copied != 0u) + void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ps2TraceGuestRangeWrite(rdram, destAddr, copied, "memmove", ctx); + uint32_t ptr1Addr = getRegU32(ctx, 4); // $a0 + uint32_t ptr2Addr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + size = sanitizeMemTransferSize(size, "memcmp"); + int result = 0; + + for (uint32_t i = 0u; i < size; ++i) + { + const uint8_t *lhs = getConstMemPtr(rdram, ptr1Addr + i); + const uint8_t *rhs = getConstMemPtr(rdram, ptr2Addr + i); + if (!lhs || !rhs) + { + result = (!lhs && !rhs) ? 0 : (lhs ? 1 : -1); + break; + } + if (*lhs != *rhs) + { + result = static_cast(*lhs) - static_cast(*rhs); + break; + } + } + setReturnS32(ctx, result); } - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} + void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 -void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t ptr1Addr = getRegU32(ctx, 4); // $a0 - uint32_t ptr2Addr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - size = sanitizeMemTransferSize(size, "memcmp"); - int result = 0; + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - for (uint32_t i = 0u; i < size; ++i) - { - const uint8_t *lhs = getConstMemPtr(rdram, ptr1Addr + i); - const uint8_t *rhs = getConstMemPtr(rdram, ptr2Addr + i); - if (!lhs || !rhs) + if (hostDest && hostSrc) { - result = (!lhs && !rhs) ? 0 : (lhs ? 1 : -1); - break; + ::strcpy(hostDest, hostSrc); + ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(::strlen(hostSrc) + 1u), "strcpy", ctx); } - if (*lhs != *rhs) + else { - result = static_cast(*lhs) - static_cast(*rhs); - break; + std::cerr << "strcpy error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; } - } - setReturnS32(ctx, result); -} - -void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - if (hostDest && hostSrc) - { - ::strcpy(hostDest, hostSrc); - ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(::strlen(hostSrc) + 1u), "strcpy", ctx); + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - else - { - std::cerr << "strcpy error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} -void strncpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - - if (hostDest && hostSrc) + void strncpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ::strncpy(hostDest, hostSrc, size); - ps2TraceGuestRangeWrite(rdram, destAddr, size, "strncpy", ctx); - } - else - { - std::cerr << "strncpy error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 -void strlen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - size_t len = 0; + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - if (hostStr) - { - len = ::strlen(hostStr); + if (hostDest && hostSrc) + { + ::strncpy(hostDest, hostSrc, size); + ps2TraceGuestRangeWrite(rdram, destAddr, size, "strncpy", ctx); + } + else + { + std::cerr << "strncpy error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - else + + void strlen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "strlen error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + size_t len = 0; + + if (hostStr) + { + len = ::strlen(hostStr); + } + else + { + std::cerr << "strlen error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + } + setReturnU32(ctx, (uint32_t)len); } - setReturnU32(ctx, (uint32_t)len); -} -void strcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t str1Addr = getRegU32(ctx, 4); // $a0 - uint32_t str2Addr = getRegU32(ctx, 5); // $a1 + void strcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t str1Addr = getRegU32(ctx, 4); // $a0 + uint32_t str2Addr = getRegU32(ctx, 5); // $a1 - const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); - const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); - int result = 0; + const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); + const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); + int result = 0; - if (hostStr1 && hostStr2) - { - result = ::strcmp(hostStr1, hostStr2); - } - else - { - std::cerr << "strcmp error: Invalid address provided." - << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" - << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec - << std::endl; - // Return non-zero on error, consistent with memcmp error handling - result = (hostStr1 == nullptr) - (hostStr2 == nullptr); - if (result == 0 && hostStr1 == nullptr) - result = 1; // Both null -> treat as different? Or 0? Let's say different. + if (hostStr1 && hostStr2) + { + result = ::strcmp(hostStr1, hostStr2); + } + else + { + std::cerr << "strcmp error: Invalid address provided." + << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" + << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec + << std::endl; + // Return non-zero on error, consistent with memcmp error handling + result = (hostStr1 == nullptr) - (hostStr2 == nullptr); + if (result == 0 && hostStr1 == nullptr) + result = 1; // Both null -> treat as different? Or 0? Let's say different. + } + setReturnS32(ctx, result); } - setReturnS32(ctx, result); -} -void strncmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t str1Addr = getRegU32(ctx, 4); // $a0 - uint32_t str2Addr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 + void strncmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t str1Addr = getRegU32(ctx, 4); // $a0 + uint32_t str2Addr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 - const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); - const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); - int result = 0; + const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); + const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); + int result = 0; - if (hostStr1 && hostStr2) - { - result = ::strncmp(hostStr1, hostStr2, size); + if (hostStr1 && hostStr2) + { + result = ::strncmp(hostStr1, hostStr2, size); + } + else + { + std::cerr << "strncmp error: Invalid address provided." + << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" + << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec + << std::endl; + result = (hostStr1 == nullptr) - (hostStr2 == nullptr); + if (result == 0 && hostStr1 == nullptr) + result = 1; // Both null -> different + } + setReturnS32(ctx, result); } - else + + void strcat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "strncmp error: Invalid address provided." - << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" - << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec - << std::endl; - result = (hostStr1 == nullptr) - (hostStr2 == nullptr); - if (result == 0 && hostStr1 == nullptr) - result = 1; // Both null -> different - } - setReturnS32(ctx, result); -} + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 -void strcat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); + if (hostDest && hostSrc) + { + ::strcat(hostDest, hostSrc); + } + else + { + std::cerr << "strcat error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } - if (hostDest && hostSrc) - { - ::strcat(hostDest, hostSrc); - } - else - { - std::cerr << "strcat error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} + void strncat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 -void strncat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); + if (hostDest && hostSrc) + { + ::strncat(hostDest, hostSrc, size); + } + else + { + std::cerr << "strncat error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } - if (hostDest && hostSrc) - { - ::strncat(hostDest, hostSrc, size); + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; } - else - { - std::cerr << "strncat error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; -} -void strchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + void strchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - char *foundPtr = nullptr; - uint32_t resultAddr = 0; + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + char *foundPtr = nullptr; + uint32_t resultAddr = 0; - if (hostStr) - { - foundPtr = ::strchr(const_cast(hostStr), char_code); - if (foundPtr) + if (hostStr) { - resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + foundPtr = ::strchr(const_cast(hostStr), char_code); + if (foundPtr) + { + resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + } + } + else + { + std::cerr << "strchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; } - } - else - { - std::cerr << "strchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; - } - // returns PS2 address or 0 (NULL) - setReturnU32(ctx, resultAddr); -} + // returns PS2 address or 0 (NULL) + setReturnU32(ctx, resultAddr); + } -void strrchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + void strrchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - char *foundPtr = nullptr; - uint32_t resultAddr = 0; + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + char *foundPtr = nullptr; + uint32_t resultAddr = 0; - if (hostStr) - { - foundPtr = ::strrchr(const_cast(hostStr), char_code); // Use const_cast carefully - if (foundPtr) + if (hostStr) + { + foundPtr = ::strrchr(const_cast(hostStr), char_code); // Use const_cast carefully + if (foundPtr) + { + resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + } + } + else { - resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + std::cerr << "strrchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; } + + // returns PS2 address or 0 (NULL) + setReturnU32(ctx, resultAddr); } - else + + void strstr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "strrchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; - } + uint32_t haystackAddr = getRegU32(ctx, 4); // $a0 + uint32_t needleAddr = getRegU32(ctx, 5); // $a1 - // returns PS2 address or 0 (NULL) - setReturnU32(ctx, resultAddr); -} + const char *hostHaystack = reinterpret_cast(getConstMemPtr(rdram, haystackAddr)); + const char *hostNeedle = reinterpret_cast(getConstMemPtr(rdram, needleAddr)); + char *foundPtr = nullptr; + uint32_t resultAddr = 0; -void strstr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t haystackAddr = getRegU32(ctx, 4); // $a0 - uint32_t needleAddr = getRegU32(ctx, 5); // $a1 + if (hostHaystack && hostNeedle) + { + foundPtr = ::strstr(const_cast(hostHaystack), hostNeedle); + if (foundPtr) + { + resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + } + } + else + { + std::cerr << "strstr error: Invalid address provided." + << " Haystack: 0x" << std::hex << haystackAddr << " (host ptr valid: " << (hostHaystack != nullptr) << ")" + << ", Needle: 0x" << needleAddr << " (host ptr valid: " << (hostNeedle != nullptr) << ")" << std::dec + << std::endl; + } - const char *hostHaystack = reinterpret_cast(getConstMemPtr(rdram, haystackAddr)); - const char *hostNeedle = reinterpret_cast(getConstMemPtr(rdram, needleAddr)); - char *foundPtr = nullptr; - uint32_t resultAddr = 0; + // returns PS2 address or 0 (NULL) + setReturnU32(ctx, resultAddr); + } - if (hostHaystack && hostNeedle) + void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - foundPtr = ::strstr(const_cast(hostHaystack), hostNeedle); - if (foundPtr) + uint32_t format_addr = getRegU32(ctx, 4); // $a0 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) { - resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); + if (rendered.size() > 2048) + { + rendered.resize(2048); + } + PS2_IF_AGRESSIVE_LOGS({ + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + RUNTIME_LOG("PS2 printf: " << logLine); + RUNTIME_LOG(std::flush); + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + }); + ret = static_cast(rendered.size()); } - } - else - { - std::cerr << "strstr error: Invalid address provided." - << " Haystack: 0x" << std::hex << haystackAddr << " (host ptr valid: " << (hostHaystack != nullptr) << ")" - << ", Needle: 0x" << needleAddr << " (host ptr valid: " << (hostNeedle != nullptr) << ")" << std::dec - << std::endl; + else + { + std::cerr << "printf error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; + } + + // returns the number of characters written, or negative on error. + setReturnS32(ctx, ret); } - // returns PS2 address or 0 (NULL) - setReturnU32(ctx, resultAddr); -} + void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + constexpr size_t kSafeSprintfBytes = 256u; // Keep guest stack temporaries from being overwritten. -void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t format_addr = getRegU32(ctx, 4); // $a0 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); - if (rendered.size() > 2048) + if (format_addr != 0) { - rendered.resize(2048); - } - PS2_IF_AGRESSIVE_LOGS({ - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; + const uint32_t watchBase = ps2PathWatchPhysAddr(); + const uint32_t watchEnd = watchBase + PS2_PATH_WATCH_BYTES; + const uint32_t dest = str_addr & PS2_RAM_MASK; + const bool touchesWatch = dest < watchEnd && dest >= watchBase; + static uint32_t watchSprintfLogCount = 0; + if (touchesWatch && watchSprintfLogCount < 64u) { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; + const uint32_t arg0 = getRegU32(ctx, 6); + const uint32_t arg1 = getRegU32(ctx, 7); + RUNTIME_LOG("[watch:sprintf] dest=0x" << std::hex << str_addr + << " fmt@0x" << format_addr + << " arg0=0x" << arg0 + << " arg1=0x" << arg1 + << " fmt=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, format_addr, 64)) << "\"" + << " s0=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg0, 64)) << "\"" + << " s1=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg1, 64)) << "\"" + << std::dec << std::endl); + ++watchSprintfLogCount; } - if (count <= kMaxPrintfLogs) + + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + if (rendered.size() >= kSafeSprintfBytes) { - RUNTIME_LOG("PS2 printf: " << logLine); - RUNTIME_LOG(std::flush); + rendered.resize(kSafeSprintfBytes - 1); } - else if (count == kMaxPrintfLogs + 1) + const size_t writeLen = rendered.size() + 1u; + if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), writeLen)) { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(writeLen), "sprintf", ctx); + ret = static_cast(rendered.size()); } - }); - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "printf error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; - } - - // returns the number of characters written, or negative on error. - setReturnS32(ctx, ret); -} - -void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - constexpr size_t kSafeSprintfBytes = 256u; // Keep guest stack temporaries from being overwritten. + else + { + std::cerr << "sprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + } + } + else + { + std::cerr << "sprintf error: Invalid format address provided." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; + // returns the number of characters written (excluding null), or negative on error. + setReturnS32(ctx, ret); + } - if (format_addr != 0) + void snprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const uint32_t watchBase = ps2PathWatchPhysAddr(); - const uint32_t watchEnd = watchBase + PS2_PATH_WATCH_BYTES; - const uint32_t dest = str_addr & PS2_RAM_MASK; - const bool touchesWatch = dest < watchEnd && dest >= watchBase; - static uint32_t watchSprintfLogCount = 0; - if (touchesWatch && watchSprintfLogCount < 64u) - { - const uint32_t arg0 = getRegU32(ctx, 6); - const uint32_t arg1 = getRegU32(ctx, 7); - RUNTIME_LOG("[watch:sprintf] dest=0x" << std::hex << str_addr - << " fmt@0x" << format_addr - << " arg0=0x" << arg0 - << " arg1=0x" << arg1 - << " fmt=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, format_addr, 64)) << "\"" - << " s0=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg0, 64)) << "\"" - << " s1=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg1, 64)) << "\"" - << std::dec << std::endl); - ++watchSprintfLogCount; - } - - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - if (rendered.size() >= kSafeSprintfBytes) - { - rendered.resize(kSafeSprintfBytes - 1); - } - const size_t writeLen = rendered.size() + 1u; - if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), writeLen)) - { - ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(writeLen), "sprintf", ctx); + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + size_t size = getRegU32(ctx, 5); // $a1 + uint32_t format_addr = getRegU32(ctx, 6); // $a2 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 3); ret = static_cast(rendered.size()); + + if (size > 0) + { + const size_t copyLen = std::min(size - 1, rendered.size()); + std::vector output(copyLen + 1u, 0u); + if (copyLen > 0u) + { + std::memcpy(output.data(), rendered.data(), copyLen); + } + if (writeGuestBytes(rdram, runtime, str_addr, output.data(), output.size())) + { + ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(output.size()), "snprintf", ctx); + } + else + { + std::cerr << "snprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + ret = -1; + } + } } else { - std::cerr << "sprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; + std::cerr << "snprintf error: Invalid address provided or size is zero." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << ", Size: " << size << std::endl; } + + // returns the number of characters that *would* have been written + // if size was large enough (excluding null), or negative on error. + setReturnS32(ctx, ret); } - else + + void puts(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "sprintf error: Invalid format address provided." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + int result = EOF; - // returns the number of characters written (excluding null), or negative on error. - setReturnS32(ctx, ret); -} + if (hostStr) + { + result = std::puts(hostStr); // std::puts adds a newline + std::fflush(stdout); // Ensure output appears + } + else + { + std::cerr << "puts error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + } -void snprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - size_t size = getRegU32(ctx, 5); // $a1 - uint32_t format_addr = getRegU32(ctx, 6); // $a2 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; + // returns non-negative on success, EOF on error. + setReturnS32(ctx, result >= 0 ? 0 : -1); // PS2 might expect 0/-1 rather than EOF + } - if (format_addr != 0) + void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 3); - ret = static_cast(rendered.size()); + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + uint32_t modeAddr = getRegU32(ctx, 5); // $a1 - if (size > 0) + const char *hostPath = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + const char *hostMode = reinterpret_cast(getConstMemPtr(rdram, modeAddr)); + uint32_t file_handle = 0; + + if (hostPath && hostMode) { - const size_t copyLen = std::min(size - 1, rendered.size()); - std::vector output(copyLen + 1u, 0u); - if (copyLen > 0u) - { - std::memcpy(output.data(), rendered.data(), copyLen); - } - if (writeGuestBytes(rdram, runtime, str_addr, output.data(), output.size())) + // TODO: Add translation for PS2 paths like mc0:, host:, cdrom:, etc. + // treating as direct host path + RUNTIME_LOG("ps2_stub fopen: path='" << hostPath << "', mode='" << hostMode << "'"); + FILE *fp = ::fopen(hostPath, hostMode); + if (fp) { - ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(output.size()), "snprintf", ctx); + std::lock_guard lock(g_file_mutex); + file_handle = generate_file_handle(); + g_file_map[file_handle] = fp; + RUNTIME_LOG(" -> handle=0x" << std::hex << file_handle << std::dec); } else { - std::cerr << "snprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; - ret = -1; + std::cerr << "ps2_stub fopen error: Failed to open '" << hostPath << "' with mode '" << hostMode << "'. Error: " << strerror(errno) << std::endl; } } - } - else - { - std::cerr << "snprintf error: Invalid address provided or size is zero." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << ", Size: " << size << std::endl; + else + { + std::cerr << "fopen error: Invalid address provided for path or mode." + << " Path: 0x" << std::hex << pathAddr << " (host ptr valid: " << (hostPath != nullptr) << ")" + << ", Mode: 0x" << modeAddr << " (host ptr valid: " << (hostMode != nullptr) << ")" << std::dec + << std::endl; + } + // returns a file handle (non-zero) on success, or NULL (0) on error. + setReturnU32(ctx, file_handle); } - // returns the number of characters that *would* have been written - // if size was large enough (excluding null), or negative on error. - setReturnS32(ctx, ret); -} + void fclose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + int ret = EOF; // Default to error -void puts(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - int result = EOF; + if (file_handle != 0) + { + std::lock_guard lock(g_file_mutex); + auto it = g_file_map.find(file_handle); + if (it != g_file_map.end()) + { + FILE *fp = it->second; + ret = ::fclose(fp); + g_file_map.erase(it); + } + else + { + std::cerr << "ps2_stub fclose error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } + } + else + { + // Closing NULL handle in Standard C defines this as no-op + ret = 0; + } - if (hostStr) - { - result = std::puts(hostStr); // std::puts adds a newline - std::fflush(stdout); // Ensure output appears - } - else - { - std::cerr << "puts error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + // returns 0 on success, EOF on error. + setReturnS32(ctx, ret); } - // returns non-negative on success, EOF on error. - setReturnS32(ctx, result >= 0 ? 0 : -1); // PS2 might expect 0/-1 rather than EOF -} - -void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - uint32_t modeAddr = getRegU32(ctx, 5); // $a1 + void fread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) + uint32_t size = getRegU32(ctx, 5); // $a1 (element size) + uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) + uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) + size_t items_read = 0; - const char *hostPath = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - const char *hostMode = reinterpret_cast(getConstMemPtr(rdram, modeAddr)); - uint32_t file_handle = 0; + uint8_t *hostPtr = getMemPtr(rdram, ptrAddr); + FILE *fp = get_file_ptr(file_handle); - if (hostPath && hostMode) - { - // TODO: Add translation for PS2 paths like mc0:, host:, cdrom:, etc. - // treating as direct host path - RUNTIME_LOG("ps2_stub fopen: path='" << hostPath << "', mode='" << hostMode << "'"); - FILE *fp = ::fopen(hostPath, hostMode); - if (fp) + if (hostPtr && fp && size > 0 && count > 0) { - std::lock_guard lock(g_file_mutex); - file_handle = generate_file_handle(); - g_file_map[file_handle] = fp; - RUNTIME_LOG(" -> handle=0x" << std::hex << file_handle << std::dec); + items_read = ::fread(hostPtr, size, count, fp); } else { - std::cerr << "ps2_stub fopen error: Failed to open '" << hostPath << "' with mode '" << hostMode << "'. Error: " << strerror(errno) << std::endl; + std::cerr << "fread error: Invalid arguments." + << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" + << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec + << ", Size: " << size << ", Count: " << count << std::endl; } + // returns the number of items successfully read. + setReturnU32(ctx, (uint32_t)items_read); } - else + + void fwrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fopen error: Invalid address provided for path or mode." - << " Path: 0x" << std::hex << pathAddr << " (host ptr valid: " << (hostPath != nullptr) << ")" - << ", Mode: 0x" << modeAddr << " (host ptr valid: " << (hostMode != nullptr) << ")" << std::dec - << std::endl; - } - // returns a file handle (non-zero) on success, or NULL (0) on error. - setReturnU32(ctx, file_handle); -} + uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) + uint32_t size = getRegU32(ctx, 5); // $a1 (element size) + uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) + uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) + size_t items_written = 0; -void fclose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - int ret = EOF; // Default to error + const uint8_t *hostPtr = getConstMemPtr(rdram, ptrAddr); + FILE *fp = get_file_ptr(file_handle); - if (file_handle != 0) - { - std::lock_guard lock(g_file_mutex); - auto it = g_file_map.find(file_handle); - if (it != g_file_map.end()) + if (hostPtr && fp && size > 0 && count > 0) { - FILE *fp = it->second; - ret = ::fclose(fp); - g_file_map.erase(it); + items_written = ::fwrite(hostPtr, size, count, fp); } else { - std::cerr << "ps2_stub fclose error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + std::cerr << "fwrite error: Invalid arguments." + << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" + << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec + << ", Size: " << size << ", Count: " << count << std::endl; } + // returns the number of items successfully written. + setReturnU32(ctx, (uint32_t)items_written); } - else - { - // Closing NULL handle in Standard C defines this as no-op - ret = 0; - } - - // returns 0 on success, EOF on error. - setReturnS32(ctx, ret); -} -void fread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) - uint32_t size = getRegU32(ctx, 5); // $a1 (element size) - uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) - uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) - size_t items_read = 0; + void fprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + FILE *fp = get_file_ptr(file_handle); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; - uint8_t *hostPtr = getMemPtr(rdram, ptrAddr); - FILE *fp = get_file_ptr(file_handle); + if (fp && format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + ret = std::fprintf(fp, "%s", rendered.c_str()); + } + else + { + std::cerr << "fprintf error: Invalid file handle or format address." + << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } - if (hostPtr && fp && size > 0 && count > 0) - { - items_read = ::fread(hostPtr, size, count, fp); + // returns the number of characters written, or negative on error. + setReturnS32(ctx, ret); } - else + + void fseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fread error: Invalid arguments." - << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" - << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec - << ", Size: " << size << ", Count: " << count << std::endl; - } - // returns the number of items successfully read. - setReturnU32(ctx, (uint32_t)items_read); -} + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + long offset = (long)getRegU32(ctx, 5); // $a1 (Note: might need 64-bit for large files?) + int whence = (int)getRegU32(ctx, 6); // $a2 (SEEK_SET, SEEK_CUR, SEEK_END) + int ret = -1; // Default error -void fwrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) - uint32_t size = getRegU32(ctx, 5); // $a1 (element size) - uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) - uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) - size_t items_written = 0; + FILE *fp = get_file_ptr(file_handle); - const uint8_t *hostPtr = getConstMemPtr(rdram, ptrAddr); - FILE *fp = get_file_ptr(file_handle); + if (fp) + { + // Ensure whence is valid (0, 1, 2) + if (whence >= 0 && whence <= 2) + { + ret = ::fseek(fp, offset, whence); + } + else + { + std::cerr << "fseek error: Invalid whence value: " << whence << std::endl; + } + } + else + { + std::cerr << "fseek error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } - if (hostPtr && fp && size > 0 && count > 0) - { - items_written = ::fwrite(hostPtr, size, count, fp); + // returns 0 on success, non-zero on error. + setReturnS32(ctx, ret); } - else - { - std::cerr << "fwrite error: Invalid arguments." - << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" - << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec - << ", Size: " << size << ", Count: " << count << std::endl; - } - // returns the number of items successfully written. - setReturnU32(ctx, (uint32_t)items_written); -} - -void fprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - FILE *fp = get_file_ptr(file_handle); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - if (fp && format_addr != 0) + void ftell(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - ret = std::fprintf(fp, "%s", rendered.c_str()); - } - else - { - std::cerr << "fprintf error: Invalid file handle or format address." - << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + long ret = -1L; - // returns the number of characters written, or negative on error. - setReturnS32(ctx, ret); -} - -void fseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - long offset = (long)getRegU32(ctx, 5); // $a1 (Note: might need 64-bit for large files?) - int whence = (int)getRegU32(ctx, 6); // $a2 (SEEK_SET, SEEK_CUR, SEEK_END) - int ret = -1; // Default error + FILE *fp = get_file_ptr(file_handle); - FILE *fp = get_file_ptr(file_handle); + if (fp) + { + ret = ::ftell(fp); + } + else + { + std::cerr << "ftell error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } - if (fp) - { - // Ensure whence is valid (0, 1, 2) - if (whence >= 0 && whence <= 2) + // returns the current position, or -1L on error. + if (ret > 0xFFFFFFFFL || ret < 0) { - ret = ::fseek(fp, offset, whence); + setReturnS32(ctx, -1); } else { - std::cerr << "fseek error: Invalid whence value: " << whence << std::endl; + setReturnU32(ctx, (uint32_t)ret); } } - else - { - std::cerr << "fseek error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; - } - - // returns 0 on success, non-zero on error. - setReturnS32(ctx, ret); -} -void ftell(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - long ret = -1L; + void fflush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + int ret = EOF; // Default error - FILE *fp = get_file_ptr(file_handle); + // If handle is 0 fflush flushes *all* output streams. + if (file_handle == 0) + { + ret = ::fflush(NULL); + } + else + { + FILE *fp = get_file_ptr(file_handle); + if (fp) + { + ret = ::fflush(fp); + } + else + { + std::cerr << "fflush error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } + } + // returns 0 on success, EOF on error. + setReturnS32(ctx, ret); + } - if (fp) + void sqrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ret = ::ftell(fp); + float arg = ctx->f[12]; + ctx->f[0] = ::sqrtf(arg); } - else + + void sin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "ftell error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + float arg = ctx->f[12]; + ctx->f[0] = ::sinf(arg); } - // returns the current position, or -1L on error. - if (ret > 0xFFFFFFFFL || ret < 0) + void __kernel_sinf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); + const float x = ctx->f[12]; + const float y = ctx->f[13]; + const int32_t iy = static_cast(getRegU32(ctx, 4)); + ctx->f[0] = ::sinf(x + (iy != 0 ? y : 0.0f)); } - else + + void cos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnU32(ctx, (uint32_t)ret); + float arg = ctx->f[12]; + ctx->f[0] = ::cosf(arg); } -} - -void fflush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - int ret = EOF; // Default error - // If handle is 0 fflush flushes *all* output streams. - if (file_handle == 0) + void __kernel_cosf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ret = ::fflush(NULL); + const float x = ctx->f[12]; + const float y = ctx->f[13]; + ctx->f[0] = ::cosf(x + y); } - else + + void __ieee754_rem_pio2f(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - FILE *fp = get_file_ptr(file_handle); - if (fp) + const float x = ctx->f[12]; + constexpr float kPi = 3.14159265358979323846f; + constexpr float kHalfPi = kPi * 0.5f; + constexpr float kInvHalfPi = 2.0f / kPi; + const int32_t n = static_cast(std::nearbyintf(x * kInvHalfPi)); + const float y0 = x - (static_cast(n) * kHalfPi); + const float y1 = 0.0f; + + const uint32_t yOutAddr = getRegU32(ctx, 4); + if (float *yOut0 = reinterpret_cast(getMemPtr(rdram, yOutAddr)); yOut0) { - ret = ::fflush(fp); + *yOut0 = y0; } - else + if (float *yOut1 = reinterpret_cast(getMemPtr(rdram, yOutAddr + 4)); yOut1) { - std::cerr << "fflush error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + *yOut1 = y1; } - } - // returns 0 on success, EOF on error. - setReturnS32(ctx, ret); -} -void sqrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::sqrtf(arg); -} - -void sin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::sinf(arg); -} - -void __kernel_sinf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const float x = ctx->f[12]; - const float y = ctx->f[13]; - const int32_t iy = static_cast(getRegU32(ctx, 4)); - ctx->f[0] = ::sinf(x + (iy != 0 ? y : 0.0f)); -} - -void cos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::cosf(arg); -} - -void __kernel_cosf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const float x = ctx->f[12]; - const float y = ctx->f[13]; - ctx->f[0] = ::cosf(x + y); -} + setReturnS32(ctx, n); + } -void __ieee754_rem_pio2f(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const float x = ctx->f[12]; - constexpr float kPi = 3.14159265358979323846f; - constexpr float kHalfPi = kPi * 0.5f; - constexpr float kInvHalfPi = 2.0f / kPi; - const int32_t n = static_cast(std::nearbyintf(x * kInvHalfPi)); - const float y0 = x - (static_cast(n) * kHalfPi); - const float y1 = 0.0f; - - const uint32_t yOutAddr = getRegU32(ctx, 4); - if (float *yOut0 = reinterpret_cast(getMemPtr(rdram, yOutAddr)); yOut0) + void tan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - *yOut0 = y0; + float arg = ctx->f[12]; + ctx->f[0] = ::tanf(arg); } - if (float *yOut1 = reinterpret_cast(getMemPtr(rdram, yOutAddr + 4)); yOut1) + + void atan2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - *yOut1 = y1; + float y = ctx->f[12]; + float x = ctx->f[14]; + ctx->f[0] = ::atan2f(y, x); } - setReturnS32(ctx, n); -} - -void tan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::tanf(arg); -} - -void atan2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float y = ctx->f[12]; - float x = ctx->f[14]; - ctx->f[0] = ::atan2f(y, x); -} - -void pow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float base = ctx->f[12]; - float exp = ctx->f[14]; - ctx->f[0] = ::powf(base, exp); -} - -void exp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::expf(arg); -} - -void log(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::logf(arg); -} + void pow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + float base = ctx->f[12]; + float exp = ctx->f[14]; + ctx->f[0] = ::powf(base, exp); + } -void log10(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::log10f(arg); -} + void exp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + float arg = ctx->f[12]; + ctx->f[0] = ::expf(arg); + } -void ceil(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::ceilf(arg); -} + void log(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + float arg = ctx->f[12]; + ctx->f[0] = ::logf(arg); + } -void floor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::floorf(arg); -} + void log10(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + float arg = ctx->f[12]; + ctx->f[0] = ::log10f(arg); + } -void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float arg = ctx->f[12]; - ctx->f[0] = ::fabsf(arg); -} -void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t value = static_cast(getRegU32(ctx, 4)); - if (value == std::numeric_limits::min()) + void ceil(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, std::numeric_limits::max()); - return; + float arg = ctx->f[12]; + ctx->f[0] = ::ceilf(arg); } - setReturnS32(ctx, value < 0 ? -value : value); -} + void floor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + float arg = ctx->f[12]; + ctx->f[0] = ::floorf(arg); + } -void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - float in = ctx ? ctx->f[12] : 0.0f; - if (in == 0.0f) + void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint32_t raw = getRegU32(ctx, 4); - std::memcpy(&in, &raw, sizeof(in)); + float arg = ctx->f[12]; + ctx->f[0] = ::fabsf(arg); } - const float out = std::atan(in); - if (ctx) + void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ctx->f[0] = out; + const int32_t value = static_cast(getRegU32(ctx, 4)); + if (value == std::numeric_limits::min()) + { + setReturnS32(ctx, std::numeric_limits::max()); + return; + } + setReturnS32(ctx, value < 0 ? -value : value); } - uint32_t outRaw = 0u; - std::memcpy(&outRaw, &out, sizeof(outRaw)); - setReturnU32(ctx, outRaw); -} - - -void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t srcAddr = getRegU32(ctx, 4); - const uint8_t needle = static_cast(getRegU32(ctx, 5) & 0xFFu); - const uint32_t size = getRegU32(ctx, 6); - - for (uint32_t i = 0; i < size; ++i) + void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - if (!src) + float in = ctx ? ctx->f[12] : 0.0f; + if (in == 0.0f) { - break; + uint32_t raw = getRegU32(ctx, 4); + std::memcpy(&in, &raw, sizeof(in)); } - if (*src == needle) + const float out = std::atan(in); + if (ctx) { - setReturnU32(ctx, srcAddr + i); - return; + ctx->f[0] = out; } - } - - setReturnU32(ctx, 0u); -} - - -void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, std::rand() & 0x7FFF); -} - -void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::srand(getRegU32(ctx, 4)); - setReturnS32(ctx, 0); -} - - -void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t lhsAddr = getRegU32(ctx, 4); - const uint32_t rhsAddr = getRegU32(ctx, 5); - const std::string lhs = readPs2CStringBounded(rdram, runtime, lhsAddr, 1024); - const std::string rhs = readPs2CStringBounded(rdram, runtime, rhsAddr, 1024); + uint32_t outRaw = 0u; + std::memcpy(&outRaw, &out, sizeof(outRaw)); + setReturnU32(ctx, outRaw); + } - const size_t n = std::min(lhs.size(), rhs.size()); - for (size_t i = 0; i < n; ++i) + void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const int a = std::tolower(static_cast(lhs[i])); - const int b = std::tolower(static_cast(rhs[i])); - if (a != b) + const uint32_t srcAddr = getRegU32(ctx, 4); + const uint8_t needle = static_cast(getRegU32(ctx, 5) & 0xFFu); + const uint32_t size = getRegU32(ctx, 6); + + for (uint32_t i = 0; i < size; ++i) { - setReturnS32(ctx, a - b); - return; + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + if (!src) + { + break; + } + if (*src == needle) + { + setReturnU32(ctx, srcAddr + i); + return; + } } - } - - setReturnS32(ctx, static_cast(lhs.size()) - static_cast(rhs.size())); -} + setReturnU32(ctx, 0u); + } -void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 - FILE *fp = get_file_ptr(file_handle); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (fp && format_addr != 0) + void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); - ret = std::fprintf(fp, "%s", rendered.c_str()); + setReturnS32(ctx, std::rand() & 0x7FFF); } - else + + void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "vfprintf error: Invalid file handle or format address." - << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" - << ", Format: 0x" << format_addr << std::dec - << std::endl; + std::srand(getRegU32(ctx, 4)); + setReturnS32(ctx, 0); } - setReturnS32(ctx, ret); -} - - -void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 - constexpr size_t kSafeVsprintfBytes = 256u; // Keep guest stack temporaries from being overwritten. - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) + void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); - if (rendered.size() >= kSafeVsprintfBytes) + const uint32_t lhsAddr = getRegU32(ctx, 4); + const uint32_t rhsAddr = getRegU32(ctx, 5); + const std::string lhs = readPs2CStringBounded(rdram, runtime, lhsAddr, 1024); + const std::string rhs = readPs2CStringBounded(rdram, runtime, rhsAddr, 1024); + + const size_t n = std::min(lhs.size(), rhs.size()); + for (size_t i = 0; i < n; ++i) { - rendered.resize(kSafeVsprintfBytes - 1); + const int a = std::tolower(static_cast(lhs[i])); + const int b = std::tolower(static_cast(rhs[i])); + if (a != b) + { + setReturnS32(ctx, a - b); + return; + } } - if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), rendered.size() + 1u)) + + setReturnS32(ctx, static_cast(lhs.size()) - static_cast(rhs.size())); + } + + void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 + FILE *fp = get_file_ptr(file_handle); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (fp && format_addr != 0) { - ret = static_cast(rendered.size()); + std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); + ret = std::fprintf(fp, "%s", rendered.c_str()); } else { - std::cerr << "vsprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; + std::cerr << "vfprintf error: Invalid file handle or format address." + << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" + << ", Format: 0x" << format_addr << std::dec + << std::endl; } + + setReturnS32(ctx, ret); } - else + + void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "vsprintf error: Invalid address provided." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 + constexpr size_t kSafeVsprintfBytes = 256u; // Keep guest stack temporaries from being overwritten. + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; - setReturnS32(ctx, ret); -} + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); + if (rendered.size() >= kSafeVsprintfBytes) + { + rendered.resize(kSafeVsprintfBytes - 1); + } + if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), rendered.size() + 1u)) + { + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "vsprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + } + } + else + { + std::cerr << "vsprintf error: Invalid address provided." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + setReturnS32(ctx, ret); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp index f9c0e424..d06f5a6e 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MPEG.cpp @@ -3,423 +3,419 @@ namespace ps2_stubs { -namespace -{ -struct MpegRegisteredCallback -{ - uint32_t type = 0u; - uint32_t func = 0u; - uint32_t data = 0u; - uint32_t handle = 0u; -}; - -struct MpegPlaybackState -{ - uint32_t picturesServed = 0u; -}; + namespace + { + struct MpegRegisteredCallback + { + uint32_t type = 0u; + uint32_t func = 0u; + uint32_t data = 0u; + uint32_t handle = 0u; + }; -struct MpegStubState -{ - bool initialized = false; - uint32_t nextCallbackHandle = 1u; - std::unordered_map> callbacksByMpeg; - std::unordered_map playbackByMpeg; - PS2MpegCompatLayout compat; -}; + struct MpegPlaybackState + { + uint32_t picturesServed = 0u; + }; -std::mutex g_mpeg_stub_mutex; -MpegStubState g_mpeg_stub_state; + struct MpegStubState + { + bool initialized = false; + uint32_t nextCallbackHandle = 1u; + std::unordered_map> callbacksByMpeg; + std::unordered_map playbackByMpeg; + PS2MpegCompatLayout compat; + }; -constexpr uint32_t kStubMovieWidth = 320u; -constexpr uint32_t kStubMovieHeight = 240u; + std::mutex g_mpeg_stub_mutex; + MpegStubState g_mpeg_stub_state; -uint32_t mpegCompatSyntheticFrames(const PS2MpegCompatLayout &layout) -{ - return layout.syntheticFramesBeforeEnd != 0u ? layout.syntheticFramesBeforeEnd : 1u; -} + constexpr uint32_t kStubMovieWidth = 320u; + constexpr uint32_t kStubMovieHeight = 240u; -MpegPlaybackState &getPlaybackState(uint32_t mpegAddr) -{ - return g_mpeg_stub_state.playbackByMpeg[mpegAddr]; -} + uint32_t mpegCompatSyntheticFrames(const PS2MpegCompatLayout &layout) + { + return layout.syntheticFramesBeforeEnd != 0u ? layout.syntheticFramesBeforeEnd : 1u; + } -void resetMpegStubStateUnlocked() -{ - const PS2MpegCompatLayout compat = g_mpeg_stub_state.compat; - g_mpeg_stub_state.initialized = false; - g_mpeg_stub_state.nextCallbackHandle = 1u; - g_mpeg_stub_state.callbacksByMpeg.clear(); - g_mpeg_stub_state.playbackByMpeg.clear(); - g_mpeg_stub_state.compat = compat; -} -} + MpegPlaybackState &getPlaybackState(uint32_t mpegAddr) + { + return g_mpeg_stub_state.playbackByMpeg[mpegAddr]; + } -void setMpegCompatLayout(const PS2MpegCompatLayout &layout) -{ - std::lock_guard lock(g_mpeg_stub_mutex); - g_mpeg_stub_state.compat = layout; -} + void resetMpegStubStateUnlocked() + { + const PS2MpegCompatLayout compat = g_mpeg_stub_state.compat; + g_mpeg_stub_state.initialized = false; + g_mpeg_stub_state.nextCallbackHandle = 1u; + g_mpeg_stub_state.callbacksByMpeg.clear(); + g_mpeg_stub_state.playbackByMpeg.clear(); + g_mpeg_stub_state.compat = compat; + } + } -void clearMpegCompatLayout() -{ - std::lock_guard lock(g_mpeg_stub_mutex); - g_mpeg_stub_state.compat = {}; -} + void setMpegCompatLayout(const PS2MpegCompatLayout &layout) + { + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.compat = layout; + } -void resetMpegStubState() -{ - std::lock_guard lock(g_mpeg_stub_mutex); - resetMpegStubStateUnlocked(); -} + void clearMpegCompatLayout() + { + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.compat = {}; + } -void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegFlush", rdram, ctx, runtime); -} + void resetMpegStubState() + { + std::lock_guard lock(g_mpeg_stub_mutex); + resetMpegStubStateUnlocked(); + } + void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegFlush", rdram, ctx, runtime); + } -void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime); -} + void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime); + } -void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; + void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; - const uint32_t mpegAddr = getRegU32(ctx, 4); - const uint32_t callbackType = getRegU32(ctx, 5); - const uint32_t callbackFunc = getRegU32(ctx, 6); - const uint32_t callbackData = getRegU32(ctx, 7); + const uint32_t mpegAddr = getRegU32(ctx, 4); + const uint32_t callbackType = getRegU32(ctx, 5); + const uint32_t callbackFunc = getRegU32(ctx, 6); + const uint32_t callbackData = getRegU32(ctx, 7); - std::lock_guard lock(g_mpeg_stub_mutex); - g_mpeg_stub_state.initialized = true; - (void)getPlaybackState(mpegAddr); + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.initialized = true; + (void)getPlaybackState(mpegAddr); - const uint32_t handle = g_mpeg_stub_state.nextCallbackHandle++; - g_mpeg_stub_state.callbacksByMpeg[mpegAddr].push_back( - MpegRegisteredCallback{callbackType, callbackFunc, callbackData, handle}); + const uint32_t handle = g_mpeg_stub_state.nextCallbackHandle++; + g_mpeg_stub_state.callbacksByMpeg[mpegAddr].push_back( + MpegRegisteredCallback{callbackType, callbackFunc, callbackData, handle}); - setReturnU32(ctx, handle); -} + setReturnU32(ctx, handle); + } -void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnU32(ctx, 0u); -} + void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + setReturnU32(ctx, 0u); + } -void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)ctx; - (void)runtime; - static const uint32_t kRefGlobalAddrs[] = { - 0x171800u, 0x17180Cu, 0x171818u, 0x171804u, 0x171810u, 0x17181Cu - }; - for (uint32_t addr : kRefGlobalAddrs) + void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint8_t *p = getMemPtr(rdram, addr); - if (!p) - continue; - uint32_t ptr = *reinterpret_cast(p); - if (ptr != 0u) + (void)ctx; + (void)runtime; + static const uint32_t kRefGlobalAddrs[] = { + 0x171800u, 0x17180Cu, 0x171818u, 0x171804u, 0x171810u, 0x17181Cu}; + for (uint32_t addr : kRefGlobalAddrs) { - uint8_t *q = getMemPtr(rdram, ptr + 0x28u); - if (q) - *reinterpret_cast(q) = 0u; + uint8_t *p = getMemPtr(rdram, addr); + if (!p) + continue; + uint32_t ptr = *reinterpret_cast(p); + if (ptr != 0u) + { + uint8_t *q = getMemPtr(rdram, ptr + 0x28u); + if (q) + *reinterpret_cast(q) = 0u; + } } + setReturnU32(ctx, 1u); } - setReturnU32(ctx, 1u); -} -static void mpegGuestWrite32(uint8_t *rdram, uint32_t addr, uint32_t value) -{ - if (uint8_t *p = getMemPtr(rdram, addr)) - *reinterpret_cast(p) = value; -} -static void mpegGuestWrite64(uint8_t *rdram, uint32_t addr, uint64_t value) -{ - if (uint8_t *p = getMemPtr(rdram, addr)) + static void mpegGuestWrite32(uint8_t *rdram, uint32_t addr, uint32_t value) { - *reinterpret_cast(p) = static_cast(value); - *reinterpret_cast(p + 4) = static_cast(value >> 32); + if (uint8_t *p = getMemPtr(rdram, addr)) + *reinterpret_cast(p) = value; + } + static void mpegGuestWrite64(uint8_t *rdram, uint32_t addr, uint64_t value) + { + if (uint8_t *p = getMemPtr(rdram, addr)) + { + *reinterpret_cast(p) = static_cast(value); + *reinterpret_cast(p + 4) = static_cast(value >> 32); + } } -} -void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t param_1 = getRegU32(ctx, 4); // a0 - const uint32_t param_2 = getRegU32(ctx, 5); // a1 - const uint32_t param_3 = getRegU32(ctx, 6); // a2 + void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t param_1 = getRegU32(ctx, 4); // a0 + const uint32_t param_2 = getRegU32(ctx, 5); // a1 + const uint32_t param_3 = getRegU32(ctx, 6); // a2 - const uint32_t uVar3 = (param_2 + 3u) & 0xFFFFFFFCu; - const int32_t iVar2_signed = static_cast(param_3) - static_cast(uVar3 - param_2); + const uint32_t uVar3 = (param_2 + 3u) & 0xFFFFFFFCu; + const int32_t iVar2_signed = static_cast(param_3) - static_cast(uVar3 - param_2); - if (iVar2_signed <= 0x117) - { - setReturnU32(ctx, 0u); - return; + if (iVar2_signed <= 0x117) + { + setReturnU32(ctx, 0u); + return; + } + + { + std::lock_guard lock(g_mpeg_stub_mutex); + getPlaybackState(param_1) = {}; + } + + const uint32_t puVar4 = uVar3 + 0x108u; + const uint32_t innerSize = static_cast(iVar2_signed) - 0x118u; + + mpegGuestWrite32(rdram, param_1 + 0x40, uVar3); + + const uint32_t a1_init = uVar3 + 0x118u; + mpegGuestWrite32(rdram, puVar4 + 0x0, a1_init); + mpegGuestWrite32(rdram, puVar4 + 0x4, innerSize); + mpegGuestWrite32(rdram, puVar4 + 0x8, a1_init); + mpegGuestWrite32(rdram, puVar4 + 0xC, a1_init); + + const uint32_t allocResult = runtime ? runtime->guestMalloc(0x600, 8u) : (uVar3 + 0x200u); + mpegGuestWrite32(rdram, uVar3 + 0x44, allocResult); + + // param_1[0..2] = 0; param_1[4..0xe] = 0xffffffff/0 as per decompilation + mpegGuestWrite32(rdram, param_1 + 0x00, 0); + mpegGuestWrite32(rdram, param_1 + 0x04, 0); + mpegGuestWrite32(rdram, param_1 + 0x08, 0); + mpegGuestWrite64(rdram, param_1 + 0x10, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x18, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x20, 0); + mpegGuestWrite64(rdram, param_1 + 0x28, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x30, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite64(rdram, param_1 + 0x38, 0); + + static const unsigned s_zeroOffsets[] = { + 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0, 0xE4, 0xE8, 0xF8, + 0x0C, 0x14, 0x2C, 0x34, 0x3C, + 0x48, 0xFC, 0x100, 0x104, 0x70, 0x90, 0xAC}; + for (unsigned off : s_zeroOffsets) + mpegGuestWrite32(rdram, uVar3 + off, 0u); + mpegGuestWrite64(rdram, uVar3 + 0x78, 0); + mpegGuestWrite64(rdram, uVar3 + 0x88, 0); + + mpegGuestWrite64(rdram, uVar3 + 0xF0, 0xFFFFFFFFFFFFFFFFULL); + mpegGuestWrite32(rdram, uVar3 + 0x1C, 0x1209F8u); + mpegGuestWrite32(rdram, uVar3 + 0x24, 0x120A08u); + mpegGuestWrite32(rdram, uVar3 + 0xB0, 1u); + mpegGuestWrite32(rdram, uVar3 + 0x9C, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, uVar3 + 0x80, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, uVar3 + 0x94, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, uVar3 + 0x98, 0xFFFFFFFFu); + + mpegGuestWrite32(rdram, 0x1717BCu, param_1); + + static const uint32_t s_refValues[] = { + 0x171A50u, 0x171C58u, 0x171CC0u, 0x171D28u, 0x171D90u, + 0x171AB8u, 0x171B20u, 0x171B88u, 0x171BF0u}; + for (unsigned i = 0; i < 9u; ++i) + mpegGuestWrite32(rdram, 0x171800u + i * 4u, s_refValues[i]); + + uint32_t setDynamicRet = a1_init; + if (uint8_t *p = getMemPtr(rdram, puVar4 + 8)) + setDynamicRet = *reinterpret_cast(p); + mpegGuestWrite32(rdram, puVar4 + 12, setDynamicRet); + + setReturnU32(ctx, setDynamicRet); } + void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + (void)rdram; + (void)runtime; + + const uint32_t mpegAddr = getRegU32(ctx, 4); std::lock_guard lock(g_mpeg_stub_mutex); - getPlaybackState(param_1) = {}; + g_mpeg_stub_state.callbacksByMpeg.erase(mpegAddr); + g_mpeg_stub_state.playbackByMpeg.erase(mpegAddr); + setReturnU32(ctx, 0u); } - const uint32_t puVar4 = uVar3 + 0x108u; - const uint32_t innerSize = static_cast(iVar2_signed) - 0x118u; - - mpegGuestWrite32(rdram, param_1 + 0x40, uVar3); - - const uint32_t a1_init = uVar3 + 0x118u; - mpegGuestWrite32(rdram, puVar4 + 0x0, a1_init); - mpegGuestWrite32(rdram, puVar4 + 0x4, innerSize); - mpegGuestWrite32(rdram, puVar4 + 0x8, a1_init); - mpegGuestWrite32(rdram, puVar4 + 0xC, a1_init); - - const uint32_t allocResult = runtime ? runtime->guestMalloc(0x600, 8u) : (uVar3 + 0x200u); - mpegGuestWrite32(rdram, uVar3 + 0x44, allocResult); - - // param_1[0..2] = 0; param_1[4..0xe] = 0xffffffff/0 as per decompilation - mpegGuestWrite32(rdram, param_1 + 0x00, 0); - mpegGuestWrite32(rdram, param_1 + 0x04, 0); - mpegGuestWrite32(rdram, param_1 + 0x08, 0); - mpegGuestWrite64(rdram, param_1 + 0x10, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x18, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x20, 0); - mpegGuestWrite64(rdram, param_1 + 0x28, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x30, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite64(rdram, param_1 + 0x38, 0); - - static const unsigned s_zeroOffsets[] = { - 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0, 0xE4, 0xE8, 0xF8, - 0x0C, 0x14, 0x2C, 0x34, 0x3C, - 0x48, 0xFC, 0x100, 0x104, 0x70, 0x90, 0xAC - }; - for (unsigned off : s_zeroOffsets) - mpegGuestWrite32(rdram, uVar3 + off, 0u); - mpegGuestWrite64(rdram, uVar3 + 0x78, 0); - mpegGuestWrite64(rdram, uVar3 + 0x88, 0); - - mpegGuestWrite64(rdram, uVar3 + 0xF0, 0xFFFFFFFFFFFFFFFFULL); - mpegGuestWrite32(rdram, uVar3 + 0x1C, 0x1209F8u); - mpegGuestWrite32(rdram, uVar3 + 0x24, 0x120A08u); - mpegGuestWrite32(rdram, uVar3 + 0xB0, 1u); - mpegGuestWrite32(rdram, uVar3 + 0x9C, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, uVar3 + 0x80, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, uVar3 + 0x94, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, uVar3 + 0x98, 0xFFFFFFFFu); - - mpegGuestWrite32(rdram, 0x1717BCu, param_1); - - static const uint32_t s_refValues[] = { - 0x171A50u, 0x171C58u, 0x171CC0u, 0x171D28u, 0x171D90u, - 0x171AB8u, 0x171B20u, 0x171B88u, 0x171BF0u - }; - for (unsigned i = 0; i < 9u; ++i) - mpegGuestWrite32(rdram, 0x171800u + i * 4u, s_refValues[i]); - - uint32_t setDynamicRet = a1_init; - if (uint8_t *p = getMemPtr(rdram, puVar4 + 8)) - setDynamicRet = *reinterpret_cast(p); - mpegGuestWrite32(rdram, puVar4 + 12, setDynamicRet); - - setReturnU32(ctx, setDynamicRet); -} - -void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - - const uint32_t mpegAddr = getRegU32(ctx, 4); - std::lock_guard lock(g_mpeg_stub_mutex); - g_mpeg_stub_state.callbacksByMpeg.erase(mpegAddr); - g_mpeg_stub_state.playbackByMpeg.erase(mpegAddr); - setReturnU32(ctx, 0u); -} - -void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime); -} - -void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; + void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime); + } - const uint32_t availableBytes = getRegU32(ctx, 6); - setReturnS32(ctx, static_cast(availableBytes)); -} + void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; -void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime); -} + const uint32_t availableBytes = getRegU32(ctx, 6); + setReturnS32(ctx, static_cast(availableBytes)); + } -void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime); -} + void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime); + } -void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime); -} + void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime); + } -void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime); -} + void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime); + } -void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime); -} + void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime); + } -void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t mpegAddr = getRegU32(ctx, 4); - uint32_t picturesServed = 0u; - PS2MpegCompatLayout compat{}; + void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mpeg_stub_mutex); - MpegPlaybackState &playback = getPlaybackState(mpegAddr); - mpegGuestWrite32(rdram, mpegAddr + 0x00u, kStubMovieWidth); - mpegGuestWrite32(rdram, mpegAddr + 0x04u, kStubMovieHeight); - mpegGuestWrite32(rdram, mpegAddr + 0x08u, playback.picturesServed); - picturesServed = playback.picturesServed; - compat = g_mpeg_stub_state.compat; - playback.picturesServed += 1u; + TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime); } - if (uint8_t *base = getMemPtr(rdram, mpegAddr)) + void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const uint32_t iVar1 = *reinterpret_cast(base + 0x40); - if (uint8_t *inner = getMemPtr(rdram, iVar1)) + (void)runtime; + const uint32_t mpegAddr = getRegU32(ctx, 4); + uint32_t picturesServed = 0u; + PS2MpegCompatLayout compat{}; { - *reinterpret_cast(inner + 0xb0) = 1; - *reinterpret_cast(inner + 0xd8) = (getRegU32(ctx, 5) & 0x0FFFFFFFu) | 0x20000000u; - *reinterpret_cast(inner + 0xe4) = getRegU32(ctx, 6); - *reinterpret_cast(inner + 0xdc) = 0; - *reinterpret_cast(inner + 0xe0) = 0; + std::lock_guard lock(g_mpeg_stub_mutex); + MpegPlaybackState &playback = getPlaybackState(mpegAddr); + mpegGuestWrite32(rdram, mpegAddr + 0x00u, kStubMovieWidth); + mpegGuestWrite32(rdram, mpegAddr + 0x04u, kStubMovieHeight); + mpegGuestWrite32(rdram, mpegAddr + 0x08u, playback.picturesServed); + picturesServed = playback.picturesServed; + compat = g_mpeg_stub_state.compat; + playback.picturesServed += 1u; } - } - if (compat.matchesMpegObject(mpegAddr) && - compat.hasFinishTargets() && - (picturesServed + 1u) >= mpegCompatSyntheticFrames(compat)) - { - // No decoder yet: synthesize a safe frame so the guest can - // initialize its movie presentation path, then mark playback finished. - if (compat.videoStateAddr != 0u) + if (uint8_t *base = getMemPtr(rdram, mpegAddr)) { - mpegGuestWrite32(rdram, compat.videoStateAddr, compat.finishedVideoStateValue); + const uint32_t iVar1 = *reinterpret_cast(base + 0x40); + if (uint8_t *inner = getMemPtr(rdram, iVar1)) + { + *reinterpret_cast(inner + 0xb0) = 1; + *reinterpret_cast(inner + 0xd8) = (getRegU32(ctx, 5) & 0x0FFFFFFFu) | 0x20000000u; + *reinterpret_cast(inner + 0xe4) = getRegU32(ctx, 6); + *reinterpret_cast(inner + 0xdc) = 0; + *reinterpret_cast(inner + 0xe0) = 0; + } } - if (compat.movieStateAddr != 0u) + + if (compat.matchesMpegObject(mpegAddr) && + compat.hasFinishTargets() && + (picturesServed + 1u) >= mpegCompatSyntheticFrames(compat)) { - mpegGuestWrite32(rdram, compat.movieStateAddr, compat.finishedMovieStateValue); + // No decoder yet: synthesize a safe frame so the guest can + // initialize its movie presentation path, then mark playback finished. + if (compat.videoStateAddr != 0u) + { + mpegGuestWrite32(rdram, compat.videoStateAddr, compat.finishedVideoStateValue); + } + if (compat.movieStateAddr != 0u) + { + mpegGuestWrite32(rdram, compat.movieStateAddr, compat.finishedMovieStateValue); + } } - } - setReturnU32(ctx, 0u); -} + setReturnU32(ctx, 0u); + } -void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime); -} + void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime); + } -void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime); -} + void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime); + } -void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; + void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; - std::lock_guard lock(g_mpeg_stub_mutex); - resetMpegStubStateUnlocked(); - g_mpeg_stub_state.initialized = true; - setReturnU32(ctx, 0u); -} + std::lock_guard lock(g_mpeg_stub_mutex); + resetMpegStubStateUnlocked(); + g_mpeg_stub_state.initialized = true; + setReturnU32(ctx, 0u); + } -void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - const uint32_t mpegAddr = getRegU32(ctx, 4); - - std::lock_guard lock(g_mpeg_stub_mutex); - g_mpeg_stub_state.initialized = true; - const MpegPlaybackState &playback = getPlaybackState(mpegAddr); - if (g_mpeg_stub_state.compat.matchesMpegObject(mpegAddr)) + void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, playback.picturesServed >= mpegCompatSyntheticFrames(g_mpeg_stub_state.compat) ? 1 : 0); - return; - } + (void)rdram; + (void)runtime; + const uint32_t mpegAddr = getRegU32(ctx, 4); - // Generic fallback: keep decode threads alive until a game-specific path - // decides to stop playback. - setReturnS32(ctx, 0); -} + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.initialized = true; + const MpegPlaybackState &playback = getPlaybackState(mpegAddr); + if (g_mpeg_stub_state.compat.matchesMpegObject(mpegAddr)) + { + setReturnS32(ctx, playback.picturesServed >= mpegCompatSyntheticFrames(g_mpeg_stub_state.compat) ? 1 : 0); + return; + } -void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime); -} + // Generic fallback: keep decode threads alive until a game-specific path + // decides to stop playback. + setReturnS32(ctx, 0); + } -void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t param_1 = getRegU32(ctx, 4); + void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mpeg_stub_mutex); - g_mpeg_stub_state.playbackByMpeg[param_1] = {}; + TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime); } - uint8_t *base = getMemPtr(rdram, param_1); - if (!base) + + void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - return; + (void)runtime; + const uint32_t param_1 = getRegU32(ctx, 4); + { + std::lock_guard lock(g_mpeg_stub_mutex); + g_mpeg_stub_state.playbackByMpeg[param_1] = {}; + } + uint8_t *base = getMemPtr(rdram, param_1); + if (!base) + { + return; + } + uint32_t inner = *reinterpret_cast(base + 0x40); + if (inner == 0u) + return; + mpegGuestWrite32(rdram, param_1 + 0x00u, 0u); + mpegGuestWrite32(rdram, param_1 + 0x04u, 0u); + mpegGuestWrite32(rdram, param_1 + 0x08u, 0u); + mpegGuestWrite32(rdram, inner + 0x00, 0u); + mpegGuestWrite32(rdram, inner + 0x04, 0u); + mpegGuestWrite32(rdram, inner + 0x08, 0u); + mpegGuestWrite32(rdram, param_1 + 0x08, 0u); + mpegGuestWrite32(rdram, inner + 0x80, 0xFFFFFFFFu); + mpegGuestWrite32(rdram, inner + 0xAC, 0u); + mpegGuestWrite32(rdram, 0x171904u, 0u); } - uint32_t inner = *reinterpret_cast(base + 0x40); - if (inner == 0u) - return; - mpegGuestWrite32(rdram, param_1 + 0x00u, 0u); - mpegGuestWrite32(rdram, param_1 + 0x04u, 0u); - mpegGuestWrite32(rdram, param_1 + 0x08u, 0u); - mpegGuestWrite32(rdram, inner + 0x00, 0u); - mpegGuestWrite32(rdram, inner + 0x04, 0u); - mpegGuestWrite32(rdram, inner + 0x08, 0u); - mpegGuestWrite32(rdram, param_1 + 0x08, 0u); - mpegGuestWrite32(rdram, inner + 0x80, 0xFFFFFFFFu); - mpegGuestWrite32(rdram, inner + 0xAC, 0u); - mpegGuestWrite32(rdram, 0x171904u, 0u); -} -void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime); -} + void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime); + } -void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime); -} + void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime); + } -void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime); -} + void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime); + } -void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime); -} + void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp index 8a741f84..224ba77c 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp @@ -3,1442 +3,1441 @@ namespace ps2_stubs { -namespace -{ - constexpr int32_t kMcCmdGetInfo = 0x01; - constexpr int32_t kMcCmdOpen = 0x02; - constexpr int32_t kMcCmdClose = 0x03; - constexpr int32_t kMcCmdSeek = 0x04; - constexpr int32_t kMcCmdRead = 0x05; - constexpr int32_t kMcCmdWrite = 0x06; - constexpr int32_t kMcCmdFlush = 0x0A; - constexpr int32_t kMcCmdMkdir = 0x0B; - constexpr int32_t kMcCmdChdir = 0x0C; - constexpr int32_t kMcCmdGetDir = 0x0D; - constexpr int32_t kMcCmdSetFileInfo = 0x0E; - constexpr int32_t kMcCmdDelete = 0x0F; - constexpr int32_t kMcCmdFormat = 0x10; - constexpr int32_t kMcCmdUnformat = 0x11; - constexpr int32_t kMcCmdGetEntSpace = 0x12; - constexpr int32_t kMcCmdRename = 0x13; - - constexpr int32_t kMcResultSucceed = 0; - constexpr int32_t kMcResultChangedCard = -1; - constexpr int32_t kMcResultNoFormat = -2; - constexpr int32_t kMcResultNoEntry = -4; - constexpr int32_t kMcResultDeniedPermit = -5; - constexpr int32_t kMcResultNotEmpty = -6; - constexpr int32_t kMcResultUpLimitHandle = -7; - - constexpr int32_t kMcTypePs2 = 2; - constexpr int32_t kMcFormatted = 1; - constexpr int32_t kMcUnformatted = 0; - constexpr int32_t kMcFreeClusters = 0x2000; - constexpr size_t kMcMaxPathLen = 1024; - constexpr size_t kMcMaxOpenFiles = 32; - - constexpr uint16_t kMcAttrReadable = 0x0001; - constexpr uint16_t kMcAttrWriteable = 0x0002; - constexpr uint16_t kMcAttrFile = 0x0010; - constexpr uint16_t kMcAttrSubdir = 0x0020; - constexpr uint16_t kMcAttrClosed = 0x0080; - constexpr uint16_t kMcAttrExists = 0x8000; - - struct SceMcStDateTime - { - uint8_t Resv2 = 0; - uint8_t Sec = 0; - uint8_t Min = 0; - uint8_t Hour = 0; - uint8_t Day = 0; - uint8_t Month = 0; - uint16_t Year = 0; - }; - - struct SceMcTblGetDir - { - SceMcStDateTime _Create{}; - SceMcStDateTime _Modify{}; - uint32_t FileSizeByte = 0; - uint16_t AttrFile = 0; - uint16_t Reserve1 = 0; - uint32_t Reserve2 = 0; - uint32_t PdaAplNo = 0; - char EntryName[32]{}; - }; - - static_assert(sizeof(SceMcTblGetDir) == 64, "sceMcTblGetDir size mismatch"); - - struct McOpenFile - { - FILE *file = nullptr; - int32_t port = 0; - std::filesystem::path hostPath; - }; - - struct McPortState + namespace { - std::string currentDir = "/"; - bool formatted = true; - }; - - std::mutex g_mcStateMutex; - int32_t g_mcNextFd = 1; - int32_t g_mcLastCmd = 0; - int32_t g_mcLastResult = 0; - std::unordered_map g_mcFiles; - std::array g_mcPorts{}; - int32_t g_cvMcFileCursor = 0; - constexpr int32_t kCvMcFreeCapacityBytes = 0x01000000; - constexpr int32_t kCvMcSaveCapacityBytes = 0x00080000; - constexpr int32_t kCvMcConfigCapacityBytes = 0x00008000; - constexpr int32_t kCvMcIconCapacityBytes = 0x00004000; + constexpr int32_t kMcCmdGetInfo = 0x01; + constexpr int32_t kMcCmdOpen = 0x02; + constexpr int32_t kMcCmdClose = 0x03; + constexpr int32_t kMcCmdSeek = 0x04; + constexpr int32_t kMcCmdRead = 0x05; + constexpr int32_t kMcCmdWrite = 0x06; + constexpr int32_t kMcCmdFlush = 0x0A; + constexpr int32_t kMcCmdMkdir = 0x0B; + constexpr int32_t kMcCmdChdir = 0x0C; + constexpr int32_t kMcCmdGetDir = 0x0D; + constexpr int32_t kMcCmdSetFileInfo = 0x0E; + constexpr int32_t kMcCmdDelete = 0x0F; + constexpr int32_t kMcCmdFormat = 0x10; + constexpr int32_t kMcCmdUnformat = 0x11; + constexpr int32_t kMcCmdGetEntSpace = 0x12; + constexpr int32_t kMcCmdRename = 0x13; + + constexpr int32_t kMcResultSucceed = 0; + constexpr int32_t kMcResultChangedCard = -1; + constexpr int32_t kMcResultNoFormat = -2; + constexpr int32_t kMcResultNoEntry = -4; + constexpr int32_t kMcResultDeniedPermit = -5; + constexpr int32_t kMcResultNotEmpty = -6; + constexpr int32_t kMcResultUpLimitHandle = -7; + + constexpr int32_t kMcTypePs2 = 2; + constexpr int32_t kMcFormatted = 1; + constexpr int32_t kMcUnformatted = 0; + constexpr int32_t kMcFreeClusters = 0x2000; + constexpr size_t kMcMaxPathLen = 1024; + constexpr size_t kMcMaxOpenFiles = 32; + + constexpr uint16_t kMcAttrReadable = 0x0001; + constexpr uint16_t kMcAttrWriteable = 0x0002; + constexpr uint16_t kMcAttrFile = 0x0010; + constexpr uint16_t kMcAttrSubdir = 0x0020; + constexpr uint16_t kMcAttrClosed = 0x0080; + constexpr uint16_t kMcAttrExists = 0x8000; + + struct SceMcStDateTime + { + uint8_t Resv2 = 0; + uint8_t Sec = 0; + uint8_t Min = 0; + uint8_t Hour = 0; + uint8_t Day = 0; + uint8_t Month = 0; + uint16_t Year = 0; + }; + + struct SceMcTblGetDir + { + SceMcStDateTime _Create{}; + SceMcStDateTime _Modify{}; + uint32_t FileSizeByte = 0; + uint16_t AttrFile = 0; + uint16_t Reserve1 = 0; + uint32_t Reserve2 = 0; + uint32_t PdaAplNo = 0; + char EntryName[32]{}; + }; + + static_assert(sizeof(SceMcTblGetDir) == 64, "sceMcTblGetDir size mismatch"); + + struct McOpenFile + { + FILE *file = nullptr; + int32_t port = 0; + std::filesystem::path hostPath; + }; - bool isValidMcPortSlot(int32_t port, int32_t slot) - { - return port >= 0 && port < static_cast(g_mcPorts.size()) && slot == 0; - } + struct McPortState + { + std::string currentDir = "/"; + bool formatted = true; + }; + + std::mutex g_mcStateMutex; + int32_t g_mcNextFd = 1; + int32_t g_mcLastCmd = 0; + int32_t g_mcLastResult = 0; + std::unordered_map g_mcFiles; + std::array g_mcPorts{}; + int32_t g_cvMcFileCursor = 0; + constexpr int32_t kCvMcFreeCapacityBytes = 0x01000000; + constexpr int32_t kCvMcSaveCapacityBytes = 0x00080000; + constexpr int32_t kCvMcConfigCapacityBytes = 0x00008000; + constexpr int32_t kCvMcIconCapacityBytes = 0x00004000; + + bool isValidMcPortSlot(int32_t port, int32_t slot) + { + return port >= 0 && port < static_cast(g_mcPorts.size()) && slot == 0; + } - std::filesystem::path getMcRootPath(int32_t port) - { - const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - std::filesystem::path root = paths.mcRoot; - if (root.empty()) + std::filesystem::path getMcRootPath(int32_t port) { - if (!paths.elfDirectory.empty()) + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + std::filesystem::path root = paths.mcRoot; + if (root.empty()) + { + if (!paths.elfDirectory.empty()) + { + root = paths.elfDirectory / "mc0"; + } + else + { + std::error_code ec; + const std::filesystem::path cwd = std::filesystem::current_path(ec); + root = ec ? std::filesystem::path("mc0") : (cwd / "mc0"); + } + } + + root = root.lexically_normal(); + if (port <= 0) { - root = paths.elfDirectory / "mc0"; + return root; } - else + + const std::filesystem::path parent = root.parent_path(); + const std::string leaf = root.filename().string(); + const std::string lowerLeaf = toLowerAscii(leaf); + if (lowerLeaf == "mc0") { - std::error_code ec; - const std::filesystem::path cwd = std::filesystem::current_path(ec); - root = ec ? std::filesystem::path("mc0") : (cwd / "mc0"); + return (parent / "mc1").lexically_normal(); + } + if (leaf.empty()) + { + return (root / "mc1").lexically_normal(); } - } - root = root.lexically_normal(); - if (port <= 0) - { - return root; + return (parent / (leaf + "_slot" + std::to_string(port))).lexically_normal(); } - const std::filesystem::path parent = root.parent_path(); - const std::string leaf = root.filename().string(); - const std::string lowerLeaf = toLowerAscii(leaf); - if (lowerLeaf == "mc0") + void ensureMcRootExists(int32_t port) { - return (parent / "mc1").lexically_normal(); - } - if (leaf.empty()) - { - return (root / "mc1").lexically_normal(); + std::error_code ec; + std::filesystem::create_directories(getMcRootPath(port), ec); } - return (parent / (leaf + "_slot" + std::to_string(port))).lexically_normal(); - } - - void ensureMcRootExists(int32_t port) - { - std::error_code ec; - std::filesystem::create_directories(getMcRootPath(port), ec); - } - - std::vector splitMcPathComponents(const std::string &value) - { - std::vector parts; - std::string current; - for (char c : value) + std::vector splitMcPathComponents(const std::string &value) { - if (c == '/' || c == '\\') + std::vector parts; + std::string current; + for (char c : value) { - if (!current.empty()) + if (c == '/' || c == '\\') + { + if (!current.empty()) + { + parts.push_back(current); + current.clear(); + } + } + else { - parts.push_back(current); - current.clear(); + current.push_back(c); } } - else + + if (!current.empty()) { - current.push_back(c); + parts.push_back(current); } - } - - if (!current.empty()) - { - parts.push_back(current); - } - return parts; - } - - std::string joinMcPathComponents(const std::vector &parts) - { - if (parts.empty()) - { - return "/"; + return parts; } - std::string joined = "/"; - for (size_t i = 0; i < parts.size(); ++i) + std::string joinMcPathComponents(const std::vector &parts) { - if (i != 0u) + if (parts.empty()) { - joined.push_back('/'); + return "/"; } - joined.append(parts[i]); - } - return joined; - } - std::string normalizeGuestMcPathLocked(int32_t port, std::string path) - { - std::replace(path.begin(), path.end(), '\\', '/'); - const std::string lower = toLowerAscii(path); - if (lower.rfind("mc0:", 0) == 0 || lower.rfind("mc1:", 0) == 0) - { - path = path.substr(4); + std::string joined = "/"; + for (size_t i = 0; i < parts.size(); ++i) + { + if (i != 0u) + { + joined.push_back('/'); + } + joined.append(parts[i]); + } + return joined; } - const bool absolute = !path.empty() && path.front() == '/'; - std::vector parts; - if (!absolute && port >= 0 && port < static_cast(g_mcPorts.size())) + std::string normalizeGuestMcPathLocked(int32_t port, std::string path) { - parts = splitMcPathComponents(g_mcPorts[static_cast(port)].currentDir); - } + std::replace(path.begin(), path.end(), '\\', '/'); + const std::string lower = toLowerAscii(path); + if (lower.rfind("mc0:", 0) == 0 || lower.rfind("mc1:", 0) == 0) + { + path = path.substr(4); + } - for (const std::string &part : splitMcPathComponents(path)) - { - if (part.empty() || part == ".") + const bool absolute = !path.empty() && path.front() == '/'; + std::vector parts; + if (!absolute && port >= 0 && port < static_cast(g_mcPorts.size())) { - continue; + parts = splitMcPathComponents(g_mcPorts[static_cast(port)].currentDir); } - if (part == "..") + for (const std::string &part : splitMcPathComponents(path)) { - if (!parts.empty()) + if (part.empty() || part == ".") { - parts.pop_back(); + continue; } - continue; + + if (part == "..") + { + if (!parts.empty()) + { + parts.pop_back(); + } + continue; + } + + parts.push_back(part); } - parts.push_back(part); + return joinMcPathComponents(parts); } - return joinMcPathComponents(parts); - } - - std::filesystem::path guestMcPathToHostPath(int32_t port, const std::string &guestPath) - { - std::filesystem::path resolved = getMcRootPath(port); - if (guestPath.size() > 1u) + std::filesystem::path guestMcPathToHostPath(int32_t port, const std::string &guestPath) { - resolved /= std::filesystem::path(guestPath.substr(1)); + std::filesystem::path resolved = getMcRootPath(port); + if (guestPath.size() > 1u) + { + resolved /= std::filesystem::path(guestPath.substr(1)); + } + return resolved.lexically_normal(); } - return resolved.lexically_normal(); - } - bool localtimeSafeMc(const std::time_t *value, std::tm *out) - { + bool localtimeSafeMc(const std::time_t *value, std::tm *out) + { #ifdef _WIN32 - return localtime_s(out, value) == 0; + return localtime_s(out, value) == 0; #else - return localtime_r(value, out) != nullptr; + return localtime_r(value, out) != nullptr; #endif - } - - std::time_t fileTimeToTimeTMc(std::filesystem::file_time_type value) - { - const auto systemTime = std::chrono::time_point_cast( - value - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); - return std::chrono::system_clock::to_time_t(systemTime); - } - - void writeMcCString(uint8_t *rdram, uint32_t addr, const std::string &value) - { - if (addr == 0u) - { - return; - } - - uint8_t *dst = getMemPtr(rdram, addr); - if (!dst) - { - return; } - std::memcpy(dst, value.c_str(), value.size() + 1u); - } - - void writeMcDateTime(SceMcStDateTime &out, std::time_t value) - { - std::tm tm{}; - if (!localtimeSafeMc(&value, &tm)) + std::time_t fileTimeToTimeTMc(std::filesystem::file_time_type value) { - std::memset(&out, 0, sizeof(out)); - return; + const auto systemTime = std::chrono::time_point_cast( + value - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); + return std::chrono::system_clock::to_time_t(systemTime); } - out.Resv2 = 0; - out.Sec = static_cast(tm.tm_sec); - out.Min = static_cast(tm.tm_min); - out.Hour = static_cast(tm.tm_hour); - out.Day = static_cast(tm.tm_mday); - out.Month = static_cast(tm.tm_mon + 1); - out.Year = static_cast(tm.tm_year + 1900); - } - - void fillMcDirTableEntry(SceMcTblGetDir &entry, - const std::string &name, - bool isDirectory, - uint32_t sizeBytes, - std::time_t modifiedTime) - { - std::memset(&entry, 0, sizeof(entry)); - writeMcDateTime(entry._Create, modifiedTime); - writeMcDateTime(entry._Modify, modifiedTime); - entry.FileSizeByte = isDirectory ? 0u : sizeBytes; - entry.AttrFile = static_cast(kMcAttrReadable | - kMcAttrWriteable | - (isDirectory ? kMcAttrSubdir : kMcAttrFile) | - kMcAttrClosed | - kMcAttrExists); - std::strncpy(entry.EntryName, name.c_str(), sizeof(entry.EntryName) - 1u); - entry.EntryName[sizeof(entry.EntryName) - 1u] = '\0'; - } - - bool wildcardMatch(const std::string &pattern, const std::string &value) - { - size_t patternPos = 0u; - size_t valuePos = 0u; - size_t starPos = std::string::npos; - size_t matchPos = 0u; - - while (valuePos < value.size()) + void writeMcCString(uint8_t *rdram, uint32_t addr, const std::string &value) { - if (patternPos < pattern.size() && - (pattern[patternPos] == '?' || pattern[patternPos] == value[valuePos])) - { - ++patternPos; - ++valuePos; - } - else if (patternPos < pattern.size() && pattern[patternPos] == '*') - { - starPos = patternPos++; - matchPos = valuePos; - } - else if (starPos != std::string::npos) + if (addr == 0u) { - patternPos = starPos + 1u; - valuePos = ++matchPos; + return; } - else + + uint8_t *dst = getMemPtr(rdram, addr); + if (!dst) { - return false; + return; } - } - while (patternPos < pattern.size() && pattern[patternPos] == '*') - { - ++patternPos; + std::memcpy(dst, value.c_str(), value.size() + 1u); } - return patternPos == pattern.size(); - } - - void setMcCommandResultLocked(int32_t cmd, int32_t result) - { - g_mcLastCmd = cmd; - g_mcLastResult = result; - } - - void closeMcFilesLocked() - { - for (auto &[fd, openFile] : g_mcFiles) + void writeMcDateTime(SceMcStDateTime &out, std::time_t value) { - if (openFile.file) + std::tm tm{}; + if (!localtimeSafeMc(&value, &tm)) { - std::fclose(openFile.file); - openFile.file = nullptr; + std::memset(&out, 0, sizeof(out)); + return; } + + out.Resv2 = 0; + out.Sec = static_cast(tm.tm_sec); + out.Min = static_cast(tm.tm_min); + out.Hour = static_cast(tm.tm_hour); + out.Day = static_cast(tm.tm_mday); + out.Month = static_cast(tm.tm_mon + 1); + out.Year = static_cast(tm.tm_year + 1900); } - g_mcFiles.clear(); - } - void closeMcFilesForPortLocked(int32_t port) - { - for (auto it = g_mcFiles.begin(); it != g_mcFiles.end();) + void fillMcDirTableEntry(SceMcTblGetDir &entry, + const std::string &name, + bool isDirectory, + uint32_t sizeBytes, + std::time_t modifiedTime) + { + std::memset(&entry, 0, sizeof(entry)); + writeMcDateTime(entry._Create, modifiedTime); + writeMcDateTime(entry._Modify, modifiedTime); + entry.FileSizeByte = isDirectory ? 0u : sizeBytes; + entry.AttrFile = static_cast(kMcAttrReadable | + kMcAttrWriteable | + (isDirectory ? kMcAttrSubdir : kMcAttrFile) | + kMcAttrClosed | + kMcAttrExists); + std::strncpy(entry.EntryName, name.c_str(), sizeof(entry.EntryName) - 1u); + entry.EntryName[sizeof(entry.EntryName) - 1u] = '\0'; + } + + bool wildcardMatch(const std::string &pattern, const std::string &value) { - if (it->second.port == port) + size_t patternPos = 0u; + size_t valuePos = 0u; + size_t starPos = std::string::npos; + size_t matchPos = 0u; + + while (valuePos < value.size()) { - if (it->second.file) + if (patternPos < pattern.size() && + (pattern[patternPos] == '?' || pattern[patternPos] == value[valuePos])) + { + ++patternPos; + ++valuePos; + } + else if (patternPos < pattern.size() && pattern[patternPos] == '*') + { + starPos = patternPos++; + matchPos = valuePos; + } + else if (starPos != std::string::npos) + { + patternPos = starPos + 1u; + valuePos = ++matchPos; + } + else { - std::fclose(it->second.file); + return false; } - it = g_mcFiles.erase(it); } - else + + while (patternPos < pattern.size() && pattern[patternPos] == '*') { - ++it; + ++patternPos; } - } - } - int32_t allocateMcFdLocked(FILE *file, int32_t port, const std::filesystem::path &hostPath) - { - if (!file) - { - return kMcResultDeniedPermit; + return patternPos == pattern.size(); } - if (g_mcFiles.size() >= kMcMaxOpenFiles) + + void setMcCommandResultLocked(int32_t cmd, int32_t result) { - return kMcResultUpLimitHandle; + g_mcLastCmd = cmd; + g_mcLastResult = result; } - for (int attempt = 0; attempt < 0x10000; ++attempt) + void closeMcFilesLocked() { - if (g_mcNextFd <= 0) + for (auto &[fd, openFile] : g_mcFiles) { - g_mcNextFd = 1; + if (openFile.file) + { + std::fclose(openFile.file); + openFile.file = nullptr; + } } + g_mcFiles.clear(); + } - const int32_t fd = g_mcNextFd++; - if (g_mcFiles.find(fd) != g_mcFiles.end()) + void closeMcFilesForPortLocked(int32_t port) + { + for (auto it = g_mcFiles.begin(); it != g_mcFiles.end();) { - continue; + if (it->second.port == port) + { + if (it->second.file) + { + std::fclose(it->second.file); + } + it = g_mcFiles.erase(it); + } + else + { + ++it; + } } - - g_mcFiles.emplace(fd, McOpenFile{file, port, hostPath}); - return fd; } - return kMcResultUpLimitHandle; - } - - FILE *openMcHostFile(const std::filesystem::path &hostPath, uint32_t flags) - { - const uint32_t access = flags & PS2_FIO_O_RDWR; - const bool read = (access == PS2_FIO_O_RDONLY) || (access == PS2_FIO_O_RDWR); - const bool write = (access == PS2_FIO_O_WRONLY) || (access == PS2_FIO_O_RDWR); - const bool append = (flags & PS2_FIO_O_APPEND) != 0u; - const bool create = (flags & PS2_FIO_O_CREAT) != 0u; - const bool truncate = (flags & PS2_FIO_O_TRUNC) != 0u; - - std::error_code ec; - const bool exists = std::filesystem::exists(hostPath, ec) && !ec; - - const char *mode = "rb"; - if (read && write) + int32_t allocateMcFdLocked(FILE *file, int32_t port, const std::filesystem::path &hostPath) { - if (append) + if (!file) { - mode = exists ? "a+b" : "w+b"; + return kMcResultDeniedPermit; } - else if (truncate || (create && !exists)) + if (g_mcFiles.size() >= kMcMaxOpenFiles) { - mode = "w+b"; + return kMcResultUpLimitHandle; } - else + + for (int attempt = 0; attempt < 0x10000; ++attempt) { - mode = "r+b"; + if (g_mcNextFd <= 0) + { + g_mcNextFd = 1; + } + + const int32_t fd = g_mcNextFd++; + if (g_mcFiles.find(fd) != g_mcFiles.end()) + { + continue; + } + + g_mcFiles.emplace(fd, McOpenFile{file, port, hostPath}); + return fd; } + + return kMcResultUpLimitHandle; } - else if (write) + + FILE *openMcHostFile(const std::filesystem::path &hostPath, uint32_t flags) { - if (append) - { - mode = exists ? "ab" : "wb"; - } - else if (truncate || (create && !exists)) + const uint32_t access = flags & PS2_FIO_O_RDWR; + const bool read = (access == PS2_FIO_O_RDONLY) || (access == PS2_FIO_O_RDWR); + const bool write = (access == PS2_FIO_O_WRONLY) || (access == PS2_FIO_O_RDWR); + const bool append = (flags & PS2_FIO_O_APPEND) != 0u; + const bool create = (flags & PS2_FIO_O_CREAT) != 0u; + const bool truncate = (flags & PS2_FIO_O_TRUNC) != 0u; + + std::error_code ec; + const bool exists = std::filesystem::exists(hostPath, ec) && !ec; + + const char *mode = "rb"; + if (read && write) { - mode = "wb"; + if (append) + { + mode = exists ? "a+b" : "w+b"; + } + else if (truncate || (create && !exists)) + { + mode = "w+b"; + } + else + { + mode = "r+b"; + } } - else + else if (write) { - mode = "r+b"; + if (append) + { + mode = exists ? "ab" : "wb"; + } + else if (truncate || (create && !exists)) + { + mode = "wb"; + } + else + { + mode = "r+b"; + } } + + return std::fopen(hostPath.string().c_str(), mode); } + } - return std::fopen(hostPath.string().c_str(), mode); + void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); } -} -void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const uint32_t pathAddr = getRegU32(ctx, 6); + const uint32_t currentDirAddr = getRegU32(ctx, 7); + const std::string requestedDir = + (pathAddr != 0u) ? readPs2CStringBounded(rdram, pathAddr, kMcMaxPathLen) : std::string{}; -void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const uint32_t pathAddr = getRegU32(ctx, 6); - const uint32_t currentDirAddr = getRegU32(ctx, 7); - const std::string requestedDir = - (pathAddr != 0u) ? readPs2CStringBounded(rdram, pathAddr, kMcMaxPathLen) : std::string{}; - - std::string currentDir = "/"; - int32_t result = kMcResultNoEntry; - { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + std::string currentDir = "/"; + int32_t result = kMcResultNoEntry; { - McPortState &state = g_mcPorts[static_cast(port)]; - currentDir = state.currentDir; - if (!state.formatted) - { - result = kMcResultNoFormat; - } - else + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - ensureMcRootExists(port); - const std::string resolvedDir = - requestedDir.empty() ? state.currentDir : normalizeGuestMcPathLocked(port, requestedDir); - const std::filesystem::path hostDir = guestMcPathToHostPath(port, resolvedDir); - std::error_code ec; - if (std::filesystem::exists(hostDir, ec) && !ec && - std::filesystem::is_directory(hostDir, ec)) + McPortState &state = g_mcPorts[static_cast(port)]; + currentDir = state.currentDir; + if (!state.formatted) { - state.currentDir = resolvedDir; - currentDir = resolvedDir; - result = kMcResultSucceed; + result = kMcResultNoFormat; + } + else + { + ensureMcRootExists(port); + const std::string resolvedDir = + requestedDir.empty() ? state.currentDir : normalizeGuestMcPathLocked(port, requestedDir); + const std::filesystem::path hostDir = guestMcPathToHostPath(port, resolvedDir); + std::error_code ec; + if (std::filesystem::exists(hostDir, ec) && !ec && + std::filesystem::is_directory(hostDir, ec)) + { + state.currentDir = resolvedDir; + currentDir = resolvedDir; + result = kMcResultSucceed; + } } } + + setMcCommandResultLocked(kMcCmdChdir, result); } - setMcCommandResultLocked(kMcCmdChdir, result); + writeMcCString(rdram, currentDirAddr, currentDir); + setReturnS32(ctx, 0); } - writeMcCString(rdram, currentDirAddr, currentDir); - setReturnS32(ctx, 0); -} - -void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t fd = static_cast(getRegU32(ctx, 4)); - int32_t result = kMcResultNoEntry; + void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - auto it = g_mcFiles.find(fd); - if (it != g_mcFiles.end()) + const int32_t fd = static_cast(getRegU32(ctx, 4)); + int32_t result = kMcResultNoEntry; { - if (!it->second.file || std::fclose(it->second.file) == 0) + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (it != g_mcFiles.end()) { - result = kMcResultSucceed; + if (!it->second.file || std::fclose(it->second.file) == 0) + { + result = kMcResultSucceed; + } + g_mcFiles.erase(it); } - g_mcFiles.erase(it); + setMcCommandResultLocked(kMcCmdClose, result); } - setMcCommandResultLocked(kMcCmdClose, result); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - -void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - int32_t result = kMcResultNoEntry; + void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; { - McPortState &state = g_mcPorts[static_cast(port)]; - if (!state.formatted) - { - result = kMcResultNoFormat; - } - else + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - const std::string guestPath = normalizeGuestMcPathLocked(port, path); - if (guestPath != "/") + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) { - const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); - std::error_code ec; - if (std::filesystem::exists(hostPath, ec) && !ec) + result = kMcResultNoFormat; + } + else + { + const std::string guestPath = normalizeGuestMcPathLocked(port, path); + if (guestPath != "/") { - if (std::filesystem::is_directory(hostPath, ec) && - !std::filesystem::is_empty(hostPath, ec)) - { - result = kMcResultNotEmpty; - } - else if (std::filesystem::remove(hostPath, ec) && !ec) - { - result = kMcResultSucceed; - } - else + const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); + std::error_code ec; + if (std::filesystem::exists(hostPath, ec) && !ec) { - result = kMcResultDeniedPermit; + if (std::filesystem::is_directory(hostPath, ec) && + !std::filesystem::is_empty(hostPath, ec)) + { + result = kMcResultNotEmpty; + } + else if (std::filesystem::remove(hostPath, ec) && !ec) + { + result = kMcResultSucceed; + } + else + { + result = kMcResultDeniedPermit; + } } } } } - } - setMcCommandResultLocked(kMcCmdDelete, result); + setMcCommandResultLocked(kMcCmdDelete, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t fd = static_cast(getRegU32(ctx, 4)); - int32_t result = kMcResultNoEntry; + void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - auto it = g_mcFiles.find(fd); - if (it != g_mcFiles.end() && it->second.file) + const int32_t fd = static_cast(getRegU32(ctx, 4)); + int32_t result = kMcResultNoEntry; { - result = (std::fflush(it->second.file) == 0) ? kMcResultSucceed : kMcResultDeniedPermit; + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (it != g_mcFiles.end() && it->second.file) + { + result = (std::fflush(it->second.file) == 0) ? kMcResultSucceed : kMcResultDeniedPermit; + } + setMcCommandResultLocked(kMcCmdFlush, result); } - setMcCommandResultLocked(kMcCmdFlush, result); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - - int32_t result = kMcResultNoEntry; + void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + + int32_t result = kMcResultNoEntry; { - closeMcFilesForPortLocked(port); - const std::filesystem::path root = getMcRootPath(port); - std::error_code ec; - std::filesystem::remove_all(root, ec); - ec.clear(); - std::filesystem::create_directories(root, ec); - if (!ec) + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - McPortState &state = g_mcPorts[static_cast(port)]; - state.currentDir = "/"; - state.formatted = true; - result = kMcResultSucceed; + closeMcFilesForPortLocked(port); + const std::filesystem::path root = getMcRootPath(port); + std::error_code ec; + std::filesystem::remove_all(root, ec); + ec.clear(); + std::filesystem::create_directories(root, ec); + if (!ec) + { + McPortState &state = g_mcPorts[static_cast(port)]; + state.currentDir = "/"; + state.formatted = true; + result = kMcResultSucceed; + } } - } - setMcCommandResultLocked(kMcCmdFormat, result); + setMcCommandResultLocked(kMcCmdFormat, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - -void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const std::string rawPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - const int32_t maxEntries = static_cast(readStackU32(rdram, ctx, 16)); - const uint32_t tableAddr = readStackU32(rdram, ctx, 20); - std::vector entries; - int32_t result = kMcResultNoEntry; + void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string rawPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + const int32_t maxEntries = static_cast(readStackU32(rdram, ctx, 16)); + const uint32_t tableAddr = readStackU32(rdram, ctx, 20); + + std::vector entries; + int32_t result = kMcResultNoEntry; { - McPortState &state = g_mcPorts[static_cast(port)]; - if (!state.formatted) + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - result = kMcResultNoFormat; - } - else - { - ensureMcRootExists(port); - const std::string guestQuery = - normalizeGuestMcPathLocked(port, rawPath.empty() ? "." : rawPath); - const bool hasWildcard = - guestQuery.find('*') != std::string::npos || guestQuery.find('?') != std::string::npos; - - const std::filesystem::path queryRel = - (guestQuery.size() > 1u) ? std::filesystem::path(guestQuery.substr(1)) : std::filesystem::path{}; - - std::filesystem::path parentRel; - std::string pattern; - if (hasWildcard) + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) { - parentRel = queryRel.parent_path(); - pattern = queryRel.filename().string(); + result = kMcResultNoFormat; } else { - const std::filesystem::path queryHostPath = guestMcPathToHostPath(port, guestQuery); - std::error_code queryEc; - if (std::filesystem::exists(queryHostPath, queryEc) && !queryEc && - std::filesystem::is_directory(queryHostPath, queryEc)) - { - parentRel = queryRel; - pattern = "*"; - } - else + ensureMcRootExists(port); + const std::string guestQuery = + normalizeGuestMcPathLocked(port, rawPath.empty() ? "." : rawPath); + const bool hasWildcard = + guestQuery.find('*') != std::string::npos || guestQuery.find('?') != std::string::npos; + + const std::filesystem::path queryRel = + (guestQuery.size() > 1u) ? std::filesystem::path(guestQuery.substr(1)) : std::filesystem::path{}; + + std::filesystem::path parentRel; + std::string pattern; + if (hasWildcard) { parentRel = queryRel.parent_path(); pattern = queryRel.filename().string(); } - } - - if (pattern.empty()) - { - pattern = "*"; - } - - std::filesystem::path hostDir = getMcRootPath(port); - if (!parentRel.empty()) - { - hostDir /= parentRel; - } - hostDir = hostDir.lexically_normal(); - - std::error_code ec; - if (std::filesystem::exists(hostDir, ec) && !ec && - std::filesystem::is_directory(hostDir, ec)) - { - const std::time_t now = std::time(nullptr); - auto appendSpecial = [&](const std::string &name) + else { - if (!wildcardMatch(pattern, name)) + const std::filesystem::path queryHostPath = guestMcPathToHostPath(port, guestQuery); + std::error_code queryEc; + if (std::filesystem::exists(queryHostPath, queryEc) && !queryEc && + std::filesystem::is_directory(queryHostPath, queryEc)) { - return; + parentRel = queryRel; + pattern = "*"; } - SceMcTblGetDir entry{}; - fillMcDirTableEntry(entry, name, true, 0u, now); - entries.push_back(entry); - }; - - appendSpecial("."); - appendSpecial(".."); - - std::vector dirEntries; - for (const auto &entry : std::filesystem::directory_iterator( - hostDir, std::filesystem::directory_options::skip_permission_denied, ec)) - { - if (ec) + else { - break; + parentRel = queryRel.parent_path(); + pattern = queryRel.filename().string(); } - dirEntries.push_back(entry); } - std::sort(dirEntries.begin(), dirEntries.end(), - [](const std::filesystem::directory_entry &lhs, - const std::filesystem::directory_entry &rhs) - { - return toLowerAscii(lhs.path().filename().string()) < - toLowerAscii(rhs.path().filename().string()); - }); - - for (const auto &entry : dirEntries) + if (pattern.empty()) { - const std::string name = entry.path().filename().string(); - if (!wildcardMatch(pattern, name)) - { - continue; - } - - std::error_code entryEc; - const bool isDirectory = entry.is_directory(entryEc) && !entryEc; - const uint32_t sizeBytes = - isDirectory ? 0u : static_cast(entry.file_size(entryEc)); - entryEc.clear(); - const std::time_t modifiedTime = fileTimeToTimeTMc(entry.last_write_time(entryEc)); - SceMcTblGetDir tableEntry{}; - fillMcDirTableEntry(tableEntry, - name, - isDirectory, - sizeBytes, - entryEc ? now : modifiedTime); - entries.push_back(tableEntry); + pattern = "*"; } - const size_t entryCount = - std::min(entries.size(), maxEntries > 0 ? static_cast(maxEntries) : 0u); - if (entryCount == 0u || tableAddr == 0u) - { - result = static_cast(entryCount); - } - else if (uint8_t *dst = getMemPtr(rdram, tableAddr)) + std::filesystem::path hostDir = getMcRootPath(port); + if (!parentRel.empty()) { - std::memcpy(dst, entries.data(), entryCount * sizeof(SceMcTblGetDir)); - result = static_cast(entryCount); + hostDir /= parentRel; } - else + hostDir = hostDir.lexically_normal(); + + std::error_code ec; + if (std::filesystem::exists(hostDir, ec) && !ec && + std::filesystem::is_directory(hostDir, ec)) { - result = kMcResultDeniedPermit; + const std::time_t now = std::time(nullptr); + auto appendSpecial = [&](const std::string &name) + { + if (!wildcardMatch(pattern, name)) + { + return; + } + SceMcTblGetDir entry{}; + fillMcDirTableEntry(entry, name, true, 0u, now); + entries.push_back(entry); + }; + + appendSpecial("."); + appendSpecial(".."); + + std::vector dirEntries; + for (const auto &entry : std::filesystem::directory_iterator( + hostDir, std::filesystem::directory_options::skip_permission_denied, ec)) + { + if (ec) + { + break; + } + dirEntries.push_back(entry); + } + + std::sort(dirEntries.begin(), dirEntries.end(), + [](const std::filesystem::directory_entry &lhs, + const std::filesystem::directory_entry &rhs) + { + return toLowerAscii(lhs.path().filename().string()) < + toLowerAscii(rhs.path().filename().string()); + }); + + for (const auto &entry : dirEntries) + { + const std::string name = entry.path().filename().string(); + if (!wildcardMatch(pattern, name)) + { + continue; + } + + std::error_code entryEc; + const bool isDirectory = entry.is_directory(entryEc) && !entryEc; + const uint32_t sizeBytes = + isDirectory ? 0u : static_cast(entry.file_size(entryEc)); + entryEc.clear(); + const std::time_t modifiedTime = fileTimeToTimeTMc(entry.last_write_time(entryEc)); + SceMcTblGetDir tableEntry{}; + fillMcDirTableEntry(tableEntry, + name, + isDirectory, + sizeBytes, + entryEc ? now : modifiedTime); + entries.push_back(tableEntry); + } + + const size_t entryCount = + std::min(entries.size(), maxEntries > 0 ? static_cast(maxEntries) : 0u); + if (entryCount == 0u || tableAddr == 0u) + { + result = static_cast(entryCount); + } + else if (uint8_t *dst = getMemPtr(rdram, tableAddr)) + { + std::memcpy(dst, entries.data(), entryCount * sizeof(SceMcTblGetDir)); + result = static_cast(entryCount); + } + else + { + result = kMcResultDeniedPermit; + } } } } - } - setMcCommandResultLocked(kMcCmdGetDir, result); + setMcCommandResultLocked(kMcCmdGetDir, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1024); -} + void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1024); + } -void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const uint32_t typePtr = getRegU32(ctx, 6); - const uint32_t freePtr = getRegU32(ctx, 7); - const uint32_t formatPtr = readStackU32(rdram, ctx, 16); + void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const uint32_t typePtr = getRegU32(ctx, 6); + const uint32_t freePtr = getRegU32(ctx, 7); + const uint32_t formatPtr = readStackU32(rdram, ctx, 16); - int32_t cardType = 0; - int32_t freeBlocks = 0; - int32_t format = kMcUnformatted; - int32_t result = kMcResultNoEntry; + int32_t cardType = 0; + int32_t freeBlocks = 0; + int32_t format = kMcUnformatted; + int32_t result = kMcResultNoEntry; - { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) { - McPortState &state = g_mcPorts[static_cast(port)]; - cardType = kMcTypePs2; - freeBlocks = state.formatted ? kMcFreeClusters : 0; - format = state.formatted ? kMcFormatted : kMcUnformatted; - result = state.formatted ? kMcResultSucceed : kMcResultNoFormat; - } + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + cardType = kMcTypePs2; + freeBlocks = state.formatted ? kMcFreeClusters : 0; + format = state.formatted ? kMcFormatted : kMcUnformatted; + result = state.formatted ? kMcResultSucceed : kMcResultNoFormat; + } - setMcCommandResultLocked(kMcCmdGetInfo, result); - } + setMcCommandResultLocked(kMcCmdGetInfo, result); + } - if (typePtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, typePtr)) + if (typePtr != 0u) { - std::memcpy(out, &cardType, sizeof(cardType)); + if (uint8_t *out = getMemPtr(rdram, typePtr)) + { + std::memcpy(out, &cardType, sizeof(cardType)); + } } - } - if (freePtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, freePtr)) + if (freePtr != 0u) { - std::memcpy(out, &freeBlocks, sizeof(freeBlocks)); + if (uint8_t *out = getMemPtr(rdram, freePtr)) + { + std::memcpy(out, &freeBlocks, sizeof(freeBlocks)); + } } - } - if (formatPtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, formatPtr)) + if (formatPtr != 0u) { - std::memcpy(out, &format, sizeof(format)); + if (uint8_t *out = getMemPtr(rdram, formatPtr)) + { + std::memcpy(out, &format, sizeof(format)); + } } - } - setReturnS32(ctx, 0); -} + setReturnS32(ctx, 0); + } -void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ + void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - closeMcFilesLocked(); - g_mcNextFd = 1; - g_mcLastCmd = 0; - g_mcLastResult = 0; - for (McPortState &state : g_mcPorts) { - state.currentDir = "/"; - state.formatted = true; + std::lock_guard lock(g_mcStateMutex); + closeMcFilesLocked(); + g_mcNextFd = 1; + g_mcLastCmd = 0; + g_mcLastResult = 0; + for (McPortState &state : g_mcPorts) + { + state.currentDir = "/"; + state.formatted = true; + } } + ensureMcRootExists(0); + ensureMcRootExists(1); + setReturnS32(ctx, 0); } - ensureMcRootExists(0); - ensureMcRootExists(1); - setReturnS32(ctx, 0); -} -void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - - int32_t result = kMcResultNoEntry; + void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; { - McPortState &state = g_mcPorts[static_cast(port)]; - if (!state.formatted) + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - result = kMcResultNoFormat; - } - else - { - ensureMcRootExists(port); - const std::string guestPath = normalizeGuestMcPathLocked(port, path); - const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); - std::error_code ec; - if (std::filesystem::exists(hostPath, ec) && !ec) - { - result = std::filesystem::is_directory(hostPath, ec) ? kMcResultSucceed : kMcResultDeniedPermit; - } - else if (std::filesystem::create_directory(hostPath, ec) && !ec) + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) { - result = kMcResultSucceed; + result = kMcResultNoFormat; } else { - result = std::filesystem::exists(hostPath.parent_path(), ec) && !ec - ? kMcResultDeniedPermit - : kMcResultNoEntry; + ensureMcRootExists(port); + const std::string guestPath = normalizeGuestMcPathLocked(port, path); + const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); + std::error_code ec; + if (std::filesystem::exists(hostPath, ec) && !ec) + { + result = std::filesystem::is_directory(hostPath, ec) ? kMcResultSucceed : kMcResultDeniedPermit; + } + else if (std::filesystem::create_directory(hostPath, ec) && !ec) + { + result = kMcResultSucceed; + } + else + { + result = std::filesystem::exists(hostPath.parent_path(), ec) && !ec + ? kMcResultDeniedPermit + : kMcResultNoEntry; + } } } - } - setMcCommandResultLocked(kMcCmdMkdir, result); + setMcCommandResultLocked(kMcCmdMkdir, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - -void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - const uint32_t flags = getRegU32(ctx, 7); - int32_t result = kMcResultNoEntry; + void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + const uint32_t flags = getRegU32(ctx, 7); + + int32_t result = kMcResultNoEntry; { - McPortState &state = g_mcPorts[static_cast(port)]; - if (!state.formatted) + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - result = kMcResultNoFormat; - } - else - { - const std::string guestPath = normalizeGuestMcPathLocked(port, path); - const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); - std::error_code ec; - const bool create = (flags & PS2_FIO_O_CREAT) != 0u; - const bool exists = std::filesystem::exists(hostPath, ec) && !ec; - if (guestPath == "/") - { - result = kMcResultDeniedPermit; - } - else if (exists && std::filesystem::is_directory(hostPath, ec)) - { - result = kMcResultDeniedPermit; - } - else if (!exists && !create) - { - result = kMcResultNoEntry; - } - else if (!std::filesystem::exists(hostPath.parent_path(), ec) || ec) + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) { - result = kMcResultNoEntry; + result = kMcResultNoFormat; } else { - FILE *file = openMcHostFile(hostPath, flags); - if (!file) + const std::string guestPath = normalizeGuestMcPathLocked(port, path); + const std::filesystem::path hostPath = guestMcPathToHostPath(port, guestPath); + std::error_code ec; + const bool create = (flags & PS2_FIO_O_CREAT) != 0u; + const bool exists = std::filesystem::exists(hostPath, ec) && !ec; + if (guestPath == "/") { - result = exists ? kMcResultDeniedPermit : kMcResultNoEntry; + result = kMcResultDeniedPermit; + } + else if (exists && std::filesystem::is_directory(hostPath, ec)) + { + result = kMcResultDeniedPermit; + } + else if (!exists && !create) + { + result = kMcResultNoEntry; + } + else if (!std::filesystem::exists(hostPath.parent_path(), ec) || ec) + { + result = kMcResultNoEntry; } else { - result = allocateMcFdLocked(file, port, hostPath); - if (result < 0) + FILE *file = openMcHostFile(hostPath, flags); + if (!file) + { + result = exists ? kMcResultDeniedPermit : kMcResultNoEntry; + } + else { - std::fclose(file); + result = allocateMcFdLocked(file, port, hostPath); + if (result < 0) + { + std::fclose(file); + } } } } } + setMcCommandResultLocked(kMcCmdOpen, result); } - setMcCommandResultLocked(kMcCmdOpen, result); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - -void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t fd = static_cast(getRegU32(ctx, 4)); - const uint32_t dstAddr = getRegU32(ctx, 5); - const int32_t size = static_cast(getRegU32(ctx, 6)); - uint8_t *dst = (size > 0) ? getMemPtr(rdram, dstAddr) : nullptr; - int32_t result = kMcResultNoEntry; + void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - auto it = g_mcFiles.find(fd); - if (size <= 0) - { - result = 0; - } - else if (it == g_mcFiles.end() || !it->second.file) - { - result = kMcResultNoEntry; - } - else if (!dst) - { - result = kMcResultDeniedPermit; - } - else + const int32_t fd = static_cast(getRegU32(ctx, 4)); + const uint32_t dstAddr = getRegU32(ctx, 5); + const int32_t size = static_cast(getRegU32(ctx, 6)); + uint8_t *dst = (size > 0) ? getMemPtr(rdram, dstAddr) : nullptr; + + int32_t result = kMcResultNoEntry; { - const size_t bytesRead = std::fread(dst, 1u, static_cast(size), it->second.file); - result = std::ferror(it->second.file) ? kMcResultDeniedPermit : static_cast(bytesRead); - if (std::ferror(it->second.file)) + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (size <= 0) { - std::clearerr(it->second.file); + result = 0; } - } - - setMcCommandResultLocked(kMcCmdRead, result); - } - setReturnS32(ctx, 0); -} - -void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const std::string oldPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - const std::string newPath = readPs2CStringBounded(rdram, getRegU32(ctx, 7), kMcMaxPathLen); - - int32_t result = kMcResultNoEntry; - { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) - { - McPortState &state = g_mcPorts[static_cast(port)]; - if (!state.formatted) + else if (it == g_mcFiles.end() || !it->second.file) { - result = kMcResultNoFormat; + result = kMcResultNoEntry; + } + else if (!dst) + { + result = kMcResultDeniedPermit; } else { - const std::filesystem::path oldHostPath = - guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, oldPath)); - const std::filesystem::path newHostPath = - guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, newPath)); - std::error_code ec; - if (std::filesystem::exists(oldHostPath, ec) && !ec && - std::filesystem::exists(newHostPath.parent_path(), ec) && !ec) + const size_t bytesRead = std::fread(dst, 1u, static_cast(size), it->second.file); + result = std::ferror(it->second.file) ? kMcResultDeniedPermit : static_cast(bytesRead); + if (std::ferror(it->second.file)) { - std::filesystem::rename(oldHostPath, newHostPath, ec); - result = ec ? kMcResultDeniedPermit : kMcResultSucceed; + std::clearerr(it->second.file); } } - } - setMcCommandResultLocked(kMcCmdRename, result); + setMcCommandResultLocked(kMcCmdRead, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - -void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t fd = static_cast(getRegU32(ctx, 4)); - const int32_t offset = static_cast(getRegU32(ctx, 5)); - const int32_t origin = static_cast(getRegU32(ctx, 6)); - int32_t result = kMcResultNoEntry; + void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - auto it = g_mcFiles.find(fd); - if (it != g_mcFiles.end() && it->second.file) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string oldPath = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); + const std::string newPath = readPs2CStringBounded(rdram, getRegU32(ctx, 7), kMcMaxPathLen); + + int32_t result = kMcResultNoEntry; { - int whence = SEEK_SET; - if (origin == PS2_FIO_SEEK_CUR) + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) { - whence = SEEK_CUR; - } - else if (origin == PS2_FIO_SEEK_END) - { - whence = SEEK_END; + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + const std::filesystem::path oldHostPath = + guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, oldPath)); + const std::filesystem::path newHostPath = + guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, newPath)); + std::error_code ec; + if (std::filesystem::exists(oldHostPath, ec) && !ec && + std::filesystem::exists(newHostPath.parent_path(), ec) && !ec) + { + std::filesystem::rename(oldHostPath, newHostPath, ec); + result = ec ? kMcResultDeniedPermit : kMcResultSucceed; + } + } } - if (std::fseek(it->second.file, offset, whence) == 0) - { - const long position = std::ftell(it->second.file); - result = (position >= 0) ? static_cast(position) : kMcResultDeniedPermit; - } - else - { - result = kMcResultDeniedPermit; - } + setMcCommandResultLocked(kMcCmdRename, result); } - - setMcCommandResultLocked(kMcCmdSeek, result); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - - int32_t result = kMcResultNoEntry; + void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + const int32_t fd = static_cast(getRegU32(ctx, 4)); + const int32_t offset = static_cast(getRegU32(ctx, 5)); + const int32_t origin = static_cast(getRegU32(ctx, 6)); + + int32_t result = kMcResultNoEntry; { - McPortState &state = g_mcPorts[static_cast(port)]; - if (!state.formatted) + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (it != g_mcFiles.end() && it->second.file) { - result = kMcResultNoFormat; - } - else - { - const std::filesystem::path hostPath = - guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, path)); - std::error_code ec; - if (std::filesystem::exists(hostPath, ec) && !ec) + int whence = SEEK_SET; + if (origin == PS2_FIO_SEEK_CUR) { - result = kMcResultSucceed; + whence = SEEK_CUR; + } + else if (origin == PS2_FIO_SEEK_END) + { + whence = SEEK_END; + } + + if (std::fseek(it->second.file, offset, whence) == 0) + { + const long position = std::ftell(it->second.file); + result = (position >= 0) ? static_cast(position) : kMcResultDeniedPermit; + } + else + { + result = kMcResultDeniedPermit; } } - } - setMcCommandResultLocked(kMcCmdSetFileInfo, result); + setMcCommandResultLocked(kMcCmdSeek, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cmdPtr = getRegU32(ctx, 5); - const uint32_t resultPtr = getRegU32(ctx, 6); - int32_t cmd = 0; - int32_t result = 0; + void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - cmd = g_mcLastCmd; - result = g_mcLastResult; - } + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + const std::string path = readPs2CStringBounded(rdram, getRegU32(ctx, 6), kMcMaxPathLen); - if (cmdPtr != 0u) - { - if (uint8_t *out = getMemPtr(rdram, cmdPtr)) + int32_t result = kMcResultNoEntry; { - std::memcpy(out, &cmd, sizeof(cmd)); + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + McPortState &state = g_mcPorts[static_cast(port)]; + if (!state.formatted) + { + result = kMcResultNoFormat; + } + else + { + const std::filesystem::path hostPath = + guestMcPathToHostPath(port, normalizeGuestMcPathLocked(port, path)); + std::error_code ec; + if (std::filesystem::exists(hostPath, ec) && !ec) + { + result = kMcResultSucceed; + } + } + } + + setMcCommandResultLocked(kMcCmdSetFileInfo, result); } + setReturnS32(ctx, 0); } - if (resultPtr != 0u) + + void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (uint8_t *out = getMemPtr(rdram, resultPtr)) + const uint32_t cmdPtr = getRegU32(ctx, 5); + const uint32_t resultPtr = getRegU32(ctx, 6); + int32_t cmd = 0; + int32_t result = 0; { - std::memcpy(out, &result, sizeof(result)); + std::lock_guard lock(g_mcStateMutex); + cmd = g_mcLastCmd; + result = g_mcLastResult; } - } - - // 1 = command finished in this runtime's immediate model. - setReturnS32(ctx, 1); -} -void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t port = static_cast(getRegU32(ctx, 4)); - const int32_t slot = static_cast(getRegU32(ctx, 5)); - - int32_t result = kMcResultNoEntry; - { - std::lock_guard lock(g_mcStateMutex); - if (isValidMcPortSlot(port, slot)) + if (cmdPtr != 0u) { - closeMcFilesForPortLocked(port); - const std::filesystem::path root = getMcRootPath(port); - std::error_code ec; - std::filesystem::remove_all(root, ec); - ec.clear(); - std::filesystem::create_directories(root, ec); - if (!ec) + if (uint8_t *out = getMemPtr(rdram, cmdPtr)) { - McPortState &state = g_mcPorts[static_cast(port)]; - state.currentDir = "/"; - state.formatted = false; - result = kMcResultSucceed; + std::memcpy(out, &cmd, sizeof(cmd)); + } + } + if (resultPtr != 0u) + { + if (uint8_t *out = getMemPtr(rdram, resultPtr)) + { + std::memcpy(out, &result, sizeof(result)); } } - setMcCommandResultLocked(kMcCmdUnformat, result); + // 1 = command finished in this runtime's immediate model. + setReturnS32(ctx, 1); } - setReturnS32(ctx, 0); -} - -void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t fd = static_cast(getRegU32(ctx, 4)); - const uint32_t srcAddr = getRegU32(ctx, 5); - const int32_t size = static_cast(getRegU32(ctx, 6)); - const uint8_t *src = (size > 0) ? getConstMemPtr(rdram, srcAddr) : nullptr; - int32_t result = kMcResultNoEntry; + void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_mcStateMutex); - auto it = g_mcFiles.find(fd); - if (size <= 0) - { - result = 0; - } - else if (it == g_mcFiles.end() || !it->second.file) - { - result = kMcResultNoEntry; - } - else if (!src) + const int32_t port = static_cast(getRegU32(ctx, 4)); + const int32_t slot = static_cast(getRegU32(ctx, 5)); + + int32_t result = kMcResultNoEntry; { - result = kMcResultDeniedPermit; + std::lock_guard lock(g_mcStateMutex); + if (isValidMcPortSlot(port, slot)) + { + closeMcFilesForPortLocked(port); + const std::filesystem::path root = getMcRootPath(port); + std::error_code ec; + std::filesystem::remove_all(root, ec); + ec.clear(); + std::filesystem::create_directories(root, ec); + if (!ec) + { + McPortState &state = g_mcPorts[static_cast(port)]; + state.currentDir = "/"; + state.formatted = false; + result = kMcResultSucceed; + } + } + + setMcCommandResultLocked(kMcCmdUnformat, result); } - else + setReturnS32(ctx, 0); + } + + void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int32_t fd = static_cast(getRegU32(ctx, 4)); + const uint32_t srcAddr = getRegU32(ctx, 5); + const int32_t size = static_cast(getRegU32(ctx, 6)); + const uint8_t *src = (size > 0) ? getConstMemPtr(rdram, srcAddr) : nullptr; + + int32_t result = kMcResultNoEntry; { - const size_t bytesWritten = std::fwrite(src, 1u, static_cast(size), it->second.file); - result = std::ferror(it->second.file) ? kMcResultDeniedPermit : static_cast(bytesWritten); - if (!std::ferror(it->second.file)) + std::lock_guard lock(g_mcStateMutex); + auto it = g_mcFiles.find(fd); + if (size <= 0) + { + result = 0; + } + else if (it == g_mcFiles.end() || !it->second.file) { - std::fflush(it->second.file); + result = kMcResultNoEntry; + } + else if (!src) + { + result = kMcResultDeniedPermit; } else { - std::clearerr(it->second.file); + const size_t bytesWritten = std::fwrite(src, 1u, static_cast(size), it->second.file); + result = std::ferror(it->second.file) ? kMcResultDeniedPermit : static_cast(bytesWritten); + if (!std::ferror(it->second.file)) + { + std::fflush(it->second.file); + } + else + { + std::clearerr(it->second.file); + } } - } - setMcCommandResultLocked(kMcCmdWrite, result); + setMcCommandResultLocked(kMcCmdWrite, result); + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} + void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcConfigCapacityBytes); -} + void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, kCvMcConfigCapacityBytes); + } -void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_cvMcFileCursor); -} + void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, g_cvMcFileCursor); + } -void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcFreeCapacityBytes); -} + void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, kCvMcFreeCapacityBytes); + } -void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcIconCapacityBytes); -} + void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, kCvMcIconCapacityBytes); + } -void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcIconCapacityBytes); -} + void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, kCvMcIconCapacityBytes); + } -void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, kCvMcSaveCapacityBytes); -} + void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, kCvMcSaveCapacityBytes); + } -void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t strAddr = getRegU32(ctx, 4); - const std::string value = readPs2CStringBounded(rdram, runtime, strAddr, 1024); - setReturnU32(ctx, strAddr + static_cast(value.size())); -} + void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t strAddr = getRegU32(ctx, 4); + const std::string value = readPs2CStringBounded(rdram, runtime, strAddr, 1024); + setReturnU32(ctx, strAddr + static_cast(value.size())); + } -void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t delta = static_cast(getRegU32(ctx, 5)); - g_cvMcFileCursor += delta; - g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); - setReturnS32(ctx, 0); -} + void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int32_t delta = static_cast(getRegU32(ctx, 5)); + g_cvMcFileCursor += delta; + g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); + setReturnS32(ctx, 0); + } -void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cvMcFileCursor = 0; - setReturnS32(ctx, 1); -} + void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + g_cvMcFileCursor = 0; + setReturnS32(ctx, 1); + } -void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cvMcFileCursor = static_cast(getRegU32(ctx, 5)); - g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); - setReturnS32(ctx, 0); -} + void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + g_cvMcFileCursor = static_cast(getRegU32(ctx, 5)); + g_cvMcFileCursor = std::clamp(g_cvMcFileCursor, -1, 15); + setReturnS32(ctx, 0); + } -void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_cvMcFileCursor = 0; - setReturnS32(ctx, 0); -} + void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + g_cvMcFileCursor = 0; + setReturnS32(ctx, 0); + } -void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp index be31d7f6..170ffd04 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp @@ -304,440 +304,440 @@ namespace ps2_stubs } } -void PadSyncCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; + void PadSyncCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_padStateMutex); - resetPadStateLocked(); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 1); -} -void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState || !portState->open) + void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; + (void)rdram; + (void)runtime; + { + std::lock_guard lock(g_padStateMutex); + resetPadStateLocked(); + } + setReturnS32(ctx, 1); } - portState->pressureEnabled = true; - portState->reqState = 0u; - setReturnS32(ctx, 1); -} - -void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState || !portState->open) + void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; - } + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } - portState->pressureEnabled = false; - portState->reqState = 0u; - setReturnS32(ctx, 1); -} + portState->pressureEnabled = true; + portState->reqState = 0u; + setReturnS32(ctx, 1); + } -void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - const uint16_t mask = portState ? portState->buttonMask : 0xFFFFu; - setReturnS32(ctx, static_cast(mask)); -} + void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } -void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - const uint32_t dmaAddr = portState ? portState->dmaAddr : getRegU32(ctx, 6); - setReturnU32(ctx, dmaAddr); -} + portState->pressureEnabled = false; + portState->reqState = 0u; + setReturnS32(ctx, 1); + } -void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - static std::atomic frameCount{0}; - setReturnU32(ctx, frameCount++); -} + void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + const uint16_t mask = portState ? portState->buttonMask : 0xFFFFu; + setReturnS32(ctx, static_cast(mask)); + } -void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Arbitrary non-zero module version. - setReturnS32(ctx, 0x0200); -} + void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + const uint32_t dmaAddr = portState ? portState->dmaAddr : getRegU32(ctx, 6); + setReturnU32(ctx, dmaAddr); + } -void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 2); -} + void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + static std::atomic frameCount{0}; + setReturnU32(ctx, frameCount++); + } -void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - setReturnS32(ctx, static_cast(portState ? portState->reqState : 0u)); -} + void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + // Arbitrary non-zero module version. + setReturnS32(ctx, 0x0200); + } -void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // Most games use one slot unless multitap is active. - setReturnS32(ctx, 1); -} + void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + setReturnS32(ctx, 2); + } -void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - setReturnS32(ctx, (portState && portState->open) ? kPadStateStable : kPadStateDisconnected); -} + void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + setReturnS32(ctx, static_cast(portState ? portState->reqState : 0u)); + } -void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t act = static_cast(getRegU32(ctx, 6)); - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState || !portState->open) + void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; + (void)rdram; + (void)runtime; + // Most games use one slot unless multitap is active. + setReturnS32(ctx, 1); } - if (act < 0) + void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 2); // small + large motors - return; + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + setReturnS32(ctx, (portState && portState->open) ? kPadStateStable : kPadStateDisconnected); } - setReturnS32(ctx, (act < 2) ? 1 : 0); -} -void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - // No combined modes reported. - setReturnS32(ctx, 0); -} + void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const int32_t act = static_cast(getRegU32(ctx, 6)); + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } -void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; + if (act < 0) + { + setReturnS32(ctx, 2); // small + large motors + return; + } + setReturnS32(ctx, (act < 2) ? 1 : 0); + } - const int32_t infoMode = static_cast(getRegU32(ctx, 6)); // a2 - const int32_t index = static_cast(getRegU32(ctx, 7)); // a3 - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState || !portState->open) + void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + (void)rdram; + (void)runtime; + // No combined modes reported. setReturnS32(ctx, 0); - return; } - const int32_t currentId = portState->analogMode ? kPadTypeDualShock : kPadTypeDigital; - switch (infoMode) + void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - case 1: // PAD_MODECURID - setReturnS32(ctx, currentId); - return; - case 2: // PAD_MODECUREXID - setReturnS32(ctx, currentId); - return; - case 3: // PAD_MODECUROFFS - setReturnS32(ctx, 0); - return; - case 4: // PAD_MODETABLE - if (index == -1) + (void)rdram; + (void)runtime; + + const int32_t infoMode = static_cast(getRegU32(ctx, 6)); // a2 + const int32_t index = static_cast(getRegU32(ctx, 7)); // a3 + std::lock_guard lock(g_padStateMutex); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) { - setReturnS32(ctx, 1); // one available mode + setReturnS32(ctx, 0); + return; } - else if (index == 0) + + const int32_t currentId = portState->analogMode ? kPadTypeDualShock : kPadTypeDigital; + switch (infoMode) { + case 1: // PAD_MODECURID setReturnS32(ctx, currentId); - } - else - { + return; + case 2: // PAD_MODECUREXID + setReturnS32(ctx, currentId); + return; + case 3: // PAD_MODECUROFFS + setReturnS32(ctx, 0); + return; + case 4: // PAD_MODETABLE + if (index == -1) + { + setReturnS32(ctx, 1); // one available mode + } + else if (index == 0) + { + setReturnS32(ctx, currentId); + } + else + { + setReturnS32(ctx, 0); + } + return; + default: setReturnS32(ctx, 0); + return; } - return; - default: - setReturnS32(ctx, 0); - return; } -} -void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - setReturnS32(ctx, (portState && portState->open) ? 1 : 0); -} - -void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; + void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + (void)rdram; + (void)runtime; std::lock_guard lock(g_padStateMutex); - resetPadStateLocked(); + const PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + setReturnS32(ctx, (portState && portState->open) ? 1 : 0); } - setReturnS32(ctx, 1); -} - -void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - scePadInit(rdram, ctx, runtime); -} -void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState) + void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; + (void)rdram; + (void)runtime; + { + std::lock_guard lock(g_padStateMutex); + resetPadStateLocked(); + } + setReturnS32(ctx, 1); } - portState->open = false; - portState->pressureEnabled = false; - portState->reqState = 0u; - setReturnS32(ctx, 1); -} - -void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t dmaAddr = getRegU32(ctx, 6); - uint8_t *dmaStr = getMemPtr(rdram, dmaAddr); - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState || (dmaAddr != 0u && !dmaStr)) + void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; + scePadInit(rdram, ctx, runtime); } - portState->open = true; - portState->analogMode = true; - portState->pressureEnabled = false; - portState->buttonMask = 0xFFFFu; - portState->dmaAddr = dmaAddr; - portState->reqState = 0u; - if (dmaStr) + void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::memset(dmaStr, 0, 32); - } - setReturnS32(ctx, 1); -} + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState) + { + setReturnS32(ctx, 0); + return; + } -void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int port = static_cast(getRegU32(ctx, 4)); - const int slot = static_cast(getRegU32(ctx, 5)); - const uint32_t dataAddr = getRegU32(ctx, 6); - uint8_t *data = getMemPtr(rdram, dataAddr); - if (!data) - { - setReturnS32(ctx, 0); - return; + portState->open = false; + portState->pressureEnabled = false; + portState->reqState = 0u; + setReturnS32(ctx, 1); } - if (!readPadPortData(port, slot, runtime, data)) + void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); - return; - } + (void)runtime; + const uint32_t dmaAddr = getRegU32(ctx, 6); + uint8_t *dmaStr = getMemPtr(rdram, dmaAddr); + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || (dmaAddr != 0u && !dmaStr)) + { + setReturnS32(ctx, 0); + return; + } - if (g_padReadLogCount < 48) - { - const int gamepad = findFirstGamepad(); - const bool gamepadStartPressed = - (gamepad >= 0) && IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT); - const bool startPressed = (data[2] != 0xFFu || data[3] != 0xFFu || - IsKeyDown(KEY_ENTER) || gamepadStartPressed); - if (startPressed) + portState->open = true; + portState->analogMode = true; + portState->pressureEnabled = false; + portState->buttonMask = 0xFFFFu; + portState->dmaAddr = dmaAddr; + portState->reqState = 0u; + if (dmaStr) { - const uint32_t guestButtons = - (static_cast(static_cast(data[2] ^ 0xFFu)) << 8) | - static_cast(static_cast(data[3] ^ 0xFFu)); - std::printf("[padread] port=%d slot=%d data2=0x%02x data3=0x%02x guestButtons=0x%04x enter=%d gamepadStart=%d\n", - port, slot, data[2], data[3], guestButtons, - IsKeyDown(KEY_ENTER) ? 1 : 0, gamepadStartPressed ? 1 : 0); - ++g_padReadLogCount; + std::memset(dmaStr, 0, 32); } + setReturnS32(ctx, 1); } - setReturnS32(ctx, 1); -} - -void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t state = getRegU32(ctx, 4); - const uint32_t strAddr = getRegU32(ctx, 5); - char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); - if (!buf) + void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; - } - - const char *text = (state == 0) ? "COMPLETE" : "BUSY"; - std::strncpy(buf, text, 31); - buf[31] = '\0'; - setReturnS32(ctx, 0); -} + const int port = static_cast(getRegU32(ctx, 4)); + const int slot = static_cast(getRegU32(ctx, 5)); + const uint32_t dataAddr = getRegU32(ctx, 6); + uint8_t *data = getMemPtr(rdram, dataAddr); + if (!data) + { + setReturnS32(ctx, 0); + return; + } -void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} + if (!readPadPortData(port, slot, runtime, data)) + { + setReturnS32(ctx, 0); + return; + } -void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} + if (g_padReadLogCount < 48) + { + const int gamepad = findFirstGamepad(); + const bool gamepadStartPressed = + (gamepad >= 0) && IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT); + const bool startPressed = (data[2] != 0xFFu || data[3] != 0xFFu || + IsKeyDown(KEY_ENTER) || gamepadStartPressed); + if (startPressed) + { + const uint32_t guestButtons = + (static_cast(static_cast(data[2] ^ 0xFFu)) << 8) | + static_cast(static_cast(data[3] ^ 0xFFu)); + std::printf("[padread] port=%d slot=%d data2=0x%02x data3=0x%02x guestButtons=0x%04x enter=%d gamepadStart=%d\n", + port, slot, data[2], data[3], guestButtons, + IsKeyDown(KEY_ENTER) ? 1 : 0, gamepadStartPressed ? 1 : 0); + ++g_padReadLogCount; + } + } -void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (portState && portState->open) - { - portState->buttonMask = static_cast(getRegU32(ctx, 6)); - portState->reqState = 0u; + setReturnS32(ctx, 1); } - setReturnS32(ctx, 1); -} -void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (!portState || !portState->open) + void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + (void)runtime; + const uint32_t state = getRegU32(ctx, 4); + const uint32_t strAddr = getRegU32(ctx, 5); + char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); + if (!buf) + { + setReturnS32(ctx, -1); + return; + } + + const char *text = (state == 0) ? "COMPLETE" : "BUSY"; + std::strncpy(buf, text, 31); + buf[31] = '\0'; setReturnS32(ctx, 0); - return; } - portState->analogMode = (getRegU32(ctx, 6) != 0u); - portState->reqState = 0u; - setReturnS32(ctx, 1); -} - -void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - std::lock_guard lock(g_padStateMutex); - PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), - static_cast(getRegU32(ctx, 5))); - if (portState && portState->open) + void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - portState->reqState = static_cast(getRegU32(ctx, 6) ? 1u : 0u); + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); } - setReturnS32(ctx, 1); -} -void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); -} + void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); + } -void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 0); -} + void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (portState && portState->open) + { + portState->buttonMask = static_cast(getRegU32(ctx, 6)); + portState->reqState = 0u; + } + setReturnS32(ctx, 1); + } -void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t state = getRegU32(ctx, 4); - const uint32_t strAddr = getRegU32(ctx, 5); - char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); - if (!buf) + void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (!portState || !portState->open) + { + setReturnS32(ctx, 0); + return; + } + + portState->analogMode = (getRegU32(ctx, 6) != 0u); + portState->reqState = 0u; + setReturnS32(ctx, 1); } - const char *text = "UNKNOWN"; - if (state == 6) + void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - text = "STABLE"; + (void)rdram; + (void)runtime; + std::lock_guard lock(g_padStateMutex); + PadPortState *portState = lookupPadPortStateLocked(static_cast(getRegU32(ctx, 4)), + static_cast(getRegU32(ctx, 5))); + if (portState && portState->open) + { + portState->reqState = static_cast(getRegU32(ctx, 6) ? 1u : 0u); + } + setReturnS32(ctx, 1); } - else if (state == 1) + + void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - text = "FINDPAD"; + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); } - else if (state == 0) + + void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - text = "DISCONNECTED"; + (void)rdram; + (void)runtime; + setReturnS32(ctx, 0); } - std::strncpy(buf, text, 31); - buf[31] = '\0'; - setReturnS32(ctx, 0); -} + void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)runtime; + const uint32_t state = getRegU32(ctx, 4); + const uint32_t strAddr = getRegU32(ctx, 5); + char *buf = reinterpret_cast(getMemPtr(rdram, strAddr)); + if (!buf) + { + setReturnS32(ctx, -1); + return; + } + + const char *text = "UNKNOWN"; + if (state == 6) + { + text = "STABLE"; + } + else if (state == 1) + { + text = "FINDPAD"; + } + else if (state == 0) + { + text = "DISCONNECTED"; + } + + std::strncpy(buf, text, 31); + buf[31] = '\0'; + setReturnS32(ctx, 0); + } void setPadOverrideState(uint16_t buttons, uint8_t lx, uint8_t ly, uint8_t rx, uint8_t ry) { @@ -750,7 +750,6 @@ void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) g_padOverrideState.ry = ry; } - void clearPadOverrideState() { std::lock_guard lock(g_padOverrideMutex); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp index 4fa86617..8c3a02d6 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/RPC.cpp @@ -3,20 +3,18 @@ namespace ps2_stubs { -void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceRpcFreePacket", rdram, ctx, runtime); -} - - -void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceRpcGetFPacket", rdram, ctx, runtime); -} + void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceRpcFreePacket", rdram, ctx, runtime); + } + void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceRpcGetFPacket", rdram, ctx, runtime); + } -void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceRpcGetFPacket2", rdram, ctx, runtime); -} + void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceRpcGetFPacket2", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp index cd8c20a4..96260854 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp @@ -4,705 +4,702 @@ namespace ps2_stubs { -void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSifCmdIntrHdlr", rdram, ctx, runtime); -} - - -void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifLoadModule(rdram, ctx, runtime); -} - - -void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t srcAddr = getRegU32(ctx, 7); // $a3 - const uint32_t dstAddr = readStackU32(rdram, ctx, 16); - const uint32_t size = readStackU32(rdram, ctx, 20); - if (size != 0u && srcAddr != 0u && dstAddr != 0u) + void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (uint32_t i = 0; i < size; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - uint8_t *dst = getMemPtr(rdram, dstAddr + i); - if (!src || !dst) - { - break; - } - *dst = *src; - } + TODO_NAMED("sceSifCmdIntrHdlr", rdram, ctx, runtime); } - setReturnS32(ctx, 1); -} - - -namespace -{ - struct Ps2SifDmaTransfer - { - uint32_t src = 0; - uint32_t dest = 0; - int32_t size = 0; - int32_t attr = 0; - }; - static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected SIF DMA descriptor size"); - - std::mutex g_sifDmaTransferMutex; - uint32_t g_nextSifDmaTransferId = 1u; - std::mutex g_sifCmdStateMutex; - std::unordered_map g_sifRegs; - std::unordered_map g_sifSregs; - std::unordered_map g_sifCmdHandlers; - uint32_t g_sifCmdBuffer = 0u; - uint32_t g_sifSysCmdBuffer = 0u; - bool g_sifCmdInitialized = false; - uint32_t g_sifGetRegLogCount = 0u; - uint32_t g_sifSetRegLogCount = 0u; - - constexpr uint32_t kSifRegBootStatus = 0x4u; - constexpr uint32_t kSifRegMainAddr = 0x80000000u; - constexpr uint32_t kSifRegSubAddr = 0x80000001u; - constexpr uint32_t kSifRegMsCom = 0x80000002u; - constexpr uint32_t kSifBootReadyMask = 0x00020000u; - - void seedDefaultSifRegsLocked() - { - g_sifRegs.clear(); - g_sifSregs.clear(); - g_sifCmdHandlers.clear(); - g_sifCmdBuffer = 0u; - g_sifSysCmdBuffer = 0u; - g_sifCmdInitialized = false; - g_sifGetRegLogCount = 0u; - g_sifSetRegLogCount = 0u; - - g_sifRegs[kSifRegBootStatus] = kSifBootReadyMask; - g_sifRegs[kSifRegMainAddr] = 0u; - g_sifRegs[kSifRegSubAddr] = 0u; - g_sifRegs[kSifRegMsCom] = 0u; - } - - bool shouldTraceSifReg(uint32_t reg) - { - switch (reg) - { - case 0x2u: - case 0x4u: - case 0x80000000u: - case 0x80000001u: - case 0x80000002u: - return true; - default: - return false; - } + void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifLoadModule(rdram, ctx, runtime); } - struct SifStateInitializer + void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - SifStateInitializer() + const uint32_t srcAddr = getRegU32(ctx, 7); // $a3 + const uint32_t dstAddr = readStackU32(rdram, ctx, 16); + const uint32_t size = readStackU32(rdram, ctx, 20); + if (size != 0u && srcAddr != 0u && dstAddr != 0u) { - std::lock_guard lock(g_sifCmdStateMutex); - seedDefaultSifRegsLocked(); + for (uint32_t i = 0; i < size; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + uint8_t *dst = getMemPtr(rdram, dstAddr + i); + if (!src || !dst) + { + break; + } + *dst = *src; + } } - } g_sifStateInitializer; - uint32_t allocateSifDmaTransferId() - { - std::lock_guard lock(g_sifDmaTransferMutex); - uint32_t id = g_nextSifDmaTransferId++; - if (id == 0u) - { - id = g_nextSifDmaTransferId++; - } - return id; + setReturnS32(ctx, 1); } - bool isCopyableGuestAddress(uint32_t addr) + namespace { - if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) + struct Ps2SifDmaTransfer { - return true; - } - - if (addr < 0x20000000u) + uint32_t src = 0; + uint32_t dest = 0; + int32_t size = 0; + int32_t attr = 0; + }; + static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected SIF DMA descriptor size"); + + std::mutex g_sifDmaTransferMutex; + uint32_t g_nextSifDmaTransferId = 1u; + std::mutex g_sifCmdStateMutex; + std::unordered_map g_sifRegs; + std::unordered_map g_sifSregs; + std::unordered_map g_sifCmdHandlers; + uint32_t g_sifCmdBuffer = 0u; + uint32_t g_sifSysCmdBuffer = 0u; + bool g_sifCmdInitialized = false; + uint32_t g_sifGetRegLogCount = 0u; + uint32_t g_sifSetRegLogCount = 0u; + + constexpr uint32_t kSifRegBootStatus = 0x4u; + constexpr uint32_t kSifRegMainAddr = 0x80000000u; + constexpr uint32_t kSifRegSubAddr = 0x80000001u; + constexpr uint32_t kSifRegMsCom = 0x80000002u; + constexpr uint32_t kSifBootReadyMask = 0x00020000u; + + void seedDefaultSifRegsLocked() { - return true; + g_sifRegs.clear(); + g_sifSregs.clear(); + g_sifCmdHandlers.clear(); + g_sifCmdBuffer = 0u; + g_sifSysCmdBuffer = 0u; + g_sifCmdInitialized = false; + g_sifGetRegLogCount = 0u; + g_sifSetRegLogCount = 0u; + + g_sifRegs[kSifRegBootStatus] = kSifBootReadyMask; + g_sifRegs[kSifRegMainAddr] = 0u; + g_sifRegs[kSifRegSubAddr] = 0u; + g_sifRegs[kSifRegMsCom] = 0u; } - if (addr >= 0x20000000u && addr < 0x40000000u) + bool shouldTraceSifReg(uint32_t reg) { - return true; + switch (reg) + { + case 0x2u: + case 0x4u: + case 0x80000000u: + case 0x80000001u: + case 0x80000002u: + return true; + default: + return false; + } } - if (addr >= 0x80000000u && addr < 0xC0000000u) + struct SifStateInitializer { - return true; - } - - return false; - } - - bool canCopyGuestByteRange(const uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) - { - if (!rdram) - { - return false; - } + SifStateInitializer() + { + std::lock_guard lock(g_sifCmdStateMutex); + seedDefaultSifRegsLocked(); + } + } g_sifStateInitializer; - if (sizeBytes == 0u) + uint32_t allocateSifDmaTransferId() { - return true; + std::lock_guard lock(g_sifDmaTransferMutex); + uint32_t id = g_nextSifDmaTransferId++; + if (id == 0u) + { + id = g_nextSifDmaTransferId++; + } + return id; } - for (uint32_t i = 0u; i < sizeBytes; ++i) + bool isCopyableGuestAddress(uint32_t addr) { - const uint32_t srcByteAddr = srcAddr + i; - const uint32_t dstByteAddr = dstAddr + i; + if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) + { + return true; + } - if (!isCopyableGuestAddress(srcByteAddr) || !isCopyableGuestAddress(dstByteAddr)) + if (addr < 0x20000000u) { - return false; + return true; } - const uint8_t *src = getConstMemPtr(rdram, srcByteAddr); - const uint8_t *dst = getConstMemPtr(rdram, dstByteAddr); - if (!src || !dst) + if (addr >= 0x20000000u && addr < 0x40000000u) { - return false; + return true; } - } - return true; - } + if (addr >= 0x80000000u && addr < 0xC0000000u) + { + return true; + } - bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) - { - if (!canCopyGuestByteRange(rdram, dstAddr, srcAddr, sizeBytes)) - { return false; } - if (sizeBytes == 0u) + bool canCopyGuestByteRange(const uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) { - return true; - } + if (!rdram) + { + return false; + } - const uint64_t srcBegin = srcAddr; - const uint64_t srcEnd = srcBegin + static_cast(sizeBytes); - const uint64_t dstBegin = dstAddr; - const bool copyBackward = (dstBegin > srcBegin) && (dstBegin < srcEnd); + if (sizeBytes == 0u) + { + return true; + } - if (copyBackward) - { - for (uint32_t i = sizeBytes; i > 0u; --i) + for (uint32_t i = 0u; i < sizeBytes; ++i) { - const uint32_t index = i - 1u; - const uint8_t *src = getConstMemPtr(rdram, srcAddr + index); - uint8_t *dst = getMemPtr(rdram, dstAddr + index); + const uint32_t srcByteAddr = srcAddr + i; + const uint32_t dstByteAddr = dstAddr + i; + + if (!isCopyableGuestAddress(srcByteAddr) || !isCopyableGuestAddress(dstByteAddr)) + { + return false; + } + + const uint8_t *src = getConstMemPtr(rdram, srcByteAddr); + const uint8_t *dst = getConstMemPtr(rdram, dstByteAddr); if (!src || !dst) { return false; } - *dst = *src; } + return true; } - for (uint32_t i = 0; i < sizeBytes; ++i) + bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - uint8_t *dst = getMemPtr(rdram, dstAddr + i); - if (!src || !dst) + if (!canCopyGuestByteRange(rdram, dstAddr, srcAddr, sizeBytes)) { return false; } - *dst = *src; + + if (sizeBytes == 0u) + { + return true; + } + + const uint64_t srcBegin = srcAddr; + const uint64_t srcEnd = srcBegin + static_cast(sizeBytes); + const uint64_t dstBegin = dstAddr; + const bool copyBackward = (dstBegin > srcBegin) && (dstBegin < srcEnd); + + if (copyBackward) + { + for (uint32_t i = sizeBytes; i > 0u; --i) + { + const uint32_t index = i - 1u; + const uint8_t *src = getConstMemPtr(rdram, srcAddr + index); + uint8_t *dst = getMemPtr(rdram, dstAddr + index); + if (!src || !dst) + { + return false; + } + *dst = *src; + } + return true; + } + + for (uint32_t i = 0; i < sizeBytes; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + uint8_t *dst = getMemPtr(rdram, dstAddr + i); + if (!src || !dst) + { + return false; + } + *dst = *src; + } + return true; } - return true; } -} -void resetSifState() -{ - std::lock_guard lock(g_sifCmdStateMutex); - seedDefaultSifRegsLocked(); -} - -void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cid = getRegU32(ctx, 4); - const uint32_t handler = getRegU32(ctx, 5); - std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdHandlers[cid] = handler; - setReturnS32(ctx, 0); -} + void resetSifState() + { + std::lock_guard lock(g_sifCmdStateMutex); + seedDefaultSifRegsLocked(); + } -void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reqSize = getRegU32(ctx, 4); - const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); - if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) + void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + const uint32_t cid = getRegU32(ctx, 4); + const uint32_t handler = getRegU32(ctx, 5); + std::lock_guard lock(g_sifCmdStateMutex); + g_sifCmdHandlers[cid] = handler; setReturnS32(ctx, 0); - return; } - const uint32_t allocAddr = g_iopHeapNext; - g_iopHeapNext += alignedSize; - setReturnS32(ctx, static_cast(allocAddr)); -} - -void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifBindRpc(rdram, ctx, runtime); -} - -void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime); -} + void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t reqSize = getRegU32(ctx, 4); + const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); + if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) + { + setReturnS32(ctx, 0); + return; + } -void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - (void)getRegU32(ctx, 4); // trid + const uint32_t allocAddr = g_iopHeapNext; + g_iopHeapNext += alignedSize; + setReturnS32(ctx, static_cast(allocAddr)); + } - // Transfers are applied immediately by sceSifSetDma in this runtime. - setReturnS32(ctx, -1); -} + void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifBindRpc(rdram, ctx, runtime); + } -void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime); + } -void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard lock(g_sifCmdStateMutex); - seedDefaultSifRegsLocked(); - setReturnS32(ctx, 0); -} + void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + (void)getRegU32(ctx, 4); // trid -void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + // Transfers are applied immediately by sceSifSetDma in this runtime. + setReturnS32(ctx, -1); + } -void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard lock(g_sifCmdStateMutex); - setReturnU32(ctx, g_sifCmdBuffer); -} + void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + std::lock_guard lock(g_sifCmdStateMutex); + seedDefaultSifRegsLocked(); + setReturnS32(ctx, 0); + } -void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 4)); -} + void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; + void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + std::lock_guard lock(g_sifCmdStateMutex); + setReturnU32(ctx, g_sifCmdBuffer); + } - const uint32_t rdAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - const uint32_t dstAddr = getRegU32(ctx, 6); - const int32_t sizeSigned = static_cast(getRegU32(ctx, 7)); + void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, getRegU32(ctx, 4)); + } - if (sizeSigned <= 0) + void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnS32(ctx, 0); - return; } - const uint32_t size = static_cast(sizeSigned); - if (size > PS2_RAM_SIZE) + void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - static uint32_t warnCount = 0; - if (warnCount < 32u) + (void)runtime; + + const uint32_t rdAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + const uint32_t dstAddr = getRegU32(ctx, 6); + const int32_t sizeSigned = static_cast(getRegU32(ctx, 7)); + + if (sizeSigned <= 0) { - std::cerr << "sceSifGetOtherData rejected oversized transfer size=0x" - << std::hex << size << std::dec << std::endl; - ++warnCount; + setReturnS32(ctx, 0); + return; + } + + const uint32_t size = static_cast(sizeSigned); + if (size > PS2_RAM_SIZE) + { + static uint32_t warnCount = 0; + if (warnCount < 32u) + { + std::cerr << "sceSifGetOtherData rejected oversized transfer size=0x" + << std::hex << size << std::dec << std::endl; + ++warnCount; + } + setReturnS32(ctx, -1); + return; } - setReturnS32(ctx, -1); - return; - } - ps2_syscalls::prepareSoundDriverStatusTransfer(rdram, srcAddr, size); + ps2_syscalls::prepareSoundDriverStatusTransfer(rdram, srcAddr, size); - if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size)) - { - static uint32_t warnCount = 0; - if (warnCount < 32u) + if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size)) { - std::cerr << "sceSifGetOtherData copy failed src=0x" << std::hex << srcAddr - << " dst=0x" << dstAddr - << " size=0x" << size - << std::dec << std::endl; - ++warnCount; + static uint32_t warnCount = 0; + if (warnCount < 32u) + { + std::cerr << "sceSifGetOtherData copy failed src=0x" << std::hex << srcAddr + << " dst=0x" << dstAddr + << " size=0x" << size + << std::dec << std::endl; + ++warnCount; + } + setReturnS32(ctx, -1); + return; } - setReturnS32(ctx, -1); - return; - } - // SifRpcReceiveData_t keeps src/dest/size at offsets 0x10/0x14/0x18. - if (uint8_t *rd = getMemPtr(rdram, rdAddr)) - { - std::memcpy(rd + 0x10u, &srcAddr, sizeof(srcAddr)); - std::memcpy(rd + 0x14u, &dstAddr, sizeof(dstAddr)); - std::memcpy(rd + 0x18u, &size, sizeof(size)); - } + // SifRpcReceiveData_t keeps src/dest/size at offsets 0x10/0x14/0x18. + if (uint8_t *rd = getMemPtr(rdram, rdAddr)) + { + std::memcpy(rd + 0x10u, &srcAddr, sizeof(srcAddr)); + std::memcpy(rd + 0x14u, &dstAddr, sizeof(dstAddr)); + std::memcpy(rd + 0x18u, &size, sizeof(size)); + } - ps2_syscalls::finalizeSoundDriverStatusTransfer(rdram, srcAddr, dstAddr, size); + ps2_syscalls::finalizeSoundDriverStatusTransfer(rdram, srcAddr, dstAddr, size); - setReturnS32(ctx, 0); -} + setReturnS32(ctx, 0); + } -void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - uint32_t value = 0u; - bool shouldLog = false; + void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifRegs.find(reg); - if (it != g_sifRegs.end()) + const uint32_t reg = getRegU32(ctx, 4); + uint32_t value = 0u; + bool shouldLog = false; { - value = it->second; + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifRegs.find(reg); + if (it != g_sifRegs.end()) + { + value = it->second; + } + shouldLog = shouldTraceSifReg(reg) && g_sifGetRegLogCount < 128u; + if (shouldLog) + { + ++g_sifGetRegLogCount; + } } - shouldLog = shouldTraceSifReg(reg) && g_sifGetRegLogCount < 128u; if (shouldLog) { - ++g_sifGetRegLogCount; + auto flags = std::cerr.flags(); + std::cerr << "[sceSifGetReg] reg=0x" << std::hex << reg + << " value=0x" << value + << " pc=0x" << (ctx ? ctx->pc : 0u) + << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) + << std::dec << std::endl; + std::cerr.flags(flags); } + setReturnU32(ctx, value); } - if (shouldLog) - { - auto flags = std::cerr.flags(); - std::cerr << "[sceSifGetReg] reg=0x" << std::hex << reg - << " value=0x" << value - << " pc=0x" << (ctx ? ctx->pc : 0u) - << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) - << std::dec << std::endl; - std::cerr.flags(flags); - } - setReturnU32(ctx, value); -} -void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - uint32_t value = 0u; + void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifSregs.find(reg); - if (it != g_sifSregs.end()) + const uint32_t reg = getRegU32(ctx, 4); + uint32_t value = 0u; { - value = it->second; + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifSregs.find(reg); + if (it != g_sifSregs.end()) + { + value = it->second; + } } + setReturnU32(ctx, value); } - setReturnU32(ctx, value); -} - -void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdInitialized = true; - setReturnS32(ctx, 0); -} - -void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - g_iopHeapNext = kIopHeapBase; - setReturnS32(ctx, 0); -} -void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifInitRpc(rdram, ctx, runtime); -} + void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + std::lock_guard lock(g_sifCmdStateMutex); + g_sifCmdInitialized = true; + setReturnS32(ctx, 0); + } -void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + g_iopHeapNext = kIopHeapBase; + setReturnS32(ctx, 0); + } -void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime); -} + void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifInitRpc(rdram, ctx, runtime); + } -void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime); -} + void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime); + } -void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime); + } -void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime); -} + void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime); -} + void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime); + } -void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cid = getRegU32(ctx, 4); - std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdHandlers.erase(cid); - setReturnS32(ctx, 0); -} + void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime); -} + void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime); + } -void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime); -} + void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t cid = getRegU32(ctx, 4); + std::lock_guard lock(g_sifCmdStateMutex); + g_sifCmdHandlers.erase(cid); + setReturnS32(ctx, 0); + } -void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime); + } -void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime); + } -void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t newBuffer = getRegU32(ctx, 4); - uint32_t prev = 0u; + void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sifCmdStateMutex); - prev = g_sifCmdBuffer; - g_sifCmdBuffer = newBuffer; + setReturnS32(ctx, 1); } - setReturnU32(ctx, prev); -} -void isceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - sceSifSetDChain(rdram, ctx, runtime); -} + void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void isceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - sceSifSetDma(rdram, ctx, runtime); -} + void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t newBuffer = getRegU32(ctx, 4); + uint32_t prev = 0u; + { + std::lock_guard lock(g_sifCmdStateMutex); + prev = g_sifCmdBuffer; + g_sifCmdBuffer = newBuffer; + } + setReturnU32(ctx, prev); + } -void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnS32(ctx, 0); -} + void isceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + sceSifSetDChain(rdram, ctx, runtime); + } -void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; + void isceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + sceSifSetDma(rdram, ctx, runtime); + } - const uint32_t dmatAddr = getRegU32(ctx, 4); - const uint32_t count = getRegU32(ctx, 5); - if (!dmatAddr || count == 0u || count > 32u) + void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + (void)rdram; + (void)runtime; setReturnS32(ctx, 0); - return; } - std::array pending{}; - uint32_t pendingCount = 0u; - bool ok = true; - for (uint32_t i = 0; i < count; ++i) + void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const uint32_t entryAddr = dmatAddr + (i * static_cast(sizeof(Ps2SifDmaTransfer))); - const uint8_t *entry = getConstMemPtr(rdram, entryAddr); - if (!entry) - { - ok = false; - break; - } + (void)runtime; - Ps2SifDmaTransfer xfer{}; - std::memcpy(&xfer, entry, sizeof(xfer)); - if (xfer.size <= 0) + const uint32_t dmatAddr = getRegU32(ctx, 4); + const uint32_t count = getRegU32(ctx, 5); + if (!dmatAddr || count == 0u || count > 32u) { - continue; + setReturnS32(ctx, 0); + return; } - const uint32_t sizeBytes = static_cast(xfer.size); - if (sizeBytes > PS2_RAM_SIZE) - { - ok = false; - break; - } - if (!canCopyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes)) + std::array pending{}; + uint32_t pendingCount = 0u; + bool ok = true; + for (uint32_t i = 0; i < count; ++i) { - ok = false; - break; - } + const uint32_t entryAddr = dmatAddr + (i * static_cast(sizeof(Ps2SifDmaTransfer))); + const uint8_t *entry = getConstMemPtr(rdram, entryAddr); + if (!entry) + { + ok = false; + break; + } - pending[pendingCount++] = xfer; - } + Ps2SifDmaTransfer xfer{}; + std::memcpy(&xfer, entry, sizeof(xfer)); + if (xfer.size <= 0) + { + continue; + } - if (ok) - { - for (uint32_t i = 0; i < pendingCount; ++i) - { - const Ps2SifDmaTransfer &xfer = pending[i]; - if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast(xfer.size))) + const uint32_t sizeBytes = static_cast(xfer.size); + if (sizeBytes > PS2_RAM_SIZE) + { + ok = false; + break; + } + if (!canCopyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes)) { ok = false; break; } - ps2_syscalls::noteDtxSifDmaTransfer( - rdram, - xfer.src, - xfer.dest, - static_cast(xfer.size)); + pending[pendingCount++] = xfer; } - } - if (!ok) - { - static uint32_t warnCount = 0; - if (warnCount < 32u) + if (ok) { - std::cerr << "sceSifSetDma failed dmat=0x" << std::hex << dmatAddr - << " count=0x" << count - << std::dec << std::endl; - ++warnCount; + for (uint32_t i = 0; i < pendingCount; ++i) + { + const Ps2SifDmaTransfer &xfer = pending[i]; + if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast(xfer.size))) + { + ok = false; + break; + } + + ps2_syscalls::noteDtxSifDmaTransfer( + rdram, + xfer.src, + xfer.dest, + static_cast(xfer.size)); + } + } + + if (!ok) + { + static uint32_t warnCount = 0; + if (warnCount < 32u) + { + std::cerr << "sceSifSetDma failed dmat=0x" << std::hex << dmatAddr + << " count=0x" << count + << std::dec << std::endl; + ++warnCount; + } + setReturnS32(ctx, 0); + return; } - setReturnS32(ctx, 0); - return; - } - ps2_syscalls::dispatchDmacHandlersForCause(rdram, runtime, 5u); + ps2_syscalls::dispatchDmacHandlersForCause(rdram, runtime, 5u); - setReturnS32(ctx, static_cast(allocateSifDmaTransferId())); -} + setReturnS32(ctx, static_cast(allocateSifDmaTransferId())); + } -void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnU32(ctx, getRegU32(ctx, 5)); -} + void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, getRegU32(ctx, 5)); + } -void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - const uint32_t value = getRegU32(ctx, 5); - uint32_t prev = 0u; - bool shouldLog = false; + void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifRegs.find(reg); - if (it != g_sifRegs.end()) + const uint32_t reg = getRegU32(ctx, 4); + const uint32_t value = getRegU32(ctx, 5); + uint32_t prev = 0u; + bool shouldLog = false; { - prev = it->second; + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifRegs.find(reg); + if (it != g_sifRegs.end()) + { + prev = it->second; + } + g_sifRegs[reg] = value; + shouldLog = shouldTraceSifReg(reg) && g_sifSetRegLogCount < 128u; + if (shouldLog) + { + ++g_sifSetRegLogCount; + } } - g_sifRegs[reg] = value; - shouldLog = shouldTraceSifReg(reg) && g_sifSetRegLogCount < 128u; if (shouldLog) { - ++g_sifSetRegLogCount; + auto flags = std::cerr.flags(); + std::cerr << "[sceSifSetReg] reg=0x" << std::hex << reg + << " prev=0x" << prev + << " value=0x" << value + << " pc=0x" << (ctx ? ctx->pc : 0u) + << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) + << std::dec << std::endl; + std::cerr.flags(flags); } + setReturnU32(ctx, prev); } - if (shouldLog) + + void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - auto flags = std::cerr.flags(); - std::cerr << "[sceSifSetReg] reg=0x" << std::hex << reg - << " prev=0x" << prev - << " value=0x" << value - << " pc=0x" << (ctx ? ctx->pc : 0u) - << " ra=0x" << (ctx ? getRegU32(ctx, 31) : 0u) - << std::dec << std::endl; - std::cerr.flags(flags); + ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime); } - setReturnU32(ctx, prev); -} -void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime); -} - -void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t reg = getRegU32(ctx, 4); - const uint32_t value = getRegU32(ctx, 5); - uint32_t prev = 0u; + void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sifCmdStateMutex); - auto it = g_sifSregs.find(reg); - if (it != g_sifSregs.end()) + const uint32_t reg = getRegU32(ctx, 4); + const uint32_t value = getRegU32(ctx, 5); + uint32_t prev = 0u; { - prev = it->second; + std::lock_guard lock(g_sifCmdStateMutex); + auto it = g_sifSregs.find(reg); + if (it != g_sifSregs.end()) + { + prev = it->second; + } + g_sifSregs[reg] = value; } - g_sifSregs[reg] = value; + setReturnU32(ctx, prev); } - setReturnU32(ctx, prev); -} -void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t newBuffer = getRegU32(ctx, 4); - uint32_t prev = 0u; + void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sifCmdStateMutex); - prev = g_sifSysCmdBuffer; - g_sifSysCmdBuffer = newBuffer; + const uint32_t newBuffer = getRegU32(ctx, 4); + uint32_t prev = 0u; + { + std::lock_guard lock(g_sifCmdStateMutex); + prev = g_sifSysCmdBuffer; + g_sifSysCmdBuffer = newBuffer; + } + setReturnU32(ctx, prev); } - setReturnU32(ctx, prev); -} -void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} + void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } -void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp index a87653c3..e2df2b54 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/System.cpp @@ -3,65 +3,59 @@ namespace ps2_stubs { -void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - if (logCount < 8) + void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("ps2_stub builtin_set_imask"); - ++logCount; + static int logCount = 0; + if (logCount < 8) + { + RUNTIME_LOG("ps2_stub builtin_set_imask"); + ++logCount; + } + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} - - -void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - - -void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} - -void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - if (runtime) + void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - runtime->requestStop(); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} + void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 1); -} - + void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + if (runtime) + { + runtime->requestStop(); + } + setReturnS32(ctx, 0); + } -void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime); -} + void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 1); + } + void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime); + } -void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceSetPtm", rdram, ctx, runtime); -} + void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceSetPtm", rdram, ctx, runtime); + } -void sceDevVif0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceDevVif0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceDevVu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceDevVu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp index 57063b00..6eb35d9a 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/TTY.cpp @@ -4,58 +4,56 @@ namespace ps2_stubs { -void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t format_addr = getRegU32(ctx, 4); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - if (format_addr == 0) - return; - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); - if (rendered.size() > 2048) - rendered.resize(2048); - PS2_IF_AGRESSIVE_LOGS({ - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - RUNTIME_LOG("PS2 scePrintf: " << logLine); - RUNTIME_LOG(std::flush); - } - else if (count == kMaxPrintfLogs + 1) - { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; - } - }); -} - - -void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceResetttyinit", rdram, ctx, runtime); -} - - -void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyHandler", rdram, ctx, runtime); -} - -void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyInit", rdram, ctx, runtime); -} - -void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyRead", rdram, ctx, runtime); -} - -void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceTtyWrite", rdram, ctx, runtime); -} + void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t format_addr = getRegU32(ctx, 4); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + if (format_addr == 0) + return; + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); + if (rendered.size() > 2048) + rendered.resize(2048); + PS2_IF_AGRESSIVE_LOGS({ + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + RUNTIME_LOG("PS2 scePrintf: " << logLine); + RUNTIME_LOG(std::flush); + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + }); + } + + void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceResetttyinit", rdram, ctx, runtime); + } + + void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceTtyHandler", rdram, ctx, runtime); + } + + void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceTtyInit", rdram, ctx, runtime); + } + + void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceTtyRead", rdram, ctx, runtime); + } + + void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceTtyWrite", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp index 6050b5a6..561d619f 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Unimplemented.cpp @@ -8,7 +8,6 @@ namespace ps2_stubs TODO_NAMED("unknown", rdram, ctx, runtime); } - void TODO_NAMED(const char *name, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const std::string stubName = name ? name : "unknown"; @@ -44,6 +43,7 @@ namespace ps2_stubs << ", $a2=0x" << getRegU32(ctx, 6) << ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl; - setReturnS32(ctx, -1); // Return error + //TODO maybe a macro to disable the exception and just return an success just to see it where goes. + throw std::runtime_error("Unimplemented PS2 stub called: " + stubName); } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp index 309d9e9a..22ad95a0 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp @@ -1,435 +1,435 @@ #include "Common.h" #include "VU.h" +//TODO use glm namespace ps2_stubs { -void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime); -} - + void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime); + } -namespace -{ - bool readVuVec4f(uint8_t *rdram, uint32_t addr, float (&out)[4]) + namespace { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) + bool readVuVec4f(uint8_t *rdram, uint32_t addr, float (&out)[4]) { - return false; + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(out, ptr, sizeof(out)); + return true; } - std::memcpy(out, ptr, sizeof(out)); - return true; - } - bool writeVuVec4f(uint8_t *rdram, uint32_t addr, const float (&in)[4]) - { - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) + bool writeVuVec4f(uint8_t *rdram, uint32_t addr, const float (&in)[4]) { - return false; + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, in, sizeof(in)); + return true; } - std::memcpy(ptr, in, sizeof(in)); - return true; - } - bool readVuVec4i(uint8_t *rdram, uint32_t addr, int32_t (&out)[4]) - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) + bool readVuVec4i(uint8_t *rdram, uint32_t addr, int32_t (&out)[4]) { - return false; + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(out, ptr, sizeof(out)); + return true; } - std::memcpy(out, ptr, sizeof(out)); - return true; - } - bool writeVuVec4i(uint8_t *rdram, uint32_t addr, const int32_t (&in)[4]) - { - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) + bool writeVuVec4i(uint8_t *rdram, uint32_t addr, const int32_t (&in)[4]) { - return false; + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, in, sizeof(in)); + return true; } - std::memcpy(ptr, in, sizeof(in)); - return true; } -} -void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnS32(ctx, 0); + } -void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t lhsAddr = getRegU32(ctx, 5); - const uint32_t rhsAddr = getRegU32(ctx, 6); - float lhs[4]{}, rhs[4]{}, out[4]{}; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t lhsAddr = getRegU32(ctx, 5); + const uint32_t rhsAddr = getRegU32(ctx, 6); + float lhs[4]{}, rhs[4]{}, out[4]{}; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) { - out[i] = lhs[i] + rhs[i]; + for (int i = 0; i < 4; ++i) + { + out[i] = lhs[i] + rhs[i]; + } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); -} + void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); + } -void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime); -} + void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime); + } -void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime); -} + void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime); + } -void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime); -} + void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime); + } -void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime); -} + void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime); + } -void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime); -} + void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime); + } -void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); -} + void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); + } -void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); -} + void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); + } -void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); -} + void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); + } -void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime); -} + void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime); + } -void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime); -} + void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime); + } -void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime); -} + void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime); + } -void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}; - int32_t out[4]{}; - if (readVuVec4f(rdram, srcAddr, src)) + void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}; + int32_t out[4]{}; + if (readVuVec4f(rdram, srcAddr, src)) { - out[i] = static_cast(src[i]); + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]); + } + (void)writeVuVec4i(rdram, dstAddr, out); } - (void)writeVuVec4i(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}; - int32_t out[4]{}; - if (readVuVec4f(rdram, srcAddr, src)) + void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}; + int32_t out[4]{}; + if (readVuVec4f(rdram, srcAddr, src)) { - out[i] = static_cast(src[i] * 16.0f); + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i] * 16.0f); + } + (void)writeVuVec4i(rdram, dstAddr, out); } - (void)writeVuVec4i(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t lhsAddr = getRegU32(ctx, 4); - const uint32_t rhsAddr = getRegU32(ctx, 5); - float lhs[4]{}, rhs[4]{}; - float dot = 0.0f; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - dot = (lhs[0] * rhs[0]) + (lhs[1] * rhs[1]) + (lhs[2] * rhs[2]) + (lhs[3] * rhs[3]); + const uint32_t lhsAddr = getRegU32(ctx, 4); + const uint32_t rhsAddr = getRegU32(ctx, 5); + float lhs[4]{}, rhs[4]{}; + float dot = 0.0f; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + { + dot = (lhs[0] * rhs[0]) + (lhs[1] * rhs[1]) + (lhs[2] * rhs[2]) + (lhs[3] * rhs[3]); + } + + if (ctx) + { + ctx->f[0] = dot; + } + uint32_t raw = 0u; + std::memcpy(&raw, &dot, sizeof(raw)); + setReturnU32(ctx, raw); } - if (ctx) + void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - ctx->f[0] = dot; + TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime); } - uint32_t raw = 0u; - std::memcpy(&raw, &dot, sizeof(raw)); - setReturnU32(ctx, raw); -} - -void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime); -} -void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime); -} + void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime); + } -void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime); -} + void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime); + } -void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - int32_t src[4]{}; - float out[4]{}; - if (readVuVec4i(rdram, srcAddr, src)) + void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + int32_t src[4]{}; + float out[4]{}; + if (readVuVec4i(rdram, srcAddr, src)) { - out[i] = static_cast(src[i]); + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]); + } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - int32_t src[4]{}; - float out[4]{}; - if (readVuVec4i(rdram, srcAddr, src)) + void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + int32_t src[4]{}; + float out[4]{}; + if (readVuVec4i(rdram, srcAddr, src)) { - out[i] = static_cast(src[i]) / 4096.0f; + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]) / 4096.0f; + } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - int32_t src[4]{}; - float out[4]{}; - if (readVuVec4i(rdram, srcAddr, src)) + void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + int32_t src[4]{}; + float out[4]{}; + if (readVuVec4i(rdram, srcAddr, src)) { - out[i] = static_cast(src[i]) / 16.0f; + for (int i = 0; i < 4; ++i) + { + out[i] = static_cast(src[i]) / 16.0f; + } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime); -} + void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime); + } -void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime); -} + void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime); + } -void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime); -} + void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime); + } -void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}, out[4]{}; - if (readVuVec4f(rdram, srcAddr, src)) + void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - const float len = std::sqrt((src[0] * src[0]) + (src[1] * src[1]) + (src[2] * src[2]) + (src[3] * src[3])); - if (len > 1.0e-6f) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}, out[4]{}; + if (readVuVec4f(rdram, srcAddr, src)) { - const float invLen = 1.0f / len; - for (int i = 0; i < 4; ++i) + const float len = std::sqrt((src[0] * src[0]) + (src[1] * src[1]) + (src[2] * src[2]) + (src[3] * src[3])); + if (len > 1.0e-6f) { - out[i] = src[i] * invLen; + const float invLen = 1.0f / len; + for (int i = 0; i < 4; ++i) + { + out[i] = src[i] * invLen; + } } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime); -} + void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime); + } -void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t lhsAddr = getRegU32(ctx, 5); - const uint32_t rhsAddr = getRegU32(ctx, 6); - float lhs[4]{}, rhs[4]{}, out[4]{}; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) - { - out[0] = (lhs[1] * rhs[2]) - (lhs[2] * rhs[1]); - out[1] = (lhs[2] * rhs[0]) - (lhs[0] * rhs[2]); - out[2] = (lhs[0] * rhs[1]) - (lhs[1] * rhs[0]); - out[3] = 0.0f; - (void)writeVuVec4f(rdram, dstAddr, out); - } - setReturnS32(ctx, 0); -} + void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t lhsAddr = getRegU32(ctx, 5); + const uint32_t rhsAddr = getRegU32(ctx, 6); + float lhs[4]{}, rhs[4]{}, out[4]{}; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + { + out[0] = (lhs[1] * rhs[2]) - (lhs[2] * rhs[1]); + out[1] = (lhs[2] * rhs[0]) - (lhs[0] * rhs[2]); + out[2] = (lhs[0] * rhs[1]) - (lhs[1] * rhs[0]); + out[3] = 0.0f; + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); + } -void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime); -} + void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime); + } -void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); -} + void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); + } -void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); -} + void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); + } -void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); -} + void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); + } -void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime); -} + void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime); + } -void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime); -} + void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime); + } -void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t srcAddr = getRegU32(ctx, 5); - float src[4]{}, out[4]{}; - float scale = ctx ? ctx->f[12] : 0.0f; - if (scale == 0.0f) - { - uint32_t raw = getRegU32(ctx, 6); - std::memcpy(&scale, &raw, sizeof(scale)); + void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[4]{}, out[4]{}; + float scale = ctx ? ctx->f[12] : 0.0f; if (scale == 0.0f) { - scale = static_cast(getRegU32(ctx, 6)); + uint32_t raw = getRegU32(ctx, 6); + std::memcpy(&scale, &raw, sizeof(scale)); + if (scale == 0.0f) + { + scale = static_cast(getRegU32(ctx, 6)); + } } - } - if (readVuVec4f(rdram, srcAddr, src)) - { - for (int i = 0; i < 4; ++i) + if (readVuVec4f(rdram, srcAddr, src)) { - out[i] = src[i] * scale; + for (int i = 0; i < 4; ++i) + { + out[i] = src[i] * scale; + } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime); -} + void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime); + } -void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); - const uint32_t lhsAddr = getRegU32(ctx, 5); - const uint32_t rhsAddr = getRegU32(ctx, 6); - float lhs[4]{}, rhs[4]{}, out[4]{}; - if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) + void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (int i = 0; i < 4; ++i) + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t lhsAddr = getRegU32(ctx, 5); + const uint32_t rhsAddr = getRegU32(ctx, 6); + float lhs[4]{}, rhs[4]{}, out[4]{}; + if (readVuVec4f(rdram, lhsAddr, lhs) && readVuVec4f(rdram, rhsAddr, rhs)) { - out[i] = lhs[i] - rhs[i]; + for (int i = 0; i < 4; ++i) + { + out[i] = lhs[i] - rhs[i]; + } + (void)writeVuVec4f(rdram, dstAddr, out); } - (void)writeVuVec4f(rdram, dstAddr, out); + setReturnS32(ctx, 0); } - setReturnS32(ctx, 0); -} -void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime); -} - -void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); -} + void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime); + } -void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst - alignas(16) const float identity[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; + void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); + } - if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast(identity), sizeof(identity))) + void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - static uint32_t warnCount = 0; - if (warnCount < 8) + const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst + alignas(16) const float identity[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + + if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast(identity), sizeof(identity))) { - std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x" - << std::hex << dstAddr << std::dec << std::endl; - ++warnCount; + static uint32_t warnCount = 0; + if (warnCount < 8) + { + std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x" + << std::hex << dstAddr << std::dec << std::endl; + ++warnCount; + } } - } - setReturnS32(ctx, 0); -} + setReturnS32(ctx, 0); + } -void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime); -} + void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp index 555921d0..172965fd 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp @@ -3,510 +3,510 @@ namespace ps2_syscalls { -static int allocatePs2Fd(FILE *file) -{ - if (!file) - return -1; + static int allocatePs2Fd(FILE *file) + { + if (!file) + return -1; - std::lock_guard lock(g_fd_mutex); - int fd = g_nextFd++; - g_fileDescriptors[fd] = file; - return fd; -} + std::lock_guard lock(g_fd_mutex); + int fd = g_nextFd++; + g_fileDescriptors[fd] = file; + return fd; + } -static FILE *getHostFile(int ps2Fd) -{ - std::lock_guard lock(g_fd_mutex); - auto it = g_fileDescriptors.find(ps2Fd); - if (it != g_fileDescriptors.end()) + static FILE *getHostFile(int ps2Fd) { - return it->second; + std::lock_guard lock(g_fd_mutex); + auto it = g_fileDescriptors.find(ps2Fd); + if (it != g_fileDescriptors.end()) + { + return it->second; + } + return nullptr; } - return nullptr; -} - -static void releasePs2Fd(int ps2Fd) -{ - std::lock_guard lock(g_fd_mutex); - g_fileDescriptors.erase(ps2Fd); -} - -struct VagAccumEntry -{ - std::vector data; - uint32_t firstBufAddr = 0; -}; -static std::unordered_map g_vagAccum; -static std::mutex g_vagAccumMutex; -static constexpr size_t kVagAccumMaxBytes = 16 * 1024 * 1024; - -static const char *translateFioMode(int ps2Flags) -{ - bool read = (ps2Flags & PS2_FIO_O_RDONLY) || (ps2Flags & PS2_FIO_O_RDWR); - bool write = (ps2Flags & PS2_FIO_O_WRONLY) || (ps2Flags & PS2_FIO_O_RDWR); - bool append = (ps2Flags & PS2_FIO_O_APPEND); - bool create = (ps2Flags & PS2_FIO_O_CREAT); - bool truncate = (ps2Flags & PS2_FIO_O_TRUNC); - if (read && write) + static void releasePs2Fd(int ps2Fd) { - if (create && truncate) - return "w+b"; - if (create) - return "a+b"; - return "r+b"; + std::lock_guard lock(g_fd_mutex); + g_fileDescriptors.erase(ps2Fd); } - else if (write) + + struct VagAccumEntry { - if (append) - return "ab"; - if (create && truncate) - return "wb"; - if (create) - return "wx"; - return "r+b"; - } - else if (read) + std::vector data; + uint32_t firstBufAddr = 0; + }; + static std::unordered_map g_vagAccum; + static std::mutex g_vagAccumMutex; + static constexpr size_t kVagAccumMaxBytes = 16 * 1024 * 1024; + + static const char *translateFioMode(int ps2Flags) { + bool read = (ps2Flags & PS2_FIO_O_RDONLY) || (ps2Flags & PS2_FIO_O_RDWR); + bool write = (ps2Flags & PS2_FIO_O_WRONLY) || (ps2Flags & PS2_FIO_O_RDWR); + bool append = (ps2Flags & PS2_FIO_O_APPEND); + bool create = (ps2Flags & PS2_FIO_O_CREAT); + bool truncate = (ps2Flags & PS2_FIO_O_TRUNC); + + if (read && write) + { + if (create && truncate) + return "w+b"; + if (create) + return "a+b"; + return "r+b"; + } + else if (write) + { + if (append) + return "ab"; + if (create && truncate) + return "wb"; + if (create) + return "wx"; + return "r+b"; + } + else if (read) + { + return "rb"; + } return "rb"; } - return "rb"; -} - -void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - int flags = (int)getRegU32(ctx, 5); // $a1 (PS2 FIO flags) - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) + void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fioOpen error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); - return; - } + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + int flags = (int)getRegU32(ctx, 5); // $a1 (PS2 FIO flags) - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioOpen error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioOpen error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); + return; + } - const char *mode = translateFioMode(flags); - RUNTIME_LOG("fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'"); + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioOpen error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } - FILE *fp = ::fopen(hostPath.c_str(), mode); - if (!fp) - { - std::cerr << "fioOpen error: fopen failed for '" << hostPath << "': " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); // e.g., -ENOENT, -EACCES - return; - } + const char *mode = translateFioMode(flags); + RUNTIME_LOG("fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'"); - int ps2Fd = allocatePs2Fd(fp); - if (ps2Fd < 0) - { - std::cerr << "fioOpen error: Failed to allocate PS2 file descriptor" << std::endl; - ::fclose(fp); - setReturnS32(ctx, -1); // e.g., -EMFILE - return; - } + FILE *fp = ::fopen(hostPath.c_str(), mode); + if (!fp) + { + std::cerr << "fioOpen error: fopen failed for '" << hostPath << "': " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); // e.g., -ENOENT, -EACCES + return; + } - // returns the PS2 file descriptor - setReturnS32(ctx, ps2Fd); -} + int ps2Fd = allocatePs2Fd(fp); + if (ps2Fd < 0) + { + std::cerr << "fioOpen error: Failed to allocate PS2 file descriptor" << std::endl; + ::fclose(fp); + setReturnS32(ctx, -1); // e.g., -EMFILE + return; + } -void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int ps2Fd = (int)getRegU32(ctx, 4); + // returns the PS2 file descriptor + setReturnS32(ctx, ps2Fd); + } - FILE *fp = getHostFile(ps2Fd); - if (!fp) + void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); - return; - } + int ps2Fd = (int)getRegU32(ctx, 4); - int ret = ::fclose(fp); - releasePs2Fd(ps2Fd); + FILE *fp = getHostFile(ps2Fd); + if (!fp) + { + std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl; + setReturnS32(ctx, -1); + return; + } + + int ret = ::fclose(fp); + releasePs2Fd(ps2Fd); - { - std::lock_guard lock(g_vagAccumMutex); - auto it = g_vagAccum.find(ps2Fd); - if (it != g_vagAccum.end()) { - VagAccumEntry &e = it->second; - if (e.data.size() >= 48) + std::lock_guard lock(g_vagAccumMutex); + auto it = g_vagAccum.find(ps2Fd); + if (it != g_vagAccum.end()) { - const uint32_t magic = (static_cast(e.data[0]) << 24) | - (static_cast(e.data[1]) << 16) | - (static_cast(e.data[2]) << 8) | - static_cast(e.data[3]); - const uint32_t magicLE = (static_cast(e.data[3]) << 24) | - (static_cast(e.data[2]) << 16) | - (static_cast(e.data[1]) << 8) | - static_cast(e.data[0]); - if (magic == 0x56414770u || magicLE == 0x56414770u) + VagAccumEntry &e = it->second; + if (e.data.size() >= 48) { - if (runtime) - runtime->audioBackend().onVagTransferFromBuffer( - e.data.data(), static_cast(e.data.size()), - e.firstBufAddr ? e.firstBufAddr : 0u); + const uint32_t magic = (static_cast(e.data[0]) << 24) | + (static_cast(e.data[1]) << 16) | + (static_cast(e.data[2]) << 8) | + static_cast(e.data[3]); + const uint32_t magicLE = (static_cast(e.data[3]) << 24) | + (static_cast(e.data[2]) << 16) | + (static_cast(e.data[1]) << 8) | + static_cast(e.data[0]); + if (magic == 0x56414770u || magicLE == 0x56414770u) + { + if (runtime) + runtime->audioBackend().onVagTransferFromBuffer( + e.data.data(), static_cast(e.data.size()), + e.firstBufAddr ? e.firstBufAddr : 0u); + } } + g_vagAccum.erase(it); } - g_vagAccum.erase(it); } - } - setReturnS32(ctx, ret == 0 ? 0 : -1); -} + setReturnS32(ctx, ret == 0 ? 0 : -1); + } -void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - uint32_t bufAddr = getRegU32(ctx, 5); // $a1 - size_t size = getRegU32(ctx, 6); // $a2 + void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + uint32_t bufAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 - uint8_t *hostBuf = getMemPtr(rdram, bufAddr); - FILE *fp = getHostFile(ps2Fd); + uint8_t *hostBuf = getMemPtr(rdram, bufAddr); + FILE *fp = getHostFile(ps2Fd); - if (!hostBuf) - { - std::cerr << "fioRead error: Invalid buffer address for fd " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EFAULT - return; - } - if (!fp) - { - std::cerr << "fioRead error: Invalid file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EBADF - return; - } - if (size == 0) - { - setReturnS32(ctx, 0); // Read 0 bytes - return; - } + if (!hostBuf) + { + std::cerr << "fioRead error: Invalid buffer address for fd " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EFAULT + return; + } + if (!fp) + { + std::cerr << "fioRead error: Invalid file descriptor " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EBADF + return; + } + if (size == 0) + { + setReturnS32(ctx, 0); // Read 0 bytes + return; + } - size_t bytesRead = 0; - { - std::lock_guard lock(g_sys_fd_mutex); - bytesRead = fread(hostBuf, 1, size, fp); - } + size_t bytesRead = 0; + { + std::lock_guard lock(g_sys_fd_mutex); + bytesRead = fread(hostBuf, 1, size, fp); + } - if (bytesRead < size && ferror(fp)) - { - std::cerr << "fioRead error: fread failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; - clearerr(fp); - setReturnS32(ctx, -1); - return; - } + if (bytesRead < size && ferror(fp)) + { + std::cerr << "fioRead error: fread failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; + clearerr(fp); + setReturnS32(ctx, -1); + return; + } - { - std::lock_guard lock(g_vagAccumMutex); - auto it = g_vagAccum.find(ps2Fd); - if (it != g_vagAccum.end()) - { - VagAccumEntry &e = it->second; - if (e.data.size() + bytesRead <= kVagAccumMaxBytes) - e.data.insert(e.data.end(), hostBuf, hostBuf + bytesRead); - } - else if (bytesRead >= 4) - { - const uint32_t magic = (static_cast(hostBuf[0]) << 24) | - (static_cast(hostBuf[1]) << 16) | - (static_cast(hostBuf[2]) << 8) | - static_cast(hostBuf[3]); - const uint32_t magicLE = (static_cast(hostBuf[3]) << 24) | - (static_cast(hostBuf[2]) << 16) | - (static_cast(hostBuf[1]) << 8) | - static_cast(hostBuf[0]); - if (magic == 0x56414770u || magicLE == 0x56414770u) + { + std::lock_guard lock(g_vagAccumMutex); + auto it = g_vagAccum.find(ps2Fd); + if (it != g_vagAccum.end()) + { + VagAccumEntry &e = it->second; + if (e.data.size() + bytesRead <= kVagAccumMaxBytes) + e.data.insert(e.data.end(), hostBuf, hostBuf + bytesRead); + } + else if (bytesRead >= 4) { - VagAccumEntry &e = g_vagAccum[ps2Fd]; - e.firstBufAddr = bufAddr; - if (bytesRead <= kVagAccumMaxBytes) - e.data.assign(hostBuf, hostBuf + bytesRead); + const uint32_t magic = (static_cast(hostBuf[0]) << 24) | + (static_cast(hostBuf[1]) << 16) | + (static_cast(hostBuf[2]) << 8) | + static_cast(hostBuf[3]); + const uint32_t magicLE = (static_cast(hostBuf[3]) << 24) | + (static_cast(hostBuf[2]) << 16) | + (static_cast(hostBuf[1]) << 8) | + static_cast(hostBuf[0]); + if (magic == 0x56414770u || magicLE == 0x56414770u) + { + VagAccumEntry &e = g_vagAccum[ps2Fd]; + e.firstBufAddr = bufAddr; + if (bytesRead <= kVagAccumMaxBytes) + e.data.assign(hostBuf, hostBuf + bytesRead); + } } } - } - - setReturnS32(ctx, (int32_t)bytesRead); -} - -void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - uint32_t bufAddr = getRegU32(ctx, 5); // $a1 - size_t size = getRegU32(ctx, 6); // $a2 - const uint8_t *hostBuf = getConstMemPtr(rdram, bufAddr); - if (!hostBuf) - { - setReturnS32(ctx, -1); - return; + setReturnS32(ctx, (int32_t)bytesRead); } - FILE *fp = getHostFile(ps2Fd); - if (!fp) + void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); // -EFAULT - return; - } + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + uint32_t bufAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 - if (size == 0) - { - setReturnS32(ctx, 0); // Wrote 0 bytes - return; - } + const uint8_t *hostBuf = getConstMemPtr(rdram, bufAddr); + if (!hostBuf) + { + setReturnS32(ctx, -1); + return; + } - size_t bytesWritten = 0; - { - std::lock_guard lock(g_sys_fd_mutex); - bytesWritten = ::fwrite(hostBuf, 1, size, fp); - if (bytesWritten < size && ferror(fp)) + FILE *fp = getHostFile(ps2Fd); + if (!fp) { - clearerr(fp); - setReturnS32(ctx, -1); // -EIO, -ENOSPC etc. + setReturnS32(ctx, -1); // -EFAULT return; } - } - // returns number of bytes written - setReturnS32(ctx, (int32_t)bytesWritten); -} + if (size == 0) + { + setReturnS32(ctx, 0); // Wrote 0 bytes + return; + } -void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - int32_t offset = getRegU32(ctx, 5); // $a1 (PS2 seems to use 32-bit offset here commonly) - int whence = (int)getRegU32(ctx, 6); // $a2 (PS2 FIO_SEEK constants) + size_t bytesWritten = 0; + { + std::lock_guard lock(g_sys_fd_mutex); + bytesWritten = ::fwrite(hostBuf, 1, size, fp); + if (bytesWritten < size && ferror(fp)) + { + clearerr(fp); + setReturnS32(ctx, -1); // -EIO, -ENOSPC etc. + return; + } + } - FILE *fp = getHostFile(ps2Fd); - if (!fp) - { - std::cerr << "fioLseek error: Invalid file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EBADF - return; + // returns number of bytes written + setReturnS32(ctx, (int32_t)bytesWritten); } - int hostWhence; - switch (whence) + void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - case PS2_FIO_SEEK_SET: - hostWhence = SEEK_SET; - break; - case PS2_FIO_SEEK_CUR: - hostWhence = SEEK_CUR; - break; - case PS2_FIO_SEEK_END: - hostWhence = SEEK_END; - break; - default: - std::cerr << "fioLseek error: Invalid whence value " << whence << " for fd " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EINVAL - return; - } + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + int32_t offset = getRegU32(ctx, 5); // $a1 (PS2 seems to use 32-bit offset here commonly) + int whence = (int)getRegU32(ctx, 6); // $a2 (PS2 FIO_SEEK constants) - if (::fseek(fp, static_cast(offset), hostWhence) != 0) - { - std::cerr << "fioLseek error: fseek failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); // Return error code - return; - } + FILE *fp = getHostFile(ps2Fd); + if (!fp) + { + std::cerr << "fioLseek error: Invalid file descriptor " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EBADF + return; + } - long newPos = ::ftell(fp); - if (newPos < 0) - { - std::cerr << "fioLseek error: ftell failed after fseek for fd " << ps2Fd << ": " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); - } - else - { - if (newPos > 0xFFFFFFFFL) + int hostWhence; + switch (whence) + { + case PS2_FIO_SEEK_SET: + hostWhence = SEEK_SET; + break; + case PS2_FIO_SEEK_CUR: + hostWhence = SEEK_CUR; + break; + case PS2_FIO_SEEK_END: + hostWhence = SEEK_END; + break; + default: + std::cerr << "fioLseek error: Invalid whence value " << whence << " for fd " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EINVAL + return; + } + + if (::fseek(fp, static_cast(offset), hostWhence) != 0) + { + std::cerr << "fioLseek error: fseek failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); // Return error code + return; + } + + long newPos = ::ftell(fp); + if (newPos < 0) { - std::cerr << "fioLseek warning: New position exceeds 32-bit for fd " << ps2Fd << std::endl; + std::cerr << "fioLseek error: ftell failed after fseek for fd " << ps2Fd << ": " << strerror(errno) << std::endl; setReturnS32(ctx, -1); } else { - setReturnS32(ctx, (int32_t)newPos); + if (newPos > 0xFFFFFFFFL) + { + std::cerr << "fioLseek warning: New position exceeds 32-bit for fd " << ps2Fd << std::endl; + setReturnS32(ctx, -1); + } + else + { + setReturnS32(ctx, (int32_t)newPos); + } } } -} - -void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - // int mode = (int)getRegU32(ctx, 5); // $a1 - ignored on host - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioMkdir error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); // -EFAULT - return; - } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fioMkdir error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } - std::error_code ec; - bool success = std::filesystem::create_directory(hostPath, ec); + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + // int mode = (int)getRegU32(ctx, 5); // $a1 - ignored on host - if (!success && ec) - { - std::cerr << "fioMkdir error: create_directory failed for '" << hostPath - << "': " << ec.message() << std::endl; - setReturnS32(ctx, -1); - } - else - { - RUNTIME_LOG("fioMkdir: Created directory '" << hostPath << "'"); - setReturnS32(ctx, 0); // Success - } -} + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioMkdir error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); // -EFAULT + return; + } + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioMkdir error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } + std::error_code ec; + bool success = std::filesystem::create_directory(hostPath, ec); -void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioChdir error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); - return; + if (!success && ec) + { + std::cerr << "fioMkdir error: create_directory failed for '" << hostPath + << "': " << ec.message() << std::endl; + setReturnS32(ctx, -1); + } + else + { + RUNTIME_LOG("fioMkdir: Created directory '" << hostPath << "'"); + setReturnS32(ctx, 0); // Success + } } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fioChdir error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioChdir error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); + return; + } + + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioChdir error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } - std::error_code ec; - std::filesystem::current_path(hostPath, ec); + std::error_code ec; + std::filesystem::current_path(hostPath, ec); - if (ec) - { - std::cerr << "fioChdir error: current_path failed for '" << hostPath - << "': " << ec.message() << std::endl; - setReturnS32(ctx, -1); - } - else - { - RUNTIME_LOG("fioChdir: Changed directory to '" << hostPath << "'"); - setReturnS32(ctx, 0); // Success + if (ec) + { + std::cerr << "fioChdir error: current_path failed for '" << hostPath + << "': " << ec.message() << std::endl; + setReturnS32(ctx, -1); + } + else + { + RUNTIME_LOG("fioChdir: Changed directory to '" << hostPath << "'"); + setReturnS32(ctx, 0); // Success + } } -} -void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioRmdir error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); - return; - } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fioRmdir error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioRmdir error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); + return; + } + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioRmdir error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } - std::error_code ec; - bool success = std::filesystem::remove(hostPath, ec); + std::error_code ec; + bool success = std::filesystem::remove(hostPath, ec); - if (!success || ec) - { - std::cerr << "fioRmdir error: remove failed for '" << hostPath - << "': " << ec.message() << std::endl; - setReturnS32(ctx, -1); + if (!success || ec) + { + std::cerr << "fioRmdir error: remove failed for '" << hostPath + << "': " << ec.message() << std::endl; + setReturnS32(ctx, -1); + } + else + { + RUNTIME_LOG("fioRmdir: Removed directory '" << hostPath << "'"); + setReturnS32(ctx, 0); // Success + } } - else + + void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("fioRmdir: Removed directory '" << hostPath << "'"); - setReturnS32(ctx, 0); // Success - } -} + // we wont implement this for now. + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + uint32_t statBufAddr = getRegU32(ctx, 5); // $a1 -void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - // we wont implement this for now. - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - uint32_t statBufAddr = getRegU32(ctx, 5); // $a1 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + uint8_t *ps2StatBuf = getMemPtr(rdram, statBufAddr); - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - uint8_t *ps2StatBuf = getMemPtr(rdram, statBufAddr); + if (!ps2Path) + { + std::cerr << "fioGetstat error: Invalid path addr" << std::endl; + setReturnS32(ctx, -1); + return; + } + if (!ps2StatBuf) + { + std::cerr << "fioGetstat error: Invalid buffer addr" << std::endl; + setReturnS32(ctx, -1); + return; + } - if (!ps2Path) - { - std::cerr << "fioGetstat error: Invalid path addr" << std::endl; - setReturnS32(ctx, -1); - return; - } - if (!ps2StatBuf) - { - std::cerr << "fioGetstat error: Invalid buffer addr" << std::endl; - setReturnS32(ctx, -1); - return; - } + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioGetstat error: Bad path translate" << std::endl; + setReturnS32(ctx, -1); + return; + } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioGetstat error: Bad path translate" << std::endl; setReturnS32(ctx, -1); - return; } - setReturnS32(ctx, -1); -} - -void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) + void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "fioRemove error: Invalid path" << std::endl; - setReturnS32(ctx, -1); - return; - } + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioRemove error: Invalid path" << std::endl; + setReturnS32(ctx, -1); + return; + } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioRemove error: Path translate fail" << std::endl; - setReturnS32(ctx, -1); - return; - } + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioRemove error: Path translate fail" << std::endl; + setReturnS32(ctx, -1); + return; + } - std::error_code ec; - bool success = std::filesystem::remove(hostPath, ec); + std::error_code ec; + bool success = std::filesystem::remove(hostPath, ec); - if (!success || ec) - { - std::cerr << "fioRemove error: remove failed for '" << hostPath - << "': " << ec.message() << std::endl; - setReturnS32(ctx, -1); - } - else - { - RUNTIME_LOG("fioRemove: Removed file '" << hostPath << "'"); - setReturnS32(ctx, 0); // Success + if (!success || ec) + { + std::cerr << "fioRemove error: remove failed for '" << hostPath + << "': " << ec.message() << std::endl; + setReturnS32(ctx, -1); + } + else + { + RUNTIME_LOG("fioRemove: Removed file '" << hostPath << "'"); + setReturnS32(ctx, 0); // Success + } } } -} diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp index 8d7e2ecc..ce14411e 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Interrupt.cpp @@ -5,672 +5,671 @@ namespace ps2_syscalls { -namespace interrupt_state -{ - constexpr uint32_t kIntcVblankStart = 2u; - constexpr uint32_t kIntcVblankEnd = 3u; - constexpr auto kVblankPeriod = std::chrono::microseconds(16667); - constexpr int kMaxCatchupTicks = 4; - - std::mutex g_irq_handler_mutex; - std::mutex g_irq_worker_mutex; - std::condition_variable g_irq_worker_cv; - std::mutex g_vsync_flag_mutex; - std::condition_variable g_vsync_cv; - std::atomic g_irq_worker_stop{false}; - std::atomic g_irq_worker_running{false}; - uint32_t g_enabled_intc_mask = 0xFFFFFFFFu; - uint32_t g_enabled_dmac_mask = 0xFFFFFFFFu; - uint64_t g_vsync_tick_counter = 0u; - VSyncFlagRegistration g_vsync_registration{}; -} - -using namespace interrupt_state; - -static void writeGuestU32NoThrow(uint8_t *rdram, uint32_t addr, uint32_t value) -{ - if (addr == 0u) + namespace interrupt_state { - return; - } + constexpr uint32_t kIntcVblankStart = 2u; + constexpr uint32_t kIntcVblankEnd = 3u; + constexpr auto kVblankPeriod = std::chrono::microseconds(16667); + constexpr int kMaxCatchupTicks = 4; - uint8_t *dst = getMemPtr(rdram, addr); - if (!dst) - { - return; + std::mutex g_irq_handler_mutex; + std::mutex g_irq_worker_mutex; + std::condition_variable g_irq_worker_cv; + std::mutex g_vsync_flag_mutex; + std::condition_variable g_vsync_cv; + std::atomic g_irq_worker_stop{false}; + std::atomic g_irq_worker_running{false}; + uint32_t g_enabled_intc_mask = 0xFFFFFFFFu; + uint32_t g_enabled_dmac_mask = 0xFFFFFFFFu; + uint64_t g_vsync_tick_counter = 0u; + VSyncFlagRegistration g_vsync_registration{}; } - std::memcpy(dst, &value, sizeof(value)); -} -static void writeGuestU64NoThrow(uint8_t *rdram, uint32_t addr, uint64_t value) -{ - if (addr == 0u) - { - return; - } + using namespace interrupt_state; - uint8_t *dst = getMemPtr(rdram, addr); - if (!dst) + static void writeGuestU32NoThrow(uint8_t *rdram, uint32_t addr, uint32_t value) { - return; - } - std::memcpy(dst, &value, sizeof(value)); -} + if (addr == 0u) + { + return; + } -static uint32_t readGuestU32NoThrow(uint8_t *rdram, uint32_t addr) -{ - if (addr == 0u) - { - return 0u; + uint8_t *dst = getMemPtr(rdram, addr); + if (!dst) + { + return; + } + std::memcpy(dst, &value, sizeof(value)); } - uint8_t *src = getMemPtr(rdram, addr); - if (!src) + static void writeGuestU64NoThrow(uint8_t *rdram, uint32_t addr, uint64_t value) { - return 0u; + if (addr == 0u) + { + return; + } + + uint8_t *dst = getMemPtr(rdram, addr); + if (!dst) + { + return; + } + std::memcpy(dst, &value, sizeof(value)); } - uint32_t value = 0u; - std::memcpy(&value, src, sizeof(value)); - return value; -} + static uint32_t readGuestU32NoThrow(uint8_t *rdram, uint32_t addr) + { + if (addr == 0u) + { + return 0u; + } -static uint32_t getAsyncHandlerStackTop(PS2Runtime *runtime) -{ - constexpr uint32_t kAsyncHandlerStackSize = 0x4000u; - thread_local PS2Runtime *s_cachedRuntime = nullptr; - thread_local uint32_t s_cachedStackTop = 0u; + uint8_t *src = getMemPtr(rdram, addr); + if (!src) + { + return 0u; + } - if (runtime == nullptr) - { - return PS2_RAM_SIZE - 0x10u; + uint32_t value = 0u; + std::memcpy(&value, src, sizeof(value)); + return value; } - if (s_cachedRuntime != runtime || s_cachedStackTop == 0u) + static uint32_t getAsyncHandlerStackTop(PS2Runtime *runtime) { - s_cachedRuntime = runtime; - s_cachedStackTop = runtime->reserveAsyncCallbackStack(kAsyncHandlerStackSize, 16u); - } + constexpr uint32_t kAsyncHandlerStackSize = 0x4000u; + thread_local PS2Runtime *s_cachedRuntime = nullptr; + thread_local uint32_t s_cachedStackTop = 0u; - return (s_cachedStackTop != 0u) ? s_cachedStackTop : (PS2_RAM_SIZE - 0x10u); -} + if (runtime == nullptr) + { + return PS2_RAM_SIZE - 0x10u; + } -static void dispatchIntcHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause) -{ - if (!rdram || !runtime) - { - return; + if (s_cachedRuntime != runtime || s_cachedStackTop == 0u) + { + s_cachedRuntime = runtime; + s_cachedStackTop = runtime->reserveAsyncCallbackStack(kAsyncHandlerStackSize, 16u); + } + + return (s_cachedStackTop != 0u) ? s_cachedStackTop : (PS2_RAM_SIZE - 0x10u); } - std::vector handlers; + static void dispatchIntcHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause) { - std::lock_guard lock(g_irq_handler_mutex); - if (cause < 32u && (g_enabled_intc_mask & (1u << cause)) == 0u) + if (!rdram || !runtime) { return; } - handlers.reserve(g_intcHandlers.size()); - for (const auto &[id, info] : g_intcHandlers) + std::vector handlers; { - (void)id; - if (!info.enabled) + std::lock_guard lock(g_irq_handler_mutex); + if (cause < 32u && (g_enabled_intc_mask & (1u << cause)) == 0u) { - continue; + return; } - if (info.cause != cause) + + handlers.reserve(g_intcHandlers.size()); + for (const auto &[id, info] : g_intcHandlers) { - continue; + (void)id; + if (!info.enabled) + { + continue; + } + if (info.cause != cause) + { + continue; + } + if (info.handler == 0u) + { + continue; + } + handlers.push_back(info); } - if (info.handler == 0u) + std::sort(handlers.begin(), handlers.end(), [](const IrqHandlerInfo &a, const IrqHandlerInfo &b) + { return a.order < b.order; }); + } + + for (const IrqHandlerInfo &info : handlers) + { + if (!runtime->hasFunction(info.handler)) { + if (cause == kIntcVblankStart) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_missingHandlerLogCount{0u}; + const uint32_t logIndex = s_missingHandlerLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + auto flags = std::cout.flags(); + std::cout << "[INTC:missing] cause=" << cause + << " handler=0x" << std::hex << info.handler + << std::dec + << " id=" << info.id + << std::endl; + std::cout.flags(flags); + } + }); + } continue; } - handlers.push_back(info); + + try + { + R5900Context irqCtx{}; + SET_GPR_U32(&irqCtx, 28, info.gp); + SET_GPR_U32(&irqCtx, 29, getAsyncHandlerStackTop(runtime)); + SET_GPR_U32(&irqCtx, 31, 0u); + SET_GPR_U32(&irqCtx, 4, cause); + SET_GPR_U32(&irqCtx, 5, info.arg); + SET_GPR_U32(&irqCtx, 6, 0u); + SET_GPR_U32(&irqCtx, 7, 0u); + irqCtx.pc = info.handler; + + while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested()) + { + PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc); + if (!step) + { + break; + } + // Interrupt handlers must be able to preempt a guest thread that is + // spinning on interrupt-produced state, such as a vblank counter. + step(rdram, &irqCtx, runtime); + } + } + catch (const ThreadExitException &) + { + } + catch (const std::exception &e) + { + static uint32_t warnCount = 0; + if (warnCount < 8u) + { + std::cerr << "[INTC] handler 0x" << std::hex << info.handler + << " threw exception: " << e.what() << std::dec << std::endl; + ++warnCount; + } + } } - std::sort(handlers.begin(), handlers.end(), [](const IrqHandlerInfo &a, const IrqHandlerInfo &b) - { return a.order < b.order; }); } - for (const IrqHandlerInfo &info : handlers) + void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause) { - if (!runtime->hasFunction(info.handler)) + if (!rdram || !runtime) { - if (cause == kIntcVblankStart) + return; + } + + std::vector handlers; + { + std::lock_guard lock(g_irq_handler_mutex); + if (cause < 32u && (g_enabled_dmac_mask & (1u << cause)) == 0u) { - PS2_IF_AGRESSIVE_LOGS({ - static std::atomic s_missingHandlerLogCount{0u}; - const uint32_t logIndex = s_missingHandlerLogCount.fetch_add(1u, std::memory_order_relaxed); - if (logIndex < 32u) - { - auto flags = std::cout.flags(); - std::cout << "[INTC:missing] cause=" << cause - << " handler=0x" << std::hex << info.handler - << std::dec - << " id=" << info.id - << std::endl; - std::cout.flags(flags); - } - }); + return; + } + + handlers.reserve(g_dmacHandlers.size()); + for (const auto &[id, info] : g_dmacHandlers) + { + (void)id; + if (!info.enabled) + { + continue; + } + if (info.cause != cause) + { + continue; + } + if (info.handler == 0u) + { + continue; + } + handlers.push_back(info); } - continue; + std::sort(handlers.begin(), handlers.end(), [](const IrqHandlerInfo &a, const IrqHandlerInfo &b) + { return a.order < b.order; }); } - try + for (const IrqHandlerInfo &info : handlers) { - R5900Context irqCtx{}; - SET_GPR_U32(&irqCtx, 28, info.gp); - SET_GPR_U32(&irqCtx, 29, getAsyncHandlerStackTop(runtime)); - SET_GPR_U32(&irqCtx, 31, 0u); - SET_GPR_U32(&irqCtx, 4, cause); - SET_GPR_U32(&irqCtx, 5, info.arg); - SET_GPR_U32(&irqCtx, 6, 0u); - SET_GPR_U32(&irqCtx, 7, 0u); - irqCtx.pc = info.handler; - - while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested()) + if (!runtime->hasFunction(info.handler)) { - PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc); - if (!step) + continue; + } + + try + { + R5900Context irqCtx{}; + SET_GPR_U32(&irqCtx, 28, info.gp); + SET_GPR_U32(&irqCtx, 29, getAsyncHandlerStackTop(runtime)); + SET_GPR_U32(&irqCtx, 31, 0u); + SET_GPR_U32(&irqCtx, 4, cause); + SET_GPR_U32(&irqCtx, 5, info.arg); + SET_GPR_U32(&irqCtx, 6, 0u); + SET_GPR_U32(&irqCtx, 7, 0u); + irqCtx.pc = info.handler; + + while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested()) { - break; + PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc); + if (!step) + { + break; + } + step(rdram, &irqCtx, runtime); + } + } + catch (const ThreadExitException &) + { + } + catch (const std::exception &e) + { + static uint32_t warnCount = 0; + if (warnCount < 8u) + { + std::cerr << "[DMAC] handler 0x" << std::hex << info.handler + << " threw exception: " << e.what() << std::dec << std::endl; + ++warnCount; } - // Interrupt handlers must be able to preempt a guest thread that is - // spinning on interrupt-produced state, such as a vblank counter. - step(rdram, &irqCtx, runtime); } + } + } + static uint64_t signalVSyncFlag(uint8_t *rdram) + { + VSyncFlagRegistration reg{}; + uint64_t tickValue = 0u; + { + std::lock_guard lock(g_vsync_flag_mutex); + reg = g_vsync_registration; + tickValue = ++g_vsync_tick_counter; } - catch (const ThreadExitException &) + + g_vsync_cv.notify_all(); + + if (reg.flagAddr != 0u) { + writeGuestU32NoThrow(rdram, reg.flagAddr, 1u); } - catch (const std::exception &e) + if (reg.tickAddr != 0u) { - static uint32_t warnCount = 0; - if (warnCount < 8u) - { - std::cerr << "[INTC] handler 0x" << std::hex << info.handler - << " threw exception: " << e.what() << std::dec << std::endl; - ++warnCount; - } + writeGuestU64NoThrow(rdram, reg.tickAddr, tickValue); } + return tickValue; } -} -void dispatchDmacHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause) -{ - if (!rdram || !runtime) + static void interruptWorkerMain(uint8_t *rdram, PS2Runtime *runtime) { - return; - } + g_currentThreadId = -1; - std::vector handlers; - { - std::lock_guard lock(g_irq_handler_mutex); - if (cause < 32u && (g_enabled_dmac_mask & (1u << cause)) == 0u) - { - return; - } + using clock = std::chrono::steady_clock; + auto nextTick = clock::now() + kVblankPeriod; - handlers.reserve(g_dmacHandlers.size()); - for (const auto &[id, info] : g_dmacHandlers) + while (runtime != nullptr && !runtime->isStopRequested()) { - (void)id; - if (!info.enabled) { - continue; + std::unique_lock lock(g_irq_worker_mutex); + if (g_irq_worker_cv.wait_until(lock, nextTick, []() + { return g_irq_worker_stop.load(std::memory_order_acquire); })) + { + break; + } } - if (info.cause != cause) + + const auto now = clock::now(); + int ticksToProcess = 0; + while (now >= nextTick && ticksToProcess < kMaxCatchupTicks) { - continue; + ++ticksToProcess; + nextTick += kVblankPeriod; } - if (info.handler == 0u) + if (ticksToProcess == 0) { continue; } - handlers.push_back(info); + + for (int i = 0; i < ticksToProcess; ++i) + { + const uint64_t tickValue = signalVSyncFlag(rdram); + ps2_stubs::dispatchGsSyncVCallback(rdram, runtime, tickValue); + dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankStart); + std::this_thread::sleep_for(std::chrono::microseconds(500)); + dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankEnd); + } } - std::sort(handlers.begin(), handlers.end(), [](const IrqHandlerInfo &a, const IrqHandlerInfo &b) - { return a.order < b.order; }); + + g_irq_worker_running.store(false, std::memory_order_release); + g_irq_worker_cv.notify_all(); } - for (const IrqHandlerInfo &info : handlers) + static void ensureInterruptWorkerRunning(uint8_t *rdram, PS2Runtime *runtime) { - if (!runtime->hasFunction(info.handler)) + if (!rdram || !runtime) { - continue; + return; } - try + std::lock_guard lock(g_irq_worker_mutex); + if (g_irq_worker_running.load(std::memory_order_acquire)) { - R5900Context irqCtx{}; - SET_GPR_U32(&irqCtx, 28, info.gp); - SET_GPR_U32(&irqCtx, 29, getAsyncHandlerStackTop(runtime)); - SET_GPR_U32(&irqCtx, 31, 0u); - SET_GPR_U32(&irqCtx, 4, cause); - SET_GPR_U32(&irqCtx, 5, info.arg); - SET_GPR_U32(&irqCtx, 6, 0u); - SET_GPR_U32(&irqCtx, 7, 0u); - irqCtx.pc = info.handler; - - while (irqCtx.pc != 0u && runtime && !runtime->isStopRequested()) - { - PS2Runtime::RecompiledFunction step = runtime->lookupFunction(irqCtx.pc); - if (!step) - { - break; - } - step(rdram, &irqCtx, runtime); - } + return; } - catch (const ThreadExitException &) + + g_irq_worker_stop.store(false, std::memory_order_release); + g_irq_worker_running.store(true, std::memory_order_release); + try { + std::thread(interruptWorkerMain, rdram, runtime).detach(); } - catch (const std::exception &e) + catch (...) { - static uint32_t warnCount = 0; - if (warnCount < 8u) - { - std::cerr << "[DMAC] handler 0x" << std::hex << info.handler - << " threw exception: " << e.what() << std::dec << std::endl; - ++warnCount; - } + g_irq_worker_running.store(false, std::memory_order_release); } } -} -static uint64_t signalVSyncFlag(uint8_t *rdram) -{ - VSyncFlagRegistration reg{}; - uint64_t tickValue = 0u; + void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime) { - std::lock_guard lock(g_vsync_flag_mutex); - reg = g_vsync_registration; - tickValue = ++g_vsync_tick_counter; + ensureInterruptWorkerRunning(rdram, runtime); } - g_vsync_cv.notify_all(); - - if (reg.flagAddr != 0u) + uint64_t GetCurrentVSyncTick() { - writeGuestU32NoThrow(rdram, reg.flagAddr, 1u); + std::lock_guard lock(g_vsync_flag_mutex); + return g_vsync_tick_counter; } - if (reg.tickAddr != 0u) + + void stopInterruptWorker() { - writeGuestU64NoThrow(rdram, reg.tickAddr, tickValue); + g_irq_worker_stop.store(true, std::memory_order_release); + g_irq_worker_cv.notify_all(); + std::unique_lock lock(g_irq_worker_mutex); + g_irq_worker_cv.wait_for(lock, std::chrono::milliseconds(500), []() + { return !g_irq_worker_running.load(std::memory_order_acquire); }); + g_vsync_cv.notify_all(); } - return tickValue; -} - -static void interruptWorkerMain(uint8_t *rdram, PS2Runtime *runtime) -{ - g_currentThreadId = -1; - using clock = std::chrono::steady_clock; - auto nextTick = clock::now() + kVblankPeriod; - - while (runtime != nullptr && !runtime->isStopRequested()) + uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime) { + ensureInterruptWorkerRunning(rdram, runtime); + std::unique_lock lock(g_vsync_flag_mutex); + uint64_t current = g_vsync_tick_counter; { - std::unique_lock lock(g_irq_worker_mutex); - if (g_irq_worker_cv.wait_until(lock, nextTick, []() - { return g_irq_worker_stop.load(std::memory_order_acquire); })) - { - break; - } + PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); + g_vsync_cv.wait(lock, [current, runtime]() + { return g_vsync_tick_counter > current || (runtime != nullptr && runtime->isStopRequested()); }); } + return g_vsync_tick_counter; + } + + void WaitVSyncTick(uint8_t *rdram, PS2Runtime *runtime) + { + (void)WaitForNextVSyncTick(rdram, runtime); + } + + void SetVSyncFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t flagAddr = getRegU32(ctx, 4); + const uint32_t tickAddr = getRegU32(ctx, 5); - const auto now = clock::now(); - int ticksToProcess = 0; - while (now >= nextTick && ticksToProcess < kMaxCatchupTicks) { - ++ticksToProcess; - nextTick += kVblankPeriod; + std::lock_guard lock(g_vsync_flag_mutex); + g_vsync_registration.flagAddr = flagAddr; + g_vsync_registration.tickAddr = tickAddr; } - if (ticksToProcess == 0) + + writeGuestU32NoThrow(rdram, flagAddr, 0u); + writeGuestU64NoThrow(rdram, tickAddr, 0u); + ensureInterruptWorkerRunning(rdram, runtime); + setReturnS32(ctx, KE_OK); + } + + void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t cause = getRegU32(ctx, 4); + if (cause < 32u) { - continue; + std::lock_guard lock(g_irq_handler_mutex); + g_enabled_intc_mask |= (1u << cause); } - - for (int i = 0; i < ticksToProcess; ++i) + if (cause == kIntcVblankStart || cause == kIntcVblankEnd) { - const uint64_t tickValue = signalVSyncFlag(rdram); - ps2_stubs::dispatchGsSyncVCallback(rdram, runtime, tickValue); - dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankStart); - std::this_thread::sleep_for(std::chrono::microseconds(500)); - dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankEnd); + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_enableLogCount{0u}; + const uint32_t logIndex = s_enableLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + RUNTIME_LOG("[EnableIntc] cause=" << cause); + } + }); } + setReturnS32(ctx, KE_OK); } - g_irq_worker_running.store(false, std::memory_order_release); - g_irq_worker_cv.notify_all(); -} - -static void ensureInterruptWorkerRunning(uint8_t *rdram, PS2Runtime *runtime) -{ - if (!rdram || !runtime) + void iEnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - return; + EnableIntc(rdram, ctx, runtime); } - std::lock_guard lock(g_irq_worker_mutex); - if (g_irq_worker_running.load(std::memory_order_acquire)) + void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - return; + const uint32_t cause = getRegU32(ctx, 4); + if (cause < 32u) + { + std::lock_guard lock(g_irq_handler_mutex); + g_enabled_intc_mask &= ~(1u << cause); + } + if (cause == kIntcVblankStart || cause == kIntcVblankEnd) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_disableLogCount{0u}; + const uint32_t logIndex = s_disableLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + RUNTIME_LOG("[DisableIntc] cause=" << cause); + } + }); + } + setReturnS32(ctx, KE_OK); } - g_irq_worker_stop.store(false, std::memory_order_release); - g_irq_worker_running.store(true, std::memory_order_release); - try + void iDisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::thread(interruptWorkerMain, rdram, runtime).detach(); + DisableIntc(rdram, ctx, runtime); } - catch (...) - { - g_irq_worker_running.store(false, std::memory_order_release); - } -} - -void EnsureVSyncWorkerRunning(uint8_t *rdram, PS2Runtime *runtime) -{ - ensureInterruptWorkerRunning(rdram, runtime); -} - -uint64_t GetCurrentVSyncTick() -{ - std::lock_guard lock(g_vsync_flag_mutex); - return g_vsync_tick_counter; -} - -void stopInterruptWorker() -{ - g_irq_worker_stop.store(true, std::memory_order_release); - g_irq_worker_cv.notify_all(); - std::unique_lock lock(g_irq_worker_mutex); - g_irq_worker_cv.wait_for(lock, std::chrono::milliseconds(500), []() - { return !g_irq_worker_running.load(std::memory_order_acquire); }); - g_vsync_cv.notify_all(); -} -uint64_t WaitForNextVSyncTick(uint8_t *rdram, PS2Runtime *runtime) -{ - ensureInterruptWorkerRunning(rdram, runtime); - std::unique_lock lock(g_vsync_flag_mutex); - uint64_t current = g_vsync_tick_counter; + void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); - g_vsync_cv.wait(lock, [current, runtime]() - { return g_vsync_tick_counter > current || (runtime != nullptr && runtime->isStopRequested()); }); - } - return g_vsync_tick_counter; -} + IrqHandlerInfo info{}; + info.cause = getRegU32(ctx, 4); + info.handler = getRegU32(ctx, 5); + uint32_t next = getRegU32(ctx, 6); + info.arg = getRegU32(ctx, 7); + info.gp = getRegU32(ctx, 28); + info.sp = getRegU32(ctx, 29); + info.enabled = true; -void WaitVSyncTick(uint8_t *rdram, PS2Runtime *runtime) -{ - (void)WaitForNextVSyncTick(rdram, runtime); -} + int handlerId = 0; + { + std::lock_guard lock(g_irq_handler_mutex); + info.order = (next == 0) ? --g_intc_head_order : ++g_intc_tail_order; + handlerId = g_nextIntcHandlerId++; + info.id = handlerId; + g_intcHandlers[handlerId] = info; + } -void SetVSyncFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t flagAddr = getRegU32(ctx, 4); - const uint32_t tickAddr = getRegU32(ctx, 5); + if (info.cause == kIntcVblankStart) + { + PS2_IF_AGRESSIVE_LOGS({ + static std::atomic s_addHandlerLogCount{0u}; + const uint32_t logIndex = s_addHandlerLogCount.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < 32u) + { + auto flags = std::cout.flags(); + std::cout << "[AddIntcHandler] cause=" << info.cause + << " handler=0x" << std::hex << info.handler + << " arg=0x" << info.arg + << " gp=0x" << info.gp + << " sp=0x" << info.sp + << std::dec + << " id=" << handlerId + << std::endl; + std::cout.flags(flags); + } + }); + } - { - std::lock_guard lock(g_vsync_flag_mutex); - g_vsync_registration.flagAddr = flagAddr; - g_vsync_registration.tickAddr = tickAddr; + ensureInterruptWorkerRunning(rdram, runtime); + setReturnS32(ctx, handlerId); } - writeGuestU32NoThrow(rdram, flagAddr, 0u); - writeGuestU64NoThrow(rdram, tickAddr, 0u); - ensureInterruptWorkerRunning(rdram, runtime); - setReturnS32(ctx, KE_OK); -} - -void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cause = getRegU32(ctx, 4); - if (cause < 32u) + void AddIntcHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - g_enabled_intc_mask |= (1u << cause); + AddIntcHandler(rdram, ctx, runtime); } - if (cause == kIntcVblankStart || cause == kIntcVblankEnd) + + void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - PS2_IF_AGRESSIVE_LOGS({ - static std::atomic s_enableLogCount{0u}; - const uint32_t logIndex = s_enableLogCount.fetch_add(1u, std::memory_order_relaxed); - if (logIndex < 32u) + const uint32_t cause = getRegU32(ctx, 4); + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (handlerId > 0) + { + std::lock_guard lock(g_irq_handler_mutex); + auto it = g_intcHandlers.find(handlerId); + if (it != g_intcHandlers.end() && it->second.cause == cause) { - RUNTIME_LOG("[EnableIntc] cause=" << cause); + g_intcHandlers.erase(it); } - }); + } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void iEnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - EnableIntc(rdram, ctx, runtime); -} - -void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cause = getRegU32(ctx, 4); - if (cause < 32u) + void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - g_enabled_intc_mask &= ~(1u << cause); + IrqHandlerInfo info{}; + info.cause = getRegU32(ctx, 4); + info.handler = getRegU32(ctx, 5); + uint32_t next = getRegU32(ctx, 6); + info.arg = getRegU32(ctx, 7); + info.gp = getRegU32(ctx, 28); + info.sp = getRegU32(ctx, 29); + info.enabled = true; + + int handlerId = 0; + { + std::lock_guard lock(g_irq_handler_mutex); + info.order = (next == 0) ? --g_dmac_head_order : ++g_dmac_tail_order; + handlerId = g_nextDmacHandlerId++; + info.id = handlerId; + g_dmacHandlers[handlerId] = info; + } + setReturnS32(ctx, handlerId); } - if (cause == kIntcVblankStart || cause == kIntcVblankEnd) + + void AddDmacHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - PS2_IF_AGRESSIVE_LOGS({ - static std::atomic s_disableLogCount{0u}; - const uint32_t logIndex = s_disableLogCount.fetch_add(1u, std::memory_order_relaxed); - if (logIndex < 32u) - { - RUNTIME_LOG("[DisableIntc] cause=" << cause); - } - }); + AddDmacHandler(rdram, ctx, runtime); } - setReturnS32(ctx, KE_OK); -} - -void iDisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - DisableIntc(rdram, ctx, runtime); -} -void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - IrqHandlerInfo info{}; - info.cause = getRegU32(ctx, 4); - info.handler = getRegU32(ctx, 5); - uint32_t next = getRegU32(ctx, 6); - info.arg = getRegU32(ctx, 7); - info.gp = getRegU32(ctx, 28); - info.sp = getRegU32(ctx, 29); - info.enabled = true; - - int handlerId = 0; - { - std::lock_guard lock(g_irq_handler_mutex); - info.order = (next == 0) ? --g_intc_head_order : ++g_intc_tail_order; - handlerId = g_nextIntcHandlerId++; - info.id = handlerId; - g_intcHandlers[handlerId] = info; - } - - if (info.cause == kIntcVblankStart) - { - PS2_IF_AGRESSIVE_LOGS({ - static std::atomic s_addHandlerLogCount{0u}; - const uint32_t logIndex = s_addHandlerLogCount.fetch_add(1u, std::memory_order_relaxed); - if (logIndex < 32u) + void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t cause = getRegU32(ctx, 4); + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (handlerId > 0) + { + std::lock_guard lock(g_irq_handler_mutex); + auto it = g_dmacHandlers.find(handlerId); + if (it != g_dmacHandlers.end() && it->second.cause == cause) { - auto flags = std::cout.flags(); - std::cout << "[AddIntcHandler] cause=" << info.cause - << " handler=0x" << std::hex << info.handler - << " arg=0x" << info.arg - << " gp=0x" << info.gp - << " sp=0x" << info.sp - << std::dec - << " id=" << handlerId - << std::endl; - std::cout.flags(flags); + g_dmacHandlers.erase(it); } - }); + } + setReturnS32(ctx, KE_OK); } - ensureInterruptWorkerRunning(rdram, runtime); - setReturnS32(ctx, handlerId); -} - -void AddIntcHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - AddIntcHandler(rdram, ctx, runtime); -} - -void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cause = getRegU32(ctx, 4); - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (handlerId > 0) + void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - auto it = g_intcHandlers.find(handlerId); - if (it != g_intcHandlers.end() && it->second.cause == cause) + const int handlerId = static_cast(getRegU32(ctx, 5)); { - g_intcHandlers.erase(it); + std::lock_guard lock(g_irq_handler_mutex); + if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) + { + it->second.enabled = true; + } } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} - -void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - IrqHandlerInfo info{}; - info.cause = getRegU32(ctx, 4); - info.handler = getRegU32(ctx, 5); - uint32_t next = getRegU32(ctx, 6); - info.arg = getRegU32(ctx, 7); - info.gp = getRegU32(ctx, 28); - info.sp = getRegU32(ctx, 29); - info.enabled = true; - - int handlerId = 0; - { - std::lock_guard lock(g_irq_handler_mutex); - info.order = (next == 0) ? --g_dmac_head_order : ++g_dmac_tail_order; - handlerId = g_nextDmacHandlerId++; - info.id = handlerId; - g_dmacHandlers[handlerId] = info; - } - setReturnS32(ctx, handlerId); -} -void AddDmacHandler2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - AddDmacHandler(rdram, ctx, runtime); -} - -void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cause = getRegU32(ctx, 4); - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (handlerId > 0) + void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - auto it = g_dmacHandlers.find(handlerId); - if (it != g_dmacHandlers.end() && it->second.cause == cause) + const int handlerId = static_cast(getRegU32(ctx, 5)); { - g_dmacHandlers.erase(it); + std::lock_guard lock(g_irq_handler_mutex); + if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) + { + it->second.enabled = false; + } } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int handlerId = static_cast(getRegU32(ctx, 5)); + void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) + const int handlerId = static_cast(getRegU32(ctx, 5)); { - it->second.enabled = true; + std::lock_guard lock(g_irq_handler_mutex); + if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) + { + it->second.enabled = true; + } } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int handlerId = static_cast(getRegU32(ctx, 5)); + void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) + const int handlerId = static_cast(getRegU32(ctx, 5)); { - it->second.enabled = false; + std::lock_guard lock(g_irq_handler_mutex); + if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) + { + it->second.enabled = false; + } } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int handlerId = static_cast(getRegU32(ctx, 5)); + void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) + const uint32_t cause = getRegU32(ctx, 4); + if (cause < 32u) { - it->second.enabled = true; + std::lock_guard lock(g_irq_handler_mutex); + g_enabled_dmac_mask |= (1u << cause); } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int handlerId = static_cast(getRegU32(ctx, 5)); + void iEnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) - { - it->second.enabled = false; - } + EnableDmac(rdram, ctx, runtime); } - setReturnS32(ctx, KE_OK); -} -void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cause = getRegU32(ctx, 4); - if (cause < 32u) + void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - g_enabled_dmac_mask |= (1u << cause); + const uint32_t cause = getRegU32(ctx, 4); + if (cause < 32u) + { + std::lock_guard lock(g_irq_handler_mutex); + g_enabled_dmac_mask &= ~(1u << cause); + } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void iEnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - EnableDmac(rdram, ctx, runtime); -} - -void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t cause = getRegU32(ctx, 4); - if (cause < 32u) + void iDisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_irq_handler_mutex); - g_enabled_dmac_mask &= ~(1u << cause); + DisableDmac(rdram, ctx, runtime); } - setReturnS32(ctx, KE_OK); -} - -void iDisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - DisableDmac(rdram, ctx, runtime); -} } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp index d2a1a83f..d5a31ea7 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Lifecycle.cpp @@ -104,13 +104,11 @@ namespace ps2_syscalls } } - void joinAllGuestHostThreads() { joinAllHostThreads(); } - void detachAllGuestHostThreads() { detachAllHostThreads(); diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp index b3c90557..f9f26ea2 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp @@ -3,2486 +3,2510 @@ namespace ps2_syscalls { -namespace -{ -constexpr uint32_t kSoundDriverStatusSize = 0x42u; -constexpr uint32_t kSoundDriverSeInfoOffset = 0x00u; -constexpr uint32_t kSoundDriverMidiInfoOffset = 0x0Cu; -constexpr uint32_t kSoundDriverMidiSumOffset = 0x1Eu; -constexpr uint32_t kSoundDriverSeSumOffset = 0x26u; -constexpr uint32_t kSoundDriverAddrTableEntries = 16u; -constexpr uint32_t kSoundDriverHdRegionSize = 0x4000u; -constexpr uint32_t kSoundDriverSqRegionSize = 0x18000u; -constexpr uint32_t kSoundDriverDataRegionSize = 0x40000u; -constexpr uint32_t kSoundDriverStatusAlignment = 0x100u; -constexpr uint32_t kSoundDriverAddrTableAlignment = 0x100u; -constexpr uint32_t kSoundDriverStorageAlignment = 0x1000u; -constexpr uint32_t kSoundDriverGuestPoolBase = 0x00120000u; -constexpr uint32_t kSoundDriverGuestPoolLimit = 0x00200000u; - -void resetDtxRpcStateUnlocked() -{ - g_dtx_remote_by_id.clear(); - g_dtx_transfer_by_id.clear(); - g_dtx_sjx_by_handle.clear(); - g_dtx_ps2rna_by_handle.clear(); - g_dtx_sjrmt_by_handle.clear(); - g_dtx_next_urpc_obj = g_dtxCompatLayout.urpcObjBase; -} - -bool readGuestU16(const uint8_t *rdram, uint32_t addr, uint16_t &out) -{ - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - out = 0u; - return false; - } - std::memcpy(&out, ptr, sizeof(out)); - return true; -} - -bool readGuestS16(const uint8_t *rdram, uint32_t addr, int16_t &out) -{ - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - out = 0; - return false; - } - std::memcpy(&out, ptr, sizeof(out)); - return true; -} - -bool writeGuestU16(uint8_t *rdram, uint32_t addr, uint16_t value) -{ - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(ptr, &value, sizeof(value)); - return true; -} - -bool writeGuestS16(uint8_t *rdram, uint32_t addr, int16_t value) -{ - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(ptr, &value, sizeof(value)); - return true; -} - -bool readGuestU32(const uint8_t *rdram, uint32_t addr, uint32_t &out) -{ - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - out = 0u; - return false; - } - std::memcpy(&out, ptr, sizeof(out)); - return true; -} + namespace + { + constexpr uint32_t kSoundDriverStatusSize = 0x42u; + constexpr uint32_t kSoundDriverSeInfoOffset = 0x00u; + constexpr uint32_t kSoundDriverMidiInfoOffset = 0x0Cu; + constexpr uint32_t kSoundDriverMidiSumOffset = 0x1Eu; + constexpr uint32_t kSoundDriverSeSumOffset = 0x26u; + constexpr uint32_t kSoundDriverAddrTableEntries = 16u; + constexpr uint32_t kSoundDriverHdRegionSize = 0x4000u; + constexpr uint32_t kSoundDriverSqRegionSize = 0x18000u; + constexpr uint32_t kSoundDriverDataRegionSize = 0x40000u; + constexpr uint32_t kSoundDriverStatusAlignment = 0x100u; + constexpr uint32_t kSoundDriverAddrTableAlignment = 0x100u; + constexpr uint32_t kSoundDriverStorageAlignment = 0x1000u; + constexpr uint32_t kSoundDriverGuestPoolBase = 0x00120000u; + constexpr uint32_t kSoundDriverGuestPoolLimit = 0x00200000u; + + void resetDtxRpcStateUnlocked() + { + g_dtx_remote_by_id.clear(); + g_dtx_transfer_by_id.clear(); + g_dtx_sjx_by_handle.clear(); + g_dtx_ps2rna_by_handle.clear(); + g_dtx_sjrmt_by_handle.clear(); + g_dtx_next_urpc_obj = g_dtxCompatLayout.urpcObjBase; + } + + bool readGuestU16(const uint8_t *rdram, uint32_t addr, uint16_t &out) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + out = 0u; + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; + } -bool writeGuestU32(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; -} + bool readGuestS16(const uint8_t *rdram, uint32_t addr, int16_t &out) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + out = 0; + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; + } -bool normalizeGuestLinearAddr(uint32_t addr, uint32_t &out) -{ - uint32_t offset = 0u; - bool scratch = false; - if (!ps2ResolveGuestPointer(addr, offset, scratch) || scratch) - { - out = 0u; - return false; - } - out = offset; - return true; -} + bool writeGuestU16(uint8_t *rdram, uint32_t addr, uint16_t value) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, &value, sizeof(value)); + return true; + } -bool hasAnyNonZero(const uint8_t *ptr, size_t bytes) -{ - if (!ptr) - { - return false; - } - for (size_t i = 0; i < bytes; ++i) - { - if (ptr[i] != 0u) + bool writeGuestS16(uint8_t *rdram, uint32_t addr, int16_t value) { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, &value, sizeof(value)); return true; } - } - return false; -} -uint32_t alignUp(uint32_t value, uint32_t alignment) -{ - if (alignment == 0u) - { - return value; - } - return (value + (alignment - 1u)) & ~(alignment - 1u); -} + bool readGuestU32(const uint8_t *rdram, uint32_t addr, uint32_t &out) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + out = 0u; + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; + } -void resetSoundDriverRpcStateUnlocked() -{ - const PS2SoundDriverCompatLayout compat = g_soundDriverCompatLayout; - g_soundDriverRpcState = {}; - g_soundDriverCompatLayout = compat; -} + bool writeGuestU32(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; + } -void adoptSoundDriverRuntimeUnlocked(PS2Runtime *runtime) -{ - const uintptr_t runtimeCookie = reinterpret_cast(runtime); - if (g_soundDriverRpcState.ownerRuntime != runtimeCookie) - { - resetSoundDriverRpcStateUnlocked(); - g_soundDriverRpcState.ownerRuntime = runtimeCookie; - } -} + bool normalizeGuestLinearAddr(uint32_t addr, uint32_t &out) + { + uint32_t offset = 0u; + bool scratch = false; + if (!ps2ResolveGuestPointer(addr, offset, scratch) || scratch) + { + out = 0u; + return false; + } + out = offset; + return true; + } -bool ensureSoundDriverMemoryUnlocked(uint8_t *rdram, PS2Runtime *runtime) -{ - if (!rdram || !runtime) - { - return false; - } + bool hasAnyNonZero(const uint8_t *ptr, size_t bytes) + { + if (!ptr) + { + return false; + } + for (size_t i = 0; i < bytes; ++i) + { + if (ptr[i] != 0u) + { + return true; + } + } + return false; + } - adoptSoundDriverRuntimeUnlocked(runtime); + uint32_t alignUp(uint32_t value, uint32_t alignment) + { + if (alignment == 0u) + { + return value; + } + return (value + (alignment - 1u)) & ~(alignment - 1u); + } - if (g_soundDriverRpcState.statusAddr == 0u) - { - const uint32_t statusAddr = alignUp(kSoundDriverGuestPoolBase, kSoundDriverStatusAlignment); - const uint32_t addrTableAddr = - alignUp(statusAddr + kSoundDriverStatusSize, kSoundDriverAddrTableAlignment); - const uint32_t hdBaseAddr = - alignUp(addrTableAddr + (kSoundDriverAddrTableEntries * sizeof(uint32_t)), kSoundDriverStorageAlignment); - const uint32_t sqBaseAddr = alignUp(hdBaseAddr + kSoundDriverHdRegionSize, kSoundDriverStorageAlignment); - const uint32_t dataBaseAddr = alignUp(sqBaseAddr + kSoundDriverSqRegionSize, kSoundDriverStorageAlignment); - const uint32_t storageEnd = dataBaseAddr + kSoundDriverDataRegionSize; - if (storageEnd > kSoundDriverGuestPoolLimit) + void resetSoundDriverRpcStateUnlocked() { - return false; + const PS2SoundDriverCompatLayout compat = g_soundDriverCompatLayout; + g_soundDriverRpcState = {}; + g_soundDriverCompatLayout = compat; } - g_soundDriverRpcState.statusAddr = statusAddr; - g_soundDriverRpcState.addrTableAddr = addrTableAddr; - g_soundDriverRpcState.hdBaseAddr = hdBaseAddr; - g_soundDriverRpcState.sqBaseAddr = sqBaseAddr; - g_soundDriverRpcState.dataBaseAddr = dataBaseAddr; - g_soundDriverRpcState.storageBaseAddr = hdBaseAddr; - g_soundDriverRpcState.storageSize = - (dataBaseAddr + kSoundDriverDataRegionSize) - hdBaseAddr; - } + void adoptSoundDriverRuntimeUnlocked(PS2Runtime *runtime) + { + const uintptr_t runtimeCookie = reinterpret_cast(runtime); + if (g_soundDriverRpcState.ownerRuntime != runtimeCookie) + { + resetSoundDriverRpcStateUnlocked(); + g_soundDriverRpcState.ownerRuntime = runtimeCookie; + } + } - if (g_soundDriverRpcState.statusAddr == 0u || - g_soundDriverRpcState.addrTableAddr == 0u || - g_soundDriverRpcState.storageBaseAddr == 0u) - { - return false; - } + bool checkSoundDriverMemoryUnlocked(uint8_t *rdram, PS2Runtime *runtime) + { + if (!rdram || !runtime) + { + return false; + } - if (!g_soundDriverRpcState.initialized) - { - rpcZeroRdram(rdram, g_soundDriverRpcState.statusAddr, kSoundDriverStatusSize); - rpcZeroRdram(rdram, - g_soundDriverRpcState.addrTableAddr, - kSoundDriverAddrTableEntries * sizeof(uint32_t)); - rpcZeroRdram(rdram, g_soundDriverRpcState.storageBaseAddr, g_soundDriverRpcState.storageSize); - (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (0u * sizeof(uint32_t)), g_soundDriverRpcState.hdBaseAddr); - (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (1u * sizeof(uint32_t)), g_soundDriverRpcState.sqBaseAddr); - (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (2u * sizeof(uint32_t)), g_soundDriverRpcState.dataBaseAddr); - g_soundDriverRpcState.initialized = true; - } + adoptSoundDriverRuntimeUnlocked(runtime); - return true; -} + if (g_soundDriverRpcState.statusAddr == 0u) + { + const uint32_t statusAddr = alignUp(kSoundDriverGuestPoolBase, kSoundDriverStatusAlignment); + const uint32_t addrTableAddr = + alignUp(statusAddr + kSoundDriverStatusSize, kSoundDriverAddrTableAlignment); + const uint32_t hdBaseAddr = + alignUp(addrTableAddr + (kSoundDriverAddrTableEntries * sizeof(uint32_t)), kSoundDriverStorageAlignment); + const uint32_t sqBaseAddr = alignUp(hdBaseAddr + kSoundDriverHdRegionSize, kSoundDriverStorageAlignment); + const uint32_t dataBaseAddr = alignUp(sqBaseAddr + kSoundDriverSqRegionSize, kSoundDriverStorageAlignment); + const uint32_t storageEnd = dataBaseAddr + kSoundDriverDataRegionSize; + if (storageEnd > kSoundDriverGuestPoolLimit) + { + return false; + } -int16_t soundDriverCheckValue(const uint8_t *rdram, uint32_t primaryBase, uint32_t fallbackBase, uint32_t index, uint32_t count) -{ - if (index >= count) - { - return 0; - } + g_soundDriverRpcState.statusAddr = statusAddr; + g_soundDriverRpcState.addrTableAddr = addrTableAddr; + g_soundDriverRpcState.hdBaseAddr = hdBaseAddr; + g_soundDriverRpcState.sqBaseAddr = sqBaseAddr; + g_soundDriverRpcState.dataBaseAddr = dataBaseAddr; + g_soundDriverRpcState.storageBaseAddr = hdBaseAddr; + g_soundDriverRpcState.storageSize = + (dataBaseAddr + kSoundDriverDataRegionSize) - hdBaseAddr; + } - int16_t value = 0; - if (readGuestS16(rdram, primaryBase + (index * sizeof(int16_t)), value) && value != 0) - { - return value; - } + if (g_soundDriverRpcState.statusAddr == 0u || + g_soundDriverRpcState.addrTableAddr == 0u || + g_soundDriverRpcState.storageBaseAddr == 0u) + { + return false; + } - (void)readGuestS16(rdram, fallbackBase + (index * sizeof(int16_t)), value); - return value; -} + if (!g_soundDriverRpcState.initialized) + { + rpcZeroRdram(rdram, g_soundDriverRpcState.statusAddr, kSoundDriverStatusSize); + rpcZeroRdram(rdram, + g_soundDriverRpcState.addrTableAddr, + kSoundDriverAddrTableEntries * sizeof(uint32_t)); + rpcZeroRdram(rdram, g_soundDriverRpcState.storageBaseAddr, g_soundDriverRpcState.storageSize); + (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (0u * sizeof(uint32_t)), g_soundDriverRpcState.hdBaseAddr); + (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (1u * sizeof(uint32_t)), g_soundDriverRpcState.sqBaseAddr); + (void)writeGuestU32(rdram, g_soundDriverRpcState.addrTableAddr + (2u * sizeof(uint32_t)), g_soundDriverRpcState.dataBaseAddr); + g_soundDriverRpcState.initialized = true; + } -bool selectSoundDriverCompatChecks(const uint8_t *rdram, uint32_t &seBase, uint32_t &midiBase) -{ - const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; - if (!compat.hasChecksumTables()) - { - return false; - } + return true; + } - seBase = compat.primarySeCheckAddr; - midiBase = compat.primaryMidiCheckAddr; + int16_t soundDriverCheckValue(const uint8_t *rdram, uint32_t primaryBase, uint32_t fallbackBase, uint32_t index, uint32_t count) + { + if (index >= count) + { + return 0; + } - const uint8_t *selectedSe = getConstMemPtr(rdram, seBase); - const uint8_t *selectedMidi = getConstMemPtr(rdram, midiBase); - const bool primaryLooksLive = - hasAnyNonZero(selectedSe, 5u * sizeof(int16_t)) || - hasAnyNonZero(selectedMidi, 4u * sizeof(int16_t)); + int16_t value = 0; + if (readGuestS16(rdram, primaryBase + (index * sizeof(int16_t)), value) && value != 0) + { + return value; + } - if ((!selectedSe || !selectedMidi) || !primaryLooksLive) - { - const uint8_t *fallbackSe = getConstMemPtr(rdram, compat.fallbackSeCheckAddr); - const uint8_t *fallbackMidi = getConstMemPtr(rdram, compat.fallbackMidiCheckAddr); - const bool fallbackLooksLive = - hasAnyNonZero(fallbackSe, 5u * sizeof(int16_t)) || - hasAnyNonZero(fallbackMidi, 4u * sizeof(int16_t)); - if (fallbackLooksLive) - { - seBase = compat.fallbackSeCheckAddr; - midiBase = compat.fallbackMidiCheckAddr; - return true; + (void)readGuestS16(rdram, fallbackBase + (index * sizeof(int16_t)), value); + return value; } - } - return selectedSe != nullptr && selectedMidi != nullptr; -} + bool selectSoundDriverCompatChecks(const uint8_t *rdram, uint32_t &seBase, uint32_t &midiBase) + { + const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; + if (!compat.hasChecksumTables()) + { + return false; + } -void backfillSoundDriverStatusFromCompatUnlocked(uint8_t *rdram) -{ - if (!rdram || g_soundDriverRpcState.statusAddr == 0u || !g_soundDriverCompatLayout.hasChecksumTables()) - { - return; - } + seBase = compat.primarySeCheckAddr; + midiBase = compat.primaryMidiCheckAddr; - uint32_t seBase = 0u; - uint32_t midiBase = 0u; - if (!selectSoundDriverCompatChecks(rdram, seBase, midiBase)) - { - return; - } + const uint8_t *selectedSe = getConstMemPtr(rdram, seBase); + const uint8_t *selectedMidi = getConstMemPtr(rdram, midiBase); + const bool primaryLooksLive = + hasAnyNonZero(selectedSe, 5u * sizeof(int16_t)) || + hasAnyNonZero(selectedMidi, 4u * sizeof(int16_t)); - const uint8_t *selectedSe = getConstMemPtr(rdram, seBase); - const uint8_t *selectedMidi = getConstMemPtr(rdram, midiBase); - uint8_t *status = getMemPtr(rdram, g_soundDriverRpcState.statusAddr); - if (!selectedSe || !selectedMidi || !status) - { - return; - } + if ((!selectedSe || !selectedMidi) || !primaryLooksLive) + { + const uint8_t *fallbackSe = getConstMemPtr(rdram, compat.fallbackSeCheckAddr); + const uint8_t *fallbackMidi = getConstMemPtr(rdram, compat.fallbackMidiCheckAddr); + const bool fallbackLooksLive = + hasAnyNonZero(fallbackSe, 5u * sizeof(int16_t)) || + hasAnyNonZero(fallbackMidi, 4u * sizeof(int16_t)); + if (fallbackLooksLive) + { + seBase = compat.fallbackSeCheckAddr; + midiBase = compat.fallbackMidiCheckAddr; + return true; + } + } - auto backfillZeroS16Slots = [&](uint32_t statusOffset, const uint8_t *compatBase, uint32_t slotCount) - { - for (uint32_t slot = 0u; slot < slotCount; ++slot) + return selectedSe != nullptr && selectedMidi != nullptr; + } + + void backfillSoundDriverStatusFromCompatUnlocked(uint8_t *rdram) { - int16_t liveValue = 0; - if (!readGuestS16(rdram, - g_soundDriverRpcState.statusAddr + statusOffset + (slot * sizeof(int16_t)), - liveValue)) + if (!rdram || g_soundDriverRpcState.statusAddr == 0u || !g_soundDriverCompatLayout.hasChecksumTables()) { - continue; + return; } - if (liveValue != 0) + uint32_t seBase = 0u; + uint32_t midiBase = 0u; + if (!selectSoundDriverCompatChecks(rdram, seBase, midiBase)) { - continue; + return; } - int16_t compatValue = 0; - std::memcpy(&compatValue, compatBase + (slot * sizeof(int16_t)), sizeof(compatValue)); - if (compatValue == 0) + const uint8_t *selectedSe = getConstMemPtr(rdram, seBase); + const uint8_t *selectedMidi = getConstMemPtr(rdram, midiBase); + uint8_t *status = getMemPtr(rdram, g_soundDriverRpcState.statusAddr); + if (!selectedSe || !selectedMidi || !status) { - continue; + return; } - (void)writeGuestS16(rdram, - g_soundDriverRpcState.statusAddr + statusOffset + (slot * sizeof(int16_t)), - compatValue); - } - }; + auto backfillZeroS16Slots = [&](uint32_t statusOffset, const uint8_t *compatBase, uint32_t slotCount) + { + for (uint32_t slot = 0u; slot < slotCount; ++slot) + { + int16_t liveValue = 0; + if (!readGuestS16(rdram, + g_soundDriverRpcState.statusAddr + statusOffset + (slot * sizeof(int16_t)), + liveValue)) + { + continue; + } - backfillZeroS16Slots(kSoundDriverSeSumOffset, selectedSe, 5u); - backfillZeroS16Slots(kSoundDriverMidiSumOffset, selectedMidi, 4u); -} + if (liveValue != 0) + { + continue; + } -size_t soundDriverCommandLength(uint8_t command) -{ - const uint8_t hi = static_cast(command & 0xF0u); - switch (hi) - { - case 0x00u: - { - size_t len = 4u; - if ((command & 0x01u) != 0u) - { - ++len; - } - if ((command & 0x02u) != 0u) - { - ++len; - } - if ((command & 0x04u) != 0u) - { - len += 2u; - } - return len; - } - case 0x10u: - return (command == 0x11u) ? 3u : 1u; - case 0x20u: - if (command == 0x22u || command == 0x23u || command == 0x24u || command == 0x25u) - { - return 3u; - } - if (command == 0x26u) - { - return 4u; - } - if (command == 0x20u) - { - return 5u; - } - if (command == 0x27u || command == 0x28u || command == 0x29u || command == 0x2Cu || command == 0x2Du) - { - return 8u; - } - return 2u; - case 0x40u: - if (command == 0x47u || command == 0x48u || command == 0x49u || command == 0x4Au || - command == 0x41u || command == 0x42u) - { - return 2u; - } - if (command == 0x4Bu) - { - return 3u; - } - if (command == 0x45u || command == 0x4Cu) - { - return 4u; - } - if (command == 0x44u) - { - return 6u; + int16_t compatValue = 0; + std::memcpy(&compatValue, compatBase + (slot * sizeof(int16_t)), sizeof(compatValue)); + if (compatValue == 0) + { + continue; + } + + (void)writeGuestS16(rdram, + g_soundDriverRpcState.statusAddr + statusOffset + (slot * sizeof(int16_t)), + compatValue); + } + }; + + backfillZeroS16Slots(kSoundDriverSeSumOffset, selectedSe, 5u); + backfillZeroS16Slots(kSoundDriverMidiSumOffset, selectedMidi, 4u); } - if (command == 0x4Du || command == 0x4Eu) + + size_t soundDriverCommandLength(uint8_t command) { - return 3u; + const uint8_t hi = static_cast(command & 0xF0u); + switch (hi) + { + case 0x00u: + { + size_t len = 4u; + if ((command & 0x01u) != 0u) + { + ++len; + } + if ((command & 0x02u) != 0u) + { + ++len; + } + if ((command & 0x04u) != 0u) + { + len += 2u; + } + return len; + } + case 0x10u: + return (command == 0x11u) ? 3u : 1u; + case 0x20u: + if (command == 0x22u || command == 0x23u || command == 0x24u || command == 0x25u) + { + return 3u; + } + if (command == 0x26u) + { + return 4u; + } + if (command == 0x20u) + { + return 5u; + } + if (command == 0x27u || command == 0x28u || command == 0x29u || command == 0x2Cu || command == 0x2Du) + { + return 8u; + } + return 2u; + case 0x40u: + if (command == 0x47u || command == 0x48u || command == 0x49u || command == 0x4Au || + command == 0x41u || command == 0x42u) + { + return 2u; + } + if (command == 0x4Bu) + { + return 3u; + } + if (command == 0x45u || command == 0x4Cu) + { + return 4u; + } + if (command == 0x44u) + { + return 6u; + } + if (command == 0x4Du || command == 0x4Eu) + { + return 3u; + } + if (command == 0x4Fu) + { + return 6u; + } + return 1u; + case 0x50u: + case 0x60u: + if (command == 0x51u || command == 0x52u || command == 0x53u || command == 0x54u) + { + return 8u; + } + return 2u; + default: + return 0u; + } } - if (command == 0x4Fu) + + void applySoundDriverCommandUnlocked(uint8_t *rdram, const uint8_t *cmd) { - return 6u; + if (!rdram || !cmd || g_soundDriverRpcState.statusAddr == 0u) + { + return; + } + + const uint8_t op = cmd[0]; + switch (op) + { + case 0x20u: // SdrBgmReq + { + const uint32_t port = cmd[1] & 0x0Fu; + uint16_t midiInfo = 0u; + (void)readGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + midiInfo = static_cast(midiInfo | static_cast(1u << port)); + (void)writeGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + break; + } + case 0x21u: // SdrBgmStop + { + const uint32_t port = cmd[1] & 0x0Fu; + uint16_t midiInfo = 0u; + (void)readGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + midiInfo = static_cast(midiInfo & ~static_cast(1u << port)); + (void)writeGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); + break; + } + case 0x28u: // SdrHDDataSet + { + const uint32_t port = cmd[1] & 0x0Fu; + const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; + if (compat.primaryMidiCheckAddr != 0u || compat.fallbackMidiCheckAddr != 0u) + { + const int16_t checksum = + soundDriverCheckValue(rdram, compat.primaryMidiCheckAddr, compat.fallbackMidiCheckAddr, port, 4u); + (void)writeGuestS16(rdram, + g_soundDriverRpcState.statusAddr + kSoundDriverMidiSumOffset + (port * sizeof(int16_t)), + checksum); + } + break; + } + case 0x29u: // SdrHDDataSet2 + { + const uint32_t port = cmd[1] & 0x0Fu; + const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; + if (compat.primarySeCheckAddr != 0u || compat.fallbackSeCheckAddr != 0u) + { + const int16_t checksum = + soundDriverCheckValue(rdram, compat.primarySeCheckAddr, compat.fallbackSeCheckAddr, port, 5u); + (void)writeGuestS16(rdram, + g_soundDriverRpcState.statusAddr + kSoundDriverSeSumOffset + (port * sizeof(int16_t)), + checksum); + } + break; + } + case 0x10u: // SdrSeAllStop + rpcZeroRdram(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverSeInfoOffset, 6u * sizeof(uint16_t)); + break; + default: + break; + } } - return 1u; - case 0x50u: - case 0x60u: - if (command == 0x51u || command == 0x52u || command == 0x53u || command == 0x54u) + + void handleSoundDriverCommandBuffer(uint8_t *rdram, PS2Runtime *runtime, uint32_t sendBuf, uint32_t sendSize) { - return 8u; - } - return 2u; - default: - return 0u; - } -} + if (!rdram || !runtime || !sendBuf || sendSize == 0u) + { + return; + } -void applySoundDriverCommandUnlocked(uint8_t *rdram, const uint8_t *cmd) -{ - if (!rdram || !cmd || g_soundDriverRpcState.statusAddr == 0u) - { - return; - } + std::lock_guard lock(g_rpc_mutex); + if (!checkSoundDriverMemoryUnlocked(rdram, runtime)) + { + return; + } - const uint8_t op = cmd[0]; - switch (op) - { - case 0x20u: // SdrBgmReq - { - const uint32_t port = cmd[1] & 0x0Fu; - uint16_t midiInfo = 0u; - (void)readGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); - midiInfo = static_cast(midiInfo | static_cast(1u << port)); - (void)writeGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); - break; - } - case 0x21u: // SdrBgmStop - { - const uint32_t port = cmd[1] & 0x0Fu; - uint16_t midiInfo = 0u; - (void)readGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); - midiInfo = static_cast(midiInfo & ~static_cast(1u << port)); - (void)writeGuestU16(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverMidiInfoOffset, midiInfo); - break; - } - case 0x28u: // SdrHDDataSet - { - const uint32_t port = cmd[1] & 0x0Fu; - const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; - if (compat.primaryMidiCheckAddr != 0u || compat.fallbackMidiCheckAddr != 0u) - { - const int16_t checksum = - soundDriverCheckValue(rdram, compat.primaryMidiCheckAddr, compat.fallbackMidiCheckAddr, port, 4u); - (void)writeGuestS16(rdram, - g_soundDriverRpcState.statusAddr + kSoundDriverMidiSumOffset + (port * sizeof(int16_t)), - checksum); + const uint8_t *packet = getConstMemPtr(rdram, sendBuf); + if (!packet) + { + return; + } + + for (uint32_t offset = 0u; offset < sendSize;) + { + const uint8_t op = packet[offset]; + if (op == 0xFFu) + { + break; + } + + const size_t len = soundDriverCommandLength(op); + if (len == 0u || (offset + len) > sendSize) + { + break; + } + + applySoundDriverCommandUnlocked(rdram, packet + offset); + offset += static_cast(len); + } } - break; - } - case 0x29u: // SdrHDDataSet2 - { - const uint32_t port = cmd[1] & 0x0Fu; - const PS2SoundDriverCompatLayout &compat = g_soundDriverCompatLayout; - if (compat.primarySeCheckAddr != 0u || compat.fallbackSeCheckAddr != 0u) + + bool handleSoundDriverRpcServiceImpl(uint8_t *rdram, PS2Runtime *runtime, + uint32_t sid, uint32_t rpcNum, + uint32_t sendBuf, uint32_t sendSize, + uint32_t recvBuf, uint32_t recvSize, + uint32_t &resultPtr, + bool &signalNowaitCompletion) { - const int16_t checksum = - soundDriverCheckValue(rdram, compat.primarySeCheckAddr, compat.fallbackSeCheckAddr, port, 5u); - (void)writeGuestS16(rdram, - g_soundDriverRpcState.statusAddr + kSoundDriverSeSumOffset + (port * sizeof(int16_t)), - checksum); - } - break; - } - case 0x10u: // SdrSeAllStop - rpcZeroRdram(rdram, g_soundDriverRpcState.statusAddr + kSoundDriverSeInfoOffset, 6u * sizeof(uint16_t)); - break; - default: - break; - } -} + resultPtr = 0u; + signalNowaitCompletion = false; -void handleSoundDriverCommandBuffer(uint8_t *rdram, PS2Runtime *runtime, uint32_t sendBuf, uint32_t sendSize) -{ - if (!rdram || !runtime || !sendBuf || sendSize == 0u) - { - return; - } + if (!runtime || !rdram) + { + return false; + } - std::lock_guard lock(g_rpc_mutex); - if (!ensureSoundDriverMemoryUnlocked(rdram, runtime)) - { - return; - } + if (sid == IOP_SID_SNDDRV_COMMAND && rpcNum == IOP_RPC_SNDDRV_SUBMIT) + { + handleSoundDriverCommandBuffer(rdram, runtime, sendBuf, sendSize); + return true; + } - const uint8_t *packet = getConstMemPtr(rdram, sendBuf); - if (!packet) - { - return; - } + if (sid == IOP_SID_SNDDRV_STATE && + (rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR || rpcNum == IOP_RPC_SNDDRV_GET_ADDR_TABLE)) + { + uint32_t responseWord = 0u; + { + std::lock_guard lock(g_rpc_mutex); + if (!checkSoundDriverMemoryUnlocked(rdram, runtime)) + { + return false; + } + responseWord = + (rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR) ? g_soundDriverRpcState.statusAddr + : g_soundDriverRpcState.addrTableAddr; + } - for (uint32_t offset = 0u; offset < sendSize;) - { - const uint8_t op = packet[offset]; - if (op == 0xFFu) - { - break; - } + if (recvBuf && recvSize >= sizeof(uint32_t)) + { + (void)writeGuestU32(rdram, recvBuf, responseWord); + if (recvSize > sizeof(uint32_t)) + { + rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + } + resultPtr = recvBuf; + } - const size_t len = soundDriverCommandLength(op); - if (len == 0u || (offset + len) > sendSize) - { - break; + signalNowaitCompletion = true; + return true; + } + + return false; } - applySoundDriverCommandUnlocked(rdram, packet + offset); - offset += static_cast(len); - } -} + } // namespace -bool handleSoundDriverRpcServiceImpl(uint8_t *rdram, PS2Runtime *runtime, + bool handleSoundDriverRpcService(uint8_t *rdram, PS2Runtime *runtime, uint32_t sid, uint32_t rpcNum, uint32_t sendBuf, uint32_t sendSize, uint32_t recvBuf, uint32_t recvSize, uint32_t &resultPtr, bool &signalNowaitCompletion) -{ - resultPtr = 0u; - signalNowaitCompletion = false; - - if (!runtime || !rdram) { - return false; + return handleSoundDriverRpcServiceImpl(rdram, runtime, + sid, rpcNum, + sendBuf, sendSize, + recvBuf, recvSize, + resultPtr, + signalNowaitCompletion); } - if (sid == IOP_SID_SNDDRV_COMMAND && rpcNum == IOP_RPC_SNDDRV_SUBMIT) + namespace { - handleSoundDriverCommandBuffer(rdram, runtime, sendBuf, sendSize); - return true; - } - if (sid == IOP_SID_SNDDRV_STATE && - (rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR || rpcNum == IOP_RPC_SNDDRV_GET_ADDR_TABLE)) - { - uint32_t responseWord = 0u; + bool signalRpcCompletionSema(uint32_t semaId) { - std::lock_guard lock(g_rpc_mutex); - if (!ensureSoundDriverMemoryUnlocked(rdram, runtime)) + if (semaId == 0u || semaId > 0xFFFFu) + { + return false; + } + + auto sema = lookupSemaInfo(static_cast(semaId)); + if (!sema) { return false; } - responseWord = - (rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR) ? g_soundDriverRpcState.statusAddr - : g_soundDriverRpcState.addrTableAddr; + + bool signaled = false; + { + std::lock_guard lock(sema->m); + if (!sema->deleted && sema->count < sema->maxCount) + { + sema->count++; + signaled = true; + } + } + + if (signaled) + { + sema->cv.notify_one(); + } + return signaled; } - if (recvBuf && recvSize >= sizeof(uint32_t)) + const char *dtxUrpcCommandName(uint32_t command) { - (void)writeGuestU32(rdram, recvBuf, responseWord); - if (recvSize > sizeof(uint32_t)) + switch (command) { - rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + case 0u: + return "SJX_CREATE"; + case 1u: + return "SJX_DESTROY"; + case 2u: + return "SJX_RESET"; + case 3u: + return "SJX_REINIT"; + case 8u: + return "PS2RNA_CREATE"; + case 9u: + return "PS2RNA_DESTROY"; + case 10u: + return "PS2RNA_REINIT"; + case 32u: + return "SJRMT_RBF_CREATE"; + case 33u: + return "SJRMT_MEM_CREATE"; + case 34u: + return "SJRMT_UNI_CREATE"; + case 35u: + return "SJRMT_DESTROY"; + case 36u: + return "SJRMT_GET_UUID"; + case 37u: + return "SJRMT_RESET"; + case 38u: + return "SJRMT_GET_CHUNK"; + case 39u: + return "SJRMT_UNGET_CHUNK"; + case 40u: + return "SJRMT_PUT_CHUNK"; + case 41u: + return "SJRMT_GET_NUM_DATA"; + case 42u: + return "SJRMT_IS_GET_CHUNK"; + case 43u: + return "SJRMT_INIT"; + case 44u: + return "SJRMT_FINISH"; + default: + return "UNKNOWN"; } - resultPtr = recvBuf; } - signalNowaitCompletion = true; - return true; - } + const char *dtxStreamName(uint32_t streamId) + { + switch (streamId) + { + case 0u: + return "room"; + case 1u: + return "data"; + default: + return "other"; + } + } - return false; -} + bool dtxMatchesTransfer(const DtxTransferState &state, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes) + { + (void)dstAddr; + return state.eeWorkAddr != 0u && + state.wkSize >= 64u && + state.eeWorkAddr == srcAddr && + state.wkSize == sizeBytes; + } -} // namespace + bool dtxCopyBytes(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t len) + { + for (uint32_t i = 0; i < len; ++i) + { + const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); + uint8_t *dst = getMemPtr(rdram, dstAddr + i); + if (!src || !dst) + { + return false; + } + *dst = *src; + } + return true; + } -bool handleSoundDriverRpcService(uint8_t *rdram, PS2Runtime *runtime, - uint32_t sid, uint32_t rpcNum, - uint32_t sendBuf, uint32_t sendSize, - uint32_t recvBuf, uint32_t recvSize, - uint32_t &resultPtr, - bool &signalNowaitCompletion) -{ - return handleSoundDriverRpcServiceImpl(rdram, runtime, - sid, rpcNum, - sendBuf, sendSize, - recvBuf, recvSize, - resultPtr, - signalNowaitCompletion); -} + void dtxAppendToSjrmtData(uint8_t *rdram, DtxSjrmtState &state, uint32_t srcDataAddr, uint32_t requestedLen) + { + if (!rdram) + { + return; + } -namespace -{ + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (cap == 0u || state.wkAddr == 0u || state.roomBytes == 0u || requestedLen == 0u) + { + return; + } -bool signalRpcCompletionSema(uint32_t semaId) -{ - if (semaId == 0u || semaId > 0xFFFFu) - { - return false; - } + const uint32_t requestedCopyLen = std::min(requestedLen, state.roomBytes); + uint32_t copiedLen = 0u; + for (uint32_t i = 0; i < requestedCopyLen; ++i) + { + const uint32_t writeOffset = (state.writePos + i) % cap; + if (!dtxCopyBytes(rdram, state.wkAddr + writeOffset, srcDataAddr + i, 1u)) + { + break; + } + ++copiedLen; + } - auto sema = lookupSemaInfo(static_cast(semaId)); - if (!sema) - { - return false; - } + state.writePos = (state.writePos + copiedLen) % cap; + state.roomBytes -= copiedLen; + state.dataBytes = std::min(cap, state.dataBytes + copiedLen); + } - bool signaled = false; - { - std::lock_guard lock(sema->m); - if (!sema->deleted && sema->count < sema->maxCount) + void dtxConsumeSjrmtDataLocked(DtxSjrmtState &state) { - sema->count++; - signaled = true; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (cap == 0u || state.dataBytes == 0u) + { + return; + } + + const uint32_t consumed = std::min(cap, state.dataBytes); + state.readPos = (state.readPos + consumed) % cap; + state.dataBytes -= consumed; + state.roomBytes = std::min(cap, state.roomBytes + consumed); } - } - if (signaled) - { - sema->cv.notify_one(); - } - return signaled; -} + void dtxConsumeActivePs2RnaStreamsLocked() + { + for (const auto &entry : g_dtx_ps2rna_by_handle) + { + const DtxPs2RnaState &rna = entry.second; + if (!rna.playEnabled) + { + continue; + } -const char *dtxUrpcCommandName(uint32_t command) -{ - switch (command) - { - case 0u: return "SJX_CREATE"; - case 1u: return "SJX_DESTROY"; - case 2u: return "SJX_RESET"; - case 3u: return "SJX_REINIT"; - case 8u: return "PS2RNA_CREATE"; - case 9u: return "PS2RNA_DESTROY"; - case 10u: return "PS2RNA_REINIT"; - case 32u: return "SJRMT_RBF_CREATE"; - case 33u: return "SJRMT_MEM_CREATE"; - case 34u: return "SJRMT_UNI_CREATE"; - case 35u: return "SJRMT_DESTROY"; - case 36u: return "SJRMT_GET_UUID"; - case 37u: return "SJRMT_RESET"; - case 38u: return "SJRMT_GET_CHUNK"; - case 39u: return "SJRMT_UNGET_CHUNK"; - case 40u: return "SJRMT_PUT_CHUNK"; - case 41u: return "SJRMT_GET_NUM_DATA"; - case 42u: return "SJRMT_IS_GET_CHUNK"; - case 43u: return "SJRMT_INIT"; - case 44u: return "SJRMT_FINISH"; - default: return "UNKNOWN"; - } -} + auto consumeHandle = [&](uint32_t sjHandle) + { + if (sjHandle == 0u) + { + return; + } -const char *dtxStreamName(uint32_t streamId) -{ - switch (streamId) - { - case 0u: return "room"; - case 1u: return "data"; - default: return "other"; - } -} + auto it = g_dtx_sjrmt_by_handle.find(sjHandle); + if (it == g_dtx_sjrmt_by_handle.end()) + { + return; + } + + dtxConsumeSjrmtDataLocked(it->second); + }; + + consumeHandle(rna.sjHandle0); + consumeHandle(rna.sjHandle1); + } + } + + void dtxApplySjxPayload(uint8_t *rdram, const DtxTransferState &transfer) + { + if (!rdram || transfer.eeWorkAddr == 0u || transfer.wkSize < 64u) + { + return; + } + + uint32_t commandCount = 0u; + if (!readGuestU32(rdram, transfer.eeWorkAddr, commandCount)) + { + return; + } + + commandCount = std::min(commandCount, 128u); + if (commandCount == 0u) + { + return; + } -bool dtxMatchesTransfer(const DtxTransferState &state, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes) -{ - (void)dstAddr; - return state.eeWorkAddr != 0u && - state.wkSize >= 64u && - state.eeWorkAddr == srcAddr && - state.wkSize == sizeBytes; -} + constexpr uint32_t kSjxHeaderSize = 16u; + constexpr uint32_t kSjxCommandSize = 16u; -bool dtxCopyBytes(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t len) -{ - for (uint32_t i = 0; i < len; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - uint8_t *dst = getMemPtr(rdram, dstAddr + i); - if (!src || !dst) - { - return false; - } - *dst = *src; - } - return true; -} + std::lock_guard lock(g_dtx_rpc_mutex); + for (uint32_t i = 0; i < commandCount; ++i) + { + const uint32_t cmdAddr = transfer.eeWorkAddr + kSjxHeaderSize + (i * kSjxCommandSize); + uint8_t *cmdPtr = getMemPtr(rdram, cmdAddr); + if (!cmdPtr) + { + break; + } -void dtxAppendToSjrmtData(uint8_t *rdram, DtxSjrmtState &state, uint32_t srcDataAddr, uint32_t requestedLen) -{ - if (!rdram) - { - return; - } + const uint8_t cmdNo = cmdPtr[0]; + const uint8_t cmdLine = cmdPtr[1]; + uint16_t cmdXid = 0u; + uint32_t sjxHandle = 0u; + uint32_t chunkDataAddr = 0u; + uint32_t chunkLen = 0u; + std::memcpy(&cmdXid, cmdPtr + 2u, sizeof(cmdXid)); + std::memcpy(&sjxHandle, cmdPtr + 4u, sizeof(sjxHandle)); + std::memcpy(&chunkDataAddr, cmdPtr + 8u, sizeof(chunkDataAddr)); + std::memcpy(&chunkLen, cmdPtr + 12u, sizeof(chunkLen)); + + if (cmdNo != 0u || chunkLen == 0u) + { + continue; + } - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (cap == 0u || state.wkAddr == 0u || state.roomBytes == 0u || requestedLen == 0u) - { - return; - } + auto sjxIt = g_dtx_sjx_by_handle.find(sjxHandle); + if (sjxIt == g_dtx_sjx_by_handle.end()) + { + continue; + } - const uint32_t requestedCopyLen = std::min(requestedLen, state.roomBytes); - uint32_t copiedLen = 0u; - for (uint32_t i = 0; i < requestedCopyLen; ++i) - { - const uint32_t writeOffset = (state.writePos + i) % cap; - if (!dtxCopyBytes(rdram, state.wkAddr + writeOffset, srcDataAddr + i, 1u)) - { - break; - } - ++copiedLen; - } + DtxSjxState &sjx = sjxIt->second; + if (sjx.xid != 0u && sjx.xid != cmdXid) + { + continue; + } - state.writePos = (state.writePos + copiedLen) % cap; - state.roomBytes -= copiedLen; - state.dataBytes = std::min(cap, state.dataBytes + copiedLen); -} + auto sjrmtIt = g_dtx_sjrmt_by_handle.find(sjx.dstSjHandle); + if (sjrmtIt == g_dtx_sjrmt_by_handle.end()) + { + continue; + } -void dtxConsumeSjrmtDataLocked(DtxSjrmtState &state) -{ - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (cap == 0u || state.dataBytes == 0u) - { - return; - } + if (cmdLine == sjx.line) + { + dtxAppendToSjrmtData(rdram, sjrmtIt->second, chunkDataAddr, chunkLen); - const uint32_t consumed = std::min(cap, state.dataBytes); - state.readPos = (state.readPos + consumed) % cap; - state.dataBytes -= consumed; - state.roomBytes = std::min(cap, state.roomBytes + consumed); -} + // The IOP side consumes data from the remote SJ stream and returns the + // chunk to the EE-side room queue. Rewriting the ack packet to line 0 + // makes the EE sjx_rcvcbf recycle the chunk instead of requeueing it + // as playable data forever. + cmdPtr[1] = 0u; + } + } -void dtxConsumeActivePs2RnaStreamsLocked() -{ - for (const auto &entry : g_dtx_ps2rna_by_handle) - { - const DtxPs2RnaState &rna = entry.second; - if (!rna.playEnabled) - { - continue; + dtxConsumeActivePs2RnaStreamsLocked(); } - auto consumeHandle = [&](uint32_t sjHandle) + void dtxApplyPs2RnaPayload(uint8_t *rdram, const DtxTransferState &transfer) { - if (sjHandle == 0u) + if (!rdram || transfer.eeWorkAddr == 0u || transfer.wkSize < 64u) { return; } - auto it = g_dtx_sjrmt_by_handle.find(sjHandle); - if (it == g_dtx_sjrmt_by_handle.end()) + uint32_t commandCount = 0u; + if (!readGuestU32(rdram, transfer.eeWorkAddr, commandCount)) { return; } - dtxConsumeSjrmtDataLocked(it->second); - }; + commandCount = std::min(commandCount, 128u); + if (commandCount == 0u) + { + return; + } - consumeHandle(rna.sjHandle0); - consumeHandle(rna.sjHandle1); - } -} + constexpr uint32_t kPs2RnaHeaderSize = 16u; + constexpr uint32_t kPs2RnaCommandSize = 16u; -void dtxApplySjxPayload(uint8_t *rdram, const DtxTransferState &transfer) -{ - if (!rdram || transfer.eeWorkAddr == 0u || transfer.wkSize < 64u) - { - return; - } + std::lock_guard lock(g_dtx_rpc_mutex); + for (uint32_t i = 0; i < commandCount; ++i) + { + const uint32_t cmdAddr = transfer.eeWorkAddr + kPs2RnaHeaderSize + (i * kPs2RnaCommandSize); + const uint8_t *cmdPtr = getConstMemPtr(rdram, cmdAddr); + if (!cmdPtr) + { + break; + } - uint32_t commandCount = 0u; - if (!readGuestU32(rdram, transfer.eeWorkAddr, commandCount)) - { - return; - } + uint16_t cmdNo = 0u; + uint32_t rnaHandle = 0u; + uint32_t arg1 = 0u; + uint32_t arg2 = 0u; + std::memcpy(&cmdNo, cmdPtr + 0u, sizeof(cmdNo)); + std::memcpy(&rnaHandle, cmdPtr + 4u, sizeof(rnaHandle)); + std::memcpy(&arg1, cmdPtr + 8u, sizeof(arg1)); + std::memcpy(&arg2, cmdPtr + 12u, sizeof(arg2)); + + auto it = g_dtx_ps2rna_by_handle.find(rnaHandle); + if (it == g_dtx_ps2rna_by_handle.end()) + { + continue; + } - commandCount = std::min(commandCount, 128u); - if (commandCount == 0u) - { - return; - } + DtxPs2RnaState &state = it->second; + switch (cmdNo) + { + case 0u: // IOPRNA_CMD_START + state.playEnabled = true; + break; + case 1u: // IOPRNA_CMD_STOP + state.playEnabled = false; + break; + case 2u: // IOPRNA_CMD_SETPSW + state.playEnabled = (arg1 != 0u); + break; + case 3u: // IOPRNA_CMD_SETNCH + state.channelCount = arg1; + break; + case 4u: // IOPRNA_CMD_SETSFREQ + state.sampleFreq = arg1; + break; + case 5u: // IOPRNA_CMD_SETVOL + state.volume = arg2; + break; + default: + break; + } + } - constexpr uint32_t kSjxHeaderSize = 16u; - constexpr uint32_t kSjxCommandSize = 16u; + dtxConsumeActivePs2RnaStreamsLocked(); + } + + } // namespace - std::lock_guard lock(g_dtx_rpc_mutex); - for (uint32_t i = 0; i < commandCount; ++i) + void noteDtxSifDmaTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes) { - const uint32_t cmdAddr = transfer.eeWorkAddr + kSjxHeaderSize + (i * kSjxCommandSize); - uint8_t *cmdPtr = getMemPtr(rdram, cmdAddr); - if (!cmdPtr) + if (!rdram || sizeBytes < 64u) { - break; + return; } - const uint8_t cmdNo = cmdPtr[0]; - const uint8_t cmdLine = cmdPtr[1]; - uint16_t cmdXid = 0u; - uint32_t sjxHandle = 0u; - uint32_t chunkDataAddr = 0u; - uint32_t chunkLen = 0u; - std::memcpy(&cmdXid, cmdPtr + 2u, sizeof(cmdXid)); - std::memcpy(&sjxHandle, cmdPtr + 4u, sizeof(sjxHandle)); - std::memcpy(&chunkDataAddr, cmdPtr + 8u, sizeof(chunkDataAddr)); - std::memcpy(&chunkLen, cmdPtr + 12u, sizeof(chunkLen)); - - if (cmdNo != 0u || chunkLen == 0u) + uint32_t normalizedSrc = 0u; + uint32_t normalizedDst = 0u; + if (!normalizeGuestLinearAddr(srcAddr, normalizedSrc) || + !normalizeGuestLinearAddr(dstAddr, normalizedDst)) { - continue; + return; } - auto sjxIt = g_dtx_sjx_by_handle.find(sjxHandle); - if (sjxIt == g_dtx_sjx_by_handle.end()) + DtxTransferState matched{}; + bool found = false; { - continue; + std::lock_guard lock(g_dtx_rpc_mutex); + if (!g_dtxCompatLayout.isConfigured()) + { + return; + } + for (const auto &entry : g_dtx_transfer_by_id) + { + if (dtxMatchesTransfer(entry.second, normalizedSrc, normalizedDst, sizeBytes)) + { + matched = entry.second; + found = true; + break; + } + } } - DtxSjxState &sjx = sjxIt->second; - if (sjx.xid != 0u && sjx.xid != cmdXid) + if (!found) { - continue; + static uint32_t dtxMissLogCount = 0u; + if (dtxMissLogCount < 64u) + { + uint32_t knownTransfers = 0u; + uint32_t sampleDtxId = 0u; + uint32_t sampleSrc = 0u; + uint32_t sampleSize = 0u; + { + std::lock_guard lock(g_dtx_rpc_mutex); + knownTransfers = static_cast(g_dtx_transfer_by_id.size()); + if (!g_dtx_transfer_by_id.empty()) + { + const auto &sample = *g_dtx_transfer_by_id.begin(); + sampleDtxId = sample.second.dtxId; + sampleSrc = sample.second.eeWorkAddr; + sampleSize = sample.second.wkSize; + } + } + + if (knownTransfers != 0u) + { + RUNTIME_LOG("[sceSifSetDma:DTX_MISS] src=0x" << std::hex << normalizedSrc + << " dst=0x" << normalizedDst + << " size=0x" << sizeBytes + << " known=" << std::dec << knownTransfers + << " sampleDtxId=0x" << std::hex << sampleDtxId + << " sampleSrc=0x" << sampleSrc + << " sampleSize=0x" << sampleSize + << std::dec << std::endl); + ++dtxMissLogCount; + } + } + return; } - auto sjrmtIt = g_dtx_sjrmt_by_handle.find(sjx.dstSjHandle); - if (sjrmtIt == g_dtx_sjrmt_by_handle.end()) + const uint32_t footerTicketAddr = matched.eeWorkAddr + matched.wkSize - sizeof(uint32_t); + uint32_t ticketNo = 0u; + if (!readGuestU32(rdram, footerTicketAddr, ticketNo)) { - continue; + return; } - if (cmdLine == sjx.line) + (void)writeGuestU32(rdram, footerTicketAddr, ticketNo + 1u); + + if (matched.dtxId == 0u) { - dtxAppendToSjrmtData(rdram, sjrmtIt->second, chunkDataAddr, chunkLen); + dtxApplySjxPayload(rdram, matched); + } + else if (matched.dtxId == 1u) + { + dtxApplyPs2RnaPayload(rdram, matched); + } - // The IOP side consumes data from the remote SJ stream and returns the - // chunk to the EE-side room queue. Rewriting the ack packet to line 0 - // makes the EE sjx_rcvcbf recycle the chunk instead of requeueing it - // as playable data forever. - cmdPtr[1] = 0u; + static uint32_t dtxAckLogCount = 0u; + if (dtxAckLogCount < 32u) + { + RUNTIME_LOG("[sceSifSetDma:DTX_ACK] dtxId=0x" << std::hex << matched.dtxId + << " ee=0x" << matched.eeWorkAddr + << " iop=0x" << matched.iopWorkAddr + << " size=0x" << matched.wkSize + << " ticket=0x" << ticketNo + << "->0x" << (ticketNo + 1u)); + if (matched.dtxId == 0u) + { + uint32_t totalData = 0u; + std::lock_guard lock(g_dtx_rpc_mutex); + for (const auto &entry : g_dtx_sjrmt_by_handle) + { + totalData += entry.second.dataBytes; + } + RUNTIME_LOG(" sjrmtData=0x" << totalData); + } + RUNTIME_LOG(std::dec << std::endl); + ++dtxAckLogCount; } } - dtxConsumeActivePs2RnaStreamsLocked(); -} - -void dtxApplyPs2RnaPayload(uint8_t *rdram, const DtxTransferState &transfer) -{ - if (!rdram || transfer.eeWorkAddr == 0u || transfer.wkSize < 64u) + void resetSoundDriverRpcState() { - return; + std::lock_guard lock(g_rpc_mutex); + resetSoundDriverRpcStateUnlocked(); } - uint32_t commandCount = 0u; - if (!readGuestU32(rdram, transfer.eeWorkAddr, commandCount)) + void setSoundDriverCompatLayout(const PS2SoundDriverCompatLayout &layout) { - return; + std::lock_guard lock(g_rpc_mutex); + g_soundDriverCompatLayout = layout; } - commandCount = std::min(commandCount, 128u); - if (commandCount == 0u) + void clearSoundDriverCompatLayout() { - return; + std::lock_guard lock(g_rpc_mutex); + g_soundDriverCompatLayout = {}; } - constexpr uint32_t kPs2RnaHeaderSize = 16u; - constexpr uint32_t kPs2RnaCommandSize = 16u; - - std::lock_guard lock(g_dtx_rpc_mutex); - for (uint32_t i = 0; i < commandCount; ++i) + void setDtxCompatLayout(const PS2DtxCompatLayout &layout) { - const uint32_t cmdAddr = transfer.eeWorkAddr + kPs2RnaHeaderSize + (i * kPs2RnaCommandSize); - const uint8_t *cmdPtr = getConstMemPtr(rdram, cmdAddr); - if (!cmdPtr) - { - break; - } - - uint16_t cmdNo = 0u; - uint32_t rnaHandle = 0u; - uint32_t arg1 = 0u; - uint32_t arg2 = 0u; - std::memcpy(&cmdNo, cmdPtr + 0u, sizeof(cmdNo)); - std::memcpy(&rnaHandle, cmdPtr + 4u, sizeof(rnaHandle)); - std::memcpy(&arg1, cmdPtr + 8u, sizeof(arg1)); - std::memcpy(&arg2, cmdPtr + 12u, sizeof(arg2)); - - auto it = g_dtx_ps2rna_by_handle.find(rnaHandle); - if (it == g_dtx_ps2rna_by_handle.end()) - { - continue; - } - - DtxPs2RnaState &state = it->second; - switch (cmdNo) - { - case 0u: // IOPRNA_CMD_START - state.playEnabled = true; - break; - case 1u: // IOPRNA_CMD_STOP - state.playEnabled = false; - break; - case 2u: // IOPRNA_CMD_SETPSW - state.playEnabled = (arg1 != 0u); - break; - case 3u: // IOPRNA_CMD_SETNCH - state.channelCount = arg1; - break; - case 4u: // IOPRNA_CMD_SETSFREQ - state.sampleFreq = arg1; - break; - case 5u: // IOPRNA_CMD_SETVOL - state.volume = arg2; - break; - default: - break; - } + std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); + g_dtxCompatLayout = layout; + resetDtxRpcStateUnlocked(); } - dtxConsumeActivePs2RnaStreamsLocked(); -} - -} // namespace + void clearDtxCompatLayout() + { + std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); + g_dtxCompatLayout = {}; + resetDtxRpcStateUnlocked(); + } -void noteDtxSifDmaTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t sizeBytes) -{ - if (!rdram || sizeBytes < 64u) + void prepareSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t size) { - return; + std::lock_guard lock(g_rpc_mutex); + if (srcAddr != g_soundDriverRpcState.statusAddr || size != kSoundDriverStatusSize) + { + return; + } + + backfillSoundDriverStatusFromCompatUnlocked(rdram); } - uint32_t normalizedSrc = 0u; - uint32_t normalizedDst = 0u; - if (!normalizeGuestLinearAddr(srcAddr, normalizedSrc) || - !normalizeGuestLinearAddr(dstAddr, normalizedDst)) + void finalizeSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t size) { - return; + (void)rdram; + (void)srcAddr; + (void)dstAddr; + (void)size; } - DtxTransferState matched{}; - bool found = false; + void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_dtx_rpc_mutex); - if (!g_dtxCompatLayout.isConfigured()) - { - return; - } - for (const auto &entry : g_dtx_transfer_by_id) + const int32_t moduleId = static_cast(getRegU32(ctx, 4)); // $a0 + const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional) + + uint32_t refsLeft = 0; + const bool knownModule = trackSifModuleStop(moduleId, &refsLeft); + const int32_t ret = knownModule ? 0 : -1; + + if (resultAddr != 0) { - if (dtxMatchesTransfer(entry.second, normalizedSrc, normalizedDst, sizeBytes)) + int32_t *hostResult = reinterpret_cast(getMemPtr(rdram, resultAddr)); + if (hostResult) { - matched = entry.second; - found = true; - break; + *hostResult = knownModule ? 0 : -1; } } - } - if (!found) - { - static uint32_t dtxMissLogCount = 0u; - if (dtxMissLogCount < 64u) + if (knownModule) { - uint32_t knownTransfers = 0u; - uint32_t sampleDtxId = 0u; - uint32_t sampleSrc = 0u; - uint32_t sampleSize = 0u; + std::string modulePath; { - std::lock_guard lock(g_dtx_rpc_mutex); - knownTransfers = static_cast(g_dtx_transfer_by_id.size()); - if (!g_dtx_transfer_by_id.empty()) + std::lock_guard lock(g_sif_module_mutex); + auto it = g_sif_modules_by_id.find(moduleId); + if (it != g_sif_modules_by_id.end()) { - const auto &sample = *g_dtx_transfer_by_id.begin(); - sampleDtxId = sample.second.dtxId; - sampleSrc = sample.second.eeWorkAddr; - sampleSize = sample.second.wkSize; + modulePath = it->second.path; } } - - if (knownTransfers != 0u) - { - RUNTIME_LOG("[sceSifSetDma:DTX_MISS] src=0x" << std::hex << normalizedSrc - << " dst=0x" << normalizedDst - << " size=0x" << sizeBytes - << " known=" << std::dec << knownTransfers - << " sampleDtxId=0x" << std::hex << sampleDtxId - << " sampleSrc=0x" << sampleSrc - << " sampleSize=0x" << sampleSize - << std::dec << std::endl); - ++dtxMissLogCount; - } + logSifModuleAction("stop", moduleId, modulePath, refsLeft); } - return; - } - - const uint32_t footerTicketAddr = matched.eeWorkAddr + matched.wkSize - sizeof(uint32_t); - uint32_t ticketNo = 0u; - if (!readGuestU32(rdram, footerTicketAddr, ticketNo)) - { - return; - } - (void)writeGuestU32(rdram, footerTicketAddr, ticketNo + 1u); - - if (matched.dtxId == 0u) - { - dtxApplySjxPayload(rdram, matched); - } - else if (matched.dtxId == 1u) - { - dtxApplyPs2RnaPayload(rdram, matched); + setReturnS32(ctx, ret); } - static uint32_t dtxAckLogCount = 0u; - if (dtxAckLogCount < 32u) + void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("[sceSifSetDma:DTX_ACK] dtxId=0x" << std::hex << matched.dtxId - << " ee=0x" << matched.eeWorkAddr - << " iop=0x" << matched.iopWorkAddr - << " size=0x" << matched.wkSize - << " ticket=0x" << ticketNo - << "->0x" << (ticketNo + 1u)); - if (matched.dtxId == 0u) + const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes); + if (modulePath.empty()) { - uint32_t totalData = 0u; - std::lock_guard lock(g_dtx_rpc_mutex); - for (const auto &entry : g_dtx_sjrmt_by_handle) - { - totalData += entry.second.dataBytes; - } - RUNTIME_LOG(" sjrmtData=0x" << totalData); + setReturnS32(ctx, -1); + return; } - RUNTIME_LOG(std::dec << std::endl); - ++dtxAckLogCount; - } -} - -void resetSoundDriverRpcState() -{ - std::lock_guard lock(g_rpc_mutex); - resetSoundDriverRpcStateUnlocked(); -} -void setSoundDriverCompatLayout(const PS2SoundDriverCompatLayout &layout) -{ - std::lock_guard lock(g_rpc_mutex); - g_soundDriverCompatLayout = layout; -} - -void clearSoundDriverCompatLayout() -{ - std::lock_guard lock(g_rpc_mutex); - g_soundDriverCompatLayout = {}; -} - -void setDtxCompatLayout(const PS2DtxCompatLayout &layout) -{ - std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); - g_dtxCompatLayout = layout; - resetDtxRpcStateUnlocked(); -} - -void clearDtxCompatLayout() -{ - std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); - g_dtxCompatLayout = {}; - resetDtxRpcStateUnlocked(); -} - -void prepareSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t size) -{ - std::lock_guard lock(g_rpc_mutex); - if (srcAddr != g_soundDriverRpcState.statusAddr || size != kSoundDriverStatusSize) - { - return; - } - - backfillSoundDriverStatusFromCompatUnlocked(rdram); -} - -void finalizeSoundDriverStatusTransfer(uint8_t *rdram, uint32_t srcAddr, uint32_t dstAddr, uint32_t size) -{ - (void)rdram; - (void)srcAddr; - (void)dstAddr; - (void)size; -} - -void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const int32_t moduleId = static_cast(getRegU32(ctx, 4)); // $a0 - const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional) - - uint32_t refsLeft = 0; - const bool knownModule = trackSifModuleStop(moduleId, &refsLeft); - const int32_t ret = knownModule ? 0 : -1; - - if (resultAddr != 0) - { - int32_t *hostResult = reinterpret_cast(getMemPtr(rdram, resultAddr)); - if (hostResult) + const int32_t moduleId = trackSifModuleLoad(modulePath); + if (moduleId <= 0) { - *hostResult = knownModule ? 0 : -1; + setReturnS32(ctx, -1); + return; } - } - if (knownModule) - { - std::string modulePath; + uint32_t refs = 0; { std::lock_guard lock(g_sif_module_mutex); auto it = g_sif_modules_by_id.find(moduleId); if (it != g_sif_modules_by_id.end()) { - modulePath = it->second.path; + refs = it->second.refCount; } } - logSifModuleAction("stop", moduleId, modulePath, refsLeft); - } - - setReturnS32(ctx, ret); -} - -void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes); - if (modulePath.empty()) - { - setReturnS32(ctx, -1); - return; - } + logSifModuleAction("load", moduleId, modulePath, refs); - const int32_t moduleId = trackSifModuleLoad(modulePath); - if (moduleId <= 0) - { - setReturnS32(ctx, -1); - return; + setReturnS32(ctx, moduleId); } - uint32_t refs = 0; + void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sif_module_mutex); - auto it = g_sif_modules_by_id.find(moduleId); - if (it != g_sif_modules_by_id.end()) + std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); + if (runtime) + { + runtime->iop().init(rdram); + } + if (!g_rpc_initialized) + { + g_rpc_servers.clear(); + g_rpc_clients.clear(); + g_rpc_next_id = 1; + g_rpc_packet_index = 0; + g_rpc_server_index = 0; + g_rpc_active_queue = 0; + resetDtxRpcStateUnlocked(); + g_soundDriverRpcState.initialized = false; + g_rpc_initialized = true; + RUNTIME_LOG("[SifInitRpc] Initialized"); + } + else { - refs = it->second.refCount; + resetDtxRpcStateUnlocked(); + g_soundDriverRpcState.initialized = false; } + setReturnS32(ctx, 0); } - logSifModuleAction("load", moduleId, modulePath, refs); - - setReturnS32(ctx, moduleId); -} -void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::scoped_lock lock(g_rpc_mutex, g_dtx_rpc_mutex); - if (runtime) - { - runtime->iop().init(rdram); - } - if (!g_rpc_initialized) - { - g_rpc_servers.clear(); - g_rpc_clients.clear(); - g_rpc_next_id = 1; - g_rpc_packet_index = 0; - g_rpc_server_index = 0; - g_rpc_active_queue = 0; - resetDtxRpcStateUnlocked(); - g_soundDriverRpcState.initialized = false; - g_rpc_initialized = true; - RUNTIME_LOG("[SifInitRpc] Initialized"); - } - else + void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - resetDtxRpcStateUnlocked(); - g_soundDriverRpcState.initialized = false; - } - setReturnS32(ctx, 0); -} - -void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clientPtr = getRegU32(ctx, 4); - uint32_t rpcId = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - - t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + uint32_t clientPtr = getRegU32(ctx, 4); + uint32_t rpcId = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); - if (!client) - { - setReturnS32(ctx, -1); - return; - } + t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); - client->command = 0; - client->buf = 0; - client->cbuf = 0; - client->end_function = 0; - client->end_param = 0; - client->server = 0; - client->hdr.pkt_addr = 0; - client->hdr.sema_id = -1; - client->hdr.mode = mode; - - uint32_t serverPtr = 0; - { - std::lock_guard lock(g_rpc_mutex); - client->hdr.rpc_id = g_rpc_next_id++; - auto it = g_rpc_servers.find(rpcId); - if (it != g_rpc_servers.end()) + if (!client) { - serverPtr = it->second.sd_ptr; + setReturnS32(ctx, -1); + return; } - g_rpc_clients[clientPtr] = {}; - g_rpc_clients[clientPtr].sid = rpcId; - } - if (!serverPtr) - { - // Allocate a dummy server so bind loops can proceed. - serverPtr = rpcAllocServerAddr(rdram); - if (serverPtr) + client->command = 0; + client->buf = 0; + client->cbuf = 0; + client->end_function = 0; + client->end_param = 0; + client->server = 0; + client->hdr.pkt_addr = 0; + client->hdr.sema_id = -1; + client->hdr.mode = mode; + + uint32_t serverPtr = 0; { - t_SifRpcServerData *dummy = reinterpret_cast(getMemPtr(rdram, serverPtr)); - if (dummy) + std::lock_guard lock(g_rpc_mutex); + client->hdr.rpc_id = g_rpc_next_id++; + auto it = g_rpc_servers.find(rpcId); + if (it != g_rpc_servers.end()) { - std::memset(dummy, 0, sizeof(*dummy)); - dummy->sid = static_cast(rpcId); + serverPtr = it->second.sd_ptr; } - std::lock_guard lock(g_rpc_mutex); - g_rpc_servers[rpcId] = {rpcId, serverPtr}; + g_rpc_clients[clientPtr] = {}; + g_rpc_clients[clientPtr].sid = rpcId; } - } - - if (serverPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, serverPtr)); - client->server = serverPtr; - client->buf = sd ? sd->buf : 0; - client->cbuf = sd ? sd->cbuf : 0; - } - else - { - client->server = 0; - client->buf = 0; - client->cbuf = 0; - } - setReturnS32(ctx, 0); -} - -void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::lock_guard rpcCallLock(g_sif_call_rpc_mutex); - - uint32_t clientPtr = getRegU32(ctx, 4); - uint32_t rpcNum = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t sendBuf = getRegU32(ctx, 7); - uint32_t sendSize = 0; - uint32_t recvBuf = 0; - uint32_t recvSize = 0; - uint32_t endFunc = 0; - uint32_t endParam = 0; - - // Decode both extended-reg convention (EE default) and standard O32 stack convention, - // picking REG whenever plausible, to avoid zero-collision on the stack. - uint32_t sp = getRegU32(ctx, 29); - - uint32_t sendSizeReg = getRegU32(ctx, 8); - uint32_t recvBufReg = getRegU32(ctx, 9); - uint32_t recvSizeReg = getRegU32(ctx, 10); - uint32_t endFuncReg = getRegU32(ctx, 11); - uint32_t endParamReg = 0; - (void)readStackU32(rdram, sp, 0x0, endParamReg); - - uint32_t sendSizeStk = 0; - uint32_t recvBufStk = 0; - uint32_t recvSizeStk = 0; - uint32_t endFuncStk = 0; - uint32_t endParamStk = 0; - (void)readStackU32(rdram, sp, 0x10, sendSizeStk); - (void)readStackU32(rdram, sp, 0x14, recvBufStk); - (void)readStackU32(rdram, sp, 0x18, recvSizeStk); - (void)readStackU32(rdram, sp, 0x1C, endFuncStk); - (void)readStackU32(rdram, sp, 0x20, endParamStk); - - auto looksLikeGuestPtr = [&](uint32_t v) -> bool - { - if (v == 0) - return true; - const uint32_t norm = v & 0x1FFFFFFFu; - return norm >= 0x10000u && norm < PS2_RAM_SIZE; - }; - - auto looksLikeSize = [&](uint32_t v) -> bool - { - return v <= 0x2000000u; - }; - - auto looksLikeFunc = [&](uint32_t v) -> bool - { - return v == 0 || looksLikeGuestPtr(v); - }; - - auto plausiblePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz, uint32_t endFn) -> bool - { - return looksLikeSize(sendSz) && looksLikeGuestPtr(rbuf) && looksLikeSize(rsz) && looksLikeFunc(endFn); - }; - - const bool regPackPlausible = plausiblePack(sendSizeReg, recvBufReg, recvSizeReg, endFuncReg); - const bool stackPackPlausible = plausiblePack(sendSizeStk, recvBufStk, recvSizeStk, endFuncStk); - - uint32_t boundSidHint = 0u; - { - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it != g_rpc_clients.end()) + if (!serverPtr) { - boundSidHint = it->second.sid; + // Allocate a dummy server so bind loops can proceed. + serverPtr = rpcAllocServerAddr(rdram); + if (serverPtr) + { + t_SifRpcServerData *dummy = reinterpret_cast(getMemPtr(rdram, serverPtr)); + if (dummy) + { + std::memset(dummy, 0, sizeof(*dummy)); + dummy->sid = static_cast(rpcId); + } + std::lock_guard lock(g_rpc_mutex); + g_rpc_servers[rpcId] = {rpcId, serverPtr}; + } } - } - PS2DtxCompatLayout dtxCompat{}; - { - std::lock_guard lock(g_dtx_rpc_mutex); - dtxCompat = g_dtxCompatLayout; - } - auto looksLikeDtxCreatePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz) -> bool - { - return rbuf != 0u && rsz >= 4u && rsz <= 0x40u && - sendSz >= 12u && sendSz <= 0x1000u; - }; - - const bool isDtxCreate34Call = dtxCompat.isConfigured() && - (boundSidHint == dtxCompat.rpcSid) && - (rpcNum == 0x422u); - const bool forceStackForDtxCreate34 = - isDtxCreate34Call && - stackPackPlausible && - looksLikeDtxCreatePack(sendSizeStk, recvBufStk, recvSizeStk) && - !looksLikeDtxCreatePack(sendSizeReg, recvBufReg, recvSizeReg); - - bool useRegConvention = true; - if (forceStackForDtxCreate34) - { - useRegConvention = false; - } - else if (!regPackPlausible && stackPackPlausible) - { - const bool regHasValidCallback = (endFuncReg != 0u) && looksLikeFunc(endFuncReg); - const bool stackHasValidCallback = (endFuncStk != 0u) && looksLikeFunc(endFuncStk); - if (!(regHasValidCallback && !stackHasValidCallback)) + if (serverPtr) { - useRegConvention = false; + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, serverPtr)); + client->server = serverPtr; + client->buf = sd ? sd->buf : 0; + client->cbuf = sd ? sd->cbuf : 0; + } + else + { + client->server = 0; + client->buf = 0; + client->cbuf = 0; } + + setReturnS32(ctx, 0); } - sendSize = useRegConvention ? sendSizeReg : sendSizeStk; - recvBuf = useRegConvention ? recvBufReg : recvBufStk; - recvSize = useRegConvention ? recvSizeReg : recvSizeStk; - endFunc = useRegConvention ? endFuncReg : endFuncStk; - endParam = useRegConvention ? endParamReg : endParamStk; + void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + std::lock_guard rpcCallLock(g_sif_call_rpc_mutex); + + uint32_t clientPtr = getRegU32(ctx, 4); + uint32_t rpcNum = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t sendBuf = getRegU32(ctx, 7); + uint32_t sendSize = 0; + uint32_t recvBuf = 0; + uint32_t recvSize = 0; + uint32_t endFunc = 0; + uint32_t endParam = 0; + + // Decode both extended-reg convention (EE default) and standard O32 stack convention, + // picking REG whenever plausible, to avoid zero-collision on the stack. + uint32_t sp = getRegU32(ctx, 29); + + uint32_t sendSizeReg = getRegU32(ctx, 8); + uint32_t recvBufReg = getRegU32(ctx, 9); + uint32_t recvSizeReg = getRegU32(ctx, 10); + uint32_t endFuncReg = getRegU32(ctx, 11); + uint32_t endParamReg = 0; + (void)readStackU32(rdram, sp, 0x0, endParamReg); + + uint32_t sendSizeStk = 0; + uint32_t recvBufStk = 0; + uint32_t recvSizeStk = 0; + uint32_t endFuncStk = 0; + uint32_t endParamStk = 0; + (void)readStackU32(rdram, sp, 0x10, sendSizeStk); + (void)readStackU32(rdram, sp, 0x14, recvBufStk); + (void)readStackU32(rdram, sp, 0x18, recvSizeStk); + (void)readStackU32(rdram, sp, 0x1C, endFuncStk); + (void)readStackU32(rdram, sp, 0x20, endParamStk); + + auto looksLikeGuestPtr = [&](uint32_t v) -> bool + { + if (v == 0) + return true; + const uint32_t norm = v & 0x1FFFFFFFu; + return norm >= 0x10000u && norm < PS2_RAM_SIZE; + }; - const bool isDtxLikeRpc = dtxCompat.isConfigured() && - ((boundSidHint == dtxCompat.rpcSid) || ((rpcNum & 0xFF00u) == 0x0400u)); - static uint32_t dtxAbiLogCount = 0u; - if (isDtxLikeRpc && dtxAbiLogCount < 96u) - { - RUNTIME_LOG("[SifCallRpc:ABI] client=0x" << std::hex << clientPtr - << " rpc=0x" << rpcNum - << " sidHint=0x" << boundSidHint - << " useReg=" << (useRegConvention ? 1 : 0) - << " reg=(" << sendSizeReg << "," << recvBufReg << "," << recvSizeReg << "," << endFuncReg << "," << endParamReg << ")" - << " stk=(" << sendSizeStk << "," << recvBufStk << "," << recvSizeStk << "," << endFuncStk << "," << endParamStk << ")" - << " plausible=(" << (regPackPlausible ? 1 : 0) << "," << (stackPackPlausible ? 1 : 0) << ")" - << " force34=" << (forceStackForDtxCreate34 ? 1 : 0) - << std::dec << std::endl); - ++dtxAbiLogCount; - } + auto looksLikeSize = [&](uint32_t v) -> bool + { + return v <= 0x2000000u; + }; - t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + auto looksLikeFunc = [&](uint32_t v) -> bool + { + return v == 0 || looksLikeGuestPtr(v); + }; - if (!client) - { - setReturnS32(ctx, -1); - return; - } + auto plausiblePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz, uint32_t endFn) -> bool + { + return looksLikeSize(sendSz) && looksLikeGuestPtr(rbuf) && looksLikeSize(rsz) && looksLikeFunc(endFn); + }; - client->command = rpcNum; - client->end_function = endFunc; - client->end_param = endParam; - client->hdr.mode = mode; + const bool regPackPlausible = plausiblePack(sendSizeReg, recvBufReg, recvSizeReg, endFuncReg); + const bool stackPackPlausible = plausiblePack(sendSizeStk, recvBufStk, recvSizeStk, endFuncStk); - { - std::lock_guard lock(g_rpc_mutex); - g_rpc_clients[clientPtr].busy = true; - g_rpc_clients[clientPtr].last_rpc = rpcNum; - uint32_t sid = g_rpc_clients[clientPtr].sid; - if (sid) + uint32_t boundSidHint = 0u; { - auto it = g_rpc_servers.find(sid); - if (it != g_rpc_servers.end()) + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it != g_rpc_clients.end()) { - uint32_t mappedServer = it->second.sd_ptr; - if (mappedServer && client->server != mappedServer) - { - client->server = mappedServer; - } + boundSidHint = it->second.sid; } } - } - - uint32_t sid = 0; - { - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it != g_rpc_clients.end()) + PS2DtxCompatLayout dtxCompat{}; { - sid = it->second.sid; + std::lock_guard lock(g_dtx_rpc_mutex); + dtxCompat = g_dtxCompatLayout; } - } - - uint32_t serverPtr = client->server; - t_SifRpcServerData *sd = serverPtr ? reinterpret_cast(getMemPtr(rdram, serverPtr)) : nullptr; - - if (sd) - { - sd->client = clientPtr; - sd->pkt_addr = client->hdr.pkt_addr; - sd->rpc_number = rpcNum; - sd->size = static_cast(sendSize); - sd->recvbuf = recvBuf; - sd->rsize = static_cast(recvSize); - sd->rmode = ((mode & kSifRpcModeNowait) && endFunc == 0) ? 0 : 1; - sd->rid = 0; - } - if (sd && sd->buf && sendBuf && sendSize > 0) - { - rpcCopyToRdram(rdram, sd->buf, sendBuf, sendSize); - } + auto looksLikeDtxCreatePack = [&](uint32_t sendSz, uint32_t rbuf, uint32_t rsz) -> bool + { + return rbuf != 0u && rsz >= 4u && rsz <= 0x40u && + sendSz >= 12u && sendSz <= 0x1000u; + }; - uint32_t resultPtr = 0; - bool handled = false; + const bool isDtxCreate34Call = dtxCompat.isConfigured() && + (boundSidHint == dtxCompat.rpcSid) && + (rpcNum == 0x422u); + const bool forceStackForDtxCreate34 = + isDtxCreate34Call && + stackPackPlausible && + looksLikeDtxCreatePack(sendSizeStk, recvBufStk, recvSizeStk) && + !looksLikeDtxCreatePack(sendSizeReg, recvBufReg, recvSizeReg); - auto readRpcU32 = [&](uint32_t addr, uint32_t &out) -> bool - { - if (!addr) + bool useRegConvention = true; + if (forceStackForDtxCreate34) { - return false; + useRegConvention = false; } - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) + else if (!regPackPlausible && stackPackPlausible) { - return false; + const bool regHasValidCallback = (endFuncReg != 0u) && looksLikeFunc(endFuncReg); + const bool stackHasValidCallback = (endFuncStk != 0u) && looksLikeFunc(endFuncStk); + if (!(regHasValidCallback && !stackHasValidCallback)) + { + useRegConvention = false; + } } - std::memcpy(&out, ptr, sizeof(out)); - return true; - }; - auto writeRpcU32 = [&](uint32_t addr, uint32_t value) -> bool - { - if (!addr) + sendSize = useRegConvention ? sendSizeReg : sendSizeStk; + recvBuf = useRegConvention ? recvBufReg : recvBufStk; + recvSize = useRegConvention ? recvSizeReg : recvSizeStk; + endFunc = useRegConvention ? endFuncReg : endFuncStk; + endParam = useRegConvention ? endParamReg : endParamStk; + + const bool isDtxLikeRpc = dtxCompat.isConfigured() && + ((boundSidHint == dtxCompat.rpcSid) || ((rpcNum & 0xFF00u) == 0x0400u)); + static uint32_t dtxAbiLogCount = 0u; + if (isDtxLikeRpc && dtxAbiLogCount < 96u) { - return false; + RUNTIME_LOG("[SifCallRpc:ABI] client=0x" << std::hex << clientPtr + << " rpc=0x" << rpcNum + << " sidHint=0x" << boundSidHint + << " useReg=" << (useRegConvention ? 1 : 0) + << " reg=(" << sendSizeReg << "," << recvBufReg << "," << recvSizeReg << "," << endFuncReg << "," << endParamReg << ")" + << " stk=(" << sendSizeStk << "," << recvBufStk << "," << recvSizeStk << "," << endFuncStk << "," << endParamStk << ")" + << " plausible=(" << (regPackPlausible ? 1 : 0) << "," << (stackPackPlausible ? 1 : 0) << ")" + << " force34=" << (forceStackForDtxCreate34 ? 1 : 0) + << std::dec << std::endl); + ++dtxAbiLogCount; } - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) + + t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + + if (!client) { - return false; + setReturnS32(ctx, -1); + return; } - std::memcpy(ptr, &value, sizeof(value)); - return true; - }; - if (!handled && runtime) - { - runtime->iop().init(rdram); - uint32_t iopResultPtr = 0u; - bool iopSignalNowaitCompletion = false; - if (runtime->iop().handleRPC(runtime, - sid, rpcNum, - sendBuf, sendSize, - recvBuf, recvSize, - iopResultPtr, - iopSignalNowaitCompletion)) - { - handled = true; - resultPtr = iopResultPtr; - if (iopSignalNowaitCompletion && (mode & kSifRpcModeNowait) != 0u) + client->command = rpcNum; + client->end_function = endFunc; + client->end_param = endParam; + client->hdr.mode = mode; + { - uint32_t semaId = static_cast(client->hdr.sema_id); - if (semaId == 0xFFFFFFFFu || semaId == 0u) + std::lock_guard lock(g_rpc_mutex); + g_rpc_clients[clientPtr].busy = true; + g_rpc_clients[clientPtr].last_rpc = rpcNum; + uint32_t sid = g_rpc_clients[clientPtr].sid; + if (sid) { - semaId = endParam; + auto it = g_rpc_servers.find(sid); + if (it != g_rpc_servers.end()) + { + uint32_t mappedServer = it->second.sd_ptr; + if (mappedServer && client->server != mappedServer) + { + client->server = mappedServer; + } + } } - (void)signalRpcCompletionSema(semaId); } - } - } - const bool isDtxUrpc = dtxCompat.isUrpcRpc(sid, rpcNum); - uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; - uint32_t dtxUrpcFn = 0; - uint32_t dtxUrpcObj = 0; - uint32_t dtxUrpcSend0 = 0; - bool dtxUrpcDispatchAttempted = false; - bool dtxUrpcFallbackEmulated = false; - bool dtxUrpcFallbackCreate34 = false; - bool hasUrpcHandler = false; - if (isDtxUrpc) - { - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, dtxUrpcSend0); - } - if (dtxCompat.hasUrpcTables() && dtxUrpcCommand < 64u) + uint32_t sid = 0; { - (void)readRpcU32(dtxCompat.urpcFnTableBase + (dtxUrpcCommand * 4u), dtxUrpcFn); - (void)readRpcU32(dtxCompat.urpcObjTableBase + (dtxUrpcCommand * 4u), dtxUrpcObj); + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it != g_rpc_clients.end()) + { + sid = it->second.sid; + } } - hasUrpcHandler = (dtxUrpcCommand < 64u) && (dtxUrpcFn != 0u); - } - const bool allowServerDispatch = !isDtxUrpc || hasUrpcHandler; - const bool isDtxSid = dtxCompat.isConfigured() && sid == dtxCompat.rpcSid; - if (sd && sd->func && (!isDtxSid || isDtxUrpc) && allowServerDispatch) - { - dtxUrpcDispatchAttempted = dtxUrpcDispatchAttempted || isDtxUrpc; - handled = rpcInvokeFunction(rdram, ctx, runtime, sd->func, rpcNum, sd->buf, sendSize, 0, &resultPtr); - if (handled && resultPtr == 0 && sd->buf) + uint32_t serverPtr = client->server; + t_SifRpcServerData *sd = serverPtr ? reinterpret_cast(getMemPtr(rdram, serverPtr)) : nullptr; + + if (sd) { - resultPtr = sd->buf; + sd->client = clientPtr; + sd->pkt_addr = client->hdr.pkt_addr; + sd->rpc_number = rpcNum; + sd->size = static_cast(sendSize); + sd->recvbuf = recvBuf; + sd->rsize = static_cast(recvSize); + sd->rmode = ((mode & kSifRpcModeNowait) && endFunc == 0) ? 0 : 1; + sd->rid = 0; } - if (handled && resultPtr == 0 && recvBuf) + + if (sd && sd->buf && sendBuf && sendSize > 0) { - resultPtr = recvBuf; + rpcCopyToRdram(rdram, sd->buf, sendBuf, sendSize); } - } - if (!handled && isDtxUrpc && sendBuf && sendSize > 0) - { - // Only dispatch through dtx_rpc_func when a URPC handler is registered in the table. - // If the slot is empty, defer to the fallback emulation below. - if (hasUrpcHandler && dtxCompat.dispatcherFuncAddr != 0u) + uint32_t resultPtr = 0; + bool handled = false; + + auto readRpcU32 = [&](uint32_t addr, uint32_t &out) -> bool { - dtxUrpcDispatchAttempted = true; - handled = rpcInvokeFunction(rdram, ctx, runtime, dtxCompat.dispatcherFuncAddr, rpcNum, sendBuf, sendSize, 0, &resultPtr); - if (handled && resultPtr == 0) + if (!addr) { - resultPtr = sendBuf; + return false; } - } - } + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; + }; - if (!handled && isDtxSid) - { - if (rpcNum == 2 && recvBuf && recvSize >= sizeof(uint32_t)) + auto writeRpcU32 = [&](uint32_t addr, uint32_t value) -> bool { - uint32_t dtxId = 0; - uint32_t eeWorkAddr = 0u; - uint32_t iopWorkAddr = 0u; - uint32_t wkSize = 0u; - if (sendBuf && sendSize >= sizeof(uint32_t)) + if (!addr) { - (void)readRpcU32(sendBuf + 0u, dtxId); + return false; } - if (sendBuf && sendSize >= (4u * sizeof(uint32_t))) + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) { - (void)readRpcU32(sendBuf + 4u, eeWorkAddr); - (void)readRpcU32(sendBuf + 8u, iopWorkAddr); - (void)readRpcU32(sendBuf + 12u, wkSize); + return false; } + std::memcpy(ptr, &value, sizeof(value)); + return true; + }; - uint32_t normalizedEeWorkAddr = 0u; - uint32_t normalizedIopWorkAddr = 0u; - (void)normalizeGuestLinearAddr(eeWorkAddr, normalizedEeWorkAddr); - (void)normalizeGuestLinearAddr(iopWorkAddr, normalizedIopWorkAddr); - - uint32_t remoteHandle = 0; + if (!handled && runtime) + { + runtime->iop().init(rdram); + uint32_t iopResultPtr = 0u; + bool iopSignalNowaitCompletion = false; + if (runtime->iop().handleRPC(runtime, + sid, rpcNum, + sendBuf, sendSize, + recvBuf, recvSize, + iopResultPtr, + iopSignalNowaitCompletion)) { - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_remote_by_id.find(dtxId); - if (it != g_dtx_remote_by_id.end()) + handled = true; + resultPtr = iopResultPtr; + if (iopSignalNowaitCompletion && (mode & kSifRpcModeNowait) != 0u) { - remoteHandle = it->second; - } - if (!remoteHandle) - { - remoteHandle = rpcAllocServerAddr(rdram); - if (!remoteHandle) - { - remoteHandle = rpcAllocPacketAddr(rdram); - } - if (!remoteHandle) + uint32_t semaId = static_cast(client->hdr.sema_id); + if (semaId == 0xFFFFFFFFu || semaId == 0u) { - remoteHandle = kRpcServerPoolBase + ((dtxId & 0xFFu) * kRpcServerStride); + semaId = endParam; } - g_dtx_remote_by_id[dtxId] = remoteHandle; + (void)signalRpcCompletionSema(semaId); } - - DtxTransferState state{}; - state.dtxId = dtxId; - state.remoteHandle = remoteHandle; - state.eeWorkAddr = normalizedEeWorkAddr; - state.iopWorkAddr = normalizedIopWorkAddr; - state.wkSize = wkSize; - g_dtx_transfer_by_id[dtxId] = state; } + } - (void)writeRpcU32(recvBuf, remoteHandle); - if (recvSize > sizeof(uint32_t)) + const bool isDtxUrpc = dtxCompat.isUrpcRpc(sid, rpcNum); + uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; + uint32_t dtxUrpcFn = 0; + uint32_t dtxUrpcObj = 0; + uint32_t dtxUrpcSend0 = 0; + bool dtxUrpcDispatchAttempted = false; + bool dtxUrpcFallbackEmulated = false; + bool dtxUrpcFallbackCreate34 = false; + bool hasUrpcHandler = false; + if (isDtxUrpc) + { + if (sendBuf && sendSize >= sizeof(uint32_t)) { - rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + (void)readRpcU32(sendBuf, dtxUrpcSend0); } - static uint32_t dtxCreateLogCount = 0; - if (dtxCreateLogCount < 64u) + if (dtxCompat.hasUrpcTables() && dtxUrpcCommand < 64u) { - RUNTIME_LOG("[SifCallRpc:DTX_CREATE] dtxId=0x" << std::hex << dtxId - << " remote=0x" << remoteHandle - << " recvBuf=0x" << recvBuf - << " recvSize=0x" << recvSize - << std::dec << std::endl); - ++dtxCreateLogCount; + (void)readRpcU32(dtxCompat.urpcFnTableBase + (dtxUrpcCommand * 4u), dtxUrpcFn); + (void)readRpcU32(dtxCompat.urpcObjTableBase + (dtxUrpcCommand * 4u), dtxUrpcObj); } - handled = true; - resultPtr = recvBuf; + hasUrpcHandler = (dtxUrpcCommand < 64u) && (dtxUrpcFn != 0u); } - else if (rpcNum == 3) + const bool allowServerDispatch = !isDtxUrpc || hasUrpcHandler; + const bool isDtxSid = dtxCompat.isConfigured() && sid == dtxCompat.rpcSid; + + if (sd && sd->func && (!isDtxSid || isDtxUrpc) && allowServerDispatch) { - uint32_t remoteHandle = 0; - if (sendBuf && sendSize >= sizeof(uint32_t) && readRpcU32(sendBuf, remoteHandle) && remoteHandle) + dtxUrpcDispatchAttempted = dtxUrpcDispatchAttempted || isDtxUrpc; + handled = rpcInvokeFunction(rdram, ctx, runtime, sd->func, rpcNum, sd->buf, sendSize, 0, &resultPtr); + if (handled && resultPtr == 0 && sd->buf) { - std::lock_guard lock(g_dtx_rpc_mutex); - for (auto it = g_dtx_remote_by_id.begin(); it != g_dtx_remote_by_id.end(); ++it) - { - if (it->second == remoteHandle) - { - g_dtx_transfer_by_id.erase(it->first); - g_dtx_remote_by_id.erase(it); - break; - } - } + resultPtr = sd->buf; } - if (recvBuf && recvSize > 0) + if (handled && resultPtr == 0 && recvBuf) { - rpcZeroRdram(rdram, recvBuf, recvSize); + resultPtr = recvBuf; } - handled = true; - resultPtr = recvBuf; } - else if (rpcNum >= 0x400 && rpcNum < 0x500) - { - dtxUrpcFallbackEmulated = true; - const uint32_t urpcCommand = rpcNum & 0xFFu; - uint32_t outWords[4] = {1u, 0u, 0u, 0u}; - uint32_t outWordCount = 1u; - auto readSendWord = [&](uint32_t index, uint32_t &out) -> bool + if (!handled && isDtxUrpc && sendBuf && sendSize > 0) + { + // Only dispatch through dtx_rpc_func when a URPC handler is registered in the table. + // If the slot is empty, defer to the fallback emulation below. + if (hasUrpcHandler && dtxCompat.dispatcherFuncAddr != 0u) { - const uint64_t byteOffset = static_cast(index) * sizeof(uint32_t); - if (!sendBuf || sendSize < (byteOffset + sizeof(uint32_t))) + dtxUrpcDispatchAttempted = true; + handled = rpcInvokeFunction(rdram, ctx, runtime, dtxCompat.dispatcherFuncAddr, rpcNum, sendBuf, sendSize, 0, &resultPtr); + if (handled && resultPtr == 0) { - return false; + resultPtr = sendBuf; } - return readRpcU32(sendBuf + static_cast(byteOffset), out); - }; + } + } - switch (urpcCommand) - { - case 0u: // SJX_CREATE + if (!handled && isDtxSid) + { + if (rpcNum == 2 && recvBuf && recvSize >= sizeof(uint32_t)) { - uint32_t srcSjHandle = 0u; - uint32_t dstSjHandle = 0u; - uint32_t line = 0u; - uint32_t eeObjAddr = 0u; - (void)readSendWord(0u, srcSjHandle); - (void)readSendWord(1u, dstSjHandle); - (void)readSendWord(2u, line); - (void)readSendWord(3u, eeObjAddr); + uint32_t dtxId = 0; + uint32_t eeWorkAddr = 0u; + uint32_t iopWorkAddr = 0u; + uint32_t wkSize = 0u; + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf + 0u, dtxId); + } + if (sendBuf && sendSize >= (4u * sizeof(uint32_t))) + { + (void)readRpcU32(sendBuf + 4u, eeWorkAddr); + (void)readRpcU32(sendBuf + 8u, iopWorkAddr); + (void)readRpcU32(sendBuf + 12u, wkSize); + } - std::lock_guard lock(g_dtx_rpc_mutex); - const uint32_t handle = dtxAllocUrpcHandleLocked(); - DtxSjxState state{}; - state.handle = handle; - state.srcSjHandle = srcSjHandle; - state.dstSjHandle = dstSjHandle; - state.line = line; - state.eeObjAddr = eeObjAddr; - g_dtx_sjx_by_handle[handle] = state; - outWords[0] = handle ? handle : 1u; - outWordCount = 1u; - break; - } - case 1u: // SJX_DESTROY - { - uint32_t handle = 0u; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - g_dtx_sjx_by_handle.erase(handle); - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 2u: // SJX_RESET - { - uint32_t handle = 0u; - uint32_t xid = 0u; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, xid); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjx_by_handle.find(handle); - if (it != g_dtx_sjx_by_handle.end()) + uint32_t normalizedEeWorkAddr = 0u; + uint32_t normalizedIopWorkAddr = 0u; + (void)normalizeGuestLinearAddr(eeWorkAddr, normalizedEeWorkAddr); + (void)normalizeGuestLinearAddr(iopWorkAddr, normalizedIopWorkAddr); + + uint32_t remoteHandle = 0; { - it->second.xid = static_cast(xid & 0xFFFFu); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_remote_by_id.find(dtxId); + if (it != g_dtx_remote_by_id.end()) + { + remoteHandle = it->second; + } + if (!remoteHandle) + { + remoteHandle = rpcAllocServerAddr(rdram); + if (!remoteHandle) + { + remoteHandle = rpcAllocPacketAddr(rdram); + } + if (!remoteHandle) + { + remoteHandle = kRpcServerPoolBase + ((dtxId & 0xFFu) * kRpcServerStride); + } + g_dtx_remote_by_id[dtxId] = remoteHandle; + } + + DtxTransferState state{}; + state.dtxId = dtxId; + state.remoteHandle = remoteHandle; + state.eeWorkAddr = normalizedEeWorkAddr; + state.iopWorkAddr = normalizedIopWorkAddr; + state.wkSize = wkSize; + g_dtx_transfer_by_id[dtxId] = state; } - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 8u: // PS2RNA_CREATE - { - uint32_t maxChannels = 0u; - uint32_t sjHandle0 = 0u; - uint32_t sjHandle1 = 0u; - (void)readSendWord(0u, maxChannels); - (void)readSendWord(2u, sjHandle0); - (void)readSendWord(3u, sjHandle1); - std::lock_guard lock(g_dtx_rpc_mutex); - const uint32_t handle = dtxAllocUrpcHandleLocked(); - DtxPs2RnaState state{}; - state.handle = handle; - state.maxChannels = maxChannels; - state.sjHandle0 = sjHandle0; - state.sjHandle1 = sjHandle1; - state.channelCount = maxChannels; - g_dtx_ps2rna_by_handle[handle] = state; - outWords[0] = handle ? handle : 1u; - outWordCount = 1u; - break; + (void)writeRpcU32(recvBuf, remoteHandle); + if (recvSize > sizeof(uint32_t)) + { + rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + } + static uint32_t dtxCreateLogCount = 0; + if (dtxCreateLogCount < 64u) + { + RUNTIME_LOG("[SifCallRpc:DTX_CREATE] dtxId=0x" << std::hex << dtxId + << " remote=0x" << remoteHandle + << " recvBuf=0x" << recvBuf + << " recvSize=0x" << recvSize + << std::dec << std::endl); + ++dtxCreateLogCount; + } + handled = true; + resultPtr = recvBuf; } - case 9u: // PS2RNA_DESTROY + else if (rpcNum == 3) { - uint32_t handle = 0u; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - g_dtx_ps2rna_by_handle.erase(handle); - outWords[0] = 1u; - outWordCount = 1u; - break; + uint32_t remoteHandle = 0; + if (sendBuf && sendSize >= sizeof(uint32_t) && readRpcU32(sendBuf, remoteHandle) && remoteHandle) + { + std::lock_guard lock(g_dtx_rpc_mutex); + for (auto it = g_dtx_remote_by_id.begin(); it != g_dtx_remote_by_id.end(); ++it) + { + if (it->second == remoteHandle) + { + g_dtx_transfer_by_id.erase(it->first); + g_dtx_remote_by_id.erase(it); + break; + } + } + } + if (recvBuf && recvSize > 0) + { + rpcZeroRdram(rdram, recvBuf, recvSize); + } + handled = true; + resultPtr = recvBuf; } - case 32u: // SJRMT_RBF_CREATE - case 33u: // SJRMT_MEM_CREATE - case 34u: // SJRMT_UNI_CREATE + else if (rpcNum >= 0x400 && rpcNum < 0x500) { - uint32_t arg0 = 0; - uint32_t arg1 = 0; - uint32_t arg2 = 0; - (void)readSendWord(0u, arg0); - (void)readSendWord(1u, arg1); - (void)readSendWord(2u, arg2); + dtxUrpcFallbackEmulated = true; + const uint32_t urpcCommand = rpcNum & 0xFFu; + uint32_t outWords[4] = {1u, 0u, 0u, 0u}; + uint32_t outWordCount = 1u; + + auto readSendWord = [&](uint32_t index, uint32_t &out) -> bool + { + const uint64_t byteOffset = static_cast(index) * sizeof(uint32_t); + if (!sendBuf || sendSize < (byteOffset + sizeof(uint32_t))) + { + return false; + } + return readRpcU32(sendBuf + static_cast(byteOffset), out); + }; + + switch (urpcCommand) + { + case 0u: // SJX_CREATE + { + uint32_t srcSjHandle = 0u; + uint32_t dstSjHandle = 0u; + uint32_t line = 0u; + uint32_t eeObjAddr = 0u; + (void)readSendWord(0u, srcSjHandle); + (void)readSendWord(1u, dstSjHandle); + (void)readSendWord(2u, line); + (void)readSendWord(3u, eeObjAddr); - uint32_t mode = 0; - uint32_t wkAddr = 0; - uint32_t wkSize = 0; - if (urpcCommand == 34u) + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxSjxState state{}; + state.handle = handle; + state.srcSjHandle = srcSjHandle; + state.dstSjHandle = dstSjHandle; + state.line = line; + state.eeObjAddr = eeObjAddr; + g_dtx_sjx_by_handle[handle] = state; + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; + } + case 1u: // SJX_DESTROY + { + uint32_t handle = 0u; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_sjx_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 2u: // SJX_RESET { - mode = arg0; - wkAddr = arg1; - wkSize = arg2; - dtxUrpcFallbackCreate34 = true; + uint32_t handle = 0u; + uint32_t xid = 0u; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, xid); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjx_by_handle.find(handle); + if (it != g_dtx_sjx_by_handle.end()) + { + it->second.xid = static_cast(xid & 0xFFFFu); + } + outWords[0] = 1u; + outWordCount = 1u; + break; } - else if (urpcCommand == 33u) + case 8u: // PS2RNA_CREATE { - wkAddr = arg0; - wkSize = arg1; + uint32_t maxChannels = 0u; + uint32_t sjHandle0 = 0u; + uint32_t sjHandle1 = 0u; + (void)readSendWord(0u, maxChannels); + (void)readSendWord(2u, sjHandle0); + (void)readSendWord(3u, sjHandle1); + + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxPs2RnaState state{}; + state.handle = handle; + state.maxChannels = maxChannels; + state.sjHandle0 = sjHandle0; + state.sjHandle1 = sjHandle1; + state.channelCount = maxChannels; + g_dtx_ps2rna_by_handle[handle] = state; + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; } - else + case 9u: // PS2RNA_DESTROY { - wkAddr = arg0; - wkSize = (arg1 != 0u) ? arg1 : arg2; + uint32_t handle = 0u; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_ps2rna_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; } + case 32u: // SJRMT_RBF_CREATE + case 33u: // SJRMT_MEM_CREATE + case 34u: // SJRMT_UNI_CREATE + { + uint32_t arg0 = 0; + uint32_t arg1 = 0; + uint32_t arg2 = 0; + (void)readSendWord(0u, arg0); + (void)readSendWord(1u, arg1); + (void)readSendWord(2u, arg2); + + uint32_t mode = 0; + uint32_t wkAddr = 0; + uint32_t wkSize = 0; + if (urpcCommand == 34u) + { + mode = arg0; + wkAddr = arg1; + wkSize = arg2; + dtxUrpcFallbackCreate34 = true; + } + else if (urpcCommand == 33u) + { + wkAddr = arg0; + wkSize = arg1; + } + else + { + wkAddr = arg0; + wkSize = (arg1 != 0u) ? arg1 : arg2; + } - wkSize = dtxNormalizeSjrmtCapacity(wkSize); + wkSize = dtxNormalizeSjrmtCapacity(wkSize); - std::lock_guard lock(g_dtx_rpc_mutex); - const uint32_t handle = dtxAllocUrpcHandleLocked(); - DtxSjrmtState state{}; - state.handle = handle; - state.mode = mode; - state.wkAddr = wkAddr; - state.wkSize = wkSize; - state.readPos = 0u; - state.writePos = 0u; - state.roomBytes = wkSize; - state.dataBytes = 0u; - state.uuid0 = 0x53524D54u; // "SRMT" - state.uuid1 = handle; - state.uuid2 = wkAddr; - state.uuid3 = wkSize; - g_dtx_sjrmt_by_handle[handle] = state; - - outWords[0] = handle ? handle : 1u; - outWordCount = 1u; - break; - } - case 35u: // SJRMT_DESTROY - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - g_dtx_sjrmt_by_handle.erase(handle); - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 36u: // SJRMT_GET_UUID - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxSjrmtState state{}; + state.handle = handle; + state.mode = mode; + state.wkAddr = wkAddr; + state.wkSize = wkSize; + state.readPos = 0u; + state.writePos = 0u; + state.roomBytes = wkSize; + state.dataBytes = 0u; + state.uuid0 = 0x53524D54u; // "SRMT" + state.uuid1 = handle; + state.uuid2 = wkAddr; + state.uuid3 = wkSize; + g_dtx_sjrmt_by_handle[handle] = state; + + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; + } + case 35u: // SJRMT_DESTROY + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_sjrmt_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 36u: // SJRMT_GET_UUID { - outWords[0] = it->second.uuid0; - outWords[1] = it->second.uuid1; - outWords[2] = it->second.uuid2; - outWords[3] = it->second.uuid3; + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + outWords[0] = it->second.uuid0; + outWords[1] = it->second.uuid1; + outWords[2] = it->second.uuid2; + outWords[3] = it->second.uuid3; + } + else + { + outWords[0] = 0u; + outWords[1] = 0u; + outWords[2] = 0u; + outWords[3] = 0u; + } + outWordCount = 4u; + break; } - else + case 37u: // SJRMT_RESET { - outWords[0] = 0u; - outWords[1] = 0u; - outWords[2] = 0u; - outWords[3] = 0u; + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + const uint32_t cap = (it->second.wkSize == 0u) ? 0x4000u : it->second.wkSize; + it->second.readPos = 0u; + it->second.writePos = 0u; + it->second.roomBytes = cap; + it->second.dataBytes = 0u; + } + outWords[0] = 1u; + outWordCount = 1u; + break; } - outWordCount = 4u; - break; - } - case 37u: // SJRMT_RESET - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) + case 38u: // SJRMT_GET_CHUNK { - const uint32_t cap = (it->second.wkSize == 0u) ? 0x4000u : it->second.wkSize; - it->second.readPos = 0u; - it->second.writePos = 0u; - it->second.roomBytes = cap; - it->second.dataBytes = 0u; + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t nbyte = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(2u, nbyte); + + uint32_t ptr = 0u; + uint32_t len = 0u; + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + + if (streamId == 0u) + { + len = std::min(nbyte, state.roomBytes); + ptr = state.wkAddr + (cap ? (state.writePos % cap) : 0u); + if (cap != 0u) + { + state.writePos = (state.writePos + len) % cap; + } + state.roomBytes -= len; + } + else if (streamId == 1u) + { + len = std::min(nbyte, state.dataBytes); + ptr = state.wkAddr + (cap ? (state.readPos % cap) : 0u); + if (cap != 0u) + { + state.readPos = (state.readPos + len) % cap; + } + state.dataBytes -= len; + } + } + + outWords[0] = ptr; + outWords[1] = len; + outWordCount = 2u; + break; } - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 38u: // SJRMT_GET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t nbyte = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(2u, nbyte); + case 39u: // SJRMT_UNGET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t len = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(3u, len); - uint32_t ptr = 0u; - uint32_t len = 0u; + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (streamId == 0u) + { + const uint32_t delta = (cap == 0u) ? 0u : (len % cap); + if (cap != 0u) + { + state.writePos = (state.writePos + cap - delta) % cap; + } + state.roomBytes = std::min(cap, state.roomBytes + len); + } + else if (streamId == 1u) + { + const uint32_t delta = (cap == 0u) ? 0u : (len % cap); + if (cap != 0u) + { + state.readPos = (state.readPos + cap - delta) % cap; + } + state.dataBytes = std::min(cap, state.dataBytes + len); + } + } - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 40u: // SJRMT_PUT_CHUNK { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t len = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(3u, len); - if (streamId == 0u) + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) { - len = std::min(nbyte, state.roomBytes); - ptr = state.wkAddr + (cap ? (state.writePos % cap) : 0u); - if (cap != 0u) + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (streamId == 0u) { - state.writePos = (state.writePos + len) % cap; + state.roomBytes = std::min(cap, state.roomBytes + len); } - state.roomBytes -= len; - } - else if (streamId == 1u) - { - len = std::min(nbyte, state.dataBytes); - ptr = state.wkAddr + (cap ? (state.readPos % cap) : 0u); - if (cap != 0u) + else if (streamId == 1u) { - state.readPos = (state.readPos + len) % cap; + state.dataBytes = std::min(cap, state.dataBytes + len); } - state.dataBytes -= len; } - } - outWords[0] = ptr; - outWords[1] = len; - outWordCount = 2u; - break; - } - case 39u: // SJRMT_UNGET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t len = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(3u, len); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 41u: // SJRMT_GET_NUM_DATA + { + uint32_t handle = 0; + uint32_t streamId = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + outWords[0] = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; + } + else + { + outWords[0] = 0u; + } + outWordCount = 1u; + break; + } + case 42u: // SJRMT_IS_GET_CHUNK { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (streamId == 0u) + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t nbyte = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(2u, nbyte); + + uint32_t available = 0u; + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) { - const uint32_t delta = (cap == 0u) ? 0u : (len % cap); - if (cap != 0u) - { - state.writePos = (state.writePos + cap - delta) % cap; - } - state.roomBytes = std::min(cap, state.roomBytes + len); + available = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; + } + outWords[0] = (available >= nbyte) ? 1u : 0u; + outWords[1] = available; + outWordCount = 2u; + break; + } + case 43u: // SJRMT_INIT + case 44u: // SJRMT_FINISH + { + outWords[0] = 1u; + outWordCount = 1u; + break; + } + default: + { + uint32_t urpcRet = 1u; + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf, urpcRet); } - else if (streamId == 1u) + if (urpcCommand == 0u) { - const uint32_t delta = (cap == 0u) ? 0u : (len % cap); - if (cap != 0u) - { - state.readPos = (state.readPos + cap - delta) % cap; - } - state.dataBytes = std::min(cap, state.dataBytes + len); + std::lock_guard lock(g_dtx_rpc_mutex); + urpcRet = dtxAllocUrpcHandleLocked(); + } + if (urpcRet == 0u) + { + urpcRet = 1u; } + outWords[0] = urpcRet; + outWordCount = 1u; + break; + } } - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 40u: // SJRMT_PUT_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t len = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(3u, len); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) + if (recvBuf && recvSize > 0u) { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (streamId == 0u) + const uint32_t recvWordCapacity = static_cast(recvSize / sizeof(uint32_t)); + const uint32_t wordsToWrite = std::min(outWordCount, recvWordCapacity); + for (uint32_t i = 0; i < wordsToWrite; ++i) + { + (void)writeRpcU32(recvBuf + (i * sizeof(uint32_t)), outWords[i]); + } + + // SJRMT_IsGetChunk callers read rbuf[1] even when nout==1. + if (urpcCommand == 42u && outWordCount > 1u) { - state.roomBytes = std::min(cap, state.roomBytes + len); + (void)writeRpcU32(recvBuf + sizeof(uint32_t), outWords[1]); } - else if (streamId == 1u) + + if (recvSize > (wordsToWrite * sizeof(uint32_t))) { - state.dataBytes = std::min(cap, state.dataBytes + len); + rpcZeroRdram(rdram, recvBuf + (wordsToWrite * sizeof(uint32_t)), + recvSize - (wordsToWrite * sizeof(uint32_t))); } } - outWords[0] = 1u; - outWordCount = 1u; - break; + handled = true; + resultPtr = recvBuf; } - case 41u: // SJRMT_GET_NUM_DATA - { - uint32_t handle = 0; - uint32_t streamId = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); + } - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - outWords[0] = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; - } - else - { - outWords[0] = 0u; - } - outWordCount = 1u; - break; + if (recvBuf && recvSize > 0) + { + if (handled && resultPtr && resultPtr != recvBuf) + { + rpcCopyToRdram(rdram, recvBuf, resultPtr, recvSize); } - case 42u: // SJRMT_IS_GET_CHUNK + else if (!handled && sendBuf && sendSize > 0 && sendBuf != recvBuf) { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t nbyte = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(2u, nbyte); - - uint32_t available = 0u; - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - available = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; - } - outWords[0] = (available >= nbyte) ? 1u : 0u; - outWords[1] = available; - outWordCount = 2u; - break; + size_t copySize = (sendSize < recvSize) ? sendSize : recvSize; + rpcCopyToRdram(rdram, recvBuf, sendBuf, copySize); } - case 43u: // SJRMT_INIT - case 44u: // SJRMT_FINISH + else if (!handled) { - outWords[0] = 1u; - outWordCount = 1u; - break; + rpcZeroRdram(rdram, recvBuf, recvSize); } - default: + } + + if (isDtxUrpc) + { + static int dtxUrpcLogCount = 0; + if (dtxUrpcLogCount < 64) { - uint32_t urpcRet = 1u; - if (sendBuf && sendSize >= sizeof(uint32_t)) + uint32_t dtxUrpcRecv0 = 0; + if (recvBuf && recvSize >= sizeof(uint32_t)) { - (void)readRpcU32(sendBuf, urpcRet); + (void)readRpcU32(recvBuf, dtxUrpcRecv0); } - if (urpcCommand == 0u) + RUNTIME_LOG("[SifCallRpc:DTX] rpcNum=0x" << std::hex << rpcNum + << " cmd=0x" << dtxUrpcCommand + << " name=" << dtxUrpcCommandName(dtxUrpcCommand) + << " fn=0x" << dtxUrpcFn + << " obj=0x" << dtxUrpcObj + << " send0=0x" << dtxUrpcSend0 + << " recv0=0x" << dtxUrpcRecv0 + << " resultPtr=0x" << resultPtr + << " handled=" << std::dec << (handled ? 1 : 0) + << " dispatch=" << (dtxUrpcDispatchAttempted ? 1 : 0) + << " emu=" << (dtxUrpcFallbackEmulated ? 1 : 0) + << " emu34=" << (dtxUrpcFallbackCreate34 ? 1 : 0) + << std::endl); + + if (dtxUrpcCommand >= 37u && dtxUrpcCommand <= 42u) { std::lock_guard lock(g_dtx_rpc_mutex); - urpcRet = dtxAllocUrpcHandleLocked(); + auto it = g_dtx_sjrmt_by_handle.find(dtxUrpcSend0); + if (it != g_dtx_sjrmt_by_handle.end()) + { + const DtxSjrmtState &state = it->second; + RUNTIME_LOG("[SifCallRpc:DTX_SJRMT] handle=0x" << std::hex << dtxUrpcSend0 + << " cmd=" << dtxUrpcCommandName(dtxUrpcCommand) + << " wk=0x" << state.wkAddr + << " size=0x" << state.wkSize + << " read=0x" << state.readPos + << " write=0x" << state.writePos + << std::dec + << " room=" << state.roomBytes + << " data=" << state.dataBytes); + if ((dtxUrpcCommand == 38u || dtxUrpcCommand == 42u) && sendBuf && sendSize >= 12u) + { + uint32_t streamId = 0u; + uint32_t nbyte = 0u; + (void)readRpcU32(sendBuf + 4u, streamId); + (void)readRpcU32(sendBuf + 8u, nbyte); + RUNTIME_LOG(" stream=" << dtxStreamName(streamId) + << " req=" << nbyte); + } + RUNTIME_LOG(std::endl); + } } - if (urpcRet == 0u) + ++dtxUrpcLogCount; + } + } + + if (endFunc) + { + bool callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr); + + if (!callbackInvoked && endFunc >= 0x10000u) + { + const uint32_t normalizedEndFunc = endFunc - 0x10000u; + if (runtime->hasFunction(normalizedEndFunc)) { - urpcRet = 1u; + callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, normalizedEndFunc, endParam, 0, 0, 0, nullptr); } - outWords[0] = urpcRet; - outWordCount = 1u; - break; } + + PS2SoundDriverCompatLayout soundCompat{}; + { + std::lock_guard lock(g_rpc_mutex); + soundCompat = g_soundDriverCompatLayout; } - if (recvBuf && recvSize > 0u) + if (soundCompat.matchesCompletionCallback(endFunc)) { - const uint32_t recvWordCapacity = static_cast(recvSize / sizeof(uint32_t)); - const uint32_t wordsToWrite = std::min(outWordCount, recvWordCapacity); - for (uint32_t i = 0; i < wordsToWrite; ++i) + uint32_t semaId = static_cast(client->hdr.sema_id); + if (semaId == 0xFFFFFFFFu || semaId == 0u) + { + semaId = endParam; + } + (void)signalRpcCompletionSema(semaId); + if (rdram && soundCompat.busyFlagAddr != 0u && soundCompat.matchesClearBusyCallback(endFunc)) { - (void)writeRpcU32(recvBuf + (i * sizeof(uint32_t)), outWords[i]); + if (uint32_t *busy = reinterpret_cast(getMemPtr(rdram, soundCompat.busyFlagAddr))) + { + *busy = 0u; + } } + } - // SJRMT_IsGetChunk callers read rbuf[1] even when nout==1. - if (urpcCommand == 42u && outWordCount > 1u) + if (!callbackInvoked) + { + uint32_t semaId = static_cast(client->hdr.sema_id); + if (semaId == 0xFFFFFFFFu || semaId == 0u) { - (void)writeRpcU32(recvBuf + sizeof(uint32_t), outWords[1]); + semaId = endParam; } + const bool fallbackSignaledSema = signalRpcCompletionSema(semaId); - if (recvSize > (wordsToWrite * sizeof(uint32_t))) + static uint32_t unresolvedEndFuncWarnCount = 0; + if (unresolvedEndFuncWarnCount < 32u) { - rpcZeroRdram(rdram, recvBuf + (wordsToWrite * sizeof(uint32_t)), - recvSize - (wordsToWrite * sizeof(uint32_t))); + std::cerr << "[SifCallRpc] unresolved end callback endFunc=0x" << std::hex << endFunc + << " semaId=0x" << semaId + << " fallbackSignal=" << std::dec << (fallbackSignaledSema ? 1 : 0) + << std::endl; + ++unresolvedEndFuncWarnCount; } } - - handled = true; - resultPtr = recvBuf; } - } - if (recvBuf && recvSize > 0) - { - if (handled && resultPtr && resultPtr != recvBuf) + static int logCount = 0; + if (logCount < 10) { - rpcCopyToRdram(rdram, recvBuf, resultPtr, recvSize); + RUNTIME_LOG("[SifCallRpc] client=0x" << std::hex << clientPtr + << " sid=0x" << sid + << " rpcNum=0x" << rpcNum + << " mode=0x" << mode + << " sendBuf=0x" << sendBuf + << " recvBuf=0x" << recvBuf + << " recvSize=0x" << recvSize + << " size=" << std::dec << sendSize << std::endl); + ++logCount; } - else if (!handled && sendBuf && sendSize > 0 && sendBuf != recvBuf) - { - size_t copySize = (sendSize < recvSize) ? sendSize : recvSize; - rpcCopyToRdram(rdram, recvBuf, sendBuf, copySize); - } - else if (!handled) + { - rpcZeroRdram(rdram, recvBuf, recvSize); + std::lock_guard lock(g_rpc_mutex); + g_rpc_clients[clientPtr].busy = false; } - } - if (isDtxUrpc) - { - static int dtxUrpcLogCount = 0; - if (dtxUrpcLogCount < 64) - { - uint32_t dtxUrpcRecv0 = 0; - if (recvBuf && recvSize >= sizeof(uint32_t)) - { - (void)readRpcU32(recvBuf, dtxUrpcRecv0); - } - RUNTIME_LOG("[SifCallRpc:DTX] rpcNum=0x" << std::hex << rpcNum - << " cmd=0x" << dtxUrpcCommand - << " name=" << dtxUrpcCommandName(dtxUrpcCommand) - << " fn=0x" << dtxUrpcFn - << " obj=0x" << dtxUrpcObj - << " send0=0x" << dtxUrpcSend0 - << " recv0=0x" << dtxUrpcRecv0 - << " resultPtr=0x" << resultPtr - << " handled=" << std::dec << (handled ? 1 : 0) - << " dispatch=" << (dtxUrpcDispatchAttempted ? 1 : 0) - << " emu=" << (dtxUrpcFallbackEmulated ? 1 : 0) - << " emu34=" << (dtxUrpcFallbackCreate34 ? 1 : 0) - << std::endl); - - if (dtxUrpcCommand >= 37u && dtxUrpcCommand <= 42u) - { - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(dtxUrpcSend0); - if (it != g_dtx_sjrmt_by_handle.end()) - { - const DtxSjrmtState &state = it->second; - RUNTIME_LOG("[SifCallRpc:DTX_SJRMT] handle=0x" << std::hex << dtxUrpcSend0 - << " cmd=" << dtxUrpcCommandName(dtxUrpcCommand) - << " wk=0x" << state.wkAddr - << " size=0x" << state.wkSize - << " read=0x" << state.readPos - << " write=0x" << state.writePos - << std::dec - << " room=" << state.roomBytes - << " data=" << state.dataBytes); - if ((dtxUrpcCommand == 38u || dtxUrpcCommand == 42u) && sendBuf && sendSize >= 12u) - { - uint32_t streamId = 0u; - uint32_t nbyte = 0u; - (void)readRpcU32(sendBuf + 4u, streamId); - (void)readRpcU32(sendBuf + 8u, nbyte); - RUNTIME_LOG(" stream=" << dtxStreamName(streamId) - << " req=" << nbyte); - } - RUNTIME_LOG(std::endl); - } - } - ++dtxUrpcLogCount; - } + setReturnS32(ctx, 0); } - if (endFunc) + void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - bool callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr); + uint32_t sdPtr = getRegU32(ctx, 4); + uint32_t sid = getRegU32(ctx, 5); + uint32_t func = getRegU32(ctx, 6); + uint32_t buf = getRegU32(ctx, 7); + // stack args: cfunc, cbuf, qd... + uint32_t sp = getRegU32(ctx, 29); + uint32_t cfunc = 0; + uint32_t cbuf = 0; + uint32_t qd = 0; + readStackU32(rdram, sp, 0x10, cfunc); + readStackU32(rdram, sp, 0x14, cbuf); + readStackU32(rdram, sp, 0x18, qd); - if (!callbackInvoked && endFunc >= 0x10000u) + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + if (!sd) { - const uint32_t normalizedEndFunc = endFunc - 0x10000u; - if (runtime->hasFunction(normalizedEndFunc)) - { - callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, normalizedEndFunc, endParam, 0, 0, 0, nullptr); - } + setReturnS32(ctx, -1); + return; } - PS2SoundDriverCompatLayout soundCompat{}; + sd->sid = static_cast(sid); + sd->func = func; + sd->buf = buf; + sd->size = 0; + sd->cfunc = cfunc; + sd->cbuf = cbuf; + sd->size2 = 0; + sd->client = 0; + sd->pkt_addr = 0; + sd->rpc_number = 0; + sd->recvbuf = 0; + sd->rsize = 0; + sd->rmode = 0; + sd->rid = 0; + sd->base = qd; + sd->link = 0; + sd->next = 0; + { std::lock_guard lock(g_rpc_mutex); - soundCompat = g_soundDriverCompatLayout; - } - if (soundCompat.matchesCompletionCallback(endFunc)) - { - uint32_t semaId = static_cast(client->hdr.sema_id); - if (semaId == 0xFFFFFFFFu || semaId == 0u) - { - semaId = endParam; - } - (void)signalRpcCompletionSema(semaId); - if (rdram && soundCompat.busyFlagAddr != 0u && soundCompat.matchesClearBusyCallback(endFunc)) + if (qd) { - if (uint32_t *busy = reinterpret_cast(getMemPtr(rdram, soundCompat.busyFlagAddr))) + t_SifRpcDataQueue *queue = reinterpret_cast(getMemPtr(rdram, qd)); + if (queue) { - *busy = 0u; + if (!queue->link) + { + queue->link = sdPtr; + } + else + { + uint32_t curPtr = queue->link; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (!cur->link) + { + cur->link = sdPtr; + break; + } + if (cur->link == sdPtr) + break; + curPtr = cur->link; + } + } } } - } - - if (!callbackInvoked) - { - uint32_t semaId = static_cast(client->hdr.sema_id); - if (semaId == 0xFFFFFFFFu || semaId == 0u) - { - semaId = endParam; - } - const bool fallbackSignaledSema = signalRpcCompletionSema(semaId); - static uint32_t unresolvedEndFuncWarnCount = 0; - if (unresolvedEndFuncWarnCount < 32u) + g_rpc_servers[sid] = {sid, sdPtr}; + for (auto &entry : g_rpc_clients) { - std::cerr << "[SifCallRpc] unresolved end callback endFunc=0x" << std::hex << endFunc - << " semaId=0x" << semaId - << " fallbackSignal=" << std::dec << (fallbackSignaledSema ? 1 : 0) - << std::endl; - ++unresolvedEndFuncWarnCount; + if (entry.second.sid == sid) + { + t_SifRpcClientData *cd = reinterpret_cast(getMemPtr(rdram, entry.first)); + if (cd) + { + cd->server = sdPtr; + cd->buf = sd->buf; + cd->cbuf = sd->cbuf; + } + } } } - } - static int logCount = 0; - if (logCount < 10) - { - RUNTIME_LOG("[SifCallRpc] client=0x" << std::hex << clientPtr - << " sid=0x" << sid - << " rpcNum=0x" << rpcNum - << " mode=0x" << mode - << " sendBuf=0x" << sendBuf - << " recvBuf=0x" << recvBuf - << " recvSize=0x" << recvSize - << " size=" << std::dec << sendSize << std::endl); - ++logCount; + RUNTIME_LOG("[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec); + setReturnS32(ctx, 0); } + void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + uint32_t clientPtr = getRegU32(ctx, 4); std::lock_guard lock(g_rpc_mutex); - g_rpc_clients[clientPtr].busy = false; + auto it = g_rpc_clients.find(clientPtr); + if (it == g_rpc_clients.end()) + { + setReturnS32(ctx, 0); + return; + } + setReturnS32(ctx, it->second.busy ? 1 : 0); } - setReturnS32(ctx, 0); -} - -void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t sdPtr = getRegU32(ctx, 4); - uint32_t sid = getRegU32(ctx, 5); - uint32_t func = getRegU32(ctx, 6); - uint32_t buf = getRegU32(ctx, 7); - // stack args: cfunc, cbuf, qd... - uint32_t sp = getRegU32(ctx, 29); - uint32_t cfunc = 0; - uint32_t cbuf = 0; - uint32_t qd = 0; - readStackU32(rdram, sp, 0x10, cfunc); - readStackU32(rdram, sp, 0x14, cbuf); - readStackU32(rdram, sp, 0x18, qd); - - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - if (!sd) + void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; - } + uint32_t qdPtr = getRegU32(ctx, 4); + int threadId = static_cast(getRegU32(ctx, 5)); - sd->sid = static_cast(sid); - sd->func = func; - sd->buf = buf; - sd->size = 0; - sd->cfunc = cfunc; - sd->cbuf = cbuf; - sd->size2 = 0; - sd->client = 0; - sd->pkt_addr = 0; - sd->rpc_number = 0; - sd->recvbuf = 0; - sd->rsize = 0; - sd->rmode = 0; - sd->rid = 0; - sd->base = qd; - sd->link = 0; - sd->next = 0; + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + if (!qd) + { + setReturnS32(ctx, -1); + return; + } - { - std::lock_guard lock(g_rpc_mutex); + qd->thread_id = threadId; + qd->active = 0; + qd->link = 0; + qd->start = 0; + qd->end = 0; + qd->next = 0; - if (qd) { - t_SifRpcDataQueue *queue = reinterpret_cast(getMemPtr(rdram, qd)); - if (queue) + std::lock_guard lock(g_rpc_mutex); + if (!g_rpc_active_queue) { - if (!queue->link) - { - queue->link = sdPtr; - } - else - { - uint32_t curPtr = queue->link; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (!cur->link) - { - cur->link = sdPtr; - break; - } - if (cur->link == sdPtr) - break; - curPtr = cur->link; - } - } + g_rpc_active_queue = qdPtr; } - } - - g_rpc_servers[sid] = {sid, sdPtr}; - for (auto &entry : g_rpc_clients) - { - if (entry.second.sid == sid) + else { - t_SifRpcClientData *cd = reinterpret_cast(getMemPtr(rdram, entry.first)); - if (cd) + uint32_t curPtr = g_rpc_active_queue; + for (int guard = 0; guard < 1024 && curPtr; ++guard) { - cd->server = sdPtr; - cd->buf = sd->buf; - cd->cbuf = sd->cbuf; + if (curPtr == qdPtr) + break; + t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (!cur->next) + { + cur->next = qdPtr; + break; + } + curPtr = cur->next; } } } - } - - RUNTIME_LOG("[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec); - setReturnS32(ctx, 0); -} -void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t clientPtr = getRegU32(ctx, 4); - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it == g_rpc_clients.end()) - { setReturnS32(ctx, 0); - return; } - setReturnS32(ctx, it->second.busy ? 1 : 0); -} - -void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t qdPtr = getRegU32(ctx, 4); - int threadId = static_cast(getRegU32(ctx, 5)); - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - if (!qd) + void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; - } - - qd->thread_id = threadId; - qd->active = 0; - qd->link = 0; - qd->start = 0; - qd->end = 0; - qd->next = 0; + uint32_t qdPtr = getRegU32(ctx, 4); + if (!qdPtr) + { + setReturnU32(ctx, 0); + return; + } - { std::lock_guard lock(g_rpc_mutex); if (!g_rpc_active_queue) { - g_rpc_active_queue = qdPtr; + setReturnU32(ctx, 0); + return; } - else + + if (g_rpc_active_queue == qdPtr) + { + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + g_rpc_active_queue = qd ? qd->next : 0; + setReturnU32(ctx, qdPtr); + return; + } + + uint32_t curPtr = g_rpc_active_queue; + for (int guard = 0; guard < 1024 && curPtr; ++guard) { - uint32_t curPtr = g_rpc_active_queue; - for (int guard = 0; guard < 1024 && curPtr; ++guard) + t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (cur->next == qdPtr) { - if (curPtr == qdPtr) - break; - t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (!cur->next) - { - cur->next = qdPtr; - break; - } - curPtr = cur->next; + t_SifRpcDataQueue *rem = reinterpret_cast(getMemPtr(rdram, qdPtr)); + cur->next = rem ? rem->next : 0; + setReturnU32(ctx, qdPtr); + return; } + curPtr = cur->next; } - } - - setReturnS32(ctx, 0); -} -void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t qdPtr = getRegU32(ctx, 4); - if (!qdPtr) - { setReturnU32(ctx, 0); - return; } - std::lock_guard lock(g_rpc_mutex); - if (!g_rpc_active_queue) + void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnU32(ctx, 0); - return; - } + uint32_t sdPtr = getRegU32(ctx, 4); + uint32_t qdPtr = getRegU32(ctx, 5); - if (g_rpc_active_queue == qdPtr) - { t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - g_rpc_active_queue = qd ? qd->next : 0; - setReturnU32(ctx, qdPtr); - return; - } - - uint32_t curPtr = g_rpc_active_queue; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (cur->next == qdPtr) + if (!qd || !sdPtr) { - t_SifRpcDataQueue *rem = reinterpret_cast(getMemPtr(rdram, qdPtr)); - cur->next = rem ? rem->next : 0; - setReturnU32(ctx, qdPtr); + setReturnU32(ctx, 0); return; } - curPtr = cur->next; - } - - setReturnU32(ctx, 0); -} -void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t sdPtr = getRegU32(ctx, 4); - uint32_t qdPtr = getRegU32(ctx, 5); - - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - if (!qd || !sdPtr) - { - setReturnU32(ctx, 0); - return; - } - - std::lock_guard lock(g_rpc_mutex); - - if (qd->link == sdPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - qd->link = sd ? sd->link : 0; - if (sd) - sd->link = 0; - setReturnU32(ctx, sdPtr); - return; - } + std::lock_guard lock(g_rpc_mutex); - uint32_t curPtr = qd->link; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (cur->link == sdPtr) + if (qd->link == sdPtr) { t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - cur->link = sd ? sd->link : 0; + qd->link = sd ? sd->link : 0; if (sd) sd->link = 0; setReturnU32(ctx, sdPtr); return; } - curPtr = cur->link; - } - setReturnU32(ctx, 0); -} + uint32_t curPtr = qd->link; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (cur->link == sdPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + cur->link = sd ? sd->link : 0; + if (sd) + sd->link = 0; + setReturnU32(ctx, sdPtr); + return; + } + curPtr = cur->link; + } -void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - SifCallRpc(rdram, ctx, runtime); -} + setReturnU32(ctx, 0); + } -void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t cid = getRegU32(ctx, 4); - uint32_t packetAddr = getRegU32(ctx, 5); - uint32_t packetSize = getRegU32(ctx, 6); - uint32_t srcExtra = getRegU32(ctx, 7); - - uint32_t sp = getRegU32(ctx, 29); - uint32_t destExtra = 0; - uint32_t sizeExtra = 0; - readStackU32(rdram, sp, 0x10, destExtra); - readStackU32(rdram, sp, 0x14, sizeExtra); - - if (sizeExtra > 0 && srcExtra && destExtra) + void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra); + SifCallRpc(rdram, ctx, runtime); } - static int logCount = 0; - if (logCount < 5) + void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("[sceSifSendCmd] cid=0x" << std::hex << cid - << " packet=0x" << packetAddr - << " psize=0x" << packetSize - << " extra=0x" << destExtra << std::dec << std::endl); - ++logCount; - } + uint32_t cid = getRegU32(ctx, 4); + uint32_t packetAddr = getRegU32(ctx, 5); + uint32_t packetSize = getRegU32(ctx, 6); + uint32_t srcExtra = getRegU32(ctx, 7); - // Return non-zero on success. - setReturnS32(ctx, 1); -} + uint32_t sp = getRegU32(ctx, 29); + uint32_t destExtra = 0; + uint32_t sizeExtra = 0; + readStackU32(rdram, sp, 0x10, destExtra); + readStackU32(rdram, sp, 0x14, sizeExtra); -void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t queuePtr = getRegU32(ctx, 4); - setReturnS32(ctx, static_cast(queuePtr)); -} + if (sizeExtra > 0 && srcExtra && destExtra) + { + rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra); + } + + static int logCount = 0; + if (logCount < 5) + { + RUNTIME_LOG("[sceSifSendCmd] cid=0x" << std::hex << cid + << " packet=0x" << packetAddr + << " psize=0x" << packetSize + << " extra=0x" << destExtra << std::dec << std::endl); + ++logCount; + } + + // Return non-zero on success. + setReturnS32(ctx, 1); + } + + void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t queuePtr = getRegU32(ctx, 4); + setReturnS32(ctx, static_cast(queuePtr)); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp index 5c1ebf1d..58b2414a 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Sync.cpp @@ -3,675 +3,758 @@ namespace ps2_syscalls { -static bool looksLikeGuestPointerOrNull(uint32_t value) -{ - if (value == 0u) - { - return true; - } - const uint32_t normalized = value & 0x1FFFFFFFu; - return normalized < PS2_RAM_SIZE; -} - -static bool readGuestU32Safe(const uint8_t *rdram, uint32_t addr, uint32_t &out) -{ - const uint8_t *b0 = getConstMemPtr(rdram, addr + 0u); - const uint8_t *b1 = getConstMemPtr(rdram, addr + 1u); - const uint8_t *b2 = getConstMemPtr(rdram, addr + 2u); - const uint8_t *b3 = getConstMemPtr(rdram, addr + 3u); - if (!b0 || !b1 || !b2 || !b3) - { - out = 0u; - return false; - } - - out = static_cast(*b0) | - (static_cast(*b1) << 8) | - (static_cast(*b2) << 16) | - (static_cast(*b3) << 24); - return true; -} - -struct DecodedSemaParams -{ - int init = 0; - int max = 1; - uint32_t attr = 0; - uint32_t option = 0; -}; - -static DecodedSemaParams decodeCreateSemaParams(const uint32_t *param, uint32_t availableWords) -{ - DecodedSemaParams out{}; - if (!param || availableWords == 0u) + static bool looksLikeGuestPointerOrNull(uint32_t value) { - return out; - } - - // EE layout (kernel.h): - // [0]=count [1]=max_count [2]=init_count [3]=wait_threads [4]=attr [5]=option - const bool hasEeLayout = availableWords >= 3u; - const int eeMax = hasEeLayout ? static_cast(param[1]) : 1; - const int eeInit = hasEeLayout ? static_cast(param[2]) : 0; - const uint32_t eeAttr = (availableWords >= 5u) ? param[4] : 0u; - const uint32_t eeOption = (availableWords >= 6u) ? param[5] : 0u; - - // Legacy layout (IOP-style): - // [0]=attr [1]=option [2]=init [3]=max - const bool hasLegacyLayout = availableWords >= 4u; - const int legacyMax = hasLegacyLayout ? static_cast(param[3]) : 1; - const int legacyInit = hasLegacyLayout ? static_cast(param[2]) : 0; - const uint32_t legacyAttr = hasLegacyLayout ? param[0] : 0u; - const uint32_t legacyOption = hasLegacyLayout ? param[1] : 0u; - - auto countLooksPlausible = [](int value) -> bool - { - return value > 0 && value <= 0x10000; - }; - - bool useLegacyLayout = hasLegacyLayout && !hasEeLayout; - if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && !countLooksPlausible(eeMax)) - { - useLegacyLayout = true; - } - else if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && countLooksPlausible(eeMax)) - { - // If both max values look valid, prefer the layout whose option field - // looks like a pointer/NULL payload. - const bool eeOptionLooksValid = looksLikeGuestPointerOrNull(eeOption); - const bool legacyOptionLooksValid = looksLikeGuestPointerOrNull(legacyOption); - if (!eeOptionLooksValid && legacyOptionLooksValid) + if (value == 0u) { - useLegacyLayout = true; + return true; } + const uint32_t normalized = value & 0x1FFFFFFFu; + return normalized < PS2_RAM_SIZE; } - if (useLegacyLayout && hasLegacyLayout) + static bool readGuestU32Safe(const uint8_t *rdram, uint32_t addr, uint32_t &out) { - out.max = legacyMax; - out.init = legacyInit; - out.attr = legacyAttr; - out.option = legacyOption; - } - else - { - if (!hasEeLayout) + const uint8_t *b0 = getConstMemPtr(rdram, addr + 0u); + const uint8_t *b1 = getConstMemPtr(rdram, addr + 1u); + const uint8_t *b2 = getConstMemPtr(rdram, addr + 2u); + const uint8_t *b3 = getConstMemPtr(rdram, addr + 3u); + if (!b0 || !b1 || !b2 || !b3) { - return out; + out = 0u; + return false; } - out.max = eeMax; - out.init = eeInit; - out.attr = eeAttr; - out.option = eeOption; - } - return out; -} + out = static_cast(*b0) | + (static_cast(*b1) << 8) | + (static_cast(*b2) << 16) | + (static_cast(*b3) << 24); + return true; + } -void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - if (paramAddr == 0u) + struct DecodedSemaParams { - setReturnS32(ctx, KE_ERROR); - return; - } + int init = 0; + int max = 1; + uint32_t attr = 0; + uint32_t option = 0; + }; - uint32_t rawParams[6] = {}; - uint32_t availableWords = 0u; - for (uint32_t i = 0; i < 6u; ++i) + static DecodedSemaParams decodeCreateSemaParams(const uint32_t *param, uint32_t availableWords) { - if (!readGuestU32Safe(rdram, paramAddr + (i * 4u), rawParams[i])) + DecodedSemaParams out{}; + if (!param || availableWords == 0u) { - break; + return out; } - availableWords = i + 1u; - } - - if (availableWords < 3u) - { - setReturnS32(ctx, KE_ERROR); - return; - } - - const DecodedSemaParams decoded = decodeCreateSemaParams(rawParams, availableWords); - int init = decoded.init; - int max = decoded.max; - uint32_t attr = decoded.attr; - uint32_t option = decoded.option; - if (max <= 0) - { - max = 1; - } - if (init < 0) - { - init = 0; - } - if (init > max) - { - init = max; - } - - int id = 0; - auto info = std::make_shared(); - info->count = init; - info->maxCount = max; - info->initCount = init; - info->attr = attr; - info->option = option; + // EE layout (kernel.h): + // [0]=count [1]=max_count [2]=init_count [3]=wait_threads [4]=attr [5]=option + const bool hasEeLayout = availableWords >= 3u; + const int eeMax = hasEeLayout ? static_cast(param[1]) : 1; + const int eeInit = hasEeLayout ? static_cast(param[2]) : 0; + const uint32_t eeAttr = (availableWords >= 5u) ? param[4] : 0u; + const uint32_t eeOption = (availableWords >= 6u) ? param[5] : 0u; + + // Legacy layout (IOP-style): + // [0]=attr [1]=option [2]=init [3]=max + const bool hasLegacyLayout = availableWords >= 4u; + const int legacyMax = hasLegacyLayout ? static_cast(param[3]) : 1; + const int legacyInit = hasLegacyLayout ? static_cast(param[2]) : 0; + const uint32_t legacyAttr = hasLegacyLayout ? param[0] : 0u; + const uint32_t legacyOption = hasLegacyLayout ? param[1] : 0u; + + auto countLooksPlausible = [](int value) -> bool + { + return value > 0 && value <= 0x10000; + }; - { - std::lock_guard lock(g_sema_map_mutex); - for (int attempts = 0; attempts < 0x7FFF; ++attempts) + bool useLegacyLayout = hasLegacyLayout && !hasEeLayout; + if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && !countLooksPlausible(eeMax)) { - if (g_nextSemaId <= 0) + useLegacyLayout = true; + } + else if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && countLooksPlausible(eeMax)) + { + // If both max values look valid, prefer the layout whose option field + // looks like a pointer/NULL payload. + const bool eeOptionLooksValid = looksLikeGuestPointerOrNull(eeOption); + const bool legacyOptionLooksValid = looksLikeGuestPointerOrNull(legacyOption); + if (!eeOptionLooksValid && legacyOptionLooksValid) { - g_nextSemaId = 1; + useLegacyLayout = true; } + } - const int candidate = g_nextSemaId++; - if (candidate <= 0) + if (useLegacyLayout && hasLegacyLayout) + { + out.max = legacyMax; + out.init = legacyInit; + out.attr = legacyAttr; + out.option = legacyOption; + } + else + { + if (!hasEeLayout) { - continue; + return out; } + out.max = eeMax; + out.init = eeInit; + out.attr = eeAttr; + out.option = eeOption; + } + + return out; + } + + void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 + if (paramAddr == 0u) + { + setReturnS32(ctx, KE_ERROR); + return; + } - if (g_semas.find(candidate) == g_semas.end()) + uint32_t rawParams[6] = {}; + uint32_t availableWords = 0u; + for (uint32_t i = 0; i < 6u; ++i) + { + if (!readGuestU32Safe(rdram, paramAddr + (i * 4u), rawParams[i])) { - id = candidate; break; } + availableWords = i + 1u; } - if (id <= 0) + if (availableWords < 3u) { setReturnS32(ctx, KE_ERROR); return; } - g_semas.emplace(id, info); - } - RUNTIME_LOG("[CreateSema] id=" << id << " init=" << init << " max=" << max); - setReturnS32(ctx, id); -} + const DecodedSemaParams decoded = decodeCreateSemaParams(rawParams, availableWords); + int init = decoded.init; + int max = decoded.max; + uint32_t attr = decoded.attr; + uint32_t option = decoded.option; -void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int sid = static_cast(getRegU32(ctx, 4)); - std::shared_ptr sema; - - { - std::lock_guard lock(g_sema_map_mutex); - auto it = g_semas.find(sid); - if (it == g_semas.end()) + if (max <= 0) { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; + max = 1; + } + if (init < 0) + { + init = 0; + } + if (init > max) + { + init = max; } - sema = it->second; - g_semas.erase(it); - } - { - std::lock_guard lock(sema->m); - sema->deleted = true; - } - sema->cv.notify_all(); + int id = 0; + auto info = std::make_shared(); + info->count = init; + info->maxCount = max; + info->initCount = init; + info->attr = attr; + info->option = option; - setReturnS32(ctx, KE_OK); -} + { + std::lock_guard lock(g_sema_map_mutex); + for (int attempts = 0; attempts < 0x7FFF; ++attempts) + { + if (g_nextSemaId <= 0) + { + g_nextSemaId = 1; + } + + const int candidate = g_nextSemaId++; + if (candidate <= 0) + { + continue; + } + + if (g_semas.find(candidate) == g_semas.end()) + { + id = candidate; + break; + } + } -void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - DeleteSema(rdram, ctx, runtime); -} + if (id <= 0) + { + setReturnS32(ctx, KE_ERROR); + return; + } -void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int sid = static_cast(getRegU32(ctx, 4)); - auto sema = lookupSemaInfo(sid); - if (!sema) - { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; + g_semas.emplace(id, info); + } + RUNTIME_LOG("[CreateSema] id=" << id << " init=" << init << " max=" << max); + setReturnS32(ctx, id); } - int ret = KE_OK; - int beforeCount = 0; - int afterCount = 0; + void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(sema->m); - beforeCount = sema->count; - if (sema->count >= sema->maxCount) + int sid = static_cast(getRegU32(ctx, 4)); + std::shared_ptr sema; + { - ret = KE_SEMA_OVF; + std::lock_guard lock(g_sema_map_mutex); + auto it = g_semas.find(sid); + if (it == g_semas.end()) + { + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; + } + sema = it->second; + g_semas.erase(it); } - else + { - sema->count++; - sema->cv.notify_one(); + std::lock_guard lock(sema->m); + sema->deleted = true; } - afterCount = sema->count; - } + sema->cv.notify_all(); - static std::atomic s_signalSemaLogs{0}; - const uint32_t sigLog = s_signalSemaLogs.fetch_add(1, std::memory_order_relaxed); - if (sigLog < 256u) - { - RUNTIME_LOG("[SignalSema] tid=" << g_currentThreadId - << " sid=" << sid - << " count=" << beforeCount << "->" << afterCount - << " ret=" << ret - << std::endl); + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, ret); -} - -void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - SignalSema(rdram, ctx, runtime); -} - -void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int sid = static_cast(getRegU32(ctx, 4)); - auto sema = lookupSemaInfo(sid); - if (!sema) + void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; + DeleteSema(rdram, ctx, runtime); } - auto info = ensureCurrentThreadInfo(ctx); - throwIfTerminated(info); - std::unique_lock lock(sema->m); - int ret = 0; - - if (sema->count == 0) + void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - static std::atomic s_waitSemaBlockLogs{0}; - const uint32_t blockLog = s_waitSemaBlockLogs.fetch_add(1, std::memory_order_relaxed); - if (blockLog < 256u) + int sid = static_cast(getRegU32(ctx, 4)); + auto sema = lookupSemaInfo(sid); + if (!sema) { - RUNTIME_LOG("[WaitSema:block] tid=" << g_currentThreadId - << " sid=" << sid - << " pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << std::dec - << std::endl); + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; } - if (info) + int ret = KE_OK; + int beforeCount = 0; + int afterCount = 0; { - std::lock_guard tLock(info->m); - info->status = (info->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT; - info->waitType = TSW_SEMA; - info->waitId = sid; - info->forceRelease = false; + std::lock_guard lock(sema->m); + beforeCount = sema->count; + if (sema->count >= sema->maxCount) + { + ret = KE_SEMA_OVF; + } + else + { + sema->count++; + sema->cv.notify_one(); + } + afterCount = sema->count; } - sema->waiters++; + static std::atomic s_signalSemaLogs{0}; + const uint32_t sigLog = s_signalSemaLogs.fetch_add(1, std::memory_order_relaxed); + if (sigLog < 256u) { - PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); - sema->cv.wait(lock, [&]() - { - bool forced = info ? info->forceRelease.load() : false; - bool terminated = info ? info->terminated.load() : false; - return sema->count > 0 || sema->deleted || forced || terminated; // - }); + RUNTIME_LOG("[SignalSema] tid=" << g_currentThreadId + << " sid=" << sid + << " count=" << beforeCount << "->" << afterCount + << " ret=" << ret + << std::endl); } - sema->waiters--; - if (sema->deleted) + + setReturnS32(ctx, ret); + } + + void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + SignalSema(rdram, ctx, runtime); + } + + void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int sid = static_cast(getRegU32(ctx, 4)); + auto sema = lookupSemaInfo(sid); + if (!sema) { - ret = KE_WAIT_DELETE; + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; } - if (info) + auto info = ensureCurrentThreadInfo(ctx); + throwIfTerminated(info); + std::unique_lock lock(sema->m); + int ret = 0; + + if (sema->count == 0) { - std::lock_guard tLock(info->m); - info->status = (info->suspendCount > 0) ? THS_SUSPEND : THS_RUN; - info->waitType = TSW_NONE; - info->waitId = 0; - if (info->forceRelease) + static std::atomic s_waitSemaBlockLogs{0}; + const uint32_t blockLog = s_waitSemaBlockLogs.fetch_add(1, std::memory_order_relaxed); + if (blockLog < 256u) + { + RUNTIME_LOG("[WaitSema:block] tid=" << g_currentThreadId + << " sid=" << sid + << " pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << std::dec + << std::endl); + } + + if (info) { + std::lock_guard tLock(info->m); + info->status = (info->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT; + info->waitType = TSW_SEMA; + info->waitId = sid; info->forceRelease = false; - ret = KE_RELEASE_WAIT; + } + + sema->waiters++; + { + PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); + sema->cv.wait(lock, [&]() + { + bool forced = info ? info->forceRelease.load() : false; + bool terminated = info ? info->terminated.load() : false; + return sema->count > 0 || sema->deleted || forced || terminated; // + }); + } + sema->waiters--; + if (sema->deleted) + { + ret = KE_WAIT_DELETE; + } + + if (info) + { + std::lock_guard tLock(info->m); + info->status = (info->suspendCount > 0) ? THS_SUSPEND : THS_RUN; + info->waitType = TSW_NONE; + info->waitId = 0; + if (info->forceRelease) + { + info->forceRelease = false; + ret = KE_RELEASE_WAIT; + } + } + + if (info && info->terminated.load()) + { + throw ThreadExitException(); } } - if (info && info->terminated.load()) + if (ret == 0 && sema->count > 0) { - throw ThreadExitException(); + sema->count--; } - } - if (ret == 0 && sema->count > 0) - { - sema->count--; + static std::atomic s_waitSemaWakeLogs{0}; + const uint32_t wakeLog = s_waitSemaWakeLogs.fetch_add(1, std::memory_order_relaxed); + if (wakeLog < 256u) + { + RUNTIME_LOG("[WaitSema:wake] tid=" << g_currentThreadId + << " sid=" << sid + << " ret=" << ret + << " count=" << sema->count + << std::endl); + } + lock.unlock(); + waitWhileSuspended(info, runtime); + setReturnS32(ctx, ret); } - static std::atomic s_waitSemaWakeLogs{0}; - const uint32_t wakeLog = s_waitSemaWakeLogs.fetch_add(1, std::memory_order_relaxed); - if (wakeLog < 256u) + void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("[WaitSema:wake] tid=" << g_currentThreadId - << " sid=" << sid - << " ret=" << ret - << " count=" << sema->count - << std::endl); - } - lock.unlock(); - waitWhileSuspended(info, runtime); - setReturnS32(ctx, ret); -} + int sid = static_cast(getRegU32(ctx, 4)); + auto sema = lookupSemaInfo(sid); + if (!sema) + { + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; + } -void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int sid = static_cast(getRegU32(ctx, 4)); - auto sema = lookupSemaInfo(sid); - if (!sema) - { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; + std::lock_guard lock(sema->m); + if (sema->count > 0) + { + sema->count--; + setReturnS32(ctx, KE_OK); + return; + } + + setReturnS32(ctx, KE_SEMA_ZERO); } - std::lock_guard lock(sema->m); - if (sema->count > 0) + void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - sema->count--; - setReturnS32(ctx, KE_OK); - return; + PollSema(rdram, ctx, runtime); } - setReturnS32(ctx, KE_SEMA_ZERO); -} + void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int sid = static_cast(getRegU32(ctx, 4)); + uint32_t statusAddr = getRegU32(ctx, 5); -void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - PollSema(rdram, ctx, runtime); -} + auto sema = lookupSemaInfo(sid); + if (!sema) + { + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; + } -void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int sid = static_cast(getRegU32(ctx, 4)); - uint32_t statusAddr = getRegU32(ctx, 5); + ee_sema_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); + if (!status) + { + setReturnS32(ctx, KE_ERROR); + return; + } - auto sema = lookupSemaInfo(sid); - if (!sema) - { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; + std::lock_guard lock(sema->m); + status->count = sema->count; + status->max_count = sema->maxCount; + status->init_count = sema->initCount; + status->wait_threads = sema->waiters; + status->attr = sema->attr; + status->option = sema->option; + setReturnS32(ctx, KE_OK); } - ee_sema_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); - if (!status) + void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_ERROR); - return; + ReferSemaStatus(rdram, ctx, runtime); } - std::lock_guard lock(sema->m); - status->count = sema->count; - status->max_count = sema->maxCount; - status->init_count = sema->initCount; - status->wait_threads = sema->waiters; - status->attr = sema->attr; - status->option = sema->option; - setReturnS32(ctx, KE_OK); -} - -void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ReferSemaStatus(rdram, ctx, runtime); -} + constexpr uint32_t WEF_OR = 1; + constexpr uint32_t WEF_CLEAR = 0x10; + constexpr uint32_t WEF_CLEAR_ALL = 0x20; + constexpr uint32_t WEF_MODE_MASK = WEF_OR | WEF_CLEAR | WEF_CLEAR_ALL; + constexpr uint32_t EA_MULTI = 0x2; -constexpr uint32_t WEF_OR = 1; -constexpr uint32_t WEF_CLEAR = 0x10; -constexpr uint32_t WEF_CLEAR_ALL = 0x20; -constexpr uint32_t WEF_MODE_MASK = WEF_OR | WEF_CLEAR | WEF_CLEAR_ALL; -constexpr uint32_t EA_MULTI = 0x2; + void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 + const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); -void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); + auto info = std::make_shared(); + if (param) + { + info->attr = param[0]; + info->option = param[1]; + info->initBits = param[2]; + info->bits = info->initBits; + } - auto info = std::make_shared(); - if (param) - { - info->attr = param[0]; - info->option = param[1]; - info->initBits = param[2]; - info->bits = info->initBits; + int id = 0; + { + std::lock_guard mapLock(g_event_flag_map_mutex); + id = g_nextEventFlagId++; + g_eventFlags[id] = info; + } + setReturnS32(ctx, id); } - int id = 0; + void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard mapLock(g_event_flag_map_mutex); - id = g_nextEventFlagId++; - g_eventFlags[id] = info; - } - setReturnS32(ctx, id); -} + int eid = static_cast(getRegU32(ctx, 4)); + std::shared_ptr info; + { + std::lock_guard mapLock(g_event_flag_map_mutex); + auto it = g_eventFlags.find(eid); + if (it == g_eventFlags.end()) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + info = it->second; + g_eventFlags.erase(it); + } -void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int eid = static_cast(getRegU32(ctx, 4)); - std::shared_ptr info; - { - std::lock_guard mapLock(g_event_flag_map_mutex); - auto it = g_eventFlags.find(eid); - if (it == g_eventFlags.end()) + if (!info) { setReturnS32(ctx, KE_UNKNOWN_EVFID); return; } - info = it->second; - g_eventFlags.erase(it); - } - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; + { + std::lock_guard lock(info->m); + info->deleted = true; + } + info->cv.notify_all(); + setReturnS32(ctx, 0); } + void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - info->deleted = true; + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t bits = getRegU32(ctx, 5); + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + if (bits == 0) + { + setReturnS32(ctx, KE_OK); + return; + } + + uint32_t newBits = 0u; + { + std::lock_guard lock(info->m); + info->bits |= bits; + newBits = info->bits; + } + + static std::atomic s_setEventFlagLogs{0}; + const uint32_t setLog = s_setEventFlagLogs.fetch_add(1, std::memory_order_relaxed); + if (setLog < 256u) + { + RUNTIME_LOG("[SetEventFlag] tid=" << g_currentThreadId + << " eid=" << eid + << " bits=0x" << std::hex << bits + << " newBits=0x" << newBits + << std::dec << std::endl); + } + info->cv.notify_all(); + setReturnS32(ctx, 0); } - info->cv.notify_all(); - setReturnS32(ctx, 0); -} -void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t bits = getRegU32(ctx, 5); - auto info = lookupEventFlagInfo(eid); - if (!info) + void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; + SetEventFlag(rdram, ctx, runtime); } - if (bits == 0) + void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t bits = getRegU32(ctx, 5); + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + { + std::lock_guard lock(info->m); + info->bits &= bits; + } + info->cv.notify_all(); setReturnS32(ctx, KE_OK); - return; } - uint32_t newBits = 0u; + void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - info->bits |= bits; - newBits = info->bits; + ClearEventFlag(rdram, ctx, runtime); } - static std::atomic s_setEventFlagLogs{0}; - const uint32_t setLog = s_setEventFlagLogs.fetch_add(1, std::memory_order_relaxed); - if (setLog < 256u) + void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("[SetEventFlag] tid=" << g_currentThreadId - << " eid=" << eid - << " bits=0x" << std::hex << bits - << " newBits=0x" << newBits - << std::dec << std::endl); - } - info->cv.notify_all(); - setReturnS32(ctx, 0); -} + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t waitBits = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t resBitsAddr = getRegU32(ctx, 7); -void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - SetEventFlag(rdram, ctx, runtime); -} + if ((mode & ~WEF_MODE_MASK) != 0) + { + setReturnS32(ctx, KE_ILLEGAL_MODE); + return; + } -void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t bits = getRegU32(ctx, 5); - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } + if (waitBits == 0) + { + setReturnS32(ctx, KE_EVF_ILPAT); + return; + } - { - std::lock_guard lock(info->m); - info->bits &= bits; - } - info->cv.notify_all(); - setReturnS32(ctx, KE_OK); -} + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } -void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ClearEventFlag(rdram, ctx, runtime); -} + uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; -void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t waitBits = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t resBitsAddr = getRegU32(ctx, 7); + std::unique_lock lock(info->m); + if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) + { + setReturnS32(ctx, KE_EVF_MULTI); + return; + } - if ((mode & ~WEF_MODE_MASK) != 0) - { - setReturnS32(ctx, KE_ILLEGAL_MODE); - return; - } + auto tInfo = ensureCurrentThreadInfo(ctx); + throwIfTerminated(tInfo); + int ret = KE_OK; - if (waitBits == 0) - { - setReturnS32(ctx, KE_EVF_ILPAT); - return; - } + auto satisfied = [&]() + { + if (tInfo && tInfo->forceRelease.load()) + return true; + if (tInfo && tInfo->terminated.load()) + return true; + if (info->deleted) + { + return true; + } + if (mode & WEF_OR) + { + return (info->bits & waitBits) != 0; + } + return (info->bits & waitBits) == waitBits; + }; - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } + if (!satisfied()) + { + static std::atomic s_waitEventBlockLogs{0}; + const uint32_t evBlockLog = s_waitEventBlockLogs.fetch_add(1, std::memory_order_relaxed); + if (evBlockLog < 256u) + { + RUNTIME_LOG("[WaitEventFlag:block] tid=" << g_currentThreadId + << " eid=" << eid + << " waitBits=0x" << std::hex << waitBits + << " mode=0x" << mode + << " bits=0x" << info->bits + << " pc=0x" << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << std::dec + << std::endl); + } - uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; + if (tInfo) + { + std::lock_guard tLock(tInfo->m); + tInfo->status = (tInfo->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT; + tInfo->waitType = TSW_EVENT; + tInfo->waitId = eid; + tInfo->forceRelease = false; + } - std::unique_lock lock(info->m); - if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) - { - setReturnS32(ctx, KE_EVF_MULTI); - return; - } + info->waiters++; + { + PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); + info->cv.wait(lock, satisfied); + } + info->waiters--; + + if (tInfo) + { + std::lock_guard tLock(tInfo->m); + tInfo->status = (tInfo->suspendCount > 0) ? THS_SUSPEND : THS_RUN; + tInfo->waitType = TSW_NONE; + tInfo->waitId = 0; + if (tInfo->forceRelease) + { + tInfo->forceRelease = false; + ret = KE_RELEASE_WAIT; + } + } - auto tInfo = ensureCurrentThreadInfo(ctx); - throwIfTerminated(tInfo); - int ret = KE_OK; + if (tInfo && tInfo->terminated.load()) + { + throw ThreadExitException(); + } + } - auto satisfied = [&]() - { - if (tInfo && tInfo->forceRelease.load()) - return true; - if (tInfo && tInfo->terminated.load()) - return true; - if (info->deleted) + if (ret == KE_OK && info->deleted) { - return true; + ret = KE_WAIT_DELETE; } - if (mode & WEF_OR) + + if (ret == KE_OK && resBitsPtr) { - return (info->bits & waitBits) != 0; + *resBitsPtr = info->bits; } - return (info->bits & waitBits) == waitBits; - }; - if (!satisfied()) - { - static std::atomic s_waitEventBlockLogs{0}; - const uint32_t evBlockLog = s_waitEventBlockLogs.fetch_add(1, std::memory_order_relaxed); - if (evBlockLog < 256u) + if (ret == KE_OK) { - RUNTIME_LOG("[WaitEventFlag:block] tid=" << g_currentThreadId - << " eid=" << eid - << " waitBits=0x" << std::hex << waitBits - << " mode=0x" << mode - << " bits=0x" << info->bits - << " pc=0x" << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << std::dec - << std::endl); + if (resBitsPtr) + { + *resBitsPtr = info->bits; + } + + if (mode & WEF_CLEAR_ALL) + { + info->bits = 0; + } + else if (mode & WEF_CLEAR) + { + info->bits &= ~waitBits; + } } - if (tInfo) + static std::atomic s_waitEventWakeLogs{0}; + const uint32_t evWakeLog = s_waitEventWakeLogs.fetch_add(1, std::memory_order_relaxed); + if (evWakeLog < 256u) { - std::lock_guard tLock(tInfo->m); - tInfo->status = (tInfo->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT; - tInfo->waitType = TSW_EVENT; - tInfo->waitId = eid; - tInfo->forceRelease = false; + RUNTIME_LOG("[WaitEventFlag:wake] tid=" << g_currentThreadId + << " eid=" << eid + << " ret=" << ret + << " bits=0x" << std::hex << info->bits + << std::dec + << std::endl); } - info->waiters++; + lock.unlock(); + waitWhileSuspended(tInfo, runtime); + setReturnS32(ctx, ret); + } + + void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t waitBits = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t resBitsAddr = getRegU32(ctx, 7); + + if ((mode & ~WEF_MODE_MASK) != 0) { - PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); - info->cv.wait(lock, satisfied); + setReturnS32(ctx, KE_ILLEGAL_MODE); + return; } - info->waiters--; - if (tInfo) + if (waitBits == 0) { - std::lock_guard tLock(tInfo->m); - tInfo->status = (tInfo->suspendCount > 0) ? THS_SUSPEND : THS_RUN; - tInfo->waitType = TSW_NONE; - tInfo->waitId = 0; - if (tInfo->forceRelease) - { - tInfo->forceRelease = false; - ret = KE_RELEASE_WAIT; - } + setReturnS32(ctx, KE_EVF_ILPAT); + return; } - if (tInfo && tInfo->terminated.load()) + auto info = lookupEventFlagInfo(eid); + if (!info) { - throw ThreadExitException(); + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; } - } - if (ret == KE_OK && info->deleted) - { - ret = KE_WAIT_DELETE; - } + uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; - if (ret == KE_OK && resBitsPtr) - { - *resBitsPtr = info->bits; - } + std::lock_guard lock(info->m); + if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) + { + setReturnS32(ctx, KE_EVF_MULTI); + return; + } + + bool ok = false; + if (mode & WEF_OR) + { + ok = (info->bits & waitBits) != 0; + } + else + { + ok = (info->bits & waitBits) == waitBits; + } + + if (!ok) + { + setReturnS32(ctx, KE_EVF_COND); + return; + } - if (ret == KE_OK) - { if (resBitsPtr) { *resBitsPtr = info->bits; @@ -685,239 +768,156 @@ void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { info->bits &= ~waitBits; } - } - - static std::atomic s_waitEventWakeLogs{0}; - const uint32_t evWakeLog = s_waitEventWakeLogs.fetch_add(1, std::memory_order_relaxed); - if (evWakeLog < 256u) - { - RUNTIME_LOG("[WaitEventFlag:wake] tid=" << g_currentThreadId - << " eid=" << eid - << " ret=" << ret - << " bits=0x" << std::hex << info->bits - << std::dec - << std::endl); - } - - lock.unlock(); - waitWhileSuspended(tInfo, runtime); - setReturnS32(ctx, ret); -} -void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t waitBits = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t resBitsAddr = getRegU32(ctx, 7); - - if ((mode & ~WEF_MODE_MASK) != 0) - { - setReturnS32(ctx, KE_ILLEGAL_MODE); - return; + setReturnS32(ctx, KE_OK); } - if (waitBits == 0) + void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_EVF_ILPAT); - return; + PollEventFlag(rdram, ctx, runtime); } - auto info = lookupEventFlagInfo(eid); - if (!info) + void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t infoAddr = getRegU32(ctx, 5); - std::lock_guard lock(info->m); - if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) - { - setReturnS32(ctx, KE_EVF_MULTI); - return; - } + struct Ps2EventFlagInfo + { + uint32_t attr; + uint32_t option; + uint32_t initBits; + uint32_t currBits; + int32_t numThreads; + int32_t reserved1; + int32_t reserved2; + }; + + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } - bool ok = false; - if (mode & WEF_OR) - { - ok = (info->bits & waitBits) != 0; - } - else - { - ok = (info->bits & waitBits) == waitBits; - } + Ps2EventFlagInfo *out = infoAddr ? reinterpret_cast(getMemPtr(rdram, infoAddr)) : nullptr; + if (!out) + { + setReturnS32(ctx, -1); + return; + } - if (!ok) - { - setReturnS32(ctx, KE_EVF_COND); - return; + std::lock_guard lock(info->m); + out->attr = info->attr; + out->option = info->option; + out->initBits = info->initBits; + out->currBits = info->bits; + out->numThreads = info->waiters; + out->reserved1 = 0; + out->reserved2 = 0; + setReturnS32(ctx, 0); } - if (resBitsPtr) + void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - *resBitsPtr = info->bits; + ReferEventFlagStatus(rdram, ctx, runtime); } - if (mode & WEF_CLEAR_ALL) + void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->bits = 0; - } - else if (mode & WEF_CLEAR) - { - info->bits &= ~waitBits; - } + uint16_t ticks = static_cast(getRegU32(ctx, 4) & 0xFFFFu); + uint32_t handler = getRegU32(ctx, 5); + uint32_t arg = getRegU32(ctx, 6); - setReturnS32(ctx, KE_OK); -} - -void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - PollEventFlag(rdram, ctx, runtime); -} + static int logCount = 0; + if (logCount < 5) + { + RUNTIME_LOG("[SetAlarm] ticks=" << ticks + << " handler=0x" << std::hex << handler + << " arg=0x" << arg << std::dec << std::endl); + ++logCount; + } -void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t infoAddr = getRegU32(ctx, 5); - - struct Ps2EventFlagInfo - { - uint32_t attr; - uint32_t option; - uint32_t initBits; - uint32_t currBits; - int32_t numThreads; - int32_t reserved1; - int32_t reserved2; - }; + if (!runtime || !handler || !runtime->hasFunction(handler)) + { + setReturnS32(ctx, KE_ERROR); + return; + } - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } + auto info = std::make_shared(); + info->ticks = ticks; + info->handler = handler; + info->commonArg = arg; + info->gp = getRegU32(ctx, 28); + info->sp = getRegU32(ctx, 29); + info->rdram = rdram; + info->runtime = runtime; + info->dueAt = std::chrono::steady_clock::now() + alarmTicksToDuration(ticks); + + int alarmId = 0; + { + std::lock_guard lock(g_alarm_mutex); + alarmId = g_nextAlarmId++; + if (g_nextAlarmId <= 0) + { + g_nextAlarmId = 1; + } + info->id = alarmId; + g_alarms[alarmId] = info; + } - Ps2EventFlagInfo *out = infoAddr ? reinterpret_cast(getMemPtr(rdram, infoAddr)) : nullptr; - if (!out) - { - setReturnS32(ctx, -1); - return; + ensureAlarmWorkerRunning(); + g_alarm_cv.notify_all(); + setReturnS32(ctx, alarmId); } - std::lock_guard lock(info->m); - out->attr = info->attr; - out->option = info->option; - out->initBits = info->initBits; - out->currBits = info->bits; - out->numThreads = info->waiters; - out->reserved1 = 0; - out->reserved2 = 0; - setReturnS32(ctx, 0); -} - -void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ReferEventFlagStatus(rdram, ctx, runtime); -} - -void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint16_t ticks = static_cast(getRegU32(ctx, 4) & 0xFFFFu); - uint32_t handler = getRegU32(ctx, 5); - uint32_t arg = getRegU32(ctx, 6); - - static int logCount = 0; - if (logCount < 5) + void InitAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("[SetAlarm] ticks=" << ticks - << " handler=0x" << std::hex << handler - << " arg=0x" << arg << std::dec << std::endl); - ++logCount; + setReturnS32(ctx, 0); } - if (!runtime || !handler || !runtime->hasFunction(handler)) + void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_ERROR); - return; + SetAlarm(rdram, ctx, runtime); } - auto info = std::make_shared(); - info->ticks = ticks; - info->handler = handler; - info->commonArg = arg; - info->gp = getRegU32(ctx, 28); - info->sp = getRegU32(ctx, 29); - info->rdram = rdram; - info->runtime = runtime; - info->dueAt = std::chrono::steady_clock::now() + alarmTicksToDuration(ticks); - - int alarmId = 0; + void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_alarm_mutex); - alarmId = g_nextAlarmId++; - if (g_nextAlarmId <= 0) + int alarmId = static_cast(getRegU32(ctx, 4)); + if (alarmId <= 0) { - g_nextAlarmId = 1; + setReturnS32(ctx, KE_ERROR); + return; } - info->id = alarmId; - g_alarms[alarmId] = info; - } - - ensureAlarmWorkerRunning(); - g_alarm_cv.notify_all(); - setReturnS32(ctx, alarmId); -} -void InitAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, 0); -} + bool removed = false; + { + std::lock_guard lock(g_alarm_mutex); + removed = g_alarms.erase(alarmId) != 0; + } -void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - SetAlarm(rdram, ctx, runtime); -} + if (removed) + { + g_alarm_cv.notify_all(); + setReturnS32(ctx, KE_OK); + return; + } -void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int alarmId = static_cast(getRegU32(ctx, 4)); - if (alarmId <= 0) - { setReturnS32(ctx, KE_ERROR); - return; } - bool removed = false; + void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_alarm_mutex); - removed = g_alarms.erase(alarmId) != 0; + CancelAlarm(rdram, ctx, runtime); } - if (removed) + void ReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - g_alarm_cv.notify_all(); - setReturnS32(ctx, KE_OK); - return; + CancelAlarm(rdram, ctx, runtime); } - setReturnS32(ctx, KE_ERROR); -} - -void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - CancelAlarm(rdram, ctx, runtime); -} - -void ReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - CancelAlarm(rdram, ctx, runtime); -} - -void iReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - iCancelAlarm(rdram, ctx, runtime); -} + void iReleaseAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + iCancelAlarm(rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp index af8110c0..0752220b 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp @@ -3,797 +3,877 @@ namespace ps2_syscalls { -void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int interlaced = getRegU32(ctx, 4); // $a0 - 0=non-interlaced, 1=interlaced - int videoMode = getRegU32(ctx, 5); // $a1 - 0=NTSC, 1=PAL, 2=VESA, 3=HiVision - int frameMode = getRegU32(ctx, 6); // $a2 - 0=field, 1=frame - - if (runtime) + void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - auto &gs = runtime->memory().gs(); - const uint64_t smode2 = - (static_cast(interlaced) & 0x1ull) | - ((static_cast(frameMode) & 0x1ull) << 1); - - gs.smode2 = smode2; + int interlaced = getRegU32(ctx, 4); // $a0 - 0=non-interlaced, 1=interlaced + int videoMode = getRegU32(ctx, 5); // $a1 - 0=NTSC, 1=PAL, 2=VESA, 3=HiVision + int frameMode = getRegU32(ctx, 6); // $a2 - 0=field, 1=frame - // Keep CRT1 enabled after the BIOS syscall selects a display mode. - if ((gs.pmode & 0x3ull) == 0ull) + if (runtime) { - gs.pmode |= 0x1ull; - } - } + auto &gs = runtime->memory().gs(); + const uint64_t smode2 = + (static_cast(interlaced) & 0x1ull) | + ((static_cast(frameMode) & 0x1ull) << 1); - RUNTIME_LOG("PS2 GsSetCrt: interlaced=" << interlaced - << ", videoMode=" << videoMode - << ", frameMode=" << frameMode << std::endl); + gs.smode2 = smode2; - setReturnS32(ctx, 0); -} + // Keep CRT1 enabled after the BIOS syscall selects a display mode. + if ((gs.pmode & 0x3ull) == 0ull) + { + gs.pmode |= 0x1ull; + } + } -void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - GsSetCrt(rdram, ctx, runtime); -} + RUNTIME_LOG("PS2 GsSetCrt: interlaced=" << interlaced + << ", videoMode=" << videoMode + << ", frameMode=" << frameMode << std::endl); -void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint64_t imr = 0; - if (runtime) + setReturnS32(ctx, 0); + } + + void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - imr = runtime->memory().gs().imr; + GsSetCrt(rdram, ctx, runtime); } - RUNTIME_LOG("PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec); + void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint64_t imr = 0; + if (runtime) + { + imr = runtime->memory().gs().imr; + } - setReturnU64(ctx, imr); // Return in $v0/$v1 -} + RUNTIME_LOG("PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec); -void iGsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - GsGetIMR(rdram, ctx, runtime); -} + setReturnU64(ctx, imr); // Return in $v0/$v1 + } -void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint64_t newImr = getRegU32(ctx, 4) | ((uint64_t)getRegU32(ctx, 5) << 32); // $a0 = lower 32 bits, $a1 = upper 32 bits - uint64_t oldImr = 0; - if (runtime) + void iGsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - oldImr = runtime->memory().gs().imr; - runtime->memory().gs().imr = newImr; + GsGetIMR(rdram, ctx, runtime); } - RUNTIME_LOG("PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec); - setReturnU64(ctx, oldImr); -} - -void iGsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - GsPutIMR(rdram, ctx, runtime); -} -void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags) + void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint64_t newImr = getRegU32(ctx, 4) | ((uint64_t)getRegU32(ctx, 5) << 32); // $a0 = lower 32 bits, $a1 = upper 32 bits + uint64_t oldImr = 0; + if (runtime) + { + oldImr = runtime->memory().gs().imr; + runtime->memory().gs().imr = newImr; + } + RUNTIME_LOG("PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec); + setReturnU64(ctx, oldImr); + } - RUNTIME_LOG("PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec); + void iGsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + GsPutIMR(rdram, ctx, runtime); + } - // Do nothing for now. -} + void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags) -void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure + RUNTIME_LOG("PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec); - if (!getMemPtr(rdram, paramAddr)) - { - std::cerr << "PS2 GetOsdConfigParam error: Invalid parameter address: 0x" - << std::hex << paramAddr << std::dec << std::endl; - setReturnS32(ctx, -1); - return; + // Do nothing for now. } - uint32_t *param = reinterpret_cast(getMemPtr(rdram, paramAddr)); - - ensureOsdConfigInitialized(); - uint32_t raw; + void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_osd_mutex); - raw = g_osd_config_raw; - } + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure + + if (!getMemPtr(rdram, paramAddr)) + { + std::cerr << "PS2 GetOsdConfigParam error: Invalid parameter address: 0x" + << std::hex << paramAddr << std::dec << std::endl; + setReturnS32(ctx, -1); + return; + } - *param = raw; + uint32_t *param = reinterpret_cast(getMemPtr(rdram, paramAddr)); - setReturnS32(ctx, 0); -} + ensureOsdConfigInitialized(); + uint32_t raw; + { + std::lock_guard lock(g_osd_mutex); + raw = g_osd_config_raw; + } -void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure + *param = raw; - if (!getConstMemPtr(rdram, paramAddr)) - { - std::cerr << "PS2 SetOsdConfigParam error: Invalid parameter address: 0x" - << std::hex << paramAddr << std::dec << std::endl; - setReturnS32(ctx, -1); - return; + setReturnS32(ctx, 0); } - const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); - uint32_t raw = param ? *param : 0; - raw = sanitizeOsdConfigRaw(raw); + void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_osd_mutex); - g_osd_config_raw = raw; - g_osd_config_initialized = true; - } + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure - setReturnS32(ctx, 0); -} + if (!getConstMemPtr(rdram, paramAddr)) + { + std::cerr << "PS2 SetOsdConfigParam error: Invalid parameter address: 0x" + << std::hex << paramAddr << std::dec << std::endl; + setReturnS32(ctx, -1); + return; + } -void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t bufAddr = getRegU32(ctx, 4); // $a0 - size_t bufSize = getRegU32(ctx, 5); // $a1 - char *hostBuf = reinterpret_cast(getMemPtr(rdram, bufAddr)); - const char *romName = "ROMVER 0100"; + const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); + uint32_t raw = param ? *param : 0; + raw = sanitizeOsdConfigRaw(raw); + { + std::lock_guard lock(g_osd_mutex); + g_osd_config_raw = raw; + g_osd_config_initialized = true; + } - if (!hostBuf) - { - std::cerr << "GetRomName error: Invalid buffer address" << std::endl; - setReturnS32(ctx, -1); // Error - return; - } - if (bufSize == 0) - { setReturnS32(ctx, 0); - return; } - strncpy(hostBuf, romName, bufSize - 1); - hostBuf[bufSize - 1] = '\0'; + void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + uint32_t bufAddr = getRegU32(ctx, 4); // $a0 + size_t bufSize = getRegU32(ctx, 5); // $a1 + char *hostBuf = reinterpret_cast(getMemPtr(rdram, bufAddr)); + const char *romName = "ROMVER 0100"; - // returns the length of the string (excluding null?) or error - setReturnS32(ctx, (int32_t)strlen(hostBuf)); -} + if (!hostBuf) + { + std::cerr << "GetRomName error: Invalid buffer address" << std::endl; + setReturnS32(ctx, -1); // Error + return; + } + if (bufSize == 0) + { + setReturnS32(ctx, 0); + return; + } -void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path - const uint32_t secNameAddr = getRegU32(ctx, 5); // $a1 - section name ("all" typically) - const uint32_t execDataAddr = getRegU32(ctx, 6); // $a2 - t_ExecData* + strncpy(hostBuf, romName, bufSize - 1); + hostBuf[bufSize - 1] = '\0'; - std::string secName = readGuestCStringBounded(rdram, secNameAddr, kLoadfileArgMaxBytes); - if (secName.empty()) - { - secName = "all"; + // returns the length of the string (excluding null?) or error + setReturnS32(ctx, (int32_t)strlen(hostBuf)); } - const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, secName, execDataAddr); - setReturnS32(ctx, ret); -} - -void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path - const uint32_t execDataAddr = getRegU32(ctx, 5); // $a1 - t_ExecData* - const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, "all", execDataAddr); - setReturnS32(ctx, ret); -} + void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path + const uint32_t secNameAddr = getRegU32(ctx, 5); // $a1 - section name ("all" typically) + const uint32_t execDataAddr = getRegU32(ctx, 6); // $a2 - t_ExecData* -void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - SifLoadElfPart(rdram, ctx, runtime); -} + std::string secName = readGuestCStringBounded(rdram, secNameAddr, kLoadfileArgMaxBytes); + if (secName.empty()) + { + secName = "all"; + } -void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - // Use the same tracker as SifLoadModule so both APIs return the same module IDs. - SifLoadModule(rdram, ctx, runtime); -} + const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, secName, execDataAddr); + setReturnS32(ctx, ret); + } -void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t bufferAddr = getRegU32(ctx, 4); // $a0 - if (!rdram || bufferAddr == 0u) + void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path + const uint32_t execDataAddr = getRegU32(ctx, 5); // $a1 - t_ExecData* + const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, "all", execDataAddr); + setReturnS32(ctx, ret); } - // Match buffer-based module loads to stable synthetic tags so module ID lookup remains deterministic. - const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr); - const int32_t moduleId = trackSifModuleLoad(moduleTag); - if (moduleId <= 0) + void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, -1); - return; + SifLoadElfPart(rdram, ctx, runtime); } - uint32_t refs = 0; + void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_sif_module_mutex); - auto it = g_sif_modules_by_id.find(moduleId); - if (it != g_sif_modules_by_id.end()) - { - refs = it->second.refCount; - } + // Use the same tracker as SifLoadModule so both APIs return the same module IDs. + SifLoadModule(rdram, ctx, runtime); } - logSifModuleAction("load-buffer", moduleId, moduleTag, refs); - setReturnS32(ctx, moduleId); -} - -void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId) -{ - // a bit more detail mayber reomve old logic, lets get it more raw - std::cerr << "[Syscall TODO]" - << " encoded=0x" << std::hex << encodedSyscallId - << " v1=0x" << getRegU32(ctx, 3) - << " v0=0x" << getRegU32(ctx, 2) - << " a0=0x" << getRegU32(ctx, 4) - << " a1=0x" << getRegU32(ctx, 5) - << " a2=0x" << getRegU32(ctx, 6) - << " a3=0x" << getRegU32(ctx, 7) - << " pc=0x" << ctx->pc - << std::dec << std::endl; - const uint32_t v0 = getRegU32(ctx, 2); - const uint32_t v1 = getRegU32(ctx, 3); - const uint32_t caller_ra = getRegU32(ctx, 31); - uint32_t syscallId = encodedSyscallId; - if (syscallId == 0u) + void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - syscallId = (v0 != 0u) ? v0 : v1; - } + const uint32_t bufferAddr = getRegU32(ctx, 4); // $a0 + if (!rdram || bufferAddr == 0u) + { + setReturnS32(ctx, -1); + return; + } - std::cerr << "Warning: Unimplemented PS2 syscall called. PC=0x" << std::hex << ctx->pc - << ", RA=0x" << caller_ra - << ", Encoded=0x" << encodedSyscallId - << ", v0=0x" << v0 - << ", v1=0x" << v1 - << ", Chosen=0x" << syscallId - << std::dec << std::endl; + // Match buffer-based module loads to stable synthetic tags so module ID lookup remains deterministic. + const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr); + const int32_t moduleId = trackSifModuleLoad(moduleTag); + if (moduleId <= 0) + { + setReturnS32(ctx, -1); + return; + } - std::cerr << " Args: $a0=0x" << std::hex << getRegU32(ctx, 4) - << ", $a1=0x" << getRegU32(ctx, 5) - << ", $a2=0x" << getRegU32(ctx, 6) - << ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl; + uint32_t refs = 0; + { + std::lock_guard lock(g_sif_module_mutex); + auto it = g_sif_modules_by_id.find(moduleId); + if (it != g_sif_modules_by_id.end()) + { + refs = it->second.refCount; + } + } + logSifModuleAction("load-buffer", moduleId, moduleTag, refs); + setReturnS32(ctx, moduleId); + } + + void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId) + { + // a bit more detail mayber reomve old logic, lets get it more raw + std::cerr << "[Syscall TODO]" + << " encoded=0x" << std::hex << encodedSyscallId + << " v1=0x" << getRegU32(ctx, 3) + << " v0=0x" << getRegU32(ctx, 2) + << " a0=0x" << getRegU32(ctx, 4) + << " a1=0x" << getRegU32(ctx, 5) + << " a2=0x" << getRegU32(ctx, 6) + << " a3=0x" << getRegU32(ctx, 7) + << " pc=0x" << ctx->pc + << std::dec << std::endl; + + const uint32_t v0 = getRegU32(ctx, 2); + const uint32_t v1 = getRegU32(ctx, 3); + const uint32_t caller_ra = getRegU32(ctx, 31); + uint32_t syscallId = encodedSyscallId; + if (syscallId == 0u) + { + syscallId = (v0 != 0u) ? v0 : v1; + } - // Common syscalls: - // 0x04: Exit - // 0x06: LoadExecPS2 - // 0x07: ExecPS2 - if (syscallId == 0x04u) - { - std::cerr << " -> Syscall is Exit(), calling ExitThread stub." << std::endl; - ExitThread(rdram, ctx, runtime); - return; - } + std::cerr << "Warning: Unimplemented PS2 syscall called. PC=0x" << std::hex << ctx->pc + << ", RA=0x" << caller_ra + << ", Encoded=0x" << encodedSyscallId + << ", v0=0x" << v0 + << ", v1=0x" << v1 + << ", Chosen=0x" << syscallId + << std::dec << std::endl; + + std::cerr << " Args: $a0=0x" << std::hex << getRegU32(ctx, 4) + << ", $a1=0x" << getRegU32(ctx, 5) + << ", $a2=0x" << getRegU32(ctx, 6) + << ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl; + + // Common syscalls: + // 0x04: Exit + // 0x06: LoadExecPS2 + // 0x07: ExecPS2 + if (syscallId == 0x04u) + { + std::cerr << " -> Syscall is Exit(), calling ExitThread stub." << std::endl; + ExitThread(rdram, ctx, runtime); + return; + } - static std::mutex s_unknownMutex; - static std::unordered_map s_unknownCounts; - { - std::lock_guard lock(s_unknownMutex); - const uint64_t count = ++s_unknownCounts[syscallId]; - if (count == 1 || (count % 5000u) == 0u) + static std::mutex s_unknownMutex; + static std::unordered_map s_unknownCounts; { - std::cerr << " -> Unknown syscallId=0x" << std::hex << syscallId - << " hits=" << std::dec << count << std::endl; + std::lock_guard lock(s_unknownMutex); + const uint64_t count = ++s_unknownCounts[syscallId]; + if (count == 1 || (count % 5000u) == 0u) + { + std::cerr << " -> Unknown syscallId=0x" << std::hex << syscallId + << " hits=" << std::dec << count << std::endl; + } } - } - // Bootstrap default: avoid hard-failing loops that probe syscall availability. - setReturnS32(ctx, 0); -} + // Bootstrap default: avoid hard-failing loops that probe syscall availability. + setReturnS32(ctx, 0); + } -static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram, - uint32_t originalStart, - uint32_t originalEnd, - uint32_t target); + static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram, + uint32_t originalStart, + uint32_t originalEnd, + uint32_t target); bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t handler = 0u; { - std::lock_guard lock(g_syscall_override_mutex); - auto it = g_syscall_overrides.find(syscallNumber); - if (it == g_syscall_overrides.end()) + uint32_t handler = 0u; + { + std::lock_guard lock(g_syscall_override_mutex); + auto it = g_syscall_overrides.find(syscallNumber); + if (it == g_syscall_overrides.end()) + { + return false; + } + handler = it->second; + } + + if (!runtime || !ctx || handler == 0u) { return false; } - handler = it->second; - } - if (!runtime || !ctx || handler == 0u) - { - return false; - } + const uint32_t overrideA0 = getRegU32(ctx, 4); + const uint32_t overrideA1 = getRegU32(ctx, 5); + const uint32_t overrideA2 = getRegU32(ctx, 6); + const uint32_t overrideA3 = getRegU32(ctx, 7); + const uint32_t overridePc = ctx->pc; + const uint32_t overrideRa = getRegU32(ctx, 31); - const uint32_t overrideA0 = getRegU32(ctx, 4); - const uint32_t overrideA1 = getRegU32(ctx, 5); - const uint32_t overrideA2 = getRegU32(ctx, 6); - const uint32_t overrideA3 = getRegU32(ctx, 7); - const uint32_t overridePc = ctx->pc; - const uint32_t overrideRa = getRegU32(ctx, 31); + thread_local std::vector s_activeSyscallOverrides; + if (std::find(s_activeSyscallOverrides.begin(), s_activeSyscallOverrides.end(), syscallNumber) != s_activeSyscallOverrides.end()) + { + static std::atomic s_reentrantLogs{0u}; + constexpr uint32_t kMaxReentrantLogs = 32u; + const uint32_t logIndex = s_reentrantLogs.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < kMaxReentrantLogs) + { + std::cerr << "[SyscallOverride:reentrant]" + << " syscall=0x" << std::hex << syscallNumber + << " handler=0x" << handler + << " pc=0x" << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << std::dec << std::endl; + } + return false; + } - thread_local std::vector s_activeSyscallOverrides; - if (std::find(s_activeSyscallOverrides.begin(), s_activeSyscallOverrides.end(), syscallNumber) != s_activeSyscallOverrides.end()) - { - static std::atomic s_reentrantLogs{0u}; - constexpr uint32_t kMaxReentrantLogs = 32u; - const uint32_t logIndex = s_reentrantLogs.fetch_add(1u, std::memory_order_relaxed); - if (logIndex < kMaxReentrantLogs) + s_activeSyscallOverrides.push_back(syscallNumber); + struct ScopedActiveOverride { - std::cerr << "[SyscallOverride:reentrant]" - << " syscall=0x" << std::hex << syscallNumber - << " handler=0x" << handler - << " pc=0x" << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << std::dec << std::endl; + std::vector &active; + ~ScopedActiveOverride() + { + if (!active.empty()) + { + active.pop_back(); + } + } + } scopedActiveOverride{s_activeSyscallOverrides}; + + uint32_t retV0 = 0u; + const bool invoked = rpcInvokeFunction(rdram, + ctx, + runtime, + handler, + getRegU32(ctx, 4), + getRegU32(ctx, 5), + getRegU32(ctx, 6), + getRegU32(ctx, 7), + &retV0); + + if (syscallNumber == 0x83u) + { + const uint32_t builtinRet = computeBuiltinFindAddressResult(rdram, overrideA0, overrideA1, overrideA2); + const bool mismatch = (retV0 != builtinRet); + + static std::atomic s_findAddressOverrideLogs{0u}; + static std::atomic s_findAddressOverrideMismatchLogs{0u}; + constexpr uint32_t kMaxFindAddressOverrideLogs = 64u; + constexpr uint32_t kMaxFindAddressOverrideMismatchLogs = 128u; + + const uint32_t logIndex = s_findAddressOverrideLogs.fetch_add(1u, std::memory_order_relaxed); + const uint32_t mismatchIndex = mismatch + ? s_findAddressOverrideMismatchLogs.fetch_add(1u, std::memory_order_relaxed) + : 0u; + if (logIndex < kMaxFindAddressOverrideLogs || + (mismatch && mismatchIndex < kMaxFindAddressOverrideMismatchLogs)) + { + const uint32_t guestMinus20c = (retV0 != 0u) ? (retV0 - 0x20Cu) : 0u; + const uint32_t guestMinus168 = (retV0 != 0u) ? (retV0 - 0x168u) : 0u; + const uint32_t builtinMinus20c = (builtinRet != 0u) ? (builtinRet - 0x20Cu) : 0u; + const uint32_t builtinMinus168 = (builtinRet != 0u) ? (builtinRet - 0x168u) : 0u; + + std::cerr << "[Syscall83:override]" + << " handler=0x" << std::hex << handler + << " invoked=" << (invoked ? "true" : "false") + << " pc=0x" << overridePc + << " ra=0x" << overrideRa + << " a0=0x" << overrideA0 + << " a1=0x" << overrideA1 + << " a2=0x" << overrideA2 + << " a3=0x" << overrideA3 + << " guestRet=0x" << retV0 + << " builtinRet=0x" << builtinRet + << " guest-20c=0x" << guestMinus20c + << " builtin-20c=0x" << builtinMinus20c + << " guest-168=0x" << guestMinus168 + << " builtin-168=0x" << builtinMinus168 + << " match=" << (mismatch ? "false" : "true") + << std::dec << std::endl; + } } - return false; - } - s_activeSyscallOverrides.push_back(syscallNumber); - struct ScopedActiveOverride - { - std::vector &active; - ~ScopedActiveOverride() + if (!invoked) { - if (!active.empty()) + static std::atomic s_fallbackLogs{0u}; + constexpr uint32_t kMaxFallbackLogs = 64u; + const uint32_t logIndex = s_fallbackLogs.fetch_add(1u, std::memory_order_relaxed); + if (logIndex < kMaxFallbackLogs) { - active.pop_back(); + std::cerr << "[SyscallOverride:fallback]" + << " syscall=0x" << std::hex << syscallNumber + << " handler=0x" << handler + << " pc=0x" << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << std::dec << std::endl; } + return false; } - } scopedActiveOverride{s_activeSyscallOverrides}; - - uint32_t retV0 = 0u; - const bool invoked = rpcInvokeFunction(rdram, - ctx, - runtime, - handler, - getRegU32(ctx, 4), - getRegU32(ctx, 5), - getRegU32(ctx, 6), - getRegU32(ctx, 7), - &retV0); - - if (syscallNumber == 0x83u) - { - const uint32_t builtinRet = computeBuiltinFindAddressResult(rdram, overrideA0, overrideA1, overrideA2); - const bool mismatch = (retV0 != builtinRet); - - static std::atomic s_findAddressOverrideLogs{0u}; - static std::atomic s_findAddressOverrideMismatchLogs{0u}; - constexpr uint32_t kMaxFindAddressOverrideLogs = 64u; - constexpr uint32_t kMaxFindAddressOverrideMismatchLogs = 128u; - - const uint32_t logIndex = s_findAddressOverrideLogs.fetch_add(1u, std::memory_order_relaxed); - const uint32_t mismatchIndex = mismatch - ? s_findAddressOverrideMismatchLogs.fetch_add(1u, std::memory_order_relaxed) - : 0u; - if (logIndex < kMaxFindAddressOverrideLogs || - (mismatch && mismatchIndex < kMaxFindAddressOverrideMismatchLogs)) - { - const uint32_t guestMinus20c = (retV0 != 0u) ? (retV0 - 0x20Cu) : 0u; - const uint32_t guestMinus168 = (retV0 != 0u) ? (retV0 - 0x168u) : 0u; - const uint32_t builtinMinus20c = (builtinRet != 0u) ? (builtinRet - 0x20Cu) : 0u; - const uint32_t builtinMinus168 = (builtinRet != 0u) ? (builtinRet - 0x168u) : 0u; - - std::cerr << "[Syscall83:override]" - << " handler=0x" << std::hex << handler - << " invoked=" << (invoked ? "true" : "false") - << " pc=0x" << overridePc - << " ra=0x" << overrideRa - << " a0=0x" << overrideA0 - << " a1=0x" << overrideA1 - << " a2=0x" << overrideA2 - << " a3=0x" << overrideA3 - << " guestRet=0x" << retV0 - << " builtinRet=0x" << builtinRet - << " guest-20c=0x" << guestMinus20c - << " builtin-20c=0x" << builtinMinus20c - << " guest-168=0x" << guestMinus168 - << " builtin-168=0x" << builtinMinus168 - << " match=" << (mismatch ? "false" : "true") - << std::dec << std::endl; - } - } - - if (!invoked) - { - static std::atomic s_fallbackLogs{0u}; - constexpr uint32_t kMaxFallbackLogs = 64u; - const uint32_t logIndex = s_fallbackLogs.fetch_add(1u, std::memory_order_relaxed); - if (logIndex < kMaxFallbackLogs) - { - std::cerr << "[SyscallOverride:fallback]" - << " syscall=0x" << std::hex << syscallNumber - << " handler=0x" << handler - << " pc=0x" << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << std::dec << std::endl; - } - return false; - } - - setReturnU32(ctx, retV0); - return true; -} -static bool tryResolveGuestSyscallMirrorAddr(uint32_t syscallIndex, uint32_t &guestAddr) -{ - const int64_t offsetBytes = - static_cast(static_cast(syscallIndex)) * static_cast(sizeof(uint32_t)); - const int64_t guestAddr64 = static_cast(kGuestSyscallTablePhysBase) + offsetBytes; - if (guestAddr64 < 0 || (guestAddr64 + static_cast(sizeof(uint32_t))) > static_cast(kGuestSyscallMirrorLimit)) - { - return false; + setReturnU32(ctx, retV0); + return true; } - guestAddr = static_cast(guestAddr64); - return true; -} - -static void writeGuestKernelWord(uint8_t *rdram, uint32_t guestAddr, uint32_t value) -{ - if (!rdram) + static bool tryResolveGuestSyscallMirrorAddr(uint32_t syscallIndex, uint32_t &guestAddr) { - return; - } + const int64_t offsetBytes = + static_cast(static_cast(syscallIndex)) * static_cast(sizeof(uint32_t)); + const int64_t guestAddr64 = static_cast(kGuestSyscallTablePhysBase) + offsetBytes; + if (guestAddr64 < 0 || (guestAddr64 + static_cast(sizeof(uint32_t))) > static_cast(kGuestSyscallMirrorLimit)) + { + return false; + } - if (uint8_t *ptr = getMemPtr(rdram, guestAddr)) - { - std::memcpy(ptr, &value, sizeof(value)); + guestAddr = static_cast(guestAddr64); + return true; } -} -static void seedGuestSyscallTableProbeLocked(uint8_t *rdram) -{ - writeGuestKernelWord(rdram, kGuestSyscallTableProbeBase + 0u, kGuestSyscallTableGuestBase >> 16); - writeGuestKernelWord(rdram, kGuestSyscallTableProbeBase + 8u, kGuestSyscallTableGuestBase & 0xFFFFu); - g_syscall_mirror_addrs.insert(kGuestSyscallTableProbeBase + 0u); - g_syscall_mirror_addrs.insert(kGuestSyscallTableProbeBase + 8u); -} - -static void mirrorGuestSyscallEntryLocked(uint8_t *rdram, uint32_t syscallIndex, uint32_t handler) -{ - uint32_t guestAddr = 0u; - if (!tryResolveGuestSyscallMirrorAddr(syscallIndex, guestAddr)) + static void writeGuestKernelWord(uint8_t *rdram, uint32_t guestAddr, uint32_t value) { - return; - } + if (!rdram) + { + return; + } - writeGuestKernelWord(rdram, guestAddr, handler); - if (handler == 0u) - { - g_syscall_mirror_addrs.erase(guestAddr); - return; + if (uint8_t *ptr = getMemPtr(rdram, guestAddr)) + { + std::memcpy(ptr, &value, sizeof(value)); + } } - g_syscall_mirror_addrs.insert(guestAddr); -} - -void initializeGuestKernelState(uint8_t *rdram) -{ - if (!rdram) + static void seedGuestSyscallTableProbeLocked(uint8_t *rdram) { - return; + writeGuestKernelWord(rdram, kGuestSyscallTableProbeBase + 0u, kGuestSyscallTableGuestBase >> 16); + writeGuestKernelWord(rdram, kGuestSyscallTableProbeBase + 8u, kGuestSyscallTableGuestBase & 0xFFFFu); + g_syscall_mirror_addrs.insert(kGuestSyscallTableProbeBase + 0u); + g_syscall_mirror_addrs.insert(kGuestSyscallTableProbeBase + 8u); } - std::lock_guard lock(g_syscall_override_mutex); - for (uint32_t guestAddr : g_syscall_mirror_addrs) + static void mirrorGuestSyscallEntryLocked(uint8_t *rdram, uint32_t syscallIndex, uint32_t handler) { - writeGuestKernelWord(rdram, guestAddr, 0u); - } - g_syscall_mirror_addrs.clear(); + uint32_t guestAddr = 0u; + if (!tryResolveGuestSyscallMirrorAddr(syscallIndex, guestAddr)) + { + return; + } - seedGuestSyscallTableProbeLocked(rdram); + writeGuestKernelWord(rdram, guestAddr, handler); + if (handler == 0u) + { + g_syscall_mirror_addrs.erase(guestAddr); + return; + } - for (const auto &entry : g_syscall_overrides) - { - mirrorGuestSyscallEntryLocked(rdram, entry.first, entry.second); + g_syscall_mirror_addrs.insert(guestAddr); } -} - -void SetSyscall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; - const uint32_t syscallIndex = getRegU32(ctx, 4); - const uint32_t handler = getRegU32(ctx, 5); + void initializeGuestKernelState(uint8_t *rdram) { - std::lock_guard lock(g_syscall_override_mutex); - if (handler == 0u) + if (!rdram) { - g_syscall_overrides.erase(syscallIndex); + return; } - else + + std::lock_guard lock(g_syscall_override_mutex); + for (uint32_t guestAddr : g_syscall_mirror_addrs) { - g_syscall_overrides[syscallIndex] = handler; + writeGuestKernelWord(rdram, guestAddr, 0u); } + g_syscall_mirror_addrs.clear(); + + seedGuestSyscallTableProbeLocked(rdram); - mirrorGuestSyscallEntryLocked(rdram, syscallIndex, handler); + for (const auto &entry : g_syscall_overrides) + { + mirrorGuestSyscallEntryLocked(rdram, entry.first, entry.second); + } } - setReturnS32(ctx, 0); -} + void SetSyscall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)runtime; + const uint32_t syscallIndex = getRegU32(ctx, 4); + const uint32_t handler = getRegU32(ctx, 5); -// 0x3C SetupThread -// args: $a0 = gp, $a1 = stack, $a2 = stack_size, $a3 = args, $t0 = root_func -void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - const uint32_t gp = getRegU32(ctx, 4); - const uint32_t stack = getRegU32(ctx, 5); - const int32_t stackSizeSigned = static_cast(getRegU32(ctx, 6)); - const uint32_t currentSp = getRegU32(ctx, 29); + { + std::lock_guard lock(g_syscall_override_mutex); + if (handler == 0u) + { + g_syscall_overrides.erase(syscallIndex); + } + else + { + g_syscall_overrides[syscallIndex] = handler; + } - if (gp != 0u) - { - setRegU32(ctx, 28, gp); + mirrorGuestSyscallEntryLocked(rdram, syscallIndex, handler); + } + + setReturnS32(ctx, 0); } - uint32_t sp = currentSp; - if (stack == 0xFFFFFFFFu) + // 0x3C SetupThread + // args: $a0 = gp, $a1 = stack, $a2 = stack_size, $a3 = args, $t0 = root_func + void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (stackSizeSigned > 0) + const uint32_t gp = getRegU32(ctx, 4); + const uint32_t stack = getRegU32(ctx, 5); + const int32_t stackSizeSigned = static_cast(getRegU32(ctx, 6)); + const uint32_t currentSp = getRegU32(ctx, 29); + + if (gp != 0u) + { + setRegU32(ctx, 28, gp); + } + + uint32_t sp = currentSp; + if (stack == 0xFFFFFFFFu) { - const uint32_t requestedSize = static_cast(stackSizeSigned); - if (requestedSize < PS2_RAM_SIZE) + if (stackSizeSigned > 0) { - sp = PS2_RAM_SIZE - requestedSize; + const uint32_t requestedSize = static_cast(stackSizeSigned); + if (requestedSize < PS2_RAM_SIZE) + { + sp = PS2_RAM_SIZE - requestedSize; + } + else + { + sp = PS2_RAM_SIZE; + } } else { sp = PS2_RAM_SIZE; } } - else + else if (stack != 0u) { - sp = PS2_RAM_SIZE; + if (stackSizeSigned > 0) + { + sp = stack + static_cast(stackSizeSigned); + } + else + { + sp = stack; + } } + + sp &= ~0xFu; + setReturnU32(ctx, sp); } - else if (stack != 0u) + + // 0x3D SetupHeap: returns heap base/start pointer + void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (stackSizeSigned > 0) - { - sp = stack + static_cast(stackSizeSigned); - } - else + const uint32_t heapBase = getRegU32(ctx, 4); // $a0 + const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) + + if (runtime) { - sp = stack; + 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); + setReturnU32(ctx, runtime->guestHeapBase()); + return; } - } - - sp &= ~0xFu; - setReturnU32(ctx, sp); -} -// 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 heapSize = getRegU32(ctx, 5); // $a1 (optional size) + setReturnU32(ctx, heapBase); + } - if (runtime) + // 0x3E EndOfHeap: commonly returns current heap end; keep it stable for now. + void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - uint32_t heapLimit = PS2_RAM_SIZE; - if (heapSize != 0u && heapBase < PS2_RAM_SIZE) + if (runtime) { - const uint64_t candidateLimit = static_cast(heapBase) + static_cast(heapSize); - heapLimit = static_cast(std::min(candidateLimit, PS2_RAM_SIZE)); + setReturnU32(ctx, runtime->guestHeapEnd()); + return; } - runtime->configureGuestHeap(heapBase, heapLimit); - setReturnU32(ctx, runtime->guestHeapBase()); - return; - } - setReturnU32(ctx, heapBase); -} - -// 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; + setReturnU32(ctx, getRegU32(ctx, 4)); } - setReturnU32(ctx, getRegU32(ctx, 4)); -} - -void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)rdram; - (void)runtime; - setReturnU32(ctx, PS2_RAM_SIZE); -} - -static inline uint32_t normalizeKernelAlias(uint32_t addr) -{ - if (addr >= 0x80000000u && addr < 0xC0000000u) + void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - return addr & 0x1FFFFFFFu; + (void)rdram; + (void)runtime; + setReturnU32(ctx, PS2_RAM_SIZE); } - return addr; -} -static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram, - uint32_t originalStart, - uint32_t originalEnd, - uint32_t target) -{ - uint32_t start = (originalStart + 3u) & ~0x3u; - uint32_t end = originalEnd & ~0x3u; - if (start >= end) + static inline uint32_t normalizeKernelAlias(uint32_t addr) { - return 0u; + if (addr >= 0x80000000u && addr < 0xC0000000u) + { + return addr & 0x1FFFFFFFu; + } + return addr; } - const uint32_t targetNorm = normalizeKernelAlias(target); - for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t)) + static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram, + uint32_t originalStart, + uint32_t originalEnd, + uint32_t target) { - const uint8_t *entryPtr = getConstMemPtr(rdram, addr); - if (!entryPtr) + uint32_t start = (originalStart + 3u) & ~0x3u; + uint32_t end = originalEnd & ~0x3u; + if (start >= end) { - break; + return 0u; } - uint32_t entry = 0u; - std::memcpy(&entry, entryPtr, sizeof(entry)); - if (entry == target || normalizeKernelAlias(entry) == targetNorm) + const uint32_t targetNorm = normalizeKernelAlias(target); + for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t)) { - return addr; - } - } + const uint8_t *entryPtr = getConstMemPtr(rdram, addr); + if (!entryPtr) + { + break; + } - return 0u; -} + uint32_t entry = 0u; + std::memcpy(&entry, entryPtr, sizeof(entry)); + if (entry == target || normalizeKernelAlias(entry) == targetNorm) + { + return addr; + } + } -struct FindAddressWordSample -{ - uint32_t addr = 0u; - uint32_t value = 0u; -}; + return 0u; + } -struct FindAddressMatchSample -{ - uint32_t addr = 0u; - uint32_t value = 0u; - bool aliasOnly = false; -}; - -static void logFindAddressDiagnostics(uint32_t callerPc, - uint32_t originalStart, - uint32_t originalEnd, - uint32_t alignedStart, - uint32_t alignedEnd, - uint32_t target, - uint32_t targetNorm, - bool found, - uint32_t resultAddr, - uint32_t scannedWords, - bool allZero, - bool aborted, - uint32_t abortedAddr, - const FindAddressWordSample *firstWords, - uint32_t firstWordCount, - const FindAddressWordSample *nonZeroWords, - uint32_t nonZeroWordCount, - const FindAddressMatchSample *matches, - uint32_t matchCount) -{ - static std::atomic s_findAddressHitLogs{0u}; - static std::atomic s_findAddressMissLogs{0u}; - constexpr uint32_t kMaxFindAddressHitLogs = 16u; - constexpr uint32_t kMaxFindAddressMissLogs = 128u; + struct FindAddressWordSample + { + uint32_t addr = 0u; + uint32_t value = 0u; + }; + + struct FindAddressMatchSample + { + uint32_t addr = 0u; + uint32_t value = 0u; + bool aliasOnly = false; + }; + + static void logFindAddressDiagnostics(uint32_t callerPc, + uint32_t originalStart, + uint32_t originalEnd, + uint32_t alignedStart, + uint32_t alignedEnd, + uint32_t target, + uint32_t targetNorm, + bool found, + uint32_t resultAddr, + uint32_t scannedWords, + bool allZero, + bool aborted, + uint32_t abortedAddr, + const FindAddressWordSample *firstWords, + uint32_t firstWordCount, + const FindAddressWordSample *nonZeroWords, + uint32_t nonZeroWordCount, + const FindAddressMatchSample *matches, + uint32_t matchCount) + { + static std::atomic s_findAddressHitLogs{0u}; + static std::atomic s_findAddressMissLogs{0u}; + constexpr uint32_t kMaxFindAddressHitLogs = 16u; + constexpr uint32_t kMaxFindAddressMissLogs = 128u; + + std::atomic &counter = found ? s_findAddressHitLogs : s_findAddressMissLogs; + const uint32_t logIndex = counter.fetch_add(1u, std::memory_order_relaxed); + const uint32_t logLimit = found ? kMaxFindAddressHitLogs : kMaxFindAddressMissLogs; + if (logIndex >= logLimit) + { + return; + } - std::atomic &counter = found ? s_findAddressHitLogs : s_findAddressMissLogs; - const uint32_t logIndex = counter.fetch_add(1u, std::memory_order_relaxed); - const uint32_t logLimit = found ? kMaxFindAddressHitLogs : kMaxFindAddressMissLogs; - if (logIndex >= logLimit) - { - return; - } + std::cerr << "[FindAddress:" << (found ? "hit" : "miss") << "]" + << " pc=0x" << std::hex << callerPc + << " start=0x" << originalStart + << " end=0x" << originalEnd + << " alignedStart=0x" << alignedStart + << " alignedEnd=0x" << alignedEnd + << " target=0x" << target + << " targetNorm=0x" << targetNorm + << " result=0x" << resultAddr + << std::dec + << " scannedWords=" << scannedWords + << " allZero=" << (allZero ? "true" : "false") + << " aborted=" << (aborted ? "true" : "false"); + if (aborted) + { + std::cerr << " abortedAddr=0x" << std::hex << abortedAddr << std::dec; + } + std::cerr << std::endl; - std::cerr << "[FindAddress:" << (found ? "hit" : "miss") << "]" - << " pc=0x" << std::hex << callerPc - << " start=0x" << originalStart - << " end=0x" << originalEnd - << " alignedStart=0x" << alignedStart - << " alignedEnd=0x" << alignedEnd - << " target=0x" << target - << " targetNorm=0x" << targetNorm - << " result=0x" << resultAddr - << std::dec - << " scannedWords=" << scannedWords - << " allZero=" << (allZero ? "true" : "false") - << " aborted=" << (aborted ? "true" : "false"); - if (aborted) - { - std::cerr << " abortedAddr=0x" << std::hex << abortedAddr << std::dec; - } - std::cerr << std::endl; + std::cerr << " firstWords:"; + if (firstWordCount == 0u) + { + std::cerr << " none"; + } + else + { + for (uint32_t i = 0; i < firstWordCount; ++i) + { + std::cerr << " [0x" << std::hex << firstWords[i].addr + << "]=0x" << firstWords[i].value; + } + std::cerr << std::dec; + } + std::cerr << std::endl; - std::cerr << " firstWords:"; - if (firstWordCount == 0u) - { - std::cerr << " none"; - } - else - { - for (uint32_t i = 0; i < firstWordCount; ++i) + std::cerr << " nonZeroSample:"; + if (nonZeroWordCount == 0u) { - std::cerr << " [0x" << std::hex << firstWords[i].addr - << "]=0x" << firstWords[i].value; + std::cerr << " none"; } - std::cerr << std::dec; - } - std::cerr << std::endl; + else + { + for (uint32_t i = 0; i < nonZeroWordCount; ++i) + { + std::cerr << " [0x" << std::hex << nonZeroWords[i].addr + << "]=0x" << nonZeroWords[i].value; + } + std::cerr << std::dec; + } + std::cerr << std::endl; - std::cerr << " nonZeroSample:"; - if (nonZeroWordCount == 0u) - { - std::cerr << " none"; - } - else - { - for (uint32_t i = 0; i < nonZeroWordCount; ++i) + std::cerr << " matches:"; + if (matchCount == 0u) { - std::cerr << " [0x" << std::hex << nonZeroWords[i].addr - << "]=0x" << nonZeroWords[i].value; + std::cerr << " none"; } - std::cerr << std::dec; + else + { + for (uint32_t i = 0; i < matchCount; ++i) + { + std::cerr << " [0x" << std::hex << matches[i].addr + << "]=0x" << matches[i].value + << (matches[i].aliasOnly ? "(alias)" : "(exact)"); + } + std::cerr << std::dec; + } + std::cerr << std::endl; } - std::cerr << std::endl; - std::cerr << " matches:"; - if (matchCount == 0u) - { - std::cerr << " none"; - } - else + // 0x83 FindAddress: + // - a0: table start (inclusive) + // - a1: table end (exclusive) + // - a2: target address to locate inside the table (word entries) + // Returns the guest address of the matching word entry, or 0 if not found. + void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - for (uint32_t i = 0; i < matchCount; ++i) + (void)runtime; + + constexpr uint32_t kFindAddressWordSamples = 8u; + constexpr uint32_t kFindAddressMatchSamples = 4u; + + const uint32_t originalStart = getRegU32(ctx, 4); + const uint32_t originalEnd = getRegU32(ctx, 5); + const uint32_t target = getRegU32(ctx, 6); + const uint32_t targetNorm = normalizeKernelAlias(target); + const uint32_t callerPc = ctx->pc; + + uint32_t start = originalStart; + uint32_t end = originalEnd; + + // Word-scan semantics: align the search window to uint32 boundaries. + start = (start + 3u) & ~0x3u; + end &= ~0x3u; + + if (start >= end) { - std::cerr << " [0x" << std::hex << matches[i].addr - << "]=0x" << matches[i].value - << (matches[i].aliasOnly ? "(alias)" : "(exact)"); + logFindAddressDiagnostics(callerPc, + originalStart, + originalEnd, + start, + end, + target, + targetNorm, + false, + 0u, + 0u, + true, + false, + 0u, + nullptr, + 0u, + nullptr, + 0u, + nullptr, + 0u); + setReturnU32(ctx, 0u); + return; } - std::cerr << std::dec; - } - std::cerr << std::endl; -} -// 0x83 FindAddress: -// - a0: table start (inclusive) -// - a1: table end (exclusive) -// - a2: target address to locate inside the table (word entries) -// Returns the guest address of the matching word entry, or 0 if not found. -void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - (void)runtime; + FindAddressWordSample firstWords[kFindAddressWordSamples]{}; + FindAddressWordSample nonZeroWords[kFindAddressWordSamples]{}; + FindAddressMatchSample matches[kFindAddressMatchSamples]{}; + uint32_t firstWordCount = 0u; + uint32_t nonZeroWordCount = 0u; + uint32_t matchCount = 0u; + uint32_t scannedWords = 0u; + uint32_t resultAddr = 0u; + uint32_t abortedAddr = 0u; + bool aborted = false; + bool allZero = true; + bool foundMatch = false; + + for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t)) + { + const uint8_t *entryPtr = getConstMemPtr(rdram, addr); + if (!entryPtr) + { + aborted = true; + abortedAddr = addr; + break; + } - constexpr uint32_t kFindAddressWordSamples = 8u; - constexpr uint32_t kFindAddressMatchSamples = 4u; + uint32_t entry = 0; + std::memcpy(&entry, entryPtr, sizeof(entry)); + ++scannedWords; - const uint32_t originalStart = getRegU32(ctx, 4); - const uint32_t originalEnd = getRegU32(ctx, 5); - const uint32_t target = getRegU32(ctx, 6); - const uint32_t targetNorm = normalizeKernelAlias(target); - const uint32_t callerPc = ctx->pc; + if (firstWordCount < kFindAddressWordSamples) + { + firstWords[firstWordCount++] = {addr, entry}; + } - uint32_t start = originalStart; - uint32_t end = originalEnd; + if (entry != 0u) + { + allZero = false; + if (nonZeroWordCount < kFindAddressWordSamples) + { + nonZeroWords[nonZeroWordCount++] = {addr, entry}; + } + } - // Word-scan semantics: align the search window to uint32 boundaries. - start = (start + 3u) & ~0x3u; - end &= ~0x3u; + const bool exactMatch = (entry == target); + const bool aliasMatch = !exactMatch && (normalizeKernelAlias(entry) == targetNorm); + if (exactMatch || aliasMatch) + { + if (!foundMatch) + { + resultAddr = addr; + foundMatch = true; + } + if (matchCount < kFindAddressMatchSamples) + { + matches[matchCount++] = {addr, entry, aliasMatch}; + } + } + } - if (start >= end) - { logFindAddressDiagnostics(callerPc, originalStart, originalEnd, @@ -801,159 +881,79 @@ void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) end, target, targetNorm, - false, - 0u, - 0u, - true, - false, - 0u, - nullptr, - 0u, - nullptr, - 0u, - nullptr, - 0u); - setReturnU32(ctx, 0u); - return; - } - - FindAddressWordSample firstWords[kFindAddressWordSamples]{}; - FindAddressWordSample nonZeroWords[kFindAddressWordSamples]{}; - FindAddressMatchSample matches[kFindAddressMatchSamples]{}; - uint32_t firstWordCount = 0u; - uint32_t nonZeroWordCount = 0u; - uint32_t matchCount = 0u; - uint32_t scannedWords = 0u; - uint32_t resultAddr = 0u; - uint32_t abortedAddr = 0u; - bool aborted = false; - bool allZero = true; - bool foundMatch = false; - - for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t)) - { - const uint8_t *entryPtr = getConstMemPtr(rdram, addr); - if (!entryPtr) - { - aborted = true; - abortedAddr = addr; - break; - } - - uint32_t entry = 0; - std::memcpy(&entry, entryPtr, sizeof(entry)); - ++scannedWords; - - if (firstWordCount < kFindAddressWordSamples) - { - firstWords[firstWordCount++] = {addr, entry}; - } - - if (entry != 0u) - { - allZero = false; - if (nonZeroWordCount < kFindAddressWordSamples) - { - nonZeroWords[nonZeroWordCount++] = {addr, entry}; - } - } + foundMatch, + resultAddr, + scannedWords, + allZero, + aborted, + abortedAddr, + firstWords, + firstWordCount, + nonZeroWords, + nonZeroWordCount, + matches, + matchCount); - const bool exactMatch = (entry == target); - const bool aliasMatch = !exactMatch && (normalizeKernelAlias(entry) == targetNorm); - if (exactMatch || aliasMatch) - { - if (!foundMatch) - { - resultAddr = addr; - foundMatch = true; - } - if (matchCount < kFindAddressMatchSamples) - { - matches[matchCount++] = {addr, entry, aliasMatch}; - } - } + setReturnU32(ctx, resultAddr); } - logFindAddressDiagnostics(callerPc, - originalStart, - originalEnd, - start, - end, - target, - targetNorm, - foundMatch, - resultAddr, - scannedWords, - allZero, - aborted, - abortedAddr, - firstWords, - firstWordCount, - nonZeroWords, - nonZeroWordCount, - matches, - matchCount); - - 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 -void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t mode = getRegU32(ctx, 4); - ensureBootModeTable(rdram); - uint32_t addr = 0; + void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_bootmode_mutex); - auto it = g_bootmode_addresses.find(static_cast(mode)); - if (it != g_bootmode_addresses.end()) - addr = it->second; + (void)rdram; + (void)runtime; + setReturnS32(ctx, KE_OK); } - setReturnU32(ctx, addr); -} -// 0x5B GetThreadTLS (stub): return 0 -void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - auto info = ensureCurrentThreadInfo(ctx); - if (!info) + // 0x5A QueryBootMode (stub): return 0 for now + void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnU32(ctx, 0); - return; + uint32_t mode = getRegU32(ctx, 4); + ensureBootModeTable(rdram); + uint32_t addr = 0; + { + std::lock_guard lock(g_bootmode_mutex); + auto it = g_bootmode_addresses.find(static_cast(mode)); + if (it != g_bootmode_addresses.end()) + addr = it->second; + } + setReturnU32(ctx, addr); } - if (info->tlsBase == 0) + // 0x5B GetThreadTLS (stub): return 0 + void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->tlsBase = allocTlsAddr(rdram); - } + auto info = ensureCurrentThreadInfo(ctx); + if (!info) + { + setReturnU32(ctx, 0); + return; + } - setReturnU32(ctx, info->tlsBase); -} + if (info->tlsBase == 0) + { + info->tlsBase = allocTlsAddr(rdram); + } -// 0x74 RegisterExitHandler (stub): return 0 -void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t func = getRegU32(ctx, 4); - uint32_t arg = getRegU32(ctx, 5); - if (func == 0) - { - setReturnS32(ctx, -1); - return; + setReturnU32(ctx, info->tlsBase); } - int tid = g_currentThreadId; + // 0x74 RegisterExitHandler (stub): return 0 + void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_exit_handler_mutex); - g_exit_handlers[tid].push_back({func, arg}); - } + uint32_t func = getRegU32(ctx, 4); + uint32_t arg = getRegU32(ctx, 5); + if (func == 0) + { + setReturnS32(ctx, -1); + return; + } - setReturnS32(ctx, 0); -} + int tid = g_currentThreadId; + { + std::lock_guard lock(g_exit_handler_mutex); + g_exit_handlers[tid].push_back({func, arg}); + } + + setReturnS32(ctx, 0); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp index cf410b81..e024063c 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Thread.cpp @@ -3,334 +3,334 @@ namespace ps2_syscalls { -static void applySuspendStatusLocked(ThreadInfo &info) -{ - if (info.waitType != TSW_NONE) - { - info.status = THS_WAITSUSPEND; - } - else + static void applySuspendStatusLocked(ThreadInfo &info) { - info.status = THS_SUSPEND; + if (info.waitType != TSW_NONE) + { + info.status = THS_WAITSUSPEND; + } + else + { + info.status = THS_SUSPEND; + } } -} - -static void runExitHandlersForThread(int tid, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - if (!runtime || !ctx) - return; - std::vector handlers; + static void runExitHandlersForThread(int tid, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_exit_handler_mutex); - auto it = g_exit_handlers.find(tid); - if (it == g_exit_handlers.end()) + if (!runtime || !ctx) return; - handlers = std::move(it->second); - g_exit_handlers.erase(it); - } - for (const auto &handler : handlers) - { - if (!handler.func) - continue; - try - { - rpcInvokeFunction(rdram, ctx, runtime, handler.func, handler.arg, 0, 0, 0, nullptr); - } - catch (const ThreadExitException &) + std::vector handlers; { - // ignore + std::lock_guard lock(g_exit_handler_mutex); + auto it = g_exit_handlers.find(tid); + if (it == g_exit_handlers.end()) + return; + handlers = std::move(it->second); + g_exit_handlers.erase(it); } - catch (const std::exception &) + + for (const auto &handler : handlers) { + if (!handler.func) + continue; + try + { + rpcInvokeFunction(rdram, ctx, runtime, handler.func, handler.arg, 0, 0, 0, nullptr); + } + catch (const ThreadExitException &) + { + // ignore + } + catch (const std::exception &) + { + } } } -} - -void FlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, KE_OK); -} - -void iFlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - FlushCache(rdram, ctx, runtime); -} - -void EnableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, KE_OK); -} - -void DisableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, KE_OK); -} - -void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - std::cerr << "Syscall: ResetEE - requesting runtime stop" << std::endl; - // runtime->requestStop(); - setReturnS32(ctx, KE_OK); -} - -void SetMemoryMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, KE_OK); -} - -void InitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - // This is a common ps2sdk helper that some games link against. - setReturnS32(ctx, 1); -} -void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 points to ThreadParam - if (paramAddr == 0u) + void FlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "CreateThread error: null ThreadParam pointer" << std::endl; - setReturnS32(ctx, KE_ERROR); - return; + setReturnS32(ctx, KE_OK); } - const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); - - if (!param) + void iFlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::cerr << "CreateThread error: invalid ThreadParam address 0x" << std::hex << paramAddr << std::dec << std::endl; - setReturnS32(ctx, KE_ERROR); - return; + FlushCache(rdram, ctx, runtime); } - auto info = std::make_shared(); - info->attr = param[0]; - info->entry = param[1]; - info->stack = param[2]; - info->stackSize = param[3]; - - auto looksLikeGuestPtr = [](uint32_t v) -> bool + void EnableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - if (v == 0) - { - return true; - } - const uint32_t norm = v & 0x1FFFFFFFu; - return norm < PS2_RAM_SIZE && norm >= 0x10000u; - }; - - auto looksLikePriority = [](uint32_t v) -> bool - { - // Typical EE priorities are very small integers (1..127). - return v <= 0x400u; - }; - - const uint32_t gpA = param[4]; - const uint32_t prioA = param[5]; - const uint32_t gpB = param[5]; - const uint32_t prioB = param[4]; - - // Prefer the standard EE layout (gp at +0x10, priority at +0x14), - // but keep a fallback for callsites that used the swapped decode. - if (looksLikeGuestPtr(gpA) && looksLikePriority(prioA)) - { - info->gp = gpA; - info->priority = prioA; + setReturnS32(ctx, KE_OK); } - else if (looksLikeGuestPtr(gpB) && looksLikePriority(prioB)) + + void DisableCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->gp = gpB; - info->priority = prioB; + setReturnS32(ctx, KE_OK); } - else + + void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->gp = gpA; - info->priority = prioA; + std::cerr << "Syscall: ResetEE - requesting runtime stop" << std::endl; + // runtime->requestStop(); + setReturnS32(ctx, KE_OK); } - info->option = param[6]; - if (info->priority == 0) + void SetMemoryMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->priority = 1; + setReturnS32(ctx, KE_OK); } - if (info->priority >= 128) + + void InitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->priority = 127; + // This is a common ps2sdk helper that some games link against. + setReturnS32(ctx, 1); } - info->currentPriority = static_cast(info->priority); - int id = 0; + void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(g_thread_map_mutex); - // Keep IDs in the classic low range used by patched libkernel helpers. - for (int attempts = 0; attempts < 0xFE; ++attempts) + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 points to ThreadParam + if (paramAddr == 0u) { - if (g_nextThreadId < 2 || g_nextThreadId > 0xFF) - { - g_nextThreadId = 2; - } + std::cerr << "CreateThread error: null ThreadParam pointer" << std::endl; + setReturnS32(ctx, KE_ERROR); + return; + } + + const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); - const int candidate = g_nextThreadId; - g_nextThreadId = (g_nextThreadId >= 0xFF) ? 2 : (g_nextThreadId + 1); + if (!param) + { + std::cerr << "CreateThread error: invalid ThreadParam address 0x" << std::hex << paramAddr << std::dec << std::endl; + setReturnS32(ctx, KE_ERROR); + return; + } - if (g_threads.find(candidate) == g_threads.end()) + auto info = std::make_shared(); + info->attr = param[0]; + info->entry = param[1]; + info->stack = param[2]; + info->stackSize = param[3]; + + auto looksLikeGuestPtr = [](uint32_t v) -> bool + { + if (v == 0) { - id = candidate; - break; + return true; } + const uint32_t norm = v & 0x1FFFFFFFu; + return norm < PS2_RAM_SIZE && norm >= 0x10000u; + }; + + auto looksLikePriority = [](uint32_t v) -> bool + { + // Typical EE priorities are very small integers (1..127). + return v <= 0x400u; + }; + + const uint32_t gpA = param[4]; + const uint32_t prioA = param[5]; + const uint32_t gpB = param[5]; + const uint32_t prioB = param[4]; + + // Prefer the standard EE layout (gp at +0x10, priority at +0x14), + // but keep a fallback for callsites that used the swapped decode. + if (looksLikeGuestPtr(gpA) && looksLikePriority(prioA)) + { + info->gp = gpA; + info->priority = prioA; + } + else if (looksLikeGuestPtr(gpB) && looksLikePriority(prioB)) + { + info->gp = gpB; + info->priority = prioB; + } + else + { + info->gp = gpA; + info->priority = prioA; } - if (id == 0) + info->option = param[6]; + if (info->priority == 0) { - setReturnS32(ctx, KE_ERROR); - return; + info->priority = 1; + } + if (info->priority >= 128) + { + info->priority = 127; } + info->currentPriority = static_cast(info->priority); - g_threads[id] = info; - } + int id = 0; + { + std::lock_guard lock(g_thread_map_mutex); + // Keep IDs in the classic low range used by patched libkernel helpers. + for (int attempts = 0; attempts < 0xFE; ++attempts) + { + if (g_nextThreadId < 2 || g_nextThreadId > 0xFF) + { + g_nextThreadId = 2; + } - RUNTIME_LOG("[CreateThread] id=" << id - << " entry=0x" << std::hex << info->entry - << " stack=0x" << info->stack - << " size=0x" << info->stackSize - << " gp=0x" << info->gp - << " prio=" << std::dec << info->priority << std::endl); + const int candidate = g_nextThreadId; + g_nextThreadId = (g_nextThreadId >= 0xFF) ? 2 : (g_nextThreadId + 1); - setReturnS32(ctx, id); -} + if (g_threads.find(candidate) == g_threads.end()) + { + id = candidate; + break; + } + } -void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); // $a0 - if (tid == 0) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } + if (id == 0) + { + setReturnS32(ctx, KE_ERROR); + return; + } - auto info = lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + g_threads[id] = info; + } + + RUNTIME_LOG("[CreateThread] id=" << id + << " entry=0x" << std::hex << info->entry + << " stack=0x" << info->stack + << " size=0x" << info->stackSize + << " gp=0x" << info->gp + << " prio=" << std::dec << info->priority << std::endl); + + setReturnS32(ctx, id); } - uint32_t autoStackToFree = 0; + void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - if (info->started || info->status != THS_DORMANT) + int tid = static_cast(getRegU32(ctx, 4)); // $a0 + if (tid == 0) { - setReturnS32(ctx, KE_NOT_DORMANT); + setReturnS32(ctx, KE_ILLEGAL_THID); return; } - if (info->ownsStack && info->stack != 0) + auto info = lookupThreadInfo(tid); + if (!info) { - autoStackToFree = info->stack; - info->stack = 0; - info->stackSize = 0; - info->ownsStack = false; + setReturnS32(ctx, KE_UNKNOWN_THID); + return; } - } - { - std::lock_guard lock(g_thread_map_mutex); - g_threads.erase(tid); - } + uint32_t autoStackToFree = 0; + { + std::lock_guard lock(info->m); + if (info->started || info->status != THS_DORMANT) + { + setReturnS32(ctx, KE_NOT_DORMANT); + return; + } - { - std::lock_guard lock(g_exit_handler_mutex); - g_exit_handlers.erase(tid); - } + if (info->ownsStack && info->stack != 0) + { + autoStackToFree = info->stack; + info->stack = 0; + info->stackSize = 0; + info->ownsStack = false; + } + } - if (runtime && autoStackToFree != 0) - { - runtime->guestFree(autoStackToFree); - } + { + std::lock_guard lock(g_thread_map_mutex); + g_threads.erase(tid); + } - setReturnS32(ctx, KE_OK); -} + { + std::lock_guard lock(g_exit_handler_mutex); + g_exit_handlers.erase(tid); + } -void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); // $a0 = thread id - uint32_t arg = getRegU32(ctx, 5); // $a1 = user arg - if (tid == 0) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } + if (runtime && autoStackToFree != 0) + { + runtime->guestFree(autoStackToFree); + } - auto info = lookupThreadInfo(tid); - if (!info) - { - std::cerr << "StartThread error: unknown thread id " << tid << std::endl; - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + setReturnS32(ctx, KE_OK); } - if (!runtime || !runtime->hasFunction(info->entry)) - { - std::cerr << "[StartThread] entry 0x" << std::hex << info->entry << std::dec << " is not registered" << std::endl; - setReturnS32(ctx, KE_ERROR); - return; - } - if (runtime->isStopRequested()) + void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_ERROR); - return; - } - - joinHostThreadById(tid); + int tid = static_cast(getRegU32(ctx, 4)); // $a0 = thread id + uint32_t arg = getRegU32(ctx, 5); // $a1 = user arg + if (tid == 0) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } - const uint32_t callerSp = getRegU32(ctx, 29); - const uint32_t callerGp = getRegU32(ctx, 28); + auto info = lookupThreadInfo(tid); + if (!info) + { + std::cerr << "StartThread error: unknown thread id " << tid << std::endl; + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } - { - std::lock_guard lock(info->m); - if (info->started || info->status != THS_DORMANT) + if (!runtime || !runtime->hasFunction(info->entry)) + { + std::cerr << "[StartThread] entry 0x" << std::hex << info->entry << std::dec << " is not registered" << std::endl; + setReturnS32(ctx, KE_ERROR); + return; + } + if (runtime->isStopRequested()) { - setReturnS32(ctx, KE_NOT_DORMANT); + setReturnS32(ctx, KE_ERROR); return; } - info->started = true; - info->status = THS_READY; - info->arg = arg; - info->terminated = false; - info->forceRelease = false; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - info->suspendCount = 0; - if (info->stack == 0 && info->stackSize != 0) + joinHostThreadById(tid); + + const uint32_t callerSp = getRegU32(ctx, 29); + const uint32_t callerGp = getRegU32(ctx, 28); + { - const uint32_t autoStack = runtime->guestMalloc(info->stackSize, 16u); - if (autoStack != 0) + std::lock_guard lock(info->m); + if (info->started || info->status != THS_DORMANT) { - info->stack = autoStack; - info->ownsStack = true; - RUNTIME_LOG("[StartThread] id=" << tid - << " auto-stack=0x" << std::hex << autoStack - << " size=0x" << info->stackSize << std::dec << std::endl); + setReturnS32(ctx, KE_NOT_DORMANT); + return; } - } - if (info->stack != 0 && info->stackSize == 0) - { - // Some games leave size zero in the thread param even though a stack - // buffer is supplied; use a conservative default instead of caller SP. - info->stackSize = 0x800u; + info->started = true; + info->status = THS_READY; + info->arg = arg; + info->terminated = false; + info->forceRelease = false; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; + info->suspendCount = 0; + if (info->stack == 0 && info->stackSize != 0) + { + const uint32_t autoStack = runtime->guestMalloc(info->stackSize, 16u); + if (autoStack != 0) + { + info->stack = autoStack; + info->ownsStack = true; + RUNTIME_LOG("[StartThread] id=" << tid + << " auto-stack=0x" << std::hex << autoStack + << " size=0x" << info->stackSize << std::dec << std::endl); + } + } + + if (info->stack != 0 && info->stackSize == 0) + { + // Some games leave size zero in the thread param even though a stack + // buffer is supplied; use a conservative default instead of caller SP. + info->stackSize = 0x800u; + } } - } - g_activeThreads.fetch_add(1, std::memory_order_relaxed); - try - { - std::thread worker([=]() mutable - { + g_activeThreads.fetch_add(1, std::memory_order_relaxed); + try + { + std::thread worker([=]() mutable + { { std::string name = "PS2Thread_" + std::to_string(tid); ThreadNaming::SetCurrentThreadName(name); @@ -496,600 +496,600 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) info->cv.notify_all(); g_activeThreads.fetch_sub(1, std::memory_order_relaxed); }); - registerHostThread(tid, std::move(worker)); - } - catch (const std::exception &e) - { - std::cerr << "[StartThread] failed to spawn host thread for tid=" << tid << ": " << e.what() << std::endl; - g_activeThreads.fetch_sub(1, std::memory_order_relaxed); - std::lock_guard lock(info->m); - info->started = false; - info->status = THS_DORMANT; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - info->suspendCount = 0; - info->forceRelease = false; - info->terminated = false; - setReturnS32(ctx, KE_ERROR); - return; - } - - setReturnS32(ctx, KE_OK); -} - -void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - runExitHandlersForThread(g_currentThreadId, rdram, ctx, runtime); - auto info = ensureCurrentThreadInfo(ctx); - if (info) - { - std::lock_guard lock(info->m); - info->terminated = true; - info->forceRelease = true; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - } - if (info) - { - info->cv.notify_all(); - } - throw ThreadExitException(); -} - -void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = g_currentThreadId; - runExitHandlersForThread(tid, rdram, ctx, runtime); - auto info = ensureCurrentThreadInfo(ctx); - if (info) - { - std::lock_guard lock(info->m); - info->terminated = true; - info->forceRelease = true; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - } - if (info) - { - info->cv.notify_all(); - } - { - std::lock_guard lock(g_thread_map_mutex); - g_threads.erase(tid); - } - throw ThreadExitException(); -} - -void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; + registerHostThread(tid, std::move(worker)); + } + catch (const std::exception &e) + { + std::cerr << "[StartThread] failed to spawn host thread for tid=" << tid << ": " << e.what() << std::endl; + g_activeThreads.fetch_sub(1, std::memory_order_relaxed); + std::lock_guard lock(info->m); + info->started = false; + info->status = THS_DORMANT; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; + info->suspendCount = 0; + info->forceRelease = false; + info->terminated = false; + setReturnS32(ctx, KE_ERROR); + return; + } - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + setReturnS32(ctx, KE_OK); } + void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) + runExitHandlersForThread(g_currentThreadId, rdram, ctx, runtime); + auto info = ensureCurrentThreadInfo(ctx); + if (info) { - setReturnS32(ctx, KE_DORMANT); - return; + std::lock_guard lock(info->m); + info->terminated = true; + info->forceRelease = true; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; } - info->terminated = true; - info->forceRelease = true; + if (info) + { + info->cv.notify_all(); + } + throw ThreadExitException(); } - info->cv.notify_all(); - if (tid == g_currentThreadId) + void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + int tid = g_currentThreadId; runExitHandlersForThread(tid, rdram, ctx, runtime); - throw ThreadExitException(); - } - else - { - // Block until the target thread actually finishes unwinding and becomes dormant - std::unique_lock lock(info->m); + auto info = ensureCurrentThreadInfo(ctx); + if (info) + { + std::lock_guard lock(info->m); + info->terminated = true; + info->forceRelease = true; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; + } + if (info) { - PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); - info->cv.wait(lock, [&]() - { return !info->started && info->status == THS_DORMANT; }); + info->cv.notify_all(); } + { + std::lock_guard lock(g_thread_map_mutex); + g_threads.erase(tid); + } + throw ThreadExitException(); } - setReturnS32(ctx, KE_OK); -} - -void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) + void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; - { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) { - setReturnS32(ctx, KE_DORMANT); + setReturnS32(ctx, KE_UNKNOWN_THID); return; } - info->suspendCount++; - applySuspendStatusLocked(*info); - } - info->cv.notify_all(); - if (tid == g_currentThreadId) - { - std::unique_lock lock(info->m); { - PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); - info->cv.wait(lock, [&]() - { return info->suspendCount == 0 || info->terminated.load(); }); + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, KE_DORMANT); + return; + } + info->terminated = true; + info->forceRelease = true; } - if (info->terminated.load()) + info->cv.notify_all(); + + if (tid == g_currentThreadId) { + runExitHandlersForThread(tid, rdram, ctx, runtime); throw ThreadExitException(); } - info->status = THS_RUN; - } - - setReturnS32(ctx, KE_OK); -} - -void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; + else + { + // Block until the target thread actually finishes unwinding and becomes dormant + std::unique_lock lock(info->m); + { + PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); + info->cv.wait(lock, [&]() + { return !info->started && info->status == THS_DORMANT; }); + } + } - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + setReturnS32(ctx, KE_OK); } + void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) { - setReturnS32(ctx, KE_DORMANT); + setReturnS32(ctx, KE_UNKNOWN_THID); return; } - if (info->suspendCount <= 0) + { - setReturnS32(ctx, KE_NOT_SUSPEND); - return; + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, KE_DORMANT); + return; + } + info->suspendCount++; + applySuspendStatusLocked(*info); } - info->suspendCount--; - if (info->suspendCount == 0) + info->cv.notify_all(); + + if (tid == g_currentThreadId) { - if (info->waitType != TSW_NONE) + std::unique_lock lock(info->m); { - info->status = THS_WAIT; + PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); + info->cv.wait(lock, [&]() + { return info->suspendCount == 0 || info->terminated.load(); }); } - else + if (info->terminated.load()) { - info->status = (tid == g_currentThreadId) ? THS_RUN : THS_READY; + throw ThreadExitException(); } + info->status = THS_RUN; } + + setReturnS32(ctx, KE_OK); } - info->cv.notify_all(); - setReturnS32(ctx, KE_OK); -} -void GetThreadId(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - setReturnS32(ctx, g_currentThreadId); -} + void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; -void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - uint32_t statusAddr = getRegU32(ctx, 5); + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } - if (tid == 0) // TH_SELF - { - tid = g_currentThreadId; + { + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, KE_DORMANT); + return; + } + if (info->suspendCount <= 0) + { + setReturnS32(ctx, KE_NOT_SUSPEND); + return; + } + info->suspendCount--; + if (info->suspendCount == 0) + { + if (info->waitType != TSW_NONE) + { + info->status = THS_WAIT; + } + else + { + info->status = (tid == g_currentThreadId) ? THS_RUN : THS_READY; + } + } + } + info->cv.notify_all(); + setReturnS32(ctx, KE_OK); } - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) + void GetThreadId(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + setReturnS32(ctx, g_currentThreadId); } - ee_thread_status_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); - if (!status) + void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_ERROR); - return; - } - - std::lock_guard lock(info->m); - status->status = info->status; - status->func = info->entry; - status->stack = info->stack; - status->stack_size = info->stackSize; - status->gp_reg = info->gp; - status->initial_priority = info->priority; - status->current_priority = info->currentPriority; - status->attr = info->attr; - status->option = info->option; - status->waitType = info->waitType; - status->waitId = info->waitId; - status->wakeupCount = info->wakeupCount; - setReturnS32(ctx, KE_OK); -} + int tid = static_cast(getRegU32(ctx, 4)); + uint32_t statusAddr = getRegU32(ctx, 5); -void iReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ReferThreadStatus(rdram, ctx, runtime); -} + if (tid == 0) // TH_SELF + { + tid = g_currentThreadId; + } -void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - auto info = ensureCurrentThreadInfo(ctx); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } - throwIfTerminated(info); + ee_thread_status_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); + if (!status) + { + setReturnS32(ctx, KE_ERROR); + return; + } - int ret = 0; - std::unique_lock lock(info->m); + std::lock_guard lock(info->m); + status->status = info->status; + status->func = info->entry; + status->stack = info->stack; + status->stack_size = info->stackSize; + status->gp_reg = info->gp; + status->initial_priority = info->priority; + status->current_priority = info->currentPriority; + status->attr = info->attr; + status->option = info->option; + status->waitType = info->waitType; + status->waitId = info->waitId; + status->wakeupCount = info->wakeupCount; + setReturnS32(ctx, KE_OK); + } - if (info->wakeupCount > 0) + void iReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - info->wakeupCount--; - info->status = THS_RUN; - info->waitType = TSW_NONE; - info->waitId = 0; - ret = 0; + ReferThreadStatus(rdram, ctx, runtime); } - else + + void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - static std::atomic s_sleepBlockLogs{0}; - const uint32_t sleepBlockLog = s_sleepBlockLogs.fetch_add(1, std::memory_order_relaxed); - if (sleepBlockLog < 256u) + auto info = ensureCurrentThreadInfo(ctx); + if (!info) { - RUNTIME_LOG("[SleepThread:block] tid=" << g_currentThreadId - << " pc=0x" << std::hex << ctx->pc - << " ra=0x" << getRegU32(ctx, 31) - << std::dec << std::endl); + setReturnS32(ctx, KE_UNKNOWN_THID); + return; } - info->status = THS_WAIT; - info->waitType = TSW_SLEEP; - info->waitId = 0; - info->forceRelease = false; + throwIfTerminated(info); - { - PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); - info->cv.wait(lock, [&]() - { return info->wakeupCount > 0 || info->forceRelease.load() || info->terminated.load(); }); - } + int ret = 0; + std::unique_lock lock(info->m); - if (info->terminated.load()) + if (info->wakeupCount > 0) { - throw ThreadExitException(); + info->wakeupCount--; + info->status = THS_RUN; + info->waitType = TSW_NONE; + info->waitId = 0; + ret = 0; } - - info->status = THS_RUN; - info->waitType = TSW_NONE; - info->waitId = 0; - - if (info->forceRelease.load()) + else { + static std::atomic s_sleepBlockLogs{0}; + const uint32_t sleepBlockLog = s_sleepBlockLogs.fetch_add(1, std::memory_order_relaxed); + if (sleepBlockLog < 256u) + { + RUNTIME_LOG("[SleepThread:block] tid=" << g_currentThreadId + << " pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << std::dec << std::endl); + } + + info->status = THS_WAIT; + info->waitType = TSW_SLEEP; + info->waitId = 0; info->forceRelease = false; - ret = KE_RELEASE_WAIT; + + { + PS2Runtime::GuestExecutionReleaseScope releaseGuestExecution(runtime); + info->cv.wait(lock, [&]() + { return info->wakeupCount > 0 || info->forceRelease.load() || info->terminated.load(); }); + } + + if (info->terminated.load()) + { + throw ThreadExitException(); + } + + info->status = THS_RUN; + info->waitType = TSW_NONE; + info->waitId = 0; + + if (info->forceRelease.load()) + { + info->forceRelease = false; + ret = KE_RELEASE_WAIT; + } + else + { + if (info->wakeupCount > 0) + info->wakeupCount--; + ret = 0; + } } - else + + static std::atomic s_sleepWakeLogs{0}; + const uint32_t sleepWakeLog = s_sleepWakeLogs.fetch_add(1, std::memory_order_relaxed); + if (sleepWakeLog < 256u) { - if (info->wakeupCount > 0) - info->wakeupCount--; - ret = 0; + RUNTIME_LOG("[SleepThread:wake] tid=" << g_currentThreadId + << " ret=" << ret + << " wakeupCount=" << info->wakeupCount + << std::endl); } - } - static std::atomic s_sleepWakeLogs{0}; - const uint32_t sleepWakeLog = s_sleepWakeLogs.fetch_add(1, std::memory_order_relaxed); - if (sleepWakeLog < 256u) - { - RUNTIME_LOG("[SleepThread:wake] tid=" << g_currentThreadId - << " ret=" << ret - << " wakeupCount=" << info->wakeupCount - << std::endl); + lock.unlock(); + waitWhileSuspended(info, runtime); + setReturnS32(ctx, ret); } - lock.unlock(); - waitWhileSuspended(info, runtime); - setReturnS32(ctx, ret); -} - -void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) + void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } - if (tid == g_currentThreadId) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } - - auto info = lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } + if (tid == g_currentThreadId) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } - int newWakeupCount = 0; - int statusAfter = THS_DORMANT; - { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) + auto info = lookupThreadInfo(tid); + if (!info) { - setReturnS32(ctx, KE_DORMANT); + setReturnS32(ctx, KE_UNKNOWN_THID); return; } - if (info->status == THS_WAIT && info->waitType == TSW_SLEEP) + + int newWakeupCount = 0; + int statusAfter = THS_DORMANT; { - if (info->suspendCount > 0) + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, KE_DORMANT); + return; + } + if (info->status == THS_WAIT && info->waitType == TSW_SLEEP) { - info->status = THS_SUSPEND; + if (info->suspendCount > 0) + { + info->status = THS_SUSPEND; + } + else + { + info->status = THS_READY; + } + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount++; + info->cv.notify_one(); } else { - info->status = THS_READY; + info->wakeupCount++; } - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount++; - info->cv.notify_one(); + newWakeupCount = info->wakeupCount; + statusAfter = info->status; } - else + + static std::atomic s_wakeupLogs{0}; + const uint32_t wakeupLog = s_wakeupLogs.fetch_add(1, std::memory_order_relaxed); + if (wakeupLog < 256u) { - info->wakeupCount++; + RUNTIME_LOG("[WakeupThread] tid=" << g_currentThreadId + << " target=" << tid + << " status=" << statusAfter + << " wakeupCount=" << newWakeupCount + << std::endl); } - newWakeupCount = info->wakeupCount; - statusAfter = info->status; + setReturnS32(ctx, KE_OK); } - static std::atomic s_wakeupLogs{0}; - const uint32_t wakeupLog = s_wakeupLogs.fetch_add(1, std::memory_order_relaxed); - if (wakeupLog < 256u) + void iWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - RUNTIME_LOG("[WakeupThread] tid=" << g_currentThreadId - << " target=" << tid - << " status=" << statusAfter - << " wakeupCount=" << newWakeupCount - << std::endl); + WakeupThread(rdram, ctx, runtime); } - setReturnS32(ctx, KE_OK); -} -void iWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - WakeupThread(rdram, ctx, runtime); -} + void CancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; -void CancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + int previous = 0; + { + std::lock_guard lock(info->m); + previous = info->wakeupCount; + info->wakeupCount = 0; + } + setReturnS32(ctx, previous); } - int previous = 0; + void iCancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - previous = info->wakeupCount; - info->wakeupCount = 0; - } - setReturnS32(ctx, previous); -} + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } -void iCancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } + auto info = lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } - auto info = lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + int previous = 0; + { + std::lock_guard lock(info->m); + previous = info->wakeupCount; + info->wakeupCount = 0; + } + setReturnS32(ctx, previous); } - int previous = 0; + void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - previous = info->wakeupCount; - info->wakeupCount = 0; - } - setReturnS32(ctx, previous); -} + int tid = static_cast(getRegU32(ctx, 4)); + int newPrio = static_cast(getRegU32(ctx, 5)); -void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - int newPrio = static_cast(getRegU32(ctx, 5)); + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + { + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, KE_DORMANT); + return; + } + + if (newPrio == 0) + { + newPrio = (info->currentPriority > 0) ? info->currentPriority : 1; + } + if (newPrio <= 0 || newPrio >= 128) + { + setReturnS32(ctx, KE_ILLEGAL_PRIORITY); + return; + } - if (tid == 0) - tid = g_currentThreadId; + info->currentPriority = newPrio; + } + + setReturnS32(ctx, KE_OK); + } - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) + void iChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; + ChangeThreadPriority(rdram, ctx, runtime); } + void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) + static int logCount = 0; + int prio = static_cast(getRegU32(ctx, 4)); + if (prio == 0) { - setReturnS32(ctx, KE_DORMANT); - return; + auto current = ensureCurrentThreadInfo(ctx); + if (current) + { + std::lock_guard lock(current->m); + prio = (current->currentPriority > 0) ? current->currentPriority : 1; + } } - - if (newPrio == 0) + if (logCount < 16) { - newPrio = (info->currentPriority > 0) ? info->currentPriority : 1; + RUNTIME_LOG("[RotateThreadReadyQueue] prio=" << prio); + ++logCount; } - if (newPrio <= 0 || newPrio >= 128) + if (prio <= 0 || prio >= 128) { setReturnS32(ctx, KE_ILLEGAL_PRIORITY); return; } - info->currentPriority = newPrio; - } - - setReturnS32(ctx, KE_OK); -} + std::this_thread::yield(); -void iChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ChangeThreadPriority(rdram, ctx, runtime); -} - -void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - static int logCount = 0; - int prio = static_cast(getRegU32(ctx, 4)); - if (prio == 0) - { - auto current = ensureCurrentThreadInfo(ctx); - if (current) - { - std::lock_guard lock(current->m); - prio = (current->currentPriority > 0) ? current->currentPriority : 1; - } + setReturnS32(ctx, KE_OK); } - if (logCount < 16) - { - RUNTIME_LOG("[RotateThreadReadyQueue] prio=" << prio); - ++logCount; - } - if (prio <= 0 || prio >= 128) - { - setReturnS32(ctx, KE_ILLEGAL_PRIORITY); - return; - } - - std::this_thread::yield(); - - setReturnS32(ctx, KE_OK); -} - -void iRotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - RotateThreadReadyQueue(rdram, ctx, runtime); -} -void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0 || tid == g_currentThreadId) + void iRotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; + RotateThreadReadyQueue(rdram, ctx, runtime); } - auto info = lookupThreadInfo(tid); - if (!info) + void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0 || tid == g_currentThreadId) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } - bool wasWaiting = false; - int waitType = 0; - int waitId = 0; + auto info = lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + bool wasWaiting = false; + int waitType = 0; + int waitId = 0; - { - std::lock_guard lock(info->m); - if (info->status == THS_WAIT || info->status == THS_WAITSUSPEND) { - wasWaiting = true; - waitType = info->waitType; - waitId = info->waitId; - info->forceRelease = true; - info->waitType = TSW_NONE; - info->waitId = 0; - if (info->suspendCount > 0) - { - info->status = THS_SUSPEND; - } - else + std::lock_guard lock(info->m); + if (info->status == THS_WAIT || info->status == THS_WAITSUSPEND) { - info->status = THS_READY; + wasWaiting = true; + waitType = info->waitType; + waitId = info->waitId; + info->forceRelease = true; + info->waitType = TSW_NONE; + info->waitId = 0; + if (info->suspendCount > 0) + { + info->status = THS_SUSPEND; + } + else + { + info->status = THS_READY; + } } } - } - if (!wasWaiting) - { - setReturnS32(ctx, KE_NOT_WAIT); - return; - } + if (!wasWaiting) + { + setReturnS32(ctx, KE_NOT_WAIT); + return; + } - info->cv.notify_all(); + info->cv.notify_all(); - if (waitType == TSW_SEMA) - { - auto sema = lookupSemaInfo(waitId); - if (sema) + if (waitType == TSW_SEMA) { - sema->cv.notify_all(); + auto sema = lookupSemaInfo(waitId); + if (sema) + { + sema->cv.notify_all(); + } } - } - else if (waitType == TSW_EVENT) - { - auto eventFlag = lookupEventFlagInfo(waitId); - if (eventFlag) + else if (waitType == TSW_EVENT) { - eventFlag->cv.notify_all(); + auto eventFlag = lookupEventFlagInfo(waitId); + if (eventFlag) + { + eventFlag->cv.notify_all(); + } } + setReturnS32(ctx, KE_OK); } - setReturnS32(ctx, KE_OK); -} -void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - ReleaseWaitThread(rdram, ctx, runtime); -} + void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + ReleaseWaitThread(rdram, ctx, runtime); + } } diff --git a/ps2xRuntime/src/lib/game_overrides.cpp b/ps2xRuntime/src/lib/game_overrides.cpp index 285388ef..bc557902 100644 --- a/ps2xRuntime/src/lib/game_overrides.cpp +++ b/ps2xRuntime/src/lib/game_overrides.cpp @@ -116,10 +116,10 @@ namespace const std::string_view resolvedSyscall = ps2_runtime_calls::resolveSyscallName(handlerName); if (!resolvedSyscall.empty()) { -#define PS2_RESOLVE_SYSCALL(name) \ - if (resolvedSyscall == std::string_view{#name}) \ - { \ - return &ps2_syscalls::name; \ +#define PS2_RESOLVE_SYSCALL(name) \ + if (resolvedSyscall == std::string_view{#name}) \ + { \ + return &ps2_syscalls::name; \ } PS2_SYSCALL_LIST(PS2_RESOLVE_SYSCALL) #undef PS2_RESOLVE_SYSCALL @@ -128,10 +128,10 @@ namespace const std::string_view resolvedStub = ps2_runtime_calls::resolveStubName(handlerName); if (!resolvedStub.empty()) { -#define PS2_RESOLVE_STUB(name) \ - if (resolvedStub == std::string_view{#name}) \ - { \ - return &ps2_stubs::name; \ +#define PS2_RESOLVE_STUB(name) \ + if (resolvedStub == std::string_view{#name}) \ + { \ + return &ps2_stubs::name; \ } PS2_STUB_LIST(PS2_RESOLVE_STUB) #undef PS2_RESOLVE_STUB diff --git a/ps2xRuntime/src/lib/ps2_audio.cpp b/ps2xRuntime/src/lib/ps2_audio.cpp index 117d016c..66e7dcd3 100644 --- a/ps2xRuntime/src/lib/ps2_audio.cpp +++ b/ps2xRuntime/src/lib/ps2_audio.cpp @@ -6,53 +6,76 @@ namespace { -std::vector buildWavFromPcm(const int16_t *pcm, size_t sampleCount, uint32_t sampleRate) -{ - const uint32_t dataSize = static_cast(sampleCount * 2); - const uint32_t fileSize = 36 + dataSize; - std::vector wav(8 + fileSize); + std::vector buildWavFromPcm(const int16_t *pcm, size_t sampleCount, uint32_t sampleRate) + { + const uint32_t dataSize = static_cast(sampleCount * 2); + const uint32_t fileSize = 36 + dataSize; + std::vector wav(8 + fileSize); - uint8_t *p = wav.data(); - p[0] = 'R'; p[1] = 'I'; p[2] = 'F'; p[3] = 'F'; - p[4] = static_cast(fileSize); - p[5] = static_cast(fileSize >> 8); - p[6] = static_cast(fileSize >> 16); - p[7] = static_cast(fileSize >> 24); - p[8] = 'W'; p[9] = 'A'; p[10] = 'V'; p[11] = 'E'; - p[12] = 'f'; p[13] = 'm'; p[14] = 't'; p[15] = ' '; - p[16] = 16; p[17] = 0; p[18] = 0; p[19] = 0; - p[20] = 1; p[21] = 0; - p[22] = 1; p[23] = 0; - p[24] = static_cast(sampleRate); - p[25] = static_cast(sampleRate >> 8); - p[26] = static_cast(sampleRate >> 16); - p[27] = static_cast(sampleRate >> 24); - const uint32_t byteRate = sampleRate * 2; - p[28] = static_cast(byteRate); - p[29] = static_cast(byteRate >> 8); - p[30] = static_cast(byteRate >> 16); - p[31] = static_cast(byteRate >> 24); - p[32] = 2; p[33] = 0; - p[34] = 16; p[35] = 0; - p[36] = 'd'; p[37] = 'a'; p[38] = 't'; p[39] = 'a'; - p[40] = static_cast(dataSize); - p[41] = static_cast(dataSize >> 8); - p[42] = static_cast(dataSize >> 16); - p[43] = static_cast(dataSize >> 24); - std::memcpy(p + 44, pcm, dataSize); - return wav; -} + uint8_t *p = wav.data(); + p[0] = 'R'; + p[1] = 'I'; + p[2] = 'F'; + p[3] = 'F'; + p[4] = static_cast(fileSize); + p[5] = static_cast(fileSize >> 8); + p[6] = static_cast(fileSize >> 16); + p[7] = static_cast(fileSize >> 24); + p[8] = 'W'; + p[9] = 'A'; + p[10] = 'V'; + p[11] = 'E'; + p[12] = 'f'; + p[13] = 'm'; + p[14] = 't'; + p[15] = ' '; + p[16] = 16; + p[17] = 0; + p[18] = 0; + p[19] = 0; + p[20] = 1; + p[21] = 0; + p[22] = 1; + p[23] = 0; + p[24] = static_cast(sampleRate); + p[25] = static_cast(sampleRate >> 8); + p[26] = static_cast(sampleRate >> 16); + p[27] = static_cast(sampleRate >> 24); + const uint32_t byteRate = sampleRate * 2; + p[28] = static_cast(byteRate); + p[29] = static_cast(byteRate >> 8); + p[30] = static_cast(byteRate >> 16); + p[31] = static_cast(byteRate >> 24); + p[32] = 2; + p[33] = 0; + p[34] = 16; + p[35] = 0; + p[36] = 'd'; + p[37] = 'a'; + p[38] = 't'; + p[39] = 'a'; + p[40] = static_cast(dataSize); + p[41] = static_cast(dataSize >> 8); + p[42] = static_cast(dataSize >> 16); + p[43] = static_cast(dataSize >> 24); + std::memcpy(p + 44, pcm, dataSize); + return wav; + } } namespace ps2_vag { -bool decode(const uint8_t *data, uint32_t sizeBytes, - std::vector &outPcm, uint32_t &outSampleRate); + bool decode(const uint8_t *data, uint32_t sizeBytes, + std::vector &outPcm, uint32_t &outSampleRate); } struct PS2AudioBackend::Impl { - struct TrackedSound { Sound snd; uint32_t sampleKey; }; + struct TrackedSound + { + Sound snd; + uint32_t sampleKey; + }; std::vector activeSounds; }; @@ -117,7 +140,7 @@ void PS2AudioBackend::onVagTransferFromBuffer(const uint8_t *data, uint32_t size namespace { -constexpr uint32_t LIBSD_CMD_SET_VOICE = 0x8010u; + constexpr uint32_t LIBSD_CMD_SET_VOICE = 0x8010u; } void PS2AudioBackend::onSoundCommand(uint32_t sid, uint32_t rpcNum, diff --git a/ps2xRuntime/src/lib/ps2_audio_vag.cpp b/ps2xRuntime/src/lib/ps2_audio_vag.cpp index 9b187f67..5d29c2a1 100644 --- a/ps2xRuntime/src/lib/ps2_audio_vag.cpp +++ b/ps2xRuntime/src/lib/ps2_audio_vag.cpp @@ -5,108 +5,110 @@ namespace { -inline int16_t clamp16(int32_t v) -{ - if (v < -32768) return -32768; - if (v > 32767) return 32767; - return static_cast(v); -} + inline int16_t clamp16(int32_t v) + { + if (v < -32768) + return -32768; + if (v > 32767) + return 32767; + return static_cast(v); + } -inline int8_t signExtend4(uint8_t nibble) -{ - uint8_t s = nibble & 0x0F; - return static_cast((s & 8) ? static_cast(s | 0xF0) : static_cast(s)); -} + inline int8_t signExtend4(uint8_t nibble) + { + uint8_t s = nibble & 0x0F; + return static_cast((s & 8) ? static_cast(s | 0xF0) : static_cast(s)); + } } namespace ps2_vag { -bool decode(const uint8_t *data, uint32_t sizeBytes, - std::vector &outPcm, uint32_t &outSampleRate) -{ - if (!data || sizeBytes < 48) - return false; - - const uint32_t magic = (static_cast(data[0]) << 24) | - (static_cast(data[1]) << 16) | - (static_cast(data[2]) << 8) | - static_cast(data[3]); - if (magic != 0x56414770u) + bool decode(const uint8_t *data, uint32_t sizeBytes, + std::vector &outPcm, uint32_t &outSampleRate) { - const uint32_t magicLE = (static_cast(data[3]) << 24) | - (static_cast(data[2]) << 16) | - (static_cast(data[1]) << 8) | - static_cast(data[0]); - if (magicLE != 0x56414770u) + if (!data || sizeBytes < 48) return false; - } - uint32_t dataSize = (static_cast(data[0x0c]) << 24) | - (static_cast(data[0x0d]) << 16) | - (static_cast(data[0x0e]) << 8) | - static_cast(data[0x0f]); - outSampleRate = (static_cast(data[0x10]) << 24) | - (static_cast(data[0x11]) << 16) | - (static_cast(data[0x12]) << 8) | - static_cast(data[0x13]); - if (outSampleRate == 0) - outSampleRate = 44100; + const uint32_t magic = (static_cast(data[0]) << 24) | + (static_cast(data[1]) << 16) | + (static_cast(data[2]) << 8) | + static_cast(data[3]); + if (magic != 0x56414770u) + { + const uint32_t magicLE = (static_cast(data[3]) << 24) | + (static_cast(data[2]) << 16) | + (static_cast(data[1]) << 8) | + static_cast(data[0]); + if (magicLE != 0x56414770u) + return false; + } - const uint32_t numBlocks = (dataSize + 15) / 16; - outPcm.clear(); - outPcm.reserve(numBlocks * 28); + uint32_t dataSize = (static_cast(data[0x0c]) << 24) | + (static_cast(data[0x0d]) << 16) | + (static_cast(data[0x0e]) << 8) | + static_cast(data[0x0f]); + outSampleRate = (static_cast(data[0x10]) << 24) | + (static_cast(data[0x11]) << 16) | + (static_cast(data[0x12]) << 8) | + static_cast(data[0x13]); + if (outSampleRate == 0) + outSampleRate = 44100; - int16_t s1 = 0, s2 = 0; - const uint8_t *block = data + 48; + const uint32_t numBlocks = (dataSize + 15) / 16; + outPcm.clear(); + outPcm.reserve(numBlocks * 28); - for (uint32_t b = 0; b < numBlocks && (block + 16) <= data + sizeBytes; ++b, block += 16) - { - uint8_t shift = block[0] & 0x0F; - if (shift > 12) - shift = 9; - uint8_t filter = (block[0] >> 4) & 0x07; - if (filter > 4) - filter = 0; + int16_t s1 = 0, s2 = 0; + const uint8_t *block = data + 48; - for (int sampleIdx = 0; sampleIdx < 28; ++sampleIdx) + for (uint32_t b = 0; b < numBlocks && (block + 16) <= data + sizeBytes; ++b, block += 16) { - const uint8_t byte = block[2 + sampleIdx / 2]; - const uint8_t nibble = (sampleIdx & 1) ? (byte >> 4) : (byte & 0x0F); - const int8_t rawSample = signExtend4(nibble); - const int32_t shiftedSample = rawSample << (12 - shift); + uint8_t shift = block[0] & 0x0F; + if (shift > 12) + shift = 9; + uint8_t filter = (block[0] >> 4) & 0x07; + if (filter > 4) + filter = 0; - int32_t filteredSample; - const int32_t old = s1; - const int32_t older = s2; - switch (filter) + for (int sampleIdx = 0; sampleIdx < 28; ++sampleIdx) { - case 0: - filteredSample = shiftedSample; - break; - case 1: - filteredSample = shiftedSample + (60 * old + 32) / 64; - break; - case 2: - filteredSample = shiftedSample + (115 * old - 52 * older + 32) / 64; - break; - case 3: - filteredSample = shiftedSample + (98 * old - 55 * older + 32) / 64; - break; - case 4: - filteredSample = shiftedSample + (122 * old - 60 * older + 32) / 64; - break; - default: - filteredSample = shiftedSample; - break; - } + const uint8_t byte = block[2 + sampleIdx / 2]; + const uint8_t nibble = (sampleIdx & 1) ? (byte >> 4) : (byte & 0x0F); + const int8_t rawSample = signExtend4(nibble); + const int32_t shiftedSample = rawSample << (12 - shift); - const int16_t clamped = clamp16(filteredSample); - s2 = s1; - s1 = clamped; - outPcm.push_back(clamped); + int32_t filteredSample; + const int32_t old = s1; + const int32_t older = s2; + switch (filter) + { + case 0: + filteredSample = shiftedSample; + break; + case 1: + filteredSample = shiftedSample + (60 * old + 32) / 64; + break; + case 2: + filteredSample = shiftedSample + (115 * old - 52 * older + 32) / 64; + break; + case 3: + filteredSample = shiftedSample + (98 * old - 55 * older + 32) / 64; + break; + case 4: + filteredSample = shiftedSample + (122 * old - 60 * older + 32) / 64; + break; + default: + filteredSample = shiftedSample; + break; + } + + const int16_t clamped = clamp16(filteredSample); + s2 = s1; + s1 = clamped; + outPcm.push_back(clamped); + } } - } - return true; -} + return true; + } } diff --git a/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp b/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp index 29d69d62..3f5f292a 100644 --- a/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp +++ b/ps2xRuntime/src/lib/ps2_gif_arbiter.cpp @@ -58,13 +58,13 @@ void GifArbiter::submit(GifPathId pathId, const uint8_t *data, uint32_t sizeByte if (nreg == 0u) nreg = 16u; RUNTIME_LOG("[gif:submit] idx=" << debugIndex - << " path=" << pathName(pathId) - << " size=" << sizeBytes - << " nloop=" << nloop - << " flg=" << static_cast(flg) - << " nreg=" << nreg - << " directhl=" << static_cast(path2DirectHl ? 1u : 0u) - << std::endl); + << " path=" << pathName(pathId) + << " size=" << sizeBytes + << " nloop=" << nloop + << " flg=" << static_cast(flg) + << " nreg=" << nreg + << " directhl=" << static_cast(path2DirectHl ? 1u : 0u) + << std::endl); } GifArbiterPacket pkt; @@ -111,14 +111,14 @@ void GifArbiter::drain() if (nreg == 0u) nreg = 16u; RUNTIME_LOG("[gif:drain] idx=" << debugIndex - << " path=" << pathName(pkt.pathId) - << " size=" << pkt.data.size() - << " nloop=" << nloop - << " flg=" << static_cast(flg) - << " nreg=" << nreg - << " directhl=" << static_cast(pkt.path2DirectHl ? 1u : 0u) - << " path3image=" << static_cast(pkt.path3Image ? 1u : 0u) - << std::endl); + << " path=" << pathName(pkt.pathId) + << " size=" << pkt.data.size() + << " nloop=" << nloop + << " flg=" << static_cast(flg) + << " nreg=" << nreg + << " directhl=" << static_cast(pkt.path2DirectHl ? 1u : 0u) + << " path3image=" << static_cast(pkt.path3Image ? 1u : 0u) + << std::endl); } m_processFn(pkt.data.data(), static_cast(pkt.data.size())); } diff --git a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp index 12566cf4..946d1cc6 100644 --- a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp @@ -64,7 +64,7 @@ namespace outWidth = (dw + 1u) / (magh + 1u); outHeight = dh + 1u; - + if (outWidth < 64u || outHeight < 64u) { outWidth = kDefaultDisplayWidth; @@ -825,14 +825,14 @@ void GS::latchHostPresentationFrame() (m_preferredDisplaySourceFrame.fbw != 0u || m_preferredDisplaySourceFrame.fbp != displayFrame.fbp)) { if (copyFrameToHostRgbaUnlocked(m_preferredDisplaySourceFrame, - width, - height, - scratch, - preserveAlpha, - true, - false, - 0u, - 0u)) + width, + height, + scratch, + preserveAlpha, + true, + false, + 0u, + 0u)) { selectedFrame = m_preferredDisplaySourceFrame; usedPreferred = true; @@ -867,14 +867,14 @@ void GS::latchHostPresentationFrame() std::vector candidatePixels; if (!copyFrameToHostRgbaUnlocked(candidate, - width, - height, - candidatePixels, - preserveAlpha, - true, - true, - 0u, - 0u)) + width, + height, + candidatePixels, + preserveAlpha, + true, + true, + 0u, + 0u)) { continue; } @@ -1092,13 +1092,13 @@ void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes) if (nreg == 0u) nreg = 16u; RUNTIME_LOG("[gs:gif] idx=" << packetIndex - << " size=" << sizeBytes - << " nloop=" << nloop - << " flg=" << static_cast(flg) - << " nreg=" << nreg - << " ctx0fbp=" << m_ctx[0].frame.fbp - << " ctx1fbp=" << m_ctx[1].frame.fbp - << std::endl); + << " size=" << sizeBytes + << " nloop=" << nloop + << " flg=" << static_cast(flg) + << " nreg=" << nreg + << " ctx0fbp=" << m_ctx[0].frame.fbp + << " ctx1fbp=" << m_ctx[1].frame.fbp + << std::endl); } }); @@ -1220,14 +1220,14 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) if (debugIndex < 64u) { RUNTIME_LOG("[gs:packed-xyzf] idx=" << debugIndex - << " x=" << x - << " y=" << y - << " z=0x" << std::hex << z - << std::dec - << " fog=" << static_cast(f) - << " kick=" << static_cast(!adk ? 1u : 0u) - << " prim=" << static_cast(m_prim.type) - << std::endl); + << " x=" << x + << " y=" << y + << " z=0x" << std::hex << z + << std::dec + << " fog=" << static_cast(f) + << " kick=" << static_cast(!adk ? 1u : 0u) + << " prim=" << static_cast(m_prim.type) + << std::endl); } }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; @@ -1258,13 +1258,13 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) if (debugIndex < 64u) { RUNTIME_LOG("[gs:packed-xyz] idx=" << debugIndex - << " x=" << x - << " y=" << y - << " z=0x" << std::hex << z - << std::dec - << " kick=" << static_cast(!adk ? 1u : 0u) - << " prim=" << static_cast(m_prim.type) - << std::endl); + << " x=" << x + << " y=" << y + << " z=0x" << std::hex << z + << std::dec + << " kick=" << static_cast(!adk ? 1u : 0u) + << " prim=" << static_cast(m_prim.type) + << std::endl); } }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; @@ -1294,11 +1294,11 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) if (debugIndex < 64u) { RUNTIME_LOG("[gs:packed-xyzf3] idx=" << debugIndex - << " x=" << static_cast(lo & 0xFFFFu) - << " y=" << static_cast((lo >> 32) & 0xFFFFu) - << " kick=0" - << " prim=" << static_cast(m_prim.type) - << std::endl); + << " x=" << static_cast(lo & 0xFFFFu) + << " y=" << static_cast((lo >> 32) & 0xFFFFu) + << " kick=0" + << " prim=" << static_cast(m_prim.type) + << std::endl); } }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; @@ -1325,11 +1325,11 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi) if (debugIndex < 64u) { RUNTIME_LOG("[gs:packed-xyz3] idx=" << debugIndex - << " x=" << static_cast(lo & 0xFFFFu) - << " y=" << static_cast((lo >> 32) & 0xFFFFu) - << " kick=0" - << " prim=" << static_cast(m_prim.type) - << std::endl); + << " x=" << static_cast(lo & 0xFFFFu) + << " y=" << static_cast((lo >> 32) & 0xFFFFu) + << " kick=0" + << " prim=" << static_cast(m_prim.type) + << std::endl); } }); GSVertex &vtx = m_vtxQueue[m_vtxCount % kMaxVerts]; @@ -1399,10 +1399,10 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) if (debugIndex < 128u) { RUNTIME_LOG("[gs:reg] idx=" << debugIndex - << " reg=0x" << std::hex << static_cast(regAddr) - << " value=0x" << value - << std::dec - << std::endl); + << " reg=0x" << std::hex << static_cast(regAddr) + << " value=0x" << value + << std::dec + << std::endl); } } }); @@ -1422,13 +1422,13 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) s_debugCopyRegCount.fetch_add(1u, std::memory_order_relaxed) < 64u) { RUNTIME_LOG("[gs:copy-reg] reg=0x" - << std::hex << static_cast(regAddr) - << " value=0x" << value - << std::dec - << " primCtxt=" << static_cast(m_prim.ctxt) - << " ctx0fbp=" << m_ctx[0].frame.fbp - << " ctx1fbp=" << m_ctx[1].frame.fbp - << std::endl); + << std::hex << static_cast(regAddr) + << " value=0x" << value + << std::dec + << " primCtxt=" << static_cast(m_prim.ctxt) + << " ctx0fbp=" << m_ctx[0].frame.fbp + << " ctx1fbp=" << m_ctx[1].frame.fbp + << std::endl); } }); @@ -1696,12 +1696,12 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) if (texaIndex < 24u) { RUNTIME_LOG("[gs:texa] idx=" << texaIndex - << " value=0x" << std::hex << value - << " ta0=0x" << ((value >> 0) & 0xFFu) - << " aem=" << ((value >> 15) & 0x1u) - << " ta1=0x" << ((value >> 32) & 0xFFu) - << std::dec - << std::endl); + << " value=0x" << std::hex << value + << " ta0=0x" << ((value >> 0) & 0xFFu) + << " aem=" << ((value >> 15) & 0x1u) + << " ta1=0x" << ((value >> 32) & 0xFFu) + << std::dec + << std::endl); } }); break; @@ -1798,16 +1798,16 @@ void GS::performLocalToLocalTransfer() s_debugLocalCopyCount.fetch_add(1u, std::memory_order_relaxed) < 96u) { RUNTIME_LOG("[gs:l2l] sbp=" << sbp - << " dbp=" << dbp - << " sbw=" << static_cast(sbw) - << " dbw=" << static_cast(dbw) - << " spsm=0x" << std::hex << static_cast(spsm) - << " dpsm=0x" << static_cast(dpsm) << std::dec - << " ss=(" << ssax << "," << ssay << ")" - << " ds=(" << dsax << "," << dsay << ")" - << " rr=(" << rrw << "," << rrh << ")" - << " dir=" << static_cast(m_trxpos.dir) - << " formatAware=" << (formatAware ? 1 : 0) << std::endl); + << " dbp=" << dbp + << " sbw=" << static_cast(sbw) + << " dbw=" << static_cast(dbw) + << " spsm=0x" << std::hex << static_cast(spsm) + << " dpsm=0x" << static_cast(dpsm) << std::dec + << " ss=(" << ssax << "," << ssay << ")" + << " ds=(" << dsax << "," << dsay << ")" + << " rr=(" << rrw << "," << rrh << ")" + << " dir=" << static_cast(m_trxpos.dir) + << " formatAware=" << (formatAware ? 1 : 0) << std::endl); } }); @@ -1880,10 +1880,10 @@ void GS::vertexKick(bool drawing) if (debugIndex < 96u) { RUNTIME_LOG("[gs:kick] idx=" << debugIndex - << " drawing=" << static_cast(drawing ? 1u : 0u) - << " prim=" << static_cast(m_prim.type) - << " vtxCount=" << m_vtxCount - << std::endl); + << " drawing=" << static_cast(drawing ? 1u : 0u) + << " prim=" << static_cast(m_prim.type) + << " vtxCount=" << m_vtxCount + << std::endl); } }); diff --git a/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp b/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp index 562345c4..785251f1 100644 --- a/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp @@ -762,14 +762,14 @@ void GSRasterizer::drawSprite(GS *gs) if (gs->m_prim.tme) { - const auto &tex = ctx.tex0; + const auto &tex = ctx.tex0; int texW = 1 << tex.tw; int texH = 1 << tex.th; if (texW == 0) texW = 1; if (texH == 0) texH = 1; - + float u0f, v0f, u1f, v1f; if (gs->m_prim.fst) { diff --git a/ps2xRuntime/src/lib/ps2_iop_audio.cpp b/ps2xRuntime/src/lib/ps2_iop_audio.cpp index e1209764..a3fa0ee3 100644 --- a/ps2xRuntime/src/lib/ps2_iop_audio.cpp +++ b/ps2xRuntime/src/lib/ps2_iop_audio.cpp @@ -3,12 +3,12 @@ namespace ps2_iop_audio { -void handleLibSdRpc(PS2Runtime *runtime, uint32_t sid, uint32_t rpcNum, - const uint8_t *sendBuf, uint32_t sendSize, - uint8_t *recvBuf, uint32_t recvSize) -{ - if (!runtime) - return; - runtime->audioBackend().onSoundCommand(sid, rpcNum, sendBuf, sendSize, recvBuf, recvSize); -} + void handleLibSdRpc(PS2Runtime *runtime, uint32_t sid, uint32_t rpcNum, + const uint8_t *sendBuf, uint32_t sendSize, + uint8_t *recvBuf, uint32_t recvSize) + { + if (!runtime) + return; + runtime->audioBackend().onSoundCommand(sid, rpcNum, sendBuf, sendSize, recvBuf, recvSize); + } } diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index 3fdca8af..a89a8074 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -746,7 +746,7 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) switch (address) { - case 0x10003C10u: // VIF1_FBRST + case 0x10003C10u: // VIF1_FBRST if (value & 0x1u) // RST { std::memset(&vif1_regs, 0, sizeof(vif1_regs)); diff --git a/ps2xRuntime/src/lib/ps2_pad.cpp b/ps2xRuntime/src/lib/ps2_pad.cpp index 0537366e..36bcc6fe 100644 --- a/ps2xRuntime/src/lib/ps2_pad.cpp +++ b/ps2xRuntime/src/lib/ps2_pad.cpp @@ -4,25 +4,25 @@ namespace { -constexpr uint8_t kPadAnalogMarker = 0x73; -constexpr uint8_t kPadStickCenter = 0x80; + constexpr uint8_t kPadAnalogMarker = 0x73; + constexpr uint8_t kPadStickCenter = 0x80; -constexpr uint16_t PAD_LEFT = 0x0080u; -constexpr uint16_t PAD_DOWN = 0x0040u; -constexpr uint16_t PAD_RIGHT = 0x0020u; -constexpr uint16_t PAD_UP = 0x0010u; -constexpr uint16_t PAD_START = 0x0008u; -constexpr uint16_t PAD_R3 = 0x0004u; -constexpr uint16_t PAD_L3 = 0x0002u; -constexpr uint16_t PAD_SELECT = 0x0001u; -constexpr uint16_t PAD_SQUARE = 0x8000u; -constexpr uint16_t PAD_CROSS = 0x4000u; -constexpr uint16_t PAD_CIRCLE = 0x2000u; -constexpr uint16_t PAD_TRIANGLE = 0x1000u; -constexpr uint16_t PAD_R1 = 0x0800u; -constexpr uint16_t PAD_L1 = 0x0400u; -constexpr uint16_t PAD_R2 = 0x0200u; -constexpr uint16_t PAD_L2 = 0x0100u; + constexpr uint16_t PAD_LEFT = 0x0080u; + constexpr uint16_t PAD_DOWN = 0x0040u; + constexpr uint16_t PAD_RIGHT = 0x0020u; + constexpr uint16_t PAD_UP = 0x0010u; + constexpr uint16_t PAD_START = 0x0008u; + constexpr uint16_t PAD_R3 = 0x0004u; + constexpr uint16_t PAD_L3 = 0x0002u; + constexpr uint16_t PAD_SELECT = 0x0001u; + constexpr uint16_t PAD_SQUARE = 0x8000u; + constexpr uint16_t PAD_CROSS = 0x4000u; + constexpr uint16_t PAD_CIRCLE = 0x2000u; + constexpr uint16_t PAD_TRIANGLE = 0x1000u; + constexpr uint16_t PAD_R1 = 0x0800u; + constexpr uint16_t PAD_L1 = 0x0400u; + constexpr uint16_t PAD_R2 = 0x0200u; + constexpr uint16_t PAD_L2 = 0x0100u; } bool PSPadBackend::readState(int /*port*/, int /*slot*/, uint8_t *data, size_t size) @@ -40,26 +40,43 @@ bool PSPadBackend::readState(int /*port*/, int /*slot*/, uint8_t *data, size_t s uint16_t btns = 0xFFFFu; constexpr int kGamepad = 0; const bool useGamepad = IsGamepadAvailable(kGamepad); - auto clearBit = [&btns](uint16_t mask) { btns &= ~mask; }; + auto clearBit = [&btns](uint16_t mask) + { btns &= ~mask; }; if (useGamepad) { - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) clearBit(PAD_UP); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) clearBit(PAD_DOWN); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) clearBit(PAD_LEFT); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) clearBit(PAD_RIGHT); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) clearBit(PAD_CROSS); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) clearBit(PAD_CIRCLE); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) clearBit(PAD_SQUARE); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) clearBit(PAD_TRIANGLE); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) clearBit(PAD_L1); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) clearBit(PAD_R1); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) clearBit(PAD_L2); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)) clearBit(PAD_R2); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) clearBit(PAD_START); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) clearBit(PAD_SELECT); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_THUMB)) clearBit(PAD_L3); - if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) clearBit(PAD_R3); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) + clearBit(PAD_UP); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) + clearBit(PAD_DOWN); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) + clearBit(PAD_LEFT); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) + clearBit(PAD_RIGHT); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) + clearBit(PAD_CROSS); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) + clearBit(PAD_CIRCLE); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) + clearBit(PAD_SQUARE); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) + clearBit(PAD_TRIANGLE); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) + clearBit(PAD_L1); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) + clearBit(PAD_R1); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_2)) + clearBit(PAD_L2); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_2)) + clearBit(PAD_R2); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) + clearBit(PAD_START); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) + clearBit(PAD_SELECT); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_LEFT_THUMB)) + clearBit(PAD_L3); + if (IsGamepadButtonDown(kGamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) + clearBit(PAD_R3); float lx = GetGamepadAxisMovement(kGamepad, GAMEPAD_AXIS_LEFT_X); float ly = GetGamepadAxisMovement(kGamepad, GAMEPAD_AXIS_LEFT_Y); @@ -72,20 +89,34 @@ bool PSPadBackend::readState(int /*port*/, int /*slot*/, uint8_t *data, size_t s } else { - if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) clearBit(PAD_UP); - if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) clearBit(PAD_DOWN); - if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) clearBit(PAD_LEFT); - if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) clearBit(PAD_RIGHT); - if (IsKeyDown(KEY_ENTER) || IsKeyDown(KEY_SPACE)) clearBit(PAD_CROSS); - if (IsKeyDown(KEY_ESCAPE)) clearBit(PAD_CIRCLE); - if (IsKeyDown(KEY_KP_0) || IsKeyDown(KEY_Z)) clearBit(PAD_SQUARE); - if (IsKeyDown(KEY_KP_1) || IsKeyDown(KEY_X)) clearBit(PAD_TRIANGLE); - if (IsKeyDown(KEY_Q)) clearBit(PAD_L1); - if (IsKeyDown(KEY_E)) clearBit(PAD_R1); - if (IsKeyDown(KEY_LEFT_SHIFT)) clearBit(PAD_L2); - if (IsKeyDown(KEY_RIGHT_SHIFT)) clearBit(PAD_R2); - if (IsKeyDown(KEY_ENTER)) clearBit(PAD_START); - if (IsKeyDown(KEY_TAB)) clearBit(PAD_SELECT); + if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) + clearBit(PAD_UP); + if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) + clearBit(PAD_DOWN); + if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) + clearBit(PAD_LEFT); + if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) + clearBit(PAD_RIGHT); + if (IsKeyDown(KEY_ENTER) || IsKeyDown(KEY_SPACE)) + clearBit(PAD_CROSS); + if (IsKeyDown(KEY_ESCAPE)) + clearBit(PAD_CIRCLE); + if (IsKeyDown(KEY_KP_0) || IsKeyDown(KEY_Z)) + clearBit(PAD_SQUARE); + if (IsKeyDown(KEY_KP_1) || IsKeyDown(KEY_X)) + clearBit(PAD_TRIANGLE); + if (IsKeyDown(KEY_Q)) + clearBit(PAD_L1); + if (IsKeyDown(KEY_E)) + clearBit(PAD_R1); + if (IsKeyDown(KEY_LEFT_SHIFT)) + clearBit(PAD_L2); + if (IsKeyDown(KEY_RIGHT_SHIFT)) + clearBit(PAD_R2); + if (IsKeyDown(KEY_ENTER)) + clearBit(PAD_START); + if (IsKeyDown(KEY_TAB)) + clearBit(PAD_SELECT); } data[2] = static_cast(btns & 0xFF); diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 462c1625..cfa39923 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -27,7 +27,6 @@ #include #include - namespace ps2_stubs { void resetSifState(); @@ -617,7 +616,7 @@ PS2Runtime::~PS2Runtime() } } -bool PS2Runtime::ensureCoreSubsystemsInitialized() +bool PS2Runtime::syncCoreSubsystems() { uint8_t *const rdram = m_memory.getRDRAM(); uint8_t *const gsVram = m_memory.getGSVRAM(); @@ -662,7 +661,7 @@ bool PS2Runtime::initialize(const char *title) return false; } - if (!ensureCoreSubsystemsInitialized()) + if (!syncCoreSubsystems()) { std::cerr << "Failed to bind runtime core subsystems" << std::endl; return false; @@ -852,10 +851,10 @@ bool PS2Runtime::loadELF(const std::string &elfPath) } RUNTIME_LOG("Loading segment: 0x" << std::hex << ph.vaddr - << " - 0x" << (static_cast(ph.vaddr) + static_cast(ph.memsz)) - << " (filesz: 0x" << ph.filesz - << ", memsz: 0x" << ph.memsz << ")" - << std::dec << std::endl); + << " - 0x" << (static_cast(ph.vaddr) + static_cast(ph.memsz)) + << " (filesz: 0x" << ph.filesz + << ", memsz: 0x" << ph.memsz << ")" + << std::dec << std::endl); if (!scratch) { @@ -1158,9 +1157,9 @@ void PS2Runtime::executeVU0Microprogram(uint8_t *rdram, R5900Context *ctx, uint3 if (count < 3) { RUNTIME_LOG("[VU0] microprogram @0x" << std::hex << address - << " pc=0x" << ctx->pc - << " ra=0x" << static_cast(_mm_extract_epi32(ctx->r[31], 0)) - << std::dec << std::endl); + << " pc=0x" << ctx->pc + << " ra=0x" << static_cast(_mm_extract_epi32(ctx->r[31], 0)) + << std::dec << std::endl); } ++count; diff --git a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp index 41a32f0d..5caa7cc1 100644 --- a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp +++ b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp @@ -78,12 +78,12 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) if (opcodeIndex < 160u) { RUNTIME_LOG("[vif1:cmd] idx=" << opcodeIndex - << " opcode=0x" << std::hex << static_cast(opcode) - << " imm=0x" << imm - << std::dec - << " num=" << static_cast(num) - << " irq=" << static_cast(irq ? 1u : 0u) - << std::endl); + << " opcode=0x" << std::hex << static_cast(opcode) + << " imm=0x" << imm + << std::dec + << " num=" << static_cast(num) + << " irq=" << static_cast(irq ? 1u : 0u) + << std::endl); } // Track most-recent command for VIFn_CODE emulation. @@ -155,11 +155,11 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) if (kickIndex < 48u) { RUNTIME_LOG("[vif1:mscal] idx=" << kickIndex - << " opcode=0x" << std::hex << static_cast(opcode) - << " imm=0x" << imm - << " startPc=0x" << startPC - << " itop=0x" << vif1_regs.itop - << std::dec << std::endl); + << " opcode=0x" << std::hex << static_cast(opcode) + << " imm=0x" << imm + << " startPc=0x" << startPC + << " itop=0x" << vif1_regs.itop + << std::dec << std::endl); } if (m_vu1MscalCallback) m_vu1MscalCallback(startPC, vif1_regs.itop); @@ -174,9 +174,9 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) if (kickIndex < 48u) { RUNTIME_LOG("[vif1:mscnt] idx=" << kickIndex - << " itop=0x" << std::hex << vif1_regs.itop - << " pc=resume" - << std::dec << std::endl); + << " itop=0x" << std::hex << vif1_regs.itop + << " pc=resume" + << std::dec << std::endl); } if (m_vu1MscntCallback) m_vu1MscntCallback(vif1_regs.itop); diff --git a/ps2xRuntime/src/lib/ps2_vu1.cpp b/ps2xRuntime/src/lib/ps2_vu1.cpp index bfd626e7..10b7dd80 100644 --- a/ps2xRuntime/src/lib/ps2_vu1.cpp +++ b/ps2xRuntime/src/lib/ps2_vu1.cpp @@ -1227,11 +1227,11 @@ void VU1Interpreter::execLower(uint32_t instr, uint8_t *vuData, uint32_t dataSiz if (debugIndex < 64u) { RUNTIME_LOG("[vu1:xgkick] idx=" << debugIndex - << " addr=0x" << std::hex << addr - << " totalBytes=0x" << totalBytes - << std::dec - << " wrap=" << static_cast((addr + totalBytes > dataSize) ? 1u : 0u) - << std::endl); + << " addr=0x" << std::hex << addr + << " totalBytes=0x" << totalBytes + << std::dec + << " wrap=" << static_cast((addr + totalBytes > dataSize) ? 1u : 0u) + << std::endl); } if (addr + totalBytes <= dataSize) diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index adb145d7..9e339c0a 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) try { std::filesystem::path exePath = getExecutablePath(argc, argv); - + std::string elfPath = exePath.filename().string(); std::filesystem::path pathObj(elfPath); std::string folderName = pathObj.filename().string(); From 5b2e04cf923fa87b9ceadcf355c461812b7274b6 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 03:01:09 -0300 Subject: [PATCH 13/30] feat: gamedp is now part of lib feat: missing file --- ps2xRecomp/src/lib/ps2_recompiler.cpp | 45 ------------------- ps2xRuntime/CMakeLists.txt | 1 + .../src/{runner => lib}/games_database.cpp | 0 ps2xStudio/CMakeLists.txt | 2 +- 4 files changed, 2 insertions(+), 46 deletions(-) rename ps2xRuntime/src/{runner => lib}/games_database.cpp (100%) diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index 42b212ab..428bbdfa 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -246,50 +246,6 @@ namespace ps2recomp return name.rfind("entry_", 0) == 0; } - void removeStaleGeneratedOutputs(const fs::path &outputDir) - { - if (!fs::exists(outputDir) || !fs::is_directory(outputDir)) - { - return; - } - - for (const auto &entry : fs::directory_iterator(outputDir)) - { - if (!entry.is_regular_file()) - { - continue; - } - - const fs::path &path = entry.path(); - const std::string filename = path.filename().string(); - const std::string extension = path.extension().string(); - - const bool isPerFunctionSource = - extension == ".cpp" && - (filename.find("_0x") != std::string::npos || - filename.rfind("entry_", 0) == 0); - const bool isAggregateSource = - filename == "ps2_recompiled_functions.cpp" || - filename == "register_functions.cpp"; - const bool isGeneratedHeader = - filename == "ps2_recompiled_functions.h" || - filename == "ps2_recompiled_stubs.h"; - - if (!isPerFunctionSource && !isAggregateSource && !isGeneratedHeader) - { - continue; - } - - std::error_code removeError; - fs::remove(path, removeError); - if (removeError) - { - std::cerr << "Warning: failed to remove stale generated file " - << path << ": " << removeError.message() << std::endl; - } - } - } - EntryDiscoveryStats discoverAdditionalEntryPointsImpl( std::vector &functions, std::unordered_map> &decodedFunctions, @@ -1092,7 +1048,6 @@ namespace ps2recomp } } - removeStaleGeneratedOutputs(fs::path(m_config.outputPath)); generateFunctionHeader(); if (m_config.singleFileOutput) diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index 3f358517..cb507bcc 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -45,6 +45,7 @@ add_library(ps2_runtime STATIC src/lib/ps2_runtime.cpp src/lib/ps2_vif1_interpreter.cpp src/lib/ps2_vu1.cpp + src/lib/games_database.cpp ) file(GLOB_RECURSE KERNEL_SRC_FILES CONFIGURE_DEPENDS diff --git a/ps2xRuntime/src/runner/games_database.cpp b/ps2xRuntime/src/lib/games_database.cpp similarity index 100% rename from ps2xRuntime/src/runner/games_database.cpp rename to ps2xRuntime/src/lib/games_database.cpp diff --git a/ps2xStudio/CMakeLists.txt b/ps2xStudio/CMakeLists.txt index f662322b..43f0936a 100644 --- a/ps2xStudio/CMakeLists.txt +++ b/ps2xStudio/CMakeLists.txt @@ -39,7 +39,7 @@ FetchContent_Declare( FetchContent_Declare( SDL2 GIT_REPOSITORY https://github.com/libsdl-org/SDL.git - GIT_TAG release-2.30.9 + GIT_TAG release-2.32.10 GIT_SHALLOW TRUE ) From 7ac1b9630837125479003c470730f0364f1f7a90 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 12:27:00 -0300 Subject: [PATCH 14/30] feat: small cleanup --- ps2xRuntime/include/ps2_stubs.h | 7 +++--- ps2xRuntime/src/main.cpp | 38 +++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/ps2xRuntime/include/ps2_stubs.h b/ps2xRuntime/include/ps2_stubs.h index 7ad07b0a..a99becab 100644 --- a/ps2xRuntime/include/ps2_stubs.h +++ b/ps2xRuntime/include/ps2_stubs.h @@ -1,11 +1,12 @@ #pragma once +struct R5900Context; +class PS2Runtime; + #include #include "ps2_call_list.h" #include "runtime/ps2_memory.h" - -struct R5900Context; -class PS2Runtime; +#include "Stubs/Unimplemented.h" struct PS2MpegCompatLayout { diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index 9e339c0a..26ba1cf2 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -55,21 +55,26 @@ namespace if (dot != std::string::npos) result.erase(dot, 1); + std::ranges::transform(result, result.begin(), [](unsigned char character) + { return static_cast(std::toupper(character)); }); + return result; } std::filesystem::path getExecutablePath(int argc, char *argv[]) { + if (argc >= 2) + { + std::cout << "Using agrs" << std::endl; + return std::filesystem::path(argv[1]); + } #if defined(PS2X_DEFAULT_BOOT_ELF) std::cout << "Using default boot file" << std::endl; - return std::filesystem::current_path() + PS2X_DEFAULT_BOOT_ELF; +#if defined(PLATFORM_VITA) + return std::filesystem::path(PS2X_DEFAULT_BOOT_ELF); +#endif + return std::filesystem::current_path() + std::filesystem::path(PS2X_DEFAULT_BOOT_ELF); #else - std::cout << "Using agrs" << std::endl; - if (argc > 0) - { - return std::filesystem::path(argv[0]).parent_path(); - } - throw std::runtime_error("Unable to determine executable path"); #endif } @@ -81,23 +86,22 @@ int main(int argc, char *argv[]) try { - std::filesystem::path exePath = getExecutablePath(argc, argv); + std::filesystem::path pathObj = getExecutablePath(argc, argv); - std::string elfPath = exePath.filename().string(); - std::filesystem::path pathObj(elfPath); - std::string folderName = pathObj.filename().string(); - std::string normalizedId = normalizeGameId(folderName); + std::string filePathStr = pathObj.string(); + std::string elfName = pathObj.filename().string(); + std::string normalizedId = normalizeGameId(elfName); std::string windowTitle = "PS2-Recomp | "; const char *gameName = getGameName(normalizedId); if (gameName) { - windowTitle += std::string(gameName) + " | " + folderName; + windowTitle += std::string(gameName) + " | " + elfName; } else { - windowTitle += folderName; + windowTitle += elfName; } PS2Runtime runtime; @@ -109,9 +113,9 @@ int main(int argc, char *argv[]) registerAllFunctions(runtime); - if (!runtime.loadELF(elfPath)) + if (!runtime.loadELF(filePathStr)) { - std::cerr << "Failed to load ELF file: " << elfPath << std::endl; + std::cerr << "Failed to load ELF file: " << filePathStr << std::endl; return 1; } @@ -130,4 +134,6 @@ int main(int argc, char *argv[]) { std::cerr << "[main] fatal exception: unknown" << std::endl; } + + return 1; } \ No newline at end of file From 64f85aaa0c6f36c091cf997b86293e2059d9f2fc Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 15:06:15 -0300 Subject: [PATCH 15/30] feat: missing vita changes --- CMakeLists.txt | 34 ++++- ps2xRuntime/CMakeLists.txt | 148 +++++++++++++++++++--- ps2xRuntime/include/ps2_host_backend.h | 3 + ps2xRuntime/src/lib/Kernel/Stubs/Common.h | 2 +- ps2xRuntime/src/lib/ps2_audio.cpp | 2 +- ps2xRuntime/src/lib/ps2_pad.cpp | 2 +- ps2xRuntime/src/lib/ps2_runtime.cpp | 85 ++++--------- ps2xRuntime/src/main.cpp | 17 ++- 8 files changed, 206 insertions(+), 87 deletions(-) create mode 100644 ps2xRuntime/include/ps2_host_backend.h diff --git a/CMakeLists.txt b/CMakeLists.txt index df7b01b9..f55bfb20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,14 @@ cmake_minimum_required(VERSION 3.21) +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{VITASDK}) + set(PS2X_VITA_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake") + if(EXISTS "${PS2X_VITA_TOOLCHAIN_FILE}") + set(CMAKE_TOOLCHAIN_FILE "${PS2X_VITA_TOOLCHAIN_FILE}" CACHE PATH + "Toolchain file used for cross-compiling" FORCE) + message(STATUS "Using VitaSDK toolchain from VITASDK: ${CMAKE_TOOLCHAIN_FILE}") + endif() +endif() + project("PS2 Retro X") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -10,9 +19,22 @@ option(PS2X_BUILD_ANALYZER "Build ps2xAnalyzer" ON) option(PS2X_BUILD_TEST "Build ps2xTest" ON) option(PS2X_BUILD_STUDIO "Build ps2xStudio" ON) -# ARM64 support using sse2neon +set(PS2X_IS_ARM_TARGET OFF) +set(PS2X_IS_AARCH64_TARGET OFF) if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64") - message(STATUS "ARM64 detected, fetching sse2neon") + set(PS2X_IS_ARM_TARGET ON) + set(PS2X_IS_AARCH64_TARGET ON) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm|^ARM") + set(PS2X_IS_ARM_TARGET ON) +endif() + +if((CMAKE_C_COMPILER MATCHES "arm-vita-eabi") OR + (CMAKE_CXX_COMPILER MATCHES "arm-vita-eabi")) + set(PS2X_IS_ARM_TARGET ON) +endif() + +if(PS2X_IS_ARM_TARGET) + message(STATUS "ARM target detected, fetching sse2neon") include(FetchContent) FetchContent_Declare( @@ -26,13 +48,13 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|ARM64") include_directories(${sse2neon_SOURCE_DIR}) add_compile_definitions(USE_SSE2NEON) - if(APPLE) + if(PS2X_IS_AARCH64_TARGET AND APPLE) # macOS ARM64 already uses optimal defaults message(STATUS "macOS ARM64 - using default compiler flags") - elseif(MSVC) + elseif(PS2X_IS_AARCH64_TARGET AND MSVC) # Windows ARM64 - MSVC already uses optimal defaults message(STATUS "Windows ARM64 (MSVC) - using default compiler flags") - else() + elseif(PS2X_IS_AARCH64_TARGET) # Linux ARM64 - add NEON flags message(STATUS "Non-Apple ARM64 - adding NEON compiler flags") add_compile_options(-march=armv8-a+fp+simd) @@ -67,4 +89,4 @@ endif() if(PS2X_BUILD_STUDIO) add_subdirectory("ps2xStudio") -endif() \ No newline at end of file +endif() diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index cb507bcc..585a7f2d 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -5,31 +5,144 @@ project(PS2Runtime VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ReleaseMode.cmake") + set(PS2X_IS_VITA OFF) if((CMAKE_C_COMPILER MATCHES "arm-vita-eabi") OR (CMAKE_CXX_COMPILER MATCHES "arm-vita-eabi")) set(PS2X_IS_VITA ON) endif() -option(PS2X_VITA_CREATE_PACKAGE "Create Vita eboot/vpk targets" OFF) +option(PS2X_VITA_CREATE_PACKAGE "Create Vita eboot/vpk targets" ON) set(PS2X_VITA_APP_NAME "PS2 Retro X" CACHE STRING "Display name for the Vita bubble") set(PS2X_VITA_TITLEID "RANJ00001" CACHE STRING "9-character Vita title id") set(PS2X_VITA_VERSION "01.00" CACHE STRING "Vita app version") +set(PS2X_DEFAULT_BOOT_ELF "" CACHE STRING "Guest ELF path passed directly to main() when argv is unavailable") -include(FetchContent) -set(FETCHCONTENT_QUIET FALSE) -set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) -set(BUILD_GAMES OFF CACHE BOOL "" FORCE) +if(PS2X_IS_VITA) + if(NOT DEFINED ENV{VITASDK}) + message(FATAL_ERROR "VITASDK is not defined. Configure inside the VitaSDK environment or pass the Vita toolchain file.") + endif() -include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ReleaseMode.cmake") + set(PS2X_VITASDK "$ENV{VITASDK}") + if(NOT EXISTS "${PS2X_VITASDK}/share/vita.cmake") + message(FATAL_ERROR "Could not find vita.cmake under ${PS2X_VITASDK}/share.") + endif() -FetchContent_Declare( - raylib - GIT_REPOSITORY "https://github.com/raysan5/raylib.git" - GIT_TAG "5.5" # we will migrate to 4.2.0 later, trust me it will be better - GIT_PROGRESS TRUE -) -FetchContent_MakeAvailable(raylib) + include("${PS2X_VITASDK}/share/vita.cmake" REQUIRED) +else() + include(FetchContent) + set(FETCHCONTENT_QUIET FALSE) + set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(BUILD_GAMES OFF CACHE BOOL "" FORCE) + + FetchContent_Declare( + raylib + GIT_REPOSITORY "https://github.com/raysan5/raylib.git" + GIT_TAG "5.5" # we will migrate to 4.2.0 later, trust me it will be better + GIT_PROGRESS TRUE + ) + FetchContent_MakeAvailable(raylib) +endif() + +add_library(ps2_host_backend INTERFACE) + +set(PS2X_DEFAULT_BOOT_ELF_DEFINE "") +if(PS2X_DEFAULT_BOOT_ELF) + set(PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF}") + string(REPLACE "\\" "\\\\" PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF_DEFINE}") + string(REPLACE "\"" "\\\"" PS2X_DEFAULT_BOOT_ELF_DEFINE "${PS2X_DEFAULT_BOOT_ELF_DEFINE}") +endif() + +if(PS2X_IS_VITA) + set(PS2X_VITA_PREFIX "${PS2X_VITASDK}/arm-vita-eabi") + set(PS2X_VITA_EXTRA_INCLUDE_DIRS "" CACHE STRING "Extra include directories for Vita host dependencies") + set(PS2X_VITA_LIBRARY_DIR "${PS2X_VITA_PREFIX}/lib") + set(PS2X_VITA_REQUIRED_LIBRARY_NAMES + raylib + SDL2 + OpenSLES + taihen_stub + SceAppMgr_stub + SceCtrl_stub + SceGxm_stub + SceCommonDialog_stub + SceLibKernel_stub + SceAudio_stub + SceTouch_stub + SceHid_stub + SceMotion_stub + SceSysmodule_stub + SceIofilemgr_stub + SceNetCtl_stub + SceNet_stub + SceDisplay_stub + SceAppUtil_stub + SceAudioIn_stub + ScePower_stub + SceProcessmgr_stub + SceIme_stub + libIMGEGL_stub_weak + libgpu_es4_ext_stub_weak + libGLESv2_stub_weak + ) + + set(PS2X_VITA_INCLUDE_DIRS + "${PS2X_VITA_PREFIX}/include" + ) + foreach(PS2X_VITA_OPTIONAL_INCLUDE_DIR IN ITEMS + "${PS2X_VITA_PREFIX}/include/raylib" + "${PS2X_VITA_PREFIX}/include/SDL2") + if(EXISTS "${PS2X_VITA_OPTIONAL_INCLUDE_DIR}") + list(APPEND PS2X_VITA_INCLUDE_DIRS "${PS2X_VITA_OPTIONAL_INCLUDE_DIR}") + endif() + endforeach() + if(PS2X_VITA_EXTRA_INCLUDE_DIRS) + list(APPEND PS2X_VITA_INCLUDE_DIRS ${PS2X_VITA_EXTRA_INCLUDE_DIRS}) + endif() + + if(NOT EXISTS "${PS2X_VITA_PREFIX}/include/SDL2/SDL.h") + message(FATAL_ERROR + "Missing SDL2 headers under ${PS2X_VITA_PREFIX}/include/SDL2. " + "Quenom/raylib-5.5-vita requires SDL2 with PVR support installed into VitaSDK.") + endif() + + if(NOT EXISTS "${PS2X_VITA_LIBRARY_DIR}") + message(FATAL_ERROR "Missing Vita library directory: ${PS2X_VITA_LIBRARY_DIR}") + endif() + + function(ps2x_find_vita_library out_var library_name) + string(MAKE_C_IDENTIFIER "${library_name}" library_id) + find_library(${out_var} + NAMES "${library_name}" "${library_name}.a" "lib${library_name}.a" + PATHS "${PS2X_VITA_LIBRARY_DIR}" + NO_DEFAULT_PATH + ) + if(NOT ${out_var}) + message(FATAL_ERROR + "Could not find Vita library '${library_name}' under ${PS2X_VITA_LIBRARY_DIR}. " + "Install Quenom/raylib-5.5-vita and its SDL2/PVR dependencies into VitaSDK first.") + endif() + set(${out_var} "${${out_var}}" PARENT_SCOPE) + endfunction() + + set(PS2X_VITA_RESOLVED_LIBRARIES "") + foreach(PS2X_VITA_LIBRARY_NAME IN LISTS PS2X_VITA_REQUIRED_LIBRARY_NAMES) + ps2x_find_vita_library(PS2X_VITA_LIBRARY_PATH "${PS2X_VITA_LIBRARY_NAME}") + list(APPEND PS2X_VITA_RESOLVED_LIBRARIES "${PS2X_VITA_LIBRARY_PATH}") + endforeach() + + target_compile_definitions(ps2_host_backend INTERFACE PLATFORM_VITA) + target_include_directories(ps2_host_backend INTERFACE ${PS2X_VITA_INCLUDE_DIRS}) + target_link_libraries(ps2_host_backend INTERFACE + ${PS2X_VITA_RESOLVED_LIBRARIES} + m + c + pthread + ) +else() + target_link_libraries(ps2_host_backend INTERFACE raylib) +endif() add_library(ps2_runtime STATIC src/lib/game_overrides.cpp @@ -73,6 +186,12 @@ add_executable(ps2EntryRunner ${RUNNER_SRC_FILES} ) +if(PS2X_DEFAULT_BOOT_ELF_DEFINE) + target_compile_definitions(ps2EntryRunner PRIVATE + PS2X_DEFAULT_BOOT_ELF="${PS2X_DEFAULT_BOOT_ELF_DEFINE}" + ) +endif() + if(MSVC) target_compile_options(ps2EntryRunner PRIVATE /FS) endif() @@ -82,11 +201,10 @@ target_include_directories(ps2_runtime PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/Kernel ) -target_link_libraries(ps2_runtime PRIVATE raylib) +target_link_libraries(ps2_runtime PUBLIC ps2_host_backend) target_link_libraries(ps2EntryRunner PRIVATE ps2_runtime - raylib ) if(PS2X_IS_VITA) diff --git a/ps2xRuntime/include/ps2_host_backend.h b/ps2xRuntime/include/ps2_host_backend.h new file mode 100644 index 00000000..54910f8e --- /dev/null +++ b/ps2xRuntime/include/ps2_host_backend.h @@ -0,0 +1,3 @@ +#pragma once + +#include "raylib.h" diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Common.h b/ps2xRuntime/src/lib/Kernel/Stubs/Common.h index 9d12cee2..59045a1c 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Common.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Common.h @@ -25,5 +25,5 @@ #include #include -#include "raylib.h" +#include "ps2_host_backend.h" #include "Helpers/Support.h" diff --git a/ps2xRuntime/src/lib/ps2_audio.cpp b/ps2xRuntime/src/lib/ps2_audio.cpp index 66e7dcd3..34533f7c 100644 --- a/ps2xRuntime/src/lib/ps2_audio.cpp +++ b/ps2xRuntime/src/lib/ps2_audio.cpp @@ -1,6 +1,6 @@ #include "runtime/ps2_audio.h" #include "runtime/ps2_memory.h" -#include "raylib.h" +#include "ps2_host_backend.h" #include #include diff --git a/ps2xRuntime/src/lib/ps2_pad.cpp b/ps2xRuntime/src/lib/ps2_pad.cpp index 36bcc6fe..10e3baf0 100644 --- a/ps2xRuntime/src/lib/ps2_pad.cpp +++ b/ps2xRuntime/src/lib/ps2_pad.cpp @@ -1,5 +1,5 @@ #include "runtime/ps2_pad.h" -#include "raylib.h" +#include "ps2_host_backend.h" #include namespace diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index cfa39923..882b3304 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -9,11 +9,8 @@ #include "Kernel/Stubs/Audio.h" #include "Kernel/Stubs/GS.h" #include "Kernel/Stubs/MPEG.h" +#include "ps2_host_backend.h" -#include -#if defined(PLATFORM_VITA) -#include -#endif #include #include #include @@ -42,6 +39,13 @@ static constexpr int FB_HEIGHT = 512; static constexpr int DEFAULT_DISPLAY_HEIGHT = 448; static constexpr uint32_t DEFAULT_FB_SIZE = FB_WIDTH * FB_HEIGHT * 4; static constexpr uint32_t DEFAULT_FB_ADDR = (PS2_RAM_SIZE - DEFAULT_FB_SIZE - 0x10000u); +#if defined(PLATFORM_VITA) +static constexpr int HOST_WINDOW_WIDTH = 960; +static constexpr int HOST_WINDOW_HEIGHT = 544; +#else +static constexpr int HOST_WINDOW_WIDTH = FB_WIDTH; +static constexpr int HOST_WINDOW_HEIGHT = DEFAULT_DISPLAY_HEIGHT; +#endif struct ElfHeader { uint32_t magic; @@ -142,11 +146,6 @@ namespace thread_local DispatchHistory g_dispatchHistory; thread_local std::unordered_map g_guestExecutionDepths; -#if defined(PLATFORM_VITA) - std::mutex g_pibInitMutex; - std::atomic g_pibInitialized{false}; -#endif - void pushDispatchPc(uint32_t pc) { DispatchHistory &h = g_dispatchHistory; @@ -276,39 +275,6 @@ namespace return absolute.lexically_normal(); } -#if defined(PLATFORM_VITA) - bool ensurePibInitialized() - { - if (g_pibInitialized.load(std::memory_order_acquire)) - { - return true; - } - - std::lock_guard lock(g_pibInitMutex); - if (g_pibInitialized.load(std::memory_order_relaxed)) - { - return true; - } - - try - { - pibInit(static_cast(PIB_SHACCCG | PIB_GET_PROC_ADDR_CORE)); - g_pibInitialized.store(true, std::memory_order_release); - return true; - } - catch (const std::exception &e) - { - std::cerr << "[vita] pibInit failed: " << e.what() << std::endl; - } - catch (...) - { - std::cerr << "[vita] pibInit failed: unknown exception" << std::endl; - } - - return false; - } -#endif - PS2Runtime::IoPaths &runtimeIoPaths() { static PS2Runtime::IoPaths paths = []() @@ -597,6 +563,14 @@ PS2Runtime::~PS2Runtime() { requestStop(); ps2_syscalls::detachAllGuestHostThreads(); +#if defined(PLATFORM_VITA) + m_audioBackend.stopAll(); + if (IsAudioDeviceReady()) + { + CloseAudioDevice(); + m_audioBackend.setAudioReady(false); + } +#endif if (IsWindowReady()) { CloseWindow(); @@ -667,15 +641,10 @@ bool PS2Runtime::initialize(const char *title) return false; } -#if defined(PLATFORM_VITA) - if (!ensurePibInitialized()) - { - return false; - } -#endif - +#if !defined(PLATFORM_VITA) SetConfigFlags(FLAG_WINDOW_RESIZABLE); - InitWindow(FB_WIDTH, DEFAULT_DISPLAY_HEIGHT, title); +#endif + InitWindow(HOST_WINDOW_WIDTH, HOST_WINDOW_HEIGHT, title); InitAudioDevice(); m_audioBackend.setAudioReady(IsAudioDeviceReady()); SetTargetFPS(60); @@ -1857,15 +1826,17 @@ void PS2Runtime::reacquireGuestExecution(uint32_t depth) void PS2Runtime::cooperativeGuestYield() { - GuestExecutionReleaseScope release(this); - if (m_guestExecutionWaiters.load(std::memory_order_acquire) != 0u) + thread_local uint32_t s_backEdgeYieldCounter = 0u; + const uint32_t waiterCount = m_guestExecutionWaiters.load(std::memory_order_acquire); + const uint32_t yieldInterval = (waiterCount != 0u) ? 64u : 100; + if (++s_backEdgeYieldCounter < yieldInterval) { - std::this_thread::sleep_for(std::chrono::microseconds(100)); - } - else - { - std::this_thread::yield(); + return; } + + s_backEdgeYieldCounter = 0u; + GuestExecutionReleaseScope release(this); + std::this_thread::yield(); } uint8_t PS2Runtime::Load8(uint8_t *rdram, R5900Context *ctx, uint32_t vaddr) diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index 26ba1cf2..94bc88f2 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -63,19 +63,24 @@ namespace std::filesystem::path getExecutablePath(int argc, char *argv[]) { - if (argc >= 2) + if (argc >= 2 && argv[1] && argv[1][0] != '\0') { - std::cout << "Using agrs" << std::endl; + std::cout << "Using argv boot path" << std::endl; return std::filesystem::path(argv[1]); } #if defined(PS2X_DEFAULT_BOOT_ELF) std::cout << "Using default boot file" << std::endl; + const std::filesystem::path configuredPath = std::filesystem::path(PS2X_DEFAULT_BOOT_ELF); #if defined(PLATFORM_VITA) - return std::filesystem::path(PS2X_DEFAULT_BOOT_ELF); + return configuredPath; #endif - return std::filesystem::current_path() + std::filesystem::path(PS2X_DEFAULT_BOOT_ELF); + if (configuredPath.is_absolute()) + { + return configuredPath; + } + return (std::filesystem::current_path() / configuredPath).lexically_normal(); #else - throw std::runtime_error("Unable to determine executable path"); + throw std::runtime_error("Unable to determine executable path. Pass the guest ELF as argv[1] or define PS2X_DEFAULT_BOOT_ELF."); #endif } } @@ -136,4 +141,4 @@ int main(int argc, char *argv[]) } return 1; -} \ No newline at end of file +} From 4bf1f89a695fffeb1beb359316c818dfcae20c92 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 20:52:14 -0300 Subject: [PATCH 16/30] feat: fix more merge --- ps2xRecomp/src/lib/code_generator.cpp | 40 +++++++--- ps2xRuntime/Readme.md | 15 +++- ps2xRuntime/include/ps2_runtime.h | 2 +- ps2xRuntime/src/lib/ps2_runtime.cpp | 9 +-- ps2xRuntime/src/main.cpp | 2 + ps2xTest/src/code_generator_tests.cpp | 81 ++++++++++++++++---- ps2xTest/src/ps2_runtime_expansion_tests.cpp | 49 ++++++------ 7 files changed, 142 insertions(+), 56 deletions(-) diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 5f6cacaf..faa05493 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -241,9 +241,14 @@ namespace ps2recomp auto emitInternalTarget = [&](uint32_t target, uint32_t sourcePc, std::string_view indent) { ss << fmt::format("{}ctx->pc = 0x{:X}u;\n", indent, target); - if (target <= sourcePc) - { - ss << fmt::format("{}runtime->cooperativeGuestYield();\n", indent); + const bool isCallLikeEdge = + (branchInst.opcode == OPCODE_JAL) || + (branchInst.opcode == OPCODE_SPECIAL && branchInst.function == SPECIAL_JALR); + if (target <= sourcePc && !isCallLikeEdge) + { + ss << fmt::format("{}if (runtime->shouldPreemptGuestExecution()) {{\n", indent); + ss << fmt::format("{} return;\n", indent); + ss << fmt::format("{}}}\n", indent); ss << fmt::format("{}goto label_{:x};\n", indent, target); } else @@ -752,6 +757,16 @@ namespace ps2recomp } }; + auto queueLoopResumeEntryTarget = [&](uint32_t target, uint32_t sourcePc) + { + if (target > sourcePc || target == function.start) + { + return; + } + + queueResumeEntryTarget(target); + }; + for (const auto &inst : instructions) { instructionAddresses.insert(inst.address); @@ -777,6 +792,7 @@ namespace ps2recomp instructionAddresses.contains(target)) { result.entryPoints.insert(target); + queueLoopResumeEntryTarget(target, inst.address); } else { @@ -790,6 +806,7 @@ namespace ps2recomp instructionAddresses.contains(target)) { result.entryPoints.insert(target); + queueLoopResumeEntryTarget(target, inst.address); if (inst.opcode == OPCODE_JAL) { @@ -1022,13 +1039,18 @@ namespace ps2recomp } AnalysisResult analysisResult = collectInternalBranchTargets(function, instructions); + std::vector resumeTargets(analysisResult.resumeEntryPoints.begin(), + analysisResult.resumeEntryPoints.end()); auto resumeIt = m_resumeEntryTargetsByOwner.find(function.start); if (resumeIt != m_resumeEntryTargetsByOwner.end()) { - for (uint32_t target : resumeIt->second) - { - analysisResult.entryPoints.insert(target); - } + resumeTargets.insert(resumeTargets.end(), resumeIt->second.begin(), resumeIt->second.end()); + } + std::sort(resumeTargets.begin(), resumeTargets.end()); + resumeTargets.erase(std::unique(resumeTargets.begin(), resumeTargets.end()), resumeTargets.end()); + for (uint32_t target : resumeTargets) + { + analysisResult.entryPoints.insert(target); } const std::unordered_set& internalTargets = analysisResult.entryPoints; @@ -1048,10 +1070,10 @@ namespace ps2recomp ss << " PS_LOG_ENTRY(\"" << sanitizedName << "\");\n"; ss << "#endif\n"; ss << "\n"; - if (resumeIt != m_resumeEntryTargetsByOwner.end() && !resumeIt->second.empty()) + if (!resumeTargets.empty()) { ss << " switch (ctx->pc) {\n"; - for (uint32_t target : resumeIt->second) + for (uint32_t target : resumeTargets) { ss << " case 0x" << std::hex << target << "u: goto label_" << target << ";\n" << std::dec; } diff --git a/ps2xRuntime/Readme.md b/ps2xRuntime/Readme.md index 8016fca6..4ae035fe 100644 --- a/ps2xRuntime/Readme.md +++ b/ps2xRuntime/Readme.md @@ -10,6 +10,19 @@ The runtime library provides the execution environment for recompiled code, incl Take your decompiled code and place the cpp files on ps2xRuntime/src/runner and header files on ps2xRuntime/include and compile/be happy. +## Vita Build Notes + +The Vita runtime uses `Quenom/raylib-5.5-vita` for vita build. I recommend build runtime only. + +Expected environment: + +* `VITASDK` points to your VitaSDK root. +* `Quenom/raylib-5.5-vita` has already been built and installed into `$VITASDK/arm-vita-eabi`. +* SDL2 with the PVR backend required by that raylib fork is also installed into the same VitaSDK prefix. +* `PS2X_DEFAULT_BOOT_ELF` is mandatory, you need to define where your game is like "ux0:data/RANJ00001/game/SLUS_201.84". + +The CMake for `ps2xRuntime` consumes those preinstalled headers and libraries from VitaSDK. It does not fetch or install the Vita raylib fork for you. + ## Adding Custom Function Implementations You can add custom implementations for PS2 system calls or game functions by: @@ -41,4 +54,4 @@ You can patch specific instructions in the recompiled code to fix game issues or * Graphics and sound output require external implementations * Some PS2-specific hardware features may not be fully supported -* Performance may vary based on the complexity of the game \ No newline at end of file +* Performance may vary based on the complexity of the game diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 62853918..e40d96b5 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -513,7 +513,7 @@ class PS2Runtime uint32_t guestHeapEnd() const; uint32_t reserveAsyncCallbackStack(uint32_t size, uint32_t alignment = 16u); void dispatchLoop(uint8_t *rdram, R5900Context *ctx); - void cooperativeGuestYield(); + bool shouldPreemptGuestExecution(); void requestStop(); bool isStopRequested() const; uint32_t guestExecutionWaiterCountForTesting() const diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 882b3304..445328b5 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -1824,19 +1824,18 @@ void PS2Runtime::reacquireGuestExecution(uint32_t depth) } } -void PS2Runtime::cooperativeGuestYield() +bool PS2Runtime::shouldPreemptGuestExecution() { thread_local uint32_t s_backEdgeYieldCounter = 0u; const uint32_t waiterCount = m_guestExecutionWaiters.load(std::memory_order_acquire); - const uint32_t yieldInterval = (waiterCount != 0u) ? 64u : 100; + const uint32_t yieldInterval = (waiterCount != 0u) ? 64u : 100u; if (++s_backEdgeYieldCounter < yieldInterval) { - return; + return false; } s_backEdgeYieldCounter = 0u; - GuestExecutionReleaseScope release(this); - std::this_thread::yield(); + return true; } uint8_t PS2Runtime::Load8(uint8_t *rdram, R5900Context *ctx, uint32_t vaddr) diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index 94bc88f2..9b574c57 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -100,11 +100,13 @@ int main(int argc, char *argv[]) std::string windowTitle = "PS2-Recomp | "; const char *gameName = getGameName(normalizedId); +#if !defined(PLATFORM_VITA) if (gameName) { windowTitle += std::string(gameName) + " | " + elfName; } else +#endif { windowTitle += elfName; } diff --git a/ps2xTest/src/code_generator_tests.cpp b/ps2xTest/src/code_generator_tests.cpp index 1550a0dd..16f5a021 100644 --- a/ps2xTest/src/code_generator_tests.cpp +++ b/ps2xTest/src/code_generator_tests.cpp @@ -1006,6 +1006,30 @@ void register_code_generator_tests() t.IsTrue(generated.find("goto label_c010;") != std::string::npos, "Internal JAL should use goto"); }); + tc.Run("backward internal JAL stays inline and does not return to dispatcher", [](TestCase &t) { + Function func; + func.name = "jal_internal_backward"; + func.start = 0xC100; + func.end = 0xC120; + func.isRecompiled = true; + func.isStub = false; + + Instruction loopHead = makeNop(0xC100); + Instruction backwardJal = makeJal(0xC108, 0xC100); + Instruction delay = makeNop(0xC10C); + + CodeGenerator gen({}, {}); + std::string generated = gen.generateFunction(func, {loopHead, backwardJal, delay}, false); + printGeneratedCode("backward internal JAL stays inline and does not return to dispatcher", generated); + + t.IsTrue(generated.find("SET_GPR_U32(ctx, 31, 0xC110u);") != std::string::npos, + "backward internal JAL should still set RA"); + t.IsTrue(generated.find("goto label_c100;") != std::string::npos, + "backward internal JAL should still re-enter the internal target directly"); + t.IsTrue(generated.find("if (runtime->shouldPreemptGuestExecution())") == std::string::npos, + "backward internal JAL should not expose a mid-call dispatcher return"); + }); + tc.Run("JALR emits indirect call", [](TestCase &t) { Function func; func.name = "jalr_test"; @@ -1034,7 +1058,7 @@ void register_code_generator_tests() t.IsTrue(generated.find("if (ctx->pc != 0xD008u) { return; }") != std::string::npos, "JALR should check return PC"); }); - tc.Run("backward BEQ yields cooperatively on sign-extended internal loop", [](TestCase &t) { + tc.Run("backward BEQ returns to the dispatcher through a resumable loop head", [](TestCase &t) { Function func; func.name = "backward_branch"; func.start = 0x1100; @@ -1043,31 +1067,43 @@ void register_code_generator_tests() func.isStub = false; // 0x1100: nop - // 0x1104: beq $1,$1, target 0x1100 (offset = -2 words) - // 0x1108: nop (delay) + // 0x1104: nop (loop head) + // 0x1108: beq $1,$1, target 0x1104 (offset = -2 words) + // 0x110c: nop (delay) std::vector instructions; instructions.push_back(makeNop(0x1100)); + instructions.push_back(makeNop(0x1104)); - Instruction br = makeBranch(0x1104, 0); - br.simmediate = static_cast(static_cast(-2)); + Instruction br = makeBranch(0x1108, 0); + br.simmediate = static_cast(static_cast(-2)); instructions.push_back(br); - instructions.push_back(makeNop(0x1108)); instructions.push_back(makeNop(0x110c)); + instructions.push_back(makeNop(0x1110)); CodeGenerator gen({}, {}); - std::string generated = gen.generateFunction(func, instructions, false); - printGeneratedCode("backward BEQ yields cooperatively on sign-extended internal loop", generated); + CodeGenerator::AnalysisResult analysis = gen.collectInternalBranchTargets(func, instructions); + t.IsTrue(analysis.resumeEntryPoints.contains(0x1104u), + "mid-function backward loop head should be resumable so preemption can return to the dispatcher"); - t.IsTrue(generated.find("label_1100:") != std::string::npos, "target should emit a label"); - t.IsTrue(generated.find("ctx->pc = 0x1100u;") != std::string::npos, + std::string generated = gen.generateFunction(func, instructions, false); + printGeneratedCode("backward BEQ returns to the dispatcher through a resumable loop head", generated); + + t.IsTrue(generated.find("label_1104:") != std::string::npos, "target should emit a label"); + t.IsTrue(generated.find("switch (ctx->pc)") != std::string::npos, + "resumable backward loop heads should emit a wrapper resume switch"); + t.IsTrue(generated.find("case 0x1104u: goto label_1104;") != std::string::npos, + "mid-function backward loop head should be re-enterable after returning to the dispatcher"); + t.IsTrue(generated.find("ctx->pc = 0x1104u;") != std::string::npos, "backward internal branch should preserve the loop target in ctx->pc"); - t.IsTrue(generated.find("runtime->cooperativeGuestYield();") != std::string::npos, - "backward internal branch should yield guest execution before re-entering the loop"); - t.IsTrue(generated.find("goto label_1100;") != std::string::npos, - "backward internal branch should re-enter the in-function label after yielding"); - t.IsTrue(generated.find("ctx->pc = 0x1100u;\n return;") == std::string::npos, - "backward internal branch should not return to the dispatcher for a non-entry internal label"); + t.IsTrue(generated.find("if (runtime->shouldPreemptGuestExecution()) {") != std::string::npos, + "backward internal branch should consult the runtime preemption policy before re-entering the loop"); + t.IsTrue(generated.find("return;") != std::string::npos, + "backward internal branch should return to the dispatcher when the runtime preemption policy requests it"); + t.IsTrue(generated.find("runtime->cooperativeGuestYield();") == std::string::npos, + "backward internal branch should no longer emit an unconditional cooperative-yield call"); + t.IsTrue(generated.find("goto label_1104;") != std::string::npos, + "backward internal branch should still re-enter the in-function label when it keeps the current slice"); }); tc.Run("branch-likely places delay slot only in taken path", [](TestCase &t) { @@ -1183,6 +1219,8 @@ void register_code_generator_tests() t.IsTrue(generated.find("switch (jumpTarget)") != std::string::npos, "JR via non-RA register should emit switch for internal targets"); + t.IsTrue(generated.find("switch (ctx->pc)") == std::string::npos, + "JR fallback labels should not be promoted to dispatcher resume sites"); t.IsTrue(generated.find("case 0x1400u: goto label_1400;") != std::string::npos, "switch should include in-function entry label"); t.IsTrue(generated.find("case 0x140Cu: goto label_140c;") != std::string::npos, @@ -1249,6 +1287,15 @@ void register_code_generator_tests() CodeGenerator gen({}, {}); gen.setConfiguredJumpTables({configured}); + CodeGenerator::AnalysisResult analysis = gen.collectInternalBranchTargets( + func, + {lui, addiu, sll, addu, lw, jr, jrDelay, target0, target1}); + + t.IsFalse(analysis.resumeEntryPoints.contains(0x1620u), + "configured JR table targets should stay in-function dispatch labels"); + t.IsFalse(analysis.resumeEntryPoints.contains(0x1630u), + "configured JR table targets should not become dispatcher resume sites"); + std::string generated = gen.generateFunction( func, {lui, addiu, sll, addu, lw, jr, jrDelay, target0, target1}, @@ -1261,6 +1308,8 @@ void register_code_generator_tests() "configured table target 0x1620 should be emitted"); t.IsTrue(generated.find("case 0x1630u: goto label_1630;") != std::string::npos, "configured table target 0x1630 should be emitted"); + t.IsTrue(generated.find("switch (ctx->pc)") == std::string::npos, + "configured JR table labels should not emit a top-level dispatcher resume switch"); t.IsTrue(generated.find("case 0x1600u: goto label_1600;") == std::string::npos, "configured table should avoid broad JR fallback labels"); }); diff --git a/ps2xTest/src/ps2_runtime_expansion_tests.cpp b/ps2xTest/src/ps2_runtime_expansion_tests.cpp index ec7c3981..48959d02 100644 --- a/ps2xTest/src/ps2_runtime_expansion_tests.cpp +++ b/ps2xTest/src/ps2_runtime_expansion_tests.cpp @@ -107,9 +107,9 @@ namespace std::atomic gSerializedGuestActive{0}; std::atomic gSerializedGuestMaxActive{0}; - std::atomic gCooperativeYieldEntryCount{0}; - std::atomic gCooperativeYieldAllowFirstYield{false}; - std::atomic gCooperativeYieldPeerRan{false}; + std::atomic gPreemptionPolicyEntryCount{0}; + std::atomic gPreemptionPolicyAllowFirstProbe{false}; + std::atomic gPreemptionPolicyPeerRan{false}; void testSerializedGuestStep(uint8_t *, R5900Context *ctx, PS2Runtime *) { @@ -132,32 +132,33 @@ namespace } } - void testCooperativeGuestYieldStep(uint8_t *, R5900Context *ctx, PS2Runtime *runtime) + void testPreemptionPolicyStep(uint8_t *, R5900Context *ctx, PS2Runtime *runtime) { if (!ctx || !runtime) { return; } - const int32_t entryIndex = gCooperativeYieldEntryCount.fetch_add(1, std::memory_order_acq_rel) + 1; + const int32_t entryIndex = gPreemptionPolicyEntryCount.fetch_add(1, std::memory_order_acq_rel) + 1; if (entryIndex == 1) { - while (!gCooperativeYieldAllowFirstYield.load(std::memory_order_acquire)) + while (!gPreemptionPolicyAllowFirstProbe.load(std::memory_order_acquire)) { std::this_thread::yield(); } - for (int attempt = 0; attempt < 32 && - !gCooperativeYieldPeerRan.load(std::memory_order_acquire); + bool shouldPreempt = false; + for (int attempt = 0; attempt < 256 && + !shouldPreempt; ++attempt) { - runtime->cooperativeGuestYield(); + shouldPreempt = runtime->shouldPreemptGuestExecution(); } - setRegU32(*ctx, 2, gCooperativeYieldPeerRan.load(std::memory_order_acquire) ? 1u : 0u); + setRegU32(*ctx, 2, shouldPreempt ? 1u : 0u); } else { - gCooperativeYieldPeerRan.store(true, std::memory_order_release); + gPreemptionPolicyPeerRan.store(true, std::memory_order_release); setRegU32(*ctx, 2, 2u); } @@ -297,19 +298,19 @@ void register_ps2_runtime_expansion_tests() "dispatchLoop should not execute guest code concurrently on one runtime"); }); - tc.Run("cooperative guest yield lets another guest thread run without leaving the current function", [](TestCase &t) + tc.Run("guest preemption policy requests a dispatcher handoff when another guest thread contends", [](TestCase &t) { PS2Runtime runtime; std::vector rdram(PS2_RAM_SIZE, 0u); constexpr uint32_t kFirstEntry = 0x190000u; constexpr uint32_t kSecondEntry = 0x1A0000u; - gCooperativeYieldEntryCount.store(0, std::memory_order_release); - gCooperativeYieldAllowFirstYield.store(false, std::memory_order_release); - gCooperativeYieldPeerRan.store(false, std::memory_order_release); + gPreemptionPolicyEntryCount.store(0, std::memory_order_release); + gPreemptionPolicyAllowFirstProbe.store(false, std::memory_order_release); + gPreemptionPolicyPeerRan.store(false, std::memory_order_release); - runtime.registerFunction(kFirstEntry, &testCooperativeGuestYieldStep); - runtime.registerFunction(kSecondEntry, &testCooperativeGuestYieldStep); + runtime.registerFunction(kFirstEntry, &testPreemptionPolicyStep); + runtime.registerFunction(kSecondEntry, &testPreemptionPolicyStep); R5900Context firstCtx{}; R5900Context secondCtx{}; @@ -323,7 +324,7 @@ void register_ps2_runtime_expansion_tests() const bool firstEntered = waitUntil([&]() { - return gCooperativeYieldEntryCount.load(std::memory_order_acquire) >= 1; + return gPreemptionPolicyEntryCount.load(std::memory_order_acquire) >= 1; }, std::chrono::milliseconds(100)); std::thread secondWorker([&]() @@ -336,7 +337,7 @@ void register_ps2_runtime_expansion_tests() return runtime.guestExecutionWaiterCountForTesting() > 0u; }, std::chrono::milliseconds(100)); - gCooperativeYieldAllowFirstYield.store(true, std::memory_order_release); + gPreemptionPolicyAllowFirstProbe.store(true, std::memory_order_release); if (firstWorker.joinable()) { @@ -347,12 +348,12 @@ void register_ps2_runtime_expansion_tests() secondWorker.join(); } - t.IsTrue(firstEntered, "first guest worker should enter before yielding"); - t.IsTrue(secondContending, "second guest worker should contend for guest execution before the first yields"); - t.IsTrue(gCooperativeYieldPeerRan.load(std::memory_order_acquire), - "second guest worker should run while the first cooperatively yields"); + t.IsTrue(firstEntered, "first guest worker should enter before probing for preemption"); + t.IsTrue(secondContending, "second guest worker should contend for guest execution before the first returns"); + t.IsTrue(gPreemptionPolicyPeerRan.load(std::memory_order_acquire), + "second guest worker should run after the first returns to the dispatcher"); t.Equals(getRegU32(&firstCtx, 2), 1u, - "first guest worker should observe that a peer ran before it resumed"); + "first guest worker should observe that the runtime requested preemption under contention"); }); tc.Run("vblank intc handlers can preempt serialized guest execution", [](TestCase &t) From 3f3e9d6e3fcec829bac2da053c984cd93deea807 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 1 Apr 2026 23:40:20 -0300 Subject: [PATCH 17/30] feat: fix tests --- ps2xTest/src/ps2_sif_dma_tests.cpp | 17 +++++++++++++++++ ps2xTest/src/ps2_sif_rpc_tests.cpp | 15 +++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ps2xTest/src/ps2_sif_dma_tests.cpp b/ps2xTest/src/ps2_sif_dma_tests.cpp index 32f86a84..7a573b7b 100644 --- a/ps2xTest/src/ps2_sif_dma_tests.cpp +++ b/ps2xTest/src/ps2_sif_dma_tests.cpp @@ -28,10 +28,24 @@ namespace ps2_stubs::resetSifState(); ps2_syscalls::resetSoundDriverRpcState(); ps2_syscalls::clearSoundDriverCompatLayout(); + ps2_syscalls::clearDtxCompatLayout(); std::memset(&ctx, 0, sizeof(ctx)); } }; + void setRecvxDtxCompatLayout() + { + PS2DtxCompatLayout layout{}; + layout.rpcSid = 0x7D000000u; + layout.urpcObjBase = 0x01F18000u; + layout.urpcObjLimit = 0x01F1FF00u; + layout.urpcObjStride = 0x20u; + layout.urpcFnTableBase = 0x0034FED0u; + layout.urpcObjTableBase = 0x0034FFD0u; + layout.dispatcherFuncAddr = 0x002FABC0u; + ps2_syscalls::setDtxCompatLayout(layout); + } + #pragma pack(push, 1) struct Ps2SifDmaTransfer { @@ -245,6 +259,7 @@ void register_ps2_sif_dma_tests() tc.Run("sceSifSetDma acknowledges DTX work-buffer transfers by advancing the EE footer ticket", [](TestCase &t) { TestEnv env; + setRecvxDtxCompatLayout(); constexpr uint32_t kClientAddr = 0x0002D000u; constexpr uint32_t kDtxSid = 0x7D000000u; @@ -306,6 +321,7 @@ void register_ps2_sif_dma_tests() tc.Run("sceSifSetDma applies SJX DTX payloads into the emulated SJRMT data ring", [](TestCase &t) { TestEnv env; + setRecvxDtxCompatLayout(); constexpr uint32_t kClientAddr = 0x0002E000u; constexpr uint32_t kDtxSid = 0x7D000000u; @@ -426,6 +442,7 @@ void register_ps2_sif_dma_tests() tc.Run("sceSifSetDma lets active PS2RNA playback drain emulated SJRMT data", [](TestCase &t) { TestEnv env; + setRecvxDtxCompatLayout(); constexpr uint32_t kClientAddr = 0x0002F000u; constexpr uint32_t kDtxSid = 0x7D000000u; diff --git a/ps2xTest/src/ps2_sif_rpc_tests.cpp b/ps2xTest/src/ps2_sif_rpc_tests.cpp index f5904723..612bcbb6 100644 --- a/ps2xTest/src/ps2_sif_rpc_tests.cpp +++ b/ps2xTest/src/ps2_sif_rpc_tests.cpp @@ -91,10 +91,24 @@ namespace ps2_stubs::resetSifState(); ps2_syscalls::resetSoundDriverRpcState(); ps2_syscalls::clearSoundDriverCompatLayout(); + ps2_syscalls::clearDtxCompatLayout(); std::memset(&ctx, 0, sizeof(ctx)); } }; + void setRecvxDtxCompatLayout() + { + PS2DtxCompatLayout layout{}; + layout.rpcSid = 0x7D000000u; + layout.urpcObjBase = 0x01F18000u; + layout.urpcObjLimit = 0x01F1FF00u; + layout.urpcObjStride = 0x20u; + layout.urpcFnTableBase = 0x0034FED0u; + layout.urpcObjTableBase = 0x0034FFD0u; + layout.dispatcherFuncAddr = 0x002FABC0u; + ps2_syscalls::setDtxCompatLayout(layout); + } + void setRegU32(R5900Context &ctx, int reg, uint32_t value) { ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); @@ -549,6 +563,7 @@ void register_ps2_sif_rpc_tests() tc.Run("SifCallRpc prefers stack ABI for DTX URPC when both packs look plausible", [](TestCase &t) { TestEnv env; + setRecvxDtxCompatLayout(); constexpr uint32_t kClientAddr = 0x0002B000u; constexpr uint32_t kDtxSid = 0x7D000000u; From fe9bd32d1bbe407678995fbfa02168040ffedb83 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 2 Apr 2026 01:05:07 -0300 Subject: [PATCH 18/30] feat: last missing feature --- ps2xRuntime/include/ps2_call_list.h | 2 + ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp | 84 +++++++++++++++++++ ps2xRuntime/src/lib/Kernel/Stubs/GS.h | 2 + .../src/lib/Kernel/Stubs/Helpers/Support.h | 29 +++++++ ps2xRuntime/src/lib/ps2_audio.cpp | 17 ++++ ps2xRuntime/src/lib/ps2_runtime.cpp | 8 +- ps2xRuntime/src/main.cpp | 4 +- ps2xTest/src/ps2_gs_tests.cpp | 11 --- 8 files changed, 142 insertions(+), 15 deletions(-) diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index a7464944..1a51d012 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -352,12 +352,14 @@ X(sceGsResetPath) \ X(sceGsSetDefClear) \ X(sceGsSetDefDBuffDc) \ + X(sceGsSetDefDBuff) \ X(sceGsSetDefDispEnv) \ X(sceGsSetDefDrawEnv) \ X(sceGsSetDefDrawEnv2) \ X(sceGsSetDefLoadImage) \ X(sceGsSetDefStoreImage) \ X(sceGsSwapDBuffDc) \ + X(sceGsSwapDBuff) \ X(sceGsSyncPath) \ X(sceGsSyncV) \ X(sceGsSyncVCallback) \ diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp index 948c76e3..3e72d1c5 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp @@ -779,6 +779,65 @@ namespace ps2_stubs setReturnS32(ctx, 0); } + void sceGsSetDefDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t envAddr = getRegU32(ctx, 4); + uint32_t psm = getRegU32(ctx, 5); + uint32_t w = getRegU32(ctx, 6); + uint32_t h = getRegU32(ctx, 7); + const uint32_t ztest = readStackU32(rdram, ctx, 16); + const uint32_t zpsm = readStackU32(rdram, ctx, 20); + const uint32_t clear = readStackU32(rdram, ctx, 24); + (void)clear; + + if (w == 0u) + { + w = 640u; + } + if (h == 0u) + { + h = 448u; + } + + const uint32_t fbw = std::max(1u, (w + 63u) / 64u); + const uint64_t pmode = makePmode(1u, 1u, 0u, 0u, 0u, 0x80u); + const uint64_t smode2 = + (static_cast(g_gparam.interlace & 0x1u) << 0) | + (static_cast(g_gparam.ffmode & 0x1u) << 1); + const uint64_t dispfb = makeDispFb(0u, fbw, psm, 0u, 0u); + const uint64_t display = makeDisplay(636u, 32u, 0u, 0u, w - 1u, h - 1u); + + const int32_t drawWidth = static_cast(w); + const int32_t drawHeight = static_cast(h); + + uint32_t zbufAddr = 0u; + { + R5900Context temp = *ctx; + sceGszbufaddr(rdram, &temp, runtime); + zbufAddr = getRegU32(&temp, 2); + } + + GsDBuffMem db{}; + db.disp[0].pmode = pmode; + db.disp[0].smode2 = smode2; + db.disp[0].dispfb = dispfb; + db.disp[0].display = display; + db.disp[0].bgcolor = 0u; + db.disp[1] = db.disp[0]; + + db.giftag0 = {makeGiftagAplusD(14u), 0x0E0E0E0E0E0E0E0EULL}; + seedGsDrawEnv1(db.draw0, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); + db.giftag1 = db.giftag0; + seedGsDrawEnv1(db.draw1, drawWidth, drawHeight, 0u, fbw, psm, zbufAddr, zpsm, ztest, false); + + if (!writeGsDBuff(rdram, envAddr, db)) + { + setReturnS32(ctx, -1); + return; + } + setReturnS32(ctx, 0); + } + void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t envAddr = getRegU32(ctx, 4); @@ -977,6 +1036,31 @@ namespace ps2_stubs setReturnS32(ctx, static_cast(which ^ 1u)); } + void sceGsSwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t envAddr = getRegU32(ctx, 4); + const uint32_t which = getRegU32(ctx, 5) & 1u; + + GsDBuffMem db{}; + if (!runtime || !readGsDBuff(rdram, envAddr, db)) + { + setReturnS32(ctx, -1); + return; + } + + applyGsDispEnv(runtime, db.disp[which]); + if (which == 0u) + { + applyGsRegPairs(runtime, reinterpret_cast(&db.draw0), 8u); + } + else + { + applyGsRegPairs(runtime, reinterpret_cast(&db.draw1), 8u); + } + + setReturnS32(ctx, static_cast(which ^ 1u)); + } + void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int32_t mode = static_cast(getRegU32(ctx, 4)); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/GS.h b/ps2xRuntime/src/lib/Kernel/Stubs/GS.h index 82ce7b9d..a4d5cfbd 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/GS.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.h @@ -15,12 +15,14 @@ namespace ps2_stubs void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSetDefDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGsSwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h index 9de08082..04f3d756 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h @@ -1489,6 +1489,17 @@ namespace GsClearMem clear1; }; + struct GsDBuffMem + { + GsDispEnvMem disp[2]; + GsGiftagMem giftag0; + GsDrawEnv1Mem draw0; + GsClearMem clear0; + GsGiftagMem giftag1; + GsDrawEnv1Mem draw1; + GsClearMem clear1; + }; + struct GsImageMem { uint16_t x; @@ -1746,6 +1757,15 @@ namespace return true; } + static bool readGsDBuff(uint8_t* rdram, uint32_t addr, GsDBuffMem& out) + { + const uint8_t* ptr = getConstMemPtr(rdram, addr); + if (!ptr) + return false; + std::memcpy(&out, ptr, sizeof(out)); + return true; + } + static bool writeGsDBuffDc(uint8_t *rdram, uint32_t addr, const GsDBuffDcMem &db) { uint8_t *ptr = getMemPtr(rdram, addr); @@ -1755,6 +1775,15 @@ namespace return true; } + static bool writeGsDBuff(uint8_t* rdram, uint32_t addr, const GsDBuffMem& db) + { + uint8_t* ptr = getMemPtr(rdram, addr); + if (!ptr) + return false; + std::memcpy(ptr, &db, sizeof(db)); + return true; + } + static bool readGsRegPairs(uint8_t *rdram, uint32_t addr, GsRegPairMem *pairs, size_t pairCount) { if (!pairs || pairCount == 0u) diff --git a/ps2xRuntime/src/lib/ps2_audio.cpp b/ps2xRuntime/src/lib/ps2_audio.cpp index 34533f7c..6bb64949 100644 --- a/ps2xRuntime/src/lib/ps2_audio.cpp +++ b/ps2xRuntime/src/lib/ps2_audio.cpp @@ -228,6 +228,9 @@ void PS2AudioBackend::play(uint32_t sampleAddr, float pitch, float volume, uint3 void PS2AudioBackend::pruneFinishedSounds() { +#if defined(PLATFORM_VITA) + return; +#else auto &sounds = m_impl->activeSounds; auto it = sounds.begin(); while (it != sounds.end()) @@ -242,11 +245,20 @@ void PS2AudioBackend::pruneFinishedSounds() ++it; } } +#endif } void PS2AudioBackend::playDecodedSample(uint32_t sampleKey, DecodedSample &sample, float pitch, float volume, bool isBgm) { +#if defined(PLATFORM_VITA) + (void)sampleKey; + (void)sample; + (void)pitch; + (void)volume; + (void)isBgm; + return; +#else if (!m_audioReady || sample.pcm.empty()) return; @@ -292,6 +304,7 @@ void PS2AudioBackend::playDecodedSample(uint32_t sampleKey, DecodedSample &sampl SetSoundVolume(snd, volume); m_impl->activeSounds.push_back({snd, sampleKey}); PlaySound(snd); +#endif } void PS2AudioBackend::stop(uint32_t voiceId) @@ -302,10 +315,14 @@ void PS2AudioBackend::stop(uint32_t voiceId) void PS2AudioBackend::stopAll() { std::lock_guard lock(m_mutex); +#if defined(PLATFORM_VITA) + return; +#else for (auto &t : m_impl->activeSounds) { StopSound(t.snd); UnloadSound(t.snd); } m_impl->activeSounds.clear(); +#endif } diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 445328b5..98348406 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -565,6 +565,8 @@ PS2Runtime::~PS2Runtime() ps2_syscalls::detachAllGuestHostThreads(); #if defined(PLATFORM_VITA) m_audioBackend.stopAll(); + m_audioBackend.setAudioReady(false); +#else if (IsAudioDeviceReady()) { CloseAudioDevice(); @@ -641,12 +643,14 @@ bool PS2Runtime::initialize(const char *title) return false; } -#if !defined(PLATFORM_VITA) +#if defined(PLATFORM_VITA) + InitWindow(HOST_WINDOW_WIDTH, HOST_WINDOW_HEIGHT, title); // raylib vita does not support audio +#else SetConfigFlags(FLAG_WINDOW_RESIZABLE); -#endif InitWindow(HOST_WINDOW_WIDTH, HOST_WINDOW_HEIGHT, title); InitAudioDevice(); m_audioBackend.setAudioReady(IsAudioDeviceReady()); +#endif SetTargetFPS(60); return true; diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index 9b574c57..37289aca 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -54,10 +54,10 @@ namespace size_t dot = result.find('.'); if (dot != std::string::npos) result.erase(dot, 1); - +#if !defined(PLATFORM_VITA) std::ranges::transform(result, result.begin(), [](unsigned char character) { return static_cast(std::toupper(character)); }); - +#endif return result; } diff --git a/ps2xTest/src/ps2_gs_tests.cpp b/ps2xTest/src/ps2_gs_tests.cpp index 3f4728a4..55f62092 100644 --- a/ps2xTest/src/ps2_gs_tests.cpp +++ b/ps2xTest/src/ps2_gs_tests.cpp @@ -23,17 +23,6 @@ namespace std::atomic g_gsSyncCallbackHits{0u}; std::atomic g_gsSyncCallbackLastTick{0u}; - struct GsImageMem - { - uint16_t x; - uint16_t y; - uint16_t width; - uint16_t height; - uint16_t vramAddr; - uint8_t vramWidth; - uint8_t psm; - }; - static_assert(sizeof(GsImageMem) == 12, "GsImageMem size mismatch"); void setRegU32(R5900Context &ctx, int reg, uint32_t value) From 35af001b4ab836863370cafdf1203ee2d3b3d307 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 2 Apr 2026 01:06:25 -0300 Subject: [PATCH 19/30] feat: added missing import --- ps2xTest/src/ps2_gs_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ps2xTest/src/ps2_gs_tests.cpp b/ps2xTest/src/ps2_gs_tests.cpp index 55f62092..4f1e1080 100644 --- a/ps2xTest/src/ps2_gs_tests.cpp +++ b/ps2xTest/src/ps2_gs_tests.cpp @@ -7,6 +7,7 @@ #include "runtime/ps2_gs_psmct32.h" #include "runtime/ps2_gs_psmt4.h" #include "runtime/ps2_gs_psmt8.h" +#include "Stubs/Helpers/Support.h" #include "Stubs/GS.h" #include From eadb2903316ab2be5b1d462030b2ba8a7d2fcb20 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 2 Apr 2026 01:46:02 -0300 Subject: [PATCH 20/30] feat: rename test local functions --- ps2xTest/src/ps2_gs_tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ps2xTest/src/ps2_gs_tests.cpp b/ps2xTest/src/ps2_gs_tests.cpp index 4f1e1080..2c48ef58 100644 --- a/ps2xTest/src/ps2_gs_tests.cpp +++ b/ps2xTest/src/ps2_gs_tests.cpp @@ -86,14 +86,14 @@ namespace ctx->pc = 0u; } - void writeGsImage(uint8_t *rdram, uint32_t addr, const GsImageMem &image) + void writeGsImageTest(uint8_t *rdram, uint32_t addr, const GsImageMem &image) { std::memcpy(rdram + addr, &image, sizeof(image)); } - void writeGsImage(std::vector &rdram, uint32_t addr, const GsImageMem &image) + void writeGsImageTest(std::vector &rdram, uint32_t addr, const GsImageMem &image) { - writeGsImage(rdram.data(), addr, image); + writeGsImageTest(rdram.data(), addr, image); } void writePSMT4Texel(std::vector &vram, uint32_t tbp, uint32_t tbw, uint32_t x, uint32_t y, uint8_t index) @@ -2623,7 +2623,7 @@ void register_ps2_gs_tests() 0xD0u, 0xE0u, 0xF0u, 0xFFu, }; - writeGsImage(rdram, kImageAddr, image); + writeGsImageTest(rdram, kImageAddr, image); std::memcpy(rdram + kSrcAddr, pixels, sizeof(pixels)); R5900Context loadCtx{}; From 061dc6b8821a42305382ec738a129e10d8fc48bf Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 2 Apr 2026 02:56:44 -0300 Subject: [PATCH 21/30] feat: init syscall on ps2 list --- ps2xRuntime/include/ps2_call_list.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index 1a51d012..3a02ddd7 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -10,6 +10,7 @@ X(ResetEE) \ X(SetMemoryMode) \ \ + X(InitThread) \ X(CreateThread) \ X(DeleteThread) \ X(StartThread) \ From 1ea448ec1310c22a6891544e399e9bde55cfd899 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 2 Apr 2026 11:02:59 -0300 Subject: [PATCH 22/30] feat: added DMA helpers --- ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp | 111 ++++++++++++++++++++++- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp index 49106dbb..9fcaaf4d 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp @@ -3,6 +3,37 @@ namespace ps2_stubs { + namespace + { + struct SceDmaEnv + { + uint8_t sts = 0; + uint8_t std = 0; + uint8_t mfd = 0; + uint8_t rele = 0; + uint32_t pcr = 0; + uint32_t sqwc = 0; + uint32_t rbor = 0; + uint32_t rbsr = 0; + }; + + static_assert(sizeof(SceDmaEnv) == 0x14, "sceDmaEnv must match the guest ABI"); + + constexpr uint32_t DMA_REG_CTRL = 0x1000E000u; + constexpr uint32_t DMA_REG_PCR = 0x1000E020u; + constexpr uint32_t DMA_REG_SQWC = 0x1000E030u; + constexpr uint32_t DMA_REG_RBSR = 0x1000E040u; + constexpr uint32_t DMA_REG_RBOR = 0x1000E050u; + constexpr uint32_t DMA_REG_STADR = 0x1000E060u; + + constexpr std::array kStsTable = {0u, 0u, 0u, 3u, 0u, 1u, 0u, 0u, 2u, 0u}; + constexpr std::array kStdTable = {0u, 1u, 2u, 0u, 0u, 0u, 3u, 0u, 0u, 0u}; + constexpr std::array kMfdTable = {0u, 2u, 3u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; + + std::mutex g_dmaEnvMutex; + SceDmaEnv g_dmaCurrentEnv; + } + void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { setReturnU32(ctx, getRegU32(ctx, 4)); @@ -27,7 +58,13 @@ namespace ps2_stubs void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); + const uint32_t envAddr = getRegU32(ctx, 4); + if (uint8_t *dst = getMemPtr(rdram, envAddr)) + { + std::lock_guard lock(g_dmaEnvMutex); + std::memcpy(dst, &g_dmaCurrentEnv, sizeof(g_dmaCurrentEnv)); + } + setReturnU32(ctx, envAddr); } void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -42,12 +79,80 @@ namespace ps2_stubs void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); + const uint32_t envAddr = getRegU32(ctx, 4); + const uint8_t *src = getConstMemPtr(rdram, envAddr); + if (!src || !runtime) + { + setReturnS32(ctx, -1); + return; + } + + SceDmaEnv env{}; + std::memcpy(&env, src, sizeof(env)); + + if (env.sts >= kStsTable.size()) + { + setReturnS32(ctx, -1); + return; + } + if (env.std >= kStdTable.size()) + { + setReturnS32(ctx, -2); + return; + } + if (env.mfd >= kMfdTable.size()) + { + setReturnS32(ctx, -3); + return; + } + if (env.rele >= 7u) + { + setReturnS32(ctx, -4); + return; + } + + PS2Memory &mem = runtime->memory(); + uint32_t ctrl = mem.readIORegister(DMA_REG_CTRL); + ctrl = (ctrl & 0xFFFFFFCFu) | (static_cast(kStsTable[env.sts]) << 4); + ctrl = (ctrl & 0xFFFFFF3Fu) | (static_cast(kStdTable[env.std]) << 6); + ctrl = (ctrl & 0xFFFFFFF3u) | (static_cast(kMfdTable[env.mfd]) << 2); + if (env.rele == 0u) + { + ctrl &= 0xFFFFFFFDu; + } + else + { + ctrl = ((ctrl | 0x2u) & 0xFFFFFCFFu) | ((static_cast(env.rele - 1u) & 0x7u) << 8); + } + + mem.writeIORegister(DMA_REG_CTRL, ctrl); + mem.writeIORegister(DMA_REG_PCR, env.pcr); + mem.writeIORegister(DMA_REG_SQWC, env.sqwc); + mem.writeIORegister(DMA_REG_RBOR, env.rbor); + mem.writeIORegister(DMA_REG_RBSR, env.rbsr); + + { + std::lock_guard lock(g_dmaEnvMutex); + g_dmaCurrentEnv = env; + } + + setReturnS32(ctx, 0); } void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); + const uint32_t newAddr = getRegU32(ctx, 4); + uint32_t oldAddr = 0; + if (runtime) + { + PS2Memory &mem = runtime->memory(); + oldAddr = mem.readIORegister(DMA_REG_STADR); + if (newAddr != 0xFFFFFFFFu) + { + mem.writeIORegister(DMA_REG_STADR, newAddr); + } + } + setReturnU32(ctx, oldAddr); } void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) From 95e75ef0f7d5d5cff26946a31fc480d94fc73a6a Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 12:39:10 -0300 Subject: [PATCH 23/30] feat: faster builds feat: more implement for darkcloud --- ps2xRuntime/CMakeLists.txt | 24 + ps2xRuntime/include/ps2_call_list.h | 28 + ps2xRuntime/include/runtime/ps2_gs_gpu.h | 16 + ps2xRuntime/include/runtime/ps2_memory.h | 2 + ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp | 17 +- ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp | 19 + ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp | 658 ++++++++++++++++++ ps2xRuntime/src/lib/Kernel/Stubs/GS.h | 25 + .../src/lib/Kernel/Stubs/Helpers/Support.h | 21 + .../src/lib/Kernel/Stubs/MemoryCard.cpp | 18 + ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h | 1 + ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp | 106 ++- ps2xRuntime/src/lib/Kernel/Stubs/SIF.h | 2 + ps2xRuntime/src/lib/ps2_gs_gpu.cpp | 56 +- ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp | 125 ++-- ps2xRuntime/src/lib/ps2_memory.cpp | 53 +- ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp | 57 ++ ps2xTest/src/ps2_gs_tests.cpp | 435 ++++++++++++ ps2xTest/src/ps2_memory_tests.cpp | 214 ++++++ ps2xTest/src/ps2_runtime_expansion_tests.cpp | 149 ++++ ps2xTest/src/ps2_runtime_io_tests.cpp | 28 + ps2xTest/src/ps2_sif_rpc_tests.cpp | 105 +++ 22 files changed, 2083 insertions(+), 76 deletions(-) diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index 585a7f2d..ddaf5fa5 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -5,6 +5,23 @@ project(PS2Runtime VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +option(PS2X_ENABLE_RUNNER_UNITY_BUILD "Build ps2EntryRunner with CMake unity build" ON) +set(PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE 8 CACHE STRING "Unity build batch size for ps2EntryRunner") +option(PS2X_ENABLE_SCCACHE "Use sccache as compiler launcher when available" ON) + +if(PS2X_ENABLE_SCCACHE) + find_program(PS2X_SCCACHE_PROGRAM sccache) + if(PS2X_SCCACHE_PROGRAM) + if(CMAKE_C_COMPILER) + set(CMAKE_C_COMPILER_LAUNCHER "${PS2X_SCCACHE_PROGRAM}") + endif() + set(CMAKE_CXX_COMPILER_LAUNCHER "${PS2X_SCCACHE_PROGRAM}") + message(STATUS "Using sccache: ${PS2X_SCCACHE_PROGRAM}") + else() + message(STATUS "sccache not found; continuing without compiler launcher") + endif() +endif() + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/ReleaseMode.cmake") set(PS2X_IS_VITA OFF) @@ -186,6 +203,13 @@ add_executable(ps2EntryRunner ${RUNNER_SRC_FILES} ) +if(PS2X_ENABLE_RUNNER_UNITY_BUILD) + set_target_properties(ps2EntryRunner PROPERTIES + UNITY_BUILD ON + UNITY_BUILD_BATCH_SIZE "${PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE}" + ) +endif() + if(PS2X_DEFAULT_BOOT_ELF_DEFINE) target_compile_definitions(ps2EntryRunner PRIVATE PS2X_DEFAULT_BOOT_ELF="${PS2X_DEFAULT_BOOT_ELF_DEFINE}" diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index 3a02ddd7..39fbf1e3 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -344,6 +344,18 @@ X(sceDmaWatch) \ X(sceFsInit) \ X(sceFsReset) \ + X(sceGifPkAddGsAD) \ + X(sceGifPkAddGsData) \ + X(sceGifPkCloseGifTag) \ + X(sceGifPkCnt) \ + X(sceGifPkEnd) \ + X(sceGifPkInit) \ + X(sceGifPkOpenGifTag) \ + X(sceGifPkRef) \ + X(sceGifPkRefLoadImage) \ + X(sceGifPkReset) \ + X(sceGifPkReserve) \ + X(sceGifPkTerminate) \ X(sceGsExecLoadImage) \ X(sceGsExecStoreImage) \ X(sceGsGetGParam) \ @@ -365,6 +377,19 @@ X(sceGsSyncV) \ X(sceGsSyncVCallback) \ X(sceGszbufaddr) \ + X(sceVif1PkAddGsAD) \ + X(sceVif1PkAlign) \ + X(sceVif1PkCall) \ + X(sceVif1PkCloseDirectCode) \ + X(sceVif1PkCloseGifTag) \ + X(sceVif1PkCnt) \ + X(sceVif1PkEnd) \ + X(sceVif1PkInit) \ + X(sceVif1PkOpenDirectCode) \ + X(sceVif1PkOpenGifTag) \ + X(sceVif1PkReset) \ + X(sceVif1PkReserve) \ + X(sceVif1PkTerminate) \ X(sceeFontInit) \ X(sceeFontLoadFont) \ X(sceeFontPrintfAt) \ @@ -385,6 +410,7 @@ X(sceMcChdir) \ X(sceMcClose) \ X(sceMcDelete) \ + X(sceMcEnd) \ X(sceMcFlush) \ X(sceMcFormat) \ X(sceMcGetDir) \ @@ -480,6 +506,7 @@ X(sceSetPtm) \ X(sceSifAddCmdHandler) \ X(sceSifAllocIopHeap) \ + X(sceSifAllocSysMemory) \ X(sceSifBindRpc) \ X(sceSifCheckStatRpc) \ X(sceSifDmaStat) \ @@ -487,6 +514,7 @@ X(sceSifExitCmd) \ X(sceSifExitRpc) \ X(sceSifFreeIopHeap) \ + X(sceSifFreeSysMemory) \ X(sceSifGetDataTable) \ X(sceSifGetIopAddr) \ X(sceSifGetNextRequest) \ diff --git a/ps2xRuntime/include/runtime/ps2_gs_gpu.h b/ps2xRuntime/include/runtime/ps2_gs_gpu.h index 511c2a2c..1549bbee 100644 --- a/ps2xRuntime/include/runtime/ps2_gs_gpu.h +++ b/ps2xRuntime/include/runtime/ps2_gs_gpu.h @@ -148,6 +148,20 @@ struct GSXYOffsetReg uint16_t ofy; }; +struct GSTexaReg +{ + uint8_t ta0; + bool aem; + uint8_t ta1; +}; + +struct GSTexClutReg +{ + uint8_t cbw; + uint8_t cou; + uint16_t cov; +}; + struct GSContext { GSFrameReg frame; @@ -271,6 +285,8 @@ class GS bool m_prmodecont = true; bool m_pabe = false; + GSTexaReg m_texa{0u, false, 0u}; + GSTexClutReg m_texclut{0u, 0u, 0u}; GSBitBltBuf m_bitbltbuf{}; GSTrxPos m_trxpos{}; diff --git a/ps2xRuntime/include/runtime/ps2_memory.h b/ps2xRuntime/include/runtime/ps2_memory.h index d8cd8141..6dfc4cce 100644 --- a/ps2xRuntime/include/runtime/ps2_memory.h +++ b/ps2xRuntime/include/runtime/ps2_memory.h @@ -380,6 +380,8 @@ class PS2Memory uint8_t *m_vu1Code = nullptr; uint8_t *m_vu1Data = nullptr; bool m_path3Masked = false; + uint32_t m_vif1PendingPath2ImageQwc = 0u; + bool m_vif1PendingPath2DirectHl = false; std::vector> m_path3MaskedFifo; struct PendingTransfer diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp index 0b37516c..19f55b2c 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Audio.cpp @@ -6,7 +6,8 @@ namespace ps2_stubs namespace { constexpr uint32_t kLibSdCmdSetParam = 0x8010u; - constexpr uint32_t kLibSdCmdBlockTrans = 0x80E0u; + constexpr uint32_t kLibSdCmdBlockTrans = 0x80D0u; + constexpr uint32_t kLibSdCmdBlockTransAlt = 0x80E0u; constexpr uint32_t kAudioPositionMask = 0x00FFFFFFu; struct AudioStubState @@ -45,14 +46,20 @@ namespace ps2_stubs const uint32_t cmdArg0 = getRegU32(ctx, 6); const uint32_t cmdArg1 = getRegU32(ctx, 7); const uint32_t sp = getRegU32(ctx, 29); - const uint32_t arg4 = FAST_READ32(sp + 0x10u); - const uint32_t arg5 = FAST_READ32(sp + 0x14u); - const uint32_t arg6 = FAST_READ32(sp + 0x18u); + const uint32_t arg4Reg = getRegU32(ctx, 8); + const uint32_t arg5Reg = getRegU32(ctx, 9); + const uint32_t arg6Reg = getRegU32(ctx, 10); + const uint32_t arg4Stk = FAST_READ32(sp + 0x10u); + const uint32_t arg5Stk = FAST_READ32(sp + 0x14u); + const uint32_t arg6Stk = FAST_READ32(sp + 0x18u); + const uint32_t arg4 = (arg4Reg != 0u) ? arg4Reg : arg4Stk; + const uint32_t arg5 = (arg5Reg != 0u) ? arg5Reg : arg5Stk; + const uint32_t arg6 = (arg6Reg != 0u) ? arg6Reg : arg6Stk; std::lock_guard lock(g_audio_stub_mutex); g_audio_stub_state.initialized = true; - if (cmd == kLibSdCmdBlockTrans) + if (cmd == kLibSdCmdBlockTrans || cmd == kLibSdCmdBlockTransAlt) { if (arg4 != 0u) { diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp index 9fcaaf4d..b32e44a2 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/DMA.cpp @@ -172,6 +172,25 @@ namespace ps2_stubs void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + if (runtime) + { + PS2Memory &mem = runtime->memory(); + + // libdma reset leaves the controller runnable; DMAE must be re-enabled or chain submissions will be accepted but never execute. + mem.writeIORegister(DMA_REG_CTRL, 0u); + mem.writeIORegister(DMA_REG_PCR, 0u); + mem.writeIORegister(DMA_REG_SQWC, 0u); + mem.writeIORegister(DMA_REG_RBOR, 0u); + mem.writeIORegister(DMA_REG_RBSR, 0u); + mem.writeIORegister(DMA_REG_STADR, 0u); + mem.writeIORegister(DMA_REG_CTRL, 1u); + } + + { + std::lock_guard lock(g_dmaEnvMutex); + g_dmaCurrentEnv = {}; + } + setReturnS32(ctx, 0); } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp index 3e72d1c5..2d1b2aaa 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.cpp @@ -278,6 +278,494 @@ namespace ps2_stubs runtime->gs().writeRegister(static_cast(clear.xyz2b.reg & 0xFFu), clear.xyz2b.value); runtime->gs().writeRegister(static_cast(clear.testb.reg & 0xFFu), clear.testb.value); } + + void refreshPacketBuilderPendingCount(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr); + void writePacketBuilderCurrent(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t currentAddr); + void logVif1PacketStateOp(const char *op, + R5900Context *ctx, + uint32_t stateAddr, + uint32_t currentAddr, + uint32_t aux0, + uint32_t aux1) + { + static uint32_t s_vif1PacketOpLogCount = 0u; + if (s_vif1PacketOpLogCount >= 96u) + { + return; + } + + RUNTIME_LOG("[vif1:packet] idx=" << s_vif1PacketOpLogCount + << " op=" << op + << " pc=0x" << std::hex << ctx->pc + << " ra=0x" << getRegU32(ctx, 31) + << " state=0x" << stateAddr + << " current=0x" << currentAddr + << " aux0=0x" << aux0 + << " aux1=0x" << aux1 + << std::dec << std::endl); + ++s_vif1PacketOpLogCount; + } + + void initPacketBuilderState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t baseAddr = getRegU32(ctx, 5); + const uint32_t words[4] = {baseAddr, baseAddr, 0u, 0u}; + writeGuestBytes(rdram, + runtime, + stateAddr, + reinterpret_cast(words), + sizeof(words)); + } + + uint32_t terminatePacketBuilderState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return 0u; + } + + const uint32_t zero = 0u; + while ((currentAddr & 0xCu) != 0u) + { + writeGuestBytes(rdram, + runtime, + currentAddr, + reinterpret_cast(&zero), + sizeof(zero)); + currentAddr += 4u; + } + + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr); + writeGuestBytes(rdram, + runtime, + stateAddr + 8u, + reinterpret_cast(&zero), + sizeof(zero)); + return currentAddr; + } + + void resetPacketBuilderState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + uint32_t baseAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr + 4u, baseAddr)) + { + setReturnU32(ctx, 0u); + return; + } + + const uint32_t words[4] = {baseAddr, baseAddr, 0u, 0u}; + writeGuestBytes(rdram, + runtime, + stateAddr, + reinterpret_cast(words), + sizeof(words)); + setReturnU32(ctx, baseAddr); + } + + bool tryReadQwordFromGuest(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint64_t &outQword) + { + uint32_t low = 0u; + uint32_t high = 0u; + if (!tryReadWordFromGuest(rdram, runtime, addr, low) || + !tryReadWordFromGuest(rdram, runtime, addr + 4u, high)) + { + return false; + } + + outQword = static_cast(low) | (static_cast(high) << 32u); + return true; + } + + void writeGuestU32(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint32_t value) + { + writeGuestBytes(rdram, + runtime, + addr, + reinterpret_cast(&value), + sizeof(value)); + } + + void writeGuestU64(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint64_t value) + { + writeGuestBytes(rdram, + runtime, + addr, + reinterpret_cast(&value), + sizeof(value)); + } + + void writeGuestVec128(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, __m128i value) + { + alignas(16) __m128i temp = value; + writeGuestBytes(rdram, + runtime, + addr, + reinterpret_cast(&temp), + sizeof(temp)); + } + + void refreshPacketBuilderPendingCount(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr) + { + uint32_t currentAddr = 0u; + uint32_t pendingCountAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr) || + !tryReadWordFromGuest(rdram, runtime, stateAddr + 8u, pendingCountAddr) || + pendingCountAddr == 0u || + currentAddr <= pendingCountAddr) + { + return; + } + + uint32_t countWord = 0u; + if (!tryReadWordFromGuest(rdram, runtime, pendingCountAddr, countWord)) + { + return; + } + + const uint32_t deltaBytes = currentAddr - pendingCountAddr; + uint32_t deltaQwords = 0u; + if (deltaBytes >= 16u) + { + deltaQwords = (deltaBytes >> 4u) - 1u; + } + + countWord = (countWord & 0xFFFF0000u) | (deltaQwords & 0xFFFFu); + writeGuestU32(rdram, runtime, pendingCountAddr, countWord); + } + + void writePacketBuilderCurrent(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t currentAddr) + { + writeGuestU32(rdram, runtime, stateAddr, currentAddr); + refreshPacketBuilderPendingCount(rdram, runtime, stateAddr); + } + + uint32_t reservePacketBuilderWords(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t wordCount) + { + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return 0u; + } + + const uint32_t reservedAddr = currentAddr; + currentAddr += wordCount * 4u; + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr); + return reservedAddr; + } + + void alignPacketBuilderState(uint8_t *rdram, + PS2Runtime *runtime, + uint32_t stateAddr, + uint32_t alignMode, + uint32_t reserveWords) + { + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return; + } + + const uint32_t adjusted = (alignMode + 2u) & 31u; + const uint32_t shift = (32u - adjusted) & 31u; + const uint32_t lowMask = 0xFFFFFFFFu >> shift; + const uint32_t alignedBase = currentAddr & ~lowMask; + uint32_t targetAddr = alignedBase + (reserveWords << 2u); + if (targetAddr < currentAddr) + { + targetAddr = (targetAddr + 1u) + lowMask; + } + + const uint32_t zero = 0u; + while (currentAddr < targetAddr) + { + writeGuestU32(rdram, runtime, currentAddr, zero); + currentAddr += 4u; + } + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr); + } + + void openPacketGifTag(uint8_t *rdram, + R5900Context *ctx, + PS2Runtime *runtime, + uint32_t stateAddr, + uint32_t openAddrOffset) + { + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return; + } + + writeGuestVec128(rdram, runtime, currentAddr, GPR_VEC(ctx, 5)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u); + writeGuestU32(rdram, runtime, stateAddr + openAddrOffset, currentAddr); + } + + void closePacketGifTag(uint8_t *rdram, PS2Runtime *runtime, uint32_t stateAddr, uint32_t openAddrOffset) + { + uint32_t openAddr = 0u; + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr + openAddrOffset, openAddr) || + !tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr) || + openAddr == 0u) + { + return; + } + + uint64_t tagValue = 0u; + if (!tryReadQwordFromGuest(rdram, runtime, openAddr, tagValue)) + { + return; + } + + uint32_t packetQwords = ((currentAddr - openAddr) >> 3u) - 2u; + const uint32_t flag = static_cast((tagValue >> 58u) & 0x3u); + if (flag != 1u) + { + packetQwords >>= 1u; + } + if (flag != 2u) + { + uint32_t nreg = static_cast((tagValue >> 60u) & 0xFu); + if (nreg == 0u) + { + nreg = 16u; + } + packetQwords = (packetQwords + nreg - 1u) / nreg; + } + + tagValue += static_cast(packetQwords); + writeGuestU32(rdram, runtime, stateAddr + openAddrOffset, 0u); + writeGuestU64(rdram, runtime, openAddr, tagValue); + + while ((currentAddr & 0xCu) != 0u) + { + writeGuestU32(rdram, runtime, currentAddr, 0u); + currentAddr += 4u; + } + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr); + } + } + + void sceGifPkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return; + } + + const uint64_t dataValue = GPR_U64(ctx, 6); + const uint64_t regValue = static_cast(getRegU32(ctx, 5)); + writeGuestU64(rdram, runtime, currentAddr, dataValue); + writeGuestU64(rdram, runtime, currentAddr + 8u, regValue); + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u); + } + + void sceGifPkAddGsData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return; + } + + writeGuestU64(rdram, runtime, currentAddr, GPR_U64(ctx, 5)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 8u); + } + + void sceGifPkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)ctx; + closePacketGifTag(rdram, runtime, getRegU32(ctx, 4), 12u); + } + + void sceGifPkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t countValue = getRegU32(ctx, 5); + const uint32_t extraValue = getRegU32(ctx, 6); + const uint32_t tagWord = getRegU32(ctx, 7) | 0x10000000u; + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[4] = {tagWord, 0u, countValue, extraValue}; + const uint32_t nextAddr = packetAddr + 16u; + + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, nextAddr); + } + + void sceGifPkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t countValue = getRegU32(ctx, 5); + const uint32_t extraValue = getRegU32(ctx, 6); + const uint32_t tagWord = getRegU32(ctx, 7) | 0x70000000u; + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[4] = {tagWord, countValue, extraValue, 0u}; + const uint32_t nextAddr = packetAddr + 16u; + + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, nextAddr); + } + + void sceGifPkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + initPacketBuilderState(rdram, ctx, runtime); + } + + void sceGifPkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + openPacketGifTag(rdram, ctx, runtime, getRegU32(ctx, 4), 12u); + } + + void sceGifPkRef(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t refAddr = getRegU32(ctx, 5) & 0x9FFFFFFFu; + const uint32_t tagWord = getRegU32(ctx, 9) | getRegU32(ctx, 6) | 0x30000000u; + const uint32_t extra0 = getRegU32(ctx, 7); + const uint32_t extra1 = getRegU32(ctx, 8); + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[4] = {tagWord, refAddr, extra0, extra1}; + + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 16u); + } + + void sceGifPkRefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t dbp = getRegU32(ctx, 5) & 0xFFFFu; + const uint32_t dpsm = getRegU32(ctx, 6) & 0xFFu; + const uint32_t dbw = getRegU32(ctx, 7) & 0xFFFFu; + uint32_t dataAddr = getRegU32(ctx, 8); + uint32_t qwcRemaining = getRegU32(ctx, 9); + const uint32_t dsax = getRegU32(ctx, 10); + const uint32_t dsay = getRegU32(ctx, 11); + const uint32_t width = readStackU32(rdram, ctx, 0); + const uint32_t height = readStackU32(rdram, ctx, 8); + + // Open a 4-register A+D GIF tag and emit the GS load-image setup. + { + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[4] = {0x10000000u, 0u, 0u, 0u}; + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 16u); + + const uint64_t giftag[2] = {makeGiftagAplusD(4u), 0xEULL}; + uint32_t currentAddr = packetAddr + 16u; + writeGuestBytes(rdram, runtime, currentAddr, reinterpret_cast(giftag), sizeof(giftag)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u); + writeGuestU32(rdram, runtime, stateAddr + 12u, currentAddr); + + const uint64_t bitbltbuf = + (static_cast(dbp) << 32u) | + (static_cast(dbw & 0xFFu) << 48u) | + (static_cast(dpsm) << 56u); + const uint64_t trxpos = + (static_cast(dsax) << 32u) | + (static_cast(dsay) << 48u); + const uint64_t trxreg = + static_cast(width) | + (static_cast(height) << 32u); + + { + uint32_t addr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, addr)) + { + return; + } + writeGuestU64(rdram, runtime, addr, bitbltbuf); + writeGuestU64(rdram, runtime, addr + 8u, static_cast(GS_REG_BITBLTBUF)); + addr += 16u; + writeGuestU64(rdram, runtime, addr, trxpos); + writeGuestU64(rdram, runtime, addr + 8u, static_cast(GS_REG_TRXPOS)); + addr += 16u; + writeGuestU64(rdram, runtime, addr, trxreg); + writeGuestU64(rdram, runtime, addr + 8u, static_cast(GS_REG_TRXREG)); + addr += 16u; + writeGuestU64(rdram, runtime, addr, 0u); + writeGuestU64(rdram, runtime, addr + 8u, static_cast(GS_REG_TRXDIR)); + addr += 16u; + writePacketBuilderCurrent(rdram, runtime, stateAddr, addr); + closePacketGifTag(rdram, runtime, stateAddr, 12u); + } + } + + while (qwcRemaining != 0u) + { + const uint32_t chunkQwc = std::min(qwcRemaining, 32767u); + + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[4] = {0x10000000u, 0u, 0u, 0u}; + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 16u); + + const uint32_t reservedAddr = reservePacketBuilderWords(rdram, runtime, stateAddr, 4u); + const bool isLastChunk = (chunkQwc == qwcRemaining); + const uint64_t gifTag = + static_cast(chunkQwc) | + (isLastChunk ? 0x0800000000008000ULL : 0x0800000000000000ULL); + writeGuestU64(rdram, runtime, reservedAddr, gifTag); + writeGuestU64(rdram, runtime, reservedAddr + 8u, 0u); + + const uint32_t refPacketAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t refWords[4] = {0x30000000u | chunkQwc, dataAddr & 0x9FFFFFFFu, 0u, 0u}; + writeGuestBytes(rdram, + runtime, + refPacketAddr, + reinterpret_cast(refWords), + sizeof(refWords)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, refPacketAddr + 16u); + + qwcRemaining -= chunkQwc; + dataAddr += chunkQwc * 16u; + } + } + + void sceGifPkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + resetPacketBuilderState(rdram, ctx, runtime); + } + + void sceGifPkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, reservePacketBuilderWords(rdram, runtime, getRegU32(ctx, 4), getRegU32(ctx, 5))); + } + + void sceGifPkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, terminatePacketBuilderState(rdram, ctx, runtime)); } static void resetGsSyncVState() @@ -1237,4 +1725,174 @@ namespace ps2_stubs } setReturnS32(ctx, 0); } + + void sceVif1PkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return; + } + + const uint64_t dataValue = GPR_U64(ctx, 6); + const uint32_t words[4] = { + static_cast(dataValue & 0xFFFFFFFFu), + static_cast(dataValue >> 32u), + getRegU32(ctx, 5), + 0u, + }; + writeGuestBytes(rdram, + runtime, + currentAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 16u); + } + + void sceVif1PkAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + alignPacketBuilderState(rdram, + runtime, + getRegU32(ctx, 4), + getRegU32(ctx, 5), + getRegU32(ctx, 6)); + } + + void sceVif1PkCall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t refAddr = getRegU32(ctx, 5) & 0x9FFFFFFFu; + const uint32_t tagWord = getRegU32(ctx, 6) | 0x50000000u; + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[2] = {tagWord, refAddr}; + + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 8u); + writeGuestU32(rdram, runtime, stateAddr + 12u, 0u); + } + + void sceVif1PkCloseDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + uint32_t currentAddr = 0u; + uint32_t openAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr) || + !tryReadWordFromGuest(rdram, runtime, stateAddr + 12u, openAddr) || + openAddr == 0u) + { + return; + } + + const uint32_t currentMinusTag = currentAddr - 4u; + const uint32_t wordCount = (currentMinusTag - openAddr) >> 2u; + const uint32_t qwordCount = wordCount >> 2u; + uint32_t tagWord = 0u; + if (!tryReadWordFromGuest(rdram, runtime, openAddr, tagWord)) + { + return; + } + + logVif1PacketStateOp("close-direct", ctx, stateAddr, currentAddr, openAddr, qwordCount); + + tagWord += qwordCount; + writeGuestU32(rdram, runtime, stateAddr + 12u, 0u); + writeGuestU32(rdram, runtime, openAddr, tagWord); + } + + void sceVif1PkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)ctx; + closePacketGifTag(rdram, runtime, getRegU32(ctx, 4), 20u); + } + + void sceVif1PkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t tagWord = getRegU32(ctx, 5) | 0x10000000u; + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[2] = {tagWord, 0u}; + + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writeGuestU32(rdram, runtime, stateAddr + 12u, 0u); + writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 8u); + } + + void sceVif1PkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t tagWord = getRegU32(ctx, 5) | 0x70000000u; + const uint32_t packetAddr = terminatePacketBuilderState(rdram, ctx, runtime); + const uint32_t words[2] = {tagWord, 0u}; + + writeGuestU32(rdram, runtime, stateAddr + 8u, packetAddr); + writeGuestBytes(rdram, + runtime, + packetAddr, + reinterpret_cast(words), + sizeof(words)); + writeGuestU32(rdram, runtime, stateAddr + 12u, 0u); + writePacketBuilderCurrent(rdram, runtime, stateAddr, packetAddr + 8u); + } + + void sceVif1PkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + initPacketBuilderState(rdram, ctx, runtime); + writeGuestU32(rdram, runtime, getRegU32(ctx, 4) + 20u, 0u); + } + + void sceVif1PkOpenDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + alignPacketBuilderState(rdram, runtime, stateAddr, 2u, 3u); + + uint32_t currentAddr = 0u; + if (!tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr)) + { + return; + } + + const uint32_t tagWord = (getRegU32(ctx, 5) != 0u) ? 0xD0000000u : 0x50000000u; + writeGuestU32(rdram, runtime, currentAddr, tagWord); + writePacketBuilderCurrent(rdram, runtime, stateAddr, currentAddr + 4u); + writeGuestU32(rdram, runtime, stateAddr + 12u, currentAddr); + logVif1PacketStateOp("open-direct", ctx, stateAddr, currentAddr + 4u, currentAddr, tagWord); + } + + void sceVif1PkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + openPacketGifTag(rdram, ctx, runtime, getRegU32(ctx, 4), 20u); + } + + void sceVif1PkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + resetPacketBuilderState(rdram, ctx, runtime); + writeGuestU32(rdram, runtime, getRegU32(ctx, 4) + 20u, 0u); + } + + void sceVif1PkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + const uint32_t stateAddr = getRegU32(ctx, 4); + const uint32_t wordCount = getRegU32(ctx, 5); + uint32_t currentAddr = 0u; + tryReadWordFromGuest(rdram, runtime, stateAddr, currentAddr); + const uint32_t reservedAddr = reservePacketBuilderWords(rdram, runtime, stateAddr, wordCount); + logVif1PacketStateOp("reserve", ctx, stateAddr, currentAddr, reservedAddr, wordCount); + setReturnU32(ctx, reservedAddr); + } + + void sceVif1PkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + setReturnU32(ctx, terminatePacketBuilderState(rdram, ctx, runtime)); + } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/GS.h b/ps2xRuntime/src/lib/Kernel/Stubs/GS.h index a4d5cfbd..8cc2e377 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/GS.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/GS.h @@ -6,6 +6,18 @@ namespace ps2_stubs { void resetGsSyncVCallbackState(); void dispatchGsSyncVCallback(uint8_t *rdram, PS2Runtime *runtime, uint64_t tick); + void sceGifPkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkAddGsData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkRef(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkRefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceGifPkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); @@ -28,4 +40,17 @@ namespace ps2_stubs void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkAddGsAD(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkCall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkCloseDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkCloseGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkCnt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkOpenDirectCode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkOpenGifTag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkReserve(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceVif1PkTerminate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h index 04f3d756..aa507c93 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h @@ -1360,6 +1360,27 @@ namespace << " qwc=0x" << qwc << " tadr=0x" << tadr << " chcr=0x" << chcr << std::dec << std::endl); + + if (!preferNormalCount && (channelBase == 0x10009000u || channelBase == 0x1000A000u)) + { + if (const uint8_t *tagPtr = getConstMemPtr(rdram, tadr)) + { + uint64_t tagLo = 0u; + std::memcpy(&tagLo, tagPtr, sizeof(tagLo)); + uint32_t w2 = 0u; + uint32_t w3 = 0u; + std::memcpy(&w2, tagPtr + 8u, sizeof(w2)); + std::memcpy(&w3, tagPtr + 12u, sizeof(w3)); + RUNTIME_LOG("[sceDmaSend:head] ch=0x" << std::hex << channelBase + << " tagQwc=0x" << static_cast(tagLo & 0xFFFFu) + << " id=0x" << static_cast((tagLo >> 28u) & 0x7u) + << " irq=0x" << static_cast((tagLo >> 31u) & 0x1u) + << " addr=0x" << static_cast((tagLo >> 32u) & 0x7FFFFFFFu) + << " w2=0x" << w2 + << " w3=0x" << w3 + << std::dec << std::endl); + } + } ++g_dmaStubLogCount; } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp index 224ba77c..1f82c353 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.cpp @@ -582,6 +582,24 @@ namespace ps2_stubs setReturnS32(ctx, 0); } + void sceMcEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + { + std::lock_guard lock(g_mcStateMutex); + closeMcFilesLocked(); + g_mcNextFd = 1; + g_mcLastCmd = 0; + g_mcLastResult = 0; + for (McPortState &state : g_mcPorts) + { + state.currentDir = "/"; + } + } + + // libmc teardown is just a local state reset in this immediate runtime model. + setReturnS32(ctx, 0); + } + void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const int32_t fd = static_cast(getRegU32(ctx, 4)); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h index 08e1bbf1..8cb953d3 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/MemoryCard.h @@ -8,6 +8,7 @@ namespace ps2_stubs void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceMcEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp index 96260854..780591db 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp @@ -2,6 +2,8 @@ #include "SIF.h" #include "../Syscalls/RPC.h" +#include + namespace ps2_stubs { void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -50,9 +52,11 @@ namespace ps2_stubs std::mutex g_sifDmaTransferMutex; uint32_t g_nextSifDmaTransferId = 1u; std::mutex g_sifCmdStateMutex; + std::mutex g_sifHeapMutex; std::unordered_map g_sifRegs; std::unordered_map g_sifSregs; std::unordered_map g_sifCmdHandlers; + std::map g_sifHeapAllocations; uint32_t g_sifCmdBuffer = 0u; uint32_t g_sifSysCmdBuffer = 0u; bool g_sifCmdInitialized = false; @@ -117,6 +121,69 @@ namespace ps2_stubs return id; } + uint32_t alignIopHeapSize(uint32_t size) + { + return (size + (kIopHeapAlign - 1u)) & ~(kIopHeapAlign - 1u); + } + + uint32_t allocateSifHeapBlock(uint32_t requestSize) + { + const uint32_t alignedSize = alignIopHeapSize(requestSize); + if (alignedSize == 0u) + { + return 0u; + } + + std::lock_guard lock(g_sifHeapMutex); + uint32_t candidate = kIopHeapBase; + for (const auto &[addr, size] : g_sifHeapAllocations) + { + if (candidate + alignedSize <= addr) + { + break; + } + + const uint32_t blockEnd = alignIopHeapSize(addr + size); + if (blockEnd > candidate) + { + candidate = blockEnd; + } + } + + if (candidate < kIopHeapBase || candidate + alignedSize > kIopHeapLimit) + { + return 0u; + } + + g_sifHeapAllocations[candidate] = alignedSize; + g_iopHeapNext = candidate + alignedSize; + return candidate; + } + + bool freeSifHeapBlock(uint32_t addr) + { + std::lock_guard lock(g_sifHeapMutex); + const auto it = g_sifHeapAllocations.find(addr); + if (it == g_sifHeapAllocations.end()) + { + return false; + } + + g_sifHeapAllocations.erase(it); + if (g_sifHeapAllocations.empty()) + { + g_iopHeapNext = kIopHeapBase; + } + return true; + } + + void resetSifHeapState() + { + std::lock_guard lock(g_sifHeapMutex); + g_sifHeapAllocations.clear(); + g_iopHeapNext = kIopHeapBase; + } + bool isCopyableGuestAddress(uint32_t addr) { if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) @@ -226,6 +293,7 @@ namespace ps2_stubs { std::lock_guard lock(g_sifCmdStateMutex); seedDefaultSifRegsLocked(); + resetSifHeapState(); } void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -239,17 +307,20 @@ namespace ps2_stubs void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { + (void)rdram; + (void)runtime; + const uint32_t reqSize = getRegU32(ctx, 4); - const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); - if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) - { - setReturnS32(ctx, 0); - return; - } + setReturnU32(ctx, allocateSifHeapBlock(reqSize)); + } - const uint32_t allocAddr = g_iopHeapNext; - g_iopHeapNext += alignedSize; - setReturnS32(ctx, static_cast(allocAddr)); + void sceSifAllocSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + + const uint32_t size = getRegU32(ctx, 5); + setReturnU32(ctx, allocateSifHeapBlock(size)); } void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -291,7 +362,20 @@ namespace ps2_stubs void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - setReturnS32(ctx, 0); + (void)rdram; + (void)runtime; + + const uint32_t addr = getRegU32(ctx, 4); + setReturnS32(ctx, freeSifHeapBlock(addr) ? 0 : -1); + } + + void sceSifFreeSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + (void)rdram; + (void)runtime; + + const uint32_t addr = getRegU32(ctx, 4); + setReturnS32(ctx, freeSifHeapBlock(addr) ? 0 : -1); } void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -424,7 +508,7 @@ namespace ps2_stubs void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - g_iopHeapNext = kIopHeapBase; + resetSifHeapState(); setReturnS32(ctx, 0); } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h index 63fc8b07..7d7f35c3 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h @@ -10,6 +10,7 @@ namespace ps2_stubs void resetSifState(); void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifAllocSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); @@ -17,6 +18,7 @@ namespace ps2_stubs void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); + void sceSifFreeSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); diff --git a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp index 946d1cc6..b02ecae0 100644 --- a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp @@ -545,6 +545,8 @@ void GS::reset() m_curFog = 0; m_prmodecont = true; m_pabe = false; + m_texa = {0u, false, 0u}; + m_texclut = {0u, 0u, 0u}; m_bitbltbuf = {}; m_trxpos = {}; m_trxreg = {}; @@ -1063,7 +1065,6 @@ bool GS::copyLatchedHostPresentationFrame(std::vector &outPixels, return false; } - outPixels = m_hostPresentationFrame; outWidth = m_hostPresentationWidth; outHeight = m_hostPresentationHeight; if (outDisplayFbp) @@ -1072,6 +1073,36 @@ bool GS::copyLatchedHostPresentationFrame(std::vector &outPixels, *outSourceFbp = m_hostPresentationSourceFbp; if (outUsedPreferred) *outUsedPreferred = m_hostPresentationUsedPreferred; + + const size_t packedRowBytes = static_cast(outWidth) * 4u; + outPixels.assign(packedRowBytes * static_cast(outHeight), 0u); + if (outWidth != 0u && outHeight != 0u) + { + const size_t sourceRowBytes = static_cast(kHostFrameWidth) * 4u; + for (uint32_t y = 0; y < outHeight; ++y) + { + const size_t srcOffset = static_cast(y) * sourceRowBytes; + const size_t dstOffset = static_cast(y) * packedRowBytes; + if (srcOffset + packedRowBytes > m_hostPresentationFrame.size() || + dstOffset + packedRowBytes > outPixels.size()) + { + outPixels.clear(); + outWidth = 0u; + outHeight = 0u; + if (outDisplayFbp) + *outDisplayFbp = 0u; + if (outSourceFbp) + *outSourceFbp = 0u; + if (outUsedPreferred) + *outUsedPreferred = false; + return false; + } + + std::memcpy(outPixels.data() + dstOffset, + m_hostPresentationFrame.data() + srcOffset, + packedRowBytes); + } + } return true; } @@ -1377,6 +1408,10 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) regAddr == GS_REG_XYZF3 || regAddr == GS_REG_TEX0_1 || regAddr == GS_REG_TEX0_2 || + regAddr == GS_REG_TEX2_1 || + regAddr == GS_REG_TEX2_2 || + regAddr == GS_REG_TEXCLUT || + regAddr == GS_REG_TEXA || regAddr == GS_REG_XYOFFSET_1 || regAddr == GS_REG_XYOFFSET_2 || regAddr == GS_REG_SCISSOR_1 || @@ -1553,7 +1588,17 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) } case GS_REG_TEX2_1: case GS_REG_TEX2_2: + { + int ci = (regAddr == GS_REG_TEX2_2) ? 1 : 0; + auto &t = m_ctx[ci].tex0; + t.psm = static_cast((value >> 20) & 0x3F); + t.cbp = static_cast((value >> 37) & 0x3FFF); + t.cpsm = static_cast((value >> 51) & 0xF); + t.csm = static_cast((value >> 55) & 0x1); + t.csa = static_cast((value >> 56) & 0x1F); + t.cld = static_cast((value >> 61) & 0x7); break; + } case GS_REG_XYOFFSET_1: case GS_REG_XYOFFSET_2: { @@ -1578,6 +1623,11 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) m_prim.fix = ((value >> 10) & 1) != 0; } break; + case GS_REG_TEXCLUT: + m_texclut.cbw = static_cast(value & 0x3Fu); + m_texclut.cou = static_cast((value >> 6) & 0x3Fu); + m_texclut.cov = static_cast((value >> 12) & 0x3FFu); + break; case GS_REG_SCISSOR_1: case GS_REG_SCISSOR_2: { @@ -1678,7 +1728,6 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) m_pabe = (value & 1u) != 0u; break; case GS_REG_TEXFLUSH: - case GS_REG_TEXCLUT: case GS_REG_SCANMSK: case GS_REG_FOGCOL: case GS_REG_DIMX: @@ -1691,6 +1740,9 @@ void GS::writeRegister(uint8_t regAddr, uint64_t value) break; case GS_REG_TEXA: { + m_texa.ta0 = static_cast(value & 0xFFu); + m_texa.aem = ((value >> 15) & 0x1u) != 0u; + m_texa.ta1 = static_cast((value >> 32) & 0xFFu); PS2_IF_AGRESSIVE_LOGS({ const uint32_t texaIndex = s_debugTexaWriteCount.fetch_add(1u, std::memory_order_relaxed); if (texaIndex < 24u) diff --git a/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp b/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp index 785251f1..de26dc60 100644 --- a/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_rasterizer.cpp @@ -33,6 +33,36 @@ namespace return r | (g << 8) | (b << 16) | (a << 24); } + uint32_t applyTexa(const GSTexaReg &texa, uint8_t psm, uint32_t texel) + { + if (psm == GS_PSM_CT32) + return texel; + + const uint8_t r = static_cast(texel & 0xFFu); + const uint8_t g = static_cast((texel >> 8) & 0xFFu); + const uint8_t b = static_cast((texel >> 16) & 0xFFu); + const bool rgbZero = r == 0u && g == 0u && b == 0u; + uint8_t a = static_cast((texel >> 24) & 0xFFu); + + switch (psm) + { + case GS_PSM_CT24: + a = (texa.aem && rgbZero) ? 0u : texa.ta0; + break; + case GS_PSM_CT16: + case GS_PSM_CT16S: + if ((a & 0x80u) != 0u) + a = texa.ta1; + else + a = (texa.aem && rgbZero) ? 0u : texa.ta0; + break; + default: + break; + } + + return (texel & 0x00FFFFFFu) | (static_cast(a) << 24); + } + uint16_t encodePSMCT16(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return static_cast(((r >> 3) & 0x1Fu) | @@ -137,47 +167,40 @@ namespace uint8_t tb, uint8_t ta) { - const bool useTextureRgb = tex.tcc != 0u; - TextureCombineResult out{vr, vg, vb, ta}; + const bool textureHasAlpha = tex.tcc != 0u; + TextureCombineResult out{tr, tg, tb, textureHasAlpha ? ta : va}; switch (tex.tfx) { - case 0: - if (useTextureRgb) - { - out.r = clampU8((tr * vr) >> 7); - out.g = clampU8((tg * vg) >> 7); - out.b = clampU8((tb * vb) >> 7); - } - out.a = clampU8((ta * va) >> 7); + case 0: // MODULATE + out.r = clampU8((tr * vr) >> 7); + out.g = clampU8((tg * vg) >> 7); + out.b = clampU8((tb * vb) >> 7); + out.a = textureHasAlpha ? clampU8((ta * va) >> 7) : va; break; - case 1: - if (useTextureRgb) - { - out.r = tr; - out.g = tg; - out.b = tb; - } - out.a = ta; + case 1: // DECAL + out.r = tr; + out.g = tg; + out.b = tb; + out.a = textureHasAlpha ? ta : va; break; - case 2: - case 3: - if (useTextureRgb) - { - out.r = clampU8((tr * vr) >> 7); - out.g = clampU8((tg * vg) >> 7); - out.b = clampU8((tb * vb) >> 7); - } - out.a = ta; + case 2: // HIGHLIGHT + out.r = clampU8(((tr * vr) >> 7) + va); + out.g = clampU8(((tg * vg) >> 7) + va); + out.b = clampU8(((tb * vb) >> 7) + va); + out.a = textureHasAlpha ? clampU8(ta + va) : va; + break; + case 3: // HIGHLIGHT2 + out.r = clampU8(((tr * vr) >> 7) + va); + out.g = clampU8(((tg * vg) >> 7) + va); + out.b = clampU8(((tb * vb) >> 7) + va); + out.a = textureHasAlpha ? ta : va; break; default: - if (useTextureRgb) - { - out.r = tr; - out.g = tg; - out.b = tb; - } - out.a = ta; + out.r = tr; + out.g = tg; + out.b = tb; + out.a = textureHasAlpha ? ta : va; break; } @@ -226,7 +249,7 @@ void GSRasterizer::drawPrimitive(GS *gs) { const auto &ctx = gs->activeContext(); PS2_IF_AGRESSIVE_LOGS({ - const uint32_t primitiveIndex = s_debugPrimitiveCount.fetch_add(1, std::memory_order_relaxed); + const uint32_t primitiveIndex = s_debugPrimitiveCount.fetch_add(1u, std::memory_order_relaxed); if (primitiveIndex < 64u) { std::cout << "[gs:prim] idx=" << primitiveIndex @@ -251,6 +274,11 @@ void GSRasterizer::drawPrimitive(GS *gs) << " csm=" << static_cast(ctx.tex0.csm) << " csa=" << static_cast(ctx.tex0.csa) << ")" + << " texclut=(" + << "cbw=" << static_cast(gs->m_texclut.cbw) + << " cou=" << static_cast(gs->m_texclut.cou) + << " cov=" << gs->m_texclut.cov + << ")" << " ofx=" << (ctx.xyoffset.ofx >> 4) << " ofy=" << (ctx.xyoffset.ofy >> 4) << " scissor=(" << ctx.scissor.x0 @@ -309,6 +337,11 @@ void GSRasterizer::drawPrimitive(GS *gs) << " csm=" << static_cast(ctx.tex0.csm) << " csa=" << static_cast(ctx.tex0.csa) << ")" + << " texclut=(" + << "cbw=" << static_cast(gs->m_texclut.cbw) + << " cou=" << static_cast(gs->m_texclut.cou) + << " cov=" << gs->m_texclut.cov + << ")" << " ofx=" << (ctx.xyoffset.ofx >> 4) << " ofy=" << (ctx.xyoffset.ofy >> 4) << " scissor=(" << ctx.scissor.x0 @@ -567,33 +600,29 @@ uint32_t GSRasterizer::lookupCLUT(GS *gs, uint8_t csa, uint8_t sourcePsm) { - uint32_t clutBase = cbp * 256u; const uint32_t clutIndex = resolveClutIndex(index, csm, csa, sourcePsm); - const uint32_t clutX = clutIndex & 0x0Fu; - const uint32_t clutY = clutIndex >> 4; + const uint32_t clutWidth = (gs->m_texclut.cbw != 0u) ? static_cast(gs->m_texclut.cbw) : 1u; + const uint32_t clutX = static_cast(gs->m_texclut.cou) + (clutIndex & 0x0Fu); + const uint32_t clutY = static_cast(gs->m_texclut.cov) + (clutIndex >> 4); if (cpsm == GS_PSM_CT32 || cpsm == GS_PSM_CT24) { - const uint32_t off = GSPSMCT32::addrPSMCT32(cbp, 1u, clutX, clutY); + const uint32_t off = GSPSMCT32::addrPSMCT32(cbp, clutWidth, clutX, clutY); if (off + 4 > gs->m_vramSize) return 0xFFFF00FFu; uint32_t color; std::memcpy(&color, gs->m_vram + off, 4); - return color; + return applyTexa(gs->m_texa, cpsm, color); } if (cpsm == GS_PSM_CT16 || cpsm == GS_PSM_CT16S) { - uint32_t off = addrPSMCT16Family(cbp, 1u, cpsm, clutX, clutY); + uint32_t off = addrPSMCT16Family(cbp, clutWidth, cpsm, clutX, clutY); if (off + 2 > gs->m_vramSize) return 0xFFFF00FFu; uint16_t c16; std::memcpy(&c16, gs->m_vram + off, 2); - uint32_t r = ((c16 >> 0) & 0x1Fu) << 3; - uint32_t g = ((c16 >> 5) & 0x1Fu) << 3; - uint32_t b = ((c16 >> 10) & 0x1Fu) << 3; - uint32_t a = (c16 & 0x8000u) ? 0x80u : 0u; - return r | (g << 8) | (b << 16) | (a << 24); + return applyTexa(gs->m_texa, cpsm, decodePSMCT16(c16)); } return 0xFFFF00FFu; @@ -626,10 +655,10 @@ uint32_t GSRasterizer::sampleTexture(GS *gs, float s, float t, float q, uint16_t sampleV = clampInt(sampleV, 0, texH - 1); if (tex.psm == GS_PSM_CT32 || tex.psm == GS_PSM_CT24) - return readTexelPSMCT32(gs, tex.tbp0, tex.tbw, sampleU, sampleV); + return applyTexa(gs->m_texa, tex.psm, readTexelPSMCT32(gs, tex.tbp0, tex.tbw, sampleU, sampleV)); if (tex.psm == GS_PSM_CT16 || tex.psm == GS_PSM_CT16S) - return readTexelPSMCT16(gs, tex.tbp0, tex.tbw, sampleU, sampleV); + return applyTexa(gs->m_texa, tex.psm, readTexelPSMCT16(gs, tex.tbp0, tex.tbw, sampleU, sampleV)); if (tex.psm == GS_PSM_T4) { diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index a89a8074..c1afdf33 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -1,5 +1,6 @@ #include "runtime/ps2_memory.h" #include "ps2_log.h" +#include #include #include #include @@ -168,6 +169,10 @@ bool PS2Memory::initialize(size_t ramSize) m_gsWriteCount.store(0, std::memory_order_relaxed); m_vifWriteCount.store(0, std::memory_order_relaxed); m_codeRegions.clear(); + m_path3Masked = false; + m_path3MaskedFifo.clear(); + m_vif1PendingPath2ImageQwc = 0u; + m_vif1PendingPath2DirectHl = false; try { @@ -750,6 +755,8 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) if (value & 0x1u) // RST { std::memset(&vif1_regs, 0, sizeof(vif1_regs)); + m_vif1PendingPath2ImageQwc = 0u; + m_vif1PendingPath2DirectHl = false; } if (value & 0x8u) // STC { @@ -861,15 +868,8 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) const uint64_t bytes64 = static_cast(qwCount) * 16ull; uint32_t bytes = (bytes64 > 0xFFFFFFFFull) ? 0xFFFFFFFFu : static_cast(bytes64); const bool scratch = isScratchpad(srcAddr); - uint32_t src = 0; - try - { - src = translateAddress(srcAddr); - } - catch (...) - { - return; - } + uint32_t src = 0; + src = translateAddress(srcAddr); const uint8_t *base2; uint32_t maxSz2; if (scratch) @@ -891,10 +891,27 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) chainBuf.insert(chainBuf.end(), base2 + src, base2 + src + bytes); }; + auto appendCompactVif1TagData = [&](uint32_t localTagAddr, uint32_t qwCount) + { + uint32_t tagPhys = 0u; + const bool tagScratch = isScratchpad(localTagAddr); + tagPhys = translateAddress(localTagAddr); + + const uint8_t *localBase = tagScratch ? m_scratchpad : m_rdram; + const uint32_t localMax = tagScratch ? PS2_SCRATCHPAD_SIZE : PS2_RAM_SIZE; + if (tagPhys + 16u > localMax) + return; + + // VIF1 packet helpers embed 8 bytes of VIF stream in the DMAtag's upper half. + chainBuf.insert(chainBuf.end(), localBase + tagPhys + 8u, localBase + tagPhys + 16u); + appendData(localTagAddr + 16u, qwCount); + }; + int tagsProcessed = 0; while (tagsProcessed < kMaxChainTags) { + const uint32_t currentTagAddr = tagAddr; const bool tagInSPR = isScratchpad(tagAddr); uint32_t physTag = 0; try @@ -997,7 +1014,15 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) } if (hasPayload) - appendData(dataAddr, tagQwc); + { + const bool compactVif1LocalPayload = + (channelBase == 0x10009000u) && + (id == 1u || id == 2u || id == 5u || id == 6u || id == 7u); + if (compactVif1LocalPayload) + appendCompactVif1TagData(currentTagAddr, tagQwc); + else + appendData(dataAddr, tagQwc); + } if (irq && tieEnabled) endChain = true; if (endChain) @@ -1018,10 +1043,18 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) pt.qwc = 0; pt.chainData = std::move(chainBuf); if (channelBase == 0x1000A000) + { m_pendingGifTransfers.push_back(std::move(pt)); + } else if (channelBase == 0x10009000) + { m_pendingVif1Transfers.push_back(std::move(pt)); + } } + // else if (channelBase == 0x10009000u) + // { + + // } } else if (qwc > 0) { diff --git a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp index 5caa7cc1..e478fa42 100644 --- a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp +++ b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp @@ -32,6 +32,21 @@ namespace { std::atomic s_debugVu1KickCount{0}; std::atomic s_debugVif1OpcodeCount{0}; + constexpr uint8_t kGifFmtImage = 2u; + + uint32_t gifImageQwcFromTag(const uint8_t *data, uint32_t sizeBytes) + { + if (!data || sizeBytes < 16u) + return 0u; + + uint64_t tagLo = 0u; + std::memcpy(&tagLo, data, sizeof(tagLo)); + const uint8_t flg = static_cast((tagLo >> 58) & 0x3u); + if (flg != kGifFmtImage) + return 0u; + + return static_cast(tagLo & 0x7FFFu); + } } void PS2Memory::processVIF1Data(uint32_t srcPhys, uint32_t sizeBytes) @@ -65,6 +80,37 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) while (pos + 4 <= sizeBytes) { + if (m_vif1PendingPath2ImageQwc != 0u) + { + const uint32_t availableQw = (sizeBytes - pos) / 16u; + if (availableQw == 0u) + { + break; + } + + const uint32_t chunkQw = std::min(m_vif1PendingPath2ImageQwc, availableQw); + std::vector imagePacket(16u + static_cast(chunkQw) * 16u, 0u); + const uint64_t imageTag = + static_cast(chunkQw & 0x7FFFu) | + ((m_vif1PendingPath2ImageQwc == chunkQw) ? (1ull << 15) : 0ull) | + (static_cast(kGifFmtImage) << 58); + std::memcpy(imagePacket.data(), &imageTag, sizeof(imageTag)); + std::memcpy(imagePacket.data() + 16u, data + pos, static_cast(chunkQw) * 16u); + submitGifPacket(GifPathId::Path2, + imagePacket.data(), + static_cast(imagePacket.size()), + true, + m_vif1PendingPath2DirectHl); + + pos += chunkQw * 16u; + m_vif1PendingPath2ImageQwc -= chunkQw; + if (m_vif1PendingPath2ImageQwc == 0u) + { + m_vif1PendingPath2DirectHl = false; + } + continue; + } + uint32_t cmd; memcpy(&cmd, data + pos, 4); pos += 4; @@ -242,6 +288,17 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) { const bool directHl = (opcode == VIF_DIRECTHL); submitGifPacket(GifPathId::Path2, data + pos, qwCount * 16, true, directHl); + + const uint32_t imageQw = gifImageQwcFromTag(data + pos, qwCount * 16u); + if (imageQw != 0u) + { + const uint32_t inlineImageQw = (qwCount > 0u) ? (qwCount - 1u) : 0u; + if (imageQw > inlineImageQw) + { + m_vif1PendingPath2ImageQwc = imageQw - inlineImageQw; + m_vif1PendingPath2DirectHl = directHl; + } + } } pos += qwCount * 16; diff --git a/ps2xTest/src/ps2_gs_tests.cpp b/ps2xTest/src/ps2_gs_tests.cpp index 2c48ef58..536b1ccc 100644 --- a/ps2xTest/src/ps2_gs_tests.cpp +++ b/ps2xTest/src/ps2_gs_tests.cpp @@ -1050,6 +1050,60 @@ void register_ps2_gs_tests() "relatching should pick up the updated source frame contents"); }); + tc.Run("latched host presentation frame is returned tightly packed for narrower display widths", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GSRegisters regs{}; + regs.pmode = 1ull; + regs.dispfb1 = + 150ull | + (1ull << 9) | + (static_cast(GS_PSM_CT32) << 15); + regs.display1 = + (63ull << 32) | + (63ull << 44); + + GS gs; + gs.init(vram.data(), static_cast(vram.size()), ®s); + + constexpr uint32_t kTopLeft = 0xFF332211u; + constexpr uint32_t kTopRight = 0xFF665544u; + constexpr uint32_t kBottomLeft = 0xFF998877u; + constexpr uint32_t kBottomRight = 0xFFCCBBAAu; + writeReferenceFramePSMCT32Pixel(vram, 150u, 1u, 0u, 0u, kTopLeft); + writeReferenceFramePSMCT32Pixel(vram, 150u, 1u, 1u, 0u, kTopRight); + writeReferenceFramePSMCT32Pixel(vram, 150u, 1u, 0u, 1u, kBottomLeft); + writeReferenceFramePSMCT32Pixel(vram, 150u, 1u, 1u, 1u, kBottomRight); + + gs.latchHostPresentationFrame(); + + std::vector latchedFrame; + uint32_t latchedWidth = 0u; + uint32_t latchedHeight = 0u; + t.IsTrue(gs.copyLatchedHostPresentationFrame(latchedFrame, latchedWidth, latchedHeight), + "latched host presentation should be readable for narrow display widths"); + t.Equals(latchedWidth, 64u, + "latched host presentation should preserve the decoded display width"); + t.Equals(latchedHeight, 64u, + "latched host presentation should preserve the decoded display height"); + t.Equals(static_cast(latchedFrame.size()), 64u * 64u * 4u, + "latched host presentation should return a tightly packed RGBA buffer"); + + uint32_t pixel = 0u; + std::memcpy(&pixel, latchedFrame.data() + 0u, sizeof(pixel)); + t.Equals(pixel, kTopLeft, + "latched host presentation should keep the first row intact"); + std::memcpy(&pixel, latchedFrame.data() + 4u, sizeof(pixel)); + t.Equals(pixel, kTopRight, + "latched host presentation should pack the first row contiguously"); + std::memcpy(&pixel, latchedFrame.data() + (64u * 4u), sizeof(pixel)); + t.Equals(pixel, kBottomLeft, + "latched host presentation should start the second row immediately after the first"); + std::memcpy(&pixel, latchedFrame.data() + (64u * 4u) + 4u, sizeof(pixel)); + t.Equals(pixel, kBottomRight, + "latched host presentation should preserve subsequent rows without the internal 640-pixel stride"); + }); + tc.Run("latched host presentation reads preferred CT32 source with GS swizzle", [](TestCase &t) { std::vector vram(PS2_GS_VRAM_SIZE, 0u); @@ -2344,6 +2398,387 @@ void register_ps2_gs_tests() "T8 CSM1 CLUT sampling should read CT32-uploaded palette entries through GS swizzled addressing"); }); + tc.Run("GS TEX2 updates CLUT state independently from TEX0", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint32_t kWrongClutCbp = 128u; + constexpr uint32_t kExpectedClutCbp = 192u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_T8) << 20) | + (0ull << 26) | + (0ull << 30) | + (1ull << 34) | + (1ull << 35) | + (static_cast(kWrongClutCbp) << 37) | + (static_cast(GS_PSM_CT32) << 51) | + (1ull << 55); + constexpr uint64_t kTex2 = + (static_cast(GS_PSM_T8) << 20) | + (static_cast(kExpectedClutCbp) << 37) | + (static_cast(GS_PSM_CT32) << 51) | + (1ull << 55); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint32_t kWrongColor = 0xFF00FF00u; + constexpr uint32_t kExpectedColor = 0xFF0000FFu; + + const uint32_t texOff = GSPSMT8::addrPSMT8(kTexTbp, 1u, 0u, 0u); + vram[texOff] = 8u; + + const uint32_t wrongClutOff = GSPSMCT32::addrPSMCT32(kWrongClutCbp, 1u, 8u, 0u); + const uint32_t expectedClutOff = GSPSMCT32::addrPSMCT32(kExpectedClutCbp, 1u, 8u, 0u); + std::memcpy(vram.data() + wrongClutOff, &kWrongColor, sizeof(kWrongColor)); + std::memcpy(vram.data() + expectedClutOff, &kExpectedColor, sizeof(kExpectedColor)); + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, 0ull); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_TEX2_1, kTex2); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0) | (16ull << 16)); + + uint32_t pixel = 0u; + std::memcpy(&pixel, vram.data(), sizeof(pixel)); + t.Equals(pixel, kExpectedColor, + "TEX2 should override the active CLUT base and format state without requiring a new TEX0 write"); + }); + + tc.Run("GS TEXCLUT offsets T8 CLUT fetch coordinates", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint32_t kClutCbp = 128u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_T8) << 20) | + (0ull << 26) | + (0ull << 30) | + (1ull << 34) | + (1ull << 35) | + (static_cast(kClutCbp) << 37) | + (static_cast(GS_PSM_CT32) << 51) | + (1ull << 55); + constexpr uint64_t kTexClut = + (1ull << 0) | + (3ull << 6) | + (2ull << 12); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint32_t kWrongColor = 0xFF00FF00u; + constexpr uint32_t kExpectedColor = 0xFF3366CCu; + + const uint32_t texOff = GSPSMT8::addrPSMT8(kTexTbp, 1u, 0u, 0u); + vram[texOff] = 0u; + + const uint32_t wrongClutOff = GSPSMCT32::addrPSMCT32(kClutCbp, 1u, 0u, 0u); + const uint32_t expectedClutOff = GSPSMCT32::addrPSMCT32(kClutCbp, 1u, 3u, 2u); + std::memcpy(vram.data() + wrongClutOff, &kWrongColor, sizeof(kWrongColor)); + std::memcpy(vram.data() + expectedClutOff, &kExpectedColor, sizeof(kExpectedColor)); + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, 0ull); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_TEXCLUT, kTexClut); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0) | (16ull << 16)); + + uint32_t pixel = 0u; + std::memcpy(&pixel, vram.data(), sizeof(pixel)); + t.Equals(pixel, kExpectedColor, + "TEXCLUT should offset the CLUT lookup coordinates instead of always starting from the CLUT base"); + }); + + tc.Run("GS TEXA expands CT24 alpha and honors AEM for black texels", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (1ull << 16) | + (0ull << 32) | + (0ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_CT24) << 20) | + (1ull << 26) | + (0ull << 30) | + (1ull << 34) | + (1ull << 35); + constexpr uint64_t kTexa = + (0x55ull << 0) | + (1ull << 15) | + (0xAAull << 32); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint32_t kExpectedRed = 0x550000FFu; + constexpr uint32_t kExpectedBlack = 0x00000000u; + + const uint32_t redOff = GSPSMCT32::addrPSMCT32(kTexTbp, 1u, 0u, 0u); + vram[redOff + 0u] = 0xFFu; + vram[redOff + 1u] = 0x00u; + vram[redOff + 2u] = 0x00u; + vram[redOff + 3u] = 0x00u; + + const uint32_t blackOff = GSPSMCT32::addrPSMCT32(kTexTbp, 1u, 1u, 0u); + vram[blackOff + 0u] = 0x00u; + vram[blackOff + 1u] = 0x00u; + vram[blackOff + 2u] = 0x00u; + vram[blackOff + 3u] = 0x00u; + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_TEXA, kTexa); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x80808080ull); + + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0) | (16ull << 16)); + + gs.writeRegister(GS_REG_UV, (16ull << 0)); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0)); + gs.writeRegister(GS_REG_UV, (16ull << 0)); + gs.writeRegister(GS_REG_XYZ2, (32ull << 0) | (16ull << 16)); + + const uint32_t redPixel = readReferencePSMCT32Pixel(vram, 0u, 1u, 0u, 0u); + const uint32_t blackPixel = readReferencePSMCT32Pixel(vram, 0u, 1u, 1u, 0u); + t.Equals(redPixel, kExpectedRed, + "TEXA should supply TA0 as the alpha for non-alpha CT24 texels"); + t.Equals(blackPixel, kExpectedBlack, + "TEXA AEM should force zero alpha when a CT24 texel is RGB=0"); + }); + + tc.Run("GS TCC=0 MODULATE uses texture RGB and keeps vertex alpha", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (0ull << 16) | + (0ull << 32) | + (0ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (0ull << 26) | + (0ull << 30) | + (0ull << 34) | + (0ull << 35); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint32_t kTexturePixel = + 0x12u | + (0x34u << 8) | + (0x56u << 16) | + (0x78u << 24); + constexpr uint32_t kExpectedPixel = + 0x12u | + (0x34u << 8) | + (0x56u << 16) | + (0x44u << 24); + + const uint32_t texOff = GSPSMCT32::addrPSMCT32(kTexTbp, 1u, 0u, 0u); + std::memcpy(vram.data() + texOff, &kTexturePixel, sizeof(kTexturePixel)); + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x44808080ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0) | (16ull << 16)); + + const uint32_t pixel = readReferencePSMCT32Pixel(vram, 0u, 1u, 0u, 0u); + t.Equals(pixel, kExpectedPixel, + "TCC=0 MODULATE should still use texture RGB while sourcing alpha from the shaded vertex"); + }); + + tc.Run("GS HIGHLIGHT adds vertex alpha into RGB and texture alpha into A", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (0ull << 16) | + (0ull << 32) | + (0ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (0ull << 26) | + (0ull << 30) | + (1ull << 34) | + (2ull << 35); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint32_t kTexturePixel = + 0x20u | + (0x40u << 8) | + (0x60u << 16) | + (0x10u << 24); + constexpr uint32_t kExpectedPixel = + 0x40u | + (0x60u << 8) | + (0x80u << 16) | + (0x30u << 24); + + const uint32_t texOff = GSPSMCT32::addrPSMCT32(kTexTbp, 1u, 0u, 0u); + std::memcpy(vram.data() + texOff, &kTexturePixel, sizeof(kTexturePixel)); + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x20808080ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0) | (16ull << 16)); + + const uint32_t pixel = readReferencePSMCT32Pixel(vram, 0u, 1u, 0u, 0u); + t.Equals(pixel, kExpectedPixel, + "HIGHLIGHT should add the shaded vertex alpha into RGB and accumulate texture plus vertex alpha"); + }); + + tc.Run("GS HIGHLIGHT2 keeps texture alpha while adding vertex alpha into RGB", [](TestCase &t) + { + std::vector vram(PS2_GS_VRAM_SIZE, 0u); + GS gs; + gs.init(vram.data(), static_cast(vram.size()), nullptr); + + constexpr uint32_t kTexTbp = 64u; + constexpr uint64_t kFrameReg = + (0ull << 0) | + (1ull << 16) | + (static_cast(GS_PSM_CT32) << 24); + constexpr uint64_t kScissor = + (0ull << 0) | + (0ull << 16) | + (0ull << 32) | + (0ull << 48); + constexpr uint64_t kTex0 = + (static_cast(kTexTbp) << 0) | + (1ull << 14) | + (static_cast(GS_PSM_CT32) << 20) | + (0ull << 26) | + (0ull << 30) | + (1ull << 34) | + (3ull << 35); + constexpr uint64_t kPrim = + static_cast(GS_PRIM_SPRITE) | + (1ull << 4) | + (1ull << 8); + constexpr uint32_t kTexturePixel = + 0x20u | + (0x40u << 8) | + (0x60u << 16) | + (0x10u << 24); + constexpr uint32_t kExpectedPixel = + 0x40u | + (0x60u << 8) | + (0x80u << 16) | + (0x10u << 24); + + const uint32_t texOff = GSPSMCT32::addrPSMCT32(kTexTbp, 1u, 0u, 0u); + std::memcpy(vram.data() + texOff, &kTexturePixel, sizeof(kTexturePixel)); + + gs.writeRegister(GS_REG_FRAME_1, kFrameReg); + gs.writeRegister(GS_REG_SCISSOR_1, kScissor); + gs.writeRegister(GS_REG_XYOFFSET_1, 0ull); + gs.writeRegister(GS_REG_TEST_1, 0ull); + gs.writeRegister(GS_REG_ALPHA_1, 0ull); + gs.writeRegister(GS_REG_TEX0_1, kTex0); + gs.writeRegister(GS_REG_PRIM, kPrim); + gs.writeRegister(GS_REG_RGBAQ, 0x20808080ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, 0ull); + gs.writeRegister(GS_REG_UV, 0ull); + gs.writeRegister(GS_REG_XYZ2, (16ull << 0) | (16ull << 16)); + + const uint32_t pixel = readReferencePSMCT32Pixel(vram, 0u, 1u, 0u, 0u); + t.Equals(pixel, kExpectedPixel, + "HIGHLIGHT2 should add the shaded vertex alpha into RGB while preserving the texture alpha"); + }); + tc.Run("GS TEX1 linear filter blends T4 STQ triangle samples", [](TestCase &t) { auto renderSamplePixel = [](uint64_t tex1Reg) -> uint32_t diff --git a/ps2xTest/src/ps2_memory_tests.cpp b/ps2xTest/src/ps2_memory_tests.cpp index 286f8d5b..8edcfc9f 100644 --- a/ps2xTest/src/ps2_memory_tests.cpp +++ b/ps2xTest/src/ps2_memory_tests.cpp @@ -3,7 +3,10 @@ #include "runtime/ps2_gs_gpu.h" #include "runtime/ps2_gs_psmct32.h" #include "runtime/ps2_vu1.h" +#include "ps2_runtime.h" #include "ps2_runtime_macros.h" +#include "Stubs/DMA.h" +#include "Stubs/GS.h" #include #include @@ -19,6 +22,11 @@ namespace static_cast(imm); } + void setRegU32(R5900Context &ctx, int reg, uint32_t value) + { + ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); + } + void appendU32(std::vector &dst, uint32_t value) { const size_t pos = dst.size(); @@ -901,6 +909,127 @@ void register_ps2_memory_tests() t.Equals(mem.readIORegister(kVif1Ch + 0x20u), 0u, "VIF1 QWC should be cleared after drain"); }); + tc.Run("VIF1 DMA chain preserves compact tag high bytes for DIRECT packets", [](TestCase &t) + { + PS2Memory mem; + t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed"); + + constexpr uint32_t kVif1Ch = 0x10009000u; + constexpr uint32_t kTag = 0x00025000u; + + uint8_t *rdram = mem.getRDRAM(); + std::memset(rdram + kTag, 0, 32u); + + const uint64_t endTag = makeDmaTag(1u, 7u, 0u, false); + std::memcpy(rdram + kTag, &endTag, sizeof(endTag)); + + // Compact VIF1 packet helpers place the DIRECT command in the tag's upper 64 bits. + const uint32_t directCmd = makeVifCmd(0x50u, 0u, 1u); + std::memcpy(rdram + kTag + 12u, &directCmd, sizeof(directCmd)); + for (uint32_t i = 0; i < 16u; ++i) + { + rdram[kTag + 16u + i] = static_cast(0x70u + i); + } + + std::vector> captured; + mem.setGifPacketCallback([&](const uint8_t *data, uint32_t sizeBytes) + { + captured.emplace_back(data, data + sizeBytes); + }); + + t.IsTrue(mem.writeIORegister(kVif1Ch + 0x30u, kTag), "write VIF1 TADR should succeed"); + t.IsTrue(mem.writeIORegister(kVif1Ch + 0x00u, 0x104u), "write VIF1 CHCR STR|CHAIN should succeed"); + + mem.processPendingTransfers(); + + t.Equals(captured.size(), static_cast(1u), "compact VIF1 chain should emit one GIF packet"); + t.Equals(captured[0].size(), static_cast(16u), "compact VIF1 DIRECT packet should be 1 QW"); + + bool payloadOk = true; + for (uint32_t i = 0; i < 16u; ++i) + { + if (captured[0][i] != static_cast(0x70u + i)) + { + payloadOk = false; + break; + } + } + t.IsTrue(payloadOk, "compact VIF1 chain payload should reach the GIF callback"); + t.IsTrue((mem.readIORegister(kVif1Ch + 0x00u) & 0x100u) == 0u, + "compact VIF1 chain should clear the STR bit after drain"); + }); + + tc.Run("VIF1 packet builders keep chain qwc live before terminate", [](TestCase &t) + { + PS2Memory mem; + t.IsTrue(mem.initialize(), "PS2Memory initialize should succeed"); + + constexpr uint32_t kVif1Ch = 0x10009000u; + constexpr uint32_t kStateAddr = 0x00027000u; + constexpr uint32_t kBaseAddr = 0x00027100u; + + uint8_t *rdram = mem.getRDRAM(); + std::memset(rdram + kStateAddr, 0, 0x200u); + + R5900Context ctx{}; + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, kBaseAddr); + ps2_stubs::sceVif1PkInit(rdram, &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, 0u); + ps2_stubs::sceVif1PkCnt(rdram, &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, 0u); + ps2_stubs::sceVif1PkOpenDirectCode(rdram, &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, 4u); // reserve one qword of DIRECT payload + ps2_stubs::sceVif1PkReserve(rdram, &ctx, nullptr); + const uint32_t payloadAddr = ::getRegU32(&ctx, 2); + for (uint32_t i = 0; i < 16u; ++i) + { + rdram[payloadAddr + i] = static_cast(0x30u + i); + } + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + ps2_stubs::sceVif1PkCloseDirectCode(rdram, &ctx, nullptr); + + uint32_t dmaTagWord = 0u; + std::memcpy(&dmaTagWord, rdram + kBaseAddr, sizeof(dmaTagWord)); + t.Equals(dmaTagWord & 0xFFFFu, 1u, "live packet head qwc should reflect one qword before terminate"); + + std::vector> captured; + mem.setGifPacketCallback([&](const uint8_t *data, uint32_t sizeBytes) + { + captured.emplace_back(data, data + sizeBytes); + }); + + t.IsTrue(mem.writeIORegister(kVif1Ch + 0x30u, kBaseAddr), "write VIF1 TADR should succeed"); + t.IsTrue(mem.writeIORegister(kVif1Ch + 0x00u, 0x104u), "write VIF1 CHCR STR|CHAIN should succeed"); + + mem.processPendingTransfers(); + + t.Equals(captured.size(), static_cast(1u), "live VIF1 packet should emit one GIF packet"); + t.Equals(captured[0].size(), static_cast(16u), "live VIF1 packet should emit one qword"); + + bool payloadOk = true; + for (uint32_t i = 0; i < 16u; ++i) + { + if (captured[0][i] != static_cast(0x30u + i)) + { + payloadOk = false; + break; + } + } + t.IsTrue(payloadOk, "live VIF1 packet payload should reach the GIF callback"); + }); + tc.Run("GIF DMA chain CALL sources payload from TADR+16", [](TestCase &t) { PS2Memory mem; @@ -1140,6 +1269,38 @@ void register_ps2_memory_tests() } }); + tc.Run("sceDmaReset re-enables DMAC DMAE", [](TestCase &t) + { + PS2Runtime runtime; + t.IsTrue(runtime.memory().initialize(), "PS2Memory initialize should succeed"); + + constexpr uint32_t kDctrl = 0x1000E000u; + constexpr uint32_t kDpcr = 0x1000E020u; + constexpr uint32_t kDsqwc = 0x1000E030u; + constexpr uint32_t kDrbor = 0x1000E050u; + constexpr uint32_t kDrbsr = 0x1000E040u; + constexpr uint32_t kDstadr = 0x1000E060u; + + PS2Memory &mem = runtime.memory(); + t.IsTrue(mem.writeIORegister(kDctrl, 0u), "clearing D_CTRL should succeed"); + t.IsTrue(mem.writeIORegister(kDpcr, 0x12345678u), "writing D_PCR should succeed"); + t.IsTrue(mem.writeIORegister(kDsqwc, 0x11220044u), "writing D_SQWC should succeed"); + t.IsTrue(mem.writeIORegister(kDrbor, 0x2000u), "writing D_RBOR should succeed"); + t.IsTrue(mem.writeIORegister(kDrbsr, 0x3FFFu), "writing D_RBSR should succeed"); + t.IsTrue(mem.writeIORegister(kDstadr, 0x4567u), "writing D_STADR should succeed"); + + R5900Context ctx{}; + ps2_stubs::sceDmaReset(mem.getRDRAM(), &ctx, &runtime); + + t.Equals(static_cast(::getRegU32(&ctx, 2)), 0, "sceDmaReset should return 0"); + t.Equals(mem.readIORegister(kDctrl), 1u, "sceDmaReset should leave D_CTRL DMAE enabled"); + t.Equals(mem.readIORegister(kDpcr), 0u, "sceDmaReset should clear D_PCR"); + t.Equals(mem.readIORegister(kDsqwc), 0u, "sceDmaReset should clear D_SQWC"); + t.Equals(mem.readIORegister(kDrbor), 0u, "sceDmaReset should clear D_RBOR"); + t.Equals(mem.readIORegister(kDrbsr), 0u, "sceDmaReset should clear D_RBSR"); + t.Equals(mem.readIORegister(kDstadr), 0u, "sceDmaReset should clear D_STADR"); + }); + tc.Run("VU1 XGKICK wraps packet payload across VU1 memory boundary", [](TestCase &t) { PS2Memory mem; @@ -1273,6 +1434,59 @@ void register_ps2_memory_tests() t.IsTrue(imageOk, "VIF1 DIRECT image should update GS VRAM through GIF path2"); }); + tc.Run("VIF1 DIRECT image tag can continue with raw image qwords", [](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()); + GifArbiter arbiter([&](const uint8_t *data, uint32_t sizeBytes) + { + gs.processGIFPacket(data, sizeBytes); + }); + mem.setGifArbiter(&arbiter); + + const uint64_t bitblt = + (static_cast(0u) << 0) | + (static_cast(1u) << 16) | + (static_cast(0u) << 24) | + (static_cast(0u) << 32) | + (static_cast(1u) << 48) | + (static_cast(0u) << 56); + gs.writeRegister(GS_REG_BITBLTBUF, bitblt); + gs.writeRegister(GS_REG_TRXPOS, 0ull); + gs.writeRegister(GS_REG_TRXREG, (4ull << 0) | (1ull << 32)); + gs.writeRegister(GS_REG_TRXDIR, 0ull); + + std::vector packet; + appendU32(packet, makeVifCmd(0x50u, 0u, 1u)); // DIRECT 1 QW payload: GIF IMAGE tag only. + appendU64(packet, makeGifTag(1u, GIF_FMT_IMAGE, 0u, true)); + appendU64(packet, 0ull); + for (uint32_t i = 0; i < 16u; ++i) + { + packet.push_back(static_cast(0xA0u + i)); + } + + mem.processVIF1Data(packet.data(), static_cast(packet.size())); + + const uint8_t *vramOut = mem.getGSVRAM(); + bool imageOk = true; + for (uint32_t x = 0; x < 4u && imageOk; ++x) + { + const uint32_t off = GSPSMCT32::addrPSMCT32(0u, 1u, x, 0u); + for (uint32_t c = 0; c < 4u; ++c) + { + if (vramOut[off + c] != static_cast(0xA0u + x * 4u + c)) + { + imageOk = false; + break; + } + } + } + t.IsTrue(imageOk, "raw qwords after a DIRECT image tag should continue the PATH2 image upload"); + }); + tc.Run("VIF MSCAL callback can execute XGKICK and update GS VRAM", [](TestCase &t) { PS2Memory mem; diff --git a/ps2xTest/src/ps2_runtime_expansion_tests.cpp b/ps2xTest/src/ps2_runtime_expansion_tests.cpp index 48959d02..7941743a 100644 --- a/ps2xTest/src/ps2_runtime_expansion_tests.cpp +++ b/ps2xTest/src/ps2_runtime_expansion_tests.cpp @@ -13,6 +13,7 @@ #include "Stubs/MPEG.h" #include "Stubs/Audio.h" #include "Stubs/GS.h" +#include "Stubs/VU.h" #include #include @@ -1120,5 +1121,153 @@ void register_ps2_runtime_expansion_tests() runtime.requestStop(); notifyRuntimeStop(); }); + + tc.Run("sceVu0ApplyMatrix uses libvux matrix math with the imported EE ABI", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + + constexpr uint32_t kOutAddr = 0x00100000u; + constexpr uint32_t kMatrixAddr = 0x00100040u; + constexpr uint32_t kSrcAddr = 0x00100080u; + + const float matrix[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f, + }; + const float src[4] = {1.0f, 2.0f, 3.0f, 1.0f}; + std::memcpy(rdram.data() + kMatrixAddr, matrix, sizeof(matrix)); + std::memcpy(rdram.data() + kSrcAddr, src, sizeof(src)); + + setRegU32(ctx, 4, kOutAddr); + setRegU32(ctx, 5, kMatrixAddr); + setRegU32(ctx, 6, kSrcAddr); + + ps2_stubs::sceVu0ApplyMatrix(rdram.data(), &ctx, nullptr); + + float out[4]{}; + std::memcpy(out, rdram.data() + kOutAddr, sizeof(out)); + t.Equals(out[0], 51.0f, "sceVu0ApplyMatrix should compute X with libvux layout"); + t.Equals(out[1], 58.0f, "sceVu0ApplyMatrix should compute Y with libvux layout"); + t.Equals(out[2], 65.0f, "sceVu0ApplyMatrix should compute Z with libvux layout"); + t.Equals(out[3], 72.0f, "sceVu0ApplyMatrix should compute W with libvux layout"); + t.Equals(getRegS32(ctx, 2), 0, "sceVu0ApplyMatrix should report success"); + }); + + tc.Run("sceVu0TransposeMatrix transposes a 4x4 matrix with dst/src ABI", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + + constexpr uint32_t kDstAddr = 0x00100100u; + constexpr uint32_t kSrcAddr = 0x00100140u; + const float src[16] = { + 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + 9.0f, 10.0f, 11.0f, 12.0f, + 13.0f, 14.0f, 15.0f, 16.0f, + }; + std::memcpy(rdram.data() + kSrcAddr, src, sizeof(src)); + + setRegU32(ctx, 4, kDstAddr); + setRegU32(ctx, 5, kSrcAddr); + + ps2_stubs::sceVu0TransposeMatrix(rdram.data(), &ctx, nullptr); + + float out[16]{}; + std::memcpy(out, rdram.data() + kDstAddr, sizeof(out)); + t.Equals(out[0], 1.0f, "transpose should preserve [0][0]"); + t.Equals(out[1], 5.0f, "transpose should swap row 0 col 1"); + t.Equals(out[2], 9.0f, "transpose should swap row 0 col 2"); + t.Equals(out[3], 13.0f, "transpose should swap row 0 col 3"); + t.Equals(out[4], 2.0f, "transpose should swap row 1 col 0"); + t.Equals(out[5], 6.0f, "transpose should preserve [1][1]"); + t.Equals(out[10], 11.0f, "transpose should preserve [2][2]"); + t.Equals(out[12], 4.0f, "transpose should swap row 3 col 0"); + t.Equals(out[15], 16.0f, "transpose should preserve [3][3]"); + t.Equals(getRegS32(ctx, 2), 0, "sceVu0TransposeMatrix should report success"); + }); + + tc.Run("sceVif1PkReset preserves the packet base pointer and clears open tag state", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + + constexpr uint32_t kStateAddr = 0x00100200u; + constexpr uint32_t kBaseAddr = 0x00101000u; + + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, kBaseAddr); + ps2_stubs::sceVif1PkInit(rdram.data(), &ctx, nullptr); + + const uint32_t dirtyCurrent = kBaseAddr + 0x40u; + const uint32_t dirtyPending = 0x12345678u; + const uint32_t dirtyDirectOpen = 0x00ABCDEFu; + const uint32_t dirtyGifOpen = 0x00112233u; + std::memcpy(rdram.data() + kStateAddr + 0u, &dirtyCurrent, sizeof(dirtyCurrent)); + std::memcpy(rdram.data() + kStateAddr + 8u, &dirtyPending, sizeof(dirtyPending)); + std::memcpy(rdram.data() + kStateAddr + 12u, &dirtyDirectOpen, sizeof(dirtyDirectOpen)); + std::memcpy(rdram.data() + kStateAddr + 20u, &dirtyGifOpen, sizeof(dirtyGifOpen)); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + ps2_stubs::sceVif1PkReset(rdram.data(), &ctx, nullptr); + + uint32_t current = 0u; + uint32_t base = 0u; + uint32_t pending = 0u; + uint32_t directOpen = 0u; + uint32_t gifOpen = 0u; + std::memcpy(¤t, rdram.data() + kStateAddr + 0u, sizeof(current)); + std::memcpy(&base, rdram.data() + kStateAddr + 4u, sizeof(base)); + std::memcpy(&pending, rdram.data() + kStateAddr + 8u, sizeof(pending)); + std::memcpy(&directOpen, rdram.data() + kStateAddr + 12u, sizeof(directOpen)); + std::memcpy(&gifOpen, rdram.data() + kStateAddr + 20u, sizeof(gifOpen)); + + t.Equals(current, kBaseAddr, "sceVif1PkReset should restore current pointer to the packet base"); + t.Equals(base, kBaseAddr, "sceVif1PkReset should preserve the packet base pointer"); + t.Equals(pending, 0u, "sceVif1PkReset should clear pending count tracking"); + t.Equals(directOpen, 0u, "sceVif1PkReset should clear direct-code open state"); + t.Equals(gifOpen, 0u, "sceVif1PkReset should clear GIF-tag open state"); + t.Equals(::getRegU32(&ctx, 2), kBaseAddr, "sceVif1PkReset should return the packet base pointer"); + }); + + tc.Run("sceVif1PkCloseDirectCode encodes DIRECT length in qwords", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0u); + R5900Context ctx{}; + + constexpr uint32_t kStateAddr = 0x00100400u; + constexpr uint32_t kBaseAddr = 0x00102000u; + + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, kBaseAddr); + ps2_stubs::sceVif1PkInit(rdram.data(), &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, 0u); + ps2_stubs::sceVif1PkCnt(rdram.data(), &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, 0u); + ps2_stubs::sceVif1PkOpenDirectCode(rdram.data(), &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + setRegU32(ctx, 5, 4u); // reserve one qword worth of GIF payload + ps2_stubs::sceVif1PkReserve(rdram.data(), &ctx, nullptr); + + std::memset(&ctx, 0, sizeof(ctx)); + setRegU32(ctx, 4, kStateAddr); + ps2_stubs::sceVif1PkCloseDirectCode(rdram.data(), &ctx, nullptr); + + uint32_t directCmd = 0u; + std::memcpy(&directCmd, rdram.data() + kBaseAddr + 12u, sizeof(directCmd)); + t.Equals(directCmd, 0x50000001u, "sceVif1PkCloseDirectCode should store a 1-QW DIRECT length"); + }); }); } diff --git a/ps2xTest/src/ps2_runtime_io_tests.cpp b/ps2xTest/src/ps2_runtime_io_tests.cpp index 817e5f70..e57b8f6f 100644 --- a/ps2xTest/src/ps2_runtime_io_tests.cpp +++ b/ps2xTest/src/ps2_runtime_io_tests.cpp @@ -509,6 +509,34 @@ void register_ps2_runtime_io_tests() t.Equals(readGuestS32(test.rdram.data(), formatAddr), 0, "sceMcGetInfo should report an unformatted card after sceMcUnformat"); }); + tc.Run("sceMcEnd resets libmc state so sync reports no active command", [](TestCase &t) + { + TestContext test; + + constexpr uint32_t dirAddr = GUEST_STRING_AREA_START + 0xB00; + + clearContext(test.ctx); + ps2_stubs::sceMcInit(test.rdram.data(), &test.ctx, nullptr); + t.Equals(getRegS32(&test.ctx, 2), 0, "sceMcInit should succeed"); + + writeGuestString(test.rdram.data(), dirAddr, "/SAVEDATA"); + clearContext(test.ctx); + setRegU32(test.ctx, 4, 0u); + setRegU32(test.ctx, 5, 0u); + setRegU32(test.ctx, 6, dirAddr); + ps2_stubs::sceMcMkdir(test.rdram.data(), &test.ctx, nullptr); + int32_t cmd = 0; + t.Equals(syncMc(test.rdram, &cmd), 0, "sceMcMkdir should complete before teardown"); + t.Equals(cmd, 0x0B, "sceMcSync should report MKDIR before teardown"); + + clearContext(test.ctx); + ps2_stubs::sceMcEnd(test.rdram.data(), &test.ctx, nullptr); + t.Equals(getRegS32(&test.ctx, 2), 0, "sceMcEnd should succeed"); + + t.Equals(syncMc(test.rdram, &cmd), 0, "sceMcSync should report cleared result after sceMcEnd"); + t.Equals(cmd, 0, "sceMcEnd should clear the last active libmc command"); + }); + tc.Run("sceIoctl cmd1 updates wait flag state", [](TestCase &t) { TestContext test; diff --git a/ps2xTest/src/ps2_sif_rpc_tests.cpp b/ps2xTest/src/ps2_sif_rpc_tests.cpp index 612bcbb6..84ca4ca5 100644 --- a/ps2xTest/src/ps2_sif_rpc_tests.cpp +++ b/ps2xTest/src/ps2_sif_rpc_tests.cpp @@ -109,6 +109,16 @@ namespace ps2_syscalls::setDtxCompatLayout(layout); } + void setDarkCloud2EzMidiCompatLayout(PS2Runtime &runtime) + { + PS2EzMidiCompatLayout layout{}; + layout.rpcSid = 0x00012346u; + layout.reportedFreeIopBytes = 0x00200000u; + layout.defaultPortVolume = 0x00000100u; + layout.reportedVoiceCount = 0x00000020u; + runtime.iop().setEzMidiCompatLayout(layout); + } + void setRegU32(R5900Context &ctx, int reg, uint32_t value) { ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); @@ -611,5 +621,100 @@ void register_ps2_sif_rpc_tests() t.IsTrue(stackHandle != 0u, "DTX handle should be written to stack-selected recv buffer"); t.Equals(regHandle, 0u, "register recv buffer should remain untouched when stack ABI is preferred"); }); + + tc.Run("SifCallRpc routes Dark Cloud 2 ezMidi families through compat state", [](TestCase &t) + { + TestEnv env; + setDarkCloud2EzMidiCompatLayout(env.runtime); + env.runtime.iop().init(env.rdram.data()); + + constexpr uint32_t kClientAddr = 0x0002C000u; + constexpr uint32_t kEzMidiSid = 0x00012346u; + constexpr uint32_t kSendAddr = 0x0002C100u; + constexpr uint32_t kRecvAddr = 0x0002C200u; + + SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); + + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, kEzMidiSid); + setRegU32(env.ctx, 6, 0u); + SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for ezMidi sid"); + + std::memset(env.rdram.data() + kRecvAddr, 0xCC, 0x40u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x8010u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 0x10u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 0x40u); + setRegU32(env.ctx, 11, 0u); + setRegU32(env.ctx, 29, K_STACK_ADDR); + writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, 0u); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "ezMidi init should succeed"); + t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 0u, + "ezMidi init should zero the recv status word"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 0x01A00000u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 0x01A00000u); + writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0x000117E0u); + std::memset(env.rdram.data() + kRecvAddr, 0, 0x40u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x9052u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 0x40u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 0x40u); + setRegU32(env.ctx, 11, 0u); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "MidiSetHd family should succeed"); + + std::memset(env.rdram.data() + kRecvAddr, 0, 0x40u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x8092u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 0x10u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 0x40u); + setRegU32(env.ctx, 11, 0u); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 1u, + "MidiGetStatus should report a loaded-ready port after MidiSetHd"); + t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x08u), 0x000117E0u, + "MidiGetStatus should preserve the last loaded data size"); + + writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0x00000339u); + std::memset(env.rdram.data() + kRecvAddr, 0xCD, 0x40u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x00B2u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 0x10u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 0x0u); + setRegU32(env.ctx, 11, 0u); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(getRegS32(env.ctx, 2), KE_OK, "MidiSetPortVolume family should succeed"); + t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 0u, + "zero-recv ezMidi calls should still clear the scratch reply word"); + + std::memset(env.rdram.data() + kRecvAddr, 0, 0x40u); + setRegU32(env.ctx, 4, kClientAddr); + setRegU32(env.ctx, 5, 0x80E2u); + setRegU32(env.ctx, 6, 0u); + setRegU32(env.ctx, 7, kSendAddr); + setRegU32(env.ctx, 8, 0x10u); + setRegU32(env.ctx, 9, kRecvAddr); + setRegU32(env.ctx, 10, 0x40u); + setRegU32(env.ctx, 11, 0u); + SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); + t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 0x00000339u, + "MidiGetPortVolume should mirror the last per-port volume write"); + }); }); } From 9c6918c17cbca19ef6ee510e312c899c9e24d482 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 12:45:57 -0300 Subject: [PATCH 24/30] feat: back missing file --- ps2xRuntime/src/runner/register_functions | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ps2xRuntime/src/runner/register_functions diff --git a/ps2xRuntime/src/runner/register_functions b/ps2xRuntime/src/runner/register_functions new file mode 100644 index 00000000..42a234b6 --- /dev/null +++ b/ps2xRuntime/src/runner/register_functions @@ -0,0 +1,6 @@ +#include "register_functions.h" + +// Replace this file with the actual implementation of the functions that was generated by the compiler +void registerAllFunctions(PS2Runtime &runtime) +{ +} \ No newline at end of file From c20d84d72f54c86757b8bfbd72155b12945d9ab9 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 12:59:51 -0300 Subject: [PATCH 25/30] feat: added missing includes --- ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h index aa507c93..0b746cdb 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h @@ -1,3 +1,6 @@ +#include +#include + namespace { constexpr uint32_t kCdSectorSize = 2048; From 55f16abd5d10ec77a21a51be349f64cc1d5e4e43 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 16:18:19 -0300 Subject: [PATCH 26/30] feat: remove test --- ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp | 165 ++++++++++++++++++++++-- ps2xTest/src/ps2_sif_rpc_tests.cpp | 105 --------------- 2 files changed, 157 insertions(+), 113 deletions(-) diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp index 22ad95a0..02741622 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/VU.cpp @@ -54,6 +54,52 @@ namespace ps2_stubs std::memcpy(ptr, in, sizeof(in)); return true; } + + bool readVuMatrix4f(uint8_t *rdram, uint32_t addr, float (&out)[16]) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(out, ptr, sizeof(out)); + return true; + } + + bool writeVuMatrix4f(uint8_t *rdram, uint32_t addr, const float (&in)[16]) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, in, sizeof(in)); + return true; + } + + void mulVuMatrix(const float (&lhs)[16], const float (&rhs)[16], float (&out)[16]) + { + std::fill(std::begin(out), std::end(out), 0.0f); + for (int i = 0; i < 4; ++i) + { + for (int j = 0; j < 4; ++j) + { + for (int k = 0; k < 4; ++k) + { + out[4 * i + j] += rhs[4 * k + j] * lhs[4 * i + k]; + } + } + } + } + + void makeIdentityMatrix(float (&out)[16]) + { + std::fill(std::begin(out), std::end(out), 0.0f); + out[0] = 1.0f; + out[5] = 1.0f; + out[10] = 1.0f; + out[15] = 1.0f; + } } void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -80,7 +126,23 @@ namespace ps2_stubs void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t matrixAddr = getRegU32(ctx, 5); + const uint32_t srcAddr = getRegU32(ctx, 6); + float matrix[16]{}; + float src[4]{}; + float out[4]{}; + if (readVuMatrix4f(rdram, matrixAddr, matrix) && readVuVec4f(rdram, srcAddr, src)) + { + // Match libvux VuxApplyMatrix math while honoring the imported EE ABI: + // a0=out, a1=matrix, a2=vector. + out[0] = (matrix[0] * src[0]) + (matrix[4] * src[1]) + (matrix[8] * src[2]) + (matrix[12] * src[3]); + out[1] = (matrix[1] * src[0]) + (matrix[5] * src[1]) + (matrix[9] * src[2]) + (matrix[13] * src[3]); + out[2] = (matrix[2] * src[0]) + (matrix[6] * src[1]) + (matrix[10] * src[2]) + (matrix[14] * src[3]); + out[3] = (matrix[3] * src[0]) + (matrix[7] * src[1]) + (matrix[11] * src[2]) + (matrix[15] * src[3]); + (void)writeVuVec4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); } void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -110,17 +172,41 @@ namespace ps2_stubs void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + uint8_t *dst = getMemPtr(rdram, dstAddr); + const uint8_t *src = getConstMemPtr(rdram, srcAddr); + if (dst && src) + { + std::memcpy(dst, src, sizeof(float) * 16u); + } + setReturnS32(ctx, 0); } void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + uint8_t *dst = getMemPtr(rdram, dstAddr); + const uint8_t *src = getConstMemPtr(rdram, srcAddr); + if (dst && src) + { + std::memcpy(dst, src, sizeof(float) * 4u); + } + setReturnS32(ctx, 0); } void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + uint8_t *dst = getMemPtr(rdram, dstAddr); + const uint8_t *src = getConstMemPtr(rdram, srcAddr); + if (dst && src) + { + std::memcpy(dst, src, sizeof(float) * 3u); + } + setReturnS32(ctx, 0); } void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -323,17 +409,65 @@ namespace ps2_stubs void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + const float angle = ctx ? ctx->f[12] : 0.0f; + float src[16]{}, rot[16]{}, out[16]{}; + if (readVuMatrix4f(rdram, srcAddr, src)) + { + makeIdentityMatrix(rot); + const float cs = std::cos(angle); + const float sn = std::sin(angle); + rot[5] = cs; + rot[6] = sn; + rot[9] = -sn; + rot[10] = cs; + mulVuMatrix(src, rot, out); + (void)writeVuMatrix4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); } void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + const float angle = ctx ? ctx->f[12] : 0.0f; + float src[16]{}, rot[16]{}, out[16]{}; + if (readVuMatrix4f(rdram, srcAddr, src)) + { + makeIdentityMatrix(rot); + const float cs = std::cos(angle); + const float sn = std::sin(angle); + rot[0] = cs; + rot[2] = -sn; + rot[8] = sn; + rot[10] = cs; + mulVuMatrix(src, rot, out); + (void)writeVuMatrix4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); } void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + const float angle = ctx ? ctx->f[12] : 0.0f; + float src[16]{}, rot[16]{}, out[16]{}; + if (readVuMatrix4f(rdram, srcAddr, src)) + { + makeIdentityMatrix(rot); + const float cs = std::cos(angle); + const float sn = std::sin(angle); + rot[0] = cs; + rot[1] = sn; + rot[4] = -sn; + rot[5] = cs; + mulVuMatrix(src, rot, out); + (void)writeVuMatrix4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); } void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -402,7 +536,22 @@ namespace ps2_stubs void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); + const uint32_t dstAddr = getRegU32(ctx, 4); + const uint32_t srcAddr = getRegU32(ctx, 5); + float src[16]{}; + float out[16]{}; + if (readVuMatrix4f(rdram, srcAddr, src)) + { + for (int row = 0; row < 4; ++row) + { + for (int col = 0; col < 4; ++col) + { + out[4 * row + col] = src[4 * col + row]; + } + } + (void)writeVuMatrix4f(rdram, dstAddr, out); + } + setReturnS32(ctx, 0); } void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) diff --git a/ps2xTest/src/ps2_sif_rpc_tests.cpp b/ps2xTest/src/ps2_sif_rpc_tests.cpp index 84ca4ca5..612bcbb6 100644 --- a/ps2xTest/src/ps2_sif_rpc_tests.cpp +++ b/ps2xTest/src/ps2_sif_rpc_tests.cpp @@ -109,16 +109,6 @@ namespace ps2_syscalls::setDtxCompatLayout(layout); } - void setDarkCloud2EzMidiCompatLayout(PS2Runtime &runtime) - { - PS2EzMidiCompatLayout layout{}; - layout.rpcSid = 0x00012346u; - layout.reportedFreeIopBytes = 0x00200000u; - layout.defaultPortVolume = 0x00000100u; - layout.reportedVoiceCount = 0x00000020u; - runtime.iop().setEzMidiCompatLayout(layout); - } - void setRegU32(R5900Context &ctx, int reg, uint32_t value) { ctx.r[reg] = _mm_set_epi64x(0, static_cast(value)); @@ -621,100 +611,5 @@ void register_ps2_sif_rpc_tests() t.IsTrue(stackHandle != 0u, "DTX handle should be written to stack-selected recv buffer"); t.Equals(regHandle, 0u, "register recv buffer should remain untouched when stack ABI is preferred"); }); - - tc.Run("SifCallRpc routes Dark Cloud 2 ezMidi families through compat state", [](TestCase &t) - { - TestEnv env; - setDarkCloud2EzMidiCompatLayout(env.runtime); - env.runtime.iop().init(env.rdram.data()); - - constexpr uint32_t kClientAddr = 0x0002C000u; - constexpr uint32_t kEzMidiSid = 0x00012346u; - constexpr uint32_t kSendAddr = 0x0002C100u; - constexpr uint32_t kRecvAddr = 0x0002C200u; - - SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime); - - setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, kEzMidiSid); - setRegU32(env.ctx, 6, 0u); - SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for ezMidi sid"); - - std::memset(env.rdram.data() + kRecvAddr, 0xCC, 0x40u); - setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x8010u); - setRegU32(env.ctx, 6, 0u); - setRegU32(env.ctx, 7, kSendAddr); - setRegU32(env.ctx, 8, 0x10u); - setRegU32(env.ctx, 9, kRecvAddr); - setRegU32(env.ctx, 10, 0x40u); - setRegU32(env.ctx, 11, 0u); - setRegU32(env.ctx, 29, K_STACK_ADDR); - writeGuestU32(env.rdram.data(), K_STACK_ADDR + 0x00u, 0u); - SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "ezMidi init should succeed"); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 0u, - "ezMidi init should zero the recv status word"); - - writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u); - writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 0x01A00000u); - writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 0x01A00000u); - writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0x000117E0u); - std::memset(env.rdram.data() + kRecvAddr, 0, 0x40u); - setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x9052u); - setRegU32(env.ctx, 6, 0u); - setRegU32(env.ctx, 7, kSendAddr); - setRegU32(env.ctx, 8, 0x40u); - setRegU32(env.ctx, 9, kRecvAddr); - setRegU32(env.ctx, 10, 0x40u); - setRegU32(env.ctx, 11, 0u); - SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "MidiSetHd family should succeed"); - - std::memset(env.rdram.data() + kRecvAddr, 0, 0x40u); - setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x8092u); - setRegU32(env.ctx, 6, 0u); - setRegU32(env.ctx, 7, kSendAddr); - setRegU32(env.ctx, 8, 0x10u); - setRegU32(env.ctx, 9, kRecvAddr); - setRegU32(env.ctx, 10, 0x40u); - setRegU32(env.ctx, 11, 0u); - SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 1u, - "MidiGetStatus should report a loaded-ready port after MidiSetHd"); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x08u), 0x000117E0u, - "MidiGetStatus should preserve the last loaded data size"); - - writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0x00000339u); - std::memset(env.rdram.data() + kRecvAddr, 0xCD, 0x40u); - setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x00B2u); - setRegU32(env.ctx, 6, 0u); - setRegU32(env.ctx, 7, kSendAddr); - setRegU32(env.ctx, 8, 0x10u); - setRegU32(env.ctx, 9, kRecvAddr); - setRegU32(env.ctx, 10, 0x0u); - setRegU32(env.ctx, 11, 0u); - SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(getRegS32(env.ctx, 2), KE_OK, "MidiSetPortVolume family should succeed"); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 0u, - "zero-recv ezMidi calls should still clear the scratch reply word"); - - std::memset(env.rdram.data() + kRecvAddr, 0, 0x40u); - setRegU32(env.ctx, 4, kClientAddr); - setRegU32(env.ctx, 5, 0x80E2u); - setRegU32(env.ctx, 6, 0u); - setRegU32(env.ctx, 7, kSendAddr); - setRegU32(env.ctx, 8, 0x10u); - setRegU32(env.ctx, 9, kRecvAddr); - setRegU32(env.ctx, 10, 0x40u); - setRegU32(env.ctx, 11, 0u); - SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime); - t.Equals(readGuestStruct(env.rdram.data(), kRecvAddr + 0x00u), 0x00000339u, - "MidiGetPortVolume should mirror the last per-port volume write"); - }); }); } From 90169ed214c88950929a5a258a9a31d9d0d9c262 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 16:25:02 -0300 Subject: [PATCH 27/30] feat: missing include --- ps2xRuntime/src/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index 37289aca..ff856507 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace { @@ -54,10 +55,10 @@ namespace size_t dot = result.find('.'); if (dot != std::string::npos) result.erase(dot, 1); -#if !defined(PLATFORM_VITA) + std::ranges::transform(result, result.begin(), [](unsigned char character) { return static_cast(std::toupper(character)); }); -#endif + return result; } From 66914d041e21915e0b0d28aa1ee4c564aa965994 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 17:41:11 -0300 Subject: [PATCH 28/30] feat: read register funtion --- .../src/runner/{register_functions => register_functions.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ps2xRuntime/src/runner/{register_functions => register_functions.cpp} (100%) diff --git a/ps2xRuntime/src/runner/register_functions b/ps2xRuntime/src/runner/register_functions.cpp similarity index 100% rename from ps2xRuntime/src/runner/register_functions rename to ps2xRuntime/src/runner/register_functions.cpp From 24d1ba443e5b1375b0be75a87bfba7c30a72bcf6 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 21:43:21 -0300 Subject: [PATCH 29/30] feat: build fix --- ps2xAnalyzer/CMakeLists.txt | 7 +++++++ ps2xRecomp/CMakeLists.txt | 7 +++++++ ps2xStudio/CMakeLists.txt | 9 +++++++++ ps2xTest/CMakeLists.txt | 7 +++++++ 4 files changed, 30 insertions(+) diff --git a/ps2xAnalyzer/CMakeLists.txt b/ps2xAnalyzer/CMakeLists.txt index b374c2af..14e46f6e 100644 --- a/ps2xAnalyzer/CMakeLists.txt +++ b/ps2xAnalyzer/CMakeLists.txt @@ -32,3 +32,10 @@ install(TARGETS ps2_analyzer ps2_analyzer_lib LIBRARY DESTINATION lib ARCHIVE DESTINATION lib ) + +include("${CMAKE_SOURCE_DIR}/ps2xRuntime/cmake/ReleaseMode.cmake") + +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + EnableFastReleaseMode(ps2_analyzer_lib) + EnableFastReleaseMode(ps2_analyzer) +endif() diff --git a/ps2xRecomp/CMakeLists.txt b/ps2xRecomp/CMakeLists.txt index cae5ca87..69c37102 100644 --- a/ps2xRecomp/CMakeLists.txt +++ b/ps2xRecomp/CMakeLists.txt @@ -136,3 +136,10 @@ install(TARGETS ps2_recomp ps2_recomp_lib install(DIRECTORY include/ DESTINATION include ) + +include("${CMAKE_SOURCE_DIR}/ps2xRuntime/cmake/ReleaseMode.cmake") + +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + EnableFastReleaseMode(ps2_recomp_lib) + EnableFastReleaseMode(ps2_recomp) +endif() diff --git a/ps2xStudio/CMakeLists.txt b/ps2xStudio/CMakeLists.txt index 43f0936a..7d739a27 100644 --- a/ps2xStudio/CMakeLists.txt +++ b/ps2xStudio/CMakeLists.txt @@ -128,3 +128,12 @@ target_link_libraries(ps2xStudio PRIVATE if(WIN32) target_link_libraries(ps2xStudio PRIVATE SDL2::SDL2main) endif() + +include("${CMAKE_SOURCE_DIR}/ps2xRuntime/cmake/ReleaseMode.cmake") + +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + EnableFastReleaseMode(imgui_lib) + EnableFastReleaseMode(imgui_colortextedit_lib) + EnableFastReleaseMode(imgui_filedialog_lib) + EnableFastReleaseMode(ps2xStudio) +endif() diff --git a/ps2xTest/CMakeLists.txt b/ps2xTest/CMakeLists.txt index 2a3e2ccd..a429123f 100644 --- a/ps2xTest/CMakeLists.txt +++ b/ps2xTest/CMakeLists.txt @@ -59,3 +59,10 @@ target_link_libraries(ps2x_tests PRIVATE ps2_analyzer_lib ps2_runtime ) + +include("${CMAKE_SOURCE_DIR}/ps2xRuntime/cmake/ReleaseMode.cmake") + +if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + EnableFastReleaseMode(ps2_test_lib) + EnableFastReleaseMode(ps2x_tests) +endif() From f967e72d569d40dbc22dbd5b7a6870a51fb4d6a9 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Sat, 4 Apr 2026 22:59:21 -0300 Subject: [PATCH 30/30] feat: force exit on detach thread --- ps2xRuntime/src/main.cpp | 9 +++++++-- ps2xTest/src/main.cpp | 7 ++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ps2xRuntime/src/main.cpp b/ps2xRuntime/src/main.cpp index ff856507..21cd2c63 100644 --- a/ps2xRuntime/src/main.cpp +++ b/ps2xRuntime/src/main.cpp @@ -11,6 +11,7 @@ #include #include #include +#include namespace { @@ -132,7 +133,9 @@ int main(int argc, char *argv[]) #ifdef _DEBUG ps2_log::print_saved_location(); #endif - return 0; + std::cout.flush(); + std::cerr.flush(); + std::_Exit(0); } catch (const std::exception &e) { @@ -143,5 +146,7 @@ int main(int argc, char *argv[]) std::cerr << "[main] fatal exception: unknown" << std::endl; } - return 1; + std::cout.flush(); + std::cerr.flush(); + std::_Exit(1); } diff --git a/ps2xTest/src/main.cpp b/ps2xTest/src/main.cpp index 8f19d9cf..5a5da810 100644 --- a/ps2xTest/src/main.cpp +++ b/ps2xTest/src/main.cpp @@ -1,4 +1,6 @@ #include "MiniTest.h" +#include +#include void register_code_generator_tests(); void register_r5900_decoder_tests(); @@ -29,5 +31,8 @@ int main() register_ps2_sif_dma_tests(); register_ps2_recompiler_tests(); register_ps2_runtime_expansion_tests(); - return MiniTest::Run(); + int res = MiniTest::Run(); + std::cout.flush(); + std::cerr.flush(); + std::_Exit(res); }