rpcsx/rpcs3/Emu/SysCalls/lv2/sys_interrupt.cpp

157 lines
3 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
2015-03-06 23:58:42 +01:00
#include "Emu/IdManager.h"
2014-08-23 16:51:51 +02:00
#include "Emu/SysCalls/SysCalls.h"
2015-03-02 22:09:20 +01:00
#include "Emu/SysCalls/CB_FUNC.h"
2014-08-23 16:51:51 +02:00
2014-08-26 01:55:37 +02:00
#include "Emu/CPU/CPUThreadManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/RawSPUThread.h"
#include "sys_interrupt.h"
2015-03-02 22:09:20 +01:00
SysCallBase sys_interrupt("sys_interrupt");
s32 sys_interrupt_tag_destroy(u32 intrtag)
{
2015-03-24 16:17:53 +01:00
sys_interrupt.Warning("sys_interrupt_tag_destroy(intrtag=0x%x)", intrtag);
2015-03-02 22:09:20 +01:00
const u32 class_id = intrtag >> 8;
2015-03-02 22:09:20 +01:00
if (class_id != 0 && class_id != 2)
{
return CELL_ESRCH;
}
2015-04-12 03:36:25 +02:00
const auto t = Emu.GetCPU().GetRawSPUThread(intrtag & 0xff);
2015-03-02 22:09:20 +01:00
if (!t)
{
return CELL_ESRCH;
}
2015-03-02 22:09:20 +01:00
RawSPUThread& spu = static_cast<RawSPUThread&>(*t);
auto& tag = class_id ? spu.int2 : spu.int0;
if (s32 old = tag.assigned.compare_and_swap(0, -1))
{
2015-03-02 22:09:20 +01:00
if (old > 0)
{
return CELL_EBUSY;
}
return CELL_ESRCH;
}
return CELL_OK;
}
2014-10-11 19:20:01 +02:00
s32 sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u64 intrthread, u64 arg)
{
2015-03-24 16:17:53 +01:00
sys_interrupt.Warning("sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=%lld, arg=0x%llx)", ih, intrtag, intrthread, arg);
2015-03-02 22:09:20 +01:00
const u32 class_id = intrtag >> 8;
2015-03-02 22:09:20 +01:00
if (class_id != 0 && class_id != 2)
{
return CELL_ESRCH;
}
2015-04-12 03:36:25 +02:00
const auto t = Emu.GetCPU().GetRawSPUThread(intrtag & 0xff);
2015-03-02 22:09:20 +01:00
if (!t)
{
return CELL_ESRCH;
}
2015-03-02 22:09:20 +01:00
RawSPUThread& spu = static_cast<RawSPUThread&>(*t);
auto& tag = class_id ? spu.int2 : spu.int0;
2015-03-04 22:51:14 +01:00
// CELL_ESTAT is not returned (can't detect exact condition)
2015-03-02 22:09:20 +01:00
2015-04-12 03:36:25 +02:00
const auto it = Emu.GetCPU().GetThread((u32)intrthread);
if (!it)
{
return CELL_ESRCH;
}
2015-03-02 22:09:20 +01:00
std::shared_ptr<interrupt_handler_t> handler(new interrupt_handler_t{ it });
PPUThread& ppu = static_cast<PPUThread&>(*it);
{
2015-03-04 05:42:04 +01:00
LV2_LOCK;
2015-03-02 22:09:20 +01:00
if (ppu.custom_task)
{
return CELL_EAGAIN;
}
if (s32 res = tag.assigned.atomic_op<s32>(CELL_OK, [](s32& value) -> s32
{
if (value < 0)
{
return CELL_ESRCH;
}
value++;
return CELL_OK;
}))
{
return res;
}
ppu.custom_task = [t, &tag, arg](PPUThread& CPU)
{
auto func = vm::ptr<void(u64 arg)>::make(CPU.entry);
std::unique_lock<std::mutex> cond_lock(tag.handler_mutex);
while (!Emu.IsStopped())
{
if (tag.stat.read_relaxed())
{
func(CPU, arg); // call interrupt handler until int status is clear
}
tag.cond.wait_for(cond_lock, std::chrono::milliseconds(1));
}
};
}
2015-03-06 23:10:04 +01:00
*ih = Emu.GetIdManager().GetNewID(handler, TYPE_INTR_SERVICE_HANDLE);
2015-03-02 22:09:20 +01:00
ppu.Exec();
return CELL_OK;
}
2015-03-02 22:09:20 +01:00
s32 _sys_interrupt_thread_disestablish(u32 ih, vm::ptr<u64> r13)
{
2015-03-24 16:17:53 +01:00
sys_interrupt.Todo("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
2015-04-12 03:36:25 +02:00
const auto handler = Emu.GetIdManager().GetIDData<interrupt_handler_t>(ih);
if (!handler)
{
return CELL_ESRCH;
}
2015-03-02 22:09:20 +01:00
PPUThread& ppu = static_cast<PPUThread&>(*handler->handler);
// TODO: wait for sys_interrupt_thread_eoi() and destroy interrupt thread
2015-03-02 22:09:20 +01:00
*r13 = ppu.GPR[13];
return CELL_OK;
}
2015-03-24 16:17:53 +01:00
void sys_interrupt_thread_eoi(PPUThread& CPU)
{
2014-08-23 16:51:51 +02:00
sys_interrupt.Log("sys_interrupt_thread_eoi()");
2015-03-24 16:17:53 +01:00
CPU.FastStop();
2014-07-12 09:46:14 +02:00
}