From c8c86661fab7a5b59784496ff83b4d08058c83b2 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Wed, 2 Sep 2026 18:05:55 -0300 Subject: [PATCH] refactor: Refactor PS2 IOP Host Adapter and Memory Management feat: Added PS2Vfs for virtual file system operations, including file opening, reading, writing, and path resolution. feat: Improve VIF1 data processing to handle GIF image packets more efficiently. --- ps2xRuntime/CMakeLists.txt | 6 +- ps2xRuntime/include/ps2_call_list.h | 1 - ps2xRuntime/include/ps2_runtime.h | 14 + ps2xRuntime/include/ps2_syscalls.h | 2 - ps2xRuntime/include/runtime/ee_scheduler.h | 7 + ps2xRuntime/include/runtime/gs/gs_backend.h | 1 + .../include/runtime/gs/gs_cpu_backend.h | 9 +- ps2xRuntime/include/runtime/ps2_memory.h | 2 + ps2xRuntime/include/runtime/ps2_rom_device.h | 43 ++ ps2xRuntime/include/runtime/ps2_vfs.h | 82 ++++ ps2xRuntime/src/lib/Kernel/EeScheduler.cpp | 51 +- .../src/lib/Kernel/Stubs/Helpers/Support.h | 44 +- ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp | 438 ++++++------------ ps2xRuntime/src/lib/Kernel/Stubs/SIF.h | 9 +- ps2xRuntime/src/lib/Kernel/Syscalls/Common.h | 1 + .../src/lib/Kernel/Syscalls/Dispatcher.cpp | 3 + .../src/lib/Kernel/Syscalls/FileIO.cpp | 224 +++------ .../src/lib/Kernel/Syscalls/Helpers/Loader.h | 46 -- .../src/lib/Kernel/Syscalls/Helpers/Runtime.h | 45 +- .../src/lib/Kernel/Syscalls/Helpers/State.h | 5 - ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp | 34 +- .../src/lib/Kernel/Syscalls/System.cpp | 61 +-- ps2xRuntime/src/lib/Kernel/Syscalls/System.h | 1 - ps2xRuntime/src/lib/gs/gs_cpu_backend.cpp | 205 ++++++-- ps2xRuntime/src/lib/gs/gs_frontend.cpp | 2 + ps2xRuntime/src/lib/ps2_debug_panel.cpp | 94 +++- ps2xRuntime/src/lib/ps2_iop_host.cpp | 68 ++- ps2xRuntime/src/lib/ps2_iop_host.h | 5 + ps2xRuntime/src/lib/ps2_memory.cpp | 168 ++++++- ps2xRuntime/src/lib/ps2_rom_device.cpp | 178 +++++++ ps2xRuntime/src/lib/ps2_runtime.cpp | 40 +- ps2xRuntime/src/lib/ps2_vfs.cpp | 312 +++++++++++++ ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp | 73 ++- 33 files changed, 1488 insertions(+), 786 deletions(-) create mode 100644 ps2xRuntime/include/runtime/ps2_rom_device.h create mode 100644 ps2xRuntime/include/runtime/ps2_vfs.h create mode 100644 ps2xRuntime/src/lib/ps2_rom_device.cpp create mode 100644 ps2xRuntime/src/lib/ps2_vfs.cpp diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index e4dc195..589d768 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -10,8 +10,8 @@ set(PS2X_RUNNER_UNITY_BUILD_BATCH_SIZE 32 CACHE STRING "Unity build batch size f option(PS2X_ENABLE_RUNNER_PCH "Precompile the heavy runtime headers for ps2EntryRunner" ON) option(PS2X_ENABLE_SCCACHE "Use sccache as compiler launcher when available" ON) -option(PS2X_ENABLE_RUNTIME_LOGS "Enable PS2 runtime logs" OFF) -option(PS2X_ENABLE_AGRESSIVE_LOGS "Enable very verbose/agressive PS2 runtime logs" OFF) +option(PS2X_ENABLE_RUNTIME_LOGS "Enable PS2 runtime logs" ON) +option(PS2X_ENABLE_AGRESSIVE_LOGS "Enable very verbose/agressive PS2 runtime logs" ON) option(PS2X_ENABLE_IOP_RPC_TRACE "Log unhandled IOP/SIF RPC trace suggestions" ON) option(PS2X_STRICT_RETURN_DIAGNOSTICS "Route generated JR $ra returns through runtime branch diagnostics" OFF) option(PS2X_SHOW_WINDOWS_CONSOLE "Show a console window for ps2EntryRunner on Windows release builds" ON) @@ -386,6 +386,8 @@ add_library(ps2_runtime STATIC src/lib/ps2_iop_host.cpp src/lib/ps2_memory.cpp src/lib/ps2_pad.cpp + src/lib/ps2_rom_device.cpp + src/lib/ps2_vfs.cpp src/lib/ps2_runtime.cpp src/lib/ps2_vif1_interpreter.cpp src/lib/vu/ps2_vu1_core.cpp diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index 9c03db2..94a6fa1 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -120,7 +120,6 @@ X(SetOsdConfigParam2) \ X(EnableCache) \ X(DisableCache) \ - X(GetRomName) \ X(SifLoadElfPart) \ X(sceSifLoadElf) \ X(sceSifLoadElfPart) \ diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 2bfc86f..cba28e5 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -31,6 +31,8 @@ #include "runtime/ps2_vu1.h" #include "runtime/ps2_audio.h" #include "runtime/ps2_pad.h" +#include "runtime/ps2_rom_device.h" +#include "runtime/ps2_vfs.h" #include "ps2x/iop/iop_types.h" namespace ps2x::iop @@ -293,6 +295,12 @@ public: [[nodiscard]] ps2x::iop::ModuleLoadResult loadIopModuleBuffer(uint32_t guestAddress, const void *arguments = nullptr, uint32_t argumentSize = 0); [[nodiscard]] bool stopIopModule(int32_t moduleId, int32_t *result = nullptr); [[nodiscard]] ps2x::iop::DebugSnapshot iopDebugSnapshot() const; + uint32_t allocateIopMemory(uint32_t size, uint32_t alignment = 16u); + bool freeIopMemory(uint32_t address); + bool readIopMemory(uint32_t address, void *destination, size_t size) const; + bool writeIopMemory(uint32_t address, const void *source, size_t size); + bool zeroIopMemory(uint32_t address, size_t size); + bool isIopMemoryRange(uint32_t address, size_t size) const; using DebugUiCallback = void (*)(PS2Runtime &runtime, void *userData); void setDebugUiCallbacks(DebugUiCallback initCallback, @@ -444,6 +452,10 @@ public: inline const PS2AudioBackend &audioBackend() const { return m_audioBackend; } inline PSPadBackend &padBackend() { return m_padBackend; } inline const PSPadBackend &padBackend() const { return m_padBackend; } + inline PS2RomDevice &romDevice() { return m_romDevice; } + inline const PS2RomDevice &romDevice() const { return m_romDevice; } + inline PS2Vfs &vfs() { return m_vfs; } + inline const PS2Vfs &vfs() const { return m_vfs; } private: struct GuestHeapBlock @@ -484,6 +496,8 @@ private: std::unique_ptr m_iopSubsystem; PS2AudioBackend m_audioBackend; PSPadBackend m_padBackend; + PS2RomDevice m_romDevice; + PS2Vfs m_vfs; VU1Interpreter m_vu0{VU1Interpreter::Unit::VU0}; VU1Interpreter m_vu1{VU1Interpreter::Unit::VU1}; R5900Context m_cpuContext; diff --git a/ps2xRuntime/include/ps2_syscalls.h b/ps2xRuntime/include/ps2_syscalls.h index 0360e60..e0c4f5b 100644 --- a/ps2xRuntime/include/ps2_syscalls.h +++ b/ps2xRuntime/include/ps2_syscalls.h @@ -11,8 +11,6 @@ std::string translatePs2Path(const char *ps2Path); -inline std::mutex g_sys_fd_mutex; - namespace ps2_syscalls { #define PS2_DECLARE_SYSCALL(name) void name(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); diff --git a/ps2xRuntime/include/runtime/ee_scheduler.h b/ps2xRuntime/include/runtime/ee_scheduler.h index fae8b85..f247ff9 100644 --- a/ps2xRuntime/include/runtime/ee_scheduler.h +++ b/ps2xRuntime/include/runtime/ee_scheduler.h @@ -90,6 +90,7 @@ enum class GuestInvocationKind : uint8_t SyscallOverride, ExitHandler, HleCall, + SifCommand, }; struct GuestInvocation @@ -181,6 +182,9 @@ struct EeThreadSnapshot { int id = 0; uint32_t pc = 0; + uint32_t ra = 0; + uint32_t sp = 0; + uint32_t contextGp = 0; uint32_t entry = 0; uint32_t stack = 0; uint32_t stackSize = 0; @@ -192,6 +196,7 @@ struct EeThreadSnapshot int waitId = 0; int suspendCount = 0; uint32_t wakeupCount = 0; + uint32_t invocationDepth = 0; }; struct EeSemaphoreSnapshot @@ -390,6 +395,8 @@ private: [[nodiscard]] bool hasReadyAtOrAbovePriority(int priority) const; void renewTimeSlice(); void copyMainContextToRuntime(); + void publishDebugContext(const R5900Context &context); + void publishIdleDebugContext(); PS2Runtime &m_runtime; uint8_t *m_rdram = nullptr; diff --git a/ps2xRuntime/include/runtime/gs/gs_backend.h b/ps2xRuntime/include/runtime/gs/gs_backend.h index 9419cc2..3f00d01 100644 --- a/ps2xRuntime/include/runtime/gs/gs_backend.h +++ b/ps2xRuntime/include/runtime/gs/gs_backend.h @@ -14,6 +14,7 @@ public: virtual void Reset() = 0; virtual void Submit(const GSPrimitiveBatch &batch) = 0; + virtual void LoadClut(const GSTex0Reg &tex0, const GSTexClutReg &texclut) = 0; virtual void BeginTransfer(const GSTransferCommand &command) = 0; virtual void UploadImage(const uint8_t *data, uint32_t sizeBytes) = 0; diff --git a/ps2xRuntime/include/runtime/gs/gs_cpu_backend.h b/ps2xRuntime/include/runtime/gs/gs_cpu_backend.h index 71e8032..3c144c4 100644 --- a/ps2xRuntime/include/runtime/gs/gs_cpu_backend.h +++ b/ps2xRuntime/include/runtime/gs/gs_cpu_backend.h @@ -16,6 +16,7 @@ public: void Reset() override; void Submit(const GSPrimitiveBatch &batch) override; + void LoadClut(const GSTex0Reg &tex0, const GSTexClutReg &texclut) override; void BeginTransfer(const GSTransferCommand &command) override; void UploadImage(const uint8_t *data, uint32_t sizeBytes) override; @@ -34,7 +35,9 @@ public: private: void ResetUnlocked(); + void LoadClutUnlocked(const GSTex0Reg &tex0, const GSTexClutReg &texclut); uint32_t ReadVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y) const; + uint32_t ReadTextureVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y); void WriteVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y, uint32_t value); void DrawPrimitive(const GSPrimitiveBatch &batch); @@ -43,7 +46,7 @@ private: void DrawLine(const GSPrimitiveBatch &batch); void WritePixel(const GSDrawState &state, int x, int y, int z, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t fog); uint32_t SampleTexture(const GSDrawState &state, float s, float t, float q, uint16_t u, uint16_t v); - uint32_t LookupCLUT(const GSDrawState &state, uint8_t index, uint32_t cbp, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm); + uint32_t LookupCLUT(const GSDrawState &state, uint8_t index, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm); void PerformLocalToLocalTransfer(); void PerformLocalToHostTransfer(); @@ -67,6 +70,10 @@ private: uint32_t m_vramSize = 0; std::array m_readVramFuncs{}; std::array m_writeVramFuncs{}; + std::array m_clut{}; + std::array m_clutCbp{}; + std::vector m_texturePageBuffer; + uint32_t m_texturePageIndex = UINT32_MAX; GSTransferCommand m_transfer{}; GSTransferSnapshot m_transferState{}; diff --git a/ps2xRuntime/include/runtime/ps2_memory.h b/ps2xRuntime/include/runtime/ps2_memory.h index cea5b98..ea86a9c 100644 --- a/ps2xRuntime/include/runtime/ps2_memory.h +++ b/ps2xRuntime/include/runtime/ps2_memory.h @@ -449,6 +449,8 @@ public: }; std::array m_eeTimers{}; + bool tryProcessScratchpadDma(uint32_t channelBase, uint32_t chcr); + void completeDmacChannel(uint32_t channelBase, uint32_t cause); void queueCompletedDmacCause(uint32_t cause); }; diff --git a/ps2xRuntime/include/runtime/ps2_rom_device.h b/ps2xRuntime/include/runtime/ps2_rom_device.h new file mode 100644 index 0000000..d205f6a --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_rom_device.h @@ -0,0 +1,43 @@ +#pragma once + +#include "ps2x/iop/iop_types.h" + +#include +#include +#include +#include +#include +#include +#include + +struct PS2RomProfile +{ + std::string id; + std::string provider = "application"; + ps2x::iop::GameMatcher matcher; + std::unordered_map> files; +}; + +class PS2RomDevice +{ +public: + PS2RomDevice(); + + static void registerProfile(PS2RomProfile profile); + + bool configure(const ps2x::iop::GameIdentity &identity, std::string *error = nullptr); + [[nodiscard]] bool readFile(std::string_view ps2Path, std::vector &bytes) const; + [[nodiscard]] bool fileSize(std::string_view ps2Path, uint64_t &size) const; + [[nodiscard]] bool contains(std::string_view ps2Path) const; + [[nodiscard]] std::string_view activeProfile() const noexcept { return m_activeProfile; } + [[nodiscard]] std::string_view activeProvider() const noexcept { return m_activeProvider; } + +private: + static std::string normalizePath(std::string_view path); + void mountBaseProfile(); + void mountFiles(const std::unordered_map> &files); + + std::unordered_map> m_files; + std::string m_activeProfile; + std::string m_activeProvider; +}; diff --git a/ps2xRuntime/include/runtime/ps2_vfs.h b/ps2xRuntime/include/runtime/ps2_vfs.h new file mode 100644 index 0000000..95b0bdc --- /dev/null +++ b/ps2xRuntime/include/runtime/ps2_vfs.h @@ -0,0 +1,82 @@ +#pragma once + +#include "ps2x/iop/ps2_path.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class PS2RomDevice; + +struct PS2VfsMounts +{ + std::filesystem::path hostRoot; + std::filesystem::path cdRoot; + std::filesystem::path memoryCard0Root; +}; + +struct PS2VfsStat +{ + bool directory = false; + bool readOnly = false; + uint64_t size = 0u; + std::time_t created = 0; + std::time_t accessed = 0; + std::time_t modified = 0; +}; + +struct PS2VfsDescriptorInfo +{ + int32_t descriptor = -1; + std::string device; + std::string path; +}; + +class IPS2OpenFile +{ +public: + virtual ~IPS2OpenFile() = default; + + [[nodiscard]] virtual int64_t read(void *destination, size_t size) = 0; + [[nodiscard]] virtual int64_t write(const void *source, size_t size) = 0; + [[nodiscard]] virtual int64_t seek(int64_t offset, int whence) = 0; +}; + +class PS2Vfs +{ +public: + PS2Vfs() = default; + ~PS2Vfs(); + + PS2Vfs(const PS2Vfs &) = delete; + PS2Vfs &operator=(const PS2Vfs &) = delete; + + [[nodiscard]] int32_t open(std::string_view path, uint32_t flags, const PS2VfsMounts &mounts, const PS2RomDevice &rom); + [[nodiscard]] int32_t close(int32_t descriptor); + [[nodiscard]] int64_t read(int32_t descriptor, void *destination, size_t size); + [[nodiscard]] int64_t write(int32_t descriptor, const void *source, size_t size); + [[nodiscard]] int64_t seek(int32_t descriptor, int64_t offset, int whence); + + [[nodiscard]] bool stat(std::string_view path, const PS2VfsMounts &mounts, const PS2RomDevice &rom, PS2VfsStat &result) const; + [[nodiscard]] bool resolveHostPath(std::string_view path, const PS2VfsMounts &mounts, std::filesystem::path &result) const; + [[nodiscard]] std::vector descriptors() const; + +private: + struct OpenDescriptor + { + std::unique_ptr file; + std::string device; + std::string path; + }; + + mutable std::mutex m_mutex; + std::unordered_map m_descriptors; + int32_t m_nextDescriptor = 3; +}; diff --git a/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp b/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp index 04efaa5..c5e31c4 100644 --- a/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp +++ b/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp @@ -170,6 +170,8 @@ void EeScheduler::run() GuestThread *next = selectReady(); if (!next && m_pendingInvocations.empty()) { + copyMainContextToRuntime(); + publishIdleDebugContext(); publishSnapshot(); waitForEvent(); continue; @@ -224,10 +226,7 @@ void EeScheduler::run() --m_debugPublishCountdown; } - m_runtime.m_debugPc.store(context.pc, std::memory_order_relaxed); - m_runtime.m_debugRa.store(getRegU32(&context, 31), std::memory_order_relaxed); - m_runtime.m_debugSp.store(getRegU32(&context, 29), std::memory_order_relaxed); - m_runtime.m_debugGp.store(getRegU32(&context, 28), std::memory_order_relaxed); + publishDebugContext(context); if (context.pc == 0u) { @@ -249,10 +248,13 @@ void EeScheduler::run() } makeDormant(*running); m_currentThreadId = 0; + copyMainContextToRuntime(); + publishIdleDebugContext(); + publishSnapshot(); continue; } - if (!m_pendingInvocations.empty()) + if (!m_pendingInvocations.empty() && running->invocations.empty()) { GuestInvocation invocation = std::move(m_pendingInvocations.front()); m_pendingInvocations.pop_front(); @@ -1497,7 +1499,11 @@ void EeScheduler::publishSnapshot() } EeThreadSnapshot snapshot{}; snapshot.id = id; - snapshot.pc = item.activeContext().pc; + const R5900Context &context = item.activeContext(); + snapshot.pc = context.pc; + snapshot.ra = getRegU32(&context, 31); + snapshot.sp = getRegU32(&context, 29); + snapshot.contextGp = getRegU32(&context, 28); snapshot.entry = item.entry; snapshot.stack = item.stack; snapshot.stackSize = item.stackSize; @@ -1509,6 +1515,7 @@ void EeScheduler::publishSnapshot() snapshot.waitId = waitObjectId(item.wait); snapshot.suspendCount = item.suspendCount; snapshot.wakeupCount = item.wakeupCount; + snapshot.invocationDepth = static_cast(item.invocations.size()); next.threads.push_back(snapshot); } std::sort(next.threads.begin(), next.threads.end(), [](const auto &left, const auto &right) @@ -2105,3 +2112,35 @@ void EeScheduler::copyMainContextToRuntime() m_runtime.m_cpuContext = main->context; } } + +void EeScheduler::publishDebugContext(const R5900Context &context) +{ + m_runtime.m_debugPc.store(context.pc, std::memory_order_relaxed); + m_runtime.m_debugRa.store(getRegU32(&context, 31), std::memory_order_relaxed); + m_runtime.m_debugSp.store(getRegU32(&context, 29), std::memory_order_relaxed); + m_runtime.m_debugGp.store(getRegU32(&context, 28), std::memory_order_relaxed); +} + +void EeScheduler::publishIdleDebugContext() +{ + // Temporary IRQ/RPC/alarm invocations deliberately return to PC=0. Once + // the scheduler is idle, show a real EE thread context instead of leaving + // the debugger pinned to that completed dispatcher frame. + const GuestThread *selected = thread(kMainThreadId); + if (!selected) + { + for (const auto &[id, candidate] : m_threads) + { + if (id > 0 && candidate.status != EeThreadStatus::Dormant) + { + selected = &candidate; + break; + } + } + } + + if (selected) + { + publishDebugContext(selected->activeContext()); + } +} diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h index a7fae20..b1bb6d8 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Helpers/Support.h @@ -29,11 +29,6 @@ namespace uint32_t g_cdStreamingEndLbn = 0xFFFFFFFFu; bool g_cdInitialized = false; - constexpr uint32_t kIopHeapBase = 0x04000000; - constexpr uint32_t kIopHeapLimit = 0x04500000; - constexpr uint32_t kIopHeapAlign = 64; - uint32_t g_iopHeapNext = kIopHeapBase; - std::string toLowerAscii(std::string value) { std::transform(value.begin(), value.end(), value.begin(), @@ -1360,7 +1355,11 @@ namespace uint32_t madr = 0; uint32_t qwc = 0; uint32_t tadr = payloadPhys; - uint32_t chcr = 0x00000181u; // DIR=1, TIE=1, STR=1 (normal mode). + PS2Memory &mem = runtime->memory(); + + const uint32_t configuredChcr = mem.readIORegister(channelBase + 0x00u); + const uint32_t transferTagEnable = configuredChcr & 0x00000040u; + uint32_t chcr = 0x00000181u | transferTagEnable; // DIR=1, TIE=1, STR=1 (normal mode). if (preferNormalCount) { @@ -1369,10 +1368,9 @@ namespace } else { - chcr = 0x00000185u; // MODE=1 chain, DIR=1, TIE=1, STR=1. + chcr = 0x00000185u | transferTagEnable; // MODE=1 chain, DIR=1, TIE=1, STR=1. } - PS2Memory &mem = runtime->memory(); mem.writeIORegister(channelBase + 0x20u, qwc & 0xFFFFu); mem.writeIORegister(channelBase + 0x10u, madr); mem.writeIORegister(channelBase + 0x30u, tadr); @@ -1402,10 +1400,10 @@ namespace if (g_dmaStubLogCount < kMaxDmaStubLogs) { RUNTIME_LOG("[sceDmaSend] ch=0x" << std::hex << channelBase - << " madr=0x" << madr - << " qwc=0x" << qwc - << " tadr=0x" << tadr - << " chcr=0x" << chcr << std::dec << std::endl); + << " madr=0x" << madr + << " qwc=0x" << qwc + << " tadr=0x" << tadr + << " chcr=0x" << chcr << std::dec << std::endl); if (!preferNormalCount && (channelBase == 0x10009000u || channelBase == 0x1000A000u)) { @@ -1418,13 +1416,13 @@ namespace 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); + << " 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; @@ -1838,9 +1836,9 @@ namespace return true; } - static bool readGsDBuff(uint8_t* rdram, uint32_t addr, GsDBuffMem& out) + static bool readGsDBuff(uint8_t *rdram, uint32_t addr, GsDBuffMem &out) { - const uint8_t* ptr = getConstMemPtr(rdram, addr); + const uint8_t *ptr = getConstMemPtr(rdram, addr); if (!ptr) return false; std::memcpy(&out, ptr, sizeof(out)); @@ -1856,9 +1854,9 @@ namespace return true; } - static bool writeGsDBuff(uint8_t* rdram, uint32_t addr, const GsDBuffMem& db) + static bool writeGsDBuff(uint8_t *rdram, uint32_t addr, const GsDBuffMem &db) { - uint8_t* ptr = getMemPtr(rdram, addr); + uint8_t *ptr = getMemPtr(rdram, addr); if (!ptr) return false; std::memcpy(ptr, &db, sizeof(db)); diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp index 77c2bfd..dc975ed 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.cpp @@ -3,10 +3,11 @@ #include "../Syscalls/RPC.h" #include "../../ps2_iop_transport.h" #include "runtime/ps2_address.h" +#include "runtime/ee_scheduler.h" #include +#include #include -#include #include namespace ps2_stubs @@ -28,15 +29,22 @@ namespace ps2_stubs const uint32_t size = readStackU32(rdram, ctx, 20); if (size != 0u && srcAddr != 0u && dstAddr != 0u) { + std::vector payload(size); + bool valid = runtime != nullptr; 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) + if (!src) { + valid = false; break; } - *dst = *src; + payload[i] = *src; + } + if (!valid || !runtime->writeIopMemory(dstAddr, payload.data(), payload.size())) + { + setReturnS32(ctx, 0); + return; } } @@ -57,12 +65,15 @@ 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; - std::array g_sifHeapStorage{}; + struct SifCmdHandler + { + uint32_t function = 0u; + uint32_t argument = 0u; + }; + + std::unordered_map g_sifCmdHandlers; uint32_t g_sifCmdBuffer = 0u; uint32_t g_sifSysCmdBuffer = 0u; bool g_sifCmdInitialized = false; @@ -127,92 +138,6 @@ 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; - std::fill_n(g_sifHeapStorage.data() + (candidate - kIopHeapBase), - alignedSize, - uint8_t{0}); - 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_sifHeapStorage.fill(0u); - g_iopHeapNext = kIopHeapBase; - } - - bool isAllocatedSifHeapRangeLocked(uint32_t address, size_t size) - { - if (address < kIopHeapBase || address >= kIopHeapLimit || size > static_cast(kIopHeapLimit - address)) - { - return false; - } - - auto it = g_sifHeapAllocations.upper_bound(address); - if (it == g_sifHeapAllocations.begin()) - { - return false; - } - --it; - - const uint64_t allocationEnd = static_cast(it->first) + it->second; - const uint64_t rangeEnd = static_cast(address) + size; - return address >= it->first && rangeEnd <= allocationEnd; - } - bool isCopyableGuestAddress(uint32_t addr) { if (Ps2AddressInRange(addr, PS2_SCRATCHPAD_BASE, PS2_SCRATCHPAD_SIZE)) @@ -238,13 +163,9 @@ namespace ps2_stubs return false; } - bool canCopyAddressRange(const uint8_t *rdram, uint32_t address, uint32_t sizeBytes) + bool canAccessEeRange(const uint8_t *rdram, uint32_t address, uint32_t sizeBytes) { - if (isSifIopHeapRange(address, sizeBytes)) - { - return true; - } - if (isSifIopHeapAddress(address) || !rdram) + if (!rdram) { return false; } @@ -259,7 +180,7 @@ namespace ps2_stubs for (uint32_t i = 0u; i < sizeBytes; ++i) { const uint32_t byteAddress = address + i; - if (!isCopyableGuestAddress(byteAddress) ||getConstMemPtr(rdram, byteAddress) == nullptr) + if (!isCopyableGuestAddress(byteAddress) || getConstMemPtr(rdram, byteAddress) == nullptr) { return false; } @@ -267,200 +188,129 @@ namespace ps2_stubs return true; } - bool canCopyGuestByteRange(const uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) + bool readEeRange(const uint8_t *rdram, uint32_t address, void *destination, uint32_t sizeBytes) { - return canCopyAddressRange(rdram, srcAddr, sizeBytes) && canCopyAddressRange(rdram, dstAddr, sizeBytes); - } - - bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes) - { - if (!canCopyGuestByteRange(rdram, dstAddr, srcAddr, sizeBytes)) - { + if ((!destination && sizeBytes != 0u) || !canAccessEeRange(rdram, address, sizeBytes)) return false; - } - - if (sizeBytes == 0u) + auto *bytes = static_cast(destination); + for (uint32_t i = 0u; i < sizeBytes; ++i) { - return true; - } - - const bool sourceIsIop = isSifIopHeapRange(srcAddr, sizeBytes); - const bool destinationIsIop = isSifIopHeapRange(dstAddr, sizeBytes); - if (sourceIsIop || destinationIsIop) - { - std::vector payload(sizeBytes); - if (sourceIsIop) - { - if (!readSifIopHeap(srcAddr, payload.data(), payload.size())) - { - return false; - } - } - else - { - for (uint32_t i = 0u; i < sizeBytes; ++i) - { - const uint8_t *src = getConstMemPtr(rdram, srcAddr + i); - if (!src) - { - return false; - } - payload[i] = *src; - } - } - - if (destinationIsIop) - { - return writeSifIopHeap(dstAddr, payload.data(), payload.size()); - } - - ps2TraceGuestRangeWrite(rdram, dstAddr, sizeBytes, "sifCopyGuestByteRange", nullptr); - for (uint32_t i = 0u; i < sizeBytes; ++i) - { - uint8_t *dst = getMemPtr(rdram, dstAddr + i); - if (!dst) - { - return false; - } - *dst = payload[i]; - } - return true; - } - - ps2TraceGuestRangeWrite(rdram, dstAddr, sizeBytes, "sifCopyGuestByteRange", nullptr); - - 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) - { + const uint8_t *source = getConstMemPtr(rdram, address + i); + if (!source) return false; - } - *dst = *src; + bytes[i] = *source; } return true; } - } - bool isSifIopHeapAddress(uint32_t address) - { - return address >= kIopHeapBase && address < kIopHeapLimit; - } - - bool isSifIopHeapRange(uint32_t address, size_t size) - { - std::lock_guard lock(g_sifHeapMutex); - return isAllocatedSifHeapRangeLocked(address, size); - } - - bool readSifIopHeap(uint32_t address, void *destination, size_t size) - { - if (!destination && size != 0u) + bool writeEeRange(uint8_t *rdram, uint32_t address, const void *source, uint32_t sizeBytes) { - return false; + if ((!source && sizeBytes != 0u) || !canAccessEeRange(rdram, address, sizeBytes)) + return false; + ps2TraceGuestRangeWrite(rdram, address, sizeBytes, "SIF IOP-to-EE DMA", nullptr); + const auto *bytes = static_cast(source); + for (uint32_t i = 0u; i < sizeBytes; ++i) + { + uint8_t *destination = getMemPtr(rdram, address + i); + if (!destination) + return false; + *destination = bytes[i]; + } + return true; } - std::lock_guard lock(g_sifHeapMutex); - if (!isAllocatedSifHeapRangeLocked(address, size)) - { - return false; - } - if (size != 0u) - { - std::memcpy(destination, - g_sifHeapStorage.data() + (address - kIopHeapBase), - size); - } - return true; - } - - bool writeSifIopHeap(uint32_t address, const void *source, size_t size) - { - if (!source && size != 0u) - { - return false; - } - std::lock_guard lock(g_sifHeapMutex); - if (!isAllocatedSifHeapRangeLocked(address, size)) - { - return false; - } - if (size != 0u) - { - std::memcpy(g_sifHeapStorage.data() + (address - kIopHeapBase), - source, - size); - } - return true; - } - - bool zeroSifIopHeap(uint32_t address, size_t size) - { - std::lock_guard lock(g_sifHeapMutex); - if (!isAllocatedSifHeapRangeLocked(address, size)) - { - return false; - } - if (size != 0u) - { - std::memset(g_sifHeapStorage.data() + (address - kIopHeapBase), 0, size); - } - return true; } void resetSifState() { std::lock_guard lock(g_sifCmdStateMutex); seedDefaultSifRegsLocked(); - resetSifHeapState(); + } + + bool dispatchSifCommand(uint8_t *rdram, + PS2Runtime *runtime, + uint32_t commandId, + const void *packet, + size_t packetSize) noexcept + { + if (!rdram || !runtime || !packet || packetSize < 16u || packetSize > 112u) + return false; + + SifCmdHandler registered{}; + { + std::lock_guard lock(g_sifCmdStateMutex); + const auto handler = g_sifCmdHandlers.find(commandId); + if (handler == g_sifCmdHandlers.end() || handler->second.function == 0u) + return false; + registered = handler->second; + } + + if (!runtime->hasFunction(registered.function)) + return false; + + const uint32_t packetAddress = runtime->guestMalloc(static_cast(packetSize), 16u); + if (packetAddress == 0u) + return false; + + uint8_t *const first = getMemPtr(rdram, packetAddress); + uint8_t *const last = getMemPtr(rdram, packetAddress + static_cast(packetSize - 1u)); + if (!first || !last || last < first || static_cast(last - first) != packetSize - 1u) + { + runtime->guestFree(packetAddress); + return false; + } + + ps2TraceGuestRangeWrite(rdram, packetAddress, static_cast(packetSize), "SIF command packet", nullptr); + std::memcpy(first, packet, packetSize); + + try + { + GuestInvocation invocation{}; + invocation.kind = GuestInvocationKind::SifCommand; + invocation.tag = commandId; + invocation.context = runtime->cpu(); + invocation.context.pc = registered.function; + SET_GPR_U32(&invocation.context, 4, packetAddress); + SET_GPR_U32(&invocation.context, 5, registered.argument); + SET_GPR_U32(&invocation.context, 6, 0u); + SET_GPR_U32(&invocation.context, 7, 0u); + SET_GPR_U32(&invocation.context, 29, 0u); + SET_GPR_U32(&invocation.context, 31, 0u); + invocation.onComplete = [runtime, packetAddress](const R5900Context &, R5900Context &) + { + runtime->guestFree(packetAddress); + }; + runtime->eeScheduler().queueInvocation(std::move(invocation)); + return true; + } + catch (...) + { + runtime->guestFree(packetAddress); + return false; + } } void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t cid = getRegU32(ctx, 4); const uint32_t handler = getRegU32(ctx, 5); + const uint32_t argument = getRegU32(ctx, 6); std::lock_guard lock(g_sifCmdStateMutex); - g_sifCmdHandlers[cid] = handler; + g_sifCmdHandlers[cid] = SifCmdHandler{handler, argument}; setReturnS32(ctx, 0); } void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)rdram; - (void)runtime; - const uint32_t reqSize = getRegU32(ctx, 4); - setReturnU32(ctx, allocateSifHeapBlock(reqSize)); + setReturnU32(ctx, runtime ? runtime->allocateIopMemory(reqSize, 64u) : 0u); } void sceSifAllocSysMemory(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)rdram; - (void)runtime; - const uint32_t size = getRegU32(ctx, 5); - setReturnU32(ctx, allocateSifHeapBlock(size)); + setReturnU32(ctx, runtime ? runtime->allocateIopMemory(size, 64u) : 0u); } void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -503,19 +353,15 @@ namespace ps2_stubs void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)rdram; - (void)runtime; - const uint32_t addr = getRegU32(ctx, 4); - setReturnS32(ctx, freeSifHeapBlock(addr) ? 0 : -1); + setReturnS32(ctx, runtime && runtime->freeIopMemory(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); + setReturnS32(ctx, runtime && runtime->freeIopMemory(addr) ? 0 : -1); } void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -564,15 +410,19 @@ namespace ps2_stubs if (runtime) { PS2IopTransport::notifyTransfer(runtime, rdram, { - ps2x::iop::SifTransferKind::GetOtherData, - ps2x::iop::SifTransferPhase::BeforeCopy, - srcAddr, - dstAddr, - size, - }); + ps2x::iop::SifTransferKind::GetOtherData, + ps2x::iop::SifTransferPhase::BeforeCopy, + srcAddr, + dstAddr, + size, + }); } - if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size)) + std::vector payload(size); + if (!runtime || !runtime->isIopMemoryRange(srcAddr, size) || + !canAccessEeRange(rdram, dstAddr, size) || + !runtime->readIopMemory(srcAddr, payload.data(), payload.size()) || + !writeEeRange(rdram, dstAddr, payload.data(), size)) { static uint32_t warnCount = 0; if (warnCount < 32u) @@ -600,12 +450,12 @@ namespace ps2_stubs if (runtime) { PS2IopTransport::notifyTransfer(runtime, rdram, { - ps2x::iop::SifTransferKind::GetOtherData, - ps2x::iop::SifTransferPhase::AfterCopy, - srcAddr, - dstAddr, - size, - }); + ps2x::iop::SifTransferKind::GetOtherData, + ps2x::iop::SifTransferPhase::AfterCopy, + srcAddr, + dstAddr, + size, + }); } setReturnS32(ctx, 0); @@ -668,7 +518,7 @@ namespace ps2_stubs void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { - resetSifHeapState(); + // The physical IOP allocator is initialized by IopSubsystem::reset(). setReturnS32(ctx, 0); } @@ -840,7 +690,7 @@ namespace ps2_stubs ok = false; break; } - if (!canCopyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes)) + if (!runtime || !canAccessEeRange(rdram, xfer.src, sizeBytes) || !runtime->isIopMemoryRange(xfer.dest, sizeBytes)) { ok = false; break; @@ -857,14 +707,16 @@ namespace ps2_stubs if (runtime) { PS2IopTransport::notifyTransfer(runtime, rdram, { - ps2x::iop::SifTransferKind::SetDma, - ps2x::iop::SifTransferPhase::BeforeCopy, - xfer.src, - xfer.dest, - static_cast(xfer.size), - }); + ps2x::iop::SifTransferKind::SetDma, + ps2x::iop::SifTransferPhase::BeforeCopy, + xfer.src, + xfer.dest, + static_cast(xfer.size), + }); } - if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast(xfer.size))) + const uint32_t sizeBytes = static_cast(xfer.size); + std::vector payload(sizeBytes); + if (!readEeRange(rdram, xfer.src, payload.data(), sizeBytes) || !runtime->writeIopMemory(xfer.dest, payload.data(), payload.size())) { ok = false; break; @@ -872,12 +724,12 @@ namespace ps2_stubs if (runtime) { PS2IopTransport::notifyTransfer(runtime, rdram, { - ps2x::iop::SifTransferKind::SetDma, - ps2x::iop::SifTransferPhase::AfterCopy, - xfer.src, - xfer.dest, - static_cast(xfer.size), - }); + ps2x::iop::SifTransferKind::SetDma, + ps2x::iop::SifTransferPhase::AfterCopy, + xfer.src, + xfer.dest, + static_cast(xfer.size), + }); } } } diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h index 777e1cb..8673e80 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h +++ b/ps2xRuntime/src/lib/Kernel/Stubs/SIF.h @@ -2,16 +2,9 @@ #include "ps2_stubs.h" -#include - namespace ps2_stubs { - bool isSifIopHeapAddress(uint32_t address); - bool isSifIopHeapRange(uint32_t address, size_t size); - bool readSifIopHeap(uint32_t address, void *destination, size_t size); - bool writeSifIopHeap(uint32_t address, const void *source, size_t size); - bool zeroSifIopHeap(uint32_t address, size_t size); - + bool dispatchSifCommand(uint8_t *rdram, PS2Runtime *runtime, uint32_t commandId, const void *packet, size_t packetSize) noexcept; 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); diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h index e7362e0..c0b13bf 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Common.h @@ -4,6 +4,7 @@ #include "runtime/ee_scheduler.h" #include "ps2_runtime_macros.h" #include "ps2_stubs.h" +#include "ps2x/iop/ps2_path.h" #include #include #include diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp index 89de596..3b2be31 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Dispatcher.cpp @@ -253,6 +253,9 @@ namespace ps2_syscalls case 0x64: FlushCache(rdram, ctx, runtime); return true; + case static_cast(-0x68): + iFlushCache(rdram, ctx, runtime); + return true; case 0x6E: SetOsdConfigParam2(rdram, ctx, runtime); return true; diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp index 60c68c0..ccfca5e 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/FileIO.cpp @@ -3,32 +3,10 @@ namespace ps2_syscalls { - static int allocatePs2Fd(FILE *file) + static PS2VfsMounts currentVfsMounts() { - if (!file) - return -1; - - 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()) - { - return it->second; - } - return nullptr; - } - - static void releasePs2Fd(int ps2Fd) - { - std::lock_guard lock(g_fd_mutex); - g_fileDescriptors.erase(ps2Fd); + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + return {paths.hostRoot, paths.cdRoot, paths.mcRoot}; } struct VagAccumEntry @@ -40,39 +18,6 @@ namespace ps2_syscalls 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"; - } - void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t pathAddr = getRegU32(ctx, 4); // $a0 @@ -86,52 +31,32 @@ namespace ps2_syscalls return; } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + if (!runtime) { - std::cerr << "fioOpen error: Failed to translate path '" << ps2Path << "'" << std::endl; setReturnS32(ctx, -1); return; } - const char *mode = translateFioMode(flags); - RUNTIME_LOG("fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'"); - - 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; - } - - 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; - } - - // returns the PS2 file descriptor - setReturnS32(ctx, ps2Fd); + const int32_t descriptor = runtime->vfs().open(ps2Path, static_cast(flags), currentVfsMounts(), runtime->romDevice()); + setReturnS32(ctx, descriptor); } void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int ps2Fd = (int)getRegU32(ctx, 4); - FILE *fp = getHostFile(ps2Fd); - if (!fp) + if (!runtime) { - std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl; setReturnS32(ctx, -1); return; } - int ret = ::fclose(fp); - releasePs2Fd(ps2Fd); + const int32_t ret = runtime->vfs().close(ps2Fd); + if (ret < 0) + { + setReturnS32(ctx, -1); + return; + } { std::lock_guard lock(g_vagAccumMutex); @@ -161,7 +86,7 @@ namespace ps2_syscalls } } - setReturnS32(ctx, ret == 0 ? 0 : -1); + setReturnS32(ctx, 0); } void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -171,15 +96,13 @@ namespace ps2_syscalls size_t size = getRegU32(ctx, 6); // $a2 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) + if (!runtime) { std::cerr << "fioRead error: Invalid file descriptor " << ps2Fd << std::endl; setReturnS32(ctx, -1); // -EBADF @@ -191,24 +114,18 @@ namespace ps2_syscalls return; } - size_t bytesRead = 0; + const int64_t readResult = runtime->vfs().read(ps2Fd, hostBuf, size); + if (readResult < 0) { - std::lock_guard lock(g_sys_fd_mutex); - bytesRead = fread(hostBuf, 1, size, fp); + setReturnS32(ctx, -1); + return; } + const size_t bytesRead = static_cast(readResult); if (bytesRead > 0) { ps2TraceGuestRangeWrite(rdram, bufAddr, static_cast(bytesRead), "fioRead", ctx); } - 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); @@ -254,8 +171,7 @@ namespace ps2_syscalls return; } - FILE *fp = getHostFile(ps2Fd); - if (!fp) + if (!runtime) { setReturnS32(ctx, -1); // -EFAULT return; @@ -267,20 +183,15 @@ namespace ps2_syscalls return; } - size_t bytesWritten = 0; + const int64_t writeResult = runtime->vfs().write(ps2Fd, hostBuf, size); + if (writeResult < 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; - } + setReturnS32(ctx, -1); + return; } // returns number of bytes written - setReturnS32(ctx, (int32_t)bytesWritten); + setReturnS32(ctx, static_cast(writeResult)); } void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -289,8 +200,7 @@ namespace ps2_syscalls 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) - FILE *fp = getHostFile(ps2Fd); - if (!fp) + if (!runtime) { std::cerr << "fioLseek error: Invalid file descriptor " << ps2Fd << std::endl; setReturnS32(ctx, -1); // -EBADF @@ -315,22 +225,14 @@ namespace ps2_syscalls 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); + const int64_t newPos = runtime->vfs().seek(ps2Fd, offset, hostWhence); 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) + if (static_cast(newPos) > 0x7FFFFFFFu) { std::cerr << "fioLseek warning: New position exceeds 32-bit for fd " << ps2Fd << std::endl; setReturnS32(ctx, -1); @@ -354,8 +256,8 @@ namespace ps2_syscalls setReturnS32(ctx, -1); // -EFAULT return; } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + std::filesystem::path hostPath; + if (!runtime || !runtime->vfs().resolveHostPath(ps2Path, currentVfsMounts(), hostPath)) { std::cerr << "fioMkdir error: Failed to translate path '" << ps2Path << "'" << std::endl; setReturnS32(ctx, -1); @@ -366,13 +268,13 @@ namespace ps2_syscalls if (!success && ec) { - std::cerr << "fioMkdir error: create_directory failed for '" << hostPath + std::cerr << "fioMkdir error: create_directory failed for '" << hostPath.string() << "': " << ec.message() << std::endl; setReturnS32(ctx, -1); } else { - RUNTIME_LOG("fioMkdir: Created directory '" << hostPath << "'"); + RUNTIME_LOG("fioMkdir: Created directory '" << hostPath.string() << "'"); setReturnS32(ctx, 0); // Success } } @@ -388,27 +290,14 @@ namespace ps2_syscalls return; } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + PS2VfsStat status; + if (!runtime || !runtime->vfs().stat(ps2Path, currentVfsMounts(), runtime->romDevice(), status) || !status.directory) { - 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); - - 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 + setReturnS32(ctx, 0); } } @@ -422,8 +311,8 @@ namespace ps2_syscalls setReturnS32(ctx, -1); return; } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + std::filesystem::path hostPath; + if (!runtime || !runtime->vfs().resolveHostPath(ps2Path, currentVfsMounts(), hostPath)) { std::cerr << "fioRmdir error: Failed to translate path '" << ps2Path << "'" << std::endl; setReturnS32(ctx, -1); @@ -435,20 +324,18 @@ namespace ps2_syscalls if (!success || ec) { - std::cerr << "fioRmdir error: remove failed for '" << hostPath - << "': " << ec.message() << std::endl; + std::cerr << "fioRmdir error: remove failed for '" << hostPath.string() << "': " << ec.message() << std::endl; setReturnS32(ctx, -1); } else { - RUNTIME_LOG("fioRmdir: Removed directory '" << hostPath << "'"); + RUNTIME_LOG("fioRmdir: Removed directory '" << hostPath.string() << "'"); setReturnS32(ctx, 0); // Success } } 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 @@ -468,15 +355,29 @@ namespace ps2_syscalls return; } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + if (!runtime) { - std::cerr << "fioGetstat error: Bad path translate" << std::endl; setReturnS32(ctx, -1); return; } - setReturnS32(ctx, -1); + PS2VfsStat status; + if (!runtime->vfs().stat(ps2Path, currentVfsMounts(), runtime->romDevice(), status)) + { + setReturnS32(ctx, -1); + return; + } + + io_stat_t guest{}; + guest.mode = (status.directory ? kFioSoIfDir : kFioSoIfReg) | kFioSoIROth | kFioSoIXOth | (status.readOnly ? 0u : kFioSoIWOth); + guest.size = static_cast(status.size & 0xFFFFFFFFu); + guest.hisize = static_cast(status.size >> 32u); + encodePs2Time(status.created, guest.ctime); + encodePs2Time(status.accessed, guest.atime); + encodePs2Time(status.modified, guest.mtime); + std::memcpy(ps2StatBuf, &guest, sizeof(guest)); + ps2TraceGuestRangeWrite(rdram, statBufAddr, sizeof(guest), "fioGetstat", ctx); + setReturnS32(ctx, 0); } void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) @@ -490,8 +391,8 @@ namespace ps2_syscalls return; } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) + std::filesystem::path hostPath; + if (!runtime || !runtime->vfs().resolveHostPath(ps2Path, currentVfsMounts(), hostPath)) { std::cerr << "fioRemove error: Path translate fail" << std::endl; setReturnS32(ctx, -1); @@ -503,13 +404,12 @@ namespace ps2_syscalls if (!success || ec) { - std::cerr << "fioRemove error: remove failed for '" << hostPath - << "': " << ec.message() << std::endl; + std::cerr << "fioRemove error: remove failed for '" << hostPath.string() << "': " << ec.message() << std::endl; setReturnS32(ctx, -1); } else { - RUNTIME_LOG("fioRemove: Removed file '" << hostPath << "'"); + RUNTIME_LOG("fioRemove: Removed file '" << hostPath.string() << "'"); setReturnS32(ctx, 0); // Success } } diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h index 5498e51..db46c5d 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Loader.h @@ -105,52 +105,6 @@ namespace ++g_sif_module_log_count; } - int32_t trackSifModuleLoad(const std::string &path) - { - if (path.empty()) - { - return -1; - } - - const std::string pathKey = normalizeSifModulePathKey(path); - if (pathKey.empty()) - { - return -1; - } - - std::lock_guard lock(g_sif_module_mutex); - - auto byPathIt = g_sif_module_id_by_path.find(pathKey); - if (byPathIt != g_sif_module_id_by_path.end()) - { - auto byIdIt = g_sif_modules_by_id.find(byPathIt->second); - if (byIdIt != g_sif_modules_by_id.end()) - { - SifModuleRecord &record = byIdIt->second; - record.loaded = true; - ++record.refCount; - return record.id; - } - } - - if (g_next_sif_module_id <= 0) - { - g_next_sif_module_id = 1; - } - - const int32_t moduleId = g_next_sif_module_id++; - SifModuleRecord record; - record.id = moduleId; - record.path = path; - record.pathKey = pathKey; - record.refCount = 1; - record.loaded = true; - - g_sif_module_id_by_path[pathKey] = moduleId; - g_sif_modules_by_id[moduleId] = record; - return moduleId; - } - int32_t trackSifModuleLoadExternal(const std::string &path, int32_t moduleId) { if (path.empty() || moduleId <= 0) diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h index 6909364..6204325 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/Runtime.h @@ -112,8 +112,11 @@ inline std::string translatePs2Path(const char *ps2Path) return {}; } - std::string pathStr(ps2Path); - std::string lower = toLowerAscii(pathStr); + const ps2x::iop::ParsedPs2Path parsed = ps2x::iop::parsePs2Path(ps2Path); + if (!parsed) + { + return {}; + } auto resolveWithBase = [&](const std::filesystem::path &base, const std::string &suffix) -> std::string { @@ -126,35 +129,19 @@ inline std::string translatePs2Path(const char *ps2Path) return resolved.lexically_normal().string(); }; - if (lower.rfind("host0:", 0) == 0 || lower.rfind("host:", 0) == 0) + switch (parsed.device) { - const std::size_t prefixLength = (lower.rfind("host0:", 0) == 0) ? 6 : 5; - return resolveWithBase(getConfiguredHostRoot(), pathStr.substr(prefixLength)); + case ps2x::iop::Ps2PathDevice::Host: + return resolveWithBase(getConfiguredHostRoot(), parsed.path); + case ps2x::iop::Ps2PathDevice::Cdrom: + return resolveWithBase(getConfiguredCdRoot(), parsed.path); + case ps2x::iop::Ps2PathDevice::MemoryCard0: + return resolveWithBase(getConfiguredMcRoot(), parsed.path); + case ps2x::iop::Ps2PathDevice::NativeHost: + return std::filesystem::path(parsed.path).lexically_normal().string(); + default: + return {}; } - - if (lower.rfind("cdrom0:", 0) == 0 || lower.rfind("cdrom:", 0) == 0) - { - const std::size_t prefixLength = (lower.rfind("cdrom0:", 0) == 0) ? 7 : 6; - return resolveWithBase(getConfiguredCdRoot(), pathStr.substr(prefixLength)); - } - - if (lower.rfind(kMc0Prefix, 0) == 0) - { - const std::size_t prefixLength = sizeof(kMc0Prefix) - 1; - return resolveWithBase(getConfiguredMcRoot(), pathStr.substr(prefixLength)); - } - - if (!pathStr.empty() && (pathStr.front() == '/' || pathStr.front() == '\\')) - { - return resolveWithBase(getConfiguredCdRoot(), pathStr); - } - - if (pathStr.size() > 1 && pathStr[1] == ':') - { - return pathStr; - } - - return resolveWithBase(getConfiguredCdRoot(), pathStr); } static bool localtimeSafe(const std::time_t *t, std::tm *out) diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h index 110b2f3..d6b7549 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/Helpers/State.h @@ -3,9 +3,6 @@ #include #include -inline std::unordered_map g_fileDescriptors; -inline int g_nextFd = 3; // Start after stdin, stdout, stderr - // Thread status #define THS_RUN 0x01 #define THS_READY 0x02 @@ -156,8 +153,6 @@ static constexpr uint32_t kFioSoIROth = 0x0004; static constexpr uint32_t kFioSoIWOth = 0x0002; static constexpr uint32_t kFioSoIXOth = 0x0001; -inline std::mutex g_fd_mutex; - struct RpcServerState { uint32_t sid = 0; diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp index 3a90547..07fcd29 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp @@ -207,40 +207,22 @@ namespace ps2_syscalls return; } - const auto emulated = runtime->loadIopModule(modulePath, arguments.empty() ? nullptr : arguments.data(), static_cast(arguments.size())); - if (emulated.handled) - { - if (emulated.moduleId <= 0) - { - setReturnS32(ctx, -1); - return; - } - - trackSifModuleLoadExternal(modulePath, emulated.moduleId); - logSifModuleAction("load-emulated", emulated.moduleId, modulePath, 1u); - setReturnS32(ctx, emulated.moduleId); - return; - } - - const int32_t moduleId = trackSifModuleLoad(modulePath); - if (moduleId <= 0) + if (!runtime) { setReturnS32(ctx, -1); return; } - uint32_t refs = 0; + const auto loaded = runtime->loadIopModule(modulePath, arguments.empty() ? nullptr : arguments.data(), static_cast(arguments.size())); + if (!loaded.handled || loaded.moduleId <= 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; - } + setReturnS32(ctx, -1); + return; } - logSifModuleAction("load", moduleId, modulePath, refs); - setReturnS32(ctx, moduleId); + trackSifModuleLoadExternal(modulePath, loaded.moduleId); + logSifModuleAction("load-emulated", loaded.moduleId, modulePath, 1u); + setReturnS32(ctx, loaded.moduleId); } void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp index 3b9bb6f..9614016 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.cpp @@ -250,32 +250,6 @@ namespace ps2_syscalls setReturnS32(ctx, 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"; - - 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'; - - // returns the length of the string (excluding null?) or error - setReturnS32(ctx, (int32_t)strlen(hostBuf)); - } - void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path @@ -331,39 +305,22 @@ namespace ps2_syscalls return; } - const auto emulated = runtime->loadIopModuleBuffer(bufferAddr, arguments.empty() ? nullptr : arguments.data(), static_cast(arguments.size())); - if (emulated.handled) - { - if (emulated.moduleId <= 0) - { - setReturnS32(ctx, -1); - return; - } - trackSifModuleLoadExternal(moduleTag, emulated.moduleId); - logSifModuleAction("load-buffer-emulated", emulated.moduleId, moduleTag, 1u); - setReturnS32(ctx, emulated.moduleId); - return; - } - - // Profile mode keeps the existing deterministic synthetic IDs. - const int32_t moduleId = trackSifModuleLoad(moduleTag); - if (moduleId <= 0) + if (!runtime) { setReturnS32(ctx, -1); return; } - uint32_t refs = 0; + const auto loaded = runtime->loadIopModuleBuffer(bufferAddr, arguments.empty() ? nullptr : arguments.data(), static_cast(arguments.size())); + if (!loaded.handled || loaded.moduleId <= 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; - } + setReturnS32(ctx, -1); + return; } - logSifModuleAction("load-buffer", moduleId, moduleTag, refs); - setReturnS32(ctx, moduleId); + + trackSifModuleLoadExternal(moduleTag, loaded.moduleId); + logSifModuleAction("load-buffer-emulated", loaded.moduleId, moduleTag, 1u); + setReturnS32(ctx, loaded.moduleId); } void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId) diff --git a/ps2xRuntime/src/lib/Kernel/Syscalls/System.h b/ps2xRuntime/src/lib/Kernel/Syscalls/System.h index 3bfb232..c9b9991 100644 --- a/ps2xRuntime/src/lib/Kernel/Syscalls/System.h +++ b/ps2xRuntime/src/lib/Kernel/Syscalls/System.h @@ -16,7 +16,6 @@ namespace ps2_syscalls void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void SetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void GetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); diff --git a/ps2xRuntime/src/lib/gs/gs_cpu_backend.cpp b/ps2xRuntime/src/lib/gs/gs_cpu_backend.cpp index 9c39ae2..03e625d 100644 --- a/ps2xRuntime/src/lib/gs/gs_cpu_backend.cpp +++ b/ps2xRuntime/src/lib/gs/gs_cpu_backend.cpp @@ -281,41 +281,40 @@ namespace return (index & ~0x18u) | ((index & 0x08u) << 1u) | ((index & 0x10u) >> 1u); } - // TODO: clut cache - uint32_t resolveClutIndex(uint8_t index, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm) + bool isFourBitIndexedPsm(uint8_t psm) { - uint32_t clutIndex = static_cast(index); + return psm == GS_PSM_T4 || psm == GS_PSM_T4HL || psm == GS_PSM_T4HH; + } - // CSM2 addresses the source directly through TEXCLUT. CSA is required - // to be zero there, so it must not offset the source coordinates. - if (csm != 0u) - return (sourcePsm == GS_PSM_T4 || - sourcePsm == GS_PSM_T4HH || - sourcePsm == GS_PSM_T4HL) - ? (clutIndex & 0x0Fu) - : clutIndex; + bool isEightBitIndexedPsm(uint8_t psm) + { + return psm == GS_PSM_T8 || psm == GS_PSM_T8H; + } - const bool is16BitClut = cpsm == GS_PSM_CT16 || cpsm == GS_PSM_CT16S; - const uint32_t csaMask = is16BitClut ? 0x1Fu : 0x0Fu; - const uint32_t clutIndexMask = is16BitClut ? 0x1FFu : 0x0FFu; - const uint32_t clutBase = (static_cast(csa) & csaMask) << 4u; - - switch (sourcePsm) + uint32_t texturePageIndex(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y) + { + switch (psm & 0x3Fu) { - case GS_PSM_T4: - case GS_PSM_T4HH: - case GS_PSM_T4HL: - clutIndex = clutBase + (clutIndex & 0x0Fu); - break; - case GS_PSM_T8: + case GS_PSM_CT32: + case GS_PSM_CT24: + case GS_PSM_Z32: + case GS_PSM_Z24: case GS_PSM_T8H: - clutIndex = clutBase + clutIndex; - break; + case GS_PSM_T4HL: + case GS_PSM_T4HH: + return static_cast(GSMem::PixelStorageTraits::PageId(base, bw, x, y)); + case GS_PSM_CT16: + case GS_PSM_CT16S: + case GS_PSM_Z16: + case GS_PSM_Z16S: + return static_cast(GSMem::PixelStorageTraits::PageId(base, bw, x, y)); + case GS_PSM_T8: + return static_cast(GSMem::PixelStorageTraits::PageId(base, bw, x, y)); + case GS_PSM_T4: + return static_cast(GSMem::PixelStorageTraits::PageId(base, bw, x, y)); default: - return clutIndex; + return UINT32_MAX; } - - return swizzleClutIndexCSM1(clutIndex & clutIndexMask); } uint8_t lerpChannel(uint8_t c00, uint8_t c10, uint8_t c01, uint8_t c11, float fx, float fy) @@ -544,6 +543,7 @@ void GSCpuBackend::Initialize(uint8_t *vram, uint32_t vramSize) std::lock_guard lock(m_mutex); m_vram = vram; m_vramSize = vramSize; + m_texturePageBuffer.resize(vramSize); ResetUnlocked(); } @@ -555,6 +555,9 @@ void GSCpuBackend::Reset() void GSCpuBackend::ResetUnlocked() { + m_clut.fill(0u); + m_clutCbp.fill(0u); + m_texturePageIndex = UINT32_MAX; m_transfer = {}; m_transfer.direction = 3u; m_transferState = {}; @@ -571,6 +574,92 @@ void GSCpuBackend::Submit(const GSPrimitiveBatch &batch) DrawPrimitive(batch); } +void GSCpuBackend::LoadClut(const GSTex0Reg &tex0, const GSTexClutReg &texclut) +{ + std::lock_guard lock(m_mutex); + if (!m_vram || (!isFourBitIndexedPsm(tex0.psm) && !isEightBitIndexedPsm(tex0.psm))) + return; + + switch (tex0.cld) + { + case 0u: + case 6u: + case 7u: + return; + case 1u: + break; + case 2u: + m_clutCbp[0] = tex0.cbp; + break; + case 3u: + m_clutCbp[1] = tex0.cbp; + break; + case 4u: + if (m_clutCbp[0] == tex0.cbp) + return; + m_clutCbp[0] = tex0.cbp; + break; + case 5u: + if (m_clutCbp[1] == tex0.cbp) + return; + m_clutCbp[1] = tex0.cbp; + break; + default: + return; + } + + LoadClutUnlocked(tex0, texclut); +} + +void GSCpuBackend::LoadClutUnlocked(const GSTex0Reg &tex0, const GSTexClutReg &texclut) +{ + const bool fourBit = isFourBitIndexedPsm(tex0.psm); + const bool sixteenBit = tex0.cpsm == GS_PSM_CT16 || tex0.cpsm == GS_PSM_CT16S; + const bool thirtyTwoBit = tex0.cpsm == GS_PSM_CT32 || tex0.cpsm == GS_PSM_CT24; + if (!sixteenBit && !thirtyTwoBit) + return; + + const uint32_t entryCount = fourBit ? 16u : 256u; + const uint32_t csaMask = sixteenBit ? 0x1Fu : 0x0Fu; + const uint32_t destinationBase = (static_cast(tex0.csa) & csaMask) << 4u; + + const bool loadCsm1Suffix = tex0.csm == 0u && thirtyTwoBit && !fourBit; + const uint32_t firstEntry = loadCsm1Suffix ? destinationBase : 0u; + + for (uint32_t entry = firstEntry; entry < entryCount; ++entry) + { + uint32_t sourceX = 0u; + uint32_t sourceY = 0u; + uint32_t sourceWidth = 1u; + + if (tex0.csm == 0u) + { + const uint32_t sourceIndex = swizzleClutIndexCSM1(entry); + sourceX = sourceIndex & 0x0Fu; + sourceY = sourceIndex >> 4u; + } + else + { + sourceWidth = texclut.cbw != 0u ? static_cast(texclut.cbw) : 1u; + sourceX = (static_cast(texclut.cou) << 4u) + entry; + sourceY = static_cast(texclut.cov); + } + + const uint32_t raw = ReadTextureVramUnlocked(tex0.cpsm, tex0.cbp, sourceWidth, sourceX, sourceY); + const uint32_t destination = (loadCsm1Suffix ? entry : destinationBase + entry) & + (sixteenBit ? 0x1FFu : 0x0FFu); + if (sixteenBit) + { + m_clut[destination] = static_cast(raw); + } + else + { + m_clut[destination] = static_cast(raw & 0xFFFFu); + m_clut[destination + 256u] = static_cast(raw >> 16u); + } + } +} + void GSCpuBackend::Flush() { // CPU backend is immediate. GPU backends may submit command buffers here. @@ -578,8 +667,8 @@ void GSCpuBackend::Flush() void GSCpuBackend::TextureFlush() { - // CPU texture reads are coherent with local memory. Future cached/GPU - // backends use this boundary to invalidate texture views. + std::lock_guard lock(m_mutex); + m_texturePageIndex = UINT32_MAX; } void GSCpuBackend::Sync(GSSyncReason) @@ -600,6 +689,27 @@ uint32_t GSCpuBackend::ReadVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw return m_readVramFuncs[psm & 0x3Fu](m_vram, base, bw, x, y); } +uint32_t GSCpuBackend::ReadTextureVramUnlocked(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y) +{ + if (!m_vram) + return 0u; + + const uint32_t pageCount = m_vramSize / static_cast(GSMem::GS_PAGE_SIZE); + uint32_t page = texturePageIndex(psm, base, bw, x, y); + if (page == UINT32_MAX || pageCount == 0u || m_texturePageBuffer.size() < m_vramSize) + return ReadVramUnlocked(psm, base, bw, x, y); + + page %= pageCount; + if (m_texturePageIndex != page) + { + const size_t pageOffset = static_cast(page) * GSMem::GS_PAGE_SIZE; + std::memcpy(m_texturePageBuffer.data() + pageOffset, m_vram + pageOffset, GSMem::GS_PAGE_SIZE); + m_texturePageIndex = page; + } + + return m_readVramFuncs[psm & 0x3Fu](m_texturePageBuffer.data(), base, bw, x, y); +} + void GSCpuBackend::WriteVram(uint32_t psm, uint32_t base, uint32_t bw, uint32_t x, uint32_t y, uint32_t value) { std::lock_guard lock(m_mutex); @@ -941,27 +1051,40 @@ void GSCpuBackend::WritePixel(const GSDrawState &state, int x, int y, int z, uin uint32_t GSCpuBackend::LookupCLUT(const GSDrawState &state, uint8_t index, - uint32_t cbp, uint8_t cpsm, uint8_t csm, uint8_t csa, uint8_t sourcePsm) { - const uint32_t clutIndex = resolveClutIndex(index, cpsm, csm, csa, sourcePsm); - const uint32_t clutWidth = (state.texclut.cbw != 0u) ? static_cast(state.texclut.cbw) : 1u; - const uint32_t clutX = static_cast(state.texclut.cou) + (clutIndex & 0x0Fu); - const uint32_t clutY = static_cast(state.texclut.cov) + (clutIndex >> 4); + const bool sixteenBit = cpsm == GS_PSM_CT16 || cpsm == GS_PSM_CT16S; + const uint32_t csaMask = sixteenBit ? 0x1Fu : 0x0Fu; + const uint32_t clutBase = (static_cast(csa) & csaMask) << 4u; + const uint32_t sourceIndex = isFourBitIndexedPsm(sourcePsm) + ? (static_cast(index) & 0x0Fu) + : static_cast(index); + + uint32_t clutIndex = (clutBase + sourceIndex) & (sixteenBit ? 0x1FFu : 0x0FFu); + if (!sixteenBit && csm == 0u && isEightBitIndexedPsm(sourcePsm)) + { + const uint32_t block = std::min((sourceIndex & 0xF0u) + clutBase, 240u); + clutIndex = block + (sourceIndex & 0x0Fu); + } switch (cpsm) { case GS_PSM_CT32: - return applyTexa(state.texa, cpsm, GSMem::ReadCT32(m_vram, cbp, clutWidth, clutX, clutY)); + { + const uint32_t raw = static_cast(m_clut[clutIndex]) | (static_cast(m_clut[clutIndex + 256u]) << 16u); + return applyTexa(state.texa, cpsm, raw); + } case GS_PSM_CT24: - return applyTexa(state.texa, cpsm, GSMem::ReadCT24(m_vram, cbp, clutWidth, clutX, clutY)); + { + const uint32_t raw = static_cast(m_clut[clutIndex]) | (static_cast(m_clut[clutIndex + 256u]) << 16u); + return applyTexa(state.texa, cpsm, raw & 0x00FFFFFFu); + } case GS_PSM_CT16: - return applyTexa(state.texa, cpsm, Rgba5551ToRgba8888(GSMem::ReadCT16(m_vram, cbp, clutWidth, clutX, clutY))); case GS_PSM_CT16S: - return applyTexa(state.texa, cpsm, Rgba5551ToRgba8888(GSMem::ReadCT16S(m_vram, cbp, clutWidth, clutX, clutY))); + return applyTexa(state.texa, cpsm, Rgba5551ToRgba8888(m_clut[clutIndex])); default: break; } @@ -1002,7 +1125,7 @@ uint32_t GSCpuBackend::SampleTexture(const GSDrawState &state, float s, float t, sampleU = wrapTextureCoordinate(sampleU, texW, wrapU, minU, maxU); sampleV = wrapTextureCoordinate(sampleV, texH, wrapV, minV, maxV); - u32 out = ReadVramUnlocked(tex.psm, tex.tbp0, tex.tbw, sampleU, sampleV); + u32 out = ReadTextureVramUnlocked(tex.psm, tex.tbp0, tex.tbw, sampleU, sampleV); switch (tex.psm) { @@ -1021,7 +1144,7 @@ uint32_t GSCpuBackend::SampleTexture(const GSDrawState &state, float s, float t, case GS_PSM_T4: case GS_PSM_T4HL: case GS_PSM_T4HH: - return LookupCLUT(state, static_cast(out), tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm); + return LookupCLUT(state, static_cast(out), tex.cpsm, tex.csm, tex.csa, tex.psm); } return 0xFFFF00FFu; diff --git a/ps2xRuntime/src/lib/gs/gs_frontend.cpp b/ps2xRuntime/src/lib/gs/gs_frontend.cpp index fd5a90d..5658928 100644 --- a/ps2xRuntime/src/lib/gs/gs_frontend.cpp +++ b/ps2xRuntime/src/lib/gs/gs_frontend.cpp @@ -1239,6 +1239,7 @@ void GS::writeRegisterUnlocked(uint8_t regAddr, uint64_t value) t.csm = static_cast((value >> 55) & 0x1); t.csa = static_cast((value >> 56) & 0x1F); t.cld = static_cast((value >> 61) & 0x7); + m_backend->LoadClut(t, m_texclut); break; } case GS_REG_CLAMP_1: @@ -1269,6 +1270,7 @@ void GS::writeRegisterUnlocked(uint8_t regAddr, uint64_t value) t.csm = static_cast((value >> 55) & 0x1); t.csa = static_cast((value >> 56) & 0x1F); t.cld = static_cast((value >> 61) & 0x7); + m_backend->LoadClut(t, m_texclut); break; } case GS_REG_XYOFFSET_1: diff --git a/ps2xRuntime/src/lib/ps2_debug_panel.cpp b/ps2xRuntime/src/lib/ps2_debug_panel.cpp index cf0b439..6419444 100644 --- a/ps2xRuntime/src/lib/ps2_debug_panel.cpp +++ b/ps2xRuntime/src/lib/ps2_debug_panel.cpp @@ -81,7 +81,7 @@ namespace { for (const ps2x::iop::DebugService &service : snapshot.services) { - if (std::find(service.sids.begin(), service.sids.end(), sid) != + if (service.active && std::find(service.sids.begin(), service.sids.end(), sid) != service.sids.end()) { return service.name; @@ -699,13 +699,48 @@ namespace void drawCpuTab(PS2Runtime &runtime, bool showRegisters) { - const uint32_t pc = runtime.m_debugPc.load(std::memory_order_relaxed); - const uint32_t ra = runtime.m_debugRa.load(std::memory_order_relaxed); - const uint32_t sp = runtime.m_debugSp.load(std::memory_order_relaxed); - const uint32_t gp = runtime.m_debugGp.load(std::memory_order_relaxed); + const bool executingGuest = runtime.eeScheduler().isExecutingGuest(); + const EeKernelSnapshot schedulerSnapshot = runtime.eeScheduler().snapshot(); + const EeThreadSnapshot *selectedThread = nullptr; + if (!executingGuest) + { + const auto running = std::find_if(schedulerSnapshot.threads.begin(), + schedulerSnapshot.threads.end(), + [&](const EeThreadSnapshot &thread) + { return thread.id == schedulerSnapshot.runningThreadId; }); + if (running != schedulerSnapshot.threads.end()) + { + selectedThread = &*running; + } + else + { + const auto blocked = std::find_if(schedulerSnapshot.threads.begin(), + schedulerSnapshot.threads.end(), + [](const EeThreadSnapshot &thread) + { return thread.status != EeThreadStatus::Dormant; }); + if (blocked != schedulerSnapshot.threads.end()) + { + selectedThread = &*blocked; + } + else if (!schedulerSnapshot.threads.empty()) + { + selectedThread = &schedulerSnapshot.threads.front(); + } + } + } + + const uint32_t pc = selectedThread ? selectedThread->pc : runtime.m_debugPc.load(std::memory_order_relaxed); + const uint32_t ra = selectedThread ? selectedThread->ra : runtime.m_debugRa.load(std::memory_order_relaxed); + const uint32_t sp = selectedThread ? selectedThread->sp : runtime.m_debugSp.load(std::memory_order_relaxed); + const uint32_t gp = selectedThread ? selectedThread->contextGp : runtime.m_debugGp.load(std::memory_order_relaxed); ImGui::Text("Runtime: %s", runtime.isStopRequested() ? "stop requested" : "running"); - ImGui::Text("EE executor: %s", runtime.eeScheduler().isExecutingGuest() ? "guest" : "scheduler"); + ImGui::Text("EE executor: %s", executingGuest ? "guest" : "scheduler"); + if (selectedThread) + { + ImGui::SameLine(); + ImGui::TextDisabled("(thread %d: %s/%s)", selectedThread->id, threadStatusName(selectedThread->status), waitTypeName(selectedThread->waitReason)); + } ImGui::Separator(); textHex32("PC", pc); ImGui::SameLine(); @@ -771,19 +806,23 @@ namespace static_cast(snapshot.eeCycle), static_cast(snapshot.sliceEndCycle), static_cast(snapshot.nextEventCycle)); - if (ImGui::BeginTable("threads", 11, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY, ImVec2(0, 320))) + if (ImGui::BeginTable("threads", 15, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY, ImVec2(0, 320))) { ImGui::TableSetupColumn("ID"); ImGui::TableSetupColumn("Status"); ImGui::TableSetupColumn("Wait"); ImGui::TableSetupColumn("WaitId"); ImGui::TableSetupColumn("PC"); + ImGui::TableSetupColumn("RA"); + ImGui::TableSetupColumn("SP"); + ImGui::TableSetupColumn("Ctx GP"); ImGui::TableSetupColumn("Entry"); ImGui::TableSetupColumn("Stack"); ImGui::TableSetupColumn("GP"); ImGui::TableSetupColumn("Prio"); ImGui::TableSetupColumn("Wake"); ImGui::TableSetupColumn("Susp"); + ImGui::TableSetupColumn("Inv"); ImGui::TableHeadersRow(); for (const EeThreadSnapshot &row : snapshot.threads) { @@ -799,6 +838,12 @@ namespace ImGui::TableNextColumn(); ImGui::Text("0x%08X", row.pc); ImGui::TableNextColumn(); + ImGui::Text("0x%08X", row.ra); + ImGui::TableNextColumn(); + ImGui::Text("0x%08X", row.sp); + ImGui::TableNextColumn(); + ImGui::Text("0x%08X", row.contextGp); + ImGui::TableNextColumn(); ImGui::Text("0x%08X", row.entry); ImGui::TableNextColumn(); ImGui::Text("0x%08X/%u", row.stack, row.stackSize); @@ -810,6 +855,8 @@ namespace ImGui::Text("%u", row.wakeupCount); ImGui::TableNextColumn(); ImGui::Text("%d", row.suspendCount); + ImGui::TableNextColumn(); + ImGui::Text("%u", row.invocationDepth); } ImGui::EndTable(); } @@ -1028,14 +1075,11 @@ namespace ? "builtin" : iopSnapshot.activeProvider.c_str()); - if (ImGui::BeginTable("iop_hle_services", - 5, - ImGuiTableFlags_Borders | - ImGuiTableFlags_RowBg | - ImGuiTableFlags_Resizable)) + if (ImGui::BeginTable("iop_hle_services", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) { ImGui::TableSetupColumn("Service"); ImGui::TableSetupColumn("Layer"); + ImGui::TableSetupColumn("Active"); ImGui::TableSetupColumn("SID"); ImGui::TableSetupColumn("EE server"); ImGui::TableSetupColumn("Metrics"); @@ -1050,6 +1094,8 @@ namespace ImGui::TableNextColumn(); ImGui::TextUnformatted(service.profileSpecific ? "profile" : "core"); ImGui::TableNextColumn(); + ImGui::TextUnformatted(service.active ? "yes" : "no"); + ImGui::TableNextColumn(); ImGui::TextUnformatted("-"); ImGui::TableNextColumn(); ImGui::TextUnformatted("-"); @@ -1066,6 +1112,8 @@ namespace ImGui::TableNextColumn(); ImGui::TextUnformatted(service.profileSpecific ? "profile" : "core"); ImGui::TableNextColumn(); + ImGui::TextUnformatted(service.active ? "yes" : "no"); + ImGui::TableNextColumn(); ImGui::Text("0x%08X", sid); ImGui::TableNextColumn(); ImGui::TextUnformatted(hasServer(sid) ? "yes" : "no"); @@ -1761,27 +1809,25 @@ namespace struct FdRow { int fd = 0; - FILE *file = nullptr; + std::string device; + std::string path; }; std::vector fds; + for (const PS2VfsDescriptorInfo &descriptor : runtime.vfs().descriptors()) { - std::lock_guard lock(g_fd_mutex); - fds.reserve(g_fileDescriptors.size()); - for (const auto &[fd, file] : g_fileDescriptors) - { - fds.push_back(FdRow{fd, file}); - } + fds.push_back({descriptor.descriptor, descriptor.device, descriptor.path}); } std::sort(fds.begin(), fds.end(), [](const FdRow &a, const FdRow &b) { return a.fd < b.fd; }); ImGui::SeparatorText("FileIO descriptors"); - ImGui::Text("Open host FILE* descriptors: %zu", fds.size()); - if (ImGui::BeginTable("fileio_fds", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable, ImVec2(0, 90))) + ImGui::Text("Open VFS descriptors: %zu", fds.size()); + if (ImGui::BeginTable("fileio_fds", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable, ImVec2(0, 90))) { ImGui::TableSetupColumn("FD"); - ImGui::TableSetupColumn("FILE*"); + ImGui::TableSetupColumn("Device"); + ImGui::TableSetupColumn("Path"); ImGui::TableHeadersRow(); for (const FdRow &row : fds) { @@ -1789,7 +1835,9 @@ namespace ImGui::TableNextColumn(); ImGui::Text("%d", row.fd); ImGui::TableNextColumn(); - ImGui::Text("%p", static_cast(row.file)); + ImGui::TextUnformatted(row.device.c_str()); + ImGui::TableNextColumn(); + ImGui::TextUnformatted(row.path.c_str()); } ImGui::EndTable(); } diff --git a/ps2xRuntime/src/lib/ps2_iop_host.cpp b/ps2xRuntime/src/lib/ps2_iop_host.cpp index 8709ed1..8d46e03 100644 --- a/ps2xRuntime/src/lib/ps2_iop_host.cpp +++ b/ps2xRuntime/src/lib/ps2_iop_host.cpp @@ -135,11 +135,6 @@ bool PS2IopHostAdapter::readGuest(uint32_t address, void *destination, size_t si { return false; } - if (ps2_stubs::isSifIopHeapAddress(address)) - { - return ps2_stubs::readSifIopHeap(address, destination, size); - } - uint8_t *source = nullptr; if (!guestRange(address, size, source)) { @@ -158,11 +153,6 @@ bool PS2IopHostAdapter::writeGuest(uint32_t address, const void *source, size_t { return false; } - if (ps2_stubs::isSifIopHeapAddress(address)) - { - return ps2_stubs::writeSifIopHeap(address, source, size); - } - uint8_t *destination = nullptr; if (!guestRange(address, size, destination)) { @@ -179,11 +169,6 @@ bool PS2IopHostAdapter::writeGuest(uint32_t address, const void *source, size_t bool PS2IopHostAdapter::zeroGuest(uint32_t address, size_t size) { - if (ps2_stubs::isSifIopHeapAddress(address)) - { - return ps2_stubs::zeroSifIopHeap(address, size); - } - uint8_t *destination = nullptr; if (!guestRange(address, size, destination)) { @@ -200,12 +185,6 @@ bool PS2IopHostAdapter::zeroGuest(uint32_t address, size_t size) bool PS2IopHostAdapter::normalizeGuestAddress(uint32_t address, uint32_t &normalized) const { - if (ps2_stubs::isSifIopHeapAddress(address)) - { - normalized = address; - return ps2_stubs::isSifIopHeapRange(address, 0u); - } - bool scratchpad = false; if (!ps2ResolveGuestPointer(address, normalized, scratchpad) || scratchpad) { @@ -215,6 +194,32 @@ bool PS2IopHostAdapter::normalizeGuestAddress(uint32_t address, uint32_t &normal return true; } +bool PS2IopHostAdapter::readIopMemory(uint32_t address, void *destination, size_t size) const +{ + return m_runtime.readIopMemory(address, destination, size); +} + +bool PS2IopHostAdapter::writeIopMemory(uint32_t address, const void *source, size_t size) +{ + return m_runtime.writeIopMemory(address, source, size); +} + +bool PS2IopHostAdapter::zeroIopMemory(uint32_t address, size_t size) +{ + return m_runtime.zeroIopMemory(address, size); +} + +bool PS2IopHostAdapter::normalizeIopAddress(uint32_t address, uint32_t &normalized) const +{ + if (!m_runtime.isIopMemoryRange(address, 0u)) + { + normalized = 0u; + return false; + } + normalized = address & 0x1FFFFFFFu; + return true; +} + uint32_t PS2IopHostAdapter::allocateIopHandle(ps2x::iop::IopHandleKind kind) { uint8_t *const rdram = m_activeRdram @@ -283,7 +288,12 @@ std::string PS2IopHostAdapter::hostPath(ps2x::iop::HostPathKind kind) const std::string PS2IopHostAdapter::translateGuestPath(std::string_view path) const { - return translatePs2Path(std::string(path).c_str()); + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + const PS2VfsMounts mounts{paths.hostRoot, paths.cdRoot, paths.mcRoot}; + std::filesystem::path hostPath; + if (!m_runtime.vfs().resolveHostPath(path, mounts, hostPath)) + return {}; + return hostPath.string(); } uint64_t PS2IopHostAdapter::openHostFile(std::string_view path) @@ -493,6 +503,20 @@ bool PS2IopHostAdapter::invokeGuestFunction(uint64_t callToken, return false; } +bool PS2IopHostAdapter::sendSifCommand(uint32_t commandId, + const void *packet, + size_t packetSize) +{ + uint8_t *const rdram = m_activeRdram + ? m_activeRdram + : m_runtime.memory().getRDRAM(); + return ps2_stubs::dispatchSifCommand(rdram, + &m_runtime, + commandId, + packet, + packetSize); +} + void PS2IopHostAdapter::log(ps2x::iop::LogLevel level, std::string_view message) { const char *prefix = "[ps2xIOP]"; diff --git a/ps2xRuntime/src/lib/ps2_iop_host.h b/ps2xRuntime/src/lib/ps2_iop_host.h index 48c5b22..89d8576 100644 --- a/ps2xRuntime/src/lib/ps2_iop_host.h +++ b/ps2xRuntime/src/lib/ps2_iop_host.h @@ -48,6 +48,10 @@ public: bool writeGuest(uint32_t address, const void *source, size_t size) override; bool zeroGuest(uint32_t address, size_t size) override; bool normalizeGuestAddress(uint32_t address, uint32_t &normalized) const override; + bool readIopMemory(uint32_t address, void *destination, size_t size) const override; + bool writeIopMemory(uint32_t address, const void *source, size_t size) override; + bool zeroIopMemory(uint32_t address, size_t size) override; + bool normalizeIopAddress(uint32_t address, uint32_t &normalized) const override; uint32_t allocateIopHandle(ps2x::iop::IopHandleKind kind) override; uint32_t allocateGuest(uint32_t size, uint32_t alignment) override; void freeGuest(uint32_t address) override; @@ -78,6 +82,7 @@ public: uint32_t a2, uint32_t a3, uint32_t *resultAddress) override; + bool sendSifCommand(uint32_t commandId, const void *packet, size_t packetSize) override; void log(ps2x::iop::LogLevel level, std::string_view message) override; diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index 7cb2ba4..37b6a4d 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -411,6 +411,12 @@ uint32_t PS2Memory::advanceEeTimers(uint64_t eeCycles) noexcept return 0u; } + constexpr uint32_t kGifStat = 0x10003020u; + constexpr uint32_t kGifFqcMask = 0x1F000000u; + auto gifStatIt = m_ioRegisters.find(kGifStat); + if (gifStatIt != m_ioRegisters.end()) + gifStatIt->second &= ~kGifFqcMask; + uint32_t interruptMask = 0u; for (size_t index = 0; index < m_eeTimers.size(); ++index) { @@ -1064,6 +1070,21 @@ void PS2Memory::write128(uint32_t address, __m128i value) const bool scratch = isScratchpad(address); uint32_t physAddr = translateAddress(address); + if (!scratch && physAddr == 0x10004000u) // VIF0_FIFO + { + alignas(16) uint8_t fifoData[16]; + _mm_storeu_si128(reinterpret_cast<__m128i *>(fifoData), value); + processVIF0Data(fifoData, sizeof(fifoData)); + return; + } + if (!scratch && physAddr == 0x10005000u) // VIF1_FIFO + { + alignas(16) uint8_t fifoData[16]; + _mm_storeu_si128(reinterpret_cast<__m128i *>(fifoData), value); + processVIF1Data(fifoData, sizeof(fifoData)); + return; + } + if (scratch) { inRange(physAddr, sizeof(__m128i), PS2_SCRATCHPAD_SIZE, "write128 scratchpad", address); @@ -1117,7 +1138,7 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) case kEeTimerModeOffset: { const uint32_t previousMode = timer.mode; - const uint32_t status = (previousMode & kEeTimerModeStatusMask) &~(value & kEeTimerModeStatusMask); + const uint32_t status = (previousMode & kEeTimerModeStatusMask) & ~(value & kEeTimerModeStatusMask); timer.mode = (value & kEeTimerModeConfigMask) | status; if (((previousMode ^ timer.mode) & (kEeTimerModeClksMask | kEeTimerModeCue)) != 0u) { @@ -1206,9 +1227,13 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) case 0x10003C10u: // VIF1_FBRST if (value & 0x1u) // RST { + const bool wasPath3Masked = m_path3Masked; std::memset(&vif1_regs, 0, sizeof(vif1_regs)); m_vif1PendingPath2ImageQwc = 0u; m_vif1PendingPath2DirectHl = false; + m_path3Masked = false; + if (wasPath3Masked) + flushMaskedPath3Packets(); } if (value & 0x8u) // STC { @@ -1281,8 +1306,12 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) const uint32_t qwc = m_ioRegisters[channelBase + 0x20]; m_dmaStartCount.fetch_add(1, std::memory_order_relaxed); - if ((channelBase == 0x1000A000u || channelBase == 0x10009000u || channelBase == 0x10008000u) && - (m_gsVRAM || channelBase == 0x10008000u)) + if (tryProcessScratchpadDma(channelBase, value)) + { + return true; + } + + if ((channelBase == 0x1000A000u || channelBase == 0x10009000u || channelBase == 0x10008000u) && (m_gsVRAM || channelBase == 0x10008000u)) { auto enqueueTransfer = [&](uint32_t srcAddr, uint32_t qwCount) { @@ -1353,7 +1382,7 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) } }; - auto appendCompactVif1TagData = [&](uint32_t localTagAddr, uint32_t qwCount) + auto appendVifTagData = [&](uint32_t localTagAddr) { uint32_t tagPhys = 0u; const bool tagScratch = isScratchpad(localTagAddr); @@ -1364,11 +1393,15 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) if (tagPhys + 16u > localMax) return; - // VIF packet helpers embed 8 bytes of VIF stream in the DMAtag's upper half. + // CHCR.TTE sends the DMAtag's upper 64 bits to the channel before + // the tag payload. VIF chains use those bytes for two VIFcodes. chainBuf.insert(chainBuf.end(), localBase + tagPhys + 8u, localBase + tagPhys + 16u); - appendData(localTagAddr + 16u, qwCount); }; + const bool isVifChannel = + channelBase == 0x10009000u || channelBase == 0x10008000u; + const bool transferTagData = isVifChannel && (chcr & 0x40u) != 0u; + int tagsProcessed = 0; uint32_t lastTagUpper = (chcr >> 16) & 0xFFFFu; @@ -1477,19 +1510,11 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) break; } - const bool compactVifLocalTag = - (channelBase == 0x10009000u || channelBase == 0x10008000u) && - (id == 1u || id == 2u || id == 5u || id == 6u || id == 7u); - if (compactVifLocalTag) - appendCompactVif1TagData(currentTagAddr, 0u); + if (transferTagData) + appendVifTagData(currentTagAddr); if (hasPayload) - { - if (compactVifLocalTag) - appendData(currentTagAddr + 16u, tagQwc); - else - appendData(dataAddr, tagQwc); - } + appendData(dataAddr, tagQwc); if (irq && tieEnabled) endChain = true; if (endChain) @@ -1559,9 +1584,101 @@ bool PS2Memory::writeIORegister(uint32_t address, uint32_t value) return false; } +bool PS2Memory::tryProcessScratchpadDma(uint32_t channelBase, uint32_t chcr) +{ + static constexpr uint32_t kSprFromChannel = 0x1000D000u; + static constexpr uint32_t kSprToChannel = 0x1000D400u; + if (channelBase != kSprFromChannel && channelBase != kSprToChannel) + return false; + + const uint32_t mode = (chcr >> 2u) & 0x3u; + if (mode != 0u) + return false; + + const uint32_t qwc = m_ioRegisters[channelBase + 0x20u] & 0xFFFFu; + const uint32_t byteCount = qwc * 16u; + const uint32_t originalMadr = m_ioRegisters[channelBase + 0x10u] & 0x7FFFFFF0u; + const uint32_t originalSadr = m_ioRegisters[channelBase + 0x80u] & 0x3FF0u; + + uint32_t mainOffset = 0u; + try + { + mainOffset = translateAddress(originalMadr); + } + catch (const std::exception &) + { + return false; + } + + if (mainOffset > PS2_RAM_SIZE || byteCount > PS2_RAM_SIZE - mainOffset) + return false; + + const bool fromScratchpad = channelBase == kSprFromChannel; + uint32_t scratchOffset = originalSadr; + uint32_t bytesLeft = byteCount; + uint32_t copied = 0u; + while (bytesLeft != 0u) + { + const uint32_t scratchChunk = PS2_SCRATCHPAD_SIZE - scratchOffset; + const uint32_t chunk = std::min(bytesLeft, scratchChunk); + if (fromScratchpad) + { + std::memcpy(m_rdram + mainOffset + copied, m_scratchpad + scratchOffset, chunk); + markModified(mainOffset + copied, chunk); + } + else + { + std::memcpy(m_scratchpad + scratchOffset, m_rdram + mainOffset + copied, chunk); + } + + copied += chunk; + bytesLeft -= chunk; + scratchOffset = (scratchOffset + chunk) & (PS2_SCRATCHPAD_SIZE - 1u); + } + + m_ioRegisters[channelBase + 0x10u] = (originalMadr + byteCount) & 0x7FFFFFF0u; + m_ioRegisters[channelBase + 0x20u] = 0u; + m_ioRegisters[channelBase + 0x80u] = (originalSadr + byteCount) & 0x3FF0u; + completeDmacChannel(channelBase, fromScratchpad ? 8u : 9u); + return true; +} + +void PS2Memory::completeDmacChannel(uint32_t channelBase, uint32_t cause) +{ + static constexpr uint32_t kDStat = 0x1000E010u; + m_ioRegisters[channelBase] &= ~0x100u; + + uint32_t dstat = m_ioRegisters.count(kDStat) ? m_ioRegisters[kDStat] : 0u; + dstat |= 1u << cause; + const uint32_t status = dstat & 0x3FFu; + const uint32_t mask = (dstat >> 16u) & 0x3FFu; + if ((status & mask) != 0u) + dstat |= 1u << 31u; + else + dstat &= ~(1u << 31u); + m_ioRegisters[kDStat] = dstat; + queueCompletedDmacCause(cause); +} + void PS2Memory::processPendingTransfers() { const bool hadGif = !m_pendingGifTransfers.empty(); + uint32_t observedGifQwc = 0u; + for (const auto &transfer : m_pendingGifTransfers) + { + const uint64_t transferQwc = !transfer.chainData.empty() + ? (transfer.chainData.size() / 16u) + : transfer.qwc; + observedGifQwc = static_cast(std::min(16u, static_cast(observedGifQwc) + transferQwc)); + } + if (observedGifQwc != 0u) + { + constexpr uint32_t kGifStat = 0x10003020u; + constexpr uint32_t kGifFqcMask = 0x1F000000u; + uint32_t &gifStat = m_ioRegisters[kGifStat]; + gifStat = (gifStat & ~kGifFqcMask) | (observedGifQwc << 24u); + } + for (size_t idx = 0; idx < m_pendingGifTransfers.size(); ++idx) { auto &p = m_pendingGifTransfers[idx]; @@ -2221,6 +2338,23 @@ uint32_t PS2Memory::readIORegister(uint32_t address) } return val; } + + if (address == 0x10003020u) // GIF_STAT + { + uint32_t stat = m_ioRegisters.count(address) ? m_ioRegisters[address] : 0u; + const uint32_t mode = m_ioRegisters.count(0x10003010u) ? m_ioRegisters[0x10003010u] : 0u; + const uint32_t ctrl = m_ioRegisters.count(0x10003000u) ? m_ioRegisters[0x10003000u] : 0u; + + // M3R and IMT mirror GIF_MODE, PSE mirrors GIF_CTRL, and M3P is the + // effective PATH3 mask controlled by the VIF1 MSKPATH3 command. + stat = (stat & ~0xFu) | + (mode & 0x1u) | + (m_path3Masked ? 0x2u : 0u) | + (mode & 0x4u) | + (ctrl & 0x8u); + return stat; + } + if (address >= 0x10000000 && address < 0x10010000) { if (address >= 0x10008000 && address < 0x1000F000) diff --git a/ps2xRuntime/src/lib/ps2_rom_device.cpp b/ps2xRuntime/src/lib/ps2_rom_device.cpp new file mode 100644 index 0000000..a72105b --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_rom_device.cpp @@ -0,0 +1,178 @@ +#include "runtime/ps2_rom_device.h" + +#include +#include +#include + +namespace +{ + std::mutex &profileMutex() + { + static std::mutex mutex; + return mutex; + } + + std::vector &profileRegistry() + { + static std::vector profiles; + return profiles; + } + + bool equalsIgnoreCaseAscii(std::string_view lhs, std::string_view rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (size_t i = 0; i < lhs.size(); ++i) + { + const auto left = static_cast(lhs[i]); + const auto right = static_cast(rhs[i]); + if (std::tolower(left) != std::tolower(right)) + return false; + } + return true; + } + + int matchSpecificity(const ps2x::iop::GameMatcher &matcher, const ps2x::iop::GameIdentity &identity) + { + int specificity = 0; + if (!matcher.elfName.empty()) + { + if (!equalsIgnoreCaseAscii(matcher.elfName, identity.elfName)) + return -1; + ++specificity; + } + if (matcher.entryPoint != 0u) + { + if (matcher.entryPoint != identity.entryPoint) + return -1; + ++specificity; + } + if (matcher.crc32 != 0u) + { + if (matcher.crc32 != identity.crc32) + return -1; + ++specificity; + } + return specificity; + } +} + +PS2RomDevice::PS2RomDevice() +{ + mountBaseProfile(); +} + +void PS2RomDevice::registerProfile(PS2RomProfile profile) +{ + std::lock_guard lock(profileMutex()); + profileRegistry().push_back(std::move(profile)); +} + +bool PS2RomDevice::configure(const ps2x::iop::GameIdentity &identity, std::string *error) +{ + m_files.clear(); + m_activeProfile.clear(); + m_activeProvider.clear(); + mountBaseProfile(); + + std::vector profiles; + { + std::lock_guard lock(profileMutex()); + profiles = profileRegistry(); + } + + const PS2RomProfile *selected = nullptr; + const PS2RomProfile *tie = nullptr; + int selectedSpecificity = -1; + for (const PS2RomProfile &profile : profiles) + { + const int specificity = matchSpecificity(profile.matcher, identity); + if (specificity < 0) + continue; + if (specificity > selectedSpecificity) + { + selected = &profile; + tie = nullptr; + selectedSpecificity = specificity; + } + else if (specificity == selectedSpecificity && selected) + { + tie = &profile; + } + } + + if (selected && tie) + { + if (error) + { + *error = "ambiguous ROM profiles '" + selected->provider + ":" + selected->id + "' and '" + tie->provider + ":" + tie->id + "'"; + } + return false; + } + + if (selected) + { + mountFiles(selected->files); + m_activeProfile = selected->id; + m_activeProvider = selected->provider; + } + return true; +} + +bool PS2RomDevice::readFile(std::string_view ps2Path, std::vector &bytes) const +{ + const auto file = m_files.find(normalizePath(ps2Path)); + if (file == m_files.end()) + { + bytes.clear(); + return false; + } + bytes = file->second; + return true; +} + +bool PS2RomDevice::fileSize(std::string_view ps2Path, uint64_t &size) const +{ + const auto file = m_files.find(normalizePath(ps2Path)); + if (file == m_files.end()) + { + size = 0u; + return false; + } + size = file->second.size(); + return true; +} + +bool PS2RomDevice::contains(std::string_view ps2Path) const +{ + return m_files.contains(normalizePath(ps2Path)); +} + +std::string PS2RomDevice::normalizePath(std::string_view path) +{ + constexpr std::string_view prefix = "rom0:"; + if (path.size() >= prefix.size() && equalsIgnoreCaseAscii(path.substr(0, prefix.size()), prefix)) + path.remove_prefix(prefix.size()); + while (!path.empty() && (path.front() == '/' || path.front() == '\\')) + path.remove_prefix(1u); + + std::string normalized(path); + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](unsigned char value) + { return static_cast(std::tolower(value)); }); + return normalized; +} + +void PS2RomDevice::mountBaseProfile() +{ + // TODO expose this to cmake + constexpr char romVersion[] = "0200AC20040614"; + static_assert(sizeof(romVersion) - 1u == 14u); + m_files[normalizePath("ROMVER")] = std::vector(romVersion, romVersion + 14u); +} + +void PS2RomDevice::mountFiles(const std::unordered_map> &files) +{ + for (const auto &[path, bytes] : files) + m_files[normalizePath(path)] = bytes; +} diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index 4fd4bab..742110a 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -630,6 +630,36 @@ ps2x::iop::DebugSnapshot PS2Runtime::iopDebugSnapshot() const return m_iopSubsystem->debugSnapshot(); } +uint32_t PS2Runtime::allocateIopMemory(uint32_t size, uint32_t alignment) +{ + return m_iopSubsystem ? m_iopSubsystem->allocateMemory(size, alignment) : 0u; +} + +bool PS2Runtime::freeIopMemory(uint32_t address) +{ + return m_iopSubsystem && m_iopSubsystem->freeMemory(address); +} + +bool PS2Runtime::readIopMemory(uint32_t address, void *destination, size_t size) const +{ + return m_iopSubsystem && m_iopSubsystem->readMemory(address, destination, size); +} + +bool PS2Runtime::writeIopMemory(uint32_t address, const void *source, size_t size) +{ + return m_iopSubsystem && m_iopSubsystem->writeMemory(address, source, size); +} + +bool PS2Runtime::zeroIopMemory(uint32_t address, size_t size) +{ + return m_iopSubsystem && m_iopSubsystem->zeroMemory(address, size); +} + +bool PS2Runtime::isIopMemoryRange(uint32_t address, size_t size) const +{ + return m_iopSubsystem && m_iopSubsystem->isMemoryRange(address, size); +} + bool PS2Runtime::syncCoreSubsystems() { uint8_t *const rdram = m_memory.getRDRAM(); @@ -983,6 +1013,12 @@ bool PS2Runtime::loadELF(const std::string &elfPath) identity.elfName = module.name; identity.entryPoint = m_cpuContext.pc; identity.crc32 = elfCrc32; + std::string romError; + if (!m_romDevice.configure(identity, &romError)) + { + std::cerr << "[ROM0] failed to configure profile: " << romError << std::endl; + return false; + } std::string iopError; if (!m_iopSubsystem->configure(identity, &iopError)) { @@ -1374,7 +1410,8 @@ bool PS2Runtime::dispatchGuestBranch(uint8_t *rdram, if (policy == MissingFunctionPolicy::ContinueToTarget) { ctx->pc = targetPc; - return true; + // if you need the app to keep open to open debug pannel change this to false + return false; } return false; @@ -2403,6 +2440,7 @@ void PS2Runtime::run() << " gsw=" << curGs << " vif=" << curVif << std::endl); + } }); uint32_t presentWidth = FB_WIDTH; diff --git a/ps2xRuntime/src/lib/ps2_vfs.cpp b/ps2xRuntime/src/lib/ps2_vfs.cpp new file mode 100644 index 0000000..aff25e6 --- /dev/null +++ b/ps2xRuntime/src/lib/ps2_vfs.cpp @@ -0,0 +1,312 @@ +#include "runtime/ps2_vfs.h" + +#include "runtime/ps2_memory.h" +#include "runtime/ps2_rom_device.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + class HostOpenFile final : public IPS2OpenFile + { + public: + explicit HostOpenFile(FILE *file) : m_file(file) {} + ~HostOpenFile() override + { + if (m_file) + std::fclose(m_file); + } + + int64_t read(void *destination, size_t size) override + { + if ((!destination && size != 0u) || !m_file) + return -1; + const size_t bytes = std::fread(destination, 1u, size, m_file); + if (bytes < size && std::ferror(m_file)) + { + std::clearerr(m_file); + return -1; + } + return static_cast(bytes); + } + + int64_t write(const void *source, size_t size) override + { + if ((!source && size != 0u) || !m_file) + return -1; + const size_t bytes = std::fwrite(source, 1u, size, m_file); + if (bytes < size && std::ferror(m_file)) + { + std::clearerr(m_file); + return -1; + } + return static_cast(bytes); + } + + int64_t seek(int64_t offset, int whence) override + { + if (!m_file || offset < std::numeric_limits::min() || offset > std::numeric_limits::max() || std::fseek(m_file, static_cast(offset), whence) != 0) + return -1; + const long position = std::ftell(m_file); + return position < 0 ? -1 : static_cast(position); + } + + private: + FILE *m_file = nullptr; + }; + + class MemoryOpenFile final : public IPS2OpenFile + { + public: + explicit MemoryOpenFile(std::vector bytes) : m_bytes(std::move(bytes)) {} + + int64_t read(void *destination, size_t size) override + { + if (!destination && size != 0u) + return -1; + const size_t available = m_position < m_bytes.size() ? m_bytes.size() - m_position : 0u; + const size_t count = std::min(size, available); + if (count != 0u) + std::memcpy(destination, m_bytes.data() + m_position, count); + m_position += count; + return static_cast(count); + } + + int64_t write(const void *, size_t) override + { + return -1; + } + + int64_t seek(int64_t offset, int whence) override + { + int64_t base = 0; + if (whence == SEEK_CUR) + base = static_cast(m_position); + else if (whence == SEEK_END) + base = static_cast(m_bytes.size()); + else if (whence != SEEK_SET) + return -1; + + const int64_t position = base + offset; + if (position < 0 || static_cast(position) > m_bytes.size()) + return -1; + m_position = static_cast(position); + return position; + } + + private: + std::vector m_bytes; + size_t m_position = 0u; + }; + + const char *hostMode(uint32_t flags) + { + const bool read = (flags & PS2_FIO_O_RDONLY) != 0u || (flags & PS2_FIO_O_RDWR) == PS2_FIO_O_RDWR; + const bool write = (flags & PS2_FIO_O_WRONLY) != 0u || (flags & PS2_FIO_O_RDWR) == PS2_FIO_O_RDWR; + const bool create = (flags & PS2_FIO_O_CREAT) != 0u; + const bool truncate = (flags & PS2_FIO_O_TRUNC) != 0u; + const bool append = (flags & PS2_FIO_O_APPEND) != 0u; + + if (read && write) + { + if (truncate) + return "w+b"; + if (append) + return "a+b"; + return "r+b"; + } + if (write) + { + if (append) + return "ab"; + if (create || truncate) + return "wb"; + return "r+b"; + } + return "rb"; + } + + bool safeRelativePath(std::string_view suffix, std::filesystem::path &relative) + { + relative = std::filesystem::path(suffix).lexically_normal(); + if (relative.is_absolute() || relative.has_root_name()) + return false; + for (const auto &part : relative) + { + if (part == "..") + return false; + } + return true; + } + + std::time_t toTimeT(std::filesystem::file_time_type value) + { + const auto systemValue = 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(systemValue); + } +} + +PS2Vfs::~PS2Vfs() = default; + +int32_t PS2Vfs::open(std::string_view path, uint32_t flags, const PS2VfsMounts &mounts, const PS2RomDevice &rom) +{ + const ps2x::iop::ParsedPs2Path parsed = ps2x::iop::parsePs2Path(path); + if (!parsed) + return -1; + + std::unique_ptr file; + if (parsed.device == ps2x::iop::Ps2PathDevice::Rom0) + { + const uint32_t access = flags & PS2_FIO_O_RDWR; + if (access != PS2_FIO_O_RDONLY || (flags & (PS2_FIO_O_CREAT | PS2_FIO_O_TRUNC)) != 0u) + return -1; + std::vector bytes; + if (!rom.readFile(parsed.path, bytes)) + return -1; + file = std::make_unique(std::move(bytes)); + } + else + { + std::filesystem::path hostPath; + if (!resolveHostPath(path, mounts, hostPath)) + return -1; + + std::error_code existsError; + const bool exists = std::filesystem::exists(hostPath, existsError); + if (existsError || (exists && (flags & (PS2_FIO_O_CREAT | PS2_FIO_O_EXCL)) == (PS2_FIO_O_CREAT | PS2_FIO_O_EXCL))) + { + return -1; + } + + FILE *stream = std::fopen(hostPath.string().c_str(), hostMode(flags)); + const uint32_t access = flags & PS2_FIO_O_RDWR; + if (!stream && !exists && (flags & PS2_FIO_O_CREAT) != 0u && + access == PS2_FIO_O_RDWR && + (flags & (PS2_FIO_O_TRUNC | PS2_FIO_O_APPEND)) == 0u) + { + stream = std::fopen(hostPath.string().c_str(), "w+b"); + } + if (!stream) + return -1; + file = std::make_unique(stream); + } + + std::lock_guard lock(m_mutex); + if (m_nextDescriptor < 3) + m_nextDescriptor = 3; + const int32_t descriptor = m_nextDescriptor++; + m_descriptors.emplace(descriptor, OpenDescriptor{std::move(file), parsed.deviceName, std::string(path)}); + return descriptor; +} + +int32_t PS2Vfs::close(int32_t descriptor) +{ + std::lock_guard lock(m_mutex); + return m_descriptors.erase(descriptor) == 1u ? 0 : -1; +} + +int64_t PS2Vfs::read(int32_t descriptor, void *destination, size_t size) +{ + std::lock_guard lock(m_mutex); + const auto found = m_descriptors.find(descriptor); + return found == m_descriptors.end() ? -1 : found->second.file->read(destination, size); +} + +int64_t PS2Vfs::write(int32_t descriptor, const void *source, size_t size) +{ + std::lock_guard lock(m_mutex); + const auto found = m_descriptors.find(descriptor); + return found == m_descriptors.end() ? -1 : found->second.file->write(source, size); +} + +int64_t PS2Vfs::seek(int32_t descriptor, int64_t offset, int whence) +{ + std::lock_guard lock(m_mutex); + const auto found = m_descriptors.find(descriptor); + return found == m_descriptors.end() ? -1 : found->second.file->seek(offset, whence); +} + +bool PS2Vfs::stat(std::string_view path, const PS2VfsMounts &mounts, const PS2RomDevice &rom, PS2VfsStat &result) const +{ + result = {}; + const ps2x::iop::ParsedPs2Path parsed = ps2x::iop::parsePs2Path(path); + if (!parsed) + return false; + if (parsed.device == ps2x::iop::Ps2PathDevice::Rom0) + { + if (!rom.fileSize(parsed.path, result.size)) + return false; + result.readOnly = true; + return true; + } + + std::filesystem::path hostPath; + if (!resolveHostPath(path, mounts, hostPath)) + return false; + std::error_code error; + const auto status = std::filesystem::status(hostPath, error); + if (error || !std::filesystem::exists(status)) + return false; + result.directory = std::filesystem::is_directory(status); + if (!result.directory) + { + result.size = std::filesystem::file_size(hostPath, error); + if (error) + return false; + } + const auto modified = std::filesystem::last_write_time(hostPath, error); + if (!error) + result.created = result.accessed = result.modified = toTimeT(modified); + result.readOnly = (status.permissions() & std::filesystem::perms::owner_write) == std::filesystem::perms::none; + return true; +} + +bool PS2Vfs::resolveHostPath(std::string_view path, const PS2VfsMounts &mounts, std::filesystem::path &result) const +{ + result.clear(); + const ps2x::iop::ParsedPs2Path parsed = ps2x::iop::parsePs2Path(path); + if (!parsed || parsed.device == ps2x::iop::Ps2PathDevice::Rom0) + return false; + if (parsed.device == ps2x::iop::Ps2PathDevice::NativeHost) + { + result = std::filesystem::path(parsed.path).lexically_normal(); + return !result.empty(); + } + + std::filesystem::path base; + switch (parsed.device) + { + case ps2x::iop::Ps2PathDevice::Host: + base = mounts.hostRoot; + break; + case ps2x::iop::Ps2PathDevice::Cdrom: + base = mounts.cdRoot; + break; + case ps2x::iop::Ps2PathDevice::MemoryCard0: + base = mounts.memoryCard0Root; + break; + default: + return false; + } + std::filesystem::path relative; + if (base.empty() || !safeRelativePath(parsed.path, relative)) + return false; + result = (base / relative).lexically_normal(); + return true; +} + +std::vector PS2Vfs::descriptors() const +{ + std::lock_guard lock(m_mutex); + std::vector result; + result.reserve(m_descriptors.size()); + for (const auto &[descriptor, entry] : m_descriptors) + result.push_back({descriptor, entry.device, entry.path}); + return result; +} diff --git a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp index 05fc764..b02c7ca 100644 --- a/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp +++ b/ps2xRuntime/src/lib/ps2_vif1_interpreter.cpp @@ -30,18 +30,54 @@ namespace { constexpr uint8_t kGifFmtImage = 2u; - uint32_t gifImageQwcFromTag(const uint8_t *data, uint32_t sizeBytes) + uint32_t pendingGifImageQwc(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; + uint32_t offset = 0u; + while (offset + 16u <= sizeBytes) + { + uint64_t tagLo = 0u; + std::memcpy(&tagLo, data + offset, sizeof(tagLo)); + offset += 16u; - return static_cast(tagLo & 0x7FFFu); + 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; + + uint64_t payloadBytes = 0u; + if (flg == 0u) // PACKED + { + payloadBytes = static_cast(nloop) * nreg * 16ull; + } + else if (flg == 1u) // REGLIST, padded to a quadword + { + payloadBytes = static_cast(nloop) * nreg * 8ull; + payloadBytes = (payloadBytes + 15ull) & ~15ull; + } + else if (flg == kGifFmtImage) + { + payloadBytes = static_cast(nloop) * 16ull; + const uint64_t availableBytes = sizeBytes - offset; + if (payloadBytes > availableBytes) + { + return static_cast((payloadBytes - availableBytes) / 16ull); + } + } + else + { + return 0u; + } + + if (payloadBytes > static_cast(sizeBytes - offset)) + return 0u; + offset += static_cast(payloadBytes); + } + + return 0u; } } @@ -284,11 +320,7 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) (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); + submitGifPacket(GifPathId::Path2, imagePacket.data(), static_cast(imagePacket.size()), true, m_vif1PendingPath2DirectHl); pos += chunkQw * 16u; m_vif1PendingPath2ImageQwc -= chunkQw; @@ -372,9 +404,6 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) { uint32_t startPC = (uint32_t)imm * 8u; - // Values visible to the VU program for this MSCAL. - // DobieStation semantics: ITOP = ITOPS; TOP = current TOPS; - // then TOPS/DBF are prepared for the next buffer. const uint32_t runTop = vif1_regs.tops & 0x3FFu; const uint32_t runItop = vif1_regs.itops & 0x3FFu; vif1_regs.top = runTop; @@ -438,8 +467,6 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes) else if (opcode == VIF_MPG) { uint32_t destAddr = (uint32_t)imm * 8u; - // VIF MPG semantics: NUM==0 means 256 instructions (2048 bytes). - // MPG payload is instruction-packed and should not be QW-aligned. const uint32_t instructionCount = (num == 0u) ? 256u : static_cast(num); const uint32_t mpgBytes = instructionCount * 8u; if (m_vu1Code && destAddr < PS2_VU1_CODE_SIZE && mpgBytes > 0) @@ -473,15 +500,11 @@ 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 pendingImageQw = pendingGifImageQwc(data + pos, qwCount * 16u); + if (pendingImageQw != 0u) { - const uint32_t inlineImageQw = (qwCount > 0u) ? (qwCount - 1u) : 0u; - if (imageQw > inlineImageQw) - { - m_vif1PendingPath2ImageQwc = imageQw - inlineImageQw; - m_vif1PendingPath2DirectHl = directHl; - } + m_vif1PendingPath2ImageQwc = pendingImageQw; + m_vif1PendingPath2DirectHl = directHl; } }