Files
wiicompiled/runtime/src/hle/task_thread.cpp
T
patchzyy ec226e8348 init
2026-08-23 17:10:50 +02:00

210 lines
7.3 KiB
C++

#include "hle_stubs.h"
#include "abi_bridge.h"
#include "ppc_runtime.h"
#include "memory.h"
#include "runtime_log.h"
namespace {
constexpr uint32_t kTaskMessageQueueOffset = 0x0Cu;
constexpr uint32_t kTaskCurrentJobOffset = 0x48u;
constexpr uint32_t kTaskDoneQueueOffset = 0x54u;
constexpr uint32_t kJobCallbackOffset = 0x00u;
constexpr uint32_t kJobArgOffset = 0x04u;
constexpr uint32_t kJobTokenOffset = 0x08u;
constexpr uint32_t kJobUnknown3Offset = 0x0Cu;
constexpr uint32_t kJobUnknown4Offset = 0x10u;
constexpr uint32_t kJobOnDoneOffset = 0x14u;
constexpr uint32_t kJobSize = 0x18u;
bool ShouldRetryThpPrepareAfterClose(uint32_t callback, uint32_t arg, uint32_t result)
{
if (callback != 0x80529D68u || result != 0) {
return false;
}
if (arg == 0 || !Memory::Contains(arg + 0xACu, 4)) {
return false;
}
const uint32_t state = Memory::Read32(arg + 0xACu);
if (state != 0) {
return false;
}
if (!Memory::Contains(0x809BECF0u, 4) || !Memory::Contains(0x809BEBA0u, 4)) {
return false;
}
const uint32_t thpInit = Memory::Read32(0x809BECF0u);
const uint32_t thpOpen = Memory::Read32(0x809BEBA0u);
return thpInit != 0 && thpOpen != 0;
}
uint32_t RunMovieManagerPrepareAsync(uint32_t manager, CpuContext* cpu)
{
if (!cpu || manager == 0 || !Memory::Contains(manager + 0xACu, 4)) {
return 0;
}
if (Memory::Read32(manager + 0xACu) != 0) {
return cpu->gpr[3];
}
auto callThpPlayerOpen = [&]() -> uint32_t {
CpuContextScope scope(cpu);
cpu->gpr[3] = manager + 0x28u;
cpu->gpr[4] = 0;
InvokeIndirectCpu(0x80550CC0u, cpu);
return cpu->gpr[3];
};
uint32_t openResult = callThpPlayerOpen();
if (openResult == 0 && ShouldRetryThpPrepareAfterClose(0x80529D68u, manager, 0)) {
{
CpuContextScope scope(cpu);
InvokeIndirectCpu(0x80551658u, cpu); // THP__PlayerStop
InvokeIndirectCpu(0x80550F48u, cpu); // THP__PlayerClose
}
if (Memory::Contains(0x809BEBA0u, 8) && Memory::Read32(0x809BEBA0u) != 0) {
Memory::Write32(0x809BEBA0u, 0);
Memory::Write8(0x809BEBA4u, 0);
Memory::Write8(0x809BEBA5u, 0);
Memory::Write8(0x809BEBA6u, 0);
Memory::Write8(0x809BEBA7u, 0);
}
openResult = callThpPlayerOpen();
}
if (openResult == 0) {
cpu->gpr[3] = 0;
return 0;
}
{
CpuContextScope scope(cpu);
cpu->gpr[3] = manager + 0x10u;
InvokeIndirectCpu(0x80551D38u, cpu); // THP__PlayerGetVideoInfo
InvokeIndirectCpu(0x80550F9Cu, cpu); // THP__PlayerCalcNeedMemory
}
const uint32_t needMemory = cpu->gpr[3];
Memory::Write32(manager + 0x20u, needMemory);
Memory::Write32(manager + 0x24u, 0);
const uint32_t setBufferArg = Memory::Read32(manager + 0x1Cu);
{
CpuContextScope scope(cpu);
cpu->gpr[3] = setBufferArg;
InvokeIndirectCpu(0x80551054u, cpu); // THP__PlayerSetBuffer
}
const uint32_t mode = Memory::Read32(manager + 0xA8u);
const uint32_t audioSystem = Memory::Read32(0x8088FDB8u + (mode << 2));
uint32_t prepareResult = 0;
{
CpuContextScope scope(cpu);
cpu->gpr[3] = Memory::Read32(manager + 0x24u);
cpu->gpr[4] = audioSystem;
cpu->gpr[5] = 0;
InvokeIndirectCpu(0x80551378u, cpu); // THP__PlayerPrepare
prepareResult = cpu->gpr[3];
}
if (prepareResult != 0) {
Memory::Write32(manager + 0xACu, 1);
}
cpu->gpr[3] = prepareResult;
return prepareResult;
}
void ClearTaskJob(uint32_t taskThread, uint32_t job)
{
if (job == 0 || !Memory::Contains(job, kJobSize)) {
Memory::Write32(taskThread + kTaskCurrentJobOffset, 0);
return;
}
Memory::Write32(job + kJobCallbackOffset, 0);
Memory::Write32(taskThread + kTaskCurrentJobOffset, 0);
Memory::Write32(job + kJobCallbackOffset, 0);
Memory::Write32(job + kJobUnknown3Offset, 0);
Memory::Write32(job + kJobUnknown4Offset, 0);
Memory::Write32(job + kJobOnDoneOffset, 0);
}
} // namespace
// HLE override for TaskThread_run (0x80242D7C).
// This mirrors the worker loop from the original implementation so job slots
// are retired exactly when the callback has finished.
extern "C" void TaskThread_run_HLE_80242d7c(CpuContext* ctx) {
CpuContext* cpu = ctx ? ctx : &GetPersistentCpuContext();
const uint32_t taskThread = cpu->gpr[3];
if (!Memory::Contains(taskThread + kTaskMessageQueueOffset, 0x4Cu)) {
return;
}
// Match the original PPC prologue: TaskThread::Run seeds GQR2-GQR5 before
// servicing callbacks. Some worker callbacks rely on these quantization
// registers being initialized on the task thread.
cpu->gqr[2] = 0x00040004u;
cpu->gqr[3] = 0x00050005u;
cpu->gqr[4] = 0x00060006u;
cpu->gqr[5] = 0x00070007u;
while (true) {
const uint32_t stackPointer = cpu->gpr[1];
const uint32_t outMsgPtr = stackPointer >= 0x20u ? (stackPointer - 0x20u) : 0;
if (outMsgPtr == 0 || !Memory::Contains(outMsgPtr, 4)) {
RT_LOG(RT_TAG_HLE) << "TaskThread_run: invalid guest stack scratch for task thread 0x"
<< std::hex << taskThread << std::dec << std::endl;
return;
}
{
CpuContextScope scope(cpu);
cpu->gpr[3] = taskThread + kTaskMessageQueueOffset;
cpu->gpr[4] = outMsgPtr;
cpu->gpr[5] = 1; // OS_MESSAGE_BLOCK
InvokeIndirectCpu(0x801A7424u, cpu); // OSReceiveMessage
}
const uint32_t job = Memory::Read32(outMsgPtr);
Memory::Write32(taskThread + kTaskCurrentJobOffset, job);
if (job != 0 && Memory::Contains(job, kJobSize)) {
const uint32_t callback = Memory::Read32(job + kJobCallbackOffset);
const uint32_t arg = Memory::Read32(job + kJobArgOffset);
const uint32_t token = Memory::Read32(job + kJobTokenOffset);
const uint32_t onDone = Memory::Read32(job + kJobOnDoneOffset);
if (callback != 0) {
CpuContextScope scope(cpu);
if (callback == 0x80529D68u) {
RunMovieManagerPrepareAsync(arg, cpu);
} else {
cpu->gpr[3] = arg;
InvokeIndirectCpu(callback, cpu);
}
}
const uint32_t currentJob = Memory::Read32(taskThread + kTaskCurrentJobOffset);
if (currentJob != 0 && onDone != 0) {
CpuContextScope scope(cpu);
cpu->gpr[3] = Memory::Read32(currentJob + kJobArgOffset);
InvokeIndirectCpu(onDone, cpu);
}
if (Memory::Contains(taskThread + kTaskDoneQueueOffset, 4)) {
const uint32_t doneQueue = Memory::Read32(taskThread + kTaskDoneQueueOffset);
if (doneQueue != 0) {
CpuContextScope scope(cpu);
cpu->gpr[3] = doneQueue;
cpu->gpr[4] = token;
cpu->gpr[5] = 0; // OS_MESSAGE_NOBLOCK
InvokeIndirectCpu(0x801A735Cu, cpu); // OSSendMessage
}
}
}
ClearTaskJob(taskThread, job);
}
}
PPC_NATIVE_OVERRIDE_VOID(80242D7C, TaskThread_run_HLE_80242d7c, (CpuContext* ctx), (ctx));