This commit is contained in:
Nekotekina 2016-05-13 16:55:34 +03:00
parent 38c444cfa1
commit e2d82394f6
156 changed files with 2228 additions and 1616 deletions

View file

@ -1,4 +1,5 @@
#include "stdafx.h"
#include "Utilities/Config.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
@ -93,7 +94,7 @@ void PPUThread::cpu_task()
{
const auto cpu = static_cast<PPUThread*>(get_current_cpu_thread());
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->pc);
};
const auto base = vm::_ptr<const u8>(0);
@ -104,45 +105,135 @@ void PPUThread::cpu_task()
g_cfg_ppu_decoder.get() == ppu_decoder_type::fast ? &s_ppu_interpreter_fast.get_table() :
throw std::logic_error("Invalid PPU decoder"));
u32 _pc{};
u32 op0, op1, op2;
ppu_inter_func_t func0, func1, func2;
v128 _op;
decltype(&ppu_interpreter::UNK) func0, func1, func2, func3;
while (true)
{
if (LIKELY(_pc == PC && !state.load()))
if (UNLIKELY(state.load()))
{
func0(*this, { op0 });
if (LIKELY((_pc += 4) == (PC += 4) && !state.load()))
{
func1(*this, { op1 });
if (LIKELY((_pc += 4) == (PC += 4)))
{
op0 = op2;
func0 = func2;
const auto ops = reinterpret_cast<const be_t<u32>*>(base + _pc);
func1 = table[ppu_decode(op1 = ops[1])];
func2 = table[ppu_decode(op2 = ops[2])];
continue;
}
}
if (check_status()) return;
}
// Reinitialize
_pc = PC;
const auto ops = reinterpret_cast<const be_t<u32>*>(base + _pc);
func0 = table[ppu_decode(op0 = ops[0])];
func1 = table[ppu_decode(op1 = ops[1])];
func2 = table[ppu_decode(op2 = ops[2])];
{
const auto _ops = _mm_shuffle_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(base + pc)), _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
_op.vi = _ops;
const v128 _i = v128::fromV(_mm_and_si128(_mm_or_si128(_mm_slli_epi32(_op.vi, 6), _mm_srli_epi32(_op.vi, 26)), _mm_set1_epi32(0x1ffff)));
func0 = table[_i._u32[0]];
func1 = table[_i._u32[1]];
func2 = table[_i._u32[2]];
func3 = table[_i._u32[3]];
}
if (UNLIKELY(check_status())) return;
while (LIKELY(func0(*this, { _op._u32[0] })))
{
if (pc += 4, LIKELY(func1(*this, { _op._u32[1] })))
{
if (pc += 4, LIKELY(func2(*this, { _op._u32[2] })))
{
pc += 4;
func0 = func3;
const auto _ops = _mm_shuffle_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(base + pc + 4)), _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
_op.vi = _mm_alignr_epi8(_ops, _op.vi, 12);
const v128 _i = v128::fromV(_mm_and_si128(_mm_or_si128(_mm_slli_epi32(_op.vi, 6), _mm_srli_epi32(_op.vi, 26)), _mm_set1_epi32(0x1ffff)));
func1 = table[_i._u32[1]];
func2 = table[_i._u32[2]];
func3 = table[_i._u32[3]];
if (UNLIKELY(state.load()))
{
break;
}
continue;
}
break;
}
break;
}
}
}
constexpr auto stop_state = make_bitset(cpu_state::stop, cpu_state::exit, cpu_state::suspend);
atomic_t<u32> g_ppu_core[2]{};
bool PPUThread::handle_interrupt()
{
// Reschedule and wake up a new thread, possibly this one as well.
return false;
// Check virtual core allocation
if (g_ppu_core[0] != id && g_ppu_core[1] != id)
{
auto cpu0 = idm::get<PPUThread>(g_ppu_core[0]);
auto cpu1 = idm::get<PPUThread>(g_ppu_core[1]);
if (cpu0 && cpu1)
{
if (cpu1->prio > cpu0->prio)
{
cpu0 = std::move(cpu1);
}
// Preempt thread with the lowest priority
if (prio < cpu0->prio)
{
cpu0->state += cpu_state::interrupt;
}
}
else
{
// Try to obtain a virtual core in optimistic way
if (g_ppu_core[0].compare_and_swap_test(0, id) || g_ppu_core[1].compare_and_swap_test(0, id))
{
state -= cpu_state::interrupt;
return true;
}
}
return false;
}
// Select appropriate thread
u32 top_prio = -1;
u32 selected = -1;
idm::select<PPUThread>([&](u32 id, PPUThread& ppu)
{
// Exclude suspended and low-priority threads
if (!ppu.state.test(stop_state) && ppu.prio < top_prio /*&& (!ppu.is_sleep() || ppu.state & cpu_state::signal)*/)
{
top_prio = ppu.prio;
selected = id;
}
});
// If current thread selected
if (selected == id)
{
state -= cpu_state::interrupt;
VERIFY(g_ppu_core[0] == id || g_ppu_core[1] == id);
return true;
}
// If another thread selected
const auto thread = idm::get<PPUThread>(selected);
// Lend virtual core to another thread
if (thread && thread->state.test_and_reset(cpu_state::interrupt))
{
g_ppu_core[0].compare_and_swap(id, thread->id);
g_ppu_core[1].compare_and_swap(id, thread->id);
(*thread)->lock_notify();
}
else
{
g_ppu_core[0].compare_and_swap(id, 0);
g_ppu_core[1].compare_and_swap(id, 0);
}
return false;
}
@ -167,13 +258,13 @@ be_t<u64>* PPUThread::get_stack_arg(s32 i, u64 align)
void PPUThread::fast_call(u32 addr, u32 rtoc)
{
auto old_PC = PC;
auto old_PC = pc;
auto old_stack = GPR[1];
auto old_rtoc = GPR[2];
auto old_LR = LR;
auto old_task = std::move(custom_task);
PC = addr;
pc = addr;
GPR[2] = rtoc;
LR = Emu.GetCPUThreadStop();
custom_task = nullptr;
@ -190,7 +281,7 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
state -= cpu_state::ret;
PC = old_PC;
pc = old_PC;
if (GPR[1] != old_stack) // GPR[1] shouldn't change
{
@ -200,4 +291,10 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
GPR[2] = old_rtoc;
LR = old_LR;
custom_task = std::move(old_task);
//if (custom_task)
//{
// state += cpu_state::interrupt;
// handle_interrupt();
//}
}