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.
This commit is contained in:
Ran-j
2026-09-02 18:05:55 -03:00
parent 08dc217818
commit c8c86661fa
33 changed files with 1488 additions and 786 deletions
+4 -2
View File
@@ -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
-1
View File
@@ -120,7 +120,6 @@
X(SetOsdConfigParam2) \
X(EnableCache) \
X(DisableCache) \
X(GetRomName) \
X(SifLoadElfPart) \
X(sceSifLoadElf) \
X(sceSifLoadElfPart) \
+14
View File
@@ -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<ps2x::iop::IopSubsystem> 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;
-2
View File
@@ -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);
@@ -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;
@@ -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;
@@ -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<ReadVramFunc, kPsmHandlerCount> m_readVramFuncs{};
std::array<WriteVramFunc, kPsmHandlerCount> m_writeVramFuncs{};
std::array<uint16_t, 512> m_clut{};
std::array<uint32_t, 2> m_clutCbp{};
std::vector<uint8_t> m_texturePageBuffer;
uint32_t m_texturePageIndex = UINT32_MAX;
GSTransferCommand m_transfer{};
GSTransferSnapshot m_transferState{};
+2
View File
@@ -449,6 +449,8 @@ public:
};
std::array<EeTimer, 4> m_eeTimers{};
bool tryProcessScratchpadDma(uint32_t channelBase, uint32_t chcr);
void completeDmacChannel(uint32_t channelBase, uint32_t cause);
void queueCompletedDmacCause(uint32_t cause);
};
@@ -0,0 +1,43 @@
#pragma once
#include "ps2x/iop/iop_types.h"
#include <cstdint>
#include <mutex>
#include <span>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
struct PS2RomProfile
{
std::string id;
std::string provider = "application";
ps2x::iop::GameMatcher matcher;
std::unordered_map<std::string, std::vector<uint8_t>> 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<uint8_t> &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<std::string, std::vector<uint8_t>> &files);
std::unordered_map<std::string, std::vector<uint8_t>> m_files;
std::string m_activeProfile;
std::string m_activeProvider;
};
+82
View File
@@ -0,0 +1,82 @@
#pragma once
#include "ps2x/iop/ps2_path.h"
#include <cstddef>
#include <cstdint>
#include <ctime>
#include <filesystem>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
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<PS2VfsDescriptorInfo> descriptors() const;
private:
struct OpenDescriptor
{
std::unique_ptr<IPS2OpenFile> file;
std::string device;
std::string path;
};
mutable std::mutex m_mutex;
std::unordered_map<int32_t, OpenDescriptor> m_descriptors;
int32_t m_nextDescriptor = 3;
};
+45 -6
View File
@@ -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<uint32_t>(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());
}
}
@@ -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<uint32_t>(tagLo & 0xFFFFu)
<< " id=0x" << static_cast<uint32_t>((tagLo >> 28u) & 0x7u)
<< " irq=0x" << static_cast<uint32_t>((tagLo >> 31u) & 0x1u)
<< " addr=0x" << static_cast<uint32_t>((tagLo >> 32u) & 0x7FFFFFFFu)
<< " w2=0x" << w2
<< " w3=0x" << w3
<< std::dec << std::endl);
<< " tagQwc=0x" << static_cast<uint32_t>(tagLo & 0xFFFFu)
<< " id=0x" << static_cast<uint32_t>((tagLo >> 28u) & 0x7u)
<< " irq=0x" << static_cast<uint32_t>((tagLo >> 31u) & 0x1u)
<< " addr=0x" << static_cast<uint32_t>((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));
+145 -293
View File
@@ -3,10 +3,11 @@
#include "../Syscalls/RPC.h"
#include "../../ps2_iop_transport.h"
#include "runtime/ps2_address.h"
#include "runtime/ee_scheduler.h"
#include <algorithm>
#include <cstring>
#include <limits>
#include <map>
#include <vector>
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<uint8_t> 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<uint32_t, uint32_t> g_sifRegs;
std::unordered_map<uint32_t, uint32_t> g_sifSregs;
std::unordered_map<uint32_t, uint32_t> g_sifCmdHandlers;
std::map<uint32_t, uint32_t> g_sifHeapAllocations;
std::array<uint8_t, kIopHeapLimit - kIopHeapBase> g_sifHeapStorage{};
struct SifCmdHandler
{
uint32_t function = 0u;
uint32_t argument = 0u;
};
std::unordered_map<uint32_t, SifCmdHandler> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<size_t>(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<uint64_t>(it->first) + it->second;
const uint64_t rangeEnd = static_cast<uint64_t>(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<uint8_t *>(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<uint8_t> 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<uint64_t>(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<std::mutex> 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<const uint8_t *>(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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<uint32_t>(packetSize), 16u);
if (packetAddress == 0u)
return false;
uint8_t *const first = getMemPtr(rdram, packetAddress);
uint8_t *const last = getMemPtr(rdram, packetAddress + static_cast<uint32_t>(packetSize - 1u));
if (!first || !last || last < first || static_cast<size_t>(last - first) != packetSize - 1u)
{
runtime->guestFree(packetAddress);
return false;
}
ps2TraceGuestRangeWrite(rdram, packetAddress, static_cast<uint32_t>(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<std::mutex> 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<uint8_t> 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<uint32_t>(xfer.size),
});
ps2x::iop::SifTransferKind::SetDma,
ps2x::iop::SifTransferPhase::BeforeCopy,
xfer.src,
xfer.dest,
static_cast<uint32_t>(xfer.size),
});
}
if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, static_cast<uint32_t>(xfer.size)))
const uint32_t sizeBytes = static_cast<uint32_t>(xfer.size);
std::vector<uint8_t> 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<uint32_t>(xfer.size),
});
ps2x::iop::SifTransferKind::SetDma,
ps2x::iop::SifTransferPhase::AfterCopy,
xfer.src,
xfer.dest,
static_cast<uint32_t>(xfer.size),
});
}
}
}
+1 -8
View File
@@ -2,16 +2,9 @@
#include "ps2_stubs.h"
#include <cstddef>
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);
@@ -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 <iostream>
#include <algorithm>
#include <cctype>
@@ -253,6 +253,9 @@ namespace ps2_syscalls
case 0x64:
FlushCache(rdram, ctx, runtime);
return true;
case static_cast<uint32_t>(-0x68):
iFlushCache(rdram, ctx, runtime);
return true;
case 0x6E:
SetOsdConfigParam2(rdram, ctx, runtime);
return true;
+62 -162
View File
@@ -3,32 +3,10 @@
namespace ps2_syscalls
{
static int allocatePs2Fd(FILE *file)
static PS2VfsMounts currentVfsMounts()
{
if (!file)
return -1;
std::lock_guard<std::mutex> lock(g_fd_mutex);
int fd = g_nextFd++;
g_fileDescriptors[fd] = file;
return fd;
}
static FILE *getHostFile(int ps2Fd)
{
std::lock_guard<std::mutex> 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<std::mutex> 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<uint32_t>(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<std::mutex> 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<std::mutex> lock(g_sys_fd_mutex);
bytesRead = fread(hostBuf, 1, size, fp);
setReturnS32(ctx, -1);
return;
}
const size_t bytesRead = static_cast<size_t>(readResult);
if (bytesRead > 0)
{
ps2TraceGuestRangeWrite(rdram, bufAddr, static_cast<uint32_t>(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<std::mutex> 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<std::mutex> 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<int32_t>(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<long>(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<uint64_t>(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<uint32_t>(status.size & 0xFFFFFFFFu);
guest.hisize = static_cast<uint32_t>(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
}
}
@@ -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<std::mutex> 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)
@@ -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)
@@ -3,9 +3,6 @@
#include <cstddef>
#include <cstdint>
inline std::unordered_map<int, FILE *> 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;
+8 -26
View File
@@ -207,40 +207,22 @@ namespace ps2_syscalls
return;
}
const auto emulated = runtime->loadIopModule(modulePath, arguments.empty() ? nullptr : arguments.data(), static_cast<uint32_t>(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<uint32_t>(arguments.size()));
if (!loaded.handled || loaded.moduleId <= 0)
{
std::lock_guard<std::mutex> 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)
+9 -52
View File
@@ -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<char *>(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<uint32_t>(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<uint32_t>(arguments.size()));
if (!loaded.handled || loaded.moduleId <= 0)
{
std::lock_guard<std::mutex> 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)
@@ -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);
+164 -41
View File
@@ -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<uint32_t>(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<uint32_t>(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<uint32_t>(GSMem::PixelStorageTraits<GSMem::C32>::PageId(base, bw, x, y));
case GS_PSM_CT16:
case GS_PSM_CT16S:
case GS_PSM_Z16:
case GS_PSM_Z16S:
return static_cast<uint32_t>(GSMem::PixelStorageTraits<GSMem::C16>::PageId(base, bw, x, y));
case GS_PSM_T8:
return static_cast<uint32_t>(GSMem::PixelStorageTraits<GSMem::P8>::PageId(base, bw, x, y));
case GS_PSM_T4:
return static_cast<uint32_t>(GSMem::PixelStorageTraits<GSMem::P4>::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<std::mutex> 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<std::mutex> 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<uint32_t>(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<uint32_t>(texclut.cbw) : 1u;
sourceX = (static_cast<uint32_t>(texclut.cou) << 4u) + entry;
sourceY = static_cast<uint32_t>(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<uint16_t>(raw);
}
else
{
m_clut[destination] = static_cast<uint16_t>(raw & 0xFFFFu);
m_clut[destination + 256u] = static_cast<uint16_t>(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<std::mutex> 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<uint32_t>(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<size_t>(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<std::mutex> 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<uint32_t>(state.texclut.cbw) : 1u;
const uint32_t clutX = static_cast<uint32_t>(state.texclut.cou) + (clutIndex & 0x0Fu);
const uint32_t clutY = static_cast<uint32_t>(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<uint32_t>(csa) & csaMask) << 4u;
const uint32_t sourceIndex = isFourBitIndexedPsm(sourcePsm)
? (static_cast<uint32_t>(index) & 0x0Fu)
: static_cast<uint32_t>(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<uint32_t>(m_clut[clutIndex]) | (static_cast<uint32_t>(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<uint32_t>(m_clut[clutIndex]) | (static_cast<uint32_t>(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<u8>(out), tex.cbp, tex.cpsm, tex.csm, tex.csa, tex.psm);
return LookupCLUT(state, static_cast<u8>(out), tex.cpsm, tex.csm, tex.csa, tex.psm);
}
return 0xFFFF00FFu;
+2
View File
@@ -1239,6 +1239,7 @@ void GS::writeRegisterUnlocked(uint8_t regAddr, uint64_t value)
t.csm = static_cast<uint8_t>((value >> 55) & 0x1);
t.csa = static_cast<uint8_t>((value >> 56) & 0x1F);
t.cld = static_cast<uint8_t>((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<uint8_t>((value >> 55) & 0x1);
t.csa = static_cast<uint8_t>((value >> 56) & 0x1F);
t.cld = static_cast<uint8_t>((value >> 61) & 0x7);
m_backend->LoadClut(t, m_texclut);
break;
}
case GS_REG_XYOFFSET_1:
+71 -23
View File
@@ -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<unsigned long long>(snapshot.eeCycle),
static_cast<unsigned long long>(snapshot.sliceEndCycle),
static_cast<unsigned long long>(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<FdRow> fds;
for (const PS2VfsDescriptorInfo &descriptor : runtime.vfs().descriptors())
{
std::lock_guard<std::mutex> 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<void *>(row.file));
ImGui::TextUnformatted(row.device.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(row.path.c_str());
}
ImGui::EndTable();
}
+46 -22
View File
@@ -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]";
+5
View File
@@ -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;
+151 -17
View File
@@ -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<uint32_t>(std::min<uint64_t>(16u, static_cast<uint64_t>(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)
+178
View File
@@ -0,0 +1,178 @@
#include "runtime/ps2_rom_device.h"
#include <algorithm>
#include <cctype>
#include <utility>
namespace
{
std::mutex &profileMutex()
{
static std::mutex mutex;
return mutex;
}
std::vector<PS2RomProfile> &profileRegistry()
{
static std::vector<PS2RomProfile> 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<unsigned char>(lhs[i]);
const auto right = static_cast<unsigned char>(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<std::mutex> 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<PS2RomProfile> profiles;
{
std::lock_guard<std::mutex> 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<uint8_t> &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<char>(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<uint8_t>(romVersion, romVersion + 14u);
}
void PS2RomDevice::mountFiles(const std::unordered_map<std::string, std::vector<uint8_t>> &files)
{
for (const auto &[path, bytes] : files)
m_files[normalizePath(path)] = bytes;
}
+39 -1
View File
@@ -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;
+312
View File
@@ -0,0 +1,312 @@
#include "runtime/ps2_vfs.h"
#include "runtime/ps2_memory.h"
#include "runtime/ps2_rom_device.h"
#include <algorithm>
#include <chrono>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <limits>
#include <utility>
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<int64_t>(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<int64_t>(bytes);
}
int64_t seek(int64_t offset, int whence) override
{
if (!m_file || offset < std::numeric_limits<long>::min() || offset > std::numeric_limits<long>::max() || std::fseek(m_file, static_cast<long>(offset), whence) != 0)
return -1;
const long position = std::ftell(m_file);
return position < 0 ? -1 : static_cast<int64_t>(position);
}
private:
FILE *m_file = nullptr;
};
class MemoryOpenFile final : public IPS2OpenFile
{
public:
explicit MemoryOpenFile(std::vector<uint8_t> 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<int64_t>(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<int64_t>(m_position);
else if (whence == SEEK_END)
base = static_cast<int64_t>(m_bytes.size());
else if (whence != SEEK_SET)
return -1;
const int64_t position = base + offset;
if (position < 0 || static_cast<uint64_t>(position) > m_bytes.size())
return -1;
m_position = static_cast<size_t>(position);
return position;
}
private:
std::vector<uint8_t> 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<std::chrono::system_clock::duration>(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<IPS2OpenFile> 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<uint8_t> bytes;
if (!rom.readFile(parsed.path, bytes))
return -1;
file = std::make_unique<MemoryOpenFile>(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<HostOpenFile>(stream);
}
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<PS2VfsDescriptorInfo> PS2Vfs::descriptors() const
{
std::lock_guard<std::mutex> lock(m_mutex);
std::vector<PS2VfsDescriptorInfo> result;
result.reserve(m_descriptors.size());
for (const auto &[descriptor, entry] : m_descriptors)
result.push_back({descriptor, entry.device, entry.path});
return result;
}
+48 -25
View File
@@ -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<uint8_t>((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<uint32_t>(tagLo & 0x7FFFu);
const uint32_t nloop = static_cast<uint32_t>(tagLo & 0x7FFFu);
const uint8_t flg = static_cast<uint8_t>((tagLo >> 58) & 0x3u);
uint32_t nreg = static_cast<uint32_t>((tagLo >> 60) & 0xFu);
if (nreg == 0u)
nreg = 16u;
uint64_t payloadBytes = 0u;
if (flg == 0u) // PACKED
{
payloadBytes = static_cast<uint64_t>(nloop) * nreg * 16ull;
}
else if (flg == 1u) // REGLIST, padded to a quadword
{
payloadBytes = static_cast<uint64_t>(nloop) * nreg * 8ull;
payloadBytes = (payloadBytes + 15ull) & ~15ull;
}
else if (flg == kGifFmtImage)
{
payloadBytes = static_cast<uint64_t>(nloop) * 16ull;
const uint64_t availableBytes = sizeBytes - offset;
if (payloadBytes > availableBytes)
{
return static_cast<uint32_t>((payloadBytes - availableBytes) / 16ull);
}
}
else
{
return 0u;
}
if (payloadBytes > static_cast<uint64_t>(sizeBytes - offset))
return 0u;
offset += static_cast<uint32_t>(payloadBytes);
}
return 0u;
}
}
@@ -284,11 +320,7 @@ void PS2Memory::processVIF1Data(const uint8_t *data, uint32_t sizeBytes)
(static_cast<uint64_t>(kGifFmtImage) << 58);
std::memcpy(imagePacket.data(), &imageTag, sizeof(imageTag));
std::memcpy(imagePacket.data() + 16u, data + pos, static_cast<size_t>(chunkQw) * 16u);
submitGifPacket(GifPathId::Path2,
imagePacket.data(),
static_cast<uint32_t>(imagePacket.size()),
true,
m_vif1PendingPath2DirectHl);
submitGifPacket(GifPathId::Path2, imagePacket.data(), static_cast<uint32_t>(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<uint32_t>(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;
}
}