2012-11-15 00:39:56 +01:00
|
|
|
#include "stdafx.h"
|
2014-06-02 19:27:24 +02:00
|
|
|
#include "Emu/Memory/Memory.h"
|
|
|
|
|
#include "Emu/System.h"
|
2015-07-01 00:25:52 +02:00
|
|
|
#include "Emu/IdManager.h"
|
2016-04-14 01:09:41 +02:00
|
|
|
#include "PPUThread.h"
|
|
|
|
|
#include "PPUInterpreter.h"
|
|
|
|
|
#include "PPUModule.h"
|
2015-03-16 22:38:21 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
enum class ppu_decoder_type
|
2015-03-16 22:38:21 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
precise,
|
|
|
|
|
fast,
|
|
|
|
|
llvm,
|
|
|
|
|
};
|
2015-03-16 22:38:21 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder", 1,
|
2015-03-16 22:38:21 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
{ "Interpreter (precise)", ppu_decoder_type::precise },
|
|
|
|
|
{ "Interpreter (fast)", ppu_decoder_type::fast },
|
|
|
|
|
{ "Recompiler (LLVM)", ppu_decoder_type::llvm },
|
|
|
|
|
});
|
2015-03-16 22:38:21 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
const ppu_decoder<ppu_interpreter_precise> s_ppu_interpreter_precise;
|
|
|
|
|
const ppu_decoder<ppu_interpreter_fast> s_ppu_interpreter_fast;
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2015-11-26 09:06:29 +01:00
|
|
|
std::string PPUThread::get_name() const
|
|
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return fmt::format("PPU[0x%x] Thread (%s)", id, name);
|
2015-11-26 09:06:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
std::string PPUThread::dump() const
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
std::string ret = "Registers:\n=========\n";
|
2015-09-14 18:32:35 +02:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
for (uint i = 0; i<32; ++i) ret += fmt::format("GPR[%d] = 0x%llx\n", i, GPR[i]);
|
|
|
|
|
for (uint i = 0; i<32; ++i) ret += fmt::format("FPR[%d] = %.6G\n", i, FPR[i]);
|
|
|
|
|
for (uint i = 0; i<32; ++i) ret += fmt::format("VR[%d] = 0x%s [%s]\n", i, VR[i].to_hex().c_str(), VR[i].to_xyzw().c_str());
|
|
|
|
|
ret += fmt::format("CR = 0x%08x\n", GetCR());
|
|
|
|
|
ret += fmt::format("LR = 0x%llx\n", LR);
|
|
|
|
|
ret += fmt::format("CTR = 0x%llx\n", CTR);
|
|
|
|
|
ret += fmt::format("XER = [CA=%u | OV=%u | SO=%u | CNT=%u]\n", u32{ CA }, u32{ OV }, u32{ SO }, u32{ XCNT });
|
|
|
|
|
//ret += fmt::format("FPSCR = 0x%x "
|
|
|
|
|
// "[RN=%d | NI=%d | XE=%d | ZE=%d | UE=%d | OE=%d | VE=%d | "
|
|
|
|
|
// "VXCVI=%d | VXSQRT=%d | VXSOFT=%d | FPRF=%d | "
|
|
|
|
|
// "FI=%d | FR=%d | VXVC=%d | VXIMZ=%d | "
|
|
|
|
|
// "VXZDZ=%d | VXIDI=%d | VXISI=%d | VXSNAN=%d | "
|
|
|
|
|
// "XX=%d | ZX=%d | UX=%d | OX=%d | VX=%d | FEX=%d | FX=%d]\n",
|
|
|
|
|
// FPSCR.FPSCR,
|
|
|
|
|
// u32{ FPSCR.RN },
|
|
|
|
|
// u32{ FPSCR.NI }, u32{ FPSCR.XE }, u32{ FPSCR.ZE }, u32{ FPSCR.UE }, u32{ FPSCR.OE }, u32{ FPSCR.VE },
|
|
|
|
|
// u32{ FPSCR.VXCVI }, u32{ FPSCR.VXSQRT }, u32{ FPSCR.VXSOFT }, u32{ FPSCR.FPRF },
|
|
|
|
|
// u32{ FPSCR.FI }, u32{ FPSCR.FR }, u32{ FPSCR.VXVC }, u32{ FPSCR.VXIMZ },
|
|
|
|
|
// u32{ FPSCR.VXZDZ }, u32{ FPSCR.VXIDI }, u32{ FPSCR.VXISI }, u32{ FPSCR.VXSNAN },
|
|
|
|
|
// u32{ FPSCR.XX }, u32{ FPSCR.ZX }, u32{ FPSCR.UX }, u32{ FPSCR.OX }, u32{ FPSCR.VX }, u32{ FPSCR.FEX }, u32{ FPSCR.FX });
|
2015-07-01 00:25:52 +02:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
return ret;
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
void PPUThread::cpu_init()
|
2015-02-01 14:52:34 +01:00
|
|
|
{
|
2015-07-01 00:25:52 +02:00
|
|
|
if (!stack_addr)
|
2015-02-01 14:52:34 +01:00
|
|
|
{
|
2015-07-01 00:25:52 +02:00
|
|
|
if (!stack_size)
|
|
|
|
|
{
|
|
|
|
|
throw EXCEPTION("Invalid stack size");
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-11 22:44:53 +02:00
|
|
|
stack_addr = vm::alloc(stack_size, vm::stack);
|
2015-07-01 00:25:52 +02:00
|
|
|
|
|
|
|
|
if (!stack_addr)
|
|
|
|
|
{
|
|
|
|
|
throw EXCEPTION("Out of stack memory");
|
|
|
|
|
}
|
2015-02-01 14:52:34 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
GPR[1] = align(stack_addr + stack_size, 0x200) - 0x200;
|
2015-08-10 21:39:52 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-27 00:27:24 +02:00
|
|
|
extern thread_local std::string(*g_tls_log_prefix)();
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
void PPUThread::cpu_task()
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
//SetHostRoundingMode(FPSCR_RN_NEAR);
|
2015-03-16 19:44:49 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
if (custom_task)
|
2014-04-23 13:59:14 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
if (check_status()) return;
|
2015-03-16 19:44:49 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
return custom_task(*this);
|
2014-04-23 13:59:14 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-27 00:27:24 +02:00
|
|
|
g_tls_log_prefix = []
|
2015-07-01 00:25:52 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
const auto cpu = static_cast<PPUThread*>(get_current_cpu_thread());
|
2014-08-31 13:10:33 +02:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
|
|
|
|
|
};
|
2014-11-19 15:16:30 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
const auto base = vm::_ptr<const u8>(0);
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
// Select opcode table
|
|
|
|
|
const auto& table = *(
|
|
|
|
|
g_cfg_ppu_decoder.get() == ppu_decoder_type::precise ? &s_ppu_interpreter_precise.get_table() :
|
|
|
|
|
g_cfg_ppu_decoder.get() == ppu_decoder_type::fast ? &s_ppu_interpreter_fast.get_table() :
|
|
|
|
|
throw std::logic_error("Invalid PPU decoder"));
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
u32 _pc{};
|
|
|
|
|
u32 op0, op1, op2;
|
|
|
|
|
ppu_inter_func_t func0, func1, func2;
|
2012-11-15 00:39:56 +01:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
while (true)
|
|
|
|
|
{
|
2016-04-25 12:49:12 +02:00
|
|
|
if (LIKELY(_pc == PC && !state.load()))
|
2016-04-14 01:09:41 +02:00
|
|
|
{
|
|
|
|
|
func0(*this, { op0 });
|
2016-04-25 12:49:12 +02:00
|
|
|
|
|
|
|
|
if (LIKELY((_pc += 4) == (PC += 4) && !state.load()))
|
2016-04-14 01:09:41 +02:00
|
|
|
{
|
|
|
|
|
func1(*this, { op1 });
|
|
|
|
|
|
2016-04-25 12:49:12 +02:00
|
|
|
if (LIKELY((_pc += 4) == (PC += 4)))
|
2016-04-14 01:09:41 +02:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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])];
|
|
|
|
|
|
2016-04-25 12:49:12 +02:00
|
|
|
if (UNLIKELY(check_status())) return;
|
2016-04-14 01:09:41 +02:00
|
|
|
}
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
bool PPUThread::handle_interrupt()
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
return false;
|
2012-11-15 00:39:56 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
PPUThread::~PPUThread()
|
2012-11-15 00:39:56 +01:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
if (stack_addr)
|
|
|
|
|
{
|
|
|
|
|
vm::dealloc_verbose_nothrow(stack_addr, vm::stack);
|
|
|
|
|
}
|
2014-04-10 00:54:32 +02:00
|
|
|
}
|
2014-08-15 14:50:59 +02:00
|
|
|
|
2016-04-25 12:49:12 +02:00
|
|
|
PPUThread::PPUThread(const std::string& name)
|
|
|
|
|
: cpu_thread(cpu_type::ppu, name)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
be_t<u64>* PPUThread::get_stack_arg(s32 i, u64 align)
|
2014-08-23 16:51:51 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
if (align != 1 && align != 2 && align != 4 && align != 8 && align != 16) throw fmt::exception("Unsupported alignment: 0x%llx" HERE, align);
|
|
|
|
|
return vm::_ptr<u64>(vm::cast((GPR[1] + 0x30 + 0x8 * (i - 1)) & (0 - align), HERE));
|
2014-08-23 16:51:51 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-19 13:36:32 +02:00
|
|
|
void PPUThread::fast_call(u32 addr, u32 rtoc)
|
2014-08-19 20:17:20 +02:00
|
|
|
{
|
|
|
|
|
auto old_PC = PC;
|
2015-04-18 02:25:26 +02:00
|
|
|
auto old_stack = GPR[1];
|
2014-08-19 20:17:20 +02:00
|
|
|
auto old_rtoc = GPR[2];
|
2014-08-20 16:23:48 +02:00
|
|
|
auto old_LR = LR;
|
2015-07-19 13:36:32 +02:00
|
|
|
auto old_task = std::move(custom_task);
|
|
|
|
|
|
2014-08-19 20:17:20 +02:00
|
|
|
PC = addr;
|
|
|
|
|
GPR[2] = rtoc;
|
2014-11-30 23:04:47 +01:00
|
|
|
LR = Emu.GetCPUThreadStop();
|
2015-07-19 13:36:32 +02:00
|
|
|
custom_task = nullptr;
|
2014-08-19 20:17:20 +02:00
|
|
|
|
2015-07-01 00:25:52 +02:00
|
|
|
try
|
|
|
|
|
{
|
2015-11-26 09:06:29 +01:00
|
|
|
cpu_task();
|
2015-07-01 00:25:52 +02:00
|
|
|
}
|
2016-04-14 01:09:41 +02:00
|
|
|
catch (cpu_state _s)
|
2015-07-01 00:25:52 +02:00
|
|
|
{
|
2016-04-14 01:09:41 +02:00
|
|
|
state += _s;
|
|
|
|
|
if (_s != cpu_state::ret) throw;
|
2015-07-01 00:25:52 +02:00
|
|
|
}
|
2014-08-19 20:17:20 +02:00
|
|
|
|
2016-04-14 01:09:41 +02:00
|
|
|
state -= cpu_state::ret;
|
2015-07-01 19:09:26 +02:00
|
|
|
|
2014-08-20 16:23:48 +02:00
|
|
|
PC = old_PC;
|
2015-04-18 03:35:58 +02:00
|
|
|
|
2015-07-01 00:25:52 +02:00
|
|
|
if (GPR[1] != old_stack) // GPR[1] shouldn't change
|
2015-04-18 03:35:58 +02:00
|
|
|
{
|
2015-07-01 00:25:52 +02:00
|
|
|
throw EXCEPTION("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, GPR[1], old_stack);
|
2015-04-18 03:35:58 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-19 20:17:20 +02:00
|
|
|
GPR[2] = old_rtoc;
|
|
|
|
|
LR = old_LR;
|
2015-07-19 13:36:32 +02:00
|
|
|
custom_task = std::move(old_task);
|
2014-08-19 20:17:20 +02:00
|
|
|
}
|