diff --git a/Core/MIPS/IR/IRCompBranch.cpp b/Core/MIPS/IR/IRCompBranch.cpp index dd196c0af6..3c7344303c 100644 --- a/Core/MIPS/IR/IRCompBranch.cpp +++ b/Core/MIPS/IR/IRCompBranch.cpp @@ -24,6 +24,7 @@ #include "Core/HLE/HLETables.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MeCore.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/MIPS/MIPSAnalyst.h" #include "Core/MIPS/MIPSTables.h" @@ -464,8 +465,21 @@ void IRFrontend::Comp_JumpReg(MIPSOpcode op) { void IRFrontend::Comp_Syscall(MIPSOpcode op) { if (IsMediaEngine()) { - // There's no HLE on the ME, the interpreter traps it. - Comp_Generic(op); + // There's no HLE on the ME. A call to a stub that does nothing there returns 0, ending the + // block as a syscall does on the main CPU (a jr with it in its delay slot leaves the exit to + // it); the interpreter traps the rest. + if (!MeCore::SyscallDoesNothing(op, GetCompilerPC())) { + Comp_Generic(op); + return; + } + ir.Write(IROp::Downcount, 0, 0, 0, js.downcountAmount); + js.downcountAmount = 0; + if (!js.inDelaySlot) + ir.Write(IROp::SetPCConst, 0, 0, 0, GetCompilerPC() + 4); + ir.WriteSetConstant(MIPS_REG_V0, 0); + FlushAll(); + ir.Write(IROp::ExitToPC); + js.compiling = false; return; } // Like the interpreter, the syscall sees every earlier instruction charged (including a branch it's diff --git a/Core/MIPS/Interpreter.cpp b/Core/MIPS/Interpreter.cpp index 92cd84ecaa..9b8fe9b630 100644 --- a/Core/MIPS/Interpreter.cpp +++ b/Core/MIPS/Interpreter.cpp @@ -183,9 +183,18 @@ namespace MIPSInt { void Int_Syscall(MIPSState *mips, MIPSOpcode op) { if (mips->IsMediaEngine()) { - // There's no HLE on the ME. TODO: Its exception vector. - ERROR_LOG(Log::ME, "syscall on the Media Engine at %08x", mips->pc); - Core_ExecException(mips->pc, mips->pc, ExecExceptionType::ILLEGAL); + // TODO: Its exception vector. + if (!MeCore::SyscallDoesNothing(op, mips->pc)) { + Core_ExecException(mips->pc, mips->pc, ExecExceptionType::ILLEGAL); + return; + } + mips->r[MIPS_REG_V0] = 0; + if (mips->inDelaySlot) { + mips->pc = mips->nextPC; + } else { + mips->pc += 4; + } + mips->inDelaySlot = false; return; } const u32 syscallPC = mips->pc - 4; diff --git a/Core/MIPS/MeCore.cpp b/Core/MIPS/MeCore.cpp index 47a238aef2..b4dd5d00e8 100644 --- a/Core/MIPS/MeCore.cpp +++ b/Core/MIPS/MeCore.cpp @@ -27,6 +27,7 @@ #include "Core/CoreTiming.h" #include "Core/ELF/PrxDecrypter.h" #include "Core/FileSystems/MetaFileSystem.h" +#include "Core/HLE/HLE.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/MemMap.h" @@ -552,6 +553,25 @@ static void RunUntilIdle(MEState *me) { } } +bool SyscallDoesNothing(MIPSOpcode op, u32 pc) { + static const char *const cacheFunctions[] = { + "sceKernelDcacheWritebackAll", + "sceKernelDcacheWritebackInvalidateAll", + "sceKernelDcacheWritebackRange", + "sceKernelDcacheWritebackInvalidateRange", + "sceKernelDcacheInvalidateRange", + }; + const HLEFunction *func = GetSyscallFunctionData(op, pc); + if (!func) + return true; + for (const char *name : cacheFunctions) { + if (!strcmp(func->name, name)) + return true; + } + ERROR_LOG(Log::ME, "syscall to %s on the Media Engine at %08x", func->name, pc); + return false; +} + void SetReset(bool asserted) { MEState *me = g_meState; if (asserted) { diff --git a/Core/MIPS/MeCore.h b/Core/MIPS/MeCore.h index 1e4e715d1b..9048f1a5f1 100644 --- a/Core/MIPS/MeCore.h +++ b/Core/MIPS/MeCore.h @@ -112,6 +112,11 @@ u64 GetTicks(); u32 ReadCop0(int reg); void WriteCop0(int reg, u32 value); void Halt(); +// Whether a syscall the ME makes is to a firmware function it may call, all of which do nothing on +// it: kernel code it runs (POPS's ME loop) calls the cache functions, which only work on the calling +// CPU's caches, and those aren't emulated. An unresolved import does nothing, as on the main CPU. +// Logs the others. +bool SyscallDoesNothing(MIPSOpcode op, u32 pc); // mtic. void SetInterruptEnable(u32 value);