mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-20 17:00:32 +00:00
Interrupt handler reorganization for easier GE interrupt support
This commit is contained in:
parent
522983549d
commit
8004d360dd
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user