mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Set up the basics.
This commit is contained in:
parent
bb93ab697a
commit
6732d54742
2 changed files with 110 additions and 45 deletions
118
Core/HLE/HLE.cpp
118
Core/HLE/HLE.cpp
|
@ -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(¤tMIPS->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, "?");
|
||||
}
|
||||
|
|
|
@ -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__)
|
||||
|
|
Loading…
Add table
Reference in a new issue