mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 16:59:35 -04:00
Feature/resident evil code veronica patch 1 (#60)
* feat: split runtime code in small files to be easy to develop * feat: split stubs in inl files * feat: function auto link function treat functions with underscore as same as without underscore * feat: remove underscore prefix from stubs * feat: thread and flags refactor * feat: propagate request stop feat: some elf validation on runtime * feat: remove Z7 compiler options feat: added a literal float case error on vu * fix: fix critical recompiler error on marking pc calls * feat: better system interrupt refactor: small refactor on thread system * feat: stubs implements for resident evil code veronica * feat: merge fileio
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
|
||||
namespace ps2recomp
|
||||
{
|
||||
@@ -34,6 +35,22 @@ namespace ps2recomp
|
||||
return ((address + 4) & 0xF0000000u) | (target << 2);
|
||||
}
|
||||
|
||||
static std::string formatFloatLiteral(float value)
|
||||
{
|
||||
if (!std::isfinite(value))
|
||||
{
|
||||
return (value < 0.0f) ? "-INFINITY" : "INFINITY";
|
||||
}
|
||||
|
||||
std::string literal = fmt::format("{:.9g}", value);
|
||||
if (literal.find_first_of(".eE") == std::string::npos)
|
||||
{
|
||||
literal += ".0";
|
||||
}
|
||||
literal += 'f';
|
||||
return literal;
|
||||
}
|
||||
|
||||
static std::string sanitizeIdentifierBody(const std::string &name)
|
||||
{
|
||||
std::string sanitized;
|
||||
@@ -2923,10 +2940,10 @@ namespace ps2recomp
|
||||
|
||||
return fmt::format("{{ __m128i src = _mm_castps_si128(ctx->vu0_vf[{}]); "
|
||||
"__m128 res = _mm_cvtepi32_ps(src); "
|
||||
"res = _mm_mul_ps(res, _mm_set1_ps({}f)); "
|
||||
"res = _mm_mul_ps(res, _mm_set1_ps({})); "
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, scale,
|
||||
vfs, formatFloatLiteral(scale),
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
@@ -2939,12 +2956,12 @@ namespace ps2recomp
|
||||
float scale = (shift == 0) ? 1.0f : static_cast<float>(1 << shift);
|
||||
|
||||
return fmt::format("{{ __m128 src = ctx->vu0_vf[{}]; "
|
||||
"src = _mm_mul_ps(src, _mm_set1_ps({}f)); "
|
||||
"src = _mm_mul_ps(src, _mm_set1_ps({})); "
|
||||
"__m128i res_i = _mm_cvttps_epi32(src); "
|
||||
"__m128 res = _mm_castsi128_ps(res_i); "
|
||||
"__m128i mask = _mm_set_epi32({}, {}, {}, {}); "
|
||||
"ctx->vu0_vf[{}] = _mm_blendv_ps(ctx->vu0_vf[{}], res, _mm_castsi128_ps(mask)); }}",
|
||||
vfs, scale,
|
||||
vfs, formatFloatLiteral(scale),
|
||||
(dest_mask & 0x8) ? -1 : 0, (dest_mask & 0x4) ? -1 : 0,
|
||||
(dest_mask & 0x2) ? -1 : 0, (dest_mask & 0x1) ? -1 : 0,
|
||||
inst.rt, inst.rt);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <cctype>
|
||||
#include <unordered_set>
|
||||
#include <optional>
|
||||
#include <limits>
|
||||
#include <limits>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -491,7 +491,9 @@ namespace ps2recomp
|
||||
std::string generatedName = m_codeGenerator->getFunctionName(function.start);
|
||||
std::stringstream stub;
|
||||
stub << "void " << generatedName
|
||||
<< "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) { ";
|
||||
<< "(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime) {\n"
|
||||
<< " const uint32_t __entryPc = ctx->pc;\n"
|
||||
<< " ";
|
||||
|
||||
if (function.isSkipped)
|
||||
{
|
||||
@@ -515,7 +517,12 @@ namespace ps2recomp
|
||||
}
|
||||
}
|
||||
|
||||
stub << "}";
|
||||
stub << "\n"
|
||||
<< " if (ctx->pc == __entryPc)\n"
|
||||
<< " {\n"
|
||||
<< " ctx->pc = getRegU32(ctx, 31);\n"
|
||||
<< " }\n"
|
||||
<< "}";
|
||||
m_generatedStubs[function.start] = stub.str();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ add_executable(ps2EntryRunner
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(ps2EntryRunner PRIVATE /FS /Z7)
|
||||
target_compile_options(ps2EntryRunner PRIVATE /FS)
|
||||
endif()
|
||||
|
||||
target_include_directories(ps2_runtime PUBLIC
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace ps2_syscalls
|
||||
|
||||
bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId);
|
||||
void notifyRuntimeStop();
|
||||
}
|
||||
|
||||
#endif // PS2_SYSCALLS_H
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
@@ -259,6 +260,12 @@ PS2Runtime::PS2Runtime()
|
||||
|
||||
PS2Runtime::~PS2Runtime()
|
||||
{
|
||||
requestStop();
|
||||
if (IsWindowReady())
|
||||
{
|
||||
CloseWindow();
|
||||
}
|
||||
|
||||
m_loadedModules.clear();
|
||||
|
||||
m_functionTable.clear();
|
||||
@@ -290,8 +297,21 @@ bool PS2Runtime::loadELF(const std::string &elfPath)
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfHeader header;
|
||||
file.read(reinterpret_cast<char *>(&header), sizeof(header));
|
||||
file.seekg(0, std::ios::end);
|
||||
const std::streamoff fileSize = file.tellg();
|
||||
if (fileSize < static_cast<std::streamoff>(sizeof(ElfHeader)))
|
||||
{
|
||||
std::cerr << "ELF file is too small: " << elfPath << std::endl;
|
||||
return false;
|
||||
}
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
ElfHeader header{};
|
||||
if (!file.read(reinterpret_cast<char *>(&header), sizeof(header)))
|
||||
{
|
||||
std::cerr << "Failed to read ELF header from: " << elfPath << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.magic != ELF_MAGIC)
|
||||
{
|
||||
@@ -299,70 +319,168 @@ bool PS2Runtime::loadELF(const std::string &elfPath)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.elf_class != 1u || header.endianness != 1u)
|
||||
{
|
||||
std::cerr << "Unsupported ELF format (expected 32-bit little-endian)." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.machine != EM_MIPS || header.type != ET_EXEC)
|
||||
{
|
||||
std::cerr << "Not a MIPS executable ELF file" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.phnum != 0u && header.phentsize < sizeof(ProgramHeader))
|
||||
{
|
||||
std::cerr << "Unsupported ELF program-header entry size: " << header.phentsize << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t programHeaderTableEnd =
|
||||
static_cast<uint64_t>(header.phoff) +
|
||||
static_cast<uint64_t>(header.phnum) * static_cast<uint64_t>(header.phentsize);
|
||||
if (programHeaderTableEnd > static_cast<uint64_t>(fileSize))
|
||||
{
|
||||
std::cerr << "ELF program-header table is out of range." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cpuContext.pc = header.entry;
|
||||
m_debugPc.store(m_cpuContext.pc, std::memory_order_relaxed);
|
||||
|
||||
uint32_t maxLoadedRdramEnd = kGuestHeapDefaultBase;
|
||||
uint32_t moduleBase = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t moduleEnd = 0u;
|
||||
bool loadedAnySegment = false;
|
||||
|
||||
for (uint16_t i = 0; i < header.phnum; i++)
|
||||
{
|
||||
ProgramHeader ph;
|
||||
file.seekg(header.phoff + i * header.phentsize);
|
||||
file.read(reinterpret_cast<char *>(&ph), sizeof(ph));
|
||||
|
||||
if (ph.type == PT_LOAD && ph.filesz > 0)
|
||||
const uint64_t phOffset =
|
||||
static_cast<uint64_t>(header.phoff) +
|
||||
static_cast<uint64_t>(i) * static_cast<uint64_t>(header.phentsize);
|
||||
if (phOffset + sizeof(ProgramHeader) > static_cast<uint64_t>(fileSize))
|
||||
{
|
||||
std::cout << "Loading segment: 0x" << std::hex << ph.vaddr
|
||||
<< " - 0x" << (ph.vaddr + ph.memsz)
|
||||
<< " (filesz: 0x" << ph.filesz
|
||||
<< ", memsz: 0x" << ph.memsz << ")"
|
||||
<< std::dec << std::endl;
|
||||
std::cerr << "ELF program header " << i << " is out of range." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate temporary buffer for the segment
|
||||
std::vector<uint8_t> buffer(ph.filesz);
|
||||
ProgramHeader ph{};
|
||||
file.seekg(static_cast<std::streamoff>(phOffset), std::ios::beg);
|
||||
if (!file.read(reinterpret_cast<char *>(&ph), sizeof(ph)))
|
||||
{
|
||||
std::cerr << "Failed to read ELF program header " << i << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read segment data
|
||||
file.seekg(ph.offset);
|
||||
file.read(reinterpret_cast<char *>(buffer.data()), ph.filesz);
|
||||
if (ph.type != PT_LOAD || ph.memsz == 0u)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy to memory
|
||||
uint32_t physAddr = m_memory.translateAddress(ph.vaddr);
|
||||
uint8_t *dest = nullptr;
|
||||
if (ph.vaddr >= PS2_SCRATCHPAD_BASE && ph.vaddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)
|
||||
if (ph.filesz > ph.memsz)
|
||||
{
|
||||
std::cerr << "ELF segment " << i << " has filesz > memsz." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t segmentFileEnd = static_cast<uint64_t>(ph.offset) + static_cast<uint64_t>(ph.filesz);
|
||||
if (segmentFileEnd > static_cast<uint64_t>(fileSize))
|
||||
{
|
||||
std::cerr << "ELF segment " << i << " exceeds file bounds." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool scratch =
|
||||
ph.vaddr >= PS2_SCRATCHPAD_BASE &&
|
||||
ph.vaddr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE);
|
||||
|
||||
uint32_t physAddr = 0u;
|
||||
try
|
||||
{
|
||||
physAddr = m_memory.translateAddress(ph.vaddr);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "Failed to translate ELF segment " << i
|
||||
<< " virtual address 0x" << std::hex << ph.vaddr
|
||||
<< std::dec << ": " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
const uint64_t regionSize = scratch ? static_cast<uint64_t>(PS2_SCRATCHPAD_SIZE)
|
||||
: static_cast<uint64_t>(PS2_RAM_SIZE);
|
||||
const uint64_t segmentMemEnd = static_cast<uint64_t>(physAddr) + static_cast<uint64_t>(ph.memsz);
|
||||
if (segmentMemEnd > regionSize)
|
||||
{
|
||||
std::cerr << "ELF segment " << i << " exceeds "
|
||||
<< (scratch ? "scratchpad" : "RDRAM")
|
||||
<< " bounds (vaddr=0x" << std::hex << ph.vaddr
|
||||
<< " memsz=0x" << ph.memsz << std::dec << ")." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *destBase = scratch ? m_memory.getScratchpad() : m_memory.getRDRAM();
|
||||
if (!destBase)
|
||||
{
|
||||
std::cerr << "ELF segment " << i << " has no destination memory backing." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *dest = destBase + physAddr;
|
||||
if (ph.filesz > 0u)
|
||||
{
|
||||
file.seekg(static_cast<std::streamoff>(ph.offset), std::ios::beg);
|
||||
if (!file.read(reinterpret_cast<char *>(dest), ph.filesz))
|
||||
{
|
||||
dest = m_memory.getScratchpad() + physAddr;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = m_memory.getRDRAM() + physAddr;
|
||||
}
|
||||
std::memcpy(dest, buffer.data(), ph.filesz);
|
||||
|
||||
if (ph.memsz > ph.filesz)
|
||||
{
|
||||
std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz);
|
||||
}
|
||||
|
||||
if (!(ph.vaddr >= PS2_SCRATCHPAD_BASE && ph.vaddr < PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE))
|
||||
{
|
||||
const uint64_t segmentEnd = static_cast<uint64_t>(physAddr) + static_cast<uint64_t>(ph.memsz);
|
||||
if (segmentEnd <= PS2_RAM_SIZE)
|
||||
{
|
||||
maxLoadedRdramEnd = std::max(maxLoadedRdramEnd, static_cast<uint32_t>(segmentEnd));
|
||||
}
|
||||
}
|
||||
|
||||
// Track executable regions for self-modifying code invalidation
|
||||
if (ph.flags & 0x1) // PF_X
|
||||
{
|
||||
m_memory.registerCodeRegion(ph.vaddr, ph.vaddr + ph.memsz);
|
||||
std::cerr << "Failed to read ELF segment " << i << " payload." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ph.memsz > ph.filesz)
|
||||
{
|
||||
std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz);
|
||||
}
|
||||
|
||||
std::cout << "Loading segment: 0x" << std::hex << ph.vaddr
|
||||
<< " - 0x" << (static_cast<uint64_t>(ph.vaddr) + static_cast<uint64_t>(ph.memsz))
|
||||
<< " (filesz: 0x" << ph.filesz
|
||||
<< ", memsz: 0x" << ph.memsz << ")"
|
||||
<< std::dec << std::endl;
|
||||
|
||||
if (!scratch)
|
||||
{
|
||||
maxLoadedRdramEnd = std::max(maxLoadedRdramEnd, static_cast<uint32_t>(segmentMemEnd));
|
||||
}
|
||||
|
||||
if (ph.flags & 0x1u) // PF_X
|
||||
{
|
||||
const uint64_t execEnd = static_cast<uint64_t>(ph.vaddr) + static_cast<uint64_t>(ph.memsz);
|
||||
if (execEnd <= std::numeric_limits<uint32_t>::max())
|
||||
{
|
||||
m_memory.registerCodeRegion(ph.vaddr, static_cast<uint32_t>(execEnd));
|
||||
}
|
||||
}
|
||||
|
||||
loadedAnySegment = true;
|
||||
moduleBase = std::min(moduleBase, ph.vaddr);
|
||||
const uint64_t segmentVirtualEnd = static_cast<uint64_t>(ph.vaddr) + static_cast<uint64_t>(ph.memsz);
|
||||
const uint32_t clampedVirtualEnd =
|
||||
(segmentVirtualEnd > std::numeric_limits<uint32_t>::max())
|
||||
? std::numeric_limits<uint32_t>::max()
|
||||
: static_cast<uint32_t>(segmentVirtualEnd);
|
||||
moduleEnd = std::max(moduleEnd, clampedVirtualEnd);
|
||||
}
|
||||
|
||||
if (!loadedAnySegment)
|
||||
{
|
||||
std::cerr << "ELF contains no loadable PT_LOAD segments." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxLoadedRdramEnd > PS2_RAM_SIZE)
|
||||
{
|
||||
maxLoadedRdramEnd = PS2_RAM_SIZE;
|
||||
}
|
||||
|
||||
const uint32_t paddedEnd = (maxLoadedRdramEnd > (PS2_RAM_SIZE - kGuestHeapSafetyPad))
|
||||
@@ -383,8 +501,8 @@ bool PS2Runtime::loadELF(const std::string &elfPath)
|
||||
|
||||
LoadedModule module;
|
||||
module.name = elfPath.substr(elfPath.find_last_of("/\\") + 1);
|
||||
module.baseAddress = 0x00100000; // Typical base address for PS2 executables
|
||||
module.size = 0; // Would need to calculate from segments
|
||||
module.baseAddress = (moduleBase == std::numeric_limits<uint32_t>::max()) ? 0x00100000u : moduleBase;
|
||||
module.size = (moduleEnd > module.baseAddress) ? static_cast<size_t>(moduleEnd - module.baseAddress) : 0u;
|
||||
module.active = true;
|
||||
|
||||
m_loadedModules.push_back(module);
|
||||
@@ -445,8 +563,6 @@ void PS2Runtime::configureIoPathsFromElf(const std::string &elfPath)
|
||||
paths.mcRoot = paths.elfDirectory / "mc0";
|
||||
}
|
||||
|
||||
paths.cdImage.clear();
|
||||
|
||||
setIoPaths(paths);
|
||||
}
|
||||
|
||||
@@ -1031,29 +1147,28 @@ uint32_t PS2Runtime::guestHeapEnd() const
|
||||
|
||||
void PS2Runtime::dispatchLoop(uint8_t *rdram, R5900Context *ctx)
|
||||
{
|
||||
uint32_t lastPc = 0;
|
||||
int stuckCount = 0;
|
||||
uint32_t lastPc = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t samePcCount = 0;
|
||||
constexpr uint32_t kSamePcYieldInterval = 0x4000u;
|
||||
|
||||
while (!isStopRequested())
|
||||
{
|
||||
const uint32_t pc = ctx->pc;
|
||||
|
||||
// this helps a lot but lets not forget to remove later
|
||||
if (pc == lastPc)
|
||||
{
|
||||
stuckCount++;
|
||||
if (stuckCount > 1000)
|
||||
++samePcCount;
|
||||
if ((samePcCount % kSamePcYieldInterval) == 0u)
|
||||
{
|
||||
std::cerr << "CPU Stuck at PC 0x" << std::hex << pc << ". PC not updating." << std::endl;
|
||||
requestStop();
|
||||
break;
|
||||
std::cout << "CPU is doing some work at PC 0x" << std::hex << pc << ". PC not updating." << std::endl;
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stuckCount = 0;
|
||||
samePcCount = 0;
|
||||
lastPc = pc;
|
||||
}
|
||||
lastPc = pc;
|
||||
|
||||
m_debugPc.store(pc, std::memory_order_relaxed);
|
||||
m_debugRa.store(static_cast<uint32_t>(_mm_extract_epi32(ctx->r[31], 0)), std::memory_order_relaxed);
|
||||
@@ -1206,7 +1321,11 @@ void PS2Runtime::Store128(uint8_t *rdram, R5900Context *ctx, uint32_t vaddr, __m
|
||||
|
||||
void PS2Runtime::requestStop()
|
||||
{
|
||||
m_stopRequested.store(true, std::memory_order_relaxed);
|
||||
const bool alreadyRequested = m_stopRequested.exchange(true, std::memory_order_relaxed);
|
||||
if (!alreadyRequested)
|
||||
{
|
||||
ps2_syscalls::notifyRuntimeStop();
|
||||
}
|
||||
}
|
||||
|
||||
bool PS2Runtime::isStopRequested() const
|
||||
@@ -1221,11 +1340,16 @@ void PS2Runtime::HandleIntegerOverflow(R5900Context *ctx)
|
||||
|
||||
void PS2Runtime::run()
|
||||
{
|
||||
m_stopRequested.store(false, std::memory_order_relaxed);
|
||||
m_cpuContext.r[4] = _mm_setzero_si128();
|
||||
m_cpuContext.r[5] = _mm_setzero_si128();
|
||||
m_cpuContext.r[29] = _mm_set_epi64x(0, static_cast<int64_t>(PS2_RAM_SIZE - 0x10u));
|
||||
m_debugPc.store(m_cpuContext.pc, std::memory_order_relaxed);
|
||||
m_debugRa.store(static_cast<uint32_t>(_mm_extract_epi32(m_cpuContext.r[31], 0)), std::memory_order_relaxed);
|
||||
m_debugSp.store(static_cast<uint32_t>(_mm_extract_epi32(m_cpuContext.r[29], 0)), std::memory_order_relaxed);
|
||||
m_debugGp.store(static_cast<uint32_t>(_mm_extract_epi32(m_cpuContext.r[28], 0)), std::memory_order_relaxed);
|
||||
|
||||
std::cout << "Starting execution at address 0x" << std::hex << m_debugPc.load(std::memory_order_relaxed) << std::dec << std::endl;
|
||||
std::cout << "Starting execution at address 0x" << std::hex << m_cpuContext.pc << std::dec << std::endl;
|
||||
|
||||
// A blank image to use as a framebuffer
|
||||
Image blank = GenImageColor(FB_WIDTH, FB_HEIGHT, BLANK);
|
||||
@@ -1233,6 +1357,7 @@ void PS2Runtime::run()
|
||||
UnloadImage(blank);
|
||||
|
||||
g_activeThreads.store(1, std::memory_order_relaxed);
|
||||
std::atomic<bool> gameThreadFinished{false};
|
||||
|
||||
std::thread gameThread([&]()
|
||||
{
|
||||
@@ -1248,10 +1373,15 @@ void PS2Runtime::run()
|
||||
{
|
||||
std::cerr << "Error during program execution: " << e.what() << std::endl;
|
||||
}
|
||||
g_activeThreads.fetch_sub(1, std::memory_order_relaxed); });
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "Error during program execution: unknown exception" << std::endl;
|
||||
}
|
||||
g_activeThreads.fetch_sub(1, std::memory_order_relaxed);
|
||||
gameThreadFinished.store(true, std::memory_order_release); });
|
||||
|
||||
uint64_t tick = 0;
|
||||
while (g_activeThreads.load(std::memory_order_relaxed) > 0)
|
||||
while (!gameThreadFinished.load(std::memory_order_acquire))
|
||||
{
|
||||
const uint32_t pc = m_debugPc.load(std::memory_order_relaxed);
|
||||
const uint32_t ra = m_debugRa.load(std::memory_order_relaxed);
|
||||
@@ -1264,7 +1394,8 @@ void PS2Runtime::run()
|
||||
std::cout << " pc=0x" << std::hex << pc
|
||||
<< " ra=0x" << ra
|
||||
<< " sp=0x" << sp
|
||||
<< " gp=0x" << gp;
|
||||
<< " gp=0x" << gp
|
||||
<< std::dec << std::endl;
|
||||
}
|
||||
if ((tick % 600) == 0)
|
||||
{
|
||||
@@ -1300,24 +1431,43 @@ void PS2Runtime::run()
|
||||
}
|
||||
}
|
||||
|
||||
if (g_activeThreads.load(std::memory_order_relaxed) == 0)
|
||||
requestStop();
|
||||
|
||||
const auto joinDeadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
|
||||
while (!gameThreadFinished.load(std::memory_order_acquire) &&
|
||||
std::chrono::steady_clock::now() < joinDeadline)
|
||||
{
|
||||
if (gameThread.joinable())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
if (gameThread.joinable())
|
||||
{
|
||||
if (gameThreadFinished.load(std::memory_order_acquire))
|
||||
{
|
||||
gameThread.join();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (gameThread.joinable())
|
||||
else
|
||||
{
|
||||
std::cerr << "[run] game thread did not stop within timeout; detaching" << std::endl;
|
||||
gameThread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
const auto workerDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(250);
|
||||
while (g_activeThreads.load(std::memory_order_relaxed) > 0 &&
|
||||
std::chrono::steady_clock::now() < workerDeadline)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
UnloadTexture(frameTex);
|
||||
CloseWindow();
|
||||
|
||||
std::cout << "[run] exiting loop, activeThreads=" << g_activeThreads.load(std::memory_order_relaxed) << std::endl;
|
||||
const int remainingThreads = g_activeThreads.load(std::memory_order_relaxed);
|
||||
std::cout << "[run] exiting loop, activeThreads=" << remainingThreads << std::endl;
|
||||
if (remainingThreads > 0)
|
||||
{
|
||||
std::cerr << "[run] warning: " << remainingThreads
|
||||
<< " guest worker thread(s) still active during shutdown." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace ps2_syscalls
|
||||
{
|
||||
#include "syscalls/ps2_syscalls_interrupt.inl"
|
||||
#include "syscalls/ps2_syscalls_system.inl"
|
||||
void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime);
|
||||
|
||||
bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
@@ -107,6 +108,7 @@ namespace ps2_syscalls
|
||||
ExitDeleteThread(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x25:
|
||||
case static_cast<uint32_t>(-0x26):
|
||||
TerminateThread(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x29:
|
||||
@@ -167,9 +169,11 @@ namespace ps2_syscalls
|
||||
CreateSema(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x41:
|
||||
case static_cast<uint32_t>(-0x49):
|
||||
DeleteSema(rdram, ctx, runtime);
|
||||
return true;
|
||||
case static_cast<uint32_t>(-0x49):
|
||||
iDeleteSema(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x42:
|
||||
SignalSema(rdram, ctx, runtime);
|
||||
return true;
|
||||
@@ -263,9 +267,24 @@ namespace ps2_syscalls
|
||||
case static_cast<uint32_t>(-0x71):
|
||||
GsPutIMR(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x73:
|
||||
SetVSyncFlag(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x74:
|
||||
RegisterExitHandler(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x76:
|
||||
case static_cast<uint32_t>(-0x76):
|
||||
ps2_stubs::sceSifDmaStat(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x77:
|
||||
case static_cast<uint32_t>(-0x77):
|
||||
ps2_stubs::sceSifSetDma(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x78:
|
||||
case static_cast<uint32_t>(-0x78):
|
||||
ps2_stubs::sceSifSetDChain(rdram, ctx, runtime);
|
||||
return true;
|
||||
case 0x85:
|
||||
SetMemoryMode(rdram, ctx, runtime);
|
||||
return true;
|
||||
@@ -278,4 +297,86 @@ namespace ps2_syscalls
|
||||
#include "syscalls/ps2_syscalls_flags.inl"
|
||||
#include "syscalls/ps2_syscalls_rpc.inl"
|
||||
#include "syscalls/ps2_syscalls_fileio.inl"
|
||||
|
||||
void notifyRuntimeStop()
|
||||
{
|
||||
stopInterruptWorker();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_intcHandlers.clear();
|
||||
g_dmacHandlers.clear();
|
||||
g_nextIntcHandlerId = 1;
|
||||
g_nextDmacHandlerId = 1;
|
||||
g_enabled_intc_mask = 0xFFFFFFFFu;
|
||||
g_enabled_dmac_mask = 0xFFFFFFFFu;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
|
||||
g_vsync_registration = {};
|
||||
g_vsync_tick_counter = 0u;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ThreadInfo>> threads;
|
||||
threads.reserve(32);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_thread_map_mutex);
|
||||
for (const auto &entry : g_threads)
|
||||
{
|
||||
if (entry.second)
|
||||
{
|
||||
threads.push_back(entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &threadInfo : threads)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(threadInfo->m);
|
||||
threadInfo->forceRelease = true;
|
||||
threadInfo->terminated = true;
|
||||
}
|
||||
threadInfo->cv.notify_all();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<SemaInfo>> semas;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sema_map_mutex);
|
||||
semas.reserve(g_semas.size());
|
||||
for (const auto &entry : g_semas)
|
||||
{
|
||||
if (entry.second)
|
||||
{
|
||||
semas.push_back(entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &sema : semas)
|
||||
{
|
||||
sema->cv.notify_all();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<EventFlagInfo>> eventFlags;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_event_flag_map_mutex);
|
||||
eventFlags.reserve(g_eventFlags.size());
|
||||
for (const auto &entry : g_eventFlags)
|
||||
{
|
||||
if (entry.second)
|
||||
{
|
||||
eventFlags.push_back(entry.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &eventFlag : eventFlags)
|
||||
{
|
||||
eventFlag->cv.notify_all();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_alarm_mutex);
|
||||
g_alarms.clear();
|
||||
}
|
||||
g_alarm_cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace
|
||||
std::filesystem::path g_cdLeafIndexRoot;
|
||||
bool g_cdLeafIndexBuilt = false;
|
||||
uint32_t g_nextPseudoLbn = kCdPseudoLbnStart;
|
||||
std::filesystem::path g_cdAutoImagePath;
|
||||
std::filesystem::path g_cdAutoImageRoot;
|
||||
bool g_cdAutoImageSearched = false;
|
||||
std::filesystem::path g_cdImageSizePath;
|
||||
uint64_t g_cdImageSizeBytes = 0;
|
||||
bool g_cdImageSizeValid = false;
|
||||
int32_t g_lastCdError = 0;
|
||||
uint32_t g_cdMode = 0;
|
||||
uint32_t g_cdStreamingLbn = 0;
|
||||
@@ -120,9 +126,174 @@ namespace
|
||||
return ec ? std::filesystem::path(".") : cwd.lexically_normal();
|
||||
}
|
||||
|
||||
bool hasCdImageExtension(const std::filesystem::path &path)
|
||||
{
|
||||
const std::string ext = toLowerAscii(path.extension().string());
|
||||
return ext == ".iso" || ext == ".bin" || ext == ".img" || ext == ".mdf" || ext == ".nrg";
|
||||
}
|
||||
|
||||
bool trySelectBestDiscImageFromDirectory(const std::filesystem::path &dir,
|
||||
std::filesystem::path &pathOut)
|
||||
{
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::exists(dir, ec) || ec || !std::filesystem::is_directory(dir, ec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::path bestPath;
|
||||
uint64_t bestSize = 0;
|
||||
for (const auto &entry : std::filesystem::directory_iterator(
|
||||
dir, std::filesystem::directory_options::skip_permission_denied, ec))
|
||||
{
|
||||
if (ec)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!entry.is_regular_file())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!hasCdImageExtension(entry.path()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::error_code sizeEc;
|
||||
const uint64_t size = static_cast<uint64_t>(entry.file_size(sizeEc));
|
||||
if (sizeEc || size < (64ull * 1024ull * 1024ull))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (size > bestSize)
|
||||
{
|
||||
bestSize = size;
|
||||
bestPath = entry.path();
|
||||
}
|
||||
}
|
||||
|
||||
if (bestPath.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pathOut = bestPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::filesystem::path autoDetectCdImagePath()
|
||||
{
|
||||
const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths();
|
||||
std::vector<std::filesystem::path> roots;
|
||||
|
||||
const std::filesystem::path cdRoot = getCdRootPath();
|
||||
if (!cdRoot.empty())
|
||||
{
|
||||
roots.push_back(cdRoot);
|
||||
std::filesystem::path parent = cdRoot;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
parent = parent.parent_path();
|
||||
if (parent.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
roots.push_back(parent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!paths.hostRoot.empty())
|
||||
{
|
||||
roots.push_back(paths.hostRoot);
|
||||
}
|
||||
if (!paths.elfDirectory.empty())
|
||||
{
|
||||
roots.push_back(paths.elfDirectory);
|
||||
}
|
||||
|
||||
std::filesystem::path bestPath;
|
||||
uint64_t bestSize = 0;
|
||||
std::unordered_set<std::string> seenRoots;
|
||||
for (const std::filesystem::path &root : roots)
|
||||
{
|
||||
if (root.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string key = toLowerAscii(root.lexically_normal().string());
|
||||
if (!seenRoots.emplace(key).second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::filesystem::path candidate;
|
||||
if (!trySelectBestDiscImageFromDirectory(root, candidate))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::error_code sizeEc;
|
||||
const uint64_t size = static_cast<uint64_t>(std::filesystem::file_size(candidate, sizeEc));
|
||||
if (sizeEc || size <= bestSize)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bestSize = size;
|
||||
bestPath = candidate;
|
||||
}
|
||||
|
||||
if (!bestPath.empty())
|
||||
{
|
||||
std::cout << "[CD] Auto-detected disc image: " << bestPath.string() << std::endl;
|
||||
}
|
||||
return bestPath;
|
||||
}
|
||||
|
||||
std::filesystem::path getCdImagePath()
|
||||
{
|
||||
return PS2Runtime::getIoPaths().cdImage;
|
||||
const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths();
|
||||
if (!paths.cdImage.empty())
|
||||
{
|
||||
return paths.cdImage;
|
||||
}
|
||||
|
||||
const std::filesystem::path cdRoot = getCdRootPath();
|
||||
if (!g_cdAutoImageSearched || g_cdAutoImageRoot != cdRoot)
|
||||
{
|
||||
g_cdAutoImageRoot = cdRoot;
|
||||
g_cdAutoImagePath = autoDetectCdImagePath();
|
||||
g_cdAutoImageSearched = true;
|
||||
}
|
||||
|
||||
return g_cdAutoImagePath;
|
||||
}
|
||||
|
||||
bool tryGetCdImageTotalSectors(uint64_t &totalSectorsOut)
|
||||
{
|
||||
const std::filesystem::path imagePath = getCdImagePath();
|
||||
if (imagePath.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_cdImageSizeValid || g_cdImageSizePath != imagePath)
|
||||
{
|
||||
std::error_code ec;
|
||||
g_cdImageSizeBytes = static_cast<uint64_t>(std::filesystem::file_size(imagePath, ec));
|
||||
g_cdImageSizePath = imagePath;
|
||||
g_cdImageSizeValid = !ec;
|
||||
}
|
||||
if (!g_cdImageSizeValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
totalSectorsOut = g_cdImageSizeBytes / static_cast<uint64_t>(kCdSectorSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t sectorsForBytes(uint64_t byteCount)
|
||||
@@ -348,6 +519,18 @@ namespace
|
||||
const std::filesystem::path cdImage = getCdImagePath();
|
||||
if (!cdImage.empty())
|
||||
{
|
||||
uint64_t totalSectors = 0;
|
||||
if (tryGetCdImageTotalSectors(totalSectors))
|
||||
{
|
||||
const uint64_t start = static_cast<uint64_t>(lbn);
|
||||
const uint64_t end = start + static_cast<uint64_t>(sectors);
|
||||
if (start >= totalSectors || end > totalSectors)
|
||||
{
|
||||
g_lastCdError = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const uint64_t offset = static_cast<uint64_t>(lbn) * kCdSectorSize;
|
||||
return readHostRange(cdImage, offset, dst, byteCount);
|
||||
}
|
||||
@@ -359,6 +542,26 @@ namespace
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isResolvableCdLbn(uint32_t lbn)
|
||||
{
|
||||
for (const auto &[key, entry] : g_cdFilesByKey)
|
||||
{
|
||||
const uint32_t endLbn = entry.baseLbn + entry.sectors;
|
||||
if (lbn >= entry.baseLbn && lbn < endLbn)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t totalSectors = 0;
|
||||
if (tryGetCdImageTotalSectors(totalSectors))
|
||||
{
|
||||
return static_cast<uint64_t>(lbn) < totalSectors;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool writeCdSearchResult(uint8_t *rdram, uint32_t fileAddr, const std::string &ps2Path, const CdFileEntry &entry)
|
||||
{
|
||||
// sceCdlFILE layout: u32 lsn, u32 size, char name[16], u8 date[8]
|
||||
|
||||
@@ -1365,6 +1365,73 @@ void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
TODO_NAMED("sceSetPtm", rdram, ctx, runtime);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Ps2SifDmaTransfer
|
||||
{
|
||||
uint32_t src = 0;
|
||||
uint32_t dest = 0;
|
||||
int32_t size = 0;
|
||||
int32_t attr = 0;
|
||||
};
|
||||
static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected SIF DMA descriptor size");
|
||||
|
||||
std::mutex g_sifDmaTransferMutex;
|
||||
uint32_t g_nextSifDmaTransferId = 1u;
|
||||
|
||||
uint32_t allocateSifDmaTransferId()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sifDmaTransferMutex);
|
||||
uint32_t id = g_nextSifDmaTransferId++;
|
||||
if (id == 0u)
|
||||
{
|
||||
id = g_nextSifDmaTransferId++;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
bool copyGuestByteRange(uint8_t *rdram, uint32_t dstAddr, uint32_t srcAddr, uint32_t sizeBytes)
|
||||
{
|
||||
if (!rdram || sizeBytes == 0u)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*dst = *src;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceSifAddCmdHandler", rdram, ctx, runtime);
|
||||
@@ -1397,7 +1464,12 @@ void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceSifDmaStat", rdram, ctx, runtime);
|
||||
(void)rdram;
|
||||
(void)runtime;
|
||||
(void)getRegU32(ctx, 4); // trid
|
||||
|
||||
// Transfers are applied immediately by sceSifSetDma in this runtime.
|
||||
setReturnS32(ctx, -1);
|
||||
}
|
||||
|
||||
void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -1437,6 +1509,56 @@ void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime
|
||||
|
||||
void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
(void)runtime;
|
||||
|
||||
const uint32_t rdAddr = getRegU32(ctx, 4);
|
||||
const uint32_t srcAddr = getRegU32(ctx, 5);
|
||||
const uint32_t dstAddr = getRegU32(ctx, 6);
|
||||
const int32_t sizeSigned = static_cast<int32_t>(getRegU32(ctx, 7));
|
||||
|
||||
if (sizeSigned <= 0)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t size = static_cast<uint32_t>(sizeSigned);
|
||||
if (size > PS2_RAM_SIZE)
|
||||
{
|
||||
static uint32_t warnCount = 0;
|
||||
if (warnCount < 32u)
|
||||
{
|
||||
std::cerr << "sceSifGetOtherData rejected oversized transfer size=0x"
|
||||
<< std::hex << size << std::dec << std::endl;
|
||||
++warnCount;
|
||||
}
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!copyGuestByteRange(rdram, dstAddr, srcAddr, size))
|
||||
{
|
||||
static uint32_t warnCount = 0;
|
||||
if (warnCount < 32u)
|
||||
{
|
||||
std::cerr << "sceSifGetOtherData copy failed src=0x" << std::hex << srcAddr
|
||||
<< " dst=0x" << dstAddr
|
||||
<< " size=0x" << size
|
||||
<< std::dec << std::endl;
|
||||
++warnCount;
|
||||
}
|
||||
setReturnS32(ctx, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
// SifRpcReceiveData_t keeps src/dest/size at offsets 0x10/0x14/0x18.
|
||||
if (uint8_t *rd = getMemPtr(rdram, rdAddr))
|
||||
{
|
||||
std::memcpy(rd + 0x10u, &srcAddr, sizeof(srcAddr));
|
||||
std::memcpy(rd + 0x14u, &dstAddr, sizeof(dstAddr));
|
||||
std::memcpy(rd + 0x18u, &size, sizeof(size));
|
||||
}
|
||||
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
@@ -1538,12 +1660,69 @@ void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceSifSetDChain", rdram, ctx, runtime);
|
||||
(void)rdram;
|
||||
(void)runtime;
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
TODO_NAMED("sceSifSetDma", rdram, ctx, runtime);
|
||||
(void)runtime;
|
||||
|
||||
const uint32_t dmatAddr = getRegU32(ctx, 4);
|
||||
const uint32_t count = getRegU32(ctx, 5);
|
||||
if (!dmatAddr || count == 0u || count > 32u)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
const uint32_t entryAddr = dmatAddr + (i * static_cast<uint32_t>(sizeof(Ps2SifDmaTransfer)));
|
||||
const uint8_t *entry = getConstMemPtr(rdram, entryAddr);
|
||||
if (!entry)
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
Ps2SifDmaTransfer xfer{};
|
||||
std::memcpy(&xfer, entry, sizeof(xfer));
|
||||
if (xfer.size <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t sizeBytes = static_cast<uint32_t>(xfer.size);
|
||||
if (sizeBytes > PS2_RAM_SIZE)
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (!copyGuestByteRange(rdram, xfer.dest, xfer.src, sizeBytes))
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
static uint32_t warnCount = 0;
|
||||
if (warnCount < 32u)
|
||||
{
|
||||
std::cerr << "sceSifSetDma failed dmat=0x" << std::hex << dmatAddr
|
||||
<< " count=0x" << count
|
||||
<< std::dec << std::endl;
|
||||
++warnCount;
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
setReturnS32(ctx, static_cast<int32_t>(allocateSifDmaTransferId()));
|
||||
}
|
||||
|
||||
void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -2360,4 +2539,3 @@ void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
ps2_syscalls::fioWrite(rdram, ctx, runtime);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,120 @@
|
||||
void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t lbn = getRegU32(ctx, 4); // $a0 - logical block number
|
||||
uint32_t sectors = getRegU32(ctx, 5); // $a1 - sector count
|
||||
uint32_t buf = getRegU32(ctx, 6); // $a2 - destination buffer in RDRAM
|
||||
const uint32_t a0 = getRegU32(ctx, 4); // usually lbn
|
||||
const uint32_t a1 = getRegU32(ctx, 5); // usually sector count
|
||||
const uint32_t a2 = getRegU32(ctx, 6); // usually destination buffer
|
||||
|
||||
uint32_t offset = buf & PS2_RAM_MASK;
|
||||
size_t bytes = static_cast<size_t>(sectors) * kCdSectorSize;
|
||||
if (bytes > 0)
|
||||
struct CdReadArgs
|
||||
{
|
||||
const size_t maxBytes = PS2_RAM_SIZE - offset;
|
||||
if (bytes > maxBytes)
|
||||
uint32_t lbn = 0;
|
||||
uint32_t sectors = 0;
|
||||
uint32_t buf = 0;
|
||||
const char *tag = "";
|
||||
};
|
||||
|
||||
auto clampReadBytes = [](uint32_t sectors, uint32_t offset) -> size_t
|
||||
{
|
||||
const uint64_t requested = static_cast<uint64_t>(sectors) * static_cast<uint64_t>(kCdSectorSize);
|
||||
if (requested == 0)
|
||||
{
|
||||
bytes = maxBytes;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *dst = rdram + offset;
|
||||
bool ok = true;
|
||||
if (bytes > 0)
|
||||
const uint64_t maxBytes = static_cast<uint64_t>(PS2_RAM_SIZE - offset);
|
||||
const uint64_t clamped = std::min<uint64_t>(requested, maxBytes);
|
||||
return static_cast<size_t>(clamped);
|
||||
};
|
||||
|
||||
auto tryRead = [&](const CdReadArgs &args) -> bool
|
||||
{
|
||||
ok = readCdSectors(lbn, sectors, dst, bytes);
|
||||
const uint32_t offset = args.buf & PS2_RAM_MASK;
|
||||
const size_t bytes = clampReadBytes(args.sectors, offset);
|
||||
if (bytes == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return readCdSectors(args.lbn, args.sectors, rdram + offset, bytes);
|
||||
};
|
||||
|
||||
CdReadArgs selected{a0, a1, a2, "a0/a1/a2"};
|
||||
bool ok = tryRead(selected);
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
// Some game-side wrappers use a nonstandard register layout.
|
||||
// If primary decode does not resolve to a known LBN, try safe alternatives.
|
||||
constexpr uint32_t kMaxReasonableSectors = PS2_RAM_SIZE / kCdSectorSize;
|
||||
if (!isResolvableCdLbn(selected.lbn))
|
||||
{
|
||||
const std::array<CdReadArgs, 5> alternatives = {
|
||||
CdReadArgs{a2, a1, a0, "a2/a1/a0"},
|
||||
CdReadArgs{a0, a2, a1, "a0/a2/a1"},
|
||||
CdReadArgs{a1, a0, a2, "a1/a0/a2"},
|
||||
CdReadArgs{a1, a2, a0, "a1/a2/a0"},
|
||||
CdReadArgs{a2, a0, a1, "a2/a0/a1"}};
|
||||
|
||||
for (const CdReadArgs &candidate : alternatives)
|
||||
{
|
||||
if (candidate.sectors > kMaxReasonableSectors)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!isResolvableCdLbn(candidate.lbn))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tryRead(candidate))
|
||||
{
|
||||
static uint32_t recoverLogCount = 0;
|
||||
if (recoverLogCount < 16)
|
||||
{
|
||||
std::cout << "[sceCdRead] recovered with alternate args " << candidate.tag
|
||||
<< " (pc=0x" << std::hex << ctx->pc
|
||||
<< " ra=0x" << getRegU32(ctx, 31)
|
||||
<< " a0=0x" << a0
|
||||
<< " a1=0x" << a1
|
||||
<< " a2=0x" << a2 << std::dec << ")" << std::endl;
|
||||
++recoverLogCount;
|
||||
}
|
||||
selected = candidate;
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
std::memset(dst, 0, bytes);
|
||||
const uint32_t offset = a2 & PS2_RAM_MASK;
|
||||
const size_t bytes = clampReadBytes(a1, offset);
|
||||
if (bytes > 0)
|
||||
{
|
||||
std::memset(rdram + offset, 0, bytes);
|
||||
}
|
||||
|
||||
static uint32_t unresolvedLogCount = 0;
|
||||
if (unresolvedLogCount < 32)
|
||||
{
|
||||
std::cerr << "[sceCdRead] unresolved request pc=0x" << std::hex << ctx->pc
|
||||
<< " ra=0x" << getRegU32(ctx, 31)
|
||||
<< " a0=0x" << a0
|
||||
<< " a1=0x" << a1
|
||||
<< " a2=0x" << a2 << std::dec << std::endl;
|
||||
++unresolvedLogCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
g_cdStreamingLbn = lbn + sectors;
|
||||
g_cdStreamingLbn = selected.lbn + selected.sectors;
|
||||
setReturnS32(ctx, 1); // command accepted/success
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
setReturnS32(ctx, 0);
|
||||
}
|
||||
|
||||
void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
@@ -253,13 +253,30 @@ void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
void sdDrvInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
constexpr uint32_t kSdrInitAddr = 0x2E9A20u;
|
||||
|
||||
static int logCount = 0;
|
||||
if (logCount < 8)
|
||||
{
|
||||
std::cout << "ps2_stub sdDrvInit (noop)" << std::endl;
|
||||
std::cout << "ps2_stub sdDrvInit -> SdrInit_0x2e9a20" << std::endl;
|
||||
++logCount;
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
|
||||
if (!runtime || !ctx || !rdram || !runtime->hasFunction(kSdrInitAddr))
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t returnPc = getRegU32(ctx, 31);
|
||||
PS2Runtime::RecompiledFunction sdrInit = runtime->lookupFunction(kSdrInitAddr);
|
||||
ctx->pc = kSdrInitAddr;
|
||||
sdrInit(rdram, ctx, runtime);
|
||||
|
||||
if (ctx->pc == kSdrInitAddr || ctx->pc == 0u)
|
||||
{
|
||||
ctx->pc = returnPc;
|
||||
}
|
||||
}
|
||||
|
||||
void ADXF_LoadPartitionNw(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
@@ -318,6 +318,8 @@ struct IrqHandlerInfo
|
||||
uint32_t cause = 0;
|
||||
uint32_t handler = 0;
|
||||
uint32_t arg = 0;
|
||||
uint32_t gp = 0;
|
||||
uint32_t sp = 0;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ struct ThreadInfo
|
||||
uint32_t option = 0;
|
||||
uint32_t arg = 0;
|
||||
bool started = false;
|
||||
bool ownsStack = false;
|
||||
uint32_t tlsBase = 0;
|
||||
|
||||
// Thread Status
|
||||
@@ -45,15 +46,19 @@ struct ThreadInfo
|
||||
// Common kernel-like error codes used by thread/event/alarm syscalls.
|
||||
constexpr int KE_OK = 0;
|
||||
constexpr int KE_ERROR = -1;
|
||||
constexpr int KE_ILLEGAL_PRIORITY = -403;
|
||||
constexpr int KE_ILLEGAL_MODE = -405;
|
||||
constexpr int KE_ILLEGAL_THID = -406;
|
||||
constexpr int KE_UNKNOWN_THID = -407;
|
||||
constexpr int KE_UNKNOWN_SEMID = -408;
|
||||
constexpr int KE_UNKNOWN_EVFID = -409;
|
||||
constexpr int KE_DORMANT = -413;
|
||||
constexpr int KE_NOT_DORMANT = -414;
|
||||
constexpr int KE_NOT_SUSPEND = -415;
|
||||
constexpr int KE_NOT_WAIT = -416;
|
||||
constexpr int KE_RELEASE_WAIT = -418;
|
||||
constexpr int KE_SEMA_ZERO = -419;
|
||||
constexpr int KE_SEMA_OVF = -420;
|
||||
constexpr int KE_EVF_COND = -421;
|
||||
constexpr int KE_EVF_MULTI = -422;
|
||||
constexpr int KE_EVF_ILPAT = -423;
|
||||
|
||||
@@ -440,4 +440,4 @@ void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
std::cout << "fioRemove: Removed file '" << hostPath << "'" << std::endl;
|
||||
setReturnS32(ctx, 0); // Success
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,148 @@
|
||||
void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
static bool looksLikeGuestPointerOrNull(uint32_t value)
|
||||
{
|
||||
if (value == 0u)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const uint32_t normalized = value & 0x1FFFFFFFu;
|
||||
return normalized < PS2_RAM_SIZE;
|
||||
}
|
||||
|
||||
static bool readGuestU32Safe(const uint8_t *rdram, uint32_t addr, uint32_t &out)
|
||||
{
|
||||
const uint8_t *b0 = getConstMemPtr(rdram, addr + 0u);
|
||||
const uint8_t *b1 = getConstMemPtr(rdram, addr + 1u);
|
||||
const uint8_t *b2 = getConstMemPtr(rdram, addr + 2u);
|
||||
const uint8_t *b3 = getConstMemPtr(rdram, addr + 3u);
|
||||
if (!b0 || !b1 || !b2 || !b3)
|
||||
{
|
||||
out = 0u;
|
||||
return false;
|
||||
}
|
||||
|
||||
out = static_cast<uint32_t>(*b0) |
|
||||
(static_cast<uint32_t>(*b1) << 8) |
|
||||
(static_cast<uint32_t>(*b2) << 16) |
|
||||
(static_cast<uint32_t>(*b3) << 24);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct DecodedSemaParams
|
||||
{
|
||||
uint32_t paramAddr = getRegU32(ctx, 4); // $a0
|
||||
const uint32_t *param = reinterpret_cast<const uint32_t *>(getConstMemPtr(rdram, paramAddr));
|
||||
int init = 0;
|
||||
int max = 1;
|
||||
uint32_t attr = 0;
|
||||
uint32_t option = 0;
|
||||
};
|
||||
|
||||
if (param)
|
||||
static DecodedSemaParams decodeCreateSemaParams(const uint32_t *param, uint32_t availableWords)
|
||||
{
|
||||
DecodedSemaParams out{};
|
||||
if (!param || availableWords == 0u)
|
||||
{
|
||||
// sceSemaParam layout commonly: attr(0), option(1), initCount(2), maxCount(3)
|
||||
attr = param[0];
|
||||
option = param[1];
|
||||
init = static_cast<int>(param[2]);
|
||||
max = static_cast<int>(param[3]);
|
||||
return out;
|
||||
}
|
||||
|
||||
// EE layout (kernel.h):
|
||||
// [0]=count [1]=max_count [2]=init_count [3]=wait_threads [4]=attr [5]=option
|
||||
const bool hasEeLayout = availableWords >= 3u;
|
||||
const int eeMax = hasEeLayout ? static_cast<int>(param[1]) : 1;
|
||||
const int eeInit = hasEeLayout ? static_cast<int>(param[2]) : 0;
|
||||
const uint32_t eeAttr = (availableWords >= 5u) ? param[4] : 0u;
|
||||
const uint32_t eeOption = (availableWords >= 6u) ? param[5] : 0u;
|
||||
|
||||
// Legacy layout (IOP-style):
|
||||
// [0]=attr [1]=option [2]=init [3]=max
|
||||
const bool hasLegacyLayout = availableWords >= 4u;
|
||||
const int legacyMax = hasLegacyLayout ? static_cast<int>(param[3]) : 1;
|
||||
const int legacyInit = hasLegacyLayout ? static_cast<int>(param[2]) : 0;
|
||||
const uint32_t legacyAttr = hasLegacyLayout ? param[0] : 0u;
|
||||
const uint32_t legacyOption = hasLegacyLayout ? param[1] : 0u;
|
||||
|
||||
auto countLooksPlausible = [](int value) -> bool
|
||||
{
|
||||
return value > 0 && value <= 0x10000;
|
||||
};
|
||||
|
||||
bool useLegacyLayout = hasLegacyLayout && !hasEeLayout;
|
||||
if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && !countLooksPlausible(eeMax))
|
||||
{
|
||||
useLegacyLayout = true;
|
||||
}
|
||||
else if (hasLegacyLayout && hasEeLayout && countLooksPlausible(legacyMax) && countLooksPlausible(eeMax))
|
||||
{
|
||||
// If both max values look valid, prefer the layout whose option field
|
||||
// looks like a pointer/NULL payload.
|
||||
const bool eeOptionLooksValid = looksLikeGuestPointerOrNull(eeOption);
|
||||
const bool legacyOptionLooksValid = looksLikeGuestPointerOrNull(legacyOption);
|
||||
if (!eeOptionLooksValid && legacyOptionLooksValid)
|
||||
{
|
||||
useLegacyLayout = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (useLegacyLayout && hasLegacyLayout)
|
||||
{
|
||||
out.max = legacyMax;
|
||||
out.init = legacyInit;
|
||||
out.attr = legacyAttr;
|
||||
out.option = legacyOption;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hasEeLayout)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
out.max = eeMax;
|
||||
out.init = eeInit;
|
||||
out.attr = eeAttr;
|
||||
out.option = eeOption;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t paramAddr = getRegU32(ctx, 4); // $a0
|
||||
if (paramAddr == 0u)
|
||||
{
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t rawParams[6] = {};
|
||||
uint32_t availableWords = 0u;
|
||||
for (uint32_t i = 0; i < 6u; ++i)
|
||||
{
|
||||
if (!readGuestU32Safe(rdram, paramAddr + (i * 4u), rawParams[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
availableWords = i + 1u;
|
||||
}
|
||||
|
||||
if (availableWords < 3u)
|
||||
{
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
const DecodedSemaParams decoded = decodeCreateSemaParams(rawParams, availableWords);
|
||||
int init = decoded.init;
|
||||
int max = decoded.max;
|
||||
uint32_t attr = decoded.attr;
|
||||
uint32_t option = decoded.option;
|
||||
|
||||
if (max <= 0)
|
||||
{
|
||||
max = 1;
|
||||
}
|
||||
if (init < 0)
|
||||
{
|
||||
init = 0;
|
||||
}
|
||||
if (init > max)
|
||||
{
|
||||
init = max;
|
||||
@@ -34,7 +158,32 @@ void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sema_map_mutex);
|
||||
id = g_nextSemaId++;
|
||||
for (int attempts = 0; attempts < 0x7FFF; ++attempts)
|
||||
{
|
||||
if (g_nextSemaId <= 0)
|
||||
{
|
||||
g_nextSemaId = 1;
|
||||
}
|
||||
|
||||
const int candidate = g_nextSemaId++;
|
||||
if (candidate <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_semas.find(candidate) == g_semas.end())
|
||||
{
|
||||
id = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id <= 0)
|
||||
{
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
g_semas.emplace(id, info);
|
||||
}
|
||||
std::cout << "[CreateSema] id=" << id << " init=" << init << " max=" << max << std::endl;
|
||||
@@ -67,20 +216,36 @@ void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void iDeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
DeleteSema(rdram, ctx, runtime);
|
||||
}
|
||||
|
||||
void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
int sid = static_cast<int>(getRegU32(ctx, 4));
|
||||
auto sema = lookupSemaInfo(sid);
|
||||
if (sema)
|
||||
if (!sema)
|
||||
{
|
||||
setReturnS32(ctx, KE_UNKNOWN_SEMID);
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = KE_OK;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sema->m);
|
||||
if (sema->count < sema->maxCount)
|
||||
if (sema->count >= sema->maxCount)
|
||||
{
|
||||
ret = KE_SEMA_OVF;
|
||||
}
|
||||
else
|
||||
{
|
||||
sema->count++;
|
||||
sema->cv.notify_one();
|
||||
}
|
||||
sema->cv.notify_one();
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
|
||||
setReturnS32(ctx, ret);
|
||||
}
|
||||
|
||||
void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -108,7 +273,7 @@ void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
if (info)
|
||||
{
|
||||
std::lock_guard<std::mutex> tLock(info->m);
|
||||
info->status = THS_WAIT;
|
||||
info->status = (info->suspendCount > 0) ? THS_WAITSUSPEND : THS_WAIT;
|
||||
info->waitType = TSW_SEMA;
|
||||
info->waitId = sid;
|
||||
info->forceRelease = false;
|
||||
@@ -130,7 +295,7 @@ void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
if (info)
|
||||
{
|
||||
std::lock_guard<std::mutex> tLock(info->m);
|
||||
info->status = THS_RUN;
|
||||
info->status = (info->suspendCount > 0) ? THS_SUSPEND : THS_RUN;
|
||||
info->waitType = TSW_NONE;
|
||||
info->waitId = 0;
|
||||
if (info->forceRelease)
|
||||
@@ -189,14 +354,14 @@ void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
auto sema = lookupSemaInfo(sid);
|
||||
if (!sema)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_SEMID);
|
||||
return;
|
||||
}
|
||||
|
||||
ee_sema_t *status = reinterpret_cast<ee_sema_t *>(getMemPtr(rdram, statusAddr));
|
||||
if (!status)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -207,7 +372,7 @@ void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
status->wait_threads = sema->waiters;
|
||||
status->attr = sema->attr;
|
||||
status->option = sema->option;
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
@@ -1,11 +1,262 @@
|
||||
namespace
|
||||
{
|
||||
constexpr uint32_t kIntcVblankStart = 2u;
|
||||
constexpr uint32_t kIntcVblankEnd = 3u;
|
||||
constexpr auto kVblankPeriod = std::chrono::microseconds(16667);
|
||||
constexpr int kMaxCatchupTicks = 4;
|
||||
|
||||
struct VSyncFlagRegistration
|
||||
{
|
||||
uint32_t flagAddr = 0;
|
||||
uint32_t tickAddr = 0;
|
||||
};
|
||||
|
||||
static std::mutex g_irq_handler_mutex;
|
||||
static std::mutex g_irq_worker_mutex;
|
||||
static std::mutex g_vsync_flag_mutex;
|
||||
static std::atomic<bool> g_irq_worker_stop{false};
|
||||
static std::atomic<bool> g_irq_worker_running{false};
|
||||
static uint32_t g_enabled_intc_mask = 0xFFFFFFFFu;
|
||||
static uint32_t g_enabled_dmac_mask = 0xFFFFFFFFu;
|
||||
static uint64_t g_vsync_tick_counter = 0u;
|
||||
static VSyncFlagRegistration g_vsync_registration{};
|
||||
}
|
||||
|
||||
static void writeGuestU32NoThrow(uint8_t *rdram, uint32_t addr, uint32_t value)
|
||||
{
|
||||
if (addr == 0u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *dst = getMemPtr(rdram, addr);
|
||||
if (!dst)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::memcpy(dst, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static void writeGuestU64NoThrow(uint8_t *rdram, uint32_t addr, uint64_t value)
|
||||
{
|
||||
if (addr == 0u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *dst = getMemPtr(rdram, addr);
|
||||
if (!dst)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::memcpy(dst, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static void dispatchIntcHandlersForCause(uint8_t *rdram, PS2Runtime *runtime, uint32_t cause)
|
||||
{
|
||||
if (!rdram || !runtime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<IrqHandlerInfo> handlers;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
if (cause < 32u && (g_enabled_intc_mask & (1u << cause)) == 0u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
handlers.reserve(g_intcHandlers.size());
|
||||
for (const auto &[id, info] : g_intcHandlers)
|
||||
{
|
||||
(void)id;
|
||||
if (!info.enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (info.cause != cause)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (info.handler == 0u)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
handlers.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
for (const IrqHandlerInfo &info : handlers)
|
||||
{
|
||||
if (!runtime->hasFunction(info.handler))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
R5900Context irqCtx{};
|
||||
const uint32_t sp = (info.sp != 0u) ? info.sp : (PS2_RAM_SIZE - 0x10u);
|
||||
SET_GPR_U32(&irqCtx, 28, info.gp);
|
||||
SET_GPR_U32(&irqCtx, 29, sp);
|
||||
SET_GPR_U32(&irqCtx, 31, 0u);
|
||||
SET_GPR_U32(&irqCtx, 4, cause);
|
||||
SET_GPR_U32(&irqCtx, 5, info.arg);
|
||||
SET_GPR_U32(&irqCtx, 6, 0u);
|
||||
SET_GPR_U32(&irqCtx, 7, 0u);
|
||||
irqCtx.pc = info.handler;
|
||||
|
||||
PS2Runtime::RecompiledFunction func = runtime->lookupFunction(info.handler);
|
||||
func(rdram, &irqCtx, runtime);
|
||||
}
|
||||
catch (const ThreadExitException &)
|
||||
{
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
static uint32_t warnCount = 0;
|
||||
if (warnCount < 8u)
|
||||
{
|
||||
std::cerr << "[INTC] handler 0x" << std::hex << info.handler
|
||||
<< " threw exception: " << e.what() << std::dec << std::endl;
|
||||
++warnCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void signalVSyncFlag(uint8_t *rdram, uint64_t tickValue)
|
||||
{
|
||||
VSyncFlagRegistration reg{};
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
|
||||
reg = g_vsync_registration;
|
||||
g_vsync_registration = {};
|
||||
g_vsync_tick_counter = tickValue;
|
||||
}
|
||||
|
||||
if (reg.flagAddr != 0u)
|
||||
{
|
||||
writeGuestU32NoThrow(rdram, reg.flagAddr, 1u);
|
||||
}
|
||||
if (reg.tickAddr != 0u)
|
||||
{
|
||||
writeGuestU64NoThrow(rdram, reg.tickAddr, tickValue);
|
||||
}
|
||||
}
|
||||
|
||||
static void interruptWorkerMain(uint8_t *rdram, PS2Runtime *runtime)
|
||||
{
|
||||
using clock = std::chrono::steady_clock;
|
||||
auto nextTick = clock::now() + kVblankPeriod;
|
||||
|
||||
while (!g_irq_worker_stop.load(std::memory_order_acquire) &&
|
||||
runtime != nullptr &&
|
||||
!runtime->isStopRequested())
|
||||
{
|
||||
std::this_thread::sleep_until(nextTick);
|
||||
|
||||
const auto now = clock::now();
|
||||
int ticksToProcess = 0;
|
||||
while (now >= nextTick && ticksToProcess < kMaxCatchupTicks)
|
||||
{
|
||||
++ticksToProcess;
|
||||
nextTick += kVblankPeriod;
|
||||
}
|
||||
if (ticksToProcess == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ticksToProcess; ++i)
|
||||
{
|
||||
uint64_t tickValue = 0u;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
|
||||
tickValue = ++g_vsync_tick_counter;
|
||||
}
|
||||
|
||||
signalVSyncFlag(rdram, tickValue);
|
||||
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankStart);
|
||||
dispatchIntcHandlersForCause(rdram, runtime, kIntcVblankEnd);
|
||||
}
|
||||
}
|
||||
|
||||
g_irq_worker_running.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
static void ensureInterruptWorkerRunning(uint8_t *rdram, PS2Runtime *runtime)
|
||||
{
|
||||
if (!rdram || !runtime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_irq_worker_mutex);
|
||||
if (g_irq_worker_running.load(std::memory_order_acquire))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_irq_worker_stop.store(false, std::memory_order_release);
|
||||
g_irq_worker_running.store(true, std::memory_order_release);
|
||||
try
|
||||
{
|
||||
std::thread(interruptWorkerMain, rdram, runtime).detach();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
g_irq_worker_running.store(false, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
void stopInterruptWorker()
|
||||
{
|
||||
g_irq_worker_stop.store(true, std::memory_order_release);
|
||||
for (int i = 0; i < 100 && g_irq_worker_running.load(std::memory_order_acquire); ++i)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
void SetVSyncFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const uint32_t flagAddr = getRegU32(ctx, 4);
|
||||
const uint32_t tickAddr = getRegU32(ctx, 5);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_vsync_flag_mutex);
|
||||
g_vsync_registration.flagAddr = flagAddr;
|
||||
g_vsync_registration.tickAddr = tickAddr;
|
||||
}
|
||||
|
||||
writeGuestU32NoThrow(rdram, flagAddr, 0u);
|
||||
writeGuestU64NoThrow(rdram, tickAddr, 0u);
|
||||
ensureInterruptWorkerRunning(rdram, runtime);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
const uint32_t cause = getRegU32(ctx, 4);
|
||||
if (cause < 32u)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_enabled_intc_mask |= (1u << cause);
|
||||
}
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
const uint32_t cause = getRegU32(ctx, 4);
|
||||
if (cause < 32u)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_enabled_intc_mask &= ~(1u << cause);
|
||||
}
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -13,11 +264,19 @@ void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
IrqHandlerInfo info{};
|
||||
info.cause = getRegU32(ctx, 4);
|
||||
info.handler = getRegU32(ctx, 5);
|
||||
info.arg = getRegU32(ctx, 6);
|
||||
info.arg = getRegU32(ctx, 7);
|
||||
info.gp = getRegU32(ctx, 28);
|
||||
info.sp = getRegU32(ctx, 29);
|
||||
info.enabled = true;
|
||||
|
||||
const int handlerId = g_nextIntcHandlerId++;
|
||||
g_intcHandlers[handlerId] = info;
|
||||
int handlerId = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
handlerId = g_nextIntcHandlerId++;
|
||||
g_intcHandlers[handlerId] = info;
|
||||
}
|
||||
|
||||
ensureInterruptWorkerRunning(rdram, runtime);
|
||||
setReturnS32(ctx, handlerId);
|
||||
}
|
||||
|
||||
@@ -26,9 +285,10 @@ void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
|
||||
if (handlerId > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_intcHandlers.erase(handlerId);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -36,11 +296,17 @@ void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
IrqHandlerInfo info{};
|
||||
info.cause = getRegU32(ctx, 4);
|
||||
info.handler = getRegU32(ctx, 5);
|
||||
info.arg = getRegU32(ctx, 6);
|
||||
info.arg = getRegU32(ctx, 7);
|
||||
info.gp = getRegU32(ctx, 28);
|
||||
info.sp = getRegU32(ctx, 29);
|
||||
info.enabled = true;
|
||||
|
||||
const int handlerId = g_nextDmacHandlerId++;
|
||||
g_dmacHandlers[handlerId] = info;
|
||||
int handlerId = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
handlerId = g_nextDmacHandlerId++;
|
||||
g_dmacHandlers[handlerId] = info;
|
||||
}
|
||||
setReturnS32(ctx, handlerId);
|
||||
}
|
||||
|
||||
@@ -49,57 +315,82 @@ void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
|
||||
if (handlerId > 0)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_dmacHandlers.erase(handlerId);
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
|
||||
if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end())
|
||||
{
|
||||
it->second.enabled = true;
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end())
|
||||
{
|
||||
it->second.enabled = true;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
|
||||
if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end())
|
||||
{
|
||||
it->second.enabled = false;
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end())
|
||||
{
|
||||
it->second.enabled = false;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
|
||||
if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end())
|
||||
{
|
||||
it->second.enabled = true;
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end())
|
||||
{
|
||||
it->second.enabled = true;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
const int handlerId = static_cast<int>(getRegU32(ctx, 5));
|
||||
if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end())
|
||||
{
|
||||
it->second.enabled = false;
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end())
|
||||
{
|
||||
it->second.enabled = false;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
const uint32_t cause = getRegU32(ctx, 4);
|
||||
if (cause < 32u)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_enabled_dmac_mask |= (1u << cause);
|
||||
}
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
const uint32_t cause = getRegU32(ctx, 4);
|
||||
if (cause < 32u)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_irq_handler_mutex);
|
||||
g_enabled_dmac_mask &= ~(1u << cause);
|
||||
}
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
@@ -735,6 +735,74 @@ void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
}
|
||||
}
|
||||
|
||||
auto signalRpcCompletionSema = [&](uint32_t semaId) -> bool
|
||||
{
|
||||
if (semaId == 0u || semaId > 0xFFFFu)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sema = lookupSemaInfo(static_cast<int>(semaId));
|
||||
if (!sema)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool signaled = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(sema->m);
|
||||
if (!sema->deleted && sema->count < sema->maxCount)
|
||||
{
|
||||
sema->count++;
|
||||
signaled = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (signaled)
|
||||
{
|
||||
sema->cv.notify_one();
|
||||
}
|
||||
return signaled;
|
||||
};
|
||||
|
||||
if (sid == 1u && (rpcNum == 0x12u || rpcNum == 0x13u))
|
||||
{
|
||||
uint32_t responseWord = 1u;
|
||||
if (rpcNum == 0x13u)
|
||||
{
|
||||
static uint32_t sdrStateBlobAddr = 0u;
|
||||
if (sdrStateBlobAddr == 0u)
|
||||
{
|
||||
sdrStateBlobAddr = rpcAllocPacketAddr(rdram);
|
||||
if (sdrStateBlobAddr == 0u)
|
||||
{
|
||||
sdrStateBlobAddr = kRpcPacketPoolBase;
|
||||
}
|
||||
}
|
||||
|
||||
rpcZeroRdram(rdram, sdrStateBlobAddr, 64u);
|
||||
(void)writeRpcU32(sdrStateBlobAddr + 0u, 1u);
|
||||
responseWord = sdrStateBlobAddr;
|
||||
}
|
||||
|
||||
if (recvBuf && recvSize >= sizeof(uint32_t))
|
||||
{
|
||||
(void)writeRpcU32(recvBuf, responseWord);
|
||||
if (recvSize > sizeof(uint32_t))
|
||||
{
|
||||
rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t));
|
||||
}
|
||||
resultPtr = recvBuf;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
||||
if ((mode & kSifRpcModeNowait) != 0u)
|
||||
{
|
||||
(void)signalRpcCompletionSema(endParam);
|
||||
}
|
||||
}
|
||||
|
||||
if (recvBuf && recvSize > 0)
|
||||
{
|
||||
if (handled && resultPtr)
|
||||
@@ -780,7 +848,48 @@ void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
if (endFunc)
|
||||
{
|
||||
rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr);
|
||||
bool callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr);
|
||||
|
||||
// Some generated callsites may pass 0x2fac20/0x2fac30 instead of
|
||||
// 0x2eac20/0x2eac30 for sound-driver RPC callbacks.
|
||||
if (!callbackInvoked && (endFunc == 0x2fac20u || endFunc == 0x2fac30u))
|
||||
{
|
||||
const uint32_t normalizedEndFunc = endFunc - 0x10000u;
|
||||
callbackInvoked = rpcInvokeFunction(rdram, ctx, runtime, normalizedEndFunc, endParam, 0, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
// Guard against callback dispatch gaps that would leak the semaphore
|
||||
// acquired in SdrSendReq/SdrGetStateSend.
|
||||
const bool isSoundRpcCallback =
|
||||
(endFunc == 0x2eac20u || endFunc == 0x2eac30u ||
|
||||
endFunc == 0x2fac20u || endFunc == 0x2fac30u);
|
||||
if (isSoundRpcCallback)
|
||||
{
|
||||
(void)signalRpcCompletionSema(endParam);
|
||||
if (rdram && (endFunc == 0x2eac30u || endFunc == 0x2fac30u))
|
||||
{
|
||||
constexpr uint32_t kSndBusyFlagAddr = 0x01E212C8u;
|
||||
if (uint32_t *busy = reinterpret_cast<uint32_t *>(getMemPtr(rdram, kSndBusyFlagAddr)))
|
||||
{
|
||||
*busy = 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!callbackInvoked)
|
||||
{
|
||||
const bool fallbackSignaledSema = signalRpcCompletionSema(endParam);
|
||||
|
||||
static uint32_t unresolvedEndFuncWarnCount = 0;
|
||||
if (unresolvedEndFuncWarnCount < 32u)
|
||||
{
|
||||
std::cerr << "[SifCallRpc] unresolved end callback endFunc=0x" << std::hex << endFunc
|
||||
<< " endParam=0x" << endParam
|
||||
<< " fallbackSignal=" << std::dec << (fallbackSignaledSema ? 1 : 0)
|
||||
<< std::endl;
|
||||
++unresolvedEndFuncWarnCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int logCount = 0;
|
||||
@@ -1086,4 +1195,3 @@ void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
uint32_t queuePtr = getRegU32(ctx, 4);
|
||||
setReturnS32(ctx, static_cast<int32_t>(queuePtr));
|
||||
}
|
||||
|
||||
|
||||
@@ -45,29 +45,37 @@ static void runExitHandlersForThread(int tid, uint8_t *rdram, R5900Context *ctx,
|
||||
|
||||
void FlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
std::cerr << "Syscall: ResetEE - Halting Execution (Not fully implemented)" << std::endl;
|
||||
exit(0); // Should we exit or just halt the execution?
|
||||
std::cerr << "Syscall: ResetEE - requesting runtime stop" << std::endl;
|
||||
runtime->requestStop();
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void SetMemoryMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
uint32_t paramAddr = getRegU32(ctx, 4); // $a0 points to ThreadParam
|
||||
if (paramAddr == 0u)
|
||||
{
|
||||
std::cerr << "CreateThread error: null ThreadParam pointer" << std::endl;
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t *param = reinterpret_cast<const uint32_t *>(getConstMemPtr(rdram, paramAddr));
|
||||
|
||||
if (!param)
|
||||
{
|
||||
std::cerr << "CreateThread error: invalid ThreadParam address 0x" << std::hex << paramAddr << std::dec << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,12 +125,43 @@ void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
}
|
||||
|
||||
info->option = param[6];
|
||||
if (info->priority == 0)
|
||||
{
|
||||
info->priority = 1;
|
||||
}
|
||||
if (info->priority >= 128)
|
||||
{
|
||||
info->priority = 127;
|
||||
}
|
||||
info->currentPriority = static_cast<int>(info->priority);
|
||||
|
||||
int id = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_thread_map_mutex);
|
||||
id = g_nextThreadId++;
|
||||
// Keep IDs in the classic low range used by patched libkernel helpers.
|
||||
for (int attempts = 0; attempts < 0xFE; ++attempts)
|
||||
{
|
||||
if (g_nextThreadId < 2 || g_nextThreadId > 0xFF)
|
||||
{
|
||||
g_nextThreadId = 2;
|
||||
}
|
||||
|
||||
const int candidate = g_nextThreadId;
|
||||
g_nextThreadId = (g_nextThreadId >= 0xFF) ? 2 : (g_nextThreadId + 1);
|
||||
|
||||
if (g_threads.find(candidate) == g_threads.end())
|
||||
{
|
||||
id = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
g_threads[id] = info;
|
||||
}
|
||||
|
||||
@@ -139,6 +178,12 @@ void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
int tid = static_cast<int>(getRegU32(ctx, 4)); // $a0
|
||||
if (tid == 0)
|
||||
{
|
||||
setReturnS32(ctx, KE_ILLEGAL_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
auto info = lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
@@ -146,13 +191,22 @@ void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t autoStackToFree = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->status != THS_DORMANT)
|
||||
{
|
||||
setReturnS32(ctx, KE_NOT_WAIT); // for now
|
||||
setReturnS32(ctx, KE_NOT_DORMANT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->ownsStack && info->stack != 0)
|
||||
{
|
||||
autoStackToFree = info->stack;
|
||||
info->stack = 0;
|
||||
info->stackSize = 0;
|
||||
info->ownsStack = false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@@ -160,6 +214,16 @@ void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
g_threads.erase(tid);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_exit_handler_mutex);
|
||||
g_exit_handlers.erase(tid);
|
||||
}
|
||||
|
||||
if (runtime && autoStackToFree != 0)
|
||||
{
|
||||
runtime->guestFree(autoStackToFree);
|
||||
}
|
||||
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
@@ -167,32 +231,24 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
int tid = static_cast<int>(getRegU32(ctx, 4)); // $a0 = thread id
|
||||
uint32_t arg = getRegU32(ctx, 5); // $a1 = user arg
|
||||
if (tid == 0)
|
||||
{
|
||||
setReturnS32(ctx, KE_ILLEGAL_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
auto info = lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
std::cerr << "StartThread error: unknown thread id " << tid << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->started)
|
||||
{
|
||||
setReturnS32(ctx, tid); // Already started
|
||||
return;
|
||||
}
|
||||
|
||||
info->started = true;
|
||||
info->status = THS_RUN;
|
||||
info->arg = arg;
|
||||
}
|
||||
|
||||
if (!runtime->hasFunction(info->entry))
|
||||
if (!runtime || !runtime->hasFunction(info->entry))
|
||||
{
|
||||
std::cerr << "[StartThread] entry 0x" << std::hex << info->entry << std::dec << " is not registered" << std::endl;
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -201,12 +257,28 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->started || info->status != THS_DORMANT)
|
||||
{
|
||||
setReturnS32(ctx, KE_NOT_DORMANT);
|
||||
return;
|
||||
}
|
||||
|
||||
info->started = true;
|
||||
info->status = THS_READY;
|
||||
info->arg = arg;
|
||||
info->terminated = false;
|
||||
info->forceRelease = false;
|
||||
info->waitType = TSW_NONE;
|
||||
info->waitId = 0;
|
||||
info->wakeupCount = 0;
|
||||
info->suspendCount = 0;
|
||||
if (info->stack == 0 && info->stackSize != 0)
|
||||
{
|
||||
const uint32_t autoStack = runtime->guestMalloc(info->stackSize, 16u);
|
||||
if (autoStack != 0)
|
||||
{
|
||||
info->stack = autoStack;
|
||||
info->ownsStack = true;
|
||||
std::cout << "[StartThread] id=" << tid
|
||||
<< " auto-stack=0x" << std::hex << autoStack
|
||||
<< " size=0x" << info->stackSize << std::dec << std::endl;
|
||||
@@ -222,8 +294,9 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
}
|
||||
|
||||
g_activeThreads.fetch_add(1, std::memory_order_relaxed);
|
||||
std::thread([=]() mutable
|
||||
{
|
||||
try
|
||||
{
|
||||
std::thread worker([=]() mutable {
|
||||
{
|
||||
std::string name = "PS2Thread_" + std::to_string(tid);
|
||||
ThreadNaming::SetCurrentThreadName(name);
|
||||
@@ -231,6 +304,11 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
R5900Context threadCtxCopy{};
|
||||
R5900Context *threadCtx = &threadCtxCopy;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
info->status = THS_RUN;
|
||||
}
|
||||
|
||||
uint32_t threadSp = callerSp;
|
||||
if (info->stack)
|
||||
{
|
||||
@@ -250,7 +328,6 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
SET_GPR_U32(threadCtx, 31, 0);
|
||||
threadCtx->pc = info->entry;
|
||||
|
||||
PS2Runtime::RecompiledFunction func = runtime->lookupFunction(info->entry);
|
||||
g_currentThreadId = tid;
|
||||
|
||||
std::cout << "[StartThread] id=" << tid
|
||||
@@ -262,7 +339,43 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
bool exited = false;
|
||||
try
|
||||
{
|
||||
func(rdram, threadCtx, runtime);
|
||||
uint32_t lastPc = 0xFFFFFFFFu;
|
||||
uint32_t samePcCount = 0;
|
||||
constexpr uint32_t kSamePcYieldMask = 0x3FFFu;
|
||||
constexpr uint32_t kSamePcWarnInterval = 0x400000u;
|
||||
|
||||
while (runtime && !runtime->isStopRequested())
|
||||
{
|
||||
const uint32_t pc = threadCtx->pc;
|
||||
if (pc == 0u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (pc == lastPc)
|
||||
{
|
||||
++samePcCount;
|
||||
if ((samePcCount & kSamePcYieldMask) == 0u)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
if ((samePcCount % kSamePcWarnInterval) == 0u)
|
||||
{
|
||||
std::cout << "[StartThread] id=" << tid
|
||||
<< " spinning at pc=0x" << std::hex << pc
|
||||
<< " ra=0x" << GPR_U32(threadCtx, 31)
|
||||
<< std::dec << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
samePcCount = 0;
|
||||
lastPc = pc;
|
||||
}
|
||||
|
||||
PS2Runtime::RecompiledFunction step = runtime->lookupFunction(pc);
|
||||
step(rdram, threadCtx, runtime);
|
||||
}
|
||||
}
|
||||
catch (const ThreadExitException &)
|
||||
{
|
||||
@@ -281,17 +394,64 @@ void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
runExitHandlersForThread(tid, rdram, threadCtx, runtime);
|
||||
|
||||
uint32_t detachedAutoStack = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
info->started = false;
|
||||
info->status = THS_DORMANT;
|
||||
info->waitType = TSW_NONE;
|
||||
info->waitId = 0;
|
||||
info->wakeupCount = 0;
|
||||
info->suspendCount = 0;
|
||||
info->forceRelease = false;
|
||||
info->terminated = false;
|
||||
}
|
||||
|
||||
g_activeThreads.fetch_sub(1, std::memory_order_relaxed); })
|
||||
.detach();
|
||||
bool stillRegistered = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_thread_map_mutex);
|
||||
stillRegistered = (g_threads.find(tid) != g_threads.end());
|
||||
}
|
||||
if (!stillRegistered)
|
||||
{
|
||||
// ExitDeleteThread removes the record immediately; reclaim auto stack here.
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->ownsStack && info->stack != 0)
|
||||
{
|
||||
detachedAutoStack = info->stack;
|
||||
info->stack = 0;
|
||||
info->stackSize = 0;
|
||||
info->ownsStack = false;
|
||||
}
|
||||
}
|
||||
|
||||
// for now report success to the caller.
|
||||
setReturnS32(ctx, 0);
|
||||
if (detachedAutoStack != 0 && runtime)
|
||||
{
|
||||
runtime->guestFree(detachedAutoStack);
|
||||
}
|
||||
|
||||
g_activeThreads.fetch_sub(1, std::memory_order_relaxed);
|
||||
});
|
||||
worker.detach();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "[StartThread] failed to spawn host thread for tid=" << tid << ": " << e.what() << std::endl;
|
||||
g_activeThreads.fetch_sub(1, std::memory_order_relaxed);
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
info->started = false;
|
||||
info->status = THS_DORMANT;
|
||||
info->waitType = TSW_NONE;
|
||||
info->waitId = 0;
|
||||
info->wakeupCount = 0;
|
||||
info->suspendCount = 0;
|
||||
info->forceRelease = false;
|
||||
info->terminated = false;
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -350,12 +510,17 @@ void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->status == THS_DORMANT)
|
||||
{
|
||||
setReturnS32(ctx, KE_DORMANT);
|
||||
return;
|
||||
}
|
||||
info->terminated = true;
|
||||
info->forceRelease = true;
|
||||
info->status = THS_DORMANT;
|
||||
@@ -370,7 +535,7 @@ void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
runExitHandlersForThread(tid, rdram, ctx, runtime);
|
||||
throw ThreadExitException();
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -382,7 +547,7 @@ void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -390,7 +555,7 @@ void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->status == THS_DORMANT)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_DORMANT);
|
||||
return;
|
||||
}
|
||||
info->suspendCount++;
|
||||
@@ -410,7 +575,7 @@ void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
info->status = THS_RUN;
|
||||
}
|
||||
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -422,15 +587,20 @@ void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->status == THS_DORMANT)
|
||||
{
|
||||
setReturnS32(ctx, KE_DORMANT);
|
||||
return;
|
||||
}
|
||||
if (info->suspendCount <= 0)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_NOT_SUSPEND);
|
||||
return;
|
||||
}
|
||||
info->suspendCount--;
|
||||
@@ -447,7 +617,7 @@ void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
}
|
||||
}
|
||||
info->cv.notify_all();
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void GetThreadId(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -468,14 +638,14 @@ void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
ee_thread_status_t *status = reinterpret_cast<ee_thread_status_t *>(getMemPtr(rdram, statusAddr));
|
||||
if (!status)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -492,7 +662,7 @@ void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
status->waitType = info->waitType;
|
||||
status->waitId = info->waitId;
|
||||
status->wakeupCount = info->wakeupCount;
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -562,8 +732,13 @@ void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
setReturnS32(ctx, KE_ILLEGAL_THID);
|
||||
return;
|
||||
}
|
||||
if (tid == g_currentThreadId)
|
||||
{
|
||||
setReturnS32(ctx, KE_ILLEGAL_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
auto info = lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
@@ -597,7 +772,7 @@ void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
info->wakeupCount++;
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void iWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
@@ -614,7 +789,7 @@ void CancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
if (!info)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -661,39 +836,66 @@ void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime
|
||||
tid = g_currentThreadId;
|
||||
|
||||
auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid);
|
||||
if (info)
|
||||
if (!info)
|
||||
{
|
||||
int oldPrio = info->currentPriority;
|
||||
setReturnS32(ctx, KE_UNKNOWN_THID);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->status == THS_DORMANT)
|
||||
{
|
||||
setReturnS32(ctx, KE_DORMANT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPrio == 0)
|
||||
{
|
||||
newPrio = (info->currentPriority > 0) ? info->currentPriority : 1;
|
||||
}
|
||||
if (newPrio <= 0 || newPrio >= 128)
|
||||
{
|
||||
setReturnS32(ctx, KE_ILLEGAL_PRIORITY);
|
||||
return;
|
||||
}
|
||||
|
||||
info->currentPriority = newPrio;
|
||||
setReturnS32(ctx, oldPrio); // Return old priority?
|
||||
}
|
||||
else
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
}
|
||||
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
static int logCount = 0;
|
||||
int prio = static_cast<int>(getRegU32(ctx, 4));
|
||||
if (prio == 0)
|
||||
{
|
||||
auto current = ensureCurrentThreadInfo(ctx);
|
||||
if (current)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(current->m);
|
||||
prio = (current->currentPriority > 0) ? current->currentPriority : 1;
|
||||
}
|
||||
}
|
||||
if (logCount < 16)
|
||||
{
|
||||
std::cout << "[RotateThreadReadyQueue] prio=" << prio << std::endl;
|
||||
++logCount;
|
||||
}
|
||||
if (prio >= 128)
|
||||
if (prio <= 0 || prio >= 128)
|
||||
{
|
||||
setReturnS32(ctx, -1);
|
||||
setReturnS32(ctx, KE_ILLEGAL_PRIORITY);
|
||||
return;
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
{
|
||||
int tid = static_cast<int>(getRegU32(ctx, 4));
|
||||
if (tid == 0)
|
||||
if (tid == 0 || tid == g_currentThreadId)
|
||||
{
|
||||
setReturnS32(ctx, KE_ILLEGAL_THID);
|
||||
return;
|
||||
@@ -712,7 +914,7 @@ void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(info->m);
|
||||
if (info->status == THS_WAIT)
|
||||
if (info->status == THS_WAIT || info->status == THS_WAITSUSPEND)
|
||||
{
|
||||
wasWaiting = true;
|
||||
waitType = info->waitType;
|
||||
@@ -755,7 +957,7 @@ void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
eventFlag->cv.notify_all();
|
||||
}
|
||||
}
|
||||
setReturnS32(ctx, 0);
|
||||
setReturnS32(ctx, KE_OK);
|
||||
}
|
||||
|
||||
void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
||||
|
||||
Reference in New Issue
Block a user