Files
ppsspp/Core/HLE/sceKernelInterrupt.h
T
Henrik RydgårdandClaude Opus 5.5 5f2e4bd416 Kernel: Implement sceKernelRegisterIntrHandler and friends
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>
2026-10-20 13:40:47 +02:00

183 lines
5.3 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/.
#pragma once
#include <map>
class PointerWrap;
enum PSPInterrupt {
PSP_GPIO_INTR = 4,
PSP_ATA_INTR = 5,
PSP_UMD_INTR = 6,
PSP_MSCM0_INTR = 7,
PSP_WLAN_INTR = 8,
PSP_AUDIO_INTR = 10,
PSP_I2C_INTR = 12,
PSP_SIRS_INTR = 14,
PSP_SYSTIMER0_INTR = 15,
PSP_SYSTIMER1_INTR = 16,
PSP_SYSTIMER2_INTR = 17,
PSP_SYSTIMER3_INTR = 18,
PSP_THREAD0_INTR = 19,
PSP_NAND_INTR = 20,
PSP_DMACPLUS_INTR = 21,
PSP_DMA0_INTR = 22,
PSP_DMA1_INTR = 23,
PSP_MEMLMD_INTR = 24,
PSP_GE_INTR = 25,
PSP_VBLANK_INTR = 30,
PSP_MECODEC_INTR = 31,
PSP_HPREMOTE_INTR = 36,
PSP_MSCM1_INTR = 60,
PSP_MSCM2_INTR = 61,
PSP_THREAD1_INTR = 65,
PSP_INTERRUPT_INTR = 66,
PSP_NUMBER_INTERRUPTS = 67
};
// These are invented for PPSSPP:
enum PSPGeSubInterrupts {
PSP_GE_SUBINTR_SIGNAL = 0,
PSP_GE_SUBINTR_FINISH = 1,
};
enum PSPInterruptTriggerType {
// Trigger immediately, for CoreTiming events.
PSP_INTR_IMMEDIATE = 0x0,
// Trigger after the HLE syscall finishes.
PSP_INTR_HLE = 0x1,
// Only trigger (as above) if interrupts are not suspended.
PSP_INTR_ONLY_IF_ENABLED = 0x2,
// Always reschedule, even if there's no handler registered.
// TODO: Maybe this should just always do this? Not sure.
PSP_INTR_ALWAYS_RESCHED = 0x4,
};
enum PSPSubInterruptTriggerType {
// Trigger all sub intr
PSP_INTR_SUB_ALL = -2,
// Trigger code at the interrupt handler level
PSP_INTR_SUB_NONE = -1,
// Trigger specific sub interrupt
PSP_INTR_SUB_NUMBER = 0,
};
struct PendingInterrupt {
PendingInterrupt(int intr_, int subintr_)
: intr(intr_), subintr(subintr_) {}
void DoState(PointerWrap &p);
int intr;
int subintr;
};
struct SubIntrHandler
{
bool enabled;
int intrNumber;
int subIntrNumber;
u32 handlerAddress;
u32 handlerArg;
};
class IntrHandler
{
public:
IntrHandler(int intrNumber_)
: intrNumber(intrNumber_)
{
}
virtual ~IntrHandler() {}
virtual bool run(PendingInterrupt& pend);
virtual void copyArgsToCPU(PendingInterrupt& pend);
virtual void handleResult(PendingInterrupt& pend);
// What the kernel spends around a handler: before it runs, and after the last one queued for
// this interrupt returns.
int entryCycles() const { return entryCycles_; }
int exitCycles() const { return exitCycles_; }
void setCosts(int entryCycles, int exitCycles) {
entryCycles_ = entryCycles;
exitCycles_ = exitCycles;
}
// Returns how many handlers were queued.
int queueUp(int subintr);
SubIntrHandler* add(int subIntrNum);
void remove(int subIntrNum);
bool has(int subIntrNum) const;
void enable(int subIntrNum);
void disable(int subIntrNum);
SubIntrHandler *get(int subIntrNum);
void clear();
// The handler a kernel driver registered for the whole interrupt (sceKernelRegisterIntrHandler).
// Nothing raises those interrupts yet, so it's only kept.
bool hasBaseHandler() const { return baseHandler_ != 0; }
void setBaseHandler(u32 handler, u32 arg) {
baseHandler_ = handler;
baseArg_ = arg;
baseEnabled_ = false;
}
void enableBase(bool enable) { baseEnabled_ = enable; }
void DoState(PointerWrap &p);
private:
int intrNumber;
std::map<int, SubIntrHandler> subIntrHandlers;
u32 baseHandler_ = 0;
u32 baseArg_ = 0;
bool baseEnabled_ = false;
int entryCycles_ = 0;
int exitCycles_ = 0;
};
void __DisableInterrupts();
void __EnableInterrupts();
bool __InterruptsEnabled();
bool __IsInInterrupt();
void __InterruptsInit();
void __InterruptsDoState(PointerWrap &p);
void __InterruptsDoStateLate(PointerWrap &p);
void __InterruptsShutdown();
// Returns how many handlers were queued.
int __TriggerInterrupt(int type, PSPInterrupt intno, int subInterrupts = -1);
// Forgets interrupts that have been raised but not taken, except for one that's being handled. Returns how many.
int __CancelRaisedInterrupts(PSPInterrupt intno);
bool __RunOnePendingInterrupt();
void __KernelReturnFromInterrupt();
void __RegisterIntrHandler(u32 intrNumber, IntrHandler* handler);
void __SetIntrHandlerCosts(PSPInterrupt intno, int entryCycles, int exitCycles);
SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg, u32 &error);
int __ReleaseSubIntrHandler(int intrNumber, int subIntrNumber);
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg);
u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber);
u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber);
void Register_Kernel_Library();
void Register_InterruptManager();
void Register_SysclibForKernel();
void Register_InterruptManagerForKernel();