rpcsx/rpcs3/Emu/ARMv7/ARMv7Thread.cpp

223 lines
4.1 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "ARMv7Thread.h"
2016-02-01 22:53:16 +01:00
#include "ARMv7Opcodes.h"
#include "ARMv7Interpreter.h"
2016-02-01 22:53:16 +01:00
namespace vm { using namespace psv; }
const arm_decoder<arm_interpreter> s_arm_interpreter;
2015-01-31 17:44:26 +01:00
#define TLS_MAX 128
u32 g_armv7_tls_start;
2016-02-01 22:53:16 +01:00
std::array<atomic_t<u32>, TLS_MAX> g_armv7_tls_owners;
2015-01-31 17:44:26 +01:00
void armv7_init_tls()
{
2015-07-11 22:44:53 +02:00
g_armv7_tls_start = Emu.GetTLSMemsz() ? vm::alloc(Emu.GetTLSMemsz() * TLS_MAX, vm::main) : 0;
2015-01-31 17:44:26 +01:00
for (auto& v : g_armv7_tls_owners)
{
v = 0;
2015-01-31 17:44:26 +01:00
}
}
u32 armv7_get_tls(u32 thread)
{
2015-02-01 08:09:24 +01:00
if (!Emu.GetTLSMemsz() || !thread)
2015-01-31 17:44:26 +01:00
{
return 0;
}
for (u32 i = 0; i < TLS_MAX; i++)
{
if (g_armv7_tls_owners[i] == thread)
{
return g_armv7_tls_start + i * Emu.GetTLSMemsz(); // if already initialized, return TLS address
}
}
for (u32 i = 0; i < TLS_MAX; i++)
{
2016-02-01 22:53:16 +01:00
if (g_armv7_tls_owners[i].compare_and_swap_test(0, thread))
2015-01-31 17:44:26 +01:00
{
const u32 addr = g_armv7_tls_start + i * Emu.GetTLSMemsz(); // get TLS address
std::memcpy(vm::base(addr), vm::base(Emu.GetTLSAddr()), Emu.GetTLSFilesz()); // initialize from TLS image
std::memset(vm::base(addr + Emu.GetTLSFilesz()), 0, Emu.GetTLSMemsz() - Emu.GetTLSFilesz()); // fill the rest with zeros
2015-01-31 17:44:26 +01:00
return addr;
}
}
throw EXCEPTION("Out of TLS memory");
2015-01-31 17:44:26 +01:00
}
void armv7_free_tls(u32 thread)
{
if (!Emu.GetTLSMemsz())
{
return;
}
for (auto& v : g_armv7_tls_owners)
{
2016-02-01 22:53:16 +01:00
if (v.compare_and_swap_test(thread, 0))
2015-01-31 17:44:26 +01:00
{
return;
}
}
}
2015-11-26 09:06:29 +01:00
std::string ARMv7Thread::get_name() const
{
2016-02-01 22:53:16 +01:00
return fmt::format("ARMv7[0x%x] Thread (%s)", id, name);
2015-11-26 09:06:29 +01:00
}
2016-02-01 22:53:16 +01:00
std::string ARMv7Thread::dump() const
2015-07-01 00:25:52 +02:00
{
2016-02-01 22:53:16 +01:00
std::string result = "Registers:\n=========\n";
for(int i=0; i<15; ++i)
2015-07-01 00:25:52 +02:00
{
2016-02-01 22:53:16 +01:00
result += fmt::format("r%u\t= 0x%08x\n", i, GPR[i]);
2015-07-01 00:25:52 +02:00
}
2016-02-01 22:53:16 +01:00
result += fmt::format("APSR\t= 0x%08x [N: %d, Z: %d, C: %d, V: %d, Q: %d]\n",
APSR.APSR,
u32{ APSR.N },
u32{ APSR.Z },
u32{ APSR.C },
u32{ APSR.V },
u32{ APSR.Q });
return result;
}
2016-02-01 22:53:16 +01:00
void ARMv7Thread::cpu_init()
{
2015-07-01 00:25:52 +02:00
if (!stack_addr)
2013-11-06 02:01:15 +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::main);
2015-07-01 00:25:52 +02:00
if (!stack_addr)
{
throw EXCEPTION("Out of stack memory");
}
}
2016-02-01 22:53:16 +01:00
memset(GPR, 0, sizeof(GPR));
APSR.APSR = 0;
IPSR.IPSR = 0;
ISET = PC & 1 ? Thumb : ARM; // select instruction set
PC = PC & ~1; // and fix PC
ITSTATE.IT = 0;
SP = stack_addr + stack_size;
TLS = armv7_get_tls(id);
}
2016-02-01 22:53:16 +01:00
void ARMv7Thread::cpu_task()
{
2016-02-01 22:53:16 +01:00
if (custom_task)
2013-11-06 02:01:15 +01:00
{
2016-02-01 22:53:16 +01:00
if (check_status()) return;
2016-02-01 22:53:16 +01:00
return custom_task(*this);
}
2016-02-01 22:53:16 +01:00
_log::g_tls_make_prefix = [](const auto&, auto, const auto&)
{
const auto cpu = static_cast<ARMv7Thread*>(get_current_cpu_thread());
2016-02-01 22:53:16 +01:00
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
};
2015-03-16 19:44:49 +01:00
2016-02-01 22:53:16 +01:00
while (!state.load() || !check_status())
{
2016-02-01 22:53:16 +01:00
if (ISET == Thumb)
{
const u16 op16 = vm::read16(PC);
const u32 cond = ITSTATE.advance();
if (const auto func16 = s_arm_interpreter.decode_thumb(op16))
{
func16(*this, op16, cond);
PC += 2;
}
else
{
const u32 op32 = (op16 << 16) | vm::read16(PC + 2);
s_arm_interpreter.decode_thumb(op32)(*this, op32, cond);
PC += 4;
}
}
else if (ISET == ARM)
{
const u32 op = vm::read32(PC);
s_arm_interpreter.decode_arm(op)(*this, op, op >> 28);
PC += 4;
}
else
{
throw fmt::exception("Invalid instruction set" HERE);
}
}
}
2016-02-01 22:53:16 +01:00
ARMv7Thread::~ARMv7Thread()
{
2016-02-01 22:53:16 +01:00
armv7_free_tls(id);
2016-02-01 22:53:16 +01:00
if (stack_addr)
2015-07-01 00:25:52 +02:00
{
2016-02-01 22:53:16 +01:00
vm::dealloc_verbose_nothrow(stack_addr, vm::main);
2015-07-01 00:25:52 +02:00
}
}
ARMv7Thread::ARMv7Thread(const std::string& name)
: cpu_thread(cpu_type::arm, name)
{
}
2015-07-01 00:25:52 +02:00
void ARMv7Thread::fast_call(u32 addr)
{
auto old_PC = PC;
auto old_stack = SP;
auto old_LR = LR;
2015-07-19 13:36:32 +02:00
auto old_task = std::move(custom_task);
PC = addr;
LR = Emu.GetCPUThreadStop();
2015-07-19 13:36:32 +02:00
custom_task = nullptr;
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-02-01 22:53:16 +01:00
catch (cpu_state _s)
2015-07-01 00:25:52 +02:00
{
2016-02-01 22:53:16 +01:00
state += _s;
if (_s != cpu_state::ret) throw;
2015-07-01 00:25:52 +02:00
}
2016-02-01 22:53:16 +01:00
state -= cpu_state::ret;
2015-07-01 19:09:26 +02:00
PC = old_PC;
2015-07-01 00:25:52 +02:00
if (SP != old_stack) // SP shouldn't change
{
throw EXCEPTION("Stack inconsistency (addr=0x%x, SP=0x%x, old=0x%x)", addr, SP, old_stack);
}
LR = old_LR;
2015-07-19 13:36:32 +02:00
custom_task = std::move(old_task);
}