From bafdb4512835bc2d612b26e76bb49acb4b160a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Oct 2026 22:54:27 +0200 Subject: [PATCH] ME: Syscalls to the firmware's cache functions do nothing Kernel code the ME runs calls a few firmware functions directly: POPS's ME loop calls sceKernelDcacheWritebackInvalidateAll for every sample. On a PSP the loader links kernel imports as direct jumps, so the ME runs the function's code; here they're HLE syscall stubs. The cache functions only work on the calling CPU's caches, and the ME's aren't emulated, so they return 0 and do nothing, as does an unresolved import (as on the main CPU). Others are still an error, now naming the function. The IR ends the block after one, as for a syscall on the main CPU: a jr with it in its delay slot leaves the exit to it, and the ME ran on into the next stub. A syscall instruction of the ME's own should take its exception vector (ExcCode 8); none has come up yet. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/MIPS/IR/IRCompBranch.cpp | 18 ++++++++++++++++-- Core/MIPS/Interpreter.cpp | 15 ++++++++++++--- Core/MIPS/MeCore.cpp | 20 ++++++++++++++++++++ Core/MIPS/MeCore.h | 5 +++++ 4 files changed, 53 insertions(+), 5 deletions(-) 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);