sys_interrupt improved

This commit is contained in:
Nekotekina 2015-07-13 00:02:02 +03:00
parent b8a27d8a4c
commit 3bc6c53eb3
14 changed files with 227 additions and 171 deletions

View File

@ -155,7 +155,7 @@ bool RawSPUThread::WriteReg(const u32 addr, const u32 value)
if (value)
{
int2.set(SPU_INT2_STAT_DMA_TAG_GROUP_COMPLETION_INT); // TODO
int_ctrl[2].set(SPU_INT2_STAT_DMA_TAG_GROUP_COMPLETION_INT); // TODO
}
return true;

View File

@ -10,6 +10,7 @@
#include "Emu/SysCalls/lv2/sys_spu.h"
#include "Emu/SysCalls/lv2/sys_event_flag.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "Emu/SysCalls/lv2/sys_interrupt.h"
#include "Emu/Cell/SPUDisAsm.h"
#include "Emu/Cell/SPUThread.h"
@ -22,6 +23,30 @@
extern u64 get_timebased_time();
void spu_int_ctrl_t::set(u64 ints)
{
// leave only enabled interrupts
ints &= mask.load();
// notify if at least 1 bit was set
if (ints && ~stat._or(ints) & ints && tag)
{
LV2_LOCK;
if (tag && tag->handler)
{
tag->handler->signal++;
tag->handler->thread->cv.notify_one();
}
}
}
void spu_int_ctrl_t::clear(u64 ints)
{
stat &= ~ints;
}
const g_spu_imm_table_t g_spu_imm;
class spu_inter_func_list_t
@ -193,8 +218,7 @@ void SPUThread::InitRegs()
status = {};
npc = {};
int0.clear();
int2.clear();
int_ctrl = {};
GPR[1]._u32[3] = 0x3FFF0; // initial stack frame pointer
}
@ -572,7 +596,7 @@ u32 SPUThread::get_ch_value(u32 ch)
if (count + 1 == 4 /* SPU_IN_MBOX_THRESHOLD */) // TODO: check this
{
int2.set(SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT);
int_ctrl[2].set(SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT);
}
return result;
@ -684,7 +708,7 @@ void SPUThread::set_ch_value(u32 ch, u32 value)
cv.wait_for(lock, std::chrono::milliseconds(1));
}
int2.set(SPU_INT2_STAT_MAILBOX_INT);
int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT);
return;
}
else
@ -1030,7 +1054,7 @@ void SPUThread::stop_and_signal(u32 code)
status &= ~SPU_STATUS_RUNNING;
});
int2.set(SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT);
int_ctrl[2].set(SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT);
return Stop();
}
@ -1295,7 +1319,7 @@ void SPUThread::halt()
status &= ~SPU_STATUS_RUNNING;
});
int2.set(SPU_INT2_STAT_SPU_HALT_OR_STEP_INT);
int_ctrl[2].set(SPU_INT2_STAT_SPU_HALT_OR_STEP_INT);
return Stop();
}

View File

@ -6,6 +6,7 @@
struct lv2_event_queue_t;
struct spu_group_t;
struct lv2_int_tag_t;
// SPU Channels
enum : u32
@ -276,41 +277,16 @@ public:
}
};
struct spu_interrupt_tag_t
struct spu_int_ctrl_t
{
atomic_t<u64> mask;
atomic_t<u64> stat;
atomic_t<s32> assigned;
std::shared_ptr<struct lv2_int_tag_t> tag;
std::mutex handler_mutex;
std::condition_variable cond;
void set(u64 ints);
public:
void set(u64 ints)
{
// leave only enabled interrupts
ints &= mask.load();
if (ints && ~stat._or(ints) & ints)
{
// notify if at least 1 bit was set
cond.notify_all();
}
}
void clear(u64 ints)
{
stat &= ~ints;
}
void clear()
{
mask = {};
stat = {};
assigned = { -1 };
}
void clear(u64 ints);
};
struct g_spu_imm_table_t
@ -533,8 +509,7 @@ public:
atomic_t<u32> status; // SPU Status register
atomic_t<u32> npc; // SPU Next Program Counter register
spu_interrupt_tag_t int0; // SPU Class 0 Interrupt Management
spu_interrupt_tag_t int2; // SPU Class 2 Interrupt Management
std::array<spu_int_ctrl_t, 3> int_ctrl; // SPU Class 0, 1, 2 Interrupt Management
std::weak_ptr<spu_group_t> tg; // SPU Thread Group

View File

@ -63,7 +63,7 @@ std::string SysCalls::GetFuncName(const u64 fid)
case 81: return "sys_interrupt_tag_destroy";
case 82: return "sys_event_flag_create";
case 83: return "sys_event_flag_destroy";
case 84: return "sys_interrupt_thread_establish";
case 84: return "_sys_interrupt_thread_establish";
case 85: return "sys_event_flag_wait";
case 86: return "sys_event_flag_trywait";
case 87: return "sys_event_flag_set";

View File

@ -823,7 +823,7 @@ s32 sys_interrupt_thread_disestablish(PPUThread& CPU, u32 ih)
{
sysPrxForUser.Todo("sys_interrupt_thread_disestablish(ih=0x%x)", ih);
return _sys_interrupt_thread_disestablish(ih, vm::stackvar<be_t<u64>>(CPU));
return _sys_interrupt_thread_disestablish(CPU, ih, vm::stackvar<be_t<u64>>(CPU));
}
s32 sys_process_is_stack(u32 p)

View File

@ -124,7 +124,7 @@ const ppu_func_caller g_sc_table[1024] =
bind_func(sys_interrupt_tag_destroy), //81 (0x051)
bind_func(sys_event_flag_create), //82 (0x052)
bind_func(sys_event_flag_destroy), //83 (0x053)
bind_func(sys_interrupt_thread_establish), //84 (0x054)
bind_func(_sys_interrupt_thread_establish), //84 (0x054)
bind_func(sys_event_flag_wait), //85 (0x055)
bind_func(sys_event_flag_trywait), //86 (0x056)
bind_func(sys_event_flag_set), //87 (0x057)

View File

@ -102,7 +102,7 @@ struct lv2_event_queue_t
lv2_event_queue_t(u32 protocol, s32 type, u64 name, u64 key, s32 size);
void push(lv2_lock_type& lv2_lock, u64 source, u64 data1, u64 data2, u64 data3)
void push(lv2_lock_t& lv2_lock, u64 source, u64 data1, u64 data2, u64 data3)
{
CHECK_LV2_LOCK(lv2_lock);

View File

@ -3,70 +3,83 @@
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/SysCalls/SysCalls.h"
#include "Emu/SysCalls/CB_FUNC.h"
#include "Emu/CPU/CPUThreadManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/RawSPUThread.h"
#include "sys_interrupt.h"
SysCallBase sys_interrupt("sys_interrupt");
lv2_int_tag_t::lv2_int_tag_t()
: id(Emu.GetIdManager().get_current_id())
{
}
lv2_int_serv_t::lv2_int_serv_t(const std::shared_ptr<PPUThread>& thread)
: thread(thread)
, id(Emu.GetIdManager().get_current_id())
{
}
void lv2_int_serv_t::join(PPUThread& ppu, lv2_lock_t& lv2_lock)
{
CHECK_LV2_LOCK(lv2_lock);
// Use is_joining to stop interrupt thread and signal
thread->is_joining = true;
thread->cv.notify_one();
// Start joining
while (thread->IsActive())
{
CHECK_EMU_STATUS;
ppu.cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
// Cleanup
Emu.GetIdManager().remove<lv2_int_serv_t>(id);
Emu.GetIdManager().remove<PPUThread>(thread->GetId());
}
s32 sys_interrupt_tag_destroy(u32 intrtag)
{
sys_interrupt.Warning("sys_interrupt_tag_destroy(intrtag=0x%x)", intrtag);
const u32 class_id = intrtag >> 8;
LV2_LOCK;
if (class_id != 0 && class_id != 2)
const auto tag = Emu.GetIdManager().get<lv2_int_tag_t>(intrtag);
if (!tag)
{
return CELL_ESRCH;
}
const auto thread = Emu.GetCPU().GetRawSPUThread(intrtag & 0xff);
if (!thread)
if (tag->handler)
{
return CELL_ESRCH;
return CELL_EBUSY;
}
auto& tag = class_id ? thread->int2 : thread->int0;
if (s32 old = tag.assigned.compare_and_swap(0, -1))
{
if (old > 0)
{
return CELL_EBUSY;
}
return CELL_ESRCH;
}
Emu.GetIdManager().remove<lv2_int_tag_t>(intrtag);
return CELL_OK;
}
s32 sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg)
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
{
sys_interrupt.Warning("sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, arg=0x%llx)", ih, intrtag, intrthread, arg);
sys_interrupt.Warning("_sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, arg1=0x%llx, arg2=0x%llx)", ih, intrtag, intrthread, arg1, arg2);
const u32 class_id = intrtag >> 8;
LV2_LOCK;
if (class_id != 0 && class_id != 2)
// Get interrupt tag
const auto tag = Emu.GetIdManager().get<lv2_int_tag_t>(intrtag);
if (!tag)
{
return CELL_ESRCH;
}
const auto thread = Emu.GetCPU().GetRawSPUThread(intrtag & 0xff);
if (!thread)
{
return CELL_ESRCH;
}
auto& tag = class_id ? thread->int2 : thread->int0;
// CELL_ESTAT is not returned (can't detect exact condition)
// Get interrupt thread
const auto it = Emu.GetIdManager().get<PPUThread>(intrthread);
if (!it)
@ -74,82 +87,95 @@ s32 sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread,
return CELL_ESRCH;
}
// If interrupt thread is running, it's already established on another interrupt tag
if (!it->IsStopped())
{
LV2_LOCK;
if (it->custom_task)
{
return CELL_EAGAIN;
}
if (s32 res = tag.assigned.atomic_op([](s32& value) -> s32
{
if (value < 0)
{
return CELL_ESRCH;
}
value++;
return CELL_OK;
}))
{
return res;
}
it->custom_task = [thread, &tag, arg](PPUThread& CPU)
{
const u32 pc = CPU.PC;
const u32 rtoc = CPU.GPR[2];
std::unique_lock<std::mutex> lock(tag.handler_mutex);
while (!CPU.IsStopped())
{
CHECK_EMU_STATUS;
// call interrupt handler until int status is clear
if (tag.stat.load())
{
CPU.GPR[3] = arg;
CPU.FastCall2(pc, rtoc);
}
tag.cond.wait_for(lock, std::chrono::milliseconds(1));
}
};
return CELL_EAGAIN;
}
*ih = Emu.GetIdManager().make<lv2_int_handler_t>(it);
// It's unclear if multiple handlers can be established on single interrupt tag
if (tag->handler)
{
sys_interrupt.Error("_sys_interrupt_thread_establish(): handler service already exists (intrtag=0x%x) -> CELL_ESTAT", intrtag);
return CELL_ESTAT;
}
const auto handler = Emu.GetIdManager().make_ptr<lv2_int_serv_t>(it);
tag->handler = handler;
it->custom_task = [handler, arg1, arg2](PPUThread& ppu)
{
const u32 pc = ppu.PC;
const u32 rtoc = ppu.GPR[2];
LV2_LOCK;
while (!ppu.is_joining)
{
CHECK_EMU_STATUS;
// call interrupt handler until int status is clear
if (handler->signal)
{
if (lv2_lock) lv2_lock.unlock();
ppu.GPR[3] = arg1;
ppu.GPR[4] = arg2;
ppu.FastCall2(pc, rtoc);
handler->signal--;
continue;
}
if (!lv2_lock)
{
lv2_lock.lock();
continue;
}
ppu.cv.wait(lv2_lock);
}
ppu.Exit();
};
it->Exec();
*ih = handler->id;
return CELL_OK;
}
s32 _sys_interrupt_thread_disestablish(u32 ih, vm::ptr<u64> r13)
s32 _sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih, vm::ptr<u64> r13)
{
sys_interrupt.Todo("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
sys_interrupt.Warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
const auto handler = Emu.GetIdManager().get<lv2_int_handler_t>(ih);
LV2_LOCK;
const auto handler = Emu.GetIdManager().get<lv2_int_serv_t>(ih);
if (!handler)
{
return CELL_ESRCH;
}
// TODO: wait for sys_interrupt_thread_eoi() and destroy interrupt thread
// Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
handler->join(ppu, lv2_lock);
// Save TLS base
*r13 = handler->thread->GPR[13];
return CELL_OK;
}
void sys_interrupt_thread_eoi(PPUThread& CPU)
void sys_interrupt_thread_eoi(PPUThread& ppu)
{
sys_interrupt.Log("sys_interrupt_thread_eoi()");
// 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 bypass stack check
ppu.GPR[1] = align(ppu.stack_addr + ppu.stack_size, 0x200) - 0x200; // supercrutch to bypass stack check
CPU.FastStop();
ppu.FastStop();
}

View File

@ -4,20 +4,36 @@ namespace vm { using namespace ps3; }
class PPUThread;
struct lv2_int_handler_t
struct lv2_int_tag_t
{
const std::shared_ptr<PPUThread> thread;
//const u32 class_id; // 0 or 2 for RawSPU
const u32 id;
lv2_int_handler_t(const std::shared_ptr<PPUThread>& thread)
: thread(thread)
{
}
//const std::weak_ptr<class RawSPUThread> thread; // RawSPU thread
std::shared_ptr<struct lv2_int_serv_t> handler;
lv2_int_tag_t(/*u32 class_id, const std::shared_ptr<RawSPUThread> thread*/);
};
REG_ID_TYPE(lv2_int_handler_t, 0x0B); // SYS_INTR_SERVICE_HANDLE_OBJECT
REG_ID_TYPE(lv2_int_tag_t, 0x0A); // SYS_INTR_TAG_OBJECT
struct lv2_int_serv_t
{
const std::shared_ptr<PPUThread> thread;
const u32 id;
std::atomic<u32> signal{ 0 }; // signal count
lv2_int_serv_t(const std::shared_ptr<PPUThread>& thread);
void join(PPUThread& ppu, lv2_lock_t& lv2_lock);
};
REG_ID_TYPE(lv2_int_serv_t, 0x0B); // SYS_INTR_SERVICE_HANDLE_OBJECT
// SysCalls
s32 sys_interrupt_tag_destroy(u32 intrtag);
s32 sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg);
s32 _sys_interrupt_thread_disestablish(u32 ih, vm::ptr<u64> r13);
void sys_interrupt_thread_eoi(PPUThread& CPU);
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2);
s32 _sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih, vm::ptr<u64> r13);
void sys_interrupt_thread_eoi(PPUThread& ppu);

View File

@ -11,7 +11,7 @@ SysCallBase sys_mutex("sys_mutex");
extern u64 get_system_time();
void lv2_mutex_t::unlock(lv2_lock_type& lv2_lock)
void lv2_mutex_t::unlock(lv2_lock_t& lv2_lock)
{
CHECK_LV2_LOCK(lv2_lock);

View File

@ -40,7 +40,7 @@ struct lv2_mutex_t
{
}
void unlock(lv2_lock_type& lv2_lock);
void unlock(lv2_lock_t& lv2_lock);
};
REG_ID_TYPE(lv2_mutex_t, 0x85); // SYS_MUTEX_OBJECT

View File

@ -10,6 +10,7 @@
#include "Emu/FS/vfsFile.h"
#include "Loader/ELF32.h"
#include "Crypto/unself.h"
#include "sys_interrupt.h"
#include "sys_event.h"
#include "sys_spu.h"
@ -609,7 +610,7 @@ s32 sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type)
throw EXCEPTION("Invalid SPU thread group");
}
if ((group->state < SPU_THREAD_GROUP_STATUS_WAITING) || (group->state > SPU_THREAD_GROUP_STATUS_RUNNING))
if (group->state < SPU_THREAD_GROUP_STATUS_WAITING || group->state > SPU_THREAD_GROUP_STATUS_RUNNING)
{
return CELL_ESTAT;
}
@ -651,7 +652,7 @@ s32 sys_spu_thread_read_ls(u32 id, u32 address, vm::ptr<u64> value, u32 type)
throw EXCEPTION("Invalid SPU thread group");
}
if ((group->state < SPU_THREAD_GROUP_STATUS_WAITING) || (group->state > SPU_THREAD_GROUP_STATUS_RUNNING))
if (group->state < SPU_THREAD_GROUP_STATUS_WAITING || group->state > SPU_THREAD_GROUP_STATUS_RUNNING)
{
return CELL_ESTAT;
}
@ -688,7 +689,7 @@ s32 sys_spu_thread_write_spu_mb(u32 id, u32 value)
throw EXCEPTION("Invalid SPU thread group");
}
if ((group->state < SPU_THREAD_GROUP_STATUS_WAITING) || (group->state > SPU_THREAD_GROUP_STATUS_RUNNING))
if (group->state < SPU_THREAD_GROUP_STATUS_WAITING || group->state > SPU_THREAD_GROUP_STATUS_RUNNING)
{
return CELL_ESTAT;
}
@ -765,7 +766,7 @@ s32 sys_spu_thread_write_snr(u32 id, u32 number, u32 value)
throw EXCEPTION("Invalid SPU thread group");
}
//if ((group->state < SPU_THREAD_GROUP_STATUS_WAITING) || (group->state > SPU_THREAD_GROUP_STATUS_RUNNING)) // ???
//if (group->state < SPU_THREAD_GROUP_STATUS_WAITING || group->state > SPU_THREAD_GROUP_STATUS_RUNNING) // ???
//{
// return CELL_ESTAT;
//}
@ -1149,7 +1150,7 @@ s32 sys_raw_spu_create(vm::ptr<u32> id, vm::ptr<void> attr)
return CELL_OK;
}
s32 sys_raw_spu_destroy(u32 id)
s32 sys_raw_spu_destroy(PPUThread& ppu, u32 id)
{
sys_spu.Warning("sys_raw_spu_destroy(id=%d)", id);
@ -1162,7 +1163,24 @@ s32 sys_raw_spu_destroy(u32 id)
return CELL_ESRCH;
}
// TODO: check if busy
// TODO: CELL_EBUSY is not returned
// Stop thread
thread->Stop();
// Clear interrupt handlers
for (auto& intr : thread->int_ctrl)
{
if (intr.tag)
{
if (intr.tag->handler)
{
intr.tag->handler->join(ppu, lv2_lock);
}
Emu.GetIdManager().remove<lv2_int_tag_t>(intr.tag->id);
}
}
Emu.GetIdManager().remove<RawSPUThread>(thread->GetId());
@ -1173,10 +1191,7 @@ s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr
{
sys_spu.Warning("sys_raw_spu_create_interrupt_tag(id=%d, class_id=%d, hwthread=0x%x, intrtag=*0x%x)", id, class_id, hwthread, intrtag);
if (class_id != 0 && class_id != 2)
{
return CELL_EINVAL;
}
LV2_LOCK;
const auto thread = Emu.GetCPU().GetRawSPUThread(id);
@ -1185,14 +1200,21 @@ s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr
return CELL_ESRCH;
}
auto& tag = class_id ? thread->int2 : thread->int0;
if (class_id != 0 && class_id != 2)
{
return CELL_EINVAL;
}
if (!tag.assigned.compare_and_swap_test(-1, 0))
auto& int_ctrl = thread->int_ctrl[class_id];
if (int_ctrl.tag)
{
return CELL_EAGAIN;
}
*intrtag = (id & 0xff) | (class_id << 8);
int_ctrl.tag = Emu.GetIdManager().make_ptr<lv2_int_tag_t>();
*intrtag = int_ctrl.tag->id;
return CELL_OK;
}
@ -1213,9 +1235,7 @@ s32 sys_raw_spu_set_int_mask(u32 id, u32 class_id, u64 mask)
return CELL_ESRCH;
}
auto& tag = class_id ? thread->int2 : thread->int0;
tag.mask.exchange(mask);
thread->int_ctrl[class_id].mask.exchange(mask);
return CELL_OK;
}
@ -1236,9 +1256,7 @@ s32 sys_raw_spu_get_int_mask(u32 id, u32 class_id, vm::ptr<u64> mask)
return CELL_ESRCH;
}
auto& tag = class_id ? thread->int2 : thread->int0;
*mask = tag.mask.load();
*mask = thread->int_ctrl[class_id].mask.load();
return CELL_OK;
}
@ -1259,9 +1277,7 @@ s32 sys_raw_spu_set_int_stat(u32 id, u32 class_id, u64 stat)
return CELL_ESRCH;
}
auto& tag = class_id ? thread->int2 : thread->int0;
tag.clear(stat);
thread->int_ctrl[class_id].clear(stat);
return CELL_OK;
}
@ -1282,9 +1298,7 @@ s32 sys_raw_spu_get_int_stat(u32 id, u32 class_id, vm::ptr<u64> stat)
return CELL_ESRCH;
}
auto& tag = class_id ? thread->int2 : thread->int0;
*stat = tag.stat.load();
*stat = thread->int_ctrl[class_id].stat.load();
return CELL_OK;
}

View File

@ -177,7 +177,7 @@ struct spu_group_t
{
}
void send_run_event(lv2_lock_type& lv2_lock, u64 data1, u64 data2, u64 data3)
void send_run_event(lv2_lock_t& lv2_lock, u64 data1, u64 data2, u64 data3)
{
CHECK_LV2_LOCK(lv2_lock);
@ -187,7 +187,7 @@ struct spu_group_t
}
}
void send_exception_event(lv2_lock_type& lv2_lock, u64 data1, u64 data2, u64 data3)
void send_exception_event(lv2_lock_t& lv2_lock, u64 data1, u64 data2, u64 data3)
{
CHECK_LV2_LOCK(lv2_lock);
@ -197,7 +197,7 @@ struct spu_group_t
}
}
void send_sysmodule_event(lv2_lock_type& lv2_lock, u64 data1, u64 data2, u64 data3)
void send_sysmodule_event(lv2_lock_t& lv2_lock, u64 data1, u64 data2, u64 data3)
{
CHECK_LV2_LOCK(lv2_lock);
@ -209,6 +209,7 @@ struct spu_group_t
};
struct vfsStream;
class PPUThread;
void LoadSpuImage(vfsStream& stream, u32& spu_ep, u32 addr);
u32 LoadSpuImage(vfsStream& stream, u32& spu_ep);
@ -247,7 +248,7 @@ s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num);
s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr<u32> status);
s32 sys_raw_spu_create(vm::ptr<u32> id, vm::ptr<void> attr);
s32 sys_raw_spu_destroy(u32 id);
s32 sys_raw_spu_destroy(PPUThread& ppu, u32 id);
s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr<u32> intrtag);
s32 sys_raw_spu_set_int_mask(u32 id, u32 class_id, u64 mask);
s32 sys_raw_spu_get_int_mask(u32 id, u32 class_id, vm::ptr<u64> mask);

View File

@ -200,10 +200,10 @@ public:
force_inline bool IsReady() const { return m_status == Ready; }
};
using lv2_lock_type = std::unique_lock<std::mutex>;
using lv2_lock_t = std::unique_lock<std::mutex>;
#define LV2_LOCK lv2_lock_type lv2_lock(Emu.GetCoreMutex())
#define LV2_DEFER_LOCK lv2_lock_type lv2_lock
#define LV2_LOCK lv2_lock_t lv2_lock(Emu.GetCoreMutex())
#define LV2_DEFER_LOCK lv2_lock_t lv2_lock
#define CHECK_LV2_LOCK(x) if (!(x).owns_lock() || (x).mutex() != &Emu.GetCoreMutex()) throw EXCEPTION("Invalid LV2_LOCK (locked=%d)", (x).owns_lock())
#define CHECK_EMU_STATUS if (Emu.IsStopped()) throw EXCEPTION("Aborted (emulation stopped)")