Set up the basics.

This commit is contained in:
Henrik Rydgård 2025-01-19 19:40:18 +01:00
parent bb93ab697a
commit 6732d54742
2 changed files with 110 additions and 45 deletions

View file

@ -20,6 +20,8 @@
#include <vector>
#include <string>
#include "Common/Math/CrossSIMD.h"
#include "Common/Profiler/Profiler.h"
#include "Common/Log.h"
@ -67,8 +69,14 @@ static std::vector<HLEModule> moduleDB;
static int delayedResultEvent = -1;
static int hleAfterSyscall = HLE_AFTER_NOTHING;
static const char *hleAfterSyscallReschedReason;
static const HLEFunction *latestSyscall = nullptr;
static uint32_t latestSyscallPC = 0;
#define MAX_SYSCALL_RECURSION 16
// Keep track of syscalls in flight. Note that they can call each other! But they'll have to use hleCall<>.
static const HLEFunction *g_stack[MAX_SYSCALL_RECURSION];
u32 g_syscallPC;
int g_stackSize;
static int idleOp;
// Split syscall support. NOTE: This needs to be saved in DoState somehow!
@ -105,8 +113,7 @@ static std::vector<HLEMipsCallInfo> enqueuedMipsCalls;
// Does need to be saved, referenced by the stack and owned.
static std::vector<PSPAction *> mipsCallActions;
void hleDelayResultFinish(u64 userdata, int cycleslate)
{
void hleDelayResultFinish(u64 userdata, int cycleslate) {
u32 error;
SceUID threadID = (SceUID) userdata;
SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error);
@ -114,8 +121,7 @@ void hleDelayResultFinish(u64 userdata, int cycleslate)
// We can't just put it all in userdata because we need to know the threadID...
u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error);
if (error == 0 && verify == 1)
{
if (error == 0 && verify == 1) {
__KernelResumeThreadFromWait(threadID, result);
__KernelReSchedule("woke from hle delay");
}
@ -134,9 +140,12 @@ void HLEDoState(PointerWrap &p) {
if (!s)
return;
// Can't be inside a syscall, reset this so errors aren't misleading.
latestSyscall = nullptr;
latestSyscallPC = 0;
// Can't be inside a syscall when saving state, reset this so errors aren't misleading.
if (g_stackSize) {
ERROR_LOG(Log::HLE, "Can't save state while in a HLE syscall");
}
g_stackSize = 0;
Do(p, delayedResultEvent);
CoreTiming::RestoreRegisterEvent(delayedResultEvent, "HLEDelayedResult", hleDelayResultFinish);
@ -161,8 +170,7 @@ void HLEDoState(PointerWrap &p) {
void HLEShutdown() {
hleAfterSyscall = HLE_AFTER_NOTHING;
latestSyscall = nullptr;
latestSyscallPC = 0;
g_stackSize = 0;
moduleDB.clear();
enqueuedMipsCalls.clear();
for (auto p : mipsCallActions) {
@ -385,17 +393,20 @@ static bool hleExecuteDebugBreak(const HLEFunction *func) {
}
INFO_LOG(Log::CPU, "Broke after syscall: %s", func->name);
Core_Break("hle.step", latestSyscallPC);
Core_Break("hle.step", g_syscallPC);
return true;
}
u32 hleDelayResult(u32 result, const char *reason, int usec) {
_dbg_assert_(g_stackSize > 0);
_dbg_assert_(g_stackSize == 1);
if (!__KernelIsDispatchEnabled()) {
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?");
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", g_stackSize ? g_stack[0]->name : "?");
} else {
SceUID thread = __KernelGetCurThread();
if (KernelIsThreadWaiting(thread))
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?");
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", g_stackSize ? g_stack[0]->name : "?");
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, thread);
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason);
}
@ -403,12 +414,16 @@ u32 hleDelayResult(u32 result, const char *reason, int usec) {
}
u64 hleDelayResult(u64 result, const char *reason, int usec) {
_dbg_assert_(g_stackSize > 0);
_dbg_assert_(g_stackSize == 1);
if (!__KernelIsDispatchEnabled()) {
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", latestSyscall ? latestSyscall->name : "?");
WARN_LOG(Log::HLE, "%s: Dispatch disabled, not delaying HLE result (right thing to do?)", g_stackSize ? g_stack[0]->name : "?");
} else {
// TODO: Defer this, so you can call this multiple times, in case of syscalls calling syscalls? Although, return values are tricky.
SceUID thread = __KernelGetCurThread();
if (KernelIsThreadWaiting(thread))
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", latestSyscall ? latestSyscall->name : "?");
ERROR_LOG(Log::HLE, "%s: Delaying a thread that's already waiting", g_stackSize ? g_stack[0]->name : "?");
u64 param = (result & 0xFFFFFFFF00000000) | thread;
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param);
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32)result, 0, false, reason);
@ -434,7 +449,7 @@ void hleEatMicro(int usec) {
}
bool hleIsKernelMode() {
return latestSyscall && (latestSyscall->flags & HLE_KERNEL_SYSCALL) != 0;
return g_stackSize && (g_stack[0]->flags & HLE_KERNEL_SYSCALL) != 0;
}
void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction) {
@ -450,7 +465,8 @@ void hleEnqueueCall(u32 func, int argc, const u32 *argv, PSPAction *afterAction)
void hleFlushCalls() {
u32 &sp = currentMIPS->r[MIPS_REG_SP];
PSPPointer<HLEMipsCallStack> stackData;
VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), latestSyscall ? latestSyscall->name : "?", sp);
_dbg_assert_(g_stackSize > 0);
VERBOSE_LOG(Log::HLE, "Flushing %d HLE mips calls from %s, sp=%08x", (int)enqueuedMipsCalls.size(), g_stackSize ? g_stack[0]->name : "?", sp);
// First, we'll add a marker for the final return.
sp -= sizeof(HLEMipsCallStack);
@ -579,7 +595,7 @@ inline static void SetDeadbeefRegs()
return;
currentMIPS->r[MIPS_REG_COMPILER_SCRATCH] = 0xDEADBEEF;
// Set all the arguments and temp regs.
// Set all the arguments and temp regs. TODO: Use SIMD to just do three writes.
memcpy(&currentMIPS->r[MIPS_REG_A0], deadbeefRegs, sizeof(deadbeefRegs));
currentMIPS->r[MIPS_REG_T8] = 0xDEADBEEF;
currentMIPS->r[MIPS_REG_T9] = 0xDEADBEEF;
@ -678,10 +694,12 @@ static void updateSyscallStats(int modulenum, int funcnum, double total)
}
}
inline void CallSyscallWithFlags(const HLEFunction *info)
{
latestSyscall = info;
latestSyscallPC = currentMIPS->pc;
static void CallSyscallWithFlags(const HLEFunction *info) {
_dbg_assert_(g_stackSize == 0);
g_stack[g_stackSize++] = info;
g_syscallPC = currentMIPS->pc;
const u32 flags = info->flags;
if (flags & HLE_CLEAR_STACK_BYTES) {
@ -699,25 +717,37 @@ inline void CallSyscallWithFlags(const HLEFunction *info)
info->func();
}
// Now, g_stackSize should be back to 0.
// _dbg_assert_(g_stackSize == 0);
if (hleAfterSyscall != HLE_AFTER_NOTHING)
hleFinishSyscall(info);
else
SetDeadbeefRegs();
g_stackSize = 0;
}
inline void CallSyscallWithoutFlags(const HLEFunction *info)
{
latestSyscall = info;
latestSyscallPC = currentMIPS->pc;
static void CallSyscallWithoutFlags(const HLEFunction *info) {
_dbg_assert_(g_stackSize == 0);
g_stack[g_stackSize++] = info;
g_syscallPC = currentMIPS->pc;
info->func();
// Now, g_stackSize should be back to 0.
// _dbg_assert_(g_stackSize == 0);
if (hleAfterSyscall != HLE_AFTER_NOTHING)
hleFinishSyscall(info);
else
SetDeadbeefRegs();
g_stackSize = 0;
}
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op)
const HLEFunction *GetSyscallFuncPointer(MIPSOpcode op)
{
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
int funcnum = callno & 0xFFF;
@ -768,6 +798,7 @@ void CallSyscall(MIPSOpcode op) {
const HLEFunction *info = GetSyscallFuncPointer(op);
if (!info) {
// We haven't incremented the stack yet.
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
return;
}
@ -779,8 +810,8 @@ void CallSyscall(MIPSOpcode op) {
CallSyscallWithFlags(info);
else
CallSyscallWithoutFlags(info);
}
else {
} else {
// We haven't incremented the stack yet.
RETURN(SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED);
ERROR_LOG_REPORT(Log::HLE, "Unimplemented HLE function %s", info->name ? info->name : "(\?\?\?)");
}
@ -900,20 +931,35 @@ size_t hleFormatLogArgs(char *message, size_t sz, const char *argmask) {
return used;
}
void hleLeave() {
_dbg_assert_(g_stackSize > 0);
if (g_stackSize > 0) {
g_stackSize--;
} // else warn?
}
void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, char retmask, const char *reason, const char *formatted_reason) {
char formatted_args[4096];
const char *funcName = "?";
u32 funcFlags = 0;
if (latestSyscall) {
_dbg_assert_(latestSyscall->argmask != nullptr);
hleFormatLogArgs(formatted_args, sizeof(formatted_args), latestSyscall->argmask);
if (!g_stackSize) {
ERROR_LOG(Log::HLE, "HLE function stack mismatch!");
return;
}
const HLEFunction *hleFunc = g_stack[g_stackSize - 1];
if (g_stackSize) {
_dbg_assert_(hleFunc->argmask != nullptr);
hleFormatLogArgs(formatted_args, sizeof(formatted_args), hleFunc->argmask);
// This acts as an override (for error returns which are usually hex.)
if (retmask == '\0')
retmask = latestSyscall->retmask;
retmask = hleFunc->retmask;
funcName = latestSyscall->name;
funcFlags = latestSyscall->flags;
funcName = hleFunc->name;
funcFlags = hleFunc->flags;
} else {
strcpy(formatted_args, "?");
}

View file

@ -92,7 +92,7 @@ struct Syscall {
#define PARAM(n) currentMIPS->r[MIPS_REG_A0 + n]
#define PARAM64(n) (currentMIPS->r[MIPS_REG_A0 + n] | ((u64)currentMIPS->r[MIPS_REG_A0 + n + 1] << 32))
#define PARAMF(n) currentMIPS->f[12 + n]
#define RETURN(n) currentMIPS->r[MIPS_REG_V0] = n
#define RETURN(n) currentMIPS->r[MIPS_REG_V0] = n;
#define RETURN64(n) {u64 RETURN64_tmp = n; currentMIPS->r[MIPS_REG_V0] = RETURN64_tmp & 0xFFFFFFFF; currentMIPS->r[MIPS_REG_V1] = RETURN64_tmp >> 32;}
#define RETURNF(fl) currentMIPS->f[0] = fl
@ -131,6 +131,10 @@ u64 hleDelayResult(u64 result, const char *reason, int usec);
void hleEatCycles(int cycles);
void hleEatMicro(int usec);
// Don't manually call this, it's called by the various syscall return macros.
// This should only be called once per syscall!
void hleLeave();
void hleCoreTimingForceCheck();
// Causes the syscall to not fully execute immediately, instead give the Ge a chance to
@ -169,13 +173,16 @@ void *GetQuickSyscallFunc(MIPSOpcode op);
void hleDoLogInternal(Log t, LogLevel level, u64 res, const char *file, int line, const char *reportTag, char retmask, const char *reason, const char *formatted_reason);
template <typename T>
template <bool leave, typename T>
[[nodiscard]]
#ifdef __GNUC__
__attribute__((format(printf, 8, 9)))
#endif
T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag, char retmask, const char *reasonFmt, ...) {
if ((int)level > MAX_LOGLEVEL || !GenericLogEnabled(level, t)) {
if (leave) {
hleLeave();
}
return res;
}
@ -198,13 +205,19 @@ T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char
fmtRes = (s64)res;
}
hleDoLogInternal(t, level, fmtRes, file, line, reportTag, retmask, reasonFmt, formatted_reason);
if (leave) {
hleLeave();
}
return res;
}
template <typename T>
template <bool leave, typename T>
[[nodiscard]]
T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char *reportTag, char retmask) {
if (((int)level > MAX_LOGLEVEL || !GenericLogEnabled(level, t)) && !reportTag) {
if (leave) {
hleLeave();
}
return res;
}
@ -216,16 +229,22 @@ T hleDoLog(Log t, LogLevel level, T res, const char *file, int line, const char
fmtRes = (s64)res;
}
hleDoLogInternal(t, level, fmtRes, file, line, reportTag, retmask, nullptr, "");
if (leave) {
hleLeave();
}
return res;
}
// These will become important later.
// These unwind the log stack.
template <typename T>
[[nodiscard]]
inline T hleNoLog(T t) {
hleLeave();
return t;
}
inline void hleNoLogVoid() {}
inline void hleNoLogVoid() {
hleLeave();
}
// This is just a quick way to force logging to be more visible for one file.
#ifdef HLE_LOG_FORCE
@ -244,7 +263,7 @@ inline void hleNoLogVoid() {}
// IMPORTANT: These *must* only be used directly in HLE functions. They cannot be used by utility functions
// called by them. Use regular ERROR_LOG etc for those.
#define hleLogHelper(t, level, res, retmask, ...) hleDoLog(t, level, res, __FILE__, __LINE__, nullptr, retmask, ##__VA_ARGS__)
#define hleLogHelper(t, level, res, retmask, ...) hleDoLog<true>(t, level, res, __FILE__, __LINE__, nullptr, retmask, ##__VA_ARGS__)
#define hleLogError(t, res, ...) hleLogHelper(t, LogLevel::LERROR, res, 'x', ##__VA_ARGS__)
#define hleLogWarning(t, res, ...) hleLogHelper(t, LogLevel::LWARNING, res, 'x', ##__VA_ARGS__)
#define hleLogVerbose(t, res, ...) hleLogHelper(t, HLE_LOG_LVERBOSE, res, 'x', ##__VA_ARGS__)
@ -262,6 +281,6 @@ inline void hleNoLogVoid() {}
#define hleLogSuccessVerboseX(t, res, ...) hleLogHelper(t, HLE_LOG_LVERBOSE, res, 'x', ##__VA_ARGS__)
#define hleLogSuccessVerboseI(t, res, ...) hleLogHelper(t, HLE_LOG_LVERBOSE, res, 'i', ##__VA_ARGS__)
#define hleReportError(t, res, ...) hleDoLog(t, LogLevel::LERROR, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)
#define hleReportWarning(t, res, ...) hleDoLog(t, LogLevel::LWARNING, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)
#define hleReportDebug(t, res, ...) hleDoLog(t, HLE_LOG_LDEBUG, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)
#define hleReportError(t, res, ...) hleDoLog<true>(t, LogLevel::LERROR, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)
#define hleReportWarning(t, res, ...) hleDoLog<true>(t, LogLevel::LWARNING, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)
#define hleReportDebug(t, res, ...) hleDoLog<true>(t, HLE_LOG_LDEBUG, res, __FILE__, __LINE__, "", 'x', ##__VA_ARGS__)