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

156 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-07-03 18:07:36 +02:00
const auto thread = Emu.GetCPU().GetRawSPUThread(intrtag & 0xff);
2015-03-02 22:09:20 +01:00
2015-07-03 18:07:36 +02:00
if (!thread)
{
return CELL_ESRCH;
}
2015-07-03 18:07:36 +02:00
auto& tag = class_id ? thread->int2 : thread->int0;
2015-03-02 22:09:20 +01:00
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;
}
2015-07-01 00:25:52 +02:00
s32 sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg)
{
2015-07-01 00:25:52 +02:00
sys_interrupt.Warning("sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, 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-07-03 18:07:36 +02:00
const auto thread = Emu.GetCPU().GetRawSPUThread(intrtag & 0xff);
2015-03-02 22:09:20 +01:00
2015-07-03 18:07:36 +02:00
if (!thread)
{
return CELL_ESRCH;
}
2015-07-03 18:07:36 +02:00
auto& tag = class_id ? thread->int2 : thread->int0;
2015-03-02 22:09:20 +01:00
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-07-01 00:25:52 +02:00
const auto it = Emu.GetIdManager().get<PPUThread>(intrthread);
if (!it)
{
return CELL_ESRCH;
}
{
2015-03-04 05:42:04 +01:00
LV2_LOCK;
2015-03-02 22:09:20 +01:00
2015-07-03 18:07:36 +02:00
if (it->custom_task)
2015-03-02 22:09:20 +01:00
{
return CELL_EAGAIN;
}
if (s32 res = tag.assigned.atomic_op([](s32& value) -> s32
2015-03-02 22:09:20 +01:00
{
if (value < 0)
{
return CELL_ESRCH;
}
value++;
return CELL_OK;
}))
{
return res;
}
2015-07-03 18:07:36 +02:00
it->custom_task = [thread, &tag, arg](PPUThread& CPU)
2015-03-02 22:09:20 +01:00
{
2015-07-01 00:25:52 +02:00
const auto pc = CPU.PC;
const auto rtoc = CPU.GPR[2];
2015-03-02 22:09:20 +01:00
std::unique_lock<std::mutex> cond_lock(tag.handler_mutex);
2015-07-04 01:22:24 +02:00
while (!CPU.IsStopped())
2015-03-02 22:09:20 +01:00
{
2015-07-04 01:22:24 +02:00
CHECK_EMU_STATUS;
2015-04-20 01:49:13 +02:00
// call interrupt handler until int status is clear
if (tag.stat.load())
2015-03-02 22:09:20 +01:00
{
2015-07-01 19:09:26 +02:00
CPU.GPR[3] = arg;
CPU.FastCall2(pc, rtoc);
2015-03-02 22:09:20 +01:00
}
tag.cond.wait_for(cond_lock, std::chrono::milliseconds(1));
}
};
}
*ih = Emu.GetIdManager().make<lv2_int_handler_t>(it);
2015-07-03 18:07:36 +02:00
it->Exec();
2015-03-02 22:09:20 +01:00
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);
const auto handler = Emu.GetIdManager().get<lv2_int_handler_t>(ih);
2015-04-12 03:36:25 +02:00
if (!handler)
{
return CELL_ESRCH;
}
// TODO: wait for sys_interrupt_thread_eoi() and destroy interrupt thread
2015-07-03 18:07:36 +02:00
*r13 = handler->thread->GPR[13];
2015-03-02 22:09:20 +01:00
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-07-03 18:07:36 +02:00
// TODO: maybe it should actually unwind the stack of PPU thread?
CPU.GPR[1] = align(CPU.stack_addr + CPU.stack_size, 0x200) - 0x200; // supercrutch to avoid stack check
2015-04-20 01:49:13 +02:00
2015-03-24 16:17:53 +01:00
CPU.FastStop();
2014-07-12 09:46:14 +02:00
}