mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-21 18:29:05 +02:00
sceKernelRegisterIntrHandler, sceKernelReleaseIntrHandler, sceKernelEnableIntr and sceKernelDisableIntr now keep a base handler per interrupt, and return what tests/intr/kernel/intrhandler recorded on a PSP: 80020065 for an interrupt number out of range (or no handler), 80020067 for a second handler, 80020068 for one that isn't there. Faking success for these used to stall the VSH boot, which is why they were left unresolved. It doesn't anymore: the firmware's drivers make the same 31 calls (interrupts 4, 12, 15-18, 20-24 and 31), and 6.61 reaches the XMB on the JIT and the interpreter. Nothing raises those interrupts yet, so the handlers are only kept, and saved in savestates (IntrHandler section version 2). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1126 lines
40 KiB
C++
1126 lines
40 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#include <algorithm>
|
|
#include <list>
|
|
#include <map>
|
|
#include <string>
|
|
|
|
#include "Common/Serialize/Serializer.h"
|
|
#include "Common/Serialize/SerializeFuncs.h"
|
|
#include "Common/Serialize/SerializeList.h"
|
|
#include "Common/Serialize/SerializeMap.h"
|
|
#include "Core/MemMapHelpers.h"
|
|
#include "Core/Reporting.h"
|
|
#include "Core/HLE/HLE.h"
|
|
#include "Core/HLE/FunctionWrappers.h"
|
|
#include "Core/HLE/ErrorCodes.h"
|
|
#include "Core/MIPS/MIPS.h"
|
|
|
|
#include "Core/Debugger/MemBlockInfo.h"
|
|
#include "Core/HLE/sceKernel.h"
|
|
#include "Core/HLE/sceKernelThread.h"
|
|
#include "Core/HLE/sceKernelInterrupt.h"
|
|
#include "Core/HLE/sceKernelMemory.h"
|
|
#include "Core/HLE/sceKernelMutex.h"
|
|
|
|
#include "GPU/GPUCommon.h"
|
|
#include "GPU/GPUState.h"
|
|
|
|
// Seems like some > 16 are taken but not available. Probably kernel only?
|
|
static const u32 PSP_NUMBER_SUBINTERRUPTS = 32;
|
|
|
|
// InterruptsManager
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// INTERRUPT MANAGEMENT
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class InterruptState {
|
|
public:
|
|
void save();
|
|
void restore();
|
|
void clear();
|
|
|
|
void DoState(PointerWrap &p) {
|
|
auto s = p.Section("InterruptState", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
Do(p, savedCpu);
|
|
}
|
|
|
|
PSPThreadContext savedCpu;
|
|
};
|
|
|
|
// STATE
|
|
|
|
InterruptState intState;
|
|
IntrHandler* intrHandlers[PSP_NUMBER_INTERRUPTS];
|
|
std::list<PendingInterrupt> pendingInterrupts;
|
|
|
|
// Yeah, this bit is a bit silly.
|
|
static int interruptsEnabled = 1;
|
|
static bool inInterrupt;
|
|
static SceUID threadBeforeInterrupt;
|
|
|
|
|
|
static int sceKernelCpuSuspendIntr()
|
|
{
|
|
VERBOSE_LOG(Log::sceIntc, "sceKernelCpuSuspendIntr");
|
|
int returnValue;
|
|
if (__InterruptsEnabled())
|
|
{
|
|
returnValue = 1;
|
|
__DisableInterrupts();
|
|
}
|
|
else
|
|
{
|
|
returnValue = 0;
|
|
}
|
|
hleEatCycles(15);
|
|
return hleNoLog(returnValue);
|
|
}
|
|
|
|
static void sceKernelCpuResumeIntr(u32 enable)
|
|
{
|
|
VERBOSE_LOG(Log::sceIntc, "sceKernelCpuResumeIntr(%i)", enable);
|
|
// This is mtic a0, $0, which only looks at bit 0 (tests/intr/mfic).
|
|
if (enable & 1)
|
|
{
|
|
__EnableInterrupts();
|
|
hleRunInterrupts();
|
|
hleReSchedule("interrupts resumed");
|
|
}
|
|
else
|
|
{
|
|
__DisableInterrupts();
|
|
}
|
|
hleEatCycles(15);
|
|
hleNoLogVoid();
|
|
}
|
|
|
|
static int sceKernelIsCpuIntrEnable() {
|
|
u32 retVal = __InterruptsEnabled();
|
|
return hleLogVerbose(Log::sceIntc, retVal);
|
|
}
|
|
|
|
static int sceKernelIsCpuIntrSuspended(int flag)
|
|
{
|
|
int retVal = flag == 0 ? 1 : 0;
|
|
return hleLogDebug(Log::sceIntc, retVal);
|
|
}
|
|
|
|
static void sceKernelCpuResumeIntrWithSync(u32 enable)
|
|
{
|
|
// Just a forward, don't bother with hleCall.
|
|
sceKernelCpuResumeIntr(enable);
|
|
}
|
|
|
|
bool IntrHandler::run(PendingInterrupt& pend)
|
|
{
|
|
SubIntrHandler *handler = get(pend.subintr);
|
|
if (!handler) {
|
|
WARN_LOG(Log::sceIntc, "Ignoring interrupt, already been released.");
|
|
return false;
|
|
}
|
|
|
|
copyArgsToCPU(pend);
|
|
|
|
return true;
|
|
}
|
|
|
|
void IntrHandler::copyArgsToCPU(PendingInterrupt& pend)
|
|
{
|
|
SubIntrHandler* handler = get(pend.subintr);
|
|
DEBUG_LOG(Log::CPU, "Entering interrupt handler %08x", handler->handlerAddress);
|
|
currentMIPS->pc = handler->handlerAddress;
|
|
currentMIPS->r[MIPS_REG_A0] = handler->subIntrNumber;
|
|
currentMIPS->r[MIPS_REG_A1] = handler->handlerArg;
|
|
// RA is already taken care of
|
|
}
|
|
|
|
void IntrHandler::handleResult(PendingInterrupt& pend)
|
|
{
|
|
//u32 result = currentMIPS->r[MIPS_REG_V0];
|
|
}
|
|
|
|
SubIntrHandler* IntrHandler::add(int subIntrNum)
|
|
{
|
|
return &subIntrHandlers[subIntrNum];
|
|
}
|
|
void IntrHandler::remove(int subIntrNum)
|
|
{
|
|
if (has(subIntrNum))
|
|
{
|
|
subIntrHandlers.erase(subIntrNum);
|
|
}
|
|
}
|
|
bool IntrHandler::has(int subIntrNum) const
|
|
{
|
|
return subIntrHandlers.find(subIntrNum) != subIntrHandlers.end();
|
|
}
|
|
void IntrHandler::enable(int subIntrNum)
|
|
{
|
|
subIntrHandlers[subIntrNum].enabled = true;
|
|
}
|
|
void IntrHandler::disable(int subIntrNum)
|
|
{
|
|
subIntrHandlers[subIntrNum].enabled = false;
|
|
}
|
|
SubIntrHandler* IntrHandler::get(int subIntrNum)
|
|
{
|
|
if (has(subIntrNum))
|
|
return &subIntrHandlers[subIntrNum];
|
|
else
|
|
return NULL;
|
|
}
|
|
void IntrHandler::clear()
|
|
{
|
|
subIntrHandlers.clear();
|
|
setBaseHandler(0, 0);
|
|
}
|
|
|
|
int IntrHandler::queueUp(int subintr) {
|
|
if (subintr == PSP_INTR_SUB_NONE) {
|
|
pendingInterrupts.push_back(PendingInterrupt(intrNumber, subintr));
|
|
return 1;
|
|
}
|
|
// Just call execute on all the subintr handlers for this interrupt.
|
|
// They will get queued up.
|
|
int count = 0;
|
|
for (auto iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter) {
|
|
if ((subintr == PSP_INTR_SUB_ALL || iter->first == subintr) && iter->second.enabled && iter->second.handlerAddress != 0) {
|
|
pendingInterrupts.push_back(PendingInterrupt(intrNumber, iter->first));
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void IntrHandler::DoState(PointerWrap &p)
|
|
{
|
|
auto s = p.Section("IntrHandler", 1, 2);
|
|
if (!s)
|
|
return;
|
|
|
|
Do(p, intrNumber);
|
|
Do<int, SubIntrHandler>(p, subIntrHandlers);
|
|
if (s >= 2) {
|
|
Do(p, baseHandler_);
|
|
Do(p, baseArg_);
|
|
Do(p, baseEnabled_);
|
|
} else {
|
|
setBaseHandler(0, 0);
|
|
}
|
|
if (p.mode == p.MODE_READ && (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS)) {
|
|
ERROR_LOG(Log::sceIntc, "Savestate failure: invalid interrupt handler %d", intrNumber);
|
|
p.SetError(p.ERROR_FAILURE);
|
|
}
|
|
}
|
|
|
|
void PendingInterrupt::DoState(PointerWrap &p)
|
|
{
|
|
auto s = p.Section("PendingInterrupt", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
Do(p, intr);
|
|
Do(p, subintr);
|
|
}
|
|
|
|
void __InterruptsInit()
|
|
{
|
|
interruptsEnabled = 1;
|
|
inInterrupt = false;
|
|
for (int i = 0; i < (int)ARRAY_SIZE(intrHandlers); ++i)
|
|
intrHandlers[i] = new IntrHandler(i);
|
|
intState.clear();
|
|
threadBeforeInterrupt = 0;
|
|
}
|
|
|
|
void __InterruptsDoState(PointerWrap &p)
|
|
{
|
|
auto s = p.Section("sceKernelInterrupt", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
int numInterrupts = PSP_NUMBER_INTERRUPTS;
|
|
Do(p, numInterrupts);
|
|
if (numInterrupts != PSP_NUMBER_INTERRUPTS)
|
|
{
|
|
p.SetError(p.ERROR_FAILURE);
|
|
ERROR_LOG(Log::sceIntc, "Savestate failure: wrong number of interrupts, can't load.");
|
|
return;
|
|
}
|
|
|
|
intState.DoState(p);
|
|
PendingInterrupt pi(0, 0);
|
|
Do(p, pendingInterrupts, pi);
|
|
if (p.mode == p.MODE_READ) {
|
|
for (const PendingInterrupt &pend : pendingInterrupts) {
|
|
if (pend.intr < 0 || pend.intr >= PSP_NUMBER_INTERRUPTS) {
|
|
p.SetError(p.ERROR_FAILURE);
|
|
ERROR_LOG(Log::sceIntc, "Savestate failure: invalid pending interrupt %d", pend.intr);
|
|
pendingInterrupts.clear();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
Do(p, interruptsEnabled);
|
|
Do(p, inInterrupt);
|
|
Do(p, threadBeforeInterrupt);
|
|
}
|
|
|
|
void __InterruptsDoStateLate(PointerWrap &p)
|
|
{
|
|
// We do these later to ensure the handlers have been registered.
|
|
for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i)
|
|
intrHandlers[i]->DoState(p);
|
|
p.DoMarker("sceKernelInterrupt Late");
|
|
}
|
|
|
|
void __InterruptsShutdown()
|
|
{
|
|
for (size_t i = 0; i < ARRAY_SIZE(intrHandlers); ++i)
|
|
intrHandlers[i]->clear();
|
|
for (size_t i = 0; i < ARRAY_SIZE(intrHandlers); ++i)
|
|
{
|
|
if (intrHandlers[i])
|
|
{
|
|
delete intrHandlers[i];
|
|
intrHandlers[i] = 0;
|
|
}
|
|
}
|
|
pendingInterrupts.clear();
|
|
}
|
|
|
|
void __DisableInterrupts()
|
|
{
|
|
interruptsEnabled = 0;
|
|
}
|
|
|
|
void __EnableInterrupts()
|
|
{
|
|
interruptsEnabled = 1;
|
|
}
|
|
|
|
bool __InterruptsEnabled()
|
|
{
|
|
return interruptsEnabled != 0;
|
|
}
|
|
|
|
bool __IsInInterrupt()
|
|
{
|
|
return inInterrupt;
|
|
}
|
|
|
|
void InterruptState::save()
|
|
{
|
|
__KernelSaveContext(&savedCpu, true);
|
|
}
|
|
|
|
void InterruptState::restore()
|
|
{
|
|
__KernelLoadContext(&savedCpu, true);
|
|
}
|
|
|
|
void InterruptState::clear()
|
|
{
|
|
savedCpu.reset();
|
|
}
|
|
|
|
// http://forums.ps2dev.org/viewtopic.php?t=5687
|
|
|
|
// http://www.google.se/url?sa=t&rct=j&q=&esrc=s&source=web&cd=7&ved=0CFYQFjAG&url=http%3A%2F%2Fdev.psnpt.com%2Fredmine%2Fprojects%2Fuofw%2Frepository%2Frevisions%2F65%2Fraw%2Ftrunk%2Finclude%2Finterruptman.h&ei=J4pCUKvyK4nl4QSu-YC4Cg&usg=AFQjCNFxJcgzQnv6dK7aiQlht_BM9grfQQ&sig2=GGk5QUEWI6qouYDoyE07YQ
|
|
|
|
|
|
// Returns true if anything was executed.
|
|
bool __RunOnePendingInterrupt()
|
|
{
|
|
bool needsThreadReturn = false;
|
|
|
|
if (inInterrupt || !interruptsEnabled) {
|
|
// Already in an interrupt! We'll keep going when it's done.
|
|
return false;
|
|
}
|
|
// Can easily prioritize between different kinds of interrupts if necessary.
|
|
retry:
|
|
if (!pendingInterrupts.empty()) {
|
|
PendingInterrupt pend = pendingInterrupts.front();
|
|
|
|
IntrHandler* handler = intrHandlers[pend.intr];
|
|
if (handler == NULL) {
|
|
WARN_LOG(Log::sceIntc, "Ignoring interrupt");
|
|
pendingInterrupts.pop_front();
|
|
goto retry;
|
|
}
|
|
|
|
// If we came from CoreTiming::Advance(), we might've come from a waiting thread's callback.
|
|
// To avoid "injecting" return values into our saved state, we context switch here.
|
|
SceUID savedThread = __KernelGetCurThread();
|
|
if (__KernelSwitchOffThread("interrupt")) {
|
|
threadBeforeInterrupt = savedThread;
|
|
needsThreadReturn = true;
|
|
}
|
|
|
|
intState.save();
|
|
inInterrupt = true;
|
|
|
|
if (!handler->run(pend)) {
|
|
pendingInterrupts.pop_front();
|
|
inInterrupt = false;
|
|
goto retry;
|
|
}
|
|
currentMIPS->downcount -= handler->entryCycles();
|
|
|
|
currentMIPS->r[MIPS_REG_RA] = __KernelInterruptReturnAddress();
|
|
return true;
|
|
} else {
|
|
if (needsThreadReturn)
|
|
__KernelSwitchToThread(threadBeforeInterrupt, "left interrupt");
|
|
// DEBUG_LOG(Log::sceIntc, "No more interrupts!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void __TriggerRunInterrupts(int type)
|
|
{
|
|
// If interrupts aren't enabled, we run them later.
|
|
if (interruptsEnabled && !inInterrupt)
|
|
{
|
|
if ((type & PSP_INTR_HLE) != 0)
|
|
hleRunInterrupts();
|
|
else if ((type & PSP_INTR_ALWAYS_RESCHED) != 0)
|
|
{
|
|
// "Always" only means if dispatch is enabled.
|
|
if (!__RunOnePendingInterrupt() && __KernelIsDispatchEnabled())
|
|
{
|
|
// With no handler to run and return from, just pick a thread again. This used to
|
|
// switch to idle, and the interrupted thread sat there until some later event
|
|
// rescheduled: ~775us of every frame at a vblank with no handler registered.
|
|
__KernelReSchedule("interrupt");
|
|
}
|
|
}
|
|
else
|
|
__RunOnePendingInterrupt();
|
|
}
|
|
}
|
|
|
|
int __TriggerInterrupt(int type, PSPInterrupt intno, int subintr)
|
|
{
|
|
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
|
|
{
|
|
// Returning from an interrupt clears the ll bit, handler or not (pspautotests cpu/lsu/llsc).
|
|
currentMIPS->llBit = 0;
|
|
int queued = intrHandlers[intno]->queueUp(subintr);
|
|
VERBOSE_LOG(Log::sceIntc, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, (u32)pendingInterrupts.size());
|
|
__TriggerRunInterrupts(type);
|
|
return queued;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int __CancelRaisedInterrupts(PSPInterrupt intno) {
|
|
int count = 0;
|
|
auto it = pendingInterrupts.begin();
|
|
// The one at the front is what's running, if anything is, and gets popped when it returns.
|
|
if (inInterrupt && it != pendingInterrupts.end())
|
|
++it;
|
|
while (it != pendingInterrupts.end()) {
|
|
if (it->intr == intno) {
|
|
it = pendingInterrupts.erase(it);
|
|
count++;
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void __KernelReturnFromInterrupt()
|
|
{
|
|
VERBOSE_LOG(Log::sceIntc, "Left interrupt handler at %08x", currentMIPS->pc);
|
|
|
|
hleSkipDeadbeef();
|
|
|
|
// This is what we just ran.
|
|
PendingInterrupt pend = pendingInterrupts.front();
|
|
pendingInterrupts.pop_front();
|
|
|
|
if (pend.intr >= 0 && pend.intr < ARRAY_SIZE(intrHandlers)) {
|
|
intrHandlers[pend.intr]->handleResult(pend);
|
|
// Charged once the last queued handler for this interrupt is done.
|
|
bool moreOfThis = false;
|
|
for (const PendingInterrupt &other : pendingInterrupts) {
|
|
moreOfThis = moreOfThis || other.intr == pend.intr;
|
|
}
|
|
if (!moreOfThis) {
|
|
currentMIPS->downcount -= intrHandlers[pend.intr]->exitCycles();
|
|
}
|
|
} else {
|
|
_assert_msg_(false, "Bad pend.intr: %d", pend.intr);
|
|
}
|
|
inInterrupt = false;
|
|
|
|
// Restore context after running the interrupt.
|
|
intState.restore();
|
|
// All should now be back to normal, including PC.
|
|
|
|
// Alright, let's see if there's any more interrupts queued...
|
|
if (!__RunOnePendingInterrupt())
|
|
{
|
|
// Otherwise, we reschedule when dispatch was enabled, or switch back otherwise.
|
|
if (__KernelIsDispatchEnabled())
|
|
__KernelReSchedule("left interrupt");
|
|
else
|
|
__KernelSwitchToThread(threadBeforeInterrupt, "left interrupt");
|
|
}
|
|
hleNoLogVoid();
|
|
}
|
|
|
|
void __SetIntrHandlerCosts(PSPInterrupt intno, int entryCycles, int exitCycles) {
|
|
intrHandlers[intno]->setCosts(entryCycles, exitCycles);
|
|
}
|
|
|
|
void __RegisterIntrHandler(u32 intrNumber, IntrHandler* handler)
|
|
{
|
|
delete intrHandlers[intrNumber];
|
|
intrHandlers[intrNumber] = handler;
|
|
}
|
|
|
|
SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg, u32 &error) {
|
|
if (intrNumber >= PSP_NUMBER_INTERRUPTS) {
|
|
error = SCE_KERNEL_ERROR_ILLEGAL_INTRCODE;
|
|
return NULL;
|
|
}
|
|
IntrHandler *intr = intrHandlers[intrNumber];
|
|
if (intr->has(subIntrNumber)) {
|
|
if (intr->get(subIntrNumber)->handlerAddress != 0) {
|
|
error = SCE_KERNEL_ERROR_FOUND_HANDLER;
|
|
return NULL;
|
|
} else {
|
|
SubIntrHandler *subIntrHandler = intr->get(subIntrNumber);
|
|
subIntrHandler->handlerAddress = handler;
|
|
subIntrHandler->handlerArg = handlerArg;
|
|
|
|
error = SCE_KERNEL_ERROR_OK;
|
|
return subIntrHandler;
|
|
}
|
|
}
|
|
|
|
SubIntrHandler *subIntrHandler = intr->add(subIntrNumber);
|
|
subIntrHandler->subIntrNumber = subIntrNumber;
|
|
subIntrHandler->intrNumber = intrNumber;
|
|
subIntrHandler->handlerAddress = handler;
|
|
subIntrHandler->handlerArg = handlerArg;
|
|
subIntrHandler->enabled = false;
|
|
|
|
error = SCE_KERNEL_ERROR_OK;
|
|
return subIntrHandler;
|
|
}
|
|
|
|
int __ReleaseSubIntrHandler(int intrNumber, int subIntrNumber) {
|
|
if (intrNumber >= PSP_NUMBER_INTERRUPTS) {
|
|
return SCE_KERNEL_ERROR_ILLEGAL_INTRCODE;
|
|
}
|
|
IntrHandler *intr = intrHandlers[intrNumber];
|
|
if (!intr->has(subIntrNumber) || intr->get(subIntrNumber)->handlerAddress == 0) {
|
|
return SCE_KERNEL_ERROR_NOTFOUND_HANDLER;
|
|
}
|
|
|
|
for (auto it = pendingInterrupts.begin(); it != pendingInterrupts.end(); ) {
|
|
if (it->intr == intrNumber && it->subintr == subIntrNumber) {
|
|
pendingInterrupts.erase(it++);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
|
|
// This also implicitly disables it, which is correct.
|
|
intrHandlers[intrNumber]->remove(subIntrNumber);
|
|
return 0;
|
|
}
|
|
|
|
// What a user mode caller finds on each interrupt, as interruptman.prx checks it: whether a driver
|
|
// has installed a handler for the interrupt at all, whether it has sub-interrupt slots, and whether
|
|
// user handlers are allowed in them. This is system state rather than a rule, read back from
|
|
// intr/registersub and intr/releasesub on a 6.61 PSP (running PSPLink, whose USB drivers may count.)
|
|
// Interrupt 8 had a handler in an older recording and doesn't now, so treat this as approximate.
|
|
enum class IntrUserAccess : u8 {
|
|
NO_HANDLER, // SCE_KERNEL_ERROR_NOTFOUND_HANDLER
|
|
NO_SUBS, // SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, the sub number is always out of range
|
|
KERNEL_SUBS, // SCE_KERNEL_ERROR_ILLEGAL_INTRCODE to register, empty slots to release
|
|
USER,
|
|
};
|
|
|
|
static IntrUserAccess GetIntrUserAccess(u32 intrNumber) {
|
|
switch (intrNumber) {
|
|
case PSP_GE_INTR:
|
|
case PSP_VBLANK_INTR:
|
|
return IntrUserAccess::USER;
|
|
case 4: case 6: case 21:
|
|
return IntrUserAccess::KERNEL_SUBS;
|
|
case 7: case 10: case 12: case 15: case 16: case 17: case 18: case 19: case 20: case 22:
|
|
case 23: case 24: case 26: case 31: case 36: case 50: case 56: case 57: case 58: case 59:
|
|
case 60: case 61: case 65:
|
|
return IntrUserAccess::NO_SUBS;
|
|
default:
|
|
return IntrUserAccess::NO_HANDLER;
|
|
}
|
|
}
|
|
|
|
// The vblank slots a user handler may take (the rest are the kernel's), and those the display
|
|
// driver already holds.
|
|
static bool IsUserVblankSubIntr(u32 subIntrNumber) {
|
|
return subIntrNumber < 16;
|
|
}
|
|
static bool IsKernelHeldVblankSubIntr(u32 subIntrNumber) {
|
|
return (subIntrNumber >= 18 && subIntrNumber <= 20) || (subIntrNumber >= 24 && subIntrNumber <= 26);
|
|
}
|
|
|
|
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg) {
|
|
if (intrNumber >= PSP_NUMBER_INTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid interrupt");
|
|
}
|
|
switch (GetIntrUserAccess(intrNumber)) {
|
|
case IntrUserAccess::NO_HANDLER:
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_NOTFOUND_HANDLER, "no handler for this interrupt");
|
|
case IntrUserAccess::NO_SUBS:
|
|
case IntrUserAccess::KERNEL_SUBS:
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "no user subinterrupts");
|
|
case IntrUserAccess::USER:
|
|
break;
|
|
}
|
|
if (subIntrNumber >= PSP_NUMBER_SUBINTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid subinterrupt");
|
|
}
|
|
if (intrNumber == PSP_VBLANK_INTR) {
|
|
if (IsKernelHeldVblankSubIntr(subIntrNumber))
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_FOUND_HANDLER, "held by the kernel");
|
|
if (!IsUserVblankSubIntr(subIntrNumber))
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "kernel only subinterrupt");
|
|
}
|
|
|
|
u32 error;
|
|
SubIntrHandler *subIntrHandler = __RegisterSubIntrHandler(intrNumber, subIntrNumber, handler, handlerArg, error);
|
|
if (subIntrHandler) {
|
|
if (handler == 0) {
|
|
return hleLogWarning(Log::sceIntc, error, "ignored NULL handler");
|
|
} else {
|
|
return hleLogDebug(Log::sceIntc, error);
|
|
}
|
|
} else if (error == SCE_KERNEL_ERROR_FOUND_HANDLER) {
|
|
// Pretty common. Used to report here, but not useful.
|
|
return hleLogError(Log::sceIntc, error, "duplicate handler");
|
|
}
|
|
return hleReportError(Log::sceIntc, error);
|
|
}
|
|
|
|
u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber) {
|
|
if (intrNumber >= PSP_NUMBER_INTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid interrupt");
|
|
}
|
|
const IntrUserAccess access = GetIntrUserAccess(intrNumber);
|
|
if (access == IntrUserAccess::NO_HANDLER) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_NOTFOUND_HANDLER, "no handler for this interrupt");
|
|
}
|
|
if (access == IntrUserAccess::NO_SUBS || subIntrNumber >= PSP_NUMBER_SUBINTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid subinterrupt");
|
|
}
|
|
// User code can't have put anything in the kernel's slots, and can't release what's there.
|
|
if (access == IntrUserAccess::KERNEL_SUBS || (intrNumber == PSP_VBLANK_INTR && !IsUserVblankSubIntr(subIntrNumber))) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_NOTFOUND_HANDLER, "not a user subinterrupt");
|
|
}
|
|
|
|
u32 error = __ReleaseSubIntrHandler(intrNumber, subIntrNumber);
|
|
return hleLogDebugOrError(Log::sceIntc, error);
|
|
}
|
|
|
|
u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber) {
|
|
if (intrNumber >= PSP_NUMBER_INTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid interrupt");
|
|
}
|
|
if (subIntrNumber >= PSP_NUMBER_SUBINTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid subinterrupt");
|
|
}
|
|
|
|
u32 error;
|
|
if (!intrHandlers[intrNumber]->has(subIntrNumber)) {
|
|
// Enabling a handler before registering it works fine.
|
|
__RegisterSubIntrHandler(intrNumber, subIntrNumber, 0, 0, error);
|
|
}
|
|
|
|
intrHandlers[intrNumber]->enable(subIntrNumber);
|
|
return hleLogDebug(Log::sceIntc, 0);
|
|
}
|
|
|
|
static u32 sceKernelDisableSubIntr(u32 intrNumber, u32 subIntrNumber) {
|
|
if (intrNumber >= PSP_NUMBER_INTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid interrupt");
|
|
}
|
|
if (subIntrNumber >= PSP_NUMBER_SUBINTERRUPTS) {
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE, "invalid subinterrupt");
|
|
}
|
|
|
|
if (!intrHandlers[intrNumber]->has(subIntrNumber)) {
|
|
// Disabling when not registered is not an error.
|
|
return 0;
|
|
}
|
|
|
|
intrHandlers[intrNumber]->disable(subIntrNumber);
|
|
return hleLogDebug(Log::sceIntc, 0);
|
|
}
|
|
|
|
|
|
struct PspIntrHandlerOptionParam {
|
|
int size; //+00
|
|
u32 entry; //+04
|
|
u32 common; //+08
|
|
u32 gp; //+0C
|
|
u16 intr_code; //+10
|
|
u16 sub_count; //+12
|
|
u16 intr_level; //+14
|
|
u16 enabled; //+16
|
|
u32 calls; //+18
|
|
u32 field_1C; //+1C
|
|
u32 total_clock_lo; //+20
|
|
u32 total_clock_hi; //+24
|
|
u32 min_clock_lo; //+28
|
|
u32 min_clock_hi; //+2C
|
|
u32 max_clock_lo; //+30
|
|
u32 max_clock_hi; //+34
|
|
}; //=38
|
|
|
|
static int QueryIntrHandlerInfo()
|
|
{
|
|
ERROR_LOG_REPORT(Log::sceIntc, "QueryIntrHandlerInfo()");
|
|
return 0;
|
|
}
|
|
|
|
static u32 sceKernelMemset(u32 addr, u32 fillc, u32 n) {
|
|
u8 c = fillc & 0xff;
|
|
bool skip = false;
|
|
if (n != 0) {
|
|
if (Memory::IsVRAMAddress(addr)) {
|
|
skip = gpu->PerformMemorySet(addr, fillc, n);
|
|
}
|
|
if (!skip) {
|
|
Memory::Memset(addr, c, n);
|
|
}
|
|
}
|
|
NotifyMemInfo(MemBlockFlags::WRITE, addr, n, "KernelMemset");
|
|
return hleLogDebug(Log::sceKernel, addr);
|
|
}
|
|
|
|
static u32 sceKernelMemcpy(u32 dst, u32 src, u32 size) {
|
|
bool skip = false;
|
|
if (Memory::IsVRAMAddress(src) || Memory::IsVRAMAddress(dst)) {
|
|
skip = gpu->PerformMemoryCopy(dst, src, size);
|
|
}
|
|
gpu->NotifyVideoCopy(dst, src, size);
|
|
|
|
// Technically should crash if these are invalid and size > 0...
|
|
if (!skip && Memory::IsValidAddress(dst) && Memory::IsValidAddress(src) && Memory::IsValidAddress(dst + size - 1) && Memory::IsValidAddress(src + size - 1)) {
|
|
u8 *dstp = Memory::GetPointerWriteUnchecked(dst);
|
|
const u8 *srcp = Memory::GetPointerUnchecked(src);
|
|
|
|
// If it's non-overlapping, just do it in one go.
|
|
if (dst + size < src || src + size < dst) {
|
|
memcpy(dstp, srcp, size);
|
|
} else {
|
|
// Try to handle overlapped copies with similar properties to hardware, just in case.
|
|
// Not that anyone ought to rely on it.
|
|
for (u32 size64 = size / 8; size64 > 0; --size64)
|
|
{
|
|
memmove(dstp, srcp, 8);
|
|
dstp += 8;
|
|
srcp += 8;
|
|
}
|
|
for (u32 size8 = size % 8; size8 > 0; --size8)
|
|
*dstp++ = *srcp++;
|
|
}
|
|
}
|
|
|
|
if (MemBlockInfoDetailed(size)) {
|
|
NotifyMemInfoCopy(dst, src, size, "KernelMemcpy/");
|
|
}
|
|
|
|
return hleLogDebug(Log::sceKernel, dst);
|
|
}
|
|
|
|
const HLEFunction Kernel_Library[] =
|
|
{
|
|
{0x092968F4, &WrapI_V<sceKernelCpuSuspendIntr>, "sceKernelCpuSuspendIntr", 'i', "" },
|
|
{0X5F10D406, &WrapV_U<sceKernelCpuResumeIntr>, "sceKernelCpuResumeIntr", 'v', "x" },
|
|
{0X3B84732D, &WrapV_U<sceKernelCpuResumeIntrWithSync>, "sceKernelCpuResumeIntrWithSync", 'v', "x" },
|
|
{0X47A0B729, &WrapI_I<sceKernelIsCpuIntrSuspended>, "sceKernelIsCpuIntrSuspended", 'i', "i" },
|
|
{0xb55249d2, &WrapI_V<sceKernelIsCpuIntrEnable>, "sceKernelIsCpuIntrEnable", 'i', "", },
|
|
{0XA089ECA4, &WrapU_UUU<sceKernelMemset>, "sceKernelMemset", 'x', "xxx" },
|
|
{0XDC692EE3, &WrapI_UI<sceKernelTryLockLwMutex>, "sceKernelTryLockLwMutex", 'i', "xi" },
|
|
{0X37431849, &WrapI_UI<sceKernelTryLockLwMutex_600>, "sceKernelTryLockLwMutex_600", 'i', "xi" },
|
|
{0XBEA46419, &WrapI_UIU<sceKernelLockLwMutex>, "sceKernelLockLwMutex", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
|
{0X1FC64E09, &WrapI_UIU<sceKernelLockLwMutexCB>, "sceKernelLockLwMutexCB", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED },
|
|
{0X15B6446B, &WrapI_UI<sceKernelUnlockLwMutex>, "sceKernelUnlockLwMutex", 'i', "xi" },
|
|
{0XC1734599, &WrapI_UU<sceKernelReferLwMutexStatus>, "sceKernelReferLwMutexStatus", 'i', "xp" },
|
|
{0X293B45B8, &WrapI_V<sceKernelGetThreadId>, "sceKernelGetThreadId", 'i', "" },
|
|
{0XD13BDE95, &WrapI_V<sceKernelCheckThreadStack>, "sceKernelCheckThreadStack", 'i', "" },
|
|
{0X1839852A, &WrapU_UUU<sceKernelMemcpy>, "sceKernelMemcpy", 'x', "xxx" },
|
|
{0XFA835CDE, &WrapI_I<sceKernelGetTlsAddr>, "sceKernelGetTlsAddr", 'i', "i" },
|
|
{0X05572A5F, &WrapV_V<sceKernelExitGame>, "sceKernelExitGame", 'v', "" },
|
|
{0X4AC57943, &WrapI_I<sceKernelRegisterExitCallback>, "sceKernelRegisterExitCallback", 'i', "i" },
|
|
};
|
|
|
|
static u32 sysclib_memcpy(u32 dst, u32 src, u32 size) {
|
|
if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) {
|
|
memcpy(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size);
|
|
}
|
|
if (MemBlockInfoDetailed(size)) {
|
|
NotifyMemInfoCopy(dst, src, size, "KernelMemcpy/");
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static u32 sysclib_strcat(u32 dst, u32 src) {
|
|
if (Memory::IsValidNullTerminatedString(dst) && Memory::IsValidNullTerminatedString(src)) {
|
|
char *dstp = (char *)Memory::GetPointerWriteUnchecked(dst);
|
|
const char *srcp = Memory::GetCharPointerUnchecked(src);
|
|
// The string checks above only cover the strings as they are - the concatenation is longer,
|
|
// and has to fit too, or we'd write past the end of guest memory.
|
|
const size_t dstLen = strlen(dstp);
|
|
const size_t size = dstLen + strlen(srcp) + 1;
|
|
if (!Memory::IsValidRange(dst, (u32)size)) {
|
|
return hleLogError(Log::sceKernel, dst, "result doesn't fit at %08x", dst);
|
|
}
|
|
memcpy(dstp + dstLen, srcp, size - dstLen);
|
|
}
|
|
return hleLogVerbose(Log::sceKernel, dst);
|
|
}
|
|
|
|
static int sysclib_strcmp(u32 dst, u32 src) {
|
|
if (Memory::IsValidNullTerminatedString(dst) && Memory::IsValidNullTerminatedString(src)) {
|
|
const int retval = strcmp((const char *)Memory::GetPointerUnchecked(dst), (const char *)Memory::GetPointerUnchecked(src));
|
|
return hleLogVerbose(Log::sceKernel, retval);
|
|
} else {
|
|
// What to do? Crash, probably.
|
|
return hleLogError(Log::sceKernel, 0);
|
|
}
|
|
}
|
|
|
|
static u32 sysclib_strcpy(u32 dst, u32 src) {
|
|
ERROR_LOG(Log::sceKernel, "Untested sysclib_strcpy(dest=%08x, src=%08x)", dst, src);
|
|
if (Memory::IsValidNullTerminatedString(src)) {
|
|
const char *srcp = Memory::GetCharPointerUnchecked(src);
|
|
// Note: the destination has to fit the whole string. IsValidAddress would only check one byte.
|
|
const size_t size = strlen(srcp) + 1;
|
|
if (!Memory::IsValidRange(dst, (u32)size)) {
|
|
return hleLogError(Log::sceKernel, dst, "string doesn't fit at %08x", dst);
|
|
}
|
|
memcpy(Memory::GetPointerWriteUnchecked(dst), srcp, size);
|
|
}
|
|
return hleLogVerbose(Log::sceKernel, dst);
|
|
}
|
|
|
|
static u32 sysclib_strlen(u32 src) {
|
|
if (Memory::IsValidNullTerminatedString(src)) { // TODO: This computes the length, could reuse it maybe.
|
|
const u32 retval = (u32)strlen(Memory::GetCharPointerUnchecked(src));
|
|
return hleLogVerbose(Log::sceKernel, retval);
|
|
} else {
|
|
// What to do? Crash, probably.
|
|
return hleLogError(Log::sceKernel, 0);
|
|
}
|
|
}
|
|
|
|
static int sysclib_memcmp(u32 dst, u32 src, u32 size) {
|
|
if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) {
|
|
const int retval = memcmp(Memory::GetCharPointerUnchecked(dst), Memory::GetCharPointerUnchecked(src), size);
|
|
return hleLogVerbose(Log::sceKernel, retval);
|
|
} else {
|
|
// What to do? Crash, probably.
|
|
return hleLogError(Log::sceKernel, 0);
|
|
}
|
|
}
|
|
|
|
static int sysclib_sprintf_impl(u32 dst, int limit, u32 fmt, int firstVarArg) {
|
|
std::string result;
|
|
if (!HLEFormatPrintf(fmt, firstVarArg, &result)) {
|
|
return 0;
|
|
}
|
|
|
|
const size_t retval = result.size();
|
|
|
|
// Implement the snprintf length check.
|
|
// Note: > 0, not != 0. A negative size from snprintf would resize() to a huge value and throw.
|
|
if (limit > 0 && (int)result.length() >= limit) {
|
|
result.resize(limit - 1);
|
|
}
|
|
|
|
VERBOSE_LOG(Log::sceKernel, "sysclib_sprintf result string has length %d (retval: %d), content:", (int)result.length(), (int)retval);
|
|
VERBOSE_LOG(Log::sceKernel, "%s", result.c_str());
|
|
// Since this is a sprintf function and not an actual printf, we don't log to the Sprintf log.
|
|
if (!Memory::IsValidRange(dst, (u32)result.length() + 1)) {
|
|
ERROR_LOG(Log::sceKernel, "sysclib_sprintf result string is too long or dst is invalid");
|
|
return 0;
|
|
}
|
|
memcpy((char *)Memory::GetPointerUnchecked(dst), result.c_str(), (int)result.length() + 1);
|
|
return (int)retval;
|
|
}
|
|
|
|
static int sysclib_sprintf(u32 dst, u32 fmt) {
|
|
DEBUG_LOG(Log::sceKernel, "Not fully implemented: sysclib_sprintf(dst=%08x, fmt=%08x)", dst, fmt);
|
|
// dst is a0 and fmt a1, so the varargs start at a2.
|
|
return hleLogDebug(Log::sceKernel, sysclib_sprintf_impl(dst, 0, fmt, 2));
|
|
}
|
|
|
|
static int sysclib_snprintf(u32 dst, int size, u32 fmt) {
|
|
DEBUG_LOG(Log::sceKernel, "Not fully implemented: sysclib_snprintf(dst=%08x, fmt=%08x)", dst, fmt);
|
|
return hleLogDebug(Log::sceKernel, sysclib_sprintf_impl(dst, size, fmt, 3));
|
|
}
|
|
|
|
static u32 sysclib_memset(u32 destAddr, int data, int size) {
|
|
if (Memory::IsValidRange(destAddr, size)) {
|
|
memset(Memory::GetPointerWriteUnchecked(destAddr), data, size);
|
|
}
|
|
NotifyMemInfo(MemBlockFlags::WRITE, destAddr, size, "KernelMemset");
|
|
return hleLogVerbose(Log::sceKernel, 0);
|
|
}
|
|
|
|
static int sysclib_strstr(u32 s1, u32 s2) {
|
|
if (Memory::IsValidNullTerminatedString(s1) && Memory::IsValidNullTerminatedString(s2)) {
|
|
std::string str1 = Memory::GetCharPointerUnchecked(s1);
|
|
std::string str2 = Memory::GetCharPointerUnchecked(s2);
|
|
size_t index = str1.find(str2);
|
|
if (index == str1.npos) {
|
|
return 0;
|
|
}
|
|
return s1 + (uint32_t)index;
|
|
}
|
|
return hleLogVerbose(Log::sceKernel, 0);
|
|
}
|
|
|
|
static int sysclib_strncmp(u32 s1, u32 s2, u32 size) {
|
|
if (Memory::IsValidRange(s1, size) && Memory::IsValidRange(s2, size)) {
|
|
const char * str1 = Memory::GetCharPointerUnchecked(s1);
|
|
const char * str2 = Memory::GetCharPointerUnchecked(s2);
|
|
const int retval = strncmp(str1, str2, size);
|
|
return hleLogVerbose(Log::sceKernel, retval);
|
|
}
|
|
return hleLogError(Log::sceKernel, 0, "Bad addresses");
|
|
}
|
|
|
|
static u32 sysclib_memmove(u32 dst, u32 src, u32 size) {
|
|
if (Memory::IsValidRange(dst, size) && Memory::IsValidRange(src, size)) {
|
|
memmove(Memory::GetPointerWriteUnchecked(dst), Memory::GetPointerUnchecked(src), size);
|
|
}
|
|
if (MemBlockInfoDetailed(size)) {
|
|
NotifyMemInfoCopy(dst, src, size, "KernelMemmove/");
|
|
}
|
|
return hleLogVerbose(Log::sceKernel, 0);
|
|
}
|
|
|
|
static u32 sysclib_strncpy(u32 dest, u32 src, u32 size) {
|
|
if (!Memory::IsValidAddress(dest) || !Memory::IsValidAddress(src)) {
|
|
return hleLogError(Log::sceKernel, 0, "invalid address");
|
|
}
|
|
|
|
// This is just regular strncpy, but being explicit to avoid warnings/safety fixes on missing null.
|
|
u32 i = 0;
|
|
u32 srcSize = Memory::ClampValidSizeAt(src, size);
|
|
const u8 *srcp = Memory::GetPointerUnchecked(src);
|
|
u8 *destp = Memory::GetPointerWriteUnchecked(dest);
|
|
for (i = 0; i < srcSize; ++i) {
|
|
u8 c = *srcp++;
|
|
if (c == 0)
|
|
break;
|
|
*destp++ = c;
|
|
}
|
|
|
|
u32 destSize = Memory::ClampValidSizeAt(dest, size);
|
|
for (; i < destSize; ++i) {
|
|
*destp++ = 0;
|
|
}
|
|
|
|
return hleLogVerbose(Log::sceKernel, dest);
|
|
}
|
|
|
|
static u32 sysclib_strtol(u32 strPtr, u32 endPtrPtr, int base) {
|
|
if (!Memory::IsValidNullTerminatedString(strPtr)) {
|
|
return hleLogError(Log::sceKernel, 0, "invalid address");
|
|
}
|
|
const char* str = Memory::GetCharPointer(strPtr);
|
|
char* end = nullptr;
|
|
int result = (int)strtol(str, &end, base);
|
|
if (Memory::IsValidRange(endPtrPtr, 4))
|
|
Memory::WriteUnchecked_U32(strPtr + (end - str), endPtrPtr);
|
|
return hleLogVerbose(Log::sceKernel, result);
|
|
}
|
|
|
|
static u32 sysclib_strchr(u32 src, int c) {
|
|
if (!Memory::IsValidNullTerminatedString(src)) {
|
|
return hleLogError(Log::sceKernel, 0, "invalid address");
|
|
}
|
|
const std::string str = Memory::GetCharPointer(src);
|
|
size_t cpos = str.find(str, c);
|
|
if (cpos == std::string::npos) {
|
|
return hleLogVerbose(Log::sceKernel, 0);
|
|
}
|
|
return hleLogVerbose(Log::sceKernel, src + (int)cpos);
|
|
}
|
|
|
|
static u32 sysclib_strrchr(u32 src, int c) {
|
|
if (!Memory::IsValidNullTerminatedString(src)) {
|
|
return hleLogError(Log::sceKernel, 0, "invalid address");
|
|
}
|
|
const std::string str = Memory::GetCharPointer(src);
|
|
size_t cpos = str.rfind(str, c);
|
|
if (cpos == std::string::npos) {
|
|
return hleLogVerbose(Log::sceKernel, 0);
|
|
}
|
|
return hleLogVerbose(Log::sceKernel, src + (int)cpos);
|
|
}
|
|
|
|
static u32 sysclib_toupper(u32 c) {
|
|
return hleLogVerbose(Log::sceKernel, toupper(c));
|
|
}
|
|
|
|
const HLEFunction SysclibForKernel[] =
|
|
{
|
|
{0xAB7592FF, &WrapU_UUU<sysclib_memcpy>, "memcpy", 'x', "xxx", HLE_KERNEL_SYSCALL },
|
|
{0x476FD94A, &WrapU_UU<sysclib_strcat>, "strcat", 'x', "xx", HLE_KERNEL_SYSCALL },
|
|
{0xC0AB8932, &WrapI_UU<sysclib_strcmp>, "strcmp", 'i', "xx", HLE_KERNEL_SYSCALL },
|
|
{0xEC6F1CF2, &WrapU_UU<sysclib_strcpy>, "strcpy", 'x', "xx", HLE_KERNEL_SYSCALL },
|
|
{0x52DF196C, &WrapU_U<sysclib_strlen>, "strlen", 'x', "x", HLE_KERNEL_SYSCALL },
|
|
{0x81D0D1F7, &WrapI_UUU<sysclib_memcmp>, "memcmp", 'i', "xxx", HLE_KERNEL_SYSCALL },
|
|
{0x7661E728, &WrapI_UU<sysclib_sprintf>, "sprintf", 'i', "xx", HLE_KERNEL_SYSCALL },
|
|
{0x10F3BB61, &WrapU_UII<sysclib_memset>, "memset", 'x', "xii", HLE_KERNEL_SYSCALL },
|
|
{0x0D188658, &WrapI_UU<sysclib_strstr>, "strstr", 'i', "xx", HLE_KERNEL_SYSCALL },
|
|
{0x7AB35214, &WrapI_UUU<sysclib_strncmp>, "strncmp", 'i', "xxx", HLE_KERNEL_SYSCALL },
|
|
{0xA48D2592, &WrapU_UUU<sysclib_memmove>, "memmove", 'x', "xxx", HLE_KERNEL_SYSCALL },
|
|
{0xB49A7697, &WrapU_UUU<sysclib_strncpy>, "strncpy", 'x', "xxi", HLE_KERNEL_SYSCALL },
|
|
{0x47DD934D, &WrapU_UUI<sysclib_strtol>, "strtol", 'x', "xxi", HLE_KERNEL_SYSCALL },
|
|
{0xB1DC2AE8, &WrapU_UI<sysclib_strchr>, "strchr", 'x', "xx", HLE_KERNEL_SYSCALL },
|
|
{0x4C0E0274, &WrapU_UI<sysclib_strrchr>, "strrchr", 'x', "xx", HLE_KERNEL_SYSCALL },
|
|
{0xCE2F7487, &WrapU_U<sysclib_toupper>, "toupper", 'x', "x", HLE_KERNEL_SYSCALL },
|
|
{0XC2145E80, &WrapI_UIU<sysclib_snprintf>, "snprintf", 'i', "xx", HLE_KERNEL_SYSCALL },
|
|
};
|
|
|
|
void Register_Kernel_Library()
|
|
{
|
|
RegisterHLEModule("Kernel_Library", ARRAY_SIZE(Kernel_Library), Kernel_Library);
|
|
}
|
|
|
|
void Register_SysclibForKernel()
|
|
{
|
|
RegisterHLEModule("SysclibForKernel", ARRAY_SIZE(SysclibForKernel), SysclibForKernel);
|
|
}
|
|
|
|
const HLEFunction InterruptManager[] =
|
|
{
|
|
{0XCA04A2B9, &WrapU_UUUU<sceKernelRegisterSubIntrHandler>, "sceKernelRegisterSubIntrHandler", 'x', "xxxx" },
|
|
{0XD61E6961, &WrapU_UU<sceKernelReleaseSubIntrHandler>, "sceKernelReleaseSubIntrHandler", 'x', "xx" },
|
|
{0XFB8E22EC, &WrapU_UU<sceKernelEnableSubIntr>, "sceKernelEnableSubIntr", 'x', "xx" },
|
|
{0X8A389411, &WrapU_UU<sceKernelDisableSubIntr>, "sceKernelDisableSubIntr", 'x', "xx" },
|
|
{0X5CB5A78B, nullptr, "sceKernelSuspendSubIntr", '?', "" },
|
|
{0X7860E0DC, nullptr, "sceKernelResumeSubIntr", '?', "" },
|
|
{0XFC4374B8, nullptr, "sceKernelIsSubInterruptOccurred", '?', "" },
|
|
{0xD2E8363F, &WrapI_V<QueryIntrHandlerInfo>, "QueryIntrHandlerInfo", 'i', "" }, // No sce prefix for some reason
|
|
{0XEEE43F47, nullptr, "sceKernelRegisterUserSpaceIntrStack", '?', "" },
|
|
};
|
|
|
|
|
|
void Register_InterruptManager()
|
|
{
|
|
RegisterHLEModule("InterruptManager", ARRAY_SIZE(InterruptManager), InterruptManager);
|
|
}
|
|
|
|
|
|
// What these return is from tests/intr/kernel/intrhandler, recorded on a PSP. Interrupts there go up to
|
|
// 67, one more than PSP_NUMBER_INTERRUPTS, which the firmware's drivers use anyway.
|
|
static int sceKernelRegisterIntrHandler(int intrNumber, u32 unknown, u32 handler, u32 handlerArg, u32 opt) {
|
|
if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS || handler == 0)
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE);
|
|
IntrHandler *intr = intrHandlers[intrNumber];
|
|
if (intr->hasBaseHandler())
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_FOUND_HANDLER);
|
|
intr->setBaseHandler(handler, handlerArg);
|
|
return hleLogDebug(Log::sceIntc, 0);
|
|
}
|
|
|
|
static int sceKernelReleaseIntrHandler(int intrNumber) {
|
|
if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS)
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_ILLEGAL_INTRCODE);
|
|
IntrHandler *intr = intrHandlers[intrNumber];
|
|
if (!intr->hasBaseHandler())
|
|
return hleLogError(Log::sceIntc, SCE_KERNEL_ERROR_NOTFOUND_HANDLER);
|
|
intr->setBaseHandler(0, 0);
|
|
return hleLogDebug(Log::sceIntc, 0);
|
|
}
|
|
|
|
static int SetIntrEnabled(int intrNumber, bool enable) {
|
|
if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS)
|
|
return SCE_KERNEL_ERROR_ILLEGAL_INTRCODE;
|
|
IntrHandler *intr = intrHandlers[intrNumber];
|
|
if (!intr->hasBaseHandler())
|
|
return SCE_KERNEL_ERROR_NOTFOUND_HANDLER;
|
|
intr->enableBase(enable);
|
|
return 0;
|
|
}
|
|
|
|
static int sceKernelEnableIntr(int intrNumber) {
|
|
return hleLogDebugOrError(Log::sceIntc, SetIntrEnabled(intrNumber, true));
|
|
}
|
|
|
|
static int sceKernelDisableIntr(int intrNumber) {
|
|
return hleLogDebugOrError(Log::sceIntc, SetIntrEnabled(intrNumber, false));
|
|
}
|
|
|
|
static int sceKernelIsIntrContext() {
|
|
return hleLogDebug(Log::sceKernel, __IsInInterrupt() ? 1 : 0);
|
|
}
|
|
|
|
|
|
const HLEFunction InterruptManagerForKernel[] =
|
|
{
|
|
{0x092968F4, &WrapI_V<sceKernelCpuSuspendIntr>, "sceKernelCpuSuspendIntr", 'i', "" ,HLE_KERNEL_SYSCALL },
|
|
{0X5F10D406, &WrapV_U<sceKernelCpuResumeIntr>, "sceKernelCpuResumeIntr", 'v', "x" ,HLE_KERNEL_SYSCALL },
|
|
{0X3B84732D, &WrapV_U<sceKernelCpuResumeIntrWithSync>, "sceKernelCpuResumeIntrWithSync", 'v', "x" ,HLE_KERNEL_SYSCALL },
|
|
{0X47A0B729, &WrapI_I<sceKernelIsCpuIntrSuspended>, "sceKernelIsCpuIntrSuspended", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
|
{0xb55249d2, &WrapI_V<sceKernelIsCpuIntrEnable>, "sceKernelIsCpuIntrEnable", 'i', "", HLE_KERNEL_SYSCALL },
|
|
{0XA089ECA4, &WrapU_UUU<sceKernelMemset>, "sceKernelMemset", 'x', "xxx" ,HLE_KERNEL_SYSCALL },
|
|
{0XDC692EE3, &WrapI_UI<sceKernelTryLockLwMutex>, "sceKernelTryLockLwMutex", 'i', "xi" ,HLE_KERNEL_SYSCALL },
|
|
{0X37431849, &WrapI_UI<sceKernelTryLockLwMutex_600>, "sceKernelTryLockLwMutex_600", 'i', "xi" ,HLE_KERNEL_SYSCALL },
|
|
{0XBEA46419, &WrapI_UIU<sceKernelLockLwMutex>, "sceKernelLockLwMutex", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED | HLE_KERNEL_SYSCALL},
|
|
{0X1FC64E09, &WrapI_UIU<sceKernelLockLwMutexCB>, "sceKernelLockLwMutexCB", 'i', "xix", HLE_NOT_IN_INTERRUPT | HLE_NOT_DISPATCH_SUSPENDED | HLE_KERNEL_SYSCALL},
|
|
{0X15B6446B, &WrapI_UI<sceKernelUnlockLwMutex>, "sceKernelUnlockLwMutex", 'i', "xi" ,HLE_KERNEL_SYSCALL },
|
|
{0XC1734599, &WrapI_UU<sceKernelReferLwMutexStatus>, "sceKernelReferLwMutexStatus", 'i', "xp" ,HLE_KERNEL_SYSCALL },
|
|
{0X293B45B8, &WrapI_V<sceKernelGetThreadId>, "sceKernelGetThreadId", 'i', "" ,HLE_KERNEL_SYSCALL },
|
|
{0XD13BDE95, &WrapI_V<sceKernelCheckThreadStack>, "sceKernelCheckThreadStack", 'i', "" ,HLE_KERNEL_SYSCALL },
|
|
{0X1839852A, &WrapU_UUU<sceKernelMemcpy>, "sceKernelMemcpy", 'x', "xxx" ,HLE_KERNEL_SYSCALL },
|
|
{0XFA835CDE, &WrapI_I<sceKernelGetTlsAddr>, "sceKernelGetTlsAddr", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
|
{0X05572A5F, &WrapV_V<sceKernelExitGame>, "sceKernelExitGame", 'v', "" ,HLE_KERNEL_SYSCALL },
|
|
{0X4AC57943, &WrapI_I<sceKernelRegisterExitCallback>, "sceKernelRegisterExitCallback", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
|
{0XFE28C6D9, &WrapI_V<sceKernelIsIntrContext>, "sceKernelIsIntrContext", 'i', "" ,HLE_KERNEL_SYSCALL },
|
|
{0X58DD8978, &WrapI_IUUUU<sceKernelRegisterIntrHandler>, "sceKernelRegisterIntrHandler", 'i', "ixxxx", HLE_KERNEL_SYSCALL },
|
|
{0XF987B1F0, &WrapI_I<sceKernelReleaseIntrHandler>, "sceKernelReleaseIntrHandler", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
|
{0X4D6E7305, &WrapI_I<sceKernelEnableIntr>, "sceKernelEnableIntr", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
|
{0XD774BA45, &WrapI_I<sceKernelDisableIntr>, "sceKernelDisableIntr", 'i', "i" ,HLE_KERNEL_SYSCALL },
|
|
};
|
|
|
|
void Register_InterruptManagerForKernel()
|
|
{
|
|
RegisterHLEModule("InterruptManagerForKernel", ARRAY_SIZE(InterruptManagerForKernel), InterruptManagerForKernel);
|
|
}
|