rpcsx/rpcs3/Emu/Cell/SPUThread.cpp

1292 lines
25 KiB
C++
Raw Normal View History

#include "stdafx.h"
2014-08-23 02:16:54 +02:00
#include "rpcs3/Ini.h"
#include "Utilities/Log.h"
2014-08-23 02:16:54 +02:00
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
2014-08-26 01:55:37 +02:00
#include "Emu/IdManager.h"
#include "Emu/CPU/CPUThreadManager.h"
#include "Emu/Cell/PPUThread.h"
2014-08-22 23:15:02 +02:00
#include "Emu/SysCalls/ErrorCodes.h"
#include "Emu/SysCalls/lv2/sys_spu.h"
#include "Emu/SysCalls/lv2/sys_event_flag.h"
2015-03-04 05:42:04 +01:00
#include "Emu/SysCalls/lv2/sys_event.h"
2014-08-22 23:15:02 +02:00
#include "Emu/SysCalls/lv2/sys_time.h"
2014-08-26 01:55:37 +02:00
#include "Emu/Cell/SPUDisAsm.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/SPUDecoder.h"
#include "Emu/Cell/SPUInterpreter.h"
2015-03-20 17:53:54 +01:00
#include "Emu/Cell/SPUInterpreter2.h"
2014-04-06 21:23:32 +02:00
#include "Emu/Cell/SPURecompiler.h"
2014-07-16 14:09:20 +02:00
#include <cfenv>
2015-04-05 20:01:15 +02:00
const g_spu_imm_table_t g_spu_imm;
2015-03-20 17:53:54 +01:00
class spu_inter_func_list_t
{
2015-04-26 00:56:58 +02:00
std::array<spu_inter_func_t, 2048> funcs = {};
std::mutex m_mutex;
2015-03-20 17:53:54 +01:00
public:
2015-04-26 00:56:58 +02:00
void initialize()
2015-03-20 17:53:54 +01:00
{
2015-04-26 00:56:58 +02:00
std::lock_guard<std::mutex> lock(m_mutex);
if (funcs[0]) return; // check if already initialized
2015-03-20 17:53:54 +01:00
auto inter = new SPUInterpreter2;
SPUDecoder dec(*inter);
for (u32 i = 0; i < funcs.size(); i++)
{
inter->func = spu_interpreter::DEFAULT;
dec.Decode(i << 21);
funcs[i] = inter->func;
}
}
2015-07-01 00:25:52 +02:00
force_inline spu_inter_func_t operator [](u32 opcode) const
2015-03-20 17:53:54 +01:00
{
return funcs[opcode >> 21];
}
}
2015-04-26 00:56:58 +02:00
g_spu_inter_func_list;
2015-03-20 17:53:54 +01:00
2015-07-04 21:23:10 +02:00
SPUThread::SPUThread(CPUThreadType type, const std::string& name, std::function<std::string()> thread_name, u32 index, u32 offset)
: CPUThread(type, name, std::move(thread_name))
2015-07-01 00:25:52 +02:00
, index(index)
, offset(offset)
{
2015-07-01 00:25:52 +02:00
}
2015-07-03 18:07:36 +02:00
SPUThread::SPUThread(const std::string& name, u32 index)
2015-07-04 21:23:10 +02:00
: CPUThread(CPU_THREAD_SPU, name, WRAP_EXPR(fmt::format("SPU[0x%x] Thread (%s)[0x%08x]", GetId(), GetName(), PC)))
2015-07-01 00:25:52 +02:00
, index(index)
2015-07-03 18:07:36 +02:00
, offset(Memory.MainMem.AllocAlign(0x40000))
2015-07-01 00:25:52 +02:00
{
}
SPUThread::~SPUThread()
{
2015-07-01 00:25:52 +02:00
if (m_type == CPU_THREAD_SPU)
{
join();
2015-07-03 18:07:36 +02:00
Memory.MainMem.Free(offset);
2015-07-01 00:25:52 +02:00
}
else if (joinable())
{
throw EXCEPTION("Thread not joined");
}
}
bool SPUThread::IsPaused() const
{
2015-07-03 18:07:36 +02:00
if (CPUThread::IsPaused())
{
return true;
}
2015-07-01 00:25:52 +02:00
if (const auto group = tg.lock())
{
2015-07-03 18:07:36 +02:00
if (group->state >= SPU_THREAD_GROUP_STATUS_WAITING && group->state <= SPU_THREAD_GROUP_STATUS_SUSPENDED)
2015-07-01 00:25:52 +02:00
{
return true;
}
}
2015-07-03 18:07:36 +02:00
return false;
2015-07-01 00:25:52 +02:00
}
void SPUThread::DumpInformation() const
{
CPUThread::DumpInformation();
}
2014-07-16 14:09:20 +02:00
void SPUThread::Task()
{
std::fesetround(FE_TOWARDZERO);
2014-09-24 20:44:26 +02:00
if (m_custom_task)
{
2015-07-01 00:25:52 +02:00
if (CheckStatus()) return;
2015-07-01 19:09:26 +02:00
return m_custom_task(*this);
2014-09-24 20:44:26 +02:00
}
2015-03-20 17:53:54 +01:00
if (m_dec)
2014-09-24 20:44:26 +02:00
{
2015-07-01 00:25:52 +02:00
while (true)
{
2015-07-01 19:09:26 +02:00
if (m_state.load() && CheckStatus()) break;
2015-03-20 17:53:54 +01:00
2015-07-01 00:25:52 +02:00
// decode instruction using specified decoder
m_dec->DecodeMemory(PC + offset);
2015-03-20 17:53:54 +01:00
2015-07-01 00:25:52 +02:00
// next instruction
PC += 4;
}
}
else
{
while (true)
2015-03-20 17:53:54 +01:00
{
2015-07-01 19:09:26 +02:00
if (m_state.load() && CheckStatus()) break;
2015-03-21 00:36:05 +01:00
2015-07-01 00:25:52 +02:00
// read opcode
const spu_opcode_t opcode = { vm::read32(PC + offset) };
2015-03-20 17:53:54 +01:00
2015-07-01 00:25:52 +02:00
// get interpreter function
const auto func = g_spu_inter_func_list[opcode.opcode];
2015-03-20 17:53:54 +01:00
2015-07-01 00:25:52 +02:00
// call interpreter function
func(*this, opcode);
2014-07-16 14:09:20 +02:00
2015-07-01 00:25:52 +02:00
// next instruction
PC += 4;
}
}
}
void SPUThread::InitRegs()
{
2015-03-02 03:10:41 +01:00
memset(GPR, 0, sizeof(GPR));
FPSCR.Reset();
ch_mfc_args = {};
mfc_queue.clear();
2015-03-13 02:59:25 +01:00
ch_tag_mask = 0;
2015-03-13 02:09:53 +01:00
ch_tag_stat = {};
ch_stall_stat = {};
ch_atomic_stat = {};
2015-03-02 03:10:41 +01:00
ch_in_mbox.clear();
2015-03-13 02:09:53 +01:00
ch_out_mbox = {};
ch_out_intr_mbox = {};
2014-07-16 18:10:18 +02:00
2015-03-13 02:59:25 +01:00
snr_config = 0;
2015-03-13 02:09:53 +01:00
ch_snr1 = {};
ch_snr2 = {};
2015-03-02 03:10:41 +01:00
2015-03-13 02:59:25 +01:00
ch_event_mask = 0;
2015-03-13 02:09:53 +01:00
ch_event_stat = {};
2015-03-02 03:10:41 +01:00
ch_dec_start_timestamp = get_time(); // ???
2015-03-13 02:59:25 +01:00
ch_dec_value = 0;
2015-03-02 03:10:41 +01:00
2015-03-13 02:09:53 +01:00
run_ctrl = {};
status = {};
2015-03-13 02:59:25 +01:00
npc = {};
2015-03-02 03:10:41 +01:00
int0.clear();
int2.clear();
GPR[1]._u32[3] = 0x3FFF0; // initial stack frame pointer
}
void SPUThread::InitStack()
{
2015-07-01 00:25:52 +02:00
// nothing to do
}
void SPUThread::CloseStack()
{
// nothing to do here
}
void SPUThread::DoRun()
{
2015-03-20 17:53:54 +01:00
m_dec = nullptr;
switch (auto mode = Ini.SPUDecoderMode.GetValue())
{
case 0: // original interpreter
{
2015-07-01 00:25:52 +02:00
m_dec.reset(new SPUDecoder(*new SPUInterpreter(*this)));
2015-03-20 17:53:54 +01:00
break;
}
case 1: // alternative interpreter
{
2015-04-26 00:56:58 +02:00
g_spu_inter_func_list.initialize(); // initialize helper table
2015-03-20 17:53:54 +01:00
break;
}
case 2:
2015-03-20 17:53:54 +01:00
{
2015-07-01 00:25:52 +02:00
m_dec.reset(new SPURecompilerCore(*this));
2015-03-20 17:53:54 +01:00
break;
}
default:
2015-03-20 17:53:54 +01:00
{
LOG_ERROR(SPU, "Invalid SPU decoder mode: %d", mode);
Emu.Pause();
}
2015-03-20 17:53:54 +01:00
}
}
2014-09-24 20:44:26 +02:00
void SPUThread::FastCall(u32 ls_addr)
{
2015-07-01 00:25:52 +02:00
if (!is_current())
{
throw EXCEPTION("Called from the wrong thread");
}
2015-03-02 03:10:41 +01:00
write32(0x0, 2);
2014-09-24 20:44:26 +02:00
auto old_PC = PC;
2014-10-02 12:29:20 +02:00
auto old_LR = GPR[0]._u32[3];
auto old_stack = GPR[1]._u32[3]; // only saved and restored (may be wrong)
2015-03-21 00:36:05 +01:00
auto old_task = decltype(m_custom_task)();
2014-09-24 20:44:26 +02:00
PC = ls_addr;
2014-10-02 12:29:20 +02:00
GPR[0]._u32[3] = 0x0;
2015-04-13 19:39:38 +02:00
m_custom_task.swap(old_task);
2014-09-24 20:44:26 +02:00
2015-07-01 00:25:52 +02:00
try
{
Task();
}
catch (CPUThreadReturn)
{
}
2014-09-24 20:44:26 +02:00
2015-07-01 19:09:26 +02:00
m_state &= ~CPU_STATE_RETURN;
2014-09-24 20:44:26 +02:00
PC = old_PC;
2014-10-02 12:29:20 +02:00
GPR[0]._u32[3] = old_LR;
GPR[1]._u32[3] = old_stack;
2015-04-13 19:39:38 +02:00
m_custom_task.swap(old_task);
2014-10-02 12:29:20 +02:00
}
2015-03-02 03:10:41 +01:00
void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args)
2014-08-22 23:15:02 +02:00
{
2015-02-16 02:53:53 +01:00
if (cmd & (MFC_BARRIER_MASK | MFC_FENCE_MASK))
{
_mm_mfence();
}
2014-08-22 23:15:02 +02:00
2015-07-03 01:11:44 +02:00
u32 eal = VM_CAST(args.ea);
2015-02-15 18:13:06 +01:00
2015-03-04 22:51:14 +01:00
if (eal >= SYS_SPU_THREAD_BASE_LOW && m_type == CPU_THREAD_SPU) // SPU Thread Group MMIO (LS and SNR)
2014-08-22 23:15:02 +02:00
{
2015-03-04 22:51:14 +01:00
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
2015-02-15 18:13:06 +01:00
2015-04-12 03:36:25 +02:00
const auto group = tg.lock();
2014-08-22 23:15:02 +02:00
2015-04-12 03:36:25 +02:00
if (group && index < group->num && group->threads[index])
2015-02-15 18:13:06 +01:00
{
2015-04-12 03:36:25 +02:00
auto& spu = static_cast<SPUThread&>(*group->threads[index]);
2014-09-19 02:19:22 +02:00
2015-03-02 03:10:41 +01:00
if (offset + args.size - 1 < 0x40000) // LS access
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
eal = spu.offset + offset; // redirect access
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
else if ((cmd & MFC_PUT_CMD) && args.size == 4 && (offset == SYS_SPU_THREAD_SNR1 || offset == SYS_SPU_THREAD_SNR2))
2014-08-22 23:15:02 +02:00
{
spu.write_snr(SYS_SPU_THREAD_SNR2 == offset, read32(args.lsa));
2014-08-22 23:15:02 +02:00
return;
}
else
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Invalid MMIO offset (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x)", cmd, args.lsa, args.ea, args.tag, args.size);
2014-08-22 23:15:02 +02:00
}
}
else
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Invalid thread type (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x)", cmd, args.lsa, args.ea, args.tag, args.size);
2014-08-22 23:15:02 +02:00
}
}
2015-03-02 03:10:41 +01:00
switch (cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK))
2014-08-22 23:15:02 +02:00
{
case MFC_PUT_CMD:
2015-03-02 03:10:41 +01:00
case MFC_PUTR_CMD:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
memcpy(vm::get_ptr(eal), vm::get_ptr(offset + args.lsa), args.size);
2014-08-22 23:15:02 +02:00
return;
}
case MFC_GET_CMD:
{
2015-03-02 03:10:41 +01:00
memcpy(vm::get_ptr(offset + args.lsa), vm::get_ptr(eal), args.size);
2014-08-22 23:15:02 +02:00
return;
}
}
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Invalid command %s (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x)", get_mfc_cmd_name(cmd), cmd, args.lsa, args.ea, args.tag, args.size);
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
void SPUThread::do_dma_list_cmd(u32 cmd, spu_mfc_arg_t args)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (!(cmd & MFC_LIST_MASK))
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Invalid command %s (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x)", get_mfc_cmd_name(cmd), cmd, args.lsa, args.ea, args.tag, args.size);
2015-03-02 03:10:41 +01:00
}
const u32 list_addr = args.ea & 0x3ffff;
const u32 list_size = args.size / 8;
args.lsa &= 0x3fff0;
2014-08-22 23:15:02 +02:00
struct list_element
{
2015-03-02 03:10:41 +01:00
be_t<u16> sb; // Stall-and-Notify bit (0x8000)
2014-08-22 23:15:02 +02:00
be_t<u16> ts; // List Transfer Size
be_t<u32> ea; // External Address Low
};
for (u32 i = 0; i < list_size; i++)
{
2015-03-02 03:10:41 +01:00
auto rec = vm::ptr<list_element>::make(offset + list_addr + i * 8);
2014-08-22 23:15:02 +02:00
const u32 size = rec->ts;
const u32 addr = rec->ea;
2015-03-02 03:10:41 +01:00
2014-09-24 20:44:26 +02:00
if (size)
{
2015-03-02 03:10:41 +01:00
spu_mfc_arg_t transfer;
transfer.ea = addr;
transfer.lsa = args.lsa | (addr & 0xf);
transfer.tag = args.tag;
transfer.size = size;
2014-09-24 20:44:26 +02:00
2015-03-02 03:10:41 +01:00
do_dma_transfer(cmd & ~MFC_LIST_MASK, transfer);
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
args.lsa += std::max<u32>(size, 16);
}
2014-08-22 23:15:02 +02:00
if (rec->sb & 0x8000)
2014-08-22 23:15:02 +02:00
{
2015-05-08 11:45:21 +02:00
ch_stall_stat.push_bit_or(1 << args.tag);
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
spu_mfc_arg_t stalled;
stalled.ea = (args.ea & ~0xffffffff) | (list_addr + (i + 1) * 8);
stalled.lsa = args.lsa;
stalled.tag = args.tag;
stalled.size = (list_size - i - 1) * 8;
2015-03-02 03:10:41 +01:00
mfc_queue.emplace_back(cmd, stalled);
return;
2014-08-22 23:15:02 +02:00
}
}
}
2015-03-02 03:10:41 +01:00
void SPUThread::process_mfc_cmd(u32 cmd)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(SPU, "DMA %s: cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x", get_mfc_cmd_name(cmd), cmd, ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size);
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
switch (cmd)
2014-08-22 23:15:02 +02:00
{
case MFC_PUT_CMD:
2015-03-02 03:10:41 +01:00
case MFC_PUTB_CMD:
case MFC_PUTF_CMD:
case MFC_PUTR_CMD:
case MFC_PUTRB_CMD:
case MFC_PUTRF_CMD:
2014-08-22 23:15:02 +02:00
case MFC_GET_CMD:
2015-03-02 03:10:41 +01:00
case MFC_GETB_CMD:
case MFC_GETF_CMD:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
do_dma_transfer(cmd, ch_mfc_args);
return;
2014-08-24 19:42:19 +02:00
}
2014-08-22 23:15:02 +02:00
case MFC_PUTL_CMD:
2015-03-02 03:10:41 +01:00
case MFC_PUTLB_CMD:
case MFC_PUTLF_CMD:
case MFC_PUTRL_CMD:
case MFC_PUTRLB_CMD:
case MFC_PUTRLF_CMD:
2014-08-22 23:15:02 +02:00
case MFC_GETL_CMD:
2015-03-02 03:10:41 +01:00
case MFC_GETLB_CMD:
case MFC_GETLF_CMD:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
do_dma_list_cmd(cmd, ch_mfc_args);
return;
2014-08-24 19:42:19 +02:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
case MFC_GETLLAR_CMD: // acquire reservation
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (ch_mfc_args.size != 128)
{
break;
}
2014-08-22 23:15:02 +02:00
2015-07-03 01:11:44 +02:00
vm::reservation_acquire(vm::get_ptr(offset + ch_mfc_args.lsa), VM_CAST(ch_mfc_args.ea), 128, [this]()
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
ch_event_stat |= SPU_EVENT_LR;
2015-07-01 00:25:52 +02:00
cv.notify_one();
2015-03-02 03:10:41 +01:00
});
ch_atomic_stat.push_uncond(MFC_GETLLAR_SUCCESS);
return;
}
2015-02-07 00:39:51 +01:00
2015-03-02 03:10:41 +01:00
case MFC_PUTLLC_CMD: // store conditionally
{
if (ch_mfc_args.size != 128)
{
break;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2015-07-03 01:11:44 +02:00
if (vm::reservation_update(VM_CAST(ch_mfc_args.ea), vm::get_ptr(offset + ch_mfc_args.lsa), 128))
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
ch_atomic_stat.push_uncond(MFC_PUTLLC_SUCCESS);
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
else
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
ch_atomic_stat.push_uncond(MFC_PUTLLC_FAILURE);
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
return;
2014-08-24 19:42:19 +02:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
case MFC_PUTLLUC_CMD: // store unconditionally
case MFC_PUTQLLUC_CMD:
2015-02-16 02:53:53 +01:00
{
2015-03-02 03:10:41 +01:00
if (ch_mfc_args.size != 128)
{
break;
}
2015-07-03 01:11:44 +02:00
vm::reservation_op(VM_CAST(ch_mfc_args.ea), 128, [this]()
2015-03-02 03:10:41 +01:00
{
2015-07-03 01:11:44 +02:00
memcpy(vm::priv_ptr(VM_CAST(ch_mfc_args.ea)), vm::get_ptr(offset + ch_mfc_args.lsa), 128);
2015-03-02 03:10:41 +01:00
});
if (cmd == MFC_PUTLLUC_CMD)
{
ch_atomic_stat.push_uncond(MFC_PUTLLUC_SUCCESS);
}
else
{
// tag may be used here
}
return;
2014-08-22 23:15:02 +02:00
}
2015-02-16 02:53:53 +01:00
}
2015-03-02 03:10:41 +01:00
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Unknown command %s (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x)", get_mfc_cmd_name(cmd), cmd, ch_mfc_args.lsa, ch_mfc_args.ea, ch_mfc_args.tag, ch_mfc_args.size);
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
u32 SPUThread::get_ch_count(u32 ch)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(SPU, "get_ch_count(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
}
switch (ch)
{
2015-04-12 03:36:25 +02:00
//case MFC_Cmd: return 16;
2015-03-02 03:10:41 +01:00
//case SPU_WrSRR0: return 1; break;
//case SPU_RdSRR0: return 1; break;
case SPU_WrOutMbox: return ch_out_mbox.get_count() ^ 1; break;
case SPU_WrOutIntrMbox: return ch_out_intr_mbox.get_count() ^ 1; break;
case SPU_RdInMbox: return ch_in_mbox.get_count(); break;
case MFC_RdTagStat: return ch_tag_stat.get_count(); break;
case MFC_RdListStallStat: return ch_stall_stat.get_count(); break;
case MFC_WrTagUpdate: return ch_tag_stat.get_count(); break; // hack
case SPU_RdSigNotify1: return ch_snr1.get_count(); break;
case SPU_RdSigNotify2: return ch_snr2.get_count(); break;
case MFC_RdAtomicStat: return ch_atomic_stat.get_count(); break;
case SPU_RdEventStat: return ch_event_stat.load() & ch_event_mask ? 1 : 0; break;
2015-03-02 03:10:41 +01:00
}
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Unknown/illegal channel (ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
u32 SPUThread::get_ch_value(u32 ch)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(SPU, "get_ch_value(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
}
2014-10-02 12:29:20 +02:00
2015-07-03 18:07:36 +02:00
auto read_channel = [this](spu_channel_t& channel) -> u32
{
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
u32 result;
while (!channel.try_pop(result))
{
CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{};
if (!lock) lock.lock();
cv.wait_for(lock, std::chrono::milliseconds(1));
}
return result;
};
2014-08-22 23:15:02 +02:00
switch (ch)
{
2015-03-02 03:10:41 +01:00
//case SPU_RdSRR0:
// value = SRR0;
// break;
case SPU_RdInMbox:
{
2015-07-03 18:07:36 +02:00
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
2015-03-03 00:34:49 +01:00
u32 result, count;
2015-07-01 00:25:52 +02:00
2015-07-03 18:07:36 +02:00
while (!ch_in_mbox.try_pop(result, count))
2015-03-02 03:10:41 +01:00
{
2015-07-03 18:07:36 +02:00
CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{};
if (!lock) lock.lock();
cv.wait_for(lock, std::chrono::milliseconds(1));
2015-03-02 03:10:41 +01:00
}
2015-03-03 00:34:49 +01:00
if (count + 1 == 4 /* SPU_IN_MBOX_THRESHOLD */) // TODO: check this
{
int2.set(SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT);
}
2015-03-02 03:10:41 +01:00
return result;
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
case MFC_RdTagStat:
{
2015-07-03 18:07:36 +02:00
return read_channel(ch_tag_stat);
2015-03-02 03:10:41 +01:00
}
case MFC_RdTagMask:
{
return ch_tag_mask;
}
case SPU_RdSigNotify1:
{
2015-07-03 18:07:36 +02:00
return read_channel(ch_snr1);
2015-03-02 03:10:41 +01:00
}
case SPU_RdSigNotify2:
{
2015-07-03 18:07:36 +02:00
return read_channel(ch_snr2);
2015-03-02 03:10:41 +01:00
}
case MFC_RdAtomicStat:
{
2015-07-03 18:07:36 +02:00
return read_channel(ch_atomic_stat);
2015-03-02 03:10:41 +01:00
}
case MFC_RdListStallStat:
{
2015-07-03 18:07:36 +02:00
return read_channel(ch_stall_stat);
2015-03-02 03:10:41 +01:00
}
case SPU_RdDec:
{
return ch_dec_value - (u32)(get_time() - ch_dec_start_timestamp);
}
case SPU_RdEventMask:
{
return ch_event_mask;
}
case SPU_RdEventStat:
{
2015-07-04 01:22:24 +02:00
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
2015-03-02 03:10:41 +01:00
u32 result;
2015-07-04 01:22:24 +02:00
while ((result = ch_event_stat.load() & ch_event_mask) == 0)
2015-03-02 03:10:41 +01:00
{
2015-07-04 01:22:24 +02:00
CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{};
if (!lock) lock.lock();
cv.wait_for(lock, std::chrono::milliseconds(1));
2015-03-02 03:10:41 +01:00
}
return result;
}
case SPU_RdMachStat:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
return 1; // hack (not isolated, interrupts enabled)
2014-08-22 23:15:02 +02:00
}
}
2014-10-02 12:29:20 +02:00
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Unknown/illegal channel (ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???");
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
void SPUThread::set_ch_value(u32 ch, u32 value)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(SPU, "set_ch_value(ch=%d [%s], value=0x%x)", ch, ch < 128 ? spu_ch_name[ch] : "???", value);
}
2014-10-02 12:29:20 +02:00
2014-08-22 23:15:02 +02:00
switch (ch)
{
2015-03-02 03:10:41 +01:00
//case SPU_WrSRR0:
// SRR0 = value & 0x3FFFC; //LSLR & ~3
// break;
2014-08-22 23:15:02 +02:00
case SPU_WrOutIntrMbox:
{
2015-03-02 03:10:41 +01:00
if (m_type == CPU_THREAD_RAW_SPU)
2014-08-22 23:15:02 +02:00
{
2015-07-03 18:07:36 +02:00
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
while (!ch_out_intr_mbox.try_push(value))
2014-08-22 23:15:02 +02:00
{
2015-07-03 18:07:36 +02:00
CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{};
if (!lock) lock.lock();
cv.wait_for(lock, std::chrono::milliseconds(1));
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
int2.set(SPU_INT2_STAT_MAILBOX_INT);
2015-03-02 22:09:20 +01:00
return;
2014-08-22 23:15:02 +02:00
}
else
{
2015-03-02 03:10:41 +01:00
const u8 code = value >> 24;
2014-08-22 23:15:02 +02:00
if (code < 64)
{
/* ===== sys_spu_thread_send_event (used by spu_printf) ===== */
u8 spup = code & 63;
u32 data;
2015-07-03 18:07:36 +02:00
if (!ch_out_mbox.try_pop(data))
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_spu_thread_send_event(value=0x%x, spup=%d): Out_MBox is empty", value, spup);
2014-08-22 23:15:02 +02:00
}
if (Ini.HLELogging.GetValue())
{
2015-03-02 03:10:41 +01:00
LOG_NOTICE(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data);
2014-08-22 23:15:02 +02:00
}
2015-03-04 05:42:04 +01:00
LV2_LOCK;
2014-08-22 23:15:02 +02:00
2015-04-12 03:36:25 +02:00
const auto queue = this->spup[spup].lock();
2014-08-22 23:15:02 +02:00
2015-03-04 05:42:04 +01:00
if (!queue)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING(SPU, "sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (value & 0x00ffffff), data);
2015-03-11 16:30:50 +01:00
return ch_in_mbox.push_uncond(CELL_ENOTCONN); // TODO: check error passing
2014-08-22 23:15:02 +02:00
}
2015-03-04 05:42:04 +01:00
if (queue->events.size() >= queue->size)
2014-08-22 23:15:02 +02:00
{
2015-03-11 16:30:50 +01:00
return ch_in_mbox.push_uncond(CELL_EBUSY);
2014-08-22 23:15:02 +02:00
}
2015-04-13 15:32:09 +02:00
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data);
2015-03-11 16:30:50 +01:00
return ch_in_mbox.push_uncond(CELL_OK);
2014-08-22 23:15:02 +02:00
}
else if (code < 128)
{
/* ===== sys_spu_thread_throw_event ===== */
const u8 spup = code & 63;
u32 data;
2015-07-03 18:07:36 +02:00
if (!ch_out_mbox.try_pop(data))
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_spu_thread_throw_event(value=0x%x, spup=%d): Out_MBox is empty", value, spup);
2014-08-22 23:15:02 +02:00
}
if (Ini.HLELogging.GetValue())
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, value & 0x00ffffff, data);
2014-08-22 23:15:02 +02:00
}
2015-03-04 05:42:04 +01:00
LV2_LOCK;
2014-08-22 23:15:02 +02:00
2015-04-12 03:36:25 +02:00
const auto queue = this->spup[spup].lock();
2014-08-22 23:15:02 +02:00
2015-03-04 05:42:04 +01:00
if (!queue)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (value & 0x00ffffff), data);
2014-08-22 23:15:02 +02:00
return;
}
// TODO: check passing spup value
2015-03-04 05:42:04 +01:00
if (queue->events.size() >= queue->size)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING(SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) failed (queue is full)", spup, (value & 0x00ffffff), data);
2014-08-22 23:15:02 +02:00
return;
}
2015-04-13 15:32:09 +02:00
queue->push(lv2_lock, SYS_SPU_THREAD_EVENT_USER_KEY, GetId(), ((u64)spup << 32) | (value & 0x00ffffff), data);
2014-08-22 23:15:02 +02:00
return;
}
else if (code == 128)
{
/* ===== sys_event_flag_set_bit ===== */
2015-03-02 03:10:41 +01:00
u32 flag = value & 0xffffff;
2014-08-22 23:15:02 +02:00
u32 data;
2015-07-03 18:07:36 +02:00
if (!ch_out_mbox.try_pop(data))
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_event_flag_set_bit(value=0x%x (flag=%d)): Out_MBox is empty", value, flag);
2014-08-22 23:15:02 +02:00
}
if (flag > 63)
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_event_flag_set_bit(id=%d, value=0x%x): flag > 63", data, value, flag);
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING(SPU, "sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d))", data, value, flag);
2014-08-22 23:15:02 +02:00
}
2015-03-05 22:29:05 +01:00
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(data);
2015-03-05 22:29:05 +01:00
2015-04-12 03:36:25 +02:00
if (!ef)
2014-08-22 23:15:02 +02:00
{
2015-03-11 16:30:50 +01:00
return ch_in_mbox.push_uncond(CELL_ESRCH);
2014-08-22 23:15:02 +02:00
}
2015-03-11 16:30:50 +01:00
while (ef->cancelled)
2014-08-22 23:15:02 +02:00
{
2015-03-05 22:29:05 +01:00
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2015-03-05 22:29:05 +01:00
ef->flags |= 1ull << flag;
2015-03-11 16:30:50 +01:00
if (ef->waiters)
{
ef->cv.notify_all();
}
return ch_in_mbox.push_uncond(CELL_OK);
2014-08-22 23:15:02 +02:00
}
else if (code == 192)
{
/* ===== sys_event_flag_set_bit_impatient ===== */
2015-03-02 03:10:41 +01:00
u32 flag = value & 0xffffff;
2014-08-22 23:15:02 +02:00
u32 data;
2015-07-03 18:07:36 +02:00
if (!ch_out_mbox.try_pop(data))
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_event_flag_set_bit_impatient(value=0x%x (flag=%d)): Out_MBox is empty", value, flag);
2014-08-22 23:15:02 +02:00
}
if (flag > 63)
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_event_flag_set_bit_impatient(id=%d, value=0x%x): flag > 63", data, value, flag);
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_WARNING(SPU, "sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d))", data, value, flag);
2014-08-22 23:15:02 +02:00
}
2015-03-05 22:29:05 +01:00
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(data);
2015-03-05 22:29:05 +01:00
2015-04-12 03:36:25 +02:00
if (!ef)
2014-08-22 23:15:02 +02:00
{
return;
}
2015-03-11 16:30:50 +01:00
while (ef->cancelled)
2014-08-22 23:15:02 +02:00
{
2015-03-05 22:29:05 +01:00
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2015-03-05 22:29:05 +01:00
ef->flags |= 1ull << flag;
2015-03-11 16:30:50 +01:00
if (ef->waiters)
{
ef->cv.notify_all();
}
2014-08-22 23:15:02 +02:00
return;
}
else
{
2015-03-02 03:10:41 +01:00
if (ch_out_mbox.get_count())
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("SPU_WrOutIntrMbox: unknown data (value=0x%x); Out_MBox = 0x%x", value, ch_out_mbox.get_value());
2014-08-22 23:15:02 +02:00
}
else
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("SPU_WrOutIntrMbox: unknown data (value=0x%x)", value);
2014-08-22 23:15:02 +02:00
}
}
}
}
case SPU_WrOutMbox:
{
2015-07-03 18:07:36 +02:00
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
while (!ch_out_mbox.try_push(value))
2014-12-23 00:31:11 +01:00
{
2015-07-03 18:07:36 +02:00
CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{};
if (!lock) lock.lock();
cv.wait_for(lock, std::chrono::milliseconds(1));
2014-12-23 00:31:11 +01:00
}
2015-03-02 03:10:41 +01:00
return;
2014-08-22 23:15:02 +02:00
}
case MFC_WrTagMask:
{
2015-03-02 03:10:41 +01:00
ch_tag_mask = value;
return;
2014-08-22 23:15:02 +02:00
}
case MFC_WrTagUpdate:
{
2015-03-02 03:10:41 +01:00
ch_tag_stat.push_uncond(ch_tag_mask); // hack
return;
2014-08-22 23:15:02 +02:00
}
case MFC_LSA:
{
2015-03-02 03:10:41 +01:00
if (value >= 0x40000)
{
break;
}
ch_mfc_args.lsa = value;
return;
2014-08-22 23:15:02 +02:00
}
case MFC_EAH:
{
2015-03-02 03:10:41 +01:00
ch_mfc_args.eah = value;
return;
2014-08-22 23:15:02 +02:00
}
case MFC_EAL:
{
2015-03-02 03:10:41 +01:00
ch_mfc_args.eal = value;
return;
2014-08-22 23:15:02 +02:00
}
case MFC_Size:
{
2015-03-02 03:10:41 +01:00
if (value > 16 * 1024)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
break;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
ch_mfc_args.size = (u16)value;
return;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_TagID:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (value >= 32)
2014-12-23 00:31:11 +01:00
{
2015-03-02 03:10:41 +01:00
break;
2014-12-23 00:31:11 +01:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
ch_mfc_args.tag = (u16)value;
return;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_Cmd:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
process_mfc_cmd(value);
ch_mfc_args = {}; // clear non-persistent data
return;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_WrListStallAck:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (value >= 32)
2014-10-02 12:29:20 +02:00
{
2015-03-02 03:10:41 +01:00
break;
2014-10-02 12:29:20 +02:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
size_t processed = 0;
for (size_t i = 0; i < mfc_queue.size(); i++)
2014-10-02 12:29:20 +02:00
{
2015-03-02 03:10:41 +01:00
if (mfc_queue[i].second.tag == value)
2014-12-23 00:31:11 +01:00
{
2015-03-02 03:10:41 +01:00
do_dma_list_cmd(mfc_queue[i].first, mfc_queue[i].second);
2015-03-03 21:09:23 +01:00
mfc_queue[i].second.tag = 0xdead;
2015-03-02 03:10:41 +01:00
processed++;
2014-12-23 00:31:11 +01:00
}
2014-10-02 12:29:20 +02:00
}
2015-03-02 03:10:41 +01:00
while (processed)
2014-10-02 12:29:20 +02:00
{
2015-03-02 03:10:41 +01:00
for (size_t i = 0; i < mfc_queue.size(); i++)
2014-12-23 00:31:11 +01:00
{
2015-03-03 21:09:23 +01:00
if (mfc_queue[i].second.tag == 0xdead)
2015-03-02 03:10:41 +01:00
{
mfc_queue.erase(mfc_queue.begin() + i);
processed--;
break;
}
2014-12-23 00:31:11 +01:00
}
2014-10-02 12:29:20 +02:00
}
2015-03-02 03:10:41 +01:00
return;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrDec:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
ch_dec_start_timestamp = get_time();
ch_dec_value = value;
return;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrEventMask:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
if (value & ~(SPU_EVENT_IMPLEMENTED))
2014-12-23 00:31:11 +01:00
{
2015-03-02 03:10:41 +01:00
break;
2014-12-23 00:31:11 +01:00
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
ch_event_mask = value;
return;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrEventAck:
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
ch_event_stat &= ~value;
return;
2014-08-22 23:15:02 +02:00
}
}
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Unknown/illegal channel (ch=%d [%s], value=0x%x)", ch, ch < 128 ? spu_ch_name[ch] : "???", value);
2015-03-02 03:10:41 +01:00
}
void SPUThread::stop_and_signal(u32 code)
{
if (Ini.HLELogging.GetValue())
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_NOTICE(SPU, "stop_and_signal(code=0x%x)", code);
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
if (m_type == CPU_THREAD_RAW_SPU)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
status.atomic_op([code](u32& status)
{
status = (status & 0xffff) | (code << 16);
status |= SPU_STATUS_STOPPED_BY_STOP;
status &= ~SPU_STATUS_RUNNING;
});
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
int2.set(SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT);
2015-07-01 00:25:52 +02:00
return Stop();
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
switch (code)
{
2014-10-02 12:29:20 +02:00
case 0x001:
{
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
2015-03-02 03:10:41 +01:00
return;
2014-10-02 12:29:20 +02:00
}
case 0x002:
{
2015-07-01 19:09:26 +02:00
m_state |= CPU_STATE_RETURN;
return;
2014-10-02 12:29:20 +02:00
}
case 0x003:
{
auto iter = m_addr_to_hle_function_map.find(PC);
assert(iter != m_addr_to_hle_function_map.end());
auto return_to_caller = iter->second(*this);
if (return_to_caller)
{
2015-07-01 00:25:52 +02:00
PC = (GPR[0]._u32[3] & 0x3fffc) - 4;
}
2015-03-02 03:10:41 +01:00
return;
}
2014-08-27 23:04:55 +02:00
case 0x110:
2014-08-22 23:15:02 +02:00
{
2014-08-27 23:04:55 +02:00
/* ===== sys_spu_thread_receive_event ===== */
2014-08-22 23:15:02 +02:00
u32 spuq = 0;
2015-07-03 18:07:36 +02:00
if (!ch_out_mbox.try_pop(spuq))
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_spu_thread_receive_event(): cannot read Out_MBox");
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
if (ch_in_mbox.get_count())
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_ERROR(SPU, "sys_spu_thread_receive_event(spuq=0x%x): In_MBox is not empty", spuq);
2015-03-11 16:30:50 +01:00
return ch_in_mbox.push_uncond(CELL_EBUSY);
2014-08-22 23:15:02 +02:00
}
if (Ini.HLELogging.GetValue())
{
2015-03-02 03:10:41 +01:00
LOG_NOTICE(SPU, "sys_spu_thread_receive_event(spuq=0x%x)", spuq);
2014-08-22 23:15:02 +02:00
}
2015-03-04 05:42:04 +01:00
LV2_LOCK;
2014-08-22 23:15:02 +02:00
2015-07-03 18:07:36 +02:00
const auto group = tg.lock();
if (!group)
{
throw EXCEPTION("Invalid SPU Thread Group");
}
if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate
{
return ch_in_mbox.push_uncond(CELL_EINVAL);
}
std::shared_ptr<lv2_event_queue_t> queue;
for (auto& v : this->spuq)
{
if (spuq == v.first)
{
queue = v.second.lock();
if (queue)
{
break;
}
}
}
if (!queue)
2014-08-22 23:15:02 +02:00
{
2015-03-11 16:30:50 +01:00
return ch_in_mbox.push_uncond(CELL_EINVAL); // TODO: check error value
2015-03-04 05:42:04 +01:00
}
2014-12-23 00:31:11 +01:00
2015-07-03 18:07:36 +02:00
// check thread group status
while (group->state >= SPU_THREAD_GROUP_STATUS_WAITING && group->state <= SPU_THREAD_GROUP_STATUS_SUSPENDED)
{
CHECK_EMU_STATUS;
if (IsStopped()) throw CPUThreadStop{};
group->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
2014-08-22 23:15:02 +02:00
2015-07-03 18:07:36 +02:00
// change group status
if (group->state == SPU_THREAD_GROUP_STATUS_RUNNING)
2015-03-04 05:42:04 +01:00
{
2015-07-03 18:07:36 +02:00
group->state = SPU_THREAD_GROUP_STATUS_WAITING;
for (auto& t : group->threads)
2014-12-23 00:31:11 +01:00
{
2015-07-03 18:07:36 +02:00
if (t) t->Sleep(); // trigger status check
2014-12-23 00:31:11 +01:00
}
2015-07-03 18:07:36 +02:00
}
else
{
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
}
// protocol is ignored in current implementation
queue->waiters++;
2014-12-23 00:31:11 +01:00
2015-07-03 18:07:36 +02:00
// wait on the event queue
while (queue->events.empty() && !queue->cancelled)
{
2015-07-01 00:25:52 +02:00
CHECK_EMU_STATUS;
2015-07-03 18:07:36 +02:00
if (IsStopped()) throw CPUThreadStop{};
2015-03-04 05:42:04 +01:00
queue->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
2014-08-22 23:15:02 +02:00
}
2015-03-04 05:42:04 +01:00
2015-07-03 18:07:36 +02:00
if (queue->cancelled)
{
ch_in_mbox.push_uncond(CELL_ECANCELED);
}
else
{
auto& event = queue->events.front();
ch_in_mbox.push_uncond(CELL_OK);
ch_in_mbox.push_uncond((u32)event.data1);
ch_in_mbox.push_uncond((u32)event.data2);
ch_in_mbox.push_uncond((u32)event.data3);
queue->events.pop_front();
queue->waiters--;
if (queue->events.size())
{
queue->cv.notify_one();
}
}
2015-03-02 03:10:41 +01:00
2015-07-03 18:07:36 +02:00
// restore thread group status
if (group->state == SPU_THREAD_GROUP_STATUS_WAITING)
{
group->state = SPU_THREAD_GROUP_STATUS_RUNNING;
}
else if (group->state == SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED)
{
group->state = SPU_THREAD_GROUP_STATUS_SUSPENDED;
}
else
{
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
}
2015-03-05 22:29:05 +01:00
2015-07-03 18:07:36 +02:00
for (auto& t : group->threads)
2015-03-05 22:29:05 +01:00
{
2015-07-03 18:07:36 +02:00
if (t) t->Awake(); // untrigger status check
2015-03-05 22:29:05 +01:00
}
2015-07-03 18:07:36 +02:00
group->cv.notify_all();
2015-03-02 03:10:41 +01:00
return;
2014-08-22 23:15:02 +02:00
}
2014-08-27 23:04:55 +02:00
case 0x101:
{
/* ===== sys_spu_thread_group_exit ===== */
2015-03-02 03:10:41 +01:00
u32 value;
2015-07-03 18:07:36 +02:00
if (!ch_out_mbox.try_pop(value))
2014-08-27 23:04:55 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_spu_thread_group_exit(): cannot read Out_MBox");
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
2015-03-04 22:51:14 +01:00
if (Ini.HLELogging.GetValue())
2014-08-27 23:04:55 +02:00
{
2015-03-04 22:51:14 +01:00
LOG_NOTICE(SPU, "sys_spu_thread_group_exit(status=0x%x)", value);
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
2015-03-04 22:51:14 +01:00
LV2_LOCK;
2015-04-12 03:36:25 +02:00
const auto group = tg.lock();
2015-03-06 23:10:04 +01:00
if (!group)
2014-08-27 23:04:55 +02:00
{
2015-07-03 18:07:36 +02:00
throw EXCEPTION("Invalid SPU Thread Group");
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
2015-03-04 22:51:14 +01:00
for (auto t : group->threads)
2014-08-27 23:04:55 +02:00
{
2015-07-01 00:25:52 +02:00
if (t && t.get() != this)
2014-08-27 23:04:55 +02:00
{
2015-07-01 00:25:52 +02:00
t->Stop();
2014-08-27 23:04:55 +02:00
}
}
2015-03-04 22:51:14 +01:00
group->state = SPU_THREAD_GROUP_STATUS_INITIALIZED;
group->exit_status = value;
group->join_state |= SPU_TGJSF_GROUP_EXIT;
2015-07-03 18:07:36 +02:00
group->cv.notify_one();
2015-07-01 00:25:52 +02:00
return Stop();
2014-08-27 23:04:55 +02:00
}
2014-08-22 23:15:02 +02:00
case 0x102:
2014-08-27 23:04:55 +02:00
{
/* ===== sys_spu_thread_exit ===== */
2015-03-02 03:10:41 +01:00
if (!ch_out_mbox.get_count())
2014-08-22 23:15:02 +02:00
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("sys_spu_thread_exit(): Out_MBox is empty");
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
if (Ini.HLELogging.GetValue())
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
LOG_NOTICE(SPU, "sys_spu_thread_exit(status=0x%x)", ch_out_mbox.get_value());
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2015-03-04 22:51:14 +01:00
LV2_LOCK;
2015-07-03 18:07:36 +02:00
const auto group = tg.lock();
if (!group)
{
throw EXCEPTION("Invalid SPU Thread Group");
}
2015-03-02 03:10:41 +01:00
status |= SPU_STATUS_STOPPED_BY_STOP;
2015-07-03 18:07:36 +02:00
group->cv.notify_one();
2015-07-01 00:25:52 +02:00
return Stop();
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
}
2014-08-27 23:04:55 +02:00
2015-03-02 03:10:41 +01:00
if (!ch_out_mbox.get_count())
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Unknown STOP code: 0x%x", code);
2015-03-02 03:10:41 +01:00
}
else
{
2015-07-01 19:09:26 +02:00
throw EXCEPTION("Unknown STOP code: 0x%x; Out_MBox=0x%x", code, ch_out_mbox.get_value());
2015-03-02 03:10:41 +01:00
}
}
void SPUThread::halt()
{
if (Ini.HLELogging.GetValue())
{
LOG_NOTICE(SPU, "halt()");
2015-03-02 03:10:41 +01:00
}
if (m_type == CPU_THREAD_RAW_SPU)
{
status.atomic_op([](u32& status)
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
status |= SPU_STATUS_STOPPED_BY_HALT;
status &= ~SPU_STATUS_RUNNING;
});
int2.set(SPU_INT2_STAT_SPU_HALT_OR_STEP_INT);
2015-07-01 00:25:52 +02:00
return Stop();
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
status |= SPU_STATUS_STOPPED_BY_HALT;
throw EXCEPTION("Halt");
2014-10-24 15:24:09 +02:00
}
spu_thread::spu_thread(u32 entry, const std::string& name, u32 stack_size, u32 prio)
{
2015-07-03 18:07:36 +02:00
auto spu = Emu.GetIdManager().make_ptr<SPUThread>(name, 0x13370666);
2015-07-01 00:25:52 +02:00
spu->PC = entry;
2015-07-01 00:25:52 +02:00
thread = std::move(spu);
2015-01-18 19:18:03 +01:00
}