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>
183 lines
5.3 KiB
C++
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();
|