Interrupt handler reorganization for easier GE interrupt support

This commit is contained in:
Florent Castelli 2013-02-04 00:41:16 +01:00
parent 522983549d
commit 8004d360dd
11 changed files with 343 additions and 303 deletions

View File

@ -182,8 +182,8 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
}
vblankWaitingThreads.clear();
// Trigger VBlank interrupt handlers - and resched even if there's none registered.
__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR);
// Trigger VBlank interrupt handlers.
__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED | PSP_INTR_ALWAYS_RESCHED, PSP_VBLANK_INTR, PSP_INTR_SUB_ALL);
CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1);

View File

@ -20,6 +20,7 @@
#include "../System.h"
#include "../CoreParameter.h"
#include "sceGe.h"
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#include "sceKernelInterrupt.h"
#include "../GPU/GPUState.h"
@ -28,15 +29,71 @@
static PspGeCallbackData ge_callback_data[16];
static bool ge_used_callbacks[16] = {0};
struct GeInterruptData
{
int listid;
u32 pc;
};
static std::list<GeInterruptData> ge_pending_cb;
class GeIntrHandler : public IntrHandler
{
public:
GeIntrHandler() : IntrHandler(PSP_GE_INTR) {}
bool run(PendingInterrupt& pend)
{
GeInterruptData intrdata = ge_pending_cb.front();
DisplayList* dl = gpu->getList(intrdata.listid);
gpu->InterruptStart();
u32 cmd = Memory::ReadUnchecked_U32(intrdata.pc) >> 24;
int subintr = dl->subIntrBase | (cmd == GE_CMD_FINISH ? PSP_GE_SUBINTR_FINISH : PSP_GE_SUBINTR_SIGNAL);
SubIntrHandler* handler = get(subintr);
if(handler != NULL)
{
DEBUG_LOG(CPU, "Entering interrupt handler %08x", handler->handlerAddress);
currentMIPS->pc = handler->handlerAddress;
u32 data = dl->subIntrToken;
currentMIPS->r[MIPS_REG_A0] = data & 0xFFFF;
currentMIPS->r[MIPS_REG_A1] = handler->handlerArg;
currentMIPS->r[MIPS_REG_A2] = sceKernelGetCompiledSdkVersion() <= 0x02000010 ? 0 : intrdata.pc + 4;
// RA is already taken care of in __RunOnePendingInterrupt
return true;
}
ge_pending_cb.pop_front();
gpu->InterruptEnd();
WARN_LOG(HLE, "Ignoring interrupt for display list %d, already been released.", intrdata.listid);
return false;
}
virtual void handleResult(PendingInterrupt& pend)
{
GeInterruptData intrdata = ge_pending_cb.front();
ge_pending_cb.pop_front();
gpu->InterruptEnd();
}
};
void __GeInit()
{
memset(&ge_used_callbacks, 0, sizeof(ge_used_callbacks));
ge_pending_cb.clear();
__RegisterIntrHandler(PSP_GE_INTR, new GeIntrHandler());
}
void __GeDoState(PointerWrap &p)
{
p.DoArray(ge_callback_data, ARRAY_SIZE(ge_callback_data));
p.DoArray(ge_used_callbacks, ARRAY_SIZE(ge_used_callbacks));
p.Do(ge_pending_cb);
// Everything else is done in sceDisplay.
p.DoMarker("sceGe");
}
@ -46,6 +103,20 @@ void __GeShutdown()
}
void __GeTriggerInterrupt(int listid, u32 pc)
{
GeInterruptData intrdata;
intrdata.listid = listid;
intrdata.pc = pc;
ge_pending_cb.push_back(intrdata);
__TriggerInterrupt(PSP_INTR_HLE, PSP_GE_INTR, PSP_INTR_SUB_NONE);
}
bool __GeHasPendingInterrupt()
{
return !ge_pending_cb.empty();
}
// The GE is implemented wrong - it should be parallel to the CPU execution instead of
// synchronous.
@ -63,26 +134,9 @@ u32 sceGeEdramGetSize()
return retVal;
}
// TODO: Probably shouldn't use an interrupt?
int __GeSubIntrBase(int callbackId)
{
// Negative means don't use.
if (callbackId < 0)
return 0;
if (callbackId >= (int)(ARRAY_SIZE(ge_used_callbacks)))
{
WARN_LOG(HLE, "Unexpected (too high) GE callback id %d, ignoring", callbackId);
return 0;
}
if (!ge_used_callbacks[callbackId])
{
WARN_LOG(HLE, "Unregistered GE callback id %d, ignoring", callbackId);
return 0;
}
return (callbackId + 1) << 16;
return callbackId * 2;
}
u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, int callbackId,

View File

@ -39,6 +39,8 @@ void Register_sceGe_user();
void __GeInit();
void __GeDoState(PointerWrap &p);
void __GeShutdown();
void __GeTriggerInterrupt(int listid, u32 pc);
bool __GeHasPendingInterrupt();
// Export functions for use by Util/PPGe

View File

@ -23,6 +23,8 @@
const int NATIVEALARM_SIZE = 20;
std::list<SceUID> triggeredAlarm;
struct NativeAlarm
{
SceSize size;
@ -49,35 +51,33 @@ struct Alarm : public KernelObject
void __KernelScheduleAlarm(Alarm *alarm, u64 ticks);
class AlarmIntrHandler : public SubIntrHandler
class AlarmIntrHandler : public IntrHandler
{
public:
static SubIntrHandler *Create()
{
return new AlarmIntrHandler();
}
AlarmIntrHandler() : IntrHandler(PSP_SYSTIMER0_INTR) {}
void setAlarm(Alarm *alarm)
virtual bool run(PendingInterrupt& pend)
{
alarmID = alarm->GetUID();
handlerAddress = alarm->alm.handlerPtr;
enabled = true;
}
virtual void copyArgsToCPU(const PendingInterrupt &pend)
{
SubIntrHandler::copyArgsToCPU(pend);
u32 error;
int alarmID = triggeredAlarm.front();
Alarm *alarm = kernelObjects.Get<Alarm>(alarmID, error);
if (alarm)
currentMIPS->r[MIPS_REG_A0] = alarm->alm.commonPtr;
else
ERROR_LOG(HLE, "sceKernelAlarm: Unable to send interrupt args: alarm deleted?");
if(error)
return false;
currentMIPS->pc = alarm->alm.handlerPtr;
currentMIPS->r[MIPS_REG_A0] = alarm->alm.commonPtr;
return true;
}
virtual void handleResult(int result)
virtual void handleResult(PendingInterrupt& pend)
{
int result = currentMIPS->r[MIPS_REG_V0];
int alarmID = triggeredAlarm.front();
triggeredAlarm.pop_front();
// A non-zero result means to reschedule.
if (result > 0)
{
@ -92,18 +92,8 @@ public:
// Delete the alarm if it's not rescheduled.
kernelObjects.Destroy<Alarm>(alarmID);
__ReleaseSubIntrHandler(PSP_SYSTIMER0_INTR, alarmID);
}
}
virtual void DoState(PointerWrap &p)
{
SubIntrHandler::DoState(p);
p.Do(alarmID);
p.DoMarker("AlarmIntrHandler");
}
SceUID alarmID;
};
static int alarmTimer = -1;
@ -115,18 +105,23 @@ void __KernelTriggerAlarm(u64 userdata, int cyclesLate)
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
if (alarm)
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR, uid);
{
triggeredAlarm.push_back(uid);
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR);
}
}
void __KernelAlarmInit()
{
triggeredAlarm.clear();
__RegisterIntrHandler(PSP_SYSTIMER0_INTR, new AlarmIntrHandler());
alarmTimer = CoreTiming::RegisterEvent("Alarm", __KernelTriggerAlarm);
__RegisterSubIntrCreator(PSP_SYSTIMER0_INTR, AlarmIntrHandler::Create);
}
void __KernelAlarmDoState(PointerWrap &p)
{
p.Do(alarmTimer);
p.Do(triggeredAlarm);
CoreTiming::RestoreRegisterEvent(alarmTimer, "Alarm", __KernelTriggerAlarm);
p.DoMarker("sceKernelAlarm");
}
@ -155,15 +150,6 @@ SceUID __KernelSetAlarm(u64 ticks, u32 handlerPtr, u32 commonPtr)
alarm->alm.handlerPtr = handlerPtr;
alarm->alm.commonPtr = commonPtr;
u32 error;
AlarmIntrHandler *handler = (AlarmIntrHandler *) __RegisterSubIntrHandler(PSP_SYSTIMER0_INTR, uid, error);
if (error != 0)
{
kernelObjects.Destroy<Alarm>(uid);
return error;
}
handler->setAlarm(alarm);
__KernelScheduleAlarm(alarm, ticks);
return uid;
}
@ -192,7 +178,6 @@ int sceKernelCancelAlarm(SceUID uid)
DEBUG_LOG(HLE, "sceKernelCancelAlarm(%08x)", uid);
CoreTiming::UnscheduleEvent(alarmTimer, uid);
__ReleaseSubIntrHandler(PSP_SYSTIMER0_INTR, uid);
return kernelObjects.Destroy<Alarm>(uid);
}

View File

@ -35,6 +35,34 @@ bool __InterruptsEnabled();
//////////////////////////////////////////////////////////////////////////
// INTERRUPT MANAGEMENT
//////////////////////////////////////////////////////////////////////////
class InterruptState
{
public:
void save();
void restore();
void clear();
void DoState(PointerWrap &p)
{
p.Do(savedCpu);
p.DoMarker("InterruptState");
}
ThreadContext 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;
void sceKernelCpuSuspendIntr()
{
//LOG(HLE,"sceKernelCpuSuspendIntr"); // very spammy
@ -84,154 +112,100 @@ void sceKernelCpuResumeIntrWithSync(u32 enable)
sceKernelCpuResumeIntr(enable);
}
class IntrHandler {
public:
IntrHandler()
: subIntrCreator(NULL)
{
}
SubIntrHandler *add(int subIntrNum)
{
SubIntrHandler *handler;
if (subIntrCreator != NULL)
handler = subIntrCreator();
else
handler = new SubIntrHandler();
subIntrHandlers[subIntrNum] = handler;
return handler;
}
void remove(int subIntrNum)
{
if (has(subIntrNum))
{
delete subIntrHandlers[subIntrNum];
subIntrHandlers.erase(subIntrNum);
}
}
bool has(int subIntrNum) const
{
return subIntrHandlers.find(subIntrNum) != subIntrHandlers.end();
}
SubIntrHandler *get(int subIntrNum)
{
if (has(subIntrNum))
return subIntrHandlers[subIntrNum];
else
return NULL;
}
void clear()
{
std::map<int, SubIntrHandler *>::iterator it, end;
for (it = subIntrHandlers.begin(), end = subIntrHandlers.end(); it != end; ++it)
delete it->second;
subIntrHandlers.clear();
}
void queueUp(int subintr)
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler *>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if (subintr == -1 || iter->first == subintr)
iter->second->queueUp();
}
}
void queueUpWithArg(int subintr, int arg)
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler *>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if (subintr == -1 || iter->first == subintr)
iter->second->queueUpWithArg(arg);
}
}
void setCreator(SubIntrCreator creator)
{
subIntrCreator = creator;
}
void DoState(PointerWrap &p)
{
// We assume that the same creator has already been registered.
bool hasCreator = subIntrCreator != NULL;
p.Do(hasCreator);
if (hasCreator != (subIntrCreator != NULL))
{
ERROR_LOG(HLE, "Savestate failure: incompatible sub interrupt handler.");
return;
}
int n = (int) subIntrHandlers.size();
p.Do(n);
if (p.mode == p.MODE_READ)
{
clear();
for (int i = 0; i < n; ++i)
{
int subIntrNum;
p.Do(subIntrNum);
SubIntrHandler *handler = add(subIntrNum);
handler->DoState(p);
}
}
else
{
std::map<int, SubIntrHandler *>::iterator it, end;
for (it = subIntrHandlers.begin(), end = subIntrHandlers.end(); it != end; ++it)
{
p.Do(it->first);
it->second->DoState(p);
}
}
}
private:
SubIntrCreator subIntrCreator;
std::map<int, SubIntrHandler *> subIntrHandlers;
};
class InterruptState
bool IntrHandler::run(PendingInterrupt& pend)
{
public:
void save();
void restore();
void clear();
void DoState(PointerWrap &p)
SubIntrHandler *handler = get(pend.subintr);
if (handler == NULL)
{
p.Do(insideInterrupt);
p.Do(savedCpu);
p.DoMarker("InterruptState");
WARN_LOG(HLE, "Ignoring interrupt, already been released.");
return false;
}
bool insideInterrupt;
ThreadContext savedCpu;
// Action afterInterruptAction;
// Action afterHandlerAction;
};
copyArgsToCPU(pend);
// STATE
return true;
}
InterruptState intState;
IntrHandler intrHandlers[PSP_NUMBER_INTERRUPTS];
std::list<PendingInterrupt> pendingInterrupts;
void IntrHandler::copyArgsToCPU(PendingInterrupt& pend)
{
SubIntrHandler* handler = get(pend.subintr);
DEBUG_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
}
// Yeah, this bit is a bit silly.
static int interruptsEnabled = 1;
static bool inInterrupt;
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 = true;
}
SubIntrHandler* IntrHandler::get(int subIntrNum)
{
if (has(subIntrNum))
return &subIntrHandlers[subIntrNum];
else
return NULL;
}
void IntrHandler::clear()
{
subIntrHandlers.clear();
}
void IntrHandler::queueUp(int subintr)
{
if(subintr == PSP_INTR_SUB_NONE)
{
pendingInterrupts.push_back(PendingInterrupt(intrNumber, subintr));
}
else
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if ((subintr == PSP_INTR_SUB_ALL || iter->first == subintr) && iter->second.enabled)
pendingInterrupts.push_back(PendingInterrupt(intrNumber, iter->first));
}
}
}
void IntrHandler::DoState(PointerWrap &p)
{
p.Do(intrNumber);
p.Do<int, SubIntrHandler>(subIntrHandlers);
}
void __InterruptsInit()
{
interruptsEnabled = 1;
inInterrupt = false;
for(int i = 0; i < ARRAY_SIZE(intrHandlers); ++i)
intrHandlers[i] = new IntrHandler(i);
intState.clear();
}
@ -257,14 +231,22 @@ 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);
intrHandlers[i]->DoState(p);
p.DoMarker("sceKernelInterrupt Late");
}
void __InterruptsShutdown()
{
for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i)
intrHandlers[i].clear();
for (int i = 0; i < ARRAY_SIZE(intrHandlers); ++i)
intrHandlers[i]->clear();
for(int i = 0; i < ARRAY_SIZE(intrHandlers); ++i)
{
if(intrHandlers[i])
{
delete intrHandlers[i];
intrHandlers[i] = 0;
}
}
pendingInterrupts.clear();
}
@ -295,50 +277,22 @@ bool __CanExecuteInterrupt()
void InterruptState::save()
{
insideInterrupt = __IsInInterrupt();
__KernelSaveContext(&savedCpu);
}
void InterruptState::restore()
{
::inInterrupt = insideInterrupt;
__KernelLoadContext(&savedCpu);
}
void InterruptState::clear()
{
insideInterrupt = false;
}
// 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
void SubIntrHandler::queueUp()
{
if (!enabled)
return;
pendingInterrupts.push_back(PendingInterrupt(intrNumber, number));
};
void SubIntrHandler::queueUpWithArg(int arg)
{
if (!enabled)
return;
pendingInterrupts.push_back(PendingInterrupt(intrNumber, number, arg));
}
void SubIntrHandler::copyArgsToCPU(const PendingInterrupt &pend)
{
DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress);
currentMIPS->pc = handlerAddress;
currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number;
currentMIPS->r[MIPS_REG_A1] = handlerArg;
// RA is already taken care of
}
// Returns true if anything was executed.
bool __RunOnePendingInterrupt()
@ -350,26 +304,32 @@ bool __RunOnePendingInterrupt()
}
// Can easily prioritize between different kinds of interrupts if necessary.
retry:
if (pendingInterrupts.size())
if (!pendingInterrupts.empty())
{
// 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.
__KernelSwitchOffThread("interrupt");
PendingInterrupt pend = pendingInterrupts.front();
SubIntrHandler *handler = intrHandlers[pend.intr].get(pend.subintr);
if (handler == NULL)
IntrHandler* handler = intrHandlers[pend.intr];
if(handler == NULL)
{
WARN_LOG(HLE, "Ignoring interrupt, already been released.");
WARN_LOG(HLE, "Ignoring interrupt");
pendingInterrupts.pop_front();
goto retry;
}
intState.save();
handler->copyArgsToCPU(pend);
inInterrupt = true;
if(!handler->run(pend)) {
pendingInterrupts.pop_front();
inInterrupt = false;
goto retry;
}
currentMIPS->r[MIPS_REG_RA] = __KernelInterruptReturnAddress();
inInterrupt = true;
return true;
}
else
@ -400,37 +360,22 @@ void __TriggerInterrupt(int type, PSPInterrupt intno, int subintr)
{
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
{
intrHandlers[intno].queueUp(subintr);
intrHandlers[intno]->queueUp(subintr);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, (u32)pendingInterrupts.size());
__TriggerRunInterrupts(type);
}
}
void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg)
{
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
{
intrHandlers[intno].queueUpWithArg(subintr, arg);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg,
(u32)pendingInterrupts.size());
__TriggerRunInterrupts(type);
}
}
void __KernelReturnFromInterrupt()
{
DEBUG_LOG(CPU, "Left interrupt handler at %08x", currentMIPS->pc);
inInterrupt = false;
// This is what we just ran.
PendingInterrupt pend = pendingInterrupts.front();
pendingInterrupts.pop_front();
SubIntrHandler *handler = intrHandlers[pend.intr].get(pend.subintr);
if (handler != NULL)
handler->handleResult(currentMIPS->r[MIPS_REG_V0]);
else
ERROR_LOG(HLE, "Interrupt released itself? Should not happen.");
intrHandlers[pend.intr]->handleResult(pend);
inInterrupt = false;
// Restore context after running the interrupt.
intState.restore();
@ -441,15 +386,17 @@ void __KernelReturnFromInterrupt()
__KernelReSchedule("return from interrupt");
}
void __RegisterSubIntrCreator(u32 intrNumber, SubIntrCreator creator)
void __RegisterIntrHandler(u32 intrNumber, IntrHandler* handler)
{
intrHandlers[intrNumber].setCreator(creator);
if(intrHandlers[intrNumber])
delete intrHandlers[intrNumber];
intrHandlers[intrNumber] = handler;
}
SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error)
{
SubIntrHandler *subIntrHandler = intrHandlers[intrNumber].add(subIntrNumber);
subIntrHandler->number = subIntrNumber;
SubIntrHandler *subIntrHandler = intrHandlers[intrNumber]->add(subIntrNumber);
subIntrHandler->subIntrNumber = subIntrNumber;
subIntrHandler->intrNumber = intrNumber;
error = 0;
return subIntrHandler;
@ -457,7 +404,7 @@ SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32
u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber)
{
if (!intrHandlers[intrNumber].has(subIntrNumber))
if (!intrHandlers[intrNumber]->has(subIntrNumber))
return -1;
for (std::list<PendingInterrupt>::iterator it = pendingInterrupts.begin(); it != pendingInterrupts.end(); )
@ -468,7 +415,7 @@ u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber)
++it;
}
intrHandlers[intrNumber].remove(subIntrNumber);
intrHandlers[intrNumber]->remove(subIntrNumber);
return 0;
}
@ -506,10 +453,10 @@ u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber)
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
if (!intrHandlers[intrNumber].has(subIntrNumber))
if (!intrHandlers[intrNumber]->has(subIntrNumber))
return -1;
intrHandlers[intrNumber].get(subIntrNumber)->enabled = true;
intrHandlers[intrNumber]->enable(subIntrNumber);
return 0;
}
@ -519,10 +466,10 @@ u32 sceKernelDisableSubIntr(u32 intrNumber, u32 subIntrNumber)
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
if (!intrHandlers[intrNumber].has(subIntrNumber))
if (!intrHandlers[intrNumber]->has(subIntrNumber))
return -1;
intrHandlers[intrNumber].get(subIntrNumber)->enabled = false;
intrHandlers[intrNumber]->disable(subIntrNumber);
return 0;
}

View File

@ -52,8 +52,8 @@ enum PSPInterrupt {
// These are invented for PPSSPP:
enum PSPGeSubInterrupts {
PSP_GE_SUBINTR_FINISH = 14,
PSP_GE_SUBINTR_SIGNAL = 15
PSP_GE_SUBINTR_SIGNAL = 0,
PSP_GE_SUBINTR_FINISH = 1,
};
enum PSPInterruptTriggerType {
@ -68,46 +68,61 @@ enum PSPInterruptTriggerType {
PSP_INTR_ALWAYS_RESCHED = 0x4,
};
struct PendingInterrupt {
PendingInterrupt(int intr_, int subintr_)
: intr(intr_), subintr(subintr_), hasArg(false) {}
PendingInterrupt(int intr_, int subintr_, int arg_)
: intr(intr_), subintr(subintr_), hasArg(true), arg(arg_) {}
u32 intr;
u32 subintr;
bool hasArg;
int arg;
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,
};
class SubIntrHandler
struct PendingInterrupt {
PendingInterrupt(int intr_, int subintr_)
: intr(intr_), subintr(subintr_) {}
int intr;
int subintr;
};
struct SubIntrHandler
{
public:
SubIntrHandler() {}
virtual ~SubIntrHandler() {}
virtual void queueUp();
virtual void queueUpWithArg(int arg);
virtual void copyArgsToCPU(const PendingInterrupt &pend);
virtual void handleResult(int result) {}
virtual void DoState(PointerWrap &p)
{
p.Do(enabled);
p.Do(intrNumber);
p.Do(number);
p.Do(handlerAddress);
p.Do(handlerArg);
p.DoMarker("SubIntrHandler");
}
bool enabled;
int intrNumber;
int number;
int subIntrNumber;
u32 handlerAddress;
u32 handlerArg;
};
typedef SubIntrHandler *(*SubIntrCreator)();
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);
void 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();
void DoState(PointerWrap &p);
private:
int intrNumber;
std::map<int, SubIntrHandler> subIntrHandlers;
};
bool __IsInInterrupt();
void __InterruptsInit();
@ -115,11 +130,10 @@ void __InterruptsDoState(PointerWrap &p);
void __InterruptsDoStateLate(PointerWrap &p);
void __InterruptsShutdown();
void __TriggerInterrupt(int type, PSPInterrupt intno, int subInterrupts = -1);
void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg); // For GE "callbacks"
bool __RunOnePendingInterrupt();
void __KernelReturnFromInterrupt();
void __RegisterSubIntrCreator(u32 intrNumber, SubIntrCreator creator);
void __RegisterIntrHandler(u32 intrNumber, IntrHandler* handler);
SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error);
u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber);

View File

@ -33,6 +33,7 @@
#include "../../Core/HLE/sceKernelThread.h"
#include "../../Core/HLE/sceKernelInterrupt.h"
#include "../../Core/HLE/sceGe.h"
extern u32 curTextureWidth;
extern u32 curTextureHeight;
@ -439,13 +440,15 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
case GE_CMD_SIGNAL:
{
// Processed in GE_END. Has data.
currentList->subIntrToken = data & 0xFFFF;
}
break;
case GE_CMD_FINISH:
currentList->subIntrToken = data & 0xFFFF;
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_FINISH, 0);
__GeTriggerInterrupt(currentList->id, currentList->pc);
break;
case GE_CMD_END:
@ -483,7 +486,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) {
}
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_SIGNAL, signal);
__GeTriggerInterrupt(currentList->id, currentList->pc);
}
break;
case GE_CMD_FINISH:

View File

@ -135,3 +135,13 @@ void GPUCommon::DoState(PointerWrap &p) {
p.Do<DisplayList>(dlQueue);
p.DoMarker("GPUCommon");
}
void GPUCommon::InterruptStart()
{
interruptRunning = true;
}
void GPUCommon::InterruptEnd()
{
interruptRunning = false;
ProcessDLQueue();
}

View File

@ -13,6 +13,9 @@ public:
dumpThisFrame_(false)
{}
virtual void InterruptStart();
virtual void InterruptEnd();
virtual void PreExecuteOp(u32 op, u32 diff);
virtual bool InterpretList(DisplayList &list);
virtual bool ProcessDLQueue();
@ -28,6 +31,7 @@ protected:
DisplayList *currentList;
DisplayListQueue dlQueue;
bool interruptRunning;
u32 prev;
u32 stack[2];
u32 stackptr;
@ -35,4 +39,18 @@ protected:
bool dumpNextFrame_;
bool dumpThisFrame_;
};
public:
virtual DisplayList* getList(int listid)
{
if(currentList->id == listid)
return currentList;
for(auto it = dlQueue.begin(); it != dlQueue.end(); ++it)
{
if(it->id == listid)
return &*it;
}
return NULL;
}
};

View File

@ -38,6 +38,7 @@ struct DisplayList
u32 stall;
DisplayListStatus status;
int subIntrBase;
u16 subIntrToken;
};
class GPUInterface
@ -49,12 +50,16 @@ public:
virtual void InitClear() = 0;
// Draw queue management
virtual DisplayList* getList(int listid) = 0;
// TODO: Much of this should probably be shared between the different GPU implementations.
virtual u32 EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head) = 0;
virtual void UpdateStall(int listid, u32 newstall) = 0;
virtual void DrawSync(int mode) = 0;
virtual void Continue() = 0;
virtual void InterruptStart() = 0;
virtual void InterruptEnd() = 0;
virtual void PreExecuteOp(u32 op, u32 diff) = 0;
virtual void ExecuteOp(u32 op, u32 diff) = 0;
virtual bool InterpretList(DisplayList& list) = 0;

View File

@ -21,6 +21,7 @@
#include "../ge_constants.h"
#include "../../Core/MemMap.h"
#include "../../Core/HLE/sceKernelInterrupt.h"
#include "../../Core/HLE/sceGe.h"
NullGPU::NullGPU()
{
@ -149,11 +150,11 @@ void NullGPU::ExecuteOp(u32 op, u32 diff)
{
ERROR_LOG(G3D, "DL GE_CMD_SIGNAL %08x", data & 0xFFFFFF);
int behaviour = (data >> 16) & 0xFF;
int signal = data & 0xFFFF;
currentList->subIntrToken = data & 0xFFFF;
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_SIGNAL, signal);
__GeTriggerInterrupt(currentList->id, currentList->pc);
}
break;
@ -184,9 +185,10 @@ void NullGPU::ExecuteOp(u32 op, u32 diff)
case GE_CMD_FINISH:
DEBUG_LOG(G3D,"DL CMD FINISH");
currentList->subIntrToken = data & 0xFFFF;
// TODO: Should this run while interrupts are suspended?
if (interruptsEnabled_)
__TriggerInterruptWithArg(PSP_INTR_HLE, PSP_GE_INTR, currentList->subIntrBase | PSP_GE_SUBINTR_FINISH, 0);
__GeTriggerInterrupt(currentList->id, currentList->pc);
break;
case GE_CMD_END: