diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 37e2f76173..cb0af16729 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -2,7 +2,9 @@ #include "Log.h" #include "rpcs3/Ini.h" #include "Emu/System.h" +#include "Emu/CPU/CPUThreadManager.h" #include "Emu/CPU/CPUThread.h" +#include "Emu/Cell/RawSPUThread.h" #include "Emu/SysCalls/SysCalls.h" #include "Thread.h" @@ -105,8 +107,8 @@ enum x64_reg_t : u32 enum x64_op_t : u32 { X64OP_NONE, - X64OP_LOAD, // obtain and put the value into x64 register (from Memory.ReadMMIO32, for example) - X64OP_STORE, // take the value from x64 register or an immediate and use it (pass in Memory.WriteMMIO32, for example) + X64OP_LOAD, // obtain and put the value into x64 register + X64OP_STORE, // take the value from x64 register or an immediate and use it // example: add eax,[rax] -> X64OP_LOAD_ADD (add the value to x64 register) // example: add [rax],eax -> X64OP_LOAD_ADD_STORE (this will probably never happen for MMIO registers) @@ -768,18 +770,27 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) // check if address is RawSPU MMIO register if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) { + auto t = Emu.GetCPU().GetRawSPUThread((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET); + + if (!t) + { + return false; + } + if (a_size != 4 || !d_size || !i_size) { LOG_ERROR(MEMORY, "Invalid or unsupported instruction (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", op, reg, d_size, a_size, i_size); return false; } + auto& spu = static_cast(*t); + switch (op) { case X64OP_LOAD: { u32 value; - if (is_writing || !Memory.ReadMMIO32(addr, value) || !put_x64_reg_value(context, reg, d_size, re32(value))) + if (is_writing || !spu.ReadReg(addr, value) || !put_x64_reg_value(context, reg, d_size, re32(value))) { return false; } @@ -789,7 +800,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) case X64OP_STORE: { u64 reg_value; - if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value) || !Memory.WriteMMIO32(addr, re32((u32)reg_value))) + if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value) || !spu.WriteReg(addr, re32((u32)reg_value))) { return false; } diff --git a/rpcs3/Emu/ARMv7/ARMv7Thread.cpp b/rpcs3/Emu/ARMv7/ARMv7Thread.cpp index 2e4d6c4ecb..e35e9c8f8d 100644 --- a/rpcs3/Emu/ARMv7/ARMv7Thread.cpp +++ b/rpcs3/Emu/ARMv7/ARMv7Thread.cpp @@ -232,7 +232,7 @@ void ARMv7Thread::FastStop() armv7_thread::armv7_thread(u32 entry, const std::string& name, u32 stack_size, s32 prio) { - thread = &Emu.GetCPU().AddThread(CPU_THREAD_ARMv7); + thread = Emu.GetCPU().AddThread(CPU_THREAD_ARMv7); thread->SetName(name); thread->SetEntry(entry); @@ -277,11 +277,13 @@ cpu_thread& armv7_thread::args(std::initializer_list values) cpu_thread& armv7_thread::run() { - thread->Run(); + auto& armv7 = static_cast(*thread); + + armv7.Run(); // set arguments - static_cast(thread)->context.GPR[0] = argc; - static_cast(thread)->context.GPR[1] = argv; + armv7.context.GPR[0] = argc; + armv7.context.GPR[1] = argv; return *this; } diff --git a/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp b/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp index 90b27abe84..8093a2b9dd 100644 --- a/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp +++ b/rpcs3/Emu/ARMv7/Modules/sceLibKernel.cpp @@ -47,18 +47,17 @@ s32 sceKernelCreateThread( sceLibKernel.Warning("sceKernelCreateThread(pName=0x%x, entry=0x%x, initPriority=%d, stackSize=0x%x, attr=0x%x, cpuAffinityMask=0x%x, pOptParam=0x%x)", pName, entry, initPriority, stackSize, attr, cpuAffinityMask, pOptParam); - ARMv7Thread& new_thread = static_cast(Emu.GetCPU().AddThread(CPU_THREAD_ARMv7)); + auto t = Emu.GetCPU().AddThread(CPU_THREAD_ARMv7); - const auto id = new_thread.GetId(); - new_thread.SetEntry(entry.addr()); - new_thread.SetPrio(initPriority); - new_thread.SetStackSize(stackSize); - new_thread.SetName(pName.get_ptr()); + auto& armv7 = static_cast(*t); - sceLibKernel.Warning("*** New ARMv7 Thread [%s] (entry=0x%x): id -> 0x%x", pName.get_ptr(), entry, id); + armv7.SetEntry(entry.addr()); + armv7.SetPrio(initPriority); + armv7.SetStackSize(stackSize); + armv7.SetName(pName.get_ptr()); + armv7.Run(); - new_thread.Run(); - return id; + return armv7.GetId(); } s32 sceKernelStartThread(s32 threadId, u32 argSize, vm::psv::ptr pArgBlock) diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 77de0a5529..13e8e7ebbf 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -112,6 +112,7 @@ public: u32 entry; u32 PC; u32 nPC; + u32 index; u32 offset; bool m_is_branch; bool m_trace_enabled; @@ -223,7 +224,7 @@ CPUThread* GetCurrentCPUThread(); class cpu_thread { protected: - CPUThread* thread; + std::shared_ptr thread; public: u32 get_entry() const diff --git a/rpcs3/Emu/CPU/CPUThreadManager.cpp b/rpcs3/Emu/CPU/CPUThreadManager.cpp index fe46a468d7..603c58e635 100644 --- a/rpcs3/Emu/CPU/CPUThreadManager.cpp +++ b/rpcs3/Emu/CPU/CPUThreadManager.cpp @@ -24,9 +24,9 @@ void CPUThreadManager::Close() while(m_threads.size()) RemoveThread(m_threads[0]->GetId()); } -CPUThread& CPUThreadManager::AddThread(CPUThreadType type) +std::shared_ptr CPUThreadManager::AddThread(CPUThreadType type) { - std::lock_guard lock(m_mtx_thread); + std::lock_guard lock(m_mutex); std::shared_ptr new_thread; @@ -44,7 +44,17 @@ CPUThread& CPUThreadManager::AddThread(CPUThreadType type) } case CPU_THREAD_RAW_SPU: { - new_thread.reset(new RawSPUThread()); + for (u32 i = 0; i < m_raw_spu.size(); i++) + { + if (!m_raw_spu[i]) + { + new_thread.reset(new RawSPUThread()); + new_thread->index = i; + + m_raw_spu[i] = new_thread; + break; + } + } break; } case CPU_THREAD_ARMv7: @@ -54,18 +64,21 @@ CPUThread& CPUThreadManager::AddThread(CPUThreadType type) } default: assert(0); } - - new_thread->SetId(Emu.GetIdManager().GetNewID(new_thread->GetTypeString() + " Thread", new_thread)); - m_threads.push_back(new_thread); - SendDbgCommand(DID_CREATE_THREAD, new_thread.get()); + if (new_thread) + { + new_thread->SetId(Emu.GetIdManager().GetNewID(new_thread->GetTypeString() + " Thread", new_thread)); - return *new_thread; + m_threads.push_back(new_thread); + SendDbgCommand(DID_CREATE_THREAD, new_thread.get()); + } + + return new_thread; } -void CPUThreadManager::RemoveThread(const u32 id) +void CPUThreadManager::RemoveThread(u32 id) { - std::lock_guard lock(m_mtx_thread); + std::lock_guard lock(m_mutex); std::shared_ptr thr; u32 thread_index = 0; @@ -84,6 +97,12 @@ void CPUThreadManager::RemoveThread(const u32 id) thr->Close(); m_threads.erase(m_threads.begin() + thread_index); + + if (thr->GetType() == CPU_THREAD_RAW_SPU) + { + assert(thr->index < m_raw_spu.size()); + m_raw_spu[thr->index] = nullptr; + } } // Removing the ID should trigger the actual deletion of the thread @@ -91,21 +110,6 @@ void CPUThreadManager::RemoveThread(const u32 id) Emu.CheckStatus(); } -s32 CPUThreadManager::GetThreadNumById(CPUThreadType type, u32 id) -{ - std::lock_guard lock(m_mtx_thread); - - s32 num = 0; - - for(u32 i=0; iGetId() == id) return num; - if(m_threads[i]->GetType() == type) num++; - } - - return -1; -} - std::shared_ptr CPUThreadManager::GetThread(u32 id) { std::shared_ptr res; @@ -130,21 +134,19 @@ std::shared_ptr CPUThreadManager::GetThread(u32 id, CPUThreadType typ return res; } -std::shared_ptr CPUThreadManager::GetRawSPUThread(u32 num) +std::shared_ptr CPUThreadManager::GetRawSPUThread(u32 index) { - if (num < sizeof(Memory.RawSPUMem) / sizeof(Memory.RawSPUMem[0])) - { - return GetThread(((RawSPUThread*)Memory.RawSPUMem[num])->GetId()); - } - else + if (index >= m_raw_spu.size()) { return nullptr; } + + return m_raw_spu[index]; } void CPUThreadManager::Exec() { - std::lock_guard lock(m_mtx_thread); + std::lock_guard lock(m_mutex); for(u32 i = 0; i < m_threads.size(); ++i) { diff --git a/rpcs3/Emu/CPU/CPUThreadManager.h b/rpcs3/Emu/CPU/CPUThreadManager.h index e42764eacc..f335b871da 100644 --- a/rpcs3/Emu/CPU/CPUThreadManager.h +++ b/rpcs3/Emu/CPU/CPUThreadManager.h @@ -6,8 +6,10 @@ enum CPUThreadType : unsigned char; class CPUThreadManager { + std::mutex m_mutex; + std::vector> m_threads; - std::mutex m_mtx_thread; + std::array, 5> m_raw_spu; public: CPUThreadManager(); @@ -15,14 +17,15 @@ public: void Close(); - CPUThread& AddThread(CPUThreadType type); - void RemoveThread(const u32 id); + std::shared_ptr AddThread(CPUThreadType type); + + void RemoveThread(u32 id); + + std::vector> GetThreads() { std::lock_guard lock(m_mutex); return m_threads; } - std::vector> GetThreads() { std::lock_guard lock(m_mtx_thread); return m_threads; } - s32 GetThreadNumById(CPUThreadType type, u32 id); std::shared_ptr GetThread(u32 id); std::shared_ptr GetThread(u32 id, CPUThreadType type); - std::shared_ptr GetRawSPUThread(u32 num); + std::shared_ptr GetRawSPUThread(u32 index); void Exec(); void Task(); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index f168a04235..b2a0410c4f 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -228,7 +228,7 @@ void PPUThread::Task() ppu_thread::ppu_thread(u32 entry, const std::string& name, u32 stack_size, u32 prio) { - thread = &Emu.GetCPU().AddThread(CPU_THREAD_PPU); + thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU); thread->SetName(name); thread->SetEntry(entry); @@ -277,7 +277,7 @@ ppu_thread& ppu_thread::gpr(uint index, u64 value) { assert(index < 32); - static_cast(thread)->GPR[index] = value; + static_cast(*thread).GPR[index] = value; return *this; } diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 66540fd769..7cffdbc2bf 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -10,15 +10,11 @@ thread_local spu_mfc_arg_t raw_spu_mfc[8] = {}; RawSPUThread::RawSPUThread(CPUThreadType type) : SPUThread(type) - , MemoryBlock() { - m_index = Memory.InitRawSPU(this); - Reset(); } RawSPUThread::~RawSPUThread() { - Memory.CloseRawSPU(this, m_index); } void RawSPUThread::start() @@ -29,54 +25,54 @@ void RawSPUThread::start() // (probably because Exec() creates new thread, faults of this thread aren't handled by this handler anymore) Emu.GetCallbackManager().Async([this](PPUThread& PPU) { - Exec(); + FastRun(); }); } -bool RawSPUThread::Read32(const u32 addr, u32* value) +bool RawSPUThread::ReadReg(const u32 addr, u32& value) { - const u32 offset = addr - GetStartAddr() - RAW_SPU_PROB_OFFSET; + const u32 offset = addr - RAW_SPU_BASE_ADDR - index * RAW_SPU_OFFSET - RAW_SPU_PROB_OFFSET; switch (offset) { case MFC_CMDStatus_offs: { - *value = MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL; + value = MFC_PPU_DMA_CMD_ENQUEUE_SUCCESSFUL; return true; } case MFC_QStatus_offs: { - *value = MFC_PROXY_COMMAND_QUEUE_EMPTY_FLAG | MFC_PPU_MAX_QUEUE_SPACE; + value = MFC_PROXY_COMMAND_QUEUE_EMPTY_FLAG | MFC_PPU_MAX_QUEUE_SPACE; return true; } case SPU_Out_MBox_offs: { - *value = ch_out_mbox.pop_uncond(); + value = ch_out_mbox.pop_uncond(); return true; } case SPU_MBox_Status_offs: { - *value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff) | (ch_out_intr_mbox.get_count() << 16 & 0xff); + value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff) | (ch_out_intr_mbox.get_count() << 16 & 0xff); return true; } case SPU_Status_offs: { - *value = status.read_relaxed(); + value = status.read_relaxed(); return true; } } - LOG_ERROR(Log::SPU, "RawSPUThread[%d]: Read32(): unknown/illegal offset (0x%x)", m_index, offset); + LOG_ERROR(Log::SPU, "RawSPUThread[%d]: Read32(0x%x): unknown/illegal offset (0x%x)", index, addr, offset); return false; } -bool RawSPUThread::Write32(const u32 addr, const u32 value) +bool RawSPUThread::WriteReg(const u32 addr, const u32 value) { - const u32 offset = addr - GetStartAddr() - RAW_SPU_PROB_OFFSET; + const u32 offset = addr - RAW_SPU_BASE_ADDR - index * RAW_SPU_OFFSET - RAW_SPU_PROB_OFFSET; switch (offset) { @@ -87,19 +83,19 @@ bool RawSPUThread::Write32(const u32 addr, const u32 value) break; } - raw_spu_mfc[m_index].lsa = value; + raw_spu_mfc[index].lsa = value; return true; } case MFC_EAH_offs: { - raw_spu_mfc[m_index].eah = value; + raw_spu_mfc[index].eah = value; return true; } case MFC_EAL_offs: { - raw_spu_mfc[m_index].eal = value; + raw_spu_mfc[index].eal = value; return true; } @@ -110,14 +106,14 @@ bool RawSPUThread::Write32(const u32 addr, const u32 value) break; } - raw_spu_mfc[m_index].size_tag = value; + raw_spu_mfc[index].size_tag = value; return true; } case MFC_Class_CMD_offs: { - do_dma_transfer(value & ~MFC_START_MASK, raw_spu_mfc[m_index]); - raw_spu_mfc[m_index] = {}; // clear non-persistent data + do_dma_transfer(value & ~MFC_START_MASK, raw_spu_mfc[index]); + raw_spu_mfc[index] = {}; // clear non-persistent data if (value & MFC_START_MASK) { @@ -167,7 +163,7 @@ bool RawSPUThread::Write32(const u32 addr, const u32 value) else if (value == SPU_RUNCNTL_STOP_REQUEST) { status &= ~SPU_STATUS_RUNNING; - Stop(); + FastStop(); } else { @@ -180,8 +176,7 @@ bool RawSPUThread::Write32(const u32 addr, const u32 value) case SPU_NPC_offs: { - // check if interrupts are enabled - if ((value & 3) != 1 || value >= 0x40000) + if ((value & 2) || value >= 0x40000) { break; } @@ -203,21 +198,10 @@ bool RawSPUThread::Write32(const u32 addr, const u32 value) } } - LOG_ERROR(SPU, "RawSPUThread[%d]: Write32(value=0x%x): unknown/illegal offset (0x%x)", m_index, value, offset); + LOG_ERROR(SPU, "RawSPUThread[%d]: Write32(0x%x, value=0x%x): unknown/illegal offset (0x%x)", index, addr, value, offset); return false; } -void RawSPUThread::InitRegs() -{ - offset = GetStartAddr() + RAW_SPU_LS_OFFSET; - SPUThread::InitRegs(); -} - -u32 RawSPUThread::GetIndex() const -{ - return m_index; -} - void RawSPUThread::Task() { PC = npc.exchange(0) & ~3; diff --git a/rpcs3/Emu/Cell/RawSPUThread.h b/rpcs3/Emu/Cell/RawSPUThread.h index ff65632bc4..d401a917c7 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.h +++ b/rpcs3/Emu/Cell/RawSPUThread.h @@ -6,27 +6,19 @@ __forceinline static u32 GetRawSPURegAddrByNum(int num, int offset) return RAW_SPU_OFFSET * num + RAW_SPU_BASE_ADDR + RAW_SPU_PROB_OFFSET + offset; } -class RawSPUThread - : public SPUThread - , public MemoryBlock +class RawSPUThread : public SPUThread { - u32 m_index; - public: RawSPUThread(CPUThreadType type = CPU_THREAD_RAW_SPU); virtual ~RawSPUThread(); void start(); - bool Read32(const u32 addr, u32* value); - bool Write32(const u32 addr, const u32 value); - -public: - virtual void InitRegs(); - u32 GetIndex() const; + bool ReadReg(const u32 addr, u32& value); + bool WriteReg(const u32 addr, const u32 value); private: virtual void Task(); }; -SPUThread& GetCurrentSPUThread(); \ No newline at end of file +SPUThread& GetCurrentSPUThread(); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index a0a594b4e2..8714407962 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -67,6 +67,7 @@ void SPUThread::Task() void SPUThread::DoReset() { + InitRegs(); } void SPUThread::InitRegs() @@ -178,6 +179,12 @@ void SPUThread::FastStop() m_status = Stopped; } +void SPUThread::FastRun() +{ + m_status = Running; + Exec(); +} + void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args) { if (cmd & (MFC_BARRIER_MASK | MFC_FENCE_MASK)) @@ -187,17 +194,17 @@ void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args) u32 eal = vm::cast(args.ea, "ea"); - if (eal >= SYS_SPU_THREAD_BASE_LOW && tg_id && m_type == CPU_THREAD_SPU) // SPU Thread Group MMIO (LS and SNR) + if (eal >= SYS_SPU_THREAD_BASE_LOW && m_type == CPU_THREAD_SPU) // SPU Thread Group MMIO (LS and SNR) { - const u32 num = (eal & SYS_SPU_THREAD_BASE_MASK) / SYS_SPU_THREAD_OFFSET; // thread number in group - const u32 offset = (eal & SYS_SPU_THREAD_BASE_MASK) % SYS_SPU_THREAD_OFFSET; // LS offset or MMIO register + const u32 index = (eal - SYS_SPU_THREAD_BASE_LOW) / SYS_SPU_THREAD_OFFSET; // thread number in group + const u32 offset = (eal - SYS_SPU_THREAD_BASE_LOW) % SYS_SPU_THREAD_OFFSET; // LS offset or MMIO register + std::shared_ptr group = tg.lock(); std::shared_ptr t; - std::shared_ptr tg; - if (Emu.GetIdManager().GetIDData(tg_id, tg) && num < tg->list.size() && tg->list[num] && (t = Emu.GetCPU().GetThread(tg->list[num])) && t->GetType() == CPU_THREAD_SPU) + if (group && index < group->num && (t = group->threads[index])) { - SPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); if (offset + args.size - 1 < 0x40000) // LS access { @@ -903,7 +910,7 @@ void SPUThread::stop_and_signal(u32 code) status &= ~SPU_STATUS_RUNNING; }); - Stop(); + FastStop(); int2.set(SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT); return; @@ -1013,29 +1020,34 @@ void SPUThread::stop_and_signal(u32 code) throw __FUNCTION__; } - std::shared_ptr tg; - if (!Emu.GetIdManager().GetIDData(tg_id, tg)) - { - LOG_ERROR(SPU, "sys_spu_thread_group_exit(status=0x%x): invalid group (%d)", value, tg_id); - throw __FUNCTION__; - } - if (Ini.HLELogging.GetValue()) { LOG_NOTICE(SPU, "sys_spu_thread_group_exit(status=0x%x)", value); } - - tg->m_group_exit = true; - tg->m_exit_status = value; - for (auto& v : tg->list) + LV2_LOCK; + + std::shared_ptr group = tg.lock(); + if (group) { - if (std::shared_ptr t = Emu.GetCPU().GetThread(v)) + LOG_ERROR(SPU, "sys_spu_thread_group_exit(status=0x%x): invalid group", value); + throw __FUNCTION__; + } + + for (auto t : group->threads) + { + if (t) { - t->Stop(); + auto& spu = static_cast(*t); + + spu.FastStop(); } } + group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; + group->exit_status = value; + group->join_state |= STGJSF_GROUP_EXIT; + group->join_cv.notify_one(); return; } @@ -1054,8 +1066,10 @@ void SPUThread::stop_and_signal(u32 code) LOG_NOTICE(SPU, "sys_spu_thread_exit(status=0x%x)", ch_out_mbox.get_value()); } + LV2_LOCK; + status |= SPU_STATUS_STOPPED_BY_STOP; - Stop(); + FastStop(); return; } } @@ -1087,7 +1101,7 @@ void SPUThread::halt() status &= ~SPU_STATUS_RUNNING; }); - Stop(); + FastStop(); int2.set(SPU_INT2_STAT_SPU_HALT_OR_STEP_INT); return; @@ -1099,7 +1113,7 @@ void SPUThread::halt() spu_thread::spu_thread(u32 entry, const std::string& name, u32 stack_size, u32 prio) { - thread = &Emu.GetCPU().AddThread(CPU_THREAD_SPU); + thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU); thread->SetName(name); thread->SetEntry(entry); diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 6b93726b5a..eec3c5af40 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -5,7 +5,7 @@ #include "MFC.h" struct event_queue_t; -struct event_port_t; +struct spu_group_t; // SPU Channels enum : u32 @@ -102,10 +102,9 @@ enum enum : u32 { SYS_SPU_THREAD_BASE_LOW = 0xf0000000, - SYS_SPU_THREAD_BASE_MASK = 0xfffffff, - SYS_SPU_THREAD_OFFSET = 0x00100000, - SYS_SPU_THREAD_SNR1 = 0x05400c, - SYS_SPU_THREAD_SNR2 = 0x05C00c, + SYS_SPU_THREAD_OFFSET = 0x100000, + SYS_SPU_THREAD_SNR1 = 0x5400c, + SYS_SPU_THREAD_SNR2 = 0x5C00c, }; enum @@ -505,7 +504,7 @@ public: spu_interrupt_tag_t int0; // SPU Class 0 Interrupt Management spu_interrupt_tag_t int2; // SPU Class 2 Interrupt Management - u32 tg_id; // SPU Thread Group Id + std::weak_ptr tg; // SPU Thread Group Id std::unordered_map> spuq; // Event Queue Keys for SPU Thread std::weak_ptr spup[64]; // SPU Ports @@ -653,6 +652,7 @@ public: virtual void Task(); void FastCall(u32 ls_addr); void FastStop(); + void FastRun(); protected: virtual void DoReset(); @@ -701,11 +701,13 @@ public: cpu_thread& run() override { - thread->Run(); + auto& spu = static_cast(*thread); - static_cast(thread)->GPR[3].from64(argc); - static_cast(thread)->GPR[4].from64(argv.addr()); - static_cast(thread)->GPR[5].from64(envp.addr()); + spu.Run(); + + spu.GPR[3].from64(argc); + spu.GPR[4].from64(argv.addr()); + spu.GPR[5].from64(envp.addr()); return *this; } diff --git a/rpcs3/Emu/Memory/Memory.cpp b/rpcs3/Emu/Memory/Memory.cpp index 1260b79932..9c8f038187 100644 --- a/rpcs3/Emu/Memory/Memory.cpp +++ b/rpcs3/Emu/Memory/Memory.cpp @@ -1,51 +1,16 @@ #include "stdafx.h" #include "Utilities/Log.h" -#include "Emu/System.h" #include "Memory.h" -#include "Emu/Cell/RawSPUThread.h" MemoryBase Memory; -u32 MemoryBase::InitRawSPU(MemoryBlock* raw_spu) -{ - LV2_LOCK; - - u32 index; - for (index = 0; index < sizeof(RawSPUMem) / sizeof(RawSPUMem[0]); index++) - { - if (!RawSPUMem[index]) - { - RawSPUMem[index] = raw_spu; - break; - } - } - - MemoryBlocks.push_back(raw_spu->SetRange(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index, RAW_SPU_PROB_OFFSET)); - return index; -} - -void MemoryBase::CloseRawSPU(MemoryBlock* raw_spu, const u32 num) -{ - LV2_LOCK; - - for (int i = 0; i < MemoryBlocks.size(); ++i) - { - if (MemoryBlocks[i] == raw_spu) - { - MemoryBlocks.erase(MemoryBlocks.begin() + i); - break; - } - } - if (num < sizeof(RawSPUMem) / sizeof(RawSPUMem[0])) RawSPUMem[num] = nullptr; -} +std::mutex g_memory_mutex; void MemoryBase::Init(MemoryType type) { if (m_inited) return; m_inited = true; - memset(RawSPUMem, 0, sizeof(RawSPUMem)); - LOG_NOTICE(MEMORY, "Initializing memory: g_base_addr = 0x%llx, g_priv_addr = 0x%llx", (u64)vm::g_base_addr, (u64)vm::g_priv_addr); #ifdef _WIN32 @@ -101,35 +66,11 @@ void MemoryBase::Close() MemoryBlocks.clear(); } -bool MemoryBase::WriteMMIO32(u32 addr, const u32 data) -{ - LV2_LOCK; - - if (RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET] && ((RawSPUThread*)RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET])->Write32(addr, data)) - { - return true; - } - - return false; -} - -bool MemoryBase::ReadMMIO32(u32 addr, u32& result) -{ - LV2_LOCK; - - if (RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET] && ((RawSPUThread*)RawSPUMem[(addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET])->Read32(addr, &result)) - { - return true; - } - - return false; -} - bool MemoryBase::Map(const u32 addr, const u32 size) { assert(size && (size | addr) % 4096 == 0); - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++) { @@ -147,7 +88,7 @@ bool MemoryBase::Map(const u32 addr, const u32 size) bool MemoryBase::Unmap(const u32 addr) { - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); for (u32 i = 0; i < MemoryBlocks.size(); i++) { @@ -234,7 +175,7 @@ DynamicMemoryBlockBase::DynamicMemoryBlockBase() const u32 DynamicMemoryBlockBase::GetUsedSize() const { - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); u32 size = 0; @@ -253,7 +194,7 @@ bool DynamicMemoryBlockBase::IsInMyRange(const u32 addr, const u32 size) MemoryBlock* DynamicMemoryBlockBase::SetRange(const u32 start, const u32 size) { - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); m_max_size = PAGE_4K(size); if (!MemoryBlock::SetRange(start, 0)) @@ -267,7 +208,7 @@ MemoryBlock* DynamicMemoryBlockBase::SetRange(const u32 start, const u32 size) void DynamicMemoryBlockBase::Delete() { - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); m_allocated.clear(); m_max_size = 0; @@ -289,7 +230,7 @@ bool DynamicMemoryBlockBase::AllocFixed(u32 addr, u32 size) return false; } - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); for (u32 i = 0; i lock(g_memory_mutex); for (u32 addr = MemoryBlock::GetStartAddr(); addr <= MemoryBlock::GetEndAddr() - exsize;) { @@ -371,7 +312,7 @@ bool DynamicMemoryBlockBase::Alloc() bool DynamicMemoryBlockBase::Free(u32 addr) { - LV2_LOCK; + std::lock_guard lock(g_memory_mutex); for (u32 num = 0; num < m_allocated.size(); num++) { diff --git a/rpcs3/Emu/Memory/Memory.h b/rpcs3/Emu/Memory/Memory.h index b17f8aa359..ae07d8442f 100644 --- a/rpcs3/Emu/Memory/Memory.h +++ b/rpcs3/Emu/Memory/Memory.h @@ -33,7 +33,6 @@ public: DynamicMemoryBlock Userspace; DynamicMemoryBlock RSXFBMem; DynamicMemoryBlock StackMem; - MemoryBlock* RawSPUMem[(0x100000000 - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET]; VirtualMemoryBlock RSXIOMem; struct @@ -67,18 +66,10 @@ public: void UnregisterPages(u32 addr, u32 size); - u32 InitRawSPU(MemoryBlock* raw_spu); - - void CloseRawSPU(MemoryBlock* raw_spu, const u32 num); - void Init(MemoryType type); void Close(); - bool WriteMMIO32(u32 addr, const u32 data); - - bool ReadMMIO32(u32 addr, u32& result); - u32 GetUserMemTotalSize() { return UserMemory->GetSize(); diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp index b0d10c5316..02c460d975 100644 --- a/rpcs3/Emu/SysCalls/Callback.cpp +++ b/rpcs3/Emu/SysCalls/Callback.cpp @@ -61,30 +61,30 @@ void CallbackManager::Init() if (Memory.PSV.RAM.GetStartAddr()) { - m_cb_thread = &Emu.GetCPU().AddThread(CPU_THREAD_ARMv7); + m_cb_thread = Emu.GetCPU().AddThread(CPU_THREAD_ARMv7); m_cb_thread->SetName("Callback Thread"); m_cb_thread->SetEntry(0); m_cb_thread->SetPrio(1001); m_cb_thread->SetStackSize(0x10000); m_cb_thread->InitStack(); m_cb_thread->InitRegs(); - static_cast(m_cb_thread)->DoRun(); + static_cast(*m_cb_thread).DoRun(); } else { - m_cb_thread = &Emu.GetCPU().AddThread(CPU_THREAD_PPU); + m_cb_thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU); m_cb_thread->SetName("Callback Thread"); m_cb_thread->SetEntry(0); m_cb_thread->SetPrio(1001); m_cb_thread->SetStackSize(0x10000); m_cb_thread->InitStack(); m_cb_thread->InitRegs(); - static_cast(m_cb_thread)->DoRun(); + static_cast(*m_cb_thread).DoRun(); } thread_t cb_async_thread("CallbackManager thread", [this]() { - SetCurrentNamedThread(m_cb_thread); + SetCurrentNamedThread(&*m_cb_thread); while (!Emu.IsStopped()) { diff --git a/rpcs3/Emu/SysCalls/Callback.h b/rpcs3/Emu/SysCalls/Callback.h index 6918d338a3..a1250a8acb 100644 --- a/rpcs3/Emu/SysCalls/Callback.h +++ b/rpcs3/Emu/SysCalls/Callback.h @@ -10,7 +10,7 @@ class CallbackManager std::mutex m_mutex; std::vector> m_cb_list; std::vector> m_async_list; - CPUThread* m_cb_thread; + std::shared_ptr m_cb_thread; struct PauseResumeCBS { diff --git a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp index 34f646f7d9..4dca0a6a27 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp @@ -223,7 +223,7 @@ u32 adecOpen(AudioDecoder* adec_ptr) adec.id = adec_id; - adec.adecCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); + adec.adecCb = static_cast(Emu.GetCPU().AddThread(CPU_THREAD_PPU).get()); adec.adecCb->SetName(fmt::format("AudioDecoder[%d] Callback", adec_id)); adec.adecCb->SetEntry(0); adec.adecCb->SetPrio(1001); diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp index 5e5ebd4be6..f02cd09424 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp @@ -305,7 +305,7 @@ u32 dmuxOpen(Demuxer* dmux_ptr) dmux.id = dmux_id; - dmux.dmuxCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); + dmux.dmuxCb = static_cast(Emu.GetCPU().AddThread(CPU_THREAD_PPU).get()); dmux.dmuxCb->SetName(fmt::format("Demuxer[%d] Callback", dmux_id)); dmux.dmuxCb->SetEntry(0); dmux.dmuxCb->SetPrio(1001); diff --git a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp index c8cf5a1efd..de19a0fab6 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSpurs.cpp @@ -4,6 +4,7 @@ #include "Emu/SysCalls/Modules.h" #include "Emu/SysCalls/CB_FUNC.h" +#include "Emu/CPU/CPUThreadManager.h" #include "Emu/Cell/SPUThread.h" #include "Emu/SysCalls/lv2/sleep_queue.h" #include "Emu/SysCalls/lv2/sys_lwmutex.h" @@ -143,24 +144,23 @@ s32 spursInit( if (flags & SAF_UNKNOWN_FLAG_7) tgt |= 0x102; if (flags & SAF_UNKNOWN_FLAG_8) tgt |= 0xC02; if (flags & SAF_UNKNOWN_FLAG_9) tgt |= 0x800; - auto tg = spu_thread_group_create(name + "CellSpursKernelGroup", nSpus, spuPriority, tgt, container); - assert(tg); - spurs->m.spuTG = tg->m_id; + spurs->m.spuTG = spu_thread_group_create(name + "CellSpursKernelGroup", nSpus, spuPriority, tgt, container); + assert(spurs->m.spuTG.data()); name += "CellSpursKernel0"; for (s32 num = 0; num < nSpus; num++, name[name.size() - 1]++) { - auto spu = spu_thread_initialize(tg, num, spurs->m.spuImg, name, SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE, (u64)num << 32, spurs.addr(), 0, 0); + const u32 id = spu_thread_initialize(spurs->m.spuTG, num, vm::ptr::make(spurs.addr() + offsetof(CellSpurs, m.spuImg)), name, SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE, (u64)num << 32, spurs.addr(), 0, 0); - spu->RegisterHleFunction(spurs->m.spuImg.entry_point, spursKernelEntry); + static_cast(*Emu.GetCPU().GetThread(id).get()).RegisterHleFunction(spurs->m.spuImg.entry_point, spursKernelEntry); - spurs->m.spus[num] = spu->GetId(); + spurs->m.spus[num] = id; } if (flags & SAF_SPU_PRINTF_ENABLED) { // spu_printf: attach group - if (!spu_printf_agcb || spu_printf_agcb(tg->m_id) != CELL_OK) + if (!spu_printf_agcb || spu_printf_agcb(spurs->m.spuTG) != CELL_OK) { // remove flag if failed spurs->m.flags &= ~SAF_SPU_PRINTF_ENABLED; @@ -328,13 +328,13 @@ s32 spursInit( return; } } - })->GetId(); + }); spurs->m.ppu1 = ppu_thread_create(0, 0, ppuPriority, 0x8000, true, false, name + "SpursHdlr1", [spurs](PPUThread& CPU) { // TODO - })->GetId(); + }); // enable exception event handler if (spurs->m.enableEH.compare_and_swap_test(be_t::make(0), be_t::make(1))) diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp index b99cae0696..0ad437fe47 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp @@ -213,7 +213,7 @@ u32 vdecOpen(VideoDecoder* vdec_ptr) vdec.id = vdec_id; - vdec.vdecCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); + vdec.vdecCb = static_cast(Emu.GetCPU().AddThread(CPU_THREAD_PPU).get()); vdec.vdecCb->SetName(fmt::format("VideoDecoder[%d] Callback", vdec_id)); vdec.vdecCb->SetEntry(0); vdec.vdecCb->SetPrio(1001); diff --git a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp index 7c29a074b2..dcf5cc2863 100644 --- a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp +++ b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp @@ -333,14 +333,16 @@ int cellSurMixerCreate(vm::ptr config) { AudioPortConfig& port = g_audio.ports[g_surmx.audio_port]; - PPUThread& cb_thread = *(PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); - cb_thread.SetName("Surmixer Callback Thread"); - cb_thread.SetEntry(0); - cb_thread.SetPrio(1001); - cb_thread.SetStackSize(0x10000); - cb_thread.InitStack(); - cb_thread.InitRegs(); - cb_thread.DoRun(); + auto cb_thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU); + + auto& ppu = static_cast(*cb_thread); + ppu.SetName("Surmixer Callback Thread"); + ppu.SetEntry(0); + ppu.SetPrio(1001); + ppu.SetStackSize(0x10000); + ppu.InitStack(); + ppu.InitRegs(); + ppu.DoRun(); while (port.state.read_relaxed() != AUDIO_PORT_STATE_CLOSED && !Emu.IsStopped()) { @@ -357,7 +359,7 @@ int cellSurMixerCreate(vm::ptr config) memset(mixdata, 0, sizeof(mixdata)); if (surMixerCb) { - surMixerCb(cb_thread, surMixerCbArg, (u32)mixcount, 256); + surMixerCb(ppu, surMixerCbArg, (u32)mixcount, 256); } //u64 stamp1 = get_system_time(); @@ -462,7 +464,7 @@ int cellSurMixerCreate(vm::ptr config) ssp.clear(); } - Emu.GetCPU().RemoveThread(cb_thread.GetId()); + Emu.GetCPU().RemoveThread(ppu.GetId()); surMixerCb.set(0); }); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp index a4952e0ef1..951be7b599 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.cpp @@ -176,7 +176,7 @@ s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr queue->cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } - // event data is returned in registers, second arg is not used + // event data is returned in registers (second arg is not used) auto& event = queue->events.front(); CPU.GPR[4] = event.source; CPU.GPR[5] = event.data1; @@ -206,10 +206,9 @@ s32 sys_event_queue_drain(u32 equeue_id) u32 event_port_create(u64 name) { - std::shared_ptr eport(new event_port_t()); - const u32 id = sys_event.GetNewId(eport, TYPE_EVENT_PORT); - eport->name = name ? name : ((u64)process_getpid() << 32) | (u64)id; - return id; + std::shared_ptr eport(new event_port_t(SYS_EVENT_PORT_LOCAL, name)); + + return sys_event.GetNewId(eport, TYPE_EVENT_PORT); } s32 sys_event_port_create(vm::ptr eport_id, s32 port_type, u64 name) @@ -224,7 +223,9 @@ s32 sys_event_port_create(vm::ptr eport_id, s32 port_type, u64 name) LV2_LOCK; - *eport_id = event_port_create(name); + std::shared_ptr eport(new event_port_t(port_type, name)); + + *eport_id = sys_event.GetNewId(eport, TYPE_EVENT_PORT); return CELL_OK; } @@ -262,7 +263,10 @@ s32 sys_event_port_connect_local(u32 eport_id, u32 equeue_id) return CELL_ESRCH; } - // CELL_EINVAL is never returned (I have no idea if SYS_EVENT_PORT_LOCAL is the only possible type) + if (port->type != SYS_EVENT_PORT_LOCAL) + { + return CELL_EINVAL; + } if (!port->queue.expired()) { @@ -292,11 +296,15 @@ s32 sys_event_port_disconnect(u32 eport_id) return CELL_ENOTCONN; } + // CELL_EBUSY is not returned + + //const u64 source = port->name ? port->name : ((u64)process_getpid() << 32) | (u64)eport_id; + //for (auto& event : queue->events) //{ - // if (event.source == port->name) + // if (event.source == source) // { - // return CELL_EBUSY; // not sure about it + // return CELL_EBUSY; // ??? // } //} @@ -328,7 +336,9 @@ s32 sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) return CELL_EBUSY; } - queue->events.emplace_back(port->name, data1, data2, data3); + const u64 source = port->name ? port->name : ((u64)process_getpid() << 32) | (u64)eport_id; + + queue->events.emplace_back(source, data1, data2, data3); queue->cv.notify_one(); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_event.h b/rpcs3/Emu/SysCalls/lv2/sys_event.h index 34e1b75c9e..7542dd871f 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_event.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_event.h @@ -79,6 +79,7 @@ struct event_queue_t std::deque events; + // TODO: use sleep queue, remove condition variable (use thread's one instead) std::condition_variable cv; std::atomic waiters; @@ -95,11 +96,13 @@ struct event_queue_t struct event_port_t { - u64 name; // generated or user-specified code that is passed as event source + const s32 type; // port type, must be SYS_EVENT_PORT_LOCAL + const u64 name; // passed as event source (generated from id and process id if not set) std::weak_ptr queue; // event queue this port is connected to - event_port_t(u64 name = 0) - : name(name) + event_port_t(s32 type, u64 name) + : type(type) + , name(name) { } }; diff --git a/rpcs3/Emu/SysCalls/lv2/sys_interrupt.cpp b/rpcs3/Emu/SysCalls/lv2/sys_interrupt.cpp index 830e4a29ec..d1d0dbfc66 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_interrupt.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_interrupt.cpp @@ -68,7 +68,7 @@ s32 sys_interrupt_thread_establish(vm::ptr ih, u32 intrtag, u64 intrthread, auto& tag = class_id ? spu.int2 : spu.int0; - // CELL_ESTAT is never returned (can't detect exact condition) + // CELL_ESTAT is not returned (can't detect exact condition) std::shared_ptr it = Emu.GetCPU().GetThread((u32)intrthread); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp index 74f20d8b1c..f56fa14e2a 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.cpp @@ -162,68 +162,47 @@ s32 sys_ppu_thread_restart(u64 thread_id) return CELL_OK; } -PPUThread* ppu_thread_create(u32 entry, u64 arg, s32 prio, u32 stacksize, bool is_joinable, bool is_interrupt, const std::string& name, std::function task) +u32 ppu_thread_create(u32 entry, u64 arg, s32 prio, u32 stacksize, bool is_joinable, bool is_interrupt, std::string name, std::function task) { - PPUThread& new_thread = *(PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); + auto new_thread = Emu.GetCPU().AddThread(CPU_THREAD_PPU); - // Note: (Syphurith) I haven't figured out the minimum stack size of PPU Thread. - // Maybe it can be done with pthread_attr_getstacksize function. - // And i toke 4096 (PTHREAD_STACK_MIN, and the smallest allocation unit) for this. - if ((stacksize % 4096) || (stacksize == 0)) { - // If not times of smallest allocation unit, round it up to the nearest one. - // And regard zero as a same condition. - sys_ppu_thread.Warning("sys_ppu_thread_create: stacksize increased from 0x%x to 0x%x.", - stacksize, SYS_PPU_THREAD_STACK_MIN * ((u32)(stacksize / SYS_PPU_THREAD_STACK_MIN) + 1)); - stacksize = SYS_PPU_THREAD_STACK_MIN * ((u32)(stacksize / SYS_PPU_THREAD_STACK_MIN) + 1); - } + auto& ppu = static_cast(*new_thread); - u32 id = new_thread.GetId(); - new_thread.SetEntry(entry); - new_thread.SetPrio(prio); - new_thread.SetStackSize(stacksize); - new_thread.SetJoinable(is_joinable); - new_thread.SetName(name); - new_thread.custom_task = task; - new_thread.Run(); - - sys_ppu_thread.Notice("*** New PPU Thread [%s] (%s, entry=0x%x): id = %d", name.c_str(), - is_interrupt ? "interrupt" : - (is_joinable ? "joinable" : "detached"), entry, id); + ppu.SetEntry(entry); + ppu.SetPrio(prio); + ppu.SetStackSize(stacksize < 0x4000 ? 0x4000 : stacksize); // (hack) adjust minimal stack size + ppu.SetJoinable(is_joinable); + ppu.SetName(name); + ppu.custom_task = task; + ppu.Run(); if (!is_interrupt) { - new_thread.GPR[3] = arg; - new_thread.Exec(); + ppu.GPR[3] = arg; + ppu.Exec(); } - return &new_thread; + return ppu.GetId(); } s32 sys_ppu_thread_create(vm::ptr thread_id, u32 entry, u64 arg, s32 prio, u32 stacksize, u64 flags, vm::ptr threadname) { - sys_ppu_thread.Log("sys_ppu_thread_create(thread_id_addr=0x%x, entry=0x%x, arg=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname_addr=0x%x('%s'))", - thread_id.addr(), entry, arg, prio, stacksize, flags, threadname.addr(), threadname ? threadname.get_ptr() : ""); + sys_ppu_thread.Warning("sys_ppu_thread_create(thread_id=*0x%x, entry=0x%x, arg=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname=*0x%x)", thread_id, entry, arg, prio, stacksize, flags, threadname); - bool is_joinable = false; - bool is_interrupt = false; - - switch (flags) + if (prio < 0 || prio > 3071) { - case 0: break; - case SYS_PPU_THREAD_CREATE_JOINABLE: - is_joinable = true; - break; - - case SYS_PPU_THREAD_CREATE_INTERRUPT: - is_interrupt = true; - break; - - default: sys_ppu_thread.Error("sys_ppu_thread_create(): unknown flags value (0x%llx)", flags); return CELL_EPERM; + return CELL_EINVAL; } - std::string name = threadname ? threadname.get_ptr() : ""; + bool is_joinable = flags & SYS_PPU_THREAD_CREATE_JOINABLE; + bool is_interrupt = flags & SYS_PPU_THREAD_CREATE_INTERRUPT; - *thread_id = ppu_thread_create(entry, arg, prio, stacksize, is_joinable, is_interrupt, name)->GetId(); + if (is_joinable && is_interrupt) + { + return CELL_EPERM; + } + + *thread_id = ppu_thread_create(entry, arg, prio, stacksize, is_joinable, is_interrupt, threadname ? threadname.get_ptr() : ""); return CELL_OK; } @@ -249,12 +228,15 @@ s32 sys_ppu_thread_get_id(PPUThread& CPU, vm::ptr thread_id) s32 sys_ppu_thread_rename(u64 thread_id, vm::ptr name) { - sys_ppu_thread.Log("sys_ppu_thread_rename(thread_id=%d, name_addr=0x%x('%s'))", thread_id, name.addr(), name.get_ptr()); + sys_ppu_thread.Log("sys_ppu_thread_rename(thread_id=0x%llx, name=*0x%x)", thread_id, name); - std::shared_ptr thr = Emu.GetCPU().GetThread(thread_id); - if (!thr) + std::shared_ptr t = Emu.GetCPU().GetThread(thread_id, CPU_THREAD_PPU); + + if (!t) + { return CELL_ESRCH; - - thr->SetThreadName(name.get_ptr()); + } + + t->SetThreadName(name.get_ptr()); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.h b/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.h index 3bd901338c..4ad043fccc 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_ppu_thread.h @@ -20,7 +20,7 @@ enum stackSize }; // Aux -PPUThread* ppu_thread_create(u32 entry, u64 arg, s32 prio, u32 stacksize, bool is_joinable, bool is_interrupt, const std::string& name, std::function task = nullptr); +u32 ppu_thread_create(u32 entry, u64 arg, s32 prio, u32 stacksize, bool is_joinable, bool is_interrupt, std::string name, std::function task = nullptr); // SysCalls void sys_ppu_thread_exit(PPUThread& CPU, u64 errorcode); diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp index 3eac0988b2..c75a2b1186 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.cpp @@ -45,6 +45,18 @@ s32 spu_image_import(sys_spu_image& img, u32 src, u32 type) return CELL_OK; } +s32 sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu) +{ + sys_spu.Warning("sys_spu_initialize(max_usable_spu=%d, max_raw_spu=%d)", max_usable_spu, max_raw_spu); + + if (max_raw_spu > 5) + { + return CELL_EINVAL; + } + + return CELL_OK; +} + s32 sys_spu_image_open(vm::ptr img, vm::ptr path) { sys_spu.Warning("sys_spu_image_open(img_addr=0x%x, path_addr=0x%x [%s])", img.addr(), path.addr(), path.get_ptr()); @@ -79,114 +91,118 @@ s32 sys_spu_image_open(vm::ptr img, vm::ptr path) return CELL_OK; } -SPUThread* spu_thread_initialize(std::shared_ptr& group, u32 spu_num, sys_spu_image& img, const std::string& name, u32 option, u64 a1, u64 a2, u64 a3, u64 a4, std::function task) +u32 spu_thread_initialize(u32 group_id, u32 spu_num, vm::ptr img, const std::string& name, u32 option, u64 a1, u64 a2, u64 a3, u64 a4, std::function task) { if (option) { sys_spu.Todo("Unsupported SPU Thread options (0x%x)", option); } - const u32 spu_ep = img.entry_point; - // Copy SPU image: - // TODO: use segment info - const u32 spu_offset = Memory.MainMem.AllocAlign(256 * 1024, 4096); - memcpy(vm::get_ptr(spu_offset), vm::get_ptr(img.addr), 256 * 1024); + auto t = Emu.GetCPU().AddThread(CPU_THREAD_SPU); - SPUThread& new_thread = static_cast(Emu.GetCPU().AddThread(CPU_THREAD_SPU)); - //initialize from new place: - new_thread.offset = spu_offset; - new_thread.SetEntry(spu_ep); - new_thread.SetName(name); - new_thread.m_custom_task = task; - new_thread.Run(); - new_thread.GPR[3] = u128::from64(0, a1); - new_thread.GPR[4] = u128::from64(0, a2); - new_thread.GPR[5] = u128::from64(0, a3); - new_thread.GPR[6] = u128::from64(0, a4); + auto& spu = static_cast(*t); - const u32 id = new_thread.GetId(); + spu.index = spu_num; + spu.offset = Memory.MainMem.AllocAlign(256 * 1024); + spu.SetName(name); + spu.m_custom_task = task; - if (group) + std::shared_ptr group; + Emu.GetIdManager().GetIDData(group_id, group); + + spu.tg = group; + group->threads[spu_num] = t; + group->args[spu_num] = { a1, a2, a3, a4 }; + group->images[spu_num] = img; + + u32 count = 0; + + for (auto& t : group->threads) { - group->list[spu_num] = id; - new_thread.tg_id = group->m_id; + if (t) + { + count++; + } } - sys_spu.Warning("*** New SPU Thread [%s] (ep=0x%x, opt=0x%x, a1=0x%llx, a2=0x%llx, a3=0x%llx, a4=0x%llx): id=%d, spu_offset=0x%x", - name.c_str(), spu_ep, option, a1, a2, a3, a4, id, spu_offset); - return &new_thread; + if (count >= group->num) + { + assert(count == group->num); + group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; + } + + return spu.GetId(); } -s32 sys_spu_thread_initialize(vm::ptr thread, u32 group, u32 spu_num, vm::ptr img, vm::ptr attr, vm::ptr arg) +s32 sys_spu_thread_initialize(vm::ptr thread, u32 group_id, u32 spu_num, vm::ptr img, vm::ptr attr, vm::ptr arg) { - sys_spu.Warning("sys_spu_thread_initialize(thread_addr=0x%x, group=0x%x, spu_num=%d, img_addr=0x%x, attr_addr=0x%x, arg_addr=0x%x)", - thread.addr(), group, spu_num, img.addr(), attr.addr(), arg.addr()); + sys_spu.Warning("sys_spu_thread_initialize(thread=*0x%x, group=%d, spu_num=%d, img=*0x%x, attr=*0x%x, arg=*0x%x)", thread, group_id, spu_num, img, attr, arg); - std::shared_ptr group_info; - if(!Emu.GetIdManager().GetIDData(group, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(group_id, group)) { return CELL_ESRCH; } - if(spu_num >= group_info->list.size()) + if (spu_num >= group->threads.size()) { return CELL_EINVAL; } - if(group_info->list[spu_num]) + if (group->threads[spu_num] || group->state != SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED) { return CELL_EBUSY; } - *thread = spu_thread_initialize( - group_info, - spu_num, - *img, - attr->name ? std::string(attr->name.get_ptr(), attr->name_len) : "SPUThread", - attr->option, - arg->arg1, - arg->arg2, - arg->arg3, - arg->arg4)->GetId(); + *thread = spu_thread_initialize(group_id, spu_num, img, attr->name ? std::string(attr->name.get_ptr(), attr->name_len) : "SPUThread", attr->option, arg->arg1, arg->arg2, arg->arg3, arg->arg4); return CELL_OK; } s32 sys_spu_thread_set_argument(u32 id, vm::ptr arg) { - sys_spu.Warning("sys_spu_thread_set_argument(id=%d, arg_addr=0x%x)", id, arg.addr()); + sys_spu.Warning("sys_spu_thread_set_argument(id=%d, arg=*0x%x)", id, arg); - std::shared_ptr thr = Emu.GetCPU().GetThread(id); + LV2_LOCK; - if(!thr || thr->GetType() != CPU_THREAD_SPU) + std::shared_ptr t = Emu.GetCPU().GetThread(id); + + if (!t || t->GetType() != CPU_THREAD_SPU) { return CELL_ESRCH; } - SPUThread& spu = *(SPUThread*)thr.get(); + auto& spu = static_cast(*t); - spu.GPR[3] = u128::from64(0, arg->arg1); - spu.GPR[4] = u128::from64(0, arg->arg2); - spu.GPR[5] = u128::from64(0, arg->arg3); - spu.GPR[6] = u128::from64(0, arg->arg4); + std::shared_ptr group = spu.tg.lock(); + + assert(spu.index < group->threads.size()); + + group->args[spu.index].arg1 = arg->arg1; + group->args[spu.index].arg2 = arg->arg2; + group->args[spu.index].arg3 = arg->arg3; + group->args[spu.index].arg4 = arg->arg4; return CELL_OK; } s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr status) { - sys_spu.Warning("sys_spu_thread_get_exit_status(id=%d, status_addr=0x%x)", id, status.addr()); + sys_spu.Warning("sys_spu_thread_get_exit_status(id=%d, status=*0x%x)", id, status); - std::shared_ptr thr = Emu.GetCPU().GetThread(id); + LV2_LOCK; - if (!thr || thr->GetType() != CPU_THREAD_SPU) + std::shared_ptr t = Emu.GetCPU().GetThread(id); + if (!t || t->GetType() != CPU_THREAD_SPU) { return CELL_ESRCH; } - SPUThread& spu = static_cast(*thr); + auto& spu = static_cast(*t); u32 res; - if (!spu.IsStopped() || !spu.ch_out_mbox.pop(res)) + if (!spu.IsStopped() || !spu.ch_out_mbox.pop(res)) // TODO: Is it possible to get the same status twice? If so, we shouldn't use destructive read { return CELL_ESTAT; } @@ -195,44 +211,64 @@ s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr status) return CELL_OK; } +u32 spu_thread_group_create(const std::string& name, u32 num, s32 prio, s32 type, u32 container) +{ + if (type) + { + sys_spu.Todo("Unsupported SPU Thread Group type (0x%x)", type); + } + + std::shared_ptr group(new spu_group_t(name, num, prio, type, container)); + + return sys_spu.GetNewId(group); +} + +s32 sys_spu_thread_group_create(vm::ptr id, u32 num, s32 prio, vm::ptr attr) +{ + sys_spu.Warning("sys_spu_thread_group_create(id=*0x%x, num=%d, prio=%d, attr=*0x%x)", id, num, prio, attr); + + // TODO: max num value should be affected by sys_spu_initialize() settings + + if (!num || num > 6 || prio < 16 || prio > 255) + { + return CELL_EINVAL; + } + + *id = spu_thread_group_create(std::string(attr->name.get_ptr(), attr->nsize - 1), num, prio, attr->type, attr->ct); + return CELL_OK; +} + s32 sys_spu_thread_group_destroy(u32 id) { sys_spu.Warning("sys_spu_thread_group_destroy(id=%d)", id); - std::shared_ptr group_info; - if(!Emu.GetIdManager().GetIDData(id, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) { return CELL_ESRCH; } - //TODO: New method to check busy. and even maybe in other sys_spu_thread_group_ calls. - - //TODO: SPU_THREAD_GROUP lock may not be gracefully implemented now. - // But it could still be set using simple way? - //Check the state it should be in NOT_INITIALIZED / INITIALIZED. - if ((group_info->m_state != SPU_THREAD_GROUP_STATUS_INITIALIZED) - && (group_info->m_state != SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED)) + if (group->state > SPU_THREAD_GROUP_STATUS_INITIALIZED) { - sys_spu.Error("sys_spu_thread_group_destroy(id=%d) is not in NOT_INITIALIZED / INITIALIZED, state=%d", id, group_info->m_state); - return CELL_ESTAT; //Indeed this should not be encountered. If program itself all right. + return CELL_EBUSY; } - //SET BUSY - - for (u32 i = 0; i < group_info->list.size(); i++) + // clear threads + for (auto t : group->threads) { - // TODO: disconnect all event ports - std::shared_ptr t = Emu.GetCPU().GetThread(group_info->list[i]); if (t) { - Memory.MainMem.Free(((SPUThread*)t.get())->offset); - Emu.GetCPU().RemoveThread(group_info->list[i]); + auto& spu = static_cast(*t); + + Memory.MainMem.Free(spu.offset); + Emu.GetCPU().RemoveThread(spu.GetId()); } } - group_info->m_state = SPU_THREAD_GROUP_STATUS_UNKNOWN; - //REMOVE BUSY - + group->threads = {}; + group->state = SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED; // hack Emu.GetIdManager().RemoveID(id); return CELL_OK; } @@ -241,34 +277,55 @@ s32 sys_spu_thread_group_start(u32 id) { sys_spu.Warning("sys_spu_thread_group_start(id=%d)", id); - std::shared_ptr group_info; - if(!Emu.GetIdManager().GetIDData(id, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) { return CELL_ESRCH; } - // TODO: check group state - - //Check for BUSY? - - //SET BUSY - - //Different from what i expected. Or else there would not be any with RUNNING. - group_info->m_state = SPU_THREAD_GROUP_STATUS_READY; //Added Group State - //Notice: I can not know the action preformed below be following the definition, but left unchanged. - - for (u32 i = 0; i < group_info->list.size(); i++) + if (group->state != SPU_THREAD_GROUP_STATUS_INITIALIZED) + { + return CELL_ESTAT; + } + + // SPU_THREAD_GROUP_STATUS_READY state is not used + + group->state = SPU_THREAD_GROUP_STATUS_RUNNING; + group->join_state = 0; + + for (auto t : group->threads) { - std::shared_ptr t = Emu.GetCPU().GetThread(group_info->list[i]); if (t) { - ((SPUThread*)t.get())->status.write_relaxed(SPU_STATUS_RUNNING); - t->Exec(); + auto& spu = static_cast(*t); + + assert(spu.index < group->threads.size()); + auto& args = group->args[spu.index]; + auto& image = group->images[spu.index]; + + // Copy SPU image: + // TODO: use segment info + memcpy(vm::get_ptr(spu.offset), vm::get_ptr(image->addr), 256 * 1024); + + spu.SetEntry(image->entry_point); + spu.Run(); + spu.status.write_relaxed(SPU_STATUS_RUNNING); + spu.GPR[3] = u128::from64(0, args.arg1); + spu.GPR[4] = u128::from64(0, args.arg2); + spu.GPR[5] = u128::from64(0, args.arg3); + spu.GPR[6] = u128::from64(0, args.arg4); } } - group_info->m_state = SPU_THREAD_GROUP_STATUS_RUNNING; //SPU Thread Group now all in running. - //REMOVE BUSY + for (auto t : group->threads) + { + if (t) + { + t->Exec(); + } + } return CELL_OK; } @@ -277,45 +334,53 @@ s32 sys_spu_thread_group_suspend(u32 id) { sys_spu.Log("sys_spu_thread_group_suspend(id=%d)", id); - std::shared_ptr group_info; - if(!Emu.GetIdManager().GetIDData(id, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) { return CELL_ESRCH; } - // TODO: check group state - //Experimental implementation for the state checking - if ((group_info->m_state != SPU_THREAD_GROUP_STATUS_READY) - && (group_info->m_state != SPU_THREAD_GROUP_STATUS_RUNNING) - && (group_info->m_state != SPU_THREAD_GROUP_STATUS_WAITING)) + if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate + { + return CELL_EINVAL; + } + + if (group->state <= SPU_THREAD_GROUP_STATUS_INITIALIZED || group->state == SPU_THREAD_GROUP_STATUS_STOPPED) { return CELL_ESTAT; } - //Check for BUSY? + // SPU_THREAD_GROUP_STATUS_READY state is not used - //SET BUSY - - for (u32 i = 0; i < group_info->list.size(); i++) + if (group->state == SPU_THREAD_GROUP_STATUS_RUNNING) { - if (std::shared_ptr t = Emu.GetCPU().GetThread(group_info->list[i])) + group->state = SPU_THREAD_GROUP_STATUS_SUSPENDED; + } + else if (group->state == SPU_THREAD_GROUP_STATUS_WAITING) + { + group->state = SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED; + } + else if (group->state == SPU_THREAD_GROUP_STATUS_SUSPENDED || group->state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) + { + return CELL_OK; // probably, nothing to do there + } + else + { + return CELL_ESTAT; + } + + for (auto t : group->threads) + { + if (t) { - t->Pause(); + auto& spu = static_cast(*t); + + spu.FastStop(); } } - //Now the state changes. - if ((group_info->m_state == SPU_THREAD_GROUP_STATUS_READY) - || (group_info->m_state == SPU_THREAD_GROUP_STATUS_RUNNING)) - { - group_info->m_state = SPU_THREAD_GROUP_STATUS_SUSPENDED; - } - else if (group_info->m_state == SPU_THREAD_GROUP_STATUS_WAITING) - { - group_info->m_state = SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED; - } - //REMOVE BUSY - return CELL_OK; } @@ -323,229 +388,188 @@ s32 sys_spu_thread_group_resume(u32 id) { sys_spu.Log("sys_spu_thread_group_resume(id=%d)", id); - std::shared_ptr group_info; - if(!Emu.GetIdManager().GetIDData(id, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) { return CELL_ESRCH; } - // TODO: check group state - if ((group_info->m_state != SPU_THREAD_GROUP_STATUS_SUSPENDED) - && (group_info->m_state != SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED)) + if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate + { + return CELL_EINVAL; + } + + // SPU_THREAD_GROUP_STATUS_READY state is not used + + if (group->state == SPU_THREAD_GROUP_STATUS_SUSPENDED) + { + group->state = SPU_THREAD_GROUP_STATUS_RUNNING; + } + else if (group->state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) + { + group->state = SPU_THREAD_GROUP_STATUS_WAITING; + return CELL_OK; // probably, nothing to do there + } + else { return CELL_ESTAT; } - //Maybe check for BUSY - - //SET BUSY - - if (group_info->m_state == SPU_THREAD_GROUP_STATUS_SUSPENDED) + for (auto t : group->threads) { - group_info->m_state = SPU_THREAD_GROUP_STATUS_READY; - } - else if (group_info->m_state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED) - { - group_info->m_state = SPU_THREAD_GROUP_STATUS_WAITING; - } - - for (u32 i = 0; i < group_info->list.size(); i++) - { - if (std::shared_ptr t = Emu.GetCPU().GetThread(group_info->list[i])) + if (t) { - t->Resume(); + auto& spu = static_cast(*t); + + spu.FastRun(); } } - if (group_info->m_state == SPU_THREAD_GROUP_STATUS_READY) - { - group_info->m_state = SPU_THREAD_GROUP_STATUS_RUNNING; - } - //REMOVE BUSY - return CELL_OK; } s32 sys_spu_thread_group_yield(u32 id) { - sys_spu.Error("sys_spu_thread_group_yield(id=%d)", id); + sys_spu.Log("sys_spu_thread_group_yield(id=%d)", id); - std::shared_ptr group_info; - if (!Emu.GetIdManager().GetIDData(id, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) { return CELL_ESRCH; } - - ////TODO::implement sys_spu_thread_group_yield. - //Sorry i don't know where to get the caller group. So Only checking. - //Removed some stupid comments. - //Check the priority of the target spu group info. - //And check the state of target spu. - //if ((group_info->m_prio < current_thread.GetPrio()) - // ||(group_info->m_state != SPU_THREAD_GROUP_STATUS_READY)) - //{ - // return CELL_OK; - //} - - ////Maybe Check for BUSY - - ////SET BUSY - //for (u32 i = 0; i < current_group_info->list.size(); i++) - //{ - //if (CPUThread* t = Emu.GetCPU().GetThread(current_group_info->list[i])) - //{ - //Not finding anything that suite the yield test. Do nothing. - //t->WaitFor(group_info); - //} - //} - - //Do nothing now, so not entering the WAITING state. - //current_group_info->m_state = SPU_THREAD_GROUP_STATUS_WAITING; - - ////CLEAR BUSY - - return CELL_OK; -} - -s32 sys_spu_thread_group_terminate(u32 id, int value) -{ - sys_spu.Error("sys_spu_thread_group_terminate(id=%d, value=%d)", id, value); - - std::shared_ptr group_info; - if (!Emu.GetIdManager().GetIDData(id, group_info)) - { - return CELL_ESRCH; - } - if ((group_info->m_state != SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED) - && (group_info->m_state != SPU_THREAD_GROUP_STATUS_INITIALIZED) - && (group_info->m_state != SPU_THREAD_GROUP_STATUS_WAITING)) - { - return CELL_ESTAT; - } - //TODO::I don't know who should i be referred to check the EPERM. - //Also i don't know how to check that is a primary or not. so disabled the EPERM check. - //Removed some stupid comments made. - - //Attention. This action may not check for BUSY - - //SET BUSY - for (u32 i = 0; i < group_info->list.size(); i++) - { - if (std::shared_ptr t = Emu.GetCPU().GetThread(group_info->list[i])) - { - ((SPUThread*)t.get())->status.write_relaxed(SPU_STATUS_STOPPED); - t->Stop(); - } - } - group_info->m_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; // In initialized state but not running, maybe. - //Remove BUSY - - group_info->m_exit_status = value; - - ////TODO::implement sys_spu_thread_group_terminate - return CELL_OK; -} - -std::shared_ptr spu_thread_group_create(const std::string& name, u32 num, s32 prio, s32 type, u32 container) -{ - if (type) - { - sys_spu.Todo("Unsupported SPU Thread Group type (0x%x)", type); - } - - std::shared_ptr group(new SpuGroupInfo(name, num, prio, type, container)); - - const u32 _id = sys_spu.GetNewId(group); - group->m_id = _id; - - sys_spu.Notice("*** SPU Thread Group created [%s] (num=%d, prio=%d, type=0x%x, container=%d): id=%d", name.c_str(), num, prio, type, container, _id); - return group; -} - -s32 sys_spu_thread_group_create(vm::ptr id, u32 num, s32 prio, vm::ptr attr) -{ - sys_spu.Warning("sys_spu_thread_group_create(id_addr=0x%x, num=%d, prio=%d, attr_addr=0x%x)", - id.addr(), num, prio, attr.addr()); - - if (!num || num > 6 || prio < 16 || prio > 255) + if (group->state != SPU_THREAD_GROUP_STATUS_RUNNING) { return CELL_EINVAL; } - *id = spu_thread_group_create(std::string(attr->name.get_ptr(), attr->nsize - 1), num, prio, attr->type, attr->ct)->m_id; + // SPU_THREAD_GROUP_STATUS_READY state is not used, so this function does nothing + + return CELL_OK; +} + +s32 sys_spu_thread_group_terminate(u32 id, s32 value) +{ + sys_spu.Warning("sys_spu_thread_group_terminate(id=%d, value=0x%x)", id, value); + + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) + { + return CELL_ESRCH; + } + + // CELL_EPERM is not returned (can't check the condition) + + if (group->state <= SPU_THREAD_GROUP_STATUS_INITIALIZED || group->state == SPU_THREAD_GROUP_STATUS_WAITING || group->state == SPU_THREAD_GROUP_STATUS_WAITING) + { + return CELL_EINVAL; + } + + for (auto t : group->threads) + { + if (t) + { + auto& spu = static_cast(*t); + + spu.status.write_relaxed(SPU_STATUS_STOPPED); + spu.FastStop(); + } + } + + group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; + group->exit_status = value; + group->join_state |= STGJSF_TERMINATED; + group->join_cv.notify_one(); return CELL_OK; } s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status) { - sys_spu.Warning("sys_spu_thread_group_join(id=%d, cause_addr=0x%x, status_addr=0x%x)", id, cause.addr(), status.addr()); + sys_spu.Warning("sys_spu_thread_group_join(id=%d, cause=*0x%x, status=*0x%x)", id, cause, status); - std::shared_ptr group_info; - if(!Emu.GetIdManager().GetIDData(id, group_info)) + LV2_LOCK; + + std::shared_ptr group; + if (!Emu.GetIdManager().GetIDData(id, group)) { return CELL_ESRCH; } - if (group_info->lock.exchange(1)) // acquire lock TODO:: The lock might be replaced. + if (group->state < SPU_THREAD_GROUP_STATUS_INITIALIZED) + { + return CELL_ESTAT; + } + + if (group->join_state.fetch_or(STGJSF_IS_JOINING) & STGJSF_IS_JOINING) { return CELL_EBUSY; } - bool all_threads_exit = true; - for (u32 i = 0; i < group_info->list.size(); i++) + while ((group->join_state & ~STGJSF_IS_JOINING) == 0) { - while (std::shared_ptr t = Emu.GetCPU().GetThread(group_info->list[i])) + bool stopped = true; + + for (auto t : group->threads) { - if (!t->IsAlive()) + if (t) { - if ((((SPUThread*)t.get())->status.read_sync() & SPU_STATUS_STOPPED_BY_STOP) != SPU_STATUS_STOPPED_BY_STOP) + auto& spu = static_cast(*t); + + if (!(spu.status.read_relaxed() & SPU_STATUS_STOPPED_BY_STOP)) { - all_threads_exit = false; + stopped = false; + break; } - break; } - if (Emu.IsStopped()) - { - sys_spu.Warning("sys_spu_thread_group_join(id=%d) aborted", id); - return CELL_OK; - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack } + + if (stopped) + { + break; + } + + if (Emu.IsStopped()) + { + sys_spu.Warning("sys_spu_thread_group_join(id=%d) aborted", id); + return CELL_OK; + } + + group->join_cv.wait_for(lv2_lock, std::chrono::milliseconds(1)); } - if (cause) + switch (group->join_state & ~STGJSF_IS_JOINING) { - *cause = group_info->m_group_exit - ? SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT - : (all_threads_exit - ? SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT - : SYS_SPU_THREAD_GROUP_JOIN_TERMINATED); - } - - if (status) *status = group_info->m_exit_status; - - group_info->m_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; - group_info->lock = 0; // release lock TODO: this LOCK may be replaced. - return CELL_OK; -} - -s32 sys_spu_thread_create(vm::ptr thread_id, vm::ptr entry, u64 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr) -{ - sys_spu.Todo("sys_spu_thread_create(thread_id_addr=0x%x, entry_addr=0x%x, arg=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname_addr=0x%x", - thread_id.addr(), entry.addr(), arg, prio, stacksize, flags, threadname_addr); - return CELL_OK; -} - -s32 sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu) -{ - sys_spu.Warning("sys_spu_initialize(max_usable_spu=%d, max_raw_spu=%d)", max_usable_spu, max_raw_spu); - - if(max_raw_spu > 5) + case 0: { - return CELL_EINVAL; + if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT; + break; + } + case STGJSF_GROUP_EXIT: + { + if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT; + break; + } + case STGJSF_TERMINATED: + { + if (cause) *cause = SYS_SPU_THREAD_GROUP_JOIN_TERMINATED; + break; + } + default: throw __FUNCTION__; } + if (status) + { + *status = group->exit_status; + } + + group->join_state &= ~STGJSF_IS_JOINING; + group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED; // hack return CELL_OK; } @@ -554,14 +578,14 @@ s32 sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type) sys_spu.Log("sys_spu_thread_write_ls(id=%d, address=0x%x, value=0x%llx, type=0x%x)", id, address, value, type); - std::shared_ptr thr = Emu.GetCPU().GetThread(id); + std::shared_ptr t = Emu.GetCPU().GetThread(id); - if(!thr || thr->GetType() != CPU_THREAD_SPU) + if (!t || t->GetType() != CPU_THREAD_SPU) { return CELL_ESRCH; } - if (!thr->IsRunning()) + if (!t->IsRunning()) { return CELL_ESTAT; } @@ -571,14 +595,18 @@ s32 sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type) return CELL_EINVAL; } + auto& spu = static_cast(*t); + switch (type) { - case 1: (*(SPUThread*)thr.get()).write8(address, (u8)value); return CELL_OK; - case 2: (*(SPUThread*)thr.get()).write16(address, (u16)value); return CELL_OK; - case 4: (*(SPUThread*)thr.get()).write32(address, (u32)value); return CELL_OK; - case 8: (*(SPUThread*)thr.get()).write64(address, value); return CELL_OK; + case 1: spu.write8(address, (u8)value); break; + case 2: spu.write16(address, (u16)value); break; + case 4: spu.write32(address, (u32)value); break; + case 8: spu.write64(address, value); break; default: return CELL_EINVAL; } + + return CELL_OK; } s32 sys_spu_thread_read_ls(u32 id, u32 address, vm::ptr value, u32 type) @@ -586,14 +614,14 @@ s32 sys_spu_thread_read_ls(u32 id, u32 address, vm::ptr value, u32 type) sys_spu.Log("sys_spu_thread_read_ls(id=%d, address=0x%x, value_addr=0x%x, type=0x%x)", id, address, value.addr(), type); - std::shared_ptr thr = Emu.GetCPU().GetThread(id); + std::shared_ptr t = Emu.GetCPU().GetThread(id); - if(!thr || thr->GetType() != CPU_THREAD_SPU) + if (!t || t->GetType() != CPU_THREAD_SPU) { return CELL_ESRCH; } - if (!thr->IsRunning()) + if (!t->IsRunning()) { return CELL_ESTAT; } @@ -603,14 +631,18 @@ s32 sys_spu_thread_read_ls(u32 id, u32 address, vm::ptr value, u32 type) return CELL_EINVAL; } + auto& spu = static_cast(*t); + switch (type) { - case 1: *value = (*(SPUThread*)thr.get()).read8(address); return CELL_OK; - case 2: *value = (*(SPUThread*)thr.get()).read16(address); return CELL_OK; - case 4: *value = (*(SPUThread*)thr.get()).read32(address); return CELL_OK; - case 8: *value = (*(SPUThread*)thr.get()).read64(address); return CELL_OK; + case 1: *value = spu.read8(address); break; + case 2: *value = spu.read16(address); break; + case 4: *value = spu.read32(address); break; + case 8: *value = spu.read64(address); break; default: return CELL_EINVAL; } + + return CELL_OK; } s32 sys_spu_thread_write_spu_mb(u32 id, u32 value) @@ -837,13 +869,16 @@ s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num) return CELL_OK; } -s32 sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq_id, u64 req, vm::ptr spup) +s32 sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, vm::ptr spup) { - sys_spu.Warning("sys_spu_thread_group_connect_event_all_threads(id=%d, eq_id=%d, req=0x%llx, spup_addr=0x%x)", - id, eq_id, req, spup.addr()); + sys_spu.Warning("sys_spu_thread_group_connect_event_all_threads(id=%d, eq=%d, req=0x%llx, spup=*0x%x)", id, eq, req, spup); - std::shared_ptr eq; - if (!Emu.GetIdManager().GetIDData(eq_id, eq)) + LV2_LOCK; + + std::shared_ptr group; + std::shared_ptr queue; + + if (!Emu.GetIdManager().GetIDData(id, group) || !Emu.GetIdManager().GetIDData(eq, queue)) { return CELL_ESRCH; } @@ -853,59 +888,51 @@ s32 sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq_id, u64 req, v return CELL_EINVAL; } - std::shared_ptr group; - if (!Emu.GetIdManager().GetIDData(id, group)) + if (group->state < SPU_THREAD_GROUP_STATUS_INITIALIZED) { - return CELL_ESRCH; - } - - std::vector> threads; - for (auto& v : group->list) - { - if (!v) continue; - std::shared_ptr thr = Emu.GetCPU().GetThread(v); - if (thr->GetType() != CPU_THREAD_SPU) - { - sys_spu.Error("sys_spu_thread_group_connect_event_all_threads(): CELL_ESTAT (wrong thread type)"); - return CELL_ESTAT; - } - threads.push_back(thr); - } - - if (threads.size() != group->m_count) - { - sys_spu.Error("sys_spu_thread_group_connect_event_all_threads(): CELL_ESTAT (%d from %d)", (u32)threads.size(), group->m_count); return CELL_ESTAT; } - for (u32 i = 0; i < 64; i++) // port number + for (u8 port = 0; port < 64; port++) // port number { bool found = true; - if (req & (1ull << i)) + + if (req & (1ull << port)) { - //for (auto& t : threads) ((SPUThread*)t.get())->SPUPs[i]->m_mutex.lock(); + for (auto t : group->threads) + { + if (t) + { + auto& spu = static_cast(*t); - //for (auto& t : threads) if (((SPUThread*)t.get())->SPUPs[i]->eq) found = false; - - //if (found) - //{ - // for (auto& t : threads) - // { - // eq->ports.add(((SPUThread*)t.get())->SPUPs[i]); - // ((SPUThread*)t.get())->SPUPs[i]->eq = eq; - // } - // sys_spu.Warning("*** spup -> %d", i); - // *spup = (u8)i; - //} - - //for (auto& t : threads) ((SPUThread*)t.get())->SPUPs[i]->m_mutex.unlock(); + if (!spu.spup[port].expired()) + { + found = false; + break; + } + } + } } else { - found = false; + continue; } - if (found) return CELL_OK; + if (found) + { + for (auto t : group->threads) + { + if (t) + { + auto& spu = static_cast(*t); + + spu.spup[port] = queue; + } + } + + *spup = port; + return CELL_OK; + } } return CELL_EISCONN; @@ -918,19 +945,23 @@ s32 sys_spu_thread_group_disconnect_event_all_threads(u32 id, u8 spup) return CELL_OK; } -s32 sys_raw_spu_create(vm::ptr id, u32 attr_addr) +s32 sys_raw_spu_create(vm::ptr id, vm::ptr attr) { - sys_spu.Warning("sys_raw_spu_create(id_addr=0x%x, attr_addr=0x%x)", id.addr(), attr_addr); + sys_spu.Warning("sys_raw_spu_create(id=*0x%x, attr=*0x%x)", id, attr); - CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_RAW_SPU); - if (((RawSPUThread&)new_thread).GetIndex() >= 5) + LV2_LOCK; + + auto t = Emu.GetCPU().AddThread(CPU_THREAD_RAW_SPU); + + if (!t) { - Emu.GetCPU().RemoveThread(new_thread.GetId()); return CELL_EAGAIN; } - *id = ((RawSPUThread&)new_thread).GetIndex(); - new_thread.Run(); + Memory.Map(t->offset = RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * t->index, 0x40000); + t->Run(); + + *id = t->index; return CELL_OK; } @@ -938,6 +969,8 @@ s32 sys_raw_spu_destroy(u32 id) { sys_spu.Warning("sys_raw_spu_destroy(id=%d)", id); + LV2_LOCK; + std::shared_ptr t = Emu.GetCPU().GetRawSPUThread(id); if (!t) @@ -945,10 +978,11 @@ s32 sys_raw_spu_destroy(u32 id) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); // TODO: check if busy + Memory.Unmap(spu.offset); Emu.GetCPU().RemoveThread(t->GetId()); return CELL_OK; } @@ -969,7 +1003,7 @@ s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); auto& tag = class_id ? spu.int2 : spu.int0; @@ -998,7 +1032,7 @@ s32 sys_raw_spu_set_int_mask(u32 id, u32 class_id, u64 mask) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); (class_id ? spu.int2 : spu.int0).mask.exchange(mask); @@ -1021,7 +1055,7 @@ s32 sys_raw_spu_get_int_mask(u32 id, u32 class_id, vm::ptr mask) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); *mask = (class_id ? spu.int2 : spu.int0).mask.read_sync(); @@ -1044,7 +1078,7 @@ s32 sys_raw_spu_set_int_stat(u32 id, u32 class_id, u64 stat) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); (class_id ? spu.int2 : spu.int0).clear(stat); @@ -1067,7 +1101,7 @@ s32 sys_raw_spu_get_int_stat(u32 id, u32 class_id, vm::ptr stat) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); *stat = (class_id ? spu.int2 : spu.int0).stat.read_sync(); @@ -1085,7 +1119,7 @@ s32 sys_raw_spu_read_puint_mb(u32 id, vm::ptr value) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); *value = spu.ch_out_intr_mbox.pop_uncond(); @@ -1108,7 +1142,7 @@ s32 sys_raw_spu_set_spu_cfg(u32 id, u32 value) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); spu.snr_config = value; @@ -1126,7 +1160,7 @@ s32 sys_raw_spu_get_spu_cfg(u32 id, vm::ptr value) return CELL_ESRCH; } - RawSPUThread& spu = static_cast(*t); + auto& spu = static_cast(*t); *value = (u32)spu.snr_config; diff --git a/rpcs3/Emu/SysCalls/lv2/sys_spu.h b/rpcs3/Emu/SysCalls/lv2/sys_spu.h index e129455758..9879ecbd7b 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_spu.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_spu.h @@ -32,7 +32,7 @@ enum : u64 SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE_KEY = 0xFFFFFFFF53505504ull, }; -enum +enum : u32 { SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED, SPU_THREAD_GROUP_STATUS_INITIALIZED, @@ -54,7 +54,7 @@ enum : s32 struct sys_spu_thread_group_attribute { - be_t nsize; + be_t nsize; // name length including NULL terminator vm::bptr name; be_t type; be_t ct; // memory container id @@ -121,35 +121,50 @@ enum : u32 SYS_SPU_IMAGE_DIRECT = 1, }; -struct SpuGroupInfo +struct spu_arg_t { - std::vector list; - std::atomic lock; - std::string m_name; - u32 m_id; - s32 m_prio; - s32 m_type; - u32 m_ct; - u32 m_count; - s32 m_state; //SPU Thread Group State. - u32 m_exit_status; - bool m_group_exit; + u64 arg1; + u64 arg2; + u64 arg3; + u64 arg4; +}; - SpuGroupInfo(const std::string& name, u32 num, s32 prio, s32 type, u32 ct) - : m_name(name) - , m_prio(prio) - , m_type(type) - , m_ct(ct) - , lock(0) - , m_count(num) - , m_state(0) - , m_exit_status(0) - , m_group_exit(false) +// SPU Thread Group Join State Flag +enum : u32 +{ + STGJSF_IS_JOINING = (1 << 0), + STGJSF_TERMINATED = (1 << 1), // set if SPU Thread Group is terminated by sys_spu_thread_group_terminate + STGJSF_GROUP_EXIT = (1 << 2), // set if SPU Thread Group is terminated by sys_spu_thread_group_exit +}; + +struct spu_group_t +{ + const std::string name; + const u32 num; // SPU Number + const s32 type; // SPU Thread Group Type + const u32 ct; // Memory Container Id + + std::array, 256> threads; + std::array args; // SPU Thread Arguments + std::array, 256> images; // SPU Thread Images + + s32 prio; // SPU Thread Group Priority + u32 state; // SPU Thread Group State + s32 exit_status; // SPU Thread Group Exit Status + + std::atomic join_state; // flags used to detect exit cause + std::condition_variable join_cv; // used to signal waiting PPU thread + + spu_group_t(std::string name, u32 num, s32 prio, s32 type, u32 ct) + : name(name) + , num(num) + , prio(prio) + , type(type) + , ct(ct) + , state(SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED) + , exit_status(0) + , join_state(0) { - m_state = SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED; //Before all the nums done, it is not initialized. - list.resize(256); - for (auto& v : list) v = 0; - m_state = SPU_THREAD_GROUP_STATUS_INITIALIZED; //Then Ready to Start. Cause Reference use New i can only place this here. } }; @@ -161,22 +176,21 @@ u32 LoadSpuImage(vfsStream& stream, u32& spu_ep); // Aux s32 spu_image_import(sys_spu_image& img, u32 src, u32 type); -std::shared_ptr spu_thread_group_create(const std::string& name, u32 num, s32 prio, s32 type, u32 container); -SPUThread* spu_thread_initialize(std::shared_ptr& group, u32 spu_num, sys_spu_image& img, const std::string& name, u32 option, u64 a1, u64 a2, u64 a3, u64 a4, std::function task = nullptr); +u32 spu_thread_group_create(const std::string& name, u32 num, s32 prio, s32 type, u32 container); +u32 spu_thread_initialize(u32 group, u32 spu_num, vm::ptr img, const std::string& name, u32 option, u64 a1, u64 a2, u64 a3, u64 a4, std::function task = nullptr); // SysCalls s32 sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu); s32 sys_spu_image_open(vm::ptr img, vm::ptr path); s32 sys_spu_thread_initialize(vm::ptr thread, u32 group, u32 spu_num, vm::ptr img, vm::ptr attr, vm::ptr arg); s32 sys_spu_thread_set_argument(u32 id, vm::ptr arg); +s32 sys_spu_thread_group_create(vm::ptr id, u32 num, s32 prio, vm::ptr attr); s32 sys_spu_thread_group_destroy(u32 id); s32 sys_spu_thread_group_start(u32 id); s32 sys_spu_thread_group_suspend(u32 id); s32 sys_spu_thread_group_resume(u32 id); s32 sys_spu_thread_group_yield(u32 id); -s32 sys_spu_thread_group_terminate(u32 id, int value); -s32 sys_spu_thread_group_create(vm::ptr id, u32 num, int prio, vm::ptr attr); -s32 sys_spu_thread_create(vm::ptr thread_id, vm::ptr entry, u64 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr); +s32 sys_spu_thread_group_terminate(u32 id, s32 value); s32 sys_spu_thread_group_join(u32 id, vm::ptr cause, vm::ptr status); s32 sys_spu_thread_group_connect_event(u32 id, u32 eq, u32 et); s32 sys_spu_thread_group_disconnect_event(u32 id, u32 et); @@ -194,7 +208,7 @@ s32 sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num); s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num); s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr status); -s32 sys_raw_spu_create(vm::ptr id, u32 attr_addr); +s32 sys_raw_spu_create(vm::ptr id, vm::ptr attr); s32 sys_raw_spu_destroy(u32 id); s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr intrtag); s32 sys_raw_spu_set_int_mask(u32 id, u32 class_id, u64 mask);