s32 sys_spu_thread_bind_queue(u32 id, u32 eq_id, u32 spuq_num) { sc_spu.Warning("sys_spu_thread_bind_queue(id=%d, equeue_id=%d, spuq_num=0x%x)", id, eq_id, spuq_num); EventQueue* eq; if (!Emu.GetIdManager().GetIDData(eq_id, eq)) { return CELL_ESRCH; } if (eq->type != SYS_SPU_QUEUE) { return CELL_EINVAL; } CPUThread* thr = Emu.GetCPU().GetThread(id); if(!thr || (thr->GetType() != CPU_THREAD_SPU && thr->GetType() != CPU_THREAD_RAW_SPU)) { return CELL_ESRCH; } if (!(*(SPUThread*)thr).SPUQs.RegisterKey(eq, FIX_SPUQ(spuq_num))) { return CELL_EBUSY; } return CELL_OK; }
s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num) { sc_spu.Warning("sys_spu_thread_unbind_queue(id=0x%x, spuq_num=0x%x)", id, spuq_num); CPUThread* thr = Emu.GetCPU().GetThread(id); if(!thr || (thr->GetType() != CPU_THREAD_SPU && thr->GetType() != CPU_THREAD_RAW_SPU)) { return CELL_ESRCH; } if (!(*(SPUThread*)thr).SPUQs.UnregisterKey(FIX_SPUQ(spuq_num))) { return CELL_ESRCH; // may be CELL_EINVAL } return CELL_OK; }
void SPUThread::StopAndSignal(u32 code) { SetExitStatus(code); // exit code (not status) // TODO: process interrupts for RawSPU switch (code) { case 0x110: { /* ===== sys_spu_thread_receive_event ===== */ u32 spuq = 0; if (!SPU.Out_MBox.Pop(spuq)) { LOG_ERROR(Log::SPU, "sys_spu_thread_receive_event: cannot read Out_MBox"); SPU.In_MBox.PushUncond(CELL_EINVAL); // ??? return; } if (SPU.In_MBox.GetCount()) { LOG_ERROR(Log::SPU, "sys_spu_thread_receive_event(spuq=0x%x): In_MBox is not empty", spuq); SPU.In_MBox.PushUncond(CELL_EBUSY); // ??? return; } if (Ini.HLELogging.GetValue()) { LOG_NOTICE(Log::SPU, "sys_spu_thread_receive_event(spuq=0x%x)", spuq); } EventQueue* eq; if (!SPUQs.GetEventQueue(FIX_SPUQ(spuq), eq)) { SPU.In_MBox.PushUncond(CELL_EINVAL); // TODO: check error value return; } u32 tid = GetId(); eq->sq.push(tid); // add thread to sleep queue while (true) { switch (eq->owner.trylock(tid)) { case SMR_OK: if (!eq->events.count()) { eq->owner.unlock(tid); break; } else { u32 next = (eq->protocol == SYS_SYNC_FIFO) ? eq->sq.pop() : eq->sq.pop_prio(); if (next != tid) { eq->owner.unlock(tid, next); break; } } case SMR_SIGNAL: { sys_event_data event; eq->events.pop(event); eq->owner.unlock(tid); SPU.In_MBox.PushUncond(CELL_OK); SPU.In_MBox.PushUncond((u32)event.data1); SPU.In_MBox.PushUncond((u32)event.data2); SPU.In_MBox.PushUncond((u32)event.data3); return; } case SMR_FAILED: break; default: eq->sq.invalidate(tid); SPU.In_MBox.PushUncond(CELL_ECANCELED); return; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (Emu.IsStopped()) { LOG_WARNING(Log::SPU, "sys_spu_thread_receive_event(spuq=0x%x) aborted", spuq); eq->sq.invalidate(tid); return; } } break; } case 0x101: { /* ===== sys_spu_thread_group_exit ===== */ if (!group) { LOG_ERROR(Log::SPU, "sys_spu_thread_group_exit(): group not set"); break; } else if (!SPU.Out_MBox.GetCount()) { LOG_ERROR(Log::SPU, "sys_spu_thread_group_exit(): Out_MBox is empty"); } else if (Ini.HLELogging.GetValue()) { LOG_NOTICE(Log::SPU, "sys_spu_thread_group_exit(status=0x%x)", SPU.Out_MBox.GetValue()); } group->m_group_exit = true; group->m_exit_status = SPU.Out_MBox.GetValue(); for (auto& v : group->list) { if (CPUThread* t = Emu.GetCPU().GetThread(v)) { t->Stop(); } } break; } case 0x102: { /* ===== sys_spu_thread_exit ===== */ if (!SPU.Out_MBox.GetCount()) { LOG_ERROR(Log::SPU, "sys_spu_thread_exit(): Out_MBox is empty"); } else if (Ini.HLELogging.GetValue()) { // the real exit status LOG_NOTICE(Log::SPU, "sys_spu_thread_exit(status=0x%x)", SPU.Out_MBox.GetValue()); } SPU.Status.SetValue(SPU_STATUS_STOPPED_BY_STOP); Stop(); break; } default: { if (!SPU.Out_MBox.GetCount()) { LOG_ERROR(Log::SPU, "Unknown STOP code: 0x%x (no message)", code); } else { LOG_ERROR(Log::SPU, "Unknown STOP code: 0x%x (message=0x%x)", code, SPU.Out_MBox.GetValue()); } Stop(); break; } } }