Merge branch 'fuzz'
This commit is contained in:
commit
a77ca35ec3
28 changed files with 232 additions and 14670 deletions
|
@ -31,19 +31,18 @@
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
#include "rand_int.h"
|
#include "rand_int.h"
|
||||||
#include "testenv.h"
|
#include "testenv.h"
|
||||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
#include "unicorn_emu/a32_unicorn.h"
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
|
|
||||||
using Dynarmic::Common::Bits;
|
using Dynarmic::Common::Bits;
|
||||||
|
|
||||||
static Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
|
namespace {
|
||||||
|
Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
|
||||||
Dynarmic::A32::UserConfig user_config;
|
Dynarmic::A32::UserConfig user_config;
|
||||||
user_config.enable_fast_dispatch = false;
|
user_config.enable_fast_dispatch = false;
|
||||||
user_config.callbacks = testenv;
|
user_config.callbacks = testenv;
|
||||||
return user_config;
|
return user_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct InstructionGenerator final {
|
struct InstructionGenerator final {
|
||||||
public:
|
public:
|
||||||
InstructionGenerator(const char* format, std::function<bool(u32)> is_valid = [](u32){ return true; }) : is_valid(is_valid) {
|
InstructionGenerator(const char* format, std::function<bool(u32)> is_valid = [](u32){ return true; }) : is_valid(is_valid) {
|
||||||
|
@ -92,16 +91,16 @@ private:
|
||||||
u32 mask = 0;
|
u32 mask = 0;
|
||||||
std::function<bool(u32)> is_valid;
|
std::function<bool(u32)> is_valid;
|
||||||
};
|
};
|
||||||
} // namespace
|
|
||||||
|
|
||||||
using WriteRecords = std::map<u32, u8>;
|
using WriteRecords = std::map<u32, u8>;
|
||||||
|
|
||||||
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
|
bool DoesBehaviorMatch(const A32Unicorn<ArmTestEnv>& uni, const Dynarmic::A32::Jit& jit,
|
||||||
return interp.Reg == jit.Regs()
|
const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
|
||||||
&& interp.ExtReg == jit.ExtRegs()
|
return uni.GetRegisters() == jit.Regs() &&
|
||||||
&& interp.Cpsr == jit.Cpsr()
|
uni.GetExtRegs() == jit.ExtRegs() &&
|
||||||
//&& interp.VFP[VFP_FPSCR] == jit.Fpscr()
|
uni.GetCpsr() == jit.Cpsr() &&
|
||||||
&& interp_write_records == jit_write_records;
|
// uni.GetFpscr() == jit.Fpscr() &&
|
||||||
|
interp_write_records == jit_write_records;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) {
|
void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) {
|
||||||
|
@ -112,34 +111,32 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||||
test_env.code_mem.back() = 0xEAFFFFFE; // b +#0
|
test_env.code_mem.back() = 0xEAFFFFFE; // b +#0
|
||||||
|
|
||||||
// Prepare test subjects
|
// Prepare test subjects
|
||||||
ARMul_State interp{USER32MODE};
|
A32Unicorn<ArmTestEnv> uni{test_env};
|
||||||
interp.user_callbacks = &test_env;
|
|
||||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||||
|
|
||||||
for (size_t run_number = 0; run_number < run_count; run_number++) {
|
for (size_t run_number = 0; run_number < run_count; run_number++) {
|
||||||
interp.instruction_cache.clear();
|
uni.ClearPageCache();
|
||||||
InterpreterClearCache();
|
|
||||||
jit.ClearCache();
|
jit.ClearCache();
|
||||||
|
|
||||||
// Setup initial state
|
// Setup initial state
|
||||||
|
|
||||||
u32 initial_cpsr = 0x000001D0;
|
const u32 initial_cpsr = 0x000001D0;
|
||||||
|
|
||||||
ArmTestEnv::RegisterArray initial_regs;
|
ArmTestEnv::RegisterArray initial_regs;
|
||||||
std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
||||||
initial_regs[15] = 0;
|
initial_regs[15] = 0;
|
||||||
|
|
||||||
ArmTestEnv::ExtRegsArray initial_extregs;
|
ArmTestEnv::ExtRegsArray initial_extregs;
|
||||||
std::generate(initial_extregs.begin(), initial_extregs.end(),
|
std::generate(initial_extregs.begin(), initial_extregs.end(),
|
||||||
[]{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
[]{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
||||||
|
|
||||||
u32 initial_fpscr = 0x01000000 | (RandInt<u32>(0, 3) << 22);
|
const u32 initial_fpscr = 0x01000000 | (RandInt<u32>(0, 3) << 22);
|
||||||
|
|
||||||
interp.UnsetExclusiveMemoryAddress();
|
uni.SetCpsr(initial_cpsr);
|
||||||
interp.Cpsr = initial_cpsr;
|
uni.SetRegisters(initial_regs);
|
||||||
interp.Reg = initial_regs;
|
uni.SetExtRegs(initial_extregs);
|
||||||
interp.ExtReg = initial_extregs;
|
uni.SetFpscr(initial_fpscr);
|
||||||
interp.VFP[VFP_FPSCR] = initial_fpscr;
|
uni.EnableFloatingPointAccess();
|
||||||
jit.Reset();
|
jit.Reset();
|
||||||
jit.SetCpsr(initial_cpsr);
|
jit.SetCpsr(initial_cpsr);
|
||||||
jit.Regs() = initial_regs;
|
jit.Regs() = initial_regs;
|
||||||
|
@ -157,35 +154,37 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nInitial Register Listing: \n");
|
printf("\nInitial Register Listing: \n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
for (size_t i = 0; i < initial_regs.size(); i++) {
|
||||||
auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
|
const auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
|
||||||
printf("%4s: %08x\n", reg, initial_regs[i]);
|
printf("%4s: %08x\n", reg, initial_regs[i]);
|
||||||
}
|
}
|
||||||
printf("CPSR: %08x\n", initial_cpsr);
|
printf("CPSR: %08x\n", initial_cpsr);
|
||||||
printf("FPSCR:%08x\n", initial_fpscr);
|
printf("FPSCR:%08x\n", initial_fpscr);
|
||||||
for (int i = 0; i <= 63; i++) {
|
for (size_t i = 0; i < initial_extregs.size(); i++) {
|
||||||
printf("S%3i: %08x\n", i, initial_extregs[i]);
|
printf("S%3zu: %08x\n", i, initial_extregs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nFinal Register Listing: \n");
|
printf("\nFinal Register Listing: \n");
|
||||||
printf(" interp jit\n");
|
printf(" unicorn jit\n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
const auto uni_registers = uni.GetRegisters();
|
||||||
auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
|
for (size_t i = 0; i < uni_registers.size(); i++) {
|
||||||
printf("%4s: %08x %08x %s\n", reg, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
|
const auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
|
||||||
|
printf("%4s: %08x %08x %s\n", reg, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : "");
|
||||||
}
|
}
|
||||||
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : "");
|
||||||
printf("FPSCR:%08x %08x %s\n", interp.VFP[VFP_FPSCR], jit.Fpscr(), interp.VFP[VFP_FPSCR] != jit.Fpscr() ? "*" : "");
|
printf("FPSCR:%08x %08x %s\n", uni.GetFpscr(), jit.Fpscr(), uni.GetFpscr() != jit.Fpscr() ? "*" : "");
|
||||||
for (int i = 0; i <= 63; i++) {
|
const auto uni_ext_regs = uni.GetExtRegs();
|
||||||
printf("S%3i: %08x %08x %s\n", i, interp.ExtReg[i], jit.ExtRegs()[i], interp.ExtReg[i] != jit.ExtRegs()[i] ? "*" : "");
|
for (size_t i = 0; i < uni_ext_regs.size(); i++) {
|
||||||
|
printf("S%3zu: %08x %08x %s\n", i, uni_ext_regs[i], jit.ExtRegs()[i], uni_ext_regs[i] != jit.ExtRegs()[i] ? "*" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nInterp Write Records:\n");
|
printf("\nInterp Write Records:\n");
|
||||||
for (auto& record : interp_write_records) {
|
for (const auto& record : interp_write_records) {
|
||||||
printf("[%08x] = %02x\n", record.first, record.second);
|
printf("[%08x] = %02x\n", record.first, record.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nJIT Write Records:\n");
|
printf("\nJIT Write Records:\n");
|
||||||
for (auto& record : jit_write_records) {
|
for (const auto& record : jit_write_records) {
|
||||||
printf("[%08x] = %02x\n", record.first, record.second);
|
printf("[%08x] = %02x\n", record.first, record.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,12 +208,14 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||||
|
|
||||||
// Run interpreter
|
// Run interpreter
|
||||||
test_env.modified_memory.clear();
|
test_env.modified_memory.clear();
|
||||||
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
|
test_env.ticks_left = instructions_to_execute_count;
|
||||||
InterpreterMainLoop(&interp);
|
uni.Run();
|
||||||
interp_write_records = test_env.modified_memory;
|
interp_write_records = test_env.modified_memory;
|
||||||
{
|
{
|
||||||
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
|
const bool T = Dynarmic::Common::Bit<5>(uni.GetCpsr());
|
||||||
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
const u32 mask = T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
||||||
|
const u32 new_pc = uni.GetPC() & mask;
|
||||||
|
uni.SetPC(new_pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run jit
|
// Run jit
|
||||||
|
@ -223,9 +224,10 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||||
jit.Run();
|
jit.Run();
|
||||||
jit_write_records = test_env.modified_memory;
|
jit_write_records = test_env.modified_memory;
|
||||||
|
|
||||||
REQUIRE(DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records));
|
REQUIRE(DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm][A32]" ) {
|
TEST_CASE( "arm: Optimization Failure (Randomized test case)", "[arm][A32]" ) {
|
||||||
// This was a randomized test-case that was failing.
|
// This was a randomized test-case that was failing.
|
||||||
|
|
|
@ -27,8 +27,7 @@
|
||||||
#include "ir_opt/passes.h"
|
#include "ir_opt/passes.h"
|
||||||
#include "rand_int.h"
|
#include "rand_int.h"
|
||||||
#include "testenv.h"
|
#include "testenv.h"
|
||||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
#include "unicorn_emu/a32_unicorn.h"
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
|
|
||||||
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
||||||
Dynarmic::A32::UserConfig user_config;
|
Dynarmic::A32::UserConfig user_config;
|
||||||
|
@ -64,7 +63,7 @@ public:
|
||||||
u16 inst;
|
u16 inst;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
u16 random = RandInt<u16>(0, 0xFFFF);
|
const u16 random = RandInt<u16>(0, 0xFFFF);
|
||||||
inst = bits | (random & ~mask);
|
inst = bits | (random & ~mask);
|
||||||
} while (!is_valid(inst));
|
} while (!is_valid(inst));
|
||||||
|
|
||||||
|
@ -78,46 +77,52 @@ private:
|
||||||
std::function<bool(u16)> is_valid;
|
std::function<bool(u16)> is_valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Jit& jit, WriteRecords& interp_write_records, WriteRecords& jit_write_records) {
|
static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const Dynarmic::A32::Jit& jit,
|
||||||
const auto interp_regs = interp.Reg;
|
const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
|
||||||
|
const auto interp_regs = uni.GetRegisters();
|
||||||
const auto jit_regs = jit.Regs();
|
const auto jit_regs = jit.Regs();
|
||||||
|
|
||||||
return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end())
|
return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) &&
|
||||||
&& interp.Cpsr == jit.Cpsr()
|
uni.GetCpsr() == jit.Cpsr() &&
|
||||||
&& interp_write_records == jit_write_records;
|
interp_write_records == jit_write_records;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State& interp, Dynarmic::A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs,
|
static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<ThumbTestEnv>& uni, Dynarmic::A32::Jit& jit, const ThumbTestEnv::RegisterArray& initial_regs,
|
||||||
size_t instruction_count, size_t instructions_to_execute_count) {
|
size_t instruction_count, size_t instructions_to_execute_count) {
|
||||||
interp.instruction_cache.clear();
|
uni.ClearPageCache();
|
||||||
InterpreterClearCache();
|
|
||||||
jit.ClearCache();
|
jit.ClearCache();
|
||||||
|
|
||||||
// Setup initial state
|
// Setup initial state
|
||||||
|
|
||||||
interp.Cpsr = 0x000001F0;
|
uni.SetCpsr(0x000001F0);
|
||||||
interp.Reg = initial_regs;
|
uni.SetRegisters(initial_regs);
|
||||||
jit.SetCpsr(0x000001F0);
|
jit.SetCpsr(0x000001F0);
|
||||||
jit.Regs() = initial_regs;
|
jit.Regs() = initial_regs;
|
||||||
|
|
||||||
// Run interpreter
|
// Run interpreter
|
||||||
test_env.modified_memory.clear();
|
test_env.modified_memory.clear();
|
||||||
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
|
test_env.ticks_left = instructions_to_execute_count;
|
||||||
InterpreterMainLoop(&interp);
|
uni.SetPC(uni.GetPC() | 1);
|
||||||
auto interp_write_records = test_env.modified_memory;
|
uni.Run();
|
||||||
{
|
const bool uni_code_memory_modified = test_env.code_mem_modified_by_guest;
|
||||||
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
|
const auto interp_write_records = test_env.modified_memory;
|
||||||
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run jit
|
// Run jit
|
||||||
|
test_env.code_mem_modified_by_guest = false;
|
||||||
test_env.modified_memory.clear();
|
test_env.modified_memory.clear();
|
||||||
test_env.ticks_left = instructions_to_execute_count;
|
test_env.ticks_left = instructions_to_execute_count;
|
||||||
jit.Run();
|
jit.Run();
|
||||||
auto jit_write_records = test_env.modified_memory;
|
const bool jit_code_memory_modified = test_env.code_mem_modified_by_guest;
|
||||||
|
const auto jit_write_records = test_env.modified_memory;
|
||||||
|
test_env.code_mem_modified_by_guest = false;
|
||||||
|
|
||||||
|
REQUIRE(uni_code_memory_modified == jit_code_memory_modified);
|
||||||
|
if (uni_code_memory_modified) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Compare
|
// Compare
|
||||||
if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) {
|
if (!DoesBehaviorMatch(uni, jit, interp_write_records, jit_write_records)) {
|
||||||
printf("Failed at execution number %zu\n", run_number);
|
printf("Failed at execution number %zu\n", run_number);
|
||||||
|
|
||||||
printf("\nInstruction Listing: \n");
|
printf("\nInstruction Listing: \n");
|
||||||
|
@ -126,24 +131,25 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State&
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nInitial Register Listing: \n");
|
printf("\nInitial Register Listing: \n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
for (size_t i = 0; i < initial_regs.size(); i++) {
|
||||||
printf("%4i: %08x\n", i, initial_regs[i]);
|
printf("%4zu: %08x\n", i, initial_regs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nFinal Register Listing: \n");
|
printf("\nFinal Register Listing: \n");
|
||||||
printf(" interp jit\n");
|
printf(" unicorn jit\n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
const auto uni_registers = uni.GetRegisters();
|
||||||
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
|
for (size_t i = 0; i < uni_registers.size(); i++) {
|
||||||
|
printf("%4zu: %08x %08x %s\n", i, uni_registers[i], jit.Regs()[i], uni_registers[i] != jit.Regs()[i] ? "*" : "");
|
||||||
}
|
}
|
||||||
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : "");
|
||||||
|
|
||||||
printf("\nInterp Write Records:\n");
|
printf("\nUnicorn Write Records:\n");
|
||||||
for (auto& record : interp_write_records) {
|
for (const auto& record : interp_write_records) {
|
||||||
printf("[%08x] = %02x\n", record.first, record.second);
|
printf("[%08x] = %02x\n", record.first, record.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nJIT Write Records:\n");
|
printf("\nJIT Write Records:\n");
|
||||||
for (auto& record : jit_write_records) {
|
for (const auto& record : jit_write_records) {
|
||||||
printf("[%08x] = %02x\n", record.first, record.second);
|
printf("[%08x] = %02x\n", record.first, record.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,28 +181,27 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State&
|
||||||
void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
|
void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
|
||||||
ThumbTestEnv test_env;
|
ThumbTestEnv test_env;
|
||||||
|
|
||||||
// Prepare memory
|
// Prepare memory.
|
||||||
test_env.code_mem.resize(instruction_count + 1);
|
test_env.code_mem.resize(instruction_count + 1);
|
||||||
test_env.code_mem.back() = 0xE7FE; // b +#0
|
test_env.code_mem.back() = 0xE7FE; // b +#0
|
||||||
|
|
||||||
// Prepare test subjects
|
// Prepare test subjects
|
||||||
ARMul_State interp{USER32MODE};
|
A32Unicorn uni{test_env};
|
||||||
interp.user_callbacks = &test_env;
|
|
||||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||||
|
|
||||||
for (size_t run_number = 0; run_number < run_count; run_number++) {
|
for (size_t run_number = 0; run_number < run_count; run_number++) {
|
||||||
ThumbTestEnv::RegisterArray initial_regs;
|
ThumbTestEnv::RegisterArray initial_regs;
|
||||||
std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
|
||||||
initial_regs[15] = 0;
|
initial_regs[15] = 0;
|
||||||
|
|
||||||
std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
|
std::generate_n(test_env.code_mem.begin(), instruction_count, instruction_generator);
|
||||||
|
|
||||||
RunInstance(run_number, test_env, interp, jit, initial_regs, instruction_count, instructions_to_execute_count);
|
RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
const std::array<ThumbInstGen, 25> instructions = {{
|
const std::array instructions = {
|
||||||
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
||||||
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
|
||||||
ThumbInstGen("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00010xxxxxxxxxxx"), // ASR <Rd>, <Rm>, #<imm5>
|
||||||
|
@ -224,11 +229,23 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
||||||
ThumbInstGen("10111100xxxxxxxx", // POP (P = 0)
|
ThumbInstGen("10111100xxxxxxxx", // POP (P = 0)
|
||||||
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
[](u16 inst){ return Dynarmic::Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
|
||||||
ThumbInstGen("1100xxxxxxxxxxxx"), // STMIA/LDMIA
|
ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA
|
||||||
|
[](u16 inst) {
|
||||||
|
// Ensure that the architecturally undefined case of
|
||||||
|
// the base register being within the list isn't hit.
|
||||||
|
const u32 rn = Dynarmic::Common::Bits<8, 10>(inst);
|
||||||
|
return (inst & (1U << rn)) == 0;
|
||||||
|
}),
|
||||||
|
// TODO: We should properly test against swapped
|
||||||
|
// endianness cases, however Unicorn doesn't
|
||||||
|
// expose the intended endianness of a load/store
|
||||||
|
// operation to memory through its hooks.
|
||||||
|
#if 0
|
||||||
ThumbInstGen("101101100101x000"), // SETEND
|
ThumbInstGen("101101100101x000"), // SETEND
|
||||||
}};
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
auto instruction_select = [&]() -> u16 {
|
const auto instruction_select = [&]() -> u16 {
|
||||||
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
|
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
|
||||||
|
|
||||||
return instructions[inst_index].Generate();
|
return instructions[inst_index].Generate();
|
||||||
|
@ -248,26 +265,39 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
||||||
const std::array<ThumbInstGen, 8> instructions = {{
|
const std::array instructions = {
|
||||||
|
// TODO: We currently can't test BX/BLX as we have
|
||||||
|
// no way of preventing the unpredictable
|
||||||
|
// condition from occurring with the current interface.
|
||||||
|
// (bits zero and one within the specified register
|
||||||
|
// must not be address<1:0> == '10'.
|
||||||
|
#if 0
|
||||||
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
||||||
[](u16 inst){
|
[](u16 inst){
|
||||||
u32 Rm = Dynarmic::Common::Bits<3, 6>(inst);
|
const u32 Rm = Dynarmic::Common::Bits<3, 6>(inst);
|
||||||
return Rm != 15;
|
return Rm != 15;
|
||||||
}),
|
}),
|
||||||
|
#endif
|
||||||
ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
|
ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
|
||||||
ThumbInstGen("11100xxxxxxxxxxx"), // B
|
ThumbInstGen("11100xxxxxxxxxxx"), // B
|
||||||
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
|
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
|
||||||
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
|
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
|
||||||
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
|
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
|
||||||
[](u16 inst){
|
[](u16 inst){
|
||||||
u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
const u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
||||||
return c < 0b1110; // Don't want SWI or undefined instructions.
|
return c < 0b1110; // Don't want SWI or undefined instructions.
|
||||||
}),
|
}),
|
||||||
ThumbInstGen("10110110011x0xxx"), // CPS
|
ThumbInstGen("10110110011x0xxx"), // CPS
|
||||||
ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1)
|
|
||||||
}};
|
|
||||||
|
|
||||||
auto instruction_select = [&]() -> u16 {
|
// TODO: We currently have no control over the generated
|
||||||
|
// values when creating new pages, so we can't
|
||||||
|
// reliably test this yet.
|
||||||
|
#if 0
|
||||||
|
ThumbInstGen("10111101xxxxxxxx"), // POP (R = 1)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto instruction_select = [&]() -> u16 {
|
||||||
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
|
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
|
||||||
|
|
||||||
return instructions[inst_index].Generate();
|
return instructions[inst_index].Generate();
|
||||||
|
@ -280,8 +310,7 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
|
||||||
ThumbTestEnv test_env;
|
ThumbTestEnv test_env;
|
||||||
|
|
||||||
// Prepare test subjects
|
// Prepare test subjects
|
||||||
ARMul_State interp{USER32MODE};
|
A32Unicorn<ThumbTestEnv> uni{test_env};
|
||||||
interp.user_callbacks = &test_env;
|
|
||||||
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
|
||||||
|
|
||||||
constexpr ThumbTestEnv::RegisterArray initial_regs {
|
constexpr ThumbTestEnv::RegisterArray initial_regs {
|
||||||
|
@ -312,5 +341,5 @@ TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
|
||||||
0xE7FE, // b +#0
|
0xE7FE, // b +#0
|
||||||
};
|
};
|
||||||
|
|
||||||
RunInstance(1, test_env, interp, jit, initial_regs, 5, 5);
|
RunInstance(1, test_env, uni, jit, initial_regs, 5, 5);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,470 +0,0 @@
|
||||||
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armsupp.h"
|
|
||||||
|
|
||||||
const InstructionSetEncodingItem arm_instruction[] = {
|
|
||||||
{ "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
|
||||||
{ "vmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
|
|
||||||
{ "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
|
|
||||||
{ "vnmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
|
||||||
{ "vnmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
|
|
||||||
{ "vmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
|
||||||
{ "vadd", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
|
||||||
{ "vsub", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x3, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
|
|
||||||
{ "vdiv", 5, ARMVFP2, { 23, 27, 0x1D, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
|
|
||||||
{ "vmov(i)", 4, ARMVFP3, { 23, 27, 0x1D, 20, 21, 0x3, 9, 11, 0x5, 4, 7, 0 }},
|
|
||||||
{ "vmov(r)", 5, ARMVFP3, { 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 }},
|
|
||||||
{ "vabs", 5, ARMVFP2, { 23, 27, 0x1D, 16, 21, 0x30, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 }},
|
|
||||||
{ "vneg", 5, ARMVFP2, { 23, 27, 0x1D, 17, 21, 0x18, 9, 11, 0x5, 6, 7, 1, 4, 4, 0 }},
|
|
||||||
{ "vsqrt", 5, ARMVFP2, { 23, 27, 0x1D, 16, 21, 0x31, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 }},
|
|
||||||
{ "vcmp", 5, ARMVFP2, { 23, 27, 0x1D, 16, 21, 0x34, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
|
|
||||||
{ "vcmp2", 5, ARMVFP2, { 23, 27, 0x1D, 16, 21, 0x35, 9, 11, 0x5, 0, 6, 0x40 }},
|
|
||||||
{ "vcvt(bds)", 5, ARMVFP2, { 23, 27, 0x1D, 16, 21, 0x37, 9, 11, 0x5, 6, 7, 3, 4, 4, 0 }},
|
|
||||||
{ "vcvt(bff)", 6, ARMVFP3, { 23, 27, 0x1D, 19, 21, 0x7, 17, 17, 0x1, 9, 11, 5, 6, 6, 1 }},
|
|
||||||
{ "vcvt(bfi)", 5, ARMVFP2, { 23, 27, 0x1D, 19, 21, 0x7, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
|
|
||||||
{ "vmovbrs", 3, ARMVFP2, { 21, 27, 0x70, 8, 11, 0xA, 0, 6, 0x10 }},
|
|
||||||
{ "vmsr", 2, ARMVFP2, { 20, 27, 0xEE, 0, 11, 0xA10 }},
|
|
||||||
{ "vmovbrc", 4, ARMVFP2, { 23, 27, 0x1C, 20, 20, 0x0, 8, 11, 0xB, 0, 4, 0x10 }},
|
|
||||||
{ "vmrs", 2, ARMVFP2, { 20, 27, 0xEF, 0, 11, 0xA10 }},
|
|
||||||
{ "vmovbcr", 4, ARMVFP2, { 24, 27, 0xE, 20, 20, 1, 8, 11, 0xB, 0, 4, 0x10 }},
|
|
||||||
{ "vmovbrrss", 3, ARMVFP2, { 21, 27, 0x62, 8, 11, 0xA, 4, 4, 1 }},
|
|
||||||
{ "vmovbrrd", 3, ARMVFP2, { 21, 27, 0x62, 6, 11, 0x2C, 4, 4, 1 }},
|
|
||||||
{ "vstr", 3, ARMVFP2, { 24, 27, 0xD, 20, 21, 0, 9, 11, 5 }},
|
|
||||||
{ "vpush", 3, ARMVFP2, { 23, 27, 0x1A, 16, 21, 0x2D, 9, 11, 5 }},
|
|
||||||
{ "vstm", 3, ARMVFP2, { 25, 27, 0x6, 20, 20, 0, 9, 11, 5 }},
|
|
||||||
{ "vpop", 3, ARMVFP2, { 23, 27, 0x19, 16, 21, 0x3D, 9, 11, 5 }},
|
|
||||||
{ "vldr", 3, ARMVFP2, { 24, 27, 0xD, 20, 21, 1, 9, 11, 5 }},
|
|
||||||
{ "vldm", 3, ARMVFP2, { 25, 27, 0x6, 20, 20, 1, 9, 11, 5 }},
|
|
||||||
|
|
||||||
{ "srs", 4, 6, { 25, 31, 0x0000007c, 22, 22, 0x00000001, 16, 20, 0x0000000d, 8, 11, 0x00000005 }},
|
|
||||||
{ "rfe", 4, 6, { 25, 31, 0x0000007c, 22, 22, 0x00000000, 20, 20, 0x00000001, 8, 11, 0x0000000a }},
|
|
||||||
{ "bkpt", 2, 3, { 20, 27, 0x00000012, 4, 7, 0x00000007 }},
|
|
||||||
{ "blx", 1, 3, { 25, 31, 0x0000007d }},
|
|
||||||
{ "cps", 3, 6, { 20, 31, 0x00000f10, 16, 16, 0x00000000, 5, 5, 0x00000000 }},
|
|
||||||
{ "pld", 4, 4, { 26, 31, 0x0000003d, 24, 24, 0x00000001, 20, 22, 0x00000005, 12, 15, 0x0000000f }},
|
|
||||||
{ "setend", 2, 6, { 16, 31, 0x0000f101, 4, 7, 0x00000000 }},
|
|
||||||
{ "clrex", 1, 6, { 0, 31, 0xf57ff01f }},
|
|
||||||
{ "rev16", 2, 6, { 16, 27, 0x000006bf, 4, 11, 0x000000fb }},
|
|
||||||
{ "usad8", 3, 6, { 20, 27, 0x00000078, 12, 15, 0x0000000f, 4, 7, 0x00000001 }},
|
|
||||||
{ "sxtb", 2, 6, { 16, 27, 0x000006af, 4, 7, 0x00000007 }},
|
|
||||||
{ "uxtb", 2, 6, { 16, 27, 0x000006ef, 4, 7, 0x00000007 }},
|
|
||||||
{ "sxth", 2, 6, { 16, 27, 0x000006bf, 4, 7, 0x00000007 }},
|
|
||||||
{ "sxtb16", 2, 6, { 16, 27, 0x0000068f, 4, 7, 0x00000007 }},
|
|
||||||
{ "uxth", 2, 6, { 16, 27, 0x000006ff, 4, 7, 0x00000007 }},
|
|
||||||
{ "uxtb16", 2, 6, { 16, 27, 0x000006cf, 4, 7, 0x00000007 }},
|
|
||||||
{ "cpy", 2, 6, { 20, 27, 0x0000001a, 4, 11, 0x00000000 }},
|
|
||||||
{ "uxtab", 2, 6, { 20, 27, 0x0000006e, 4, 9, 0x00000007 }},
|
|
||||||
{ "ssub8", 2, 6, { 20, 27, 0x00000061, 4, 7, 0x0000000f }},
|
|
||||||
{ "shsub8", 2, 6, { 20, 27, 0x00000063, 4, 7, 0x0000000f }},
|
|
||||||
{ "ssubaddx", 2, 6, { 20, 27, 0x00000061, 4, 7, 0x00000005 }},
|
|
||||||
{ "strex", 2, 6, { 20, 27, 0x00000018, 4, 7, 0x00000009 }},
|
|
||||||
{ "strexb", 2, 7, { 20, 27, 0x0000001c, 4, 7, 0x00000009 }},
|
|
||||||
{ "swp", 2, 0, { 20, 27, 0x00000010, 4, 7, 0x00000009 }},
|
|
||||||
{ "swpb", 2, 0, { 20, 27, 0x00000014, 4, 7, 0x00000009 }},
|
|
||||||
{ "ssub16", 2, 6, { 20, 27, 0x00000061, 4, 7, 0x00000007 }},
|
|
||||||
{ "ssat16", 2, 6, { 20, 27, 0x0000006a, 4, 7, 0x00000003 }},
|
|
||||||
{ "shsubaddx", 2, 6, { 20, 27, 0x00000063, 4, 7, 0x00000005 }},
|
|
||||||
{ "qsubaddx", 2, 6, { 20, 27, 0x00000062, 4, 7, 0x00000005 }},
|
|
||||||
{ "shaddsubx", 2, 6, { 20, 27, 0x00000063, 4, 7, 0x00000003 }},
|
|
||||||
{ "shadd8", 2, 6, { 20, 27, 0x00000063, 4, 7, 0x00000009 }},
|
|
||||||
{ "shadd16", 2, 6, { 20, 27, 0x00000063, 4, 7, 0x00000001 }},
|
|
||||||
{ "sel", 2, 6, { 20, 27, 0x00000068, 4, 7, 0x0000000b }},
|
|
||||||
{ "saddsubx", 2, 6, { 20, 27, 0x00000061, 4, 7, 0x00000003 }},
|
|
||||||
{ "sadd8", 2, 6, { 20, 27, 0x00000061, 4, 7, 0x00000009 }},
|
|
||||||
{ "sadd16", 2, 6, { 20, 27, 0x00000061, 4, 7, 0x00000001 }},
|
|
||||||
{ "shsub16", 2, 6, { 20, 27, 0x00000063, 4, 7, 0x00000007 }},
|
|
||||||
{ "umaal", 2, 6, { 20, 27, 0x00000004, 4, 7, 0x00000009 }},
|
|
||||||
{ "uxtab16", 2, 6, { 20, 27, 0x0000006c, 4, 7, 0x00000007 }},
|
|
||||||
{ "usubaddx", 2, 6, { 20, 27, 0x00000065, 4, 7, 0x00000005 }},
|
|
||||||
{ "usub8", 2, 6, { 20, 27, 0x00000065, 4, 7, 0x0000000f }},
|
|
||||||
{ "usub16", 2, 6, { 20, 27, 0x00000065, 4, 7, 0x00000007 }},
|
|
||||||
{ "usat16", 2, 6, { 20, 27, 0x0000006e, 4, 7, 0x00000003 }},
|
|
||||||
{ "usada8", 2, 6, { 20, 27, 0x00000078, 4, 7, 0x00000001 }},
|
|
||||||
{ "uqsubaddx", 2, 6, { 20, 27, 0x00000066, 4, 7, 0x00000005 }},
|
|
||||||
{ "uqsub8", 2, 6, { 20, 27, 0x00000066, 4, 7, 0x0000000f }},
|
|
||||||
{ "uqsub16", 2, 6, { 20, 27, 0x00000066, 4, 7, 0x00000007 }},
|
|
||||||
{ "uqaddsubx", 2, 6, { 20, 27, 0x00000066, 4, 7, 0x00000003 }},
|
|
||||||
{ "uqadd8", 2, 6, { 20, 27, 0x00000066, 4, 7, 0x00000009 }},
|
|
||||||
{ "uqadd16", 2, 6, { 20, 27, 0x00000066, 4, 7, 0x00000001 }},
|
|
||||||
{ "sxtab", 2, 6, { 20, 27, 0x0000006a, 4, 7, 0x00000007 }},
|
|
||||||
{ "uhsubaddx", 2, 6, { 20, 27, 0x00000067, 4, 7, 0x00000005 }},
|
|
||||||
{ "uhsub8", 2, 6, { 20, 27, 0x00000067, 4, 7, 0x0000000f }},
|
|
||||||
{ "uhsub16", 2, 6, { 20, 27, 0x00000067, 4, 7, 0x00000007 }},
|
|
||||||
{ "uhaddsubx", 2, 6, { 20, 27, 0x00000067, 4, 7, 0x00000003 }},
|
|
||||||
{ "uhadd8", 2, 6, { 20, 27, 0x00000067, 4, 7, 0x00000009 }},
|
|
||||||
{ "uhadd16", 2, 6, { 20, 27, 0x00000067, 4, 7, 0x00000001 }},
|
|
||||||
{ "uaddsubx", 2, 6, { 20, 27, 0x00000065, 4, 7, 0x00000003 }},
|
|
||||||
{ "uadd8", 2, 6, { 20, 27, 0x00000065, 4, 7, 0x00000009 }},
|
|
||||||
{ "uadd16", 2, 6, { 20, 27, 0x00000065, 4, 7, 0x00000001 }},
|
|
||||||
{ "sxtah", 2, 6, { 20, 27, 0x0000006b, 4, 7, 0x00000007 }},
|
|
||||||
{ "sxtab16", 2, 6, { 20, 27, 0x00000068, 4, 7, 0x00000007 }},
|
|
||||||
{ "qadd8", 2, 6, { 20, 27, 0x00000062, 4, 7, 0x00000009 }},
|
|
||||||
{ "bxj", 2, 5, { 20, 27, 0x00000012, 4, 7, 0x00000002 }},
|
|
||||||
{ "clz", 2, 3, { 20, 27, 0x00000016, 4, 7, 0x00000001 }},
|
|
||||||
{ "uxtah", 2, 6, { 20, 27, 0x0000006f, 4, 7, 0x00000007 }},
|
|
||||||
{ "bx", 2, 2, { 20, 27, 0x00000012, 4, 7, 0x00000001 }},
|
|
||||||
{ "rev", 2, 6, { 20, 27, 0x0000006b, 4, 7, 0x00000003 }},
|
|
||||||
{ "blx", 2, 3, { 20, 27, 0x00000012, 4, 7, 0x00000003 }},
|
|
||||||
{ "revsh", 2, 6, { 20, 27, 0x0000006f, 4, 7, 0x0000000b }},
|
|
||||||
{ "qadd", 2, 4, { 20, 27, 0x00000010, 4, 7, 0x00000005 }},
|
|
||||||
{ "qadd16", 2, 6, { 20, 27, 0x00000062, 4, 7, 0x00000001 }},
|
|
||||||
{ "qaddsubx", 2, 6, { 20, 27, 0x00000062, 4, 7, 0x00000003 }},
|
|
||||||
{ "ldrex", 2, 0, { 20, 27, 0x00000019, 4, 7, 0x00000009 }},
|
|
||||||
{ "qdadd", 2, 4, { 20, 27, 0x00000014, 4, 7, 0x00000005 }},
|
|
||||||
{ "qdsub", 2, 4, { 20, 27, 0x00000016, 4, 7, 0x00000005 }},
|
|
||||||
{ "qsub", 2, 4, { 20, 27, 0x00000012, 4, 7, 0x00000005 }},
|
|
||||||
{ "ldrexb", 2, 7, { 20, 27, 0x0000001d, 4, 7, 0x00000009 }},
|
|
||||||
{ "qsub8", 2, 6, { 20, 27, 0x00000062, 4, 7, 0x0000000f }},
|
|
||||||
{ "qsub16", 2, 6, { 20, 27, 0x00000062, 4, 7, 0x00000007 }},
|
|
||||||
{ "smuad", 4, 6, { 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 }},
|
|
||||||
{ "smmul", 4, 6, { 20, 27, 0x00000075, 12, 15, 0x0000000f, 6, 7, 0x00000000, 4, 4, 0x00000001 }},
|
|
||||||
{ "smusd", 4, 6, { 20, 27, 0x00000070, 12, 15, 0x0000000f, 6, 7, 0x00000001, 4, 4, 0x00000001 }},
|
|
||||||
{ "smlsd", 3, 6, { 20, 27, 0x00000070, 6, 7, 0x00000001, 4, 4, 0x00000001 }},
|
|
||||||
{ "smlsld", 3, 6, { 20, 27, 0x00000074, 6, 7, 0x00000001, 4, 4, 0x00000001 }},
|
|
||||||
{ "smmla", 3, 6, { 20, 27, 0x00000075, 6, 7, 0x00000000, 4, 4, 0x00000001 }},
|
|
||||||
{ "smmls", 3, 6, { 20, 27, 0x00000075, 6, 7, 0x00000003, 4, 4, 0x00000001 }},
|
|
||||||
{ "smlald", 3, 6, { 20, 27, 0x00000074, 6, 7, 0x00000000, 4, 4, 0x00000001 }},
|
|
||||||
{ "smlad", 3, 6, { 20, 27, 0x00000070, 6, 7, 0x00000000, 4, 4, 0x00000001 }},
|
|
||||||
{ "smlaw", 3, 4, { 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000000 }},
|
|
||||||
{ "smulw", 3, 4, { 20, 27, 0x00000012, 7, 7, 0x00000001, 4, 5, 0x00000002 }},
|
|
||||||
{ "pkhtb", 2, 6, { 20, 27, 0x00000068, 4, 6, 0x00000005 }},
|
|
||||||
{ "pkhbt", 2, 6, { 20, 27, 0x00000068, 4, 6, 0x00000001 }},
|
|
||||||
{ "smul", 3, 4, { 20, 27, 0x00000016, 7, 7, 0x00000001, 4, 4, 0x00000000 }},
|
|
||||||
{ "smlalxy", 3, 4, { 20, 27, 0x00000014, 7, 7, 0x00000001, 4, 4, 0x00000000 }},
|
|
||||||
{ "smla", 3, 4, { 20, 27, 0x00000010, 7, 7, 0x00000001, 4, 4, 0x00000000 }},
|
|
||||||
{ "mcrr", 1, 6, { 20, 27, 0x000000c4 }},
|
|
||||||
{ "mrrc", 1, 6, { 20, 27, 0x000000c5 }},
|
|
||||||
{ "cmp", 2, 0, { 26, 27, 0x00000000, 20, 24, 0x00000015 }},
|
|
||||||
{ "tst", 2, 0, { 26, 27, 0x00000000, 20, 24, 0x00000011 }},
|
|
||||||
{ "teq", 2, 0, { 26, 27, 0x00000000, 20, 24, 0x00000013 }},
|
|
||||||
{ "cmn", 2, 0, { 26, 27, 0x00000000, 20, 24, 0x00000017 }},
|
|
||||||
{ "smull", 2, 0, { 21, 27, 0x00000006, 4, 7, 0x00000009 }},
|
|
||||||
{ "umull", 2, 0, { 21, 27, 0x00000004, 4, 7, 0x00000009 }},
|
|
||||||
{ "umlal", 2, 0, { 21, 27, 0x00000005, 4, 7, 0x00000009 }},
|
|
||||||
{ "smlal", 2, 0, { 21, 27, 0x00000007, 4, 7, 0x00000009 }},
|
|
||||||
{ "mul", 2, 0, { 21, 27, 0x00000000, 4, 7, 0x00000009 }},
|
|
||||||
{ "mla", 2, 0, { 21, 27, 0x00000001, 4, 7, 0x00000009 }},
|
|
||||||
{ "ssat", 2, 6, { 21, 27, 0x00000035, 4, 5, 0x00000001 }},
|
|
||||||
{ "usat", 2, 6, { 21, 27, 0x00000037, 4, 5, 0x00000001 }},
|
|
||||||
{ "mrs", 4, 0, { 23, 27, 0x00000002, 20, 21, 0x00000000, 16, 19, 0x0000000f, 0, 11, 0x00000000 }},
|
|
||||||
{ "msr", 3, 0, { 23, 27, 0x00000002, 20, 21, 0x00000002, 4, 7, 0x00000000 }},
|
|
||||||
{ "and", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000000 }},
|
|
||||||
{ "bic", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x0000000e }},
|
|
||||||
{ "ldm", 3, 0, { 25, 27, 0x00000004, 20, 22, 0x00000005, 15, 15, 0x00000000 }},
|
|
||||||
{ "eor", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000001 }},
|
|
||||||
{ "add", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000004 }},
|
|
||||||
{ "rsb", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000003 }},
|
|
||||||
{ "rsc", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000007 }},
|
|
||||||
{ "sbc", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000006 }},
|
|
||||||
{ "adc", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000005 }},
|
|
||||||
{ "sub", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x00000002 }},
|
|
||||||
{ "orr", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x0000000c }},
|
|
||||||
{ "mvn", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x0000000f }},
|
|
||||||
{ "mov", 2, 0, { 26, 27, 0x00000000, 21, 24, 0x0000000d }},
|
|
||||||
{ "stm", 2, 0, { 25, 27, 0x00000004, 20, 22, 0x00000004 }},
|
|
||||||
{ "ldm", 4, 0, { 25, 27, 0x00000004, 22, 22, 0x00000001, 20, 20, 0x00000001, 15, 15, 0x00000001 }},
|
|
||||||
{ "ldrsh", 3, 2, { 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000f }},
|
|
||||||
{ "stm", 3, 0, { 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000000 }},
|
|
||||||
{ "ldm", 3, 0, { 25, 27, 0x00000004, 22, 22, 0x00000000, 20, 20, 0x00000001 }},
|
|
||||||
{ "ldrsb", 3, 2, { 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000d }},
|
|
||||||
{ "strd", 3, 4, { 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000f }},
|
|
||||||
{ "ldrh", 3, 0, { 25, 27, 0x00000000, 20, 20, 0x00000001, 4, 7, 0x0000000b }},
|
|
||||||
{ "strh", 3, 0, { 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000b }},
|
|
||||||
{ "ldrd", 3, 4, { 25, 27, 0x00000000, 20, 20, 0x00000000, 4, 7, 0x0000000d }},
|
|
||||||
{ "strt", 3, 0, { 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000002 }},
|
|
||||||
{ "strbt", 3, 0, { 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000006 }},
|
|
||||||
{ "ldrbt", 3, 0, { 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000007 }},
|
|
||||||
{ "ldrt", 3, 0, { 26, 27, 0x00000001, 24, 24, 0x00000000, 20, 22, 0x00000003 }},
|
|
||||||
{ "mrc", 3, 6, { 24, 27, 0x0000000e, 20, 20, 0x00000001, 4, 4, 0x00000001 }},
|
|
||||||
{ "mcr", 3, 0, { 24, 27, 0x0000000e, 20, 20, 0x00000000, 4, 4, 0x00000001 }},
|
|
||||||
{ "msr", 3, 0, { 23, 27, 0x00000006, 20, 21, 0x00000002, 22, 22, 0x00000001 }},
|
|
||||||
{ "msr", 4, 0, { 23, 27, 0x00000006, 20, 21, 0x00000002, 22, 22, 0x00000000, 16, 19, 0x00000004 }},
|
|
||||||
{ "msr", 5, 0, { 23, 27, 0x00000006, 20, 21, 0x00000002, 22, 22, 0x00000000, 19, 19, 0x00000001, 16, 17, 0x00000000 }},
|
|
||||||
{ "msr", 4, 0, { 23, 27, 0x00000006, 20, 21, 0x00000002, 22, 22, 0x00000000, 16, 17, 0x00000001 }},
|
|
||||||
{ "msr", 4, 0, { 23, 27, 0x00000006, 20, 21, 0x00000002, 22, 22, 0x00000000, 17, 17, 0x00000001 }},
|
|
||||||
{ "ldrb", 3, 0, { 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000001 }},
|
|
||||||
{ "strb", 3, 0, { 26, 27, 0x00000001, 22, 22, 0x00000001, 20, 20, 0x00000000 }},
|
|
||||||
{ "ldr", 4, 0, { 28, 31, 0x0000000e, 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 }},
|
|
||||||
{ "ldrcond", 3, 0, { 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000001 }},
|
|
||||||
{ "str", 3, 0, { 26, 27, 0x00000001, 22, 22, 0x00000000, 20, 20, 0x00000000 }},
|
|
||||||
{ "cdp", 2, 0, { 24, 27, 0x0000000e, 4, 4, 0x00000000 }},
|
|
||||||
{ "stc", 2, 0, { 25, 27, 0x00000006, 20, 20, 0x00000000 }},
|
|
||||||
{ "ldc", 2, 0, { 25, 27, 0x00000006, 20, 20, 0x00000001 }},
|
|
||||||
{ "ldrexd", 2, ARMV6K, { 20, 27, 0x0000001B, 4, 7, 0x00000009 }},
|
|
||||||
{ "strexd", 2, ARMV6K, { 20, 27, 0x0000001A, 4, 7, 0x00000009 }},
|
|
||||||
{ "ldrexh", 2, ARMV6K, { 20, 27, 0x0000001F, 4, 7, 0x00000009 }},
|
|
||||||
{ "strexh", 2, ARMV6K, { 20, 27, 0x0000001E, 4, 7, 0x00000009 }},
|
|
||||||
{ "nop", 5, ARMV6K, { 23, 27, 0x00000006, 22, 22, 0x00000000, 20, 21, 0x00000002, 16, 19, 0x00000000, 0, 7, 0x00000000 }},
|
|
||||||
{ "yield", 5, ARMV6K, { 23, 27, 0x00000006, 22, 22, 0x00000000, 20, 21, 0x00000002, 16, 19, 0x00000000, 0, 7, 0x00000001 }},
|
|
||||||
{ "wfe", 5, ARMV6K, { 23, 27, 0x00000006, 22, 22, 0x00000000, 20, 21, 0x00000002, 16, 19, 0x00000000, 0, 7, 0x00000002 }},
|
|
||||||
{ "wfi", 5, ARMV6K, { 23, 27, 0x00000006, 22, 22, 0x00000000, 20, 21, 0x00000002, 16, 19, 0x00000000, 0, 7, 0x00000003 }},
|
|
||||||
{ "sev", 5, ARMV6K, { 23, 27, 0x00000006, 22, 22, 0x00000000, 20, 21, 0x00000002, 16, 19, 0x00000000, 0, 7, 0x00000004 }},
|
|
||||||
{ "swi", 1, 0, { 24, 27, 0x0000000f }},
|
|
||||||
{ "bbl", 1, 0, { 25, 27, 0x00000005 }},
|
|
||||||
};
|
|
||||||
|
|
||||||
const InstructionSetEncodingItem arm_exclusion_code[] = {
|
|
||||||
{ "vmla", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmls", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vnmla", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vnmls", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vnmul", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmul", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vadd", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vsub", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vdiv", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmov(i)", 0, ARMVFP3, { 0 }},
|
|
||||||
{ "vmov(r)", 0, ARMVFP3, { 0 }},
|
|
||||||
{ "vabs", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vneg", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vsqrt", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vcmp", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vcmp2", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vcvt(bff)", 0, ARMVFP3, { 4, 4, 1 }},
|
|
||||||
{ "vcvt(bds)", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vcvt(bfi)", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmovbrs", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmsr", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmovbrc", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmrs", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmovbcr", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmovbrrss", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vmovbrrd", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vstr", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vpush", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vstm", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vpop", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vldr", 0, ARMVFP2, { 0 }},
|
|
||||||
{ "vldm", 0, ARMVFP2, { 0 }},
|
|
||||||
|
|
||||||
{ "srs", 0, 6, { 0 }},
|
|
||||||
{ "rfe", 0, 6, { 0 }},
|
|
||||||
{ "bkpt", 0, 3, { 0 }},
|
|
||||||
{ "blx", 0, 3, { 0 }},
|
|
||||||
{ "cps", 0, 6, { 0 }},
|
|
||||||
{ "pld", 0, 4, { 0 }},
|
|
||||||
{ "setend", 0, 6, { 0 }},
|
|
||||||
{ "clrex", 0, 6, { 0 }},
|
|
||||||
{ "rev16", 0, 6, { 0 }},
|
|
||||||
{ "usad8", 0, 6, { 0 }},
|
|
||||||
{ "sxtb", 0, 6, { 0 }},
|
|
||||||
{ "uxtb", 0, 6, { 0 }},
|
|
||||||
{ "sxth", 0, 6, { 0 }},
|
|
||||||
{ "sxtb16", 0, 6, { 0 }},
|
|
||||||
{ "uxth", 0, 6, { 0 }},
|
|
||||||
{ "uxtb16", 0, 6, { 0 }},
|
|
||||||
{ "cpy", 0, 6, { 0 }},
|
|
||||||
{ "uxtab", 0, 6, { 0 }},
|
|
||||||
{ "ssub8", 0, 6, { 0 }},
|
|
||||||
{ "shsub8", 0, 6, { 0 }},
|
|
||||||
{ "ssubaddx", 0, 6, { 0 }},
|
|
||||||
{ "strex", 0, 6, { 0 }},
|
|
||||||
{ "strexb", 0, 7, { 0 }},
|
|
||||||
{ "swp", 0, 0, { 0 }},
|
|
||||||
{ "swpb", 0, 0, { 0 }},
|
|
||||||
{ "ssub16", 0, 6, { 0 }},
|
|
||||||
{ "ssat16", 0, 6, { 0 }},
|
|
||||||
{ "shsubaddx", 0, 6, { 0 }},
|
|
||||||
{ "qsubaddx", 0, 6, { 0 }},
|
|
||||||
{ "shaddsubx", 0, 6, { 0 }},
|
|
||||||
{ "shadd8", 0, 6, { 0 }},
|
|
||||||
{ "shadd16", 0, 6, { 0 }},
|
|
||||||
{ "sel", 0, 6, { 0 }},
|
|
||||||
{ "saddsubx", 0, 6, { 0 }},
|
|
||||||
{ "sadd8", 0, 6, { 0 }},
|
|
||||||
{ "sadd16", 0, 6, { 0 }},
|
|
||||||
{ "shsub16", 0, 6, { 0 }},
|
|
||||||
{ "umaal", 0, 6, { 0 }},
|
|
||||||
{ "uxtab16", 0, 6, { 0 }},
|
|
||||||
{ "usubaddx", 0, 6, { 0 }},
|
|
||||||
{ "usub8", 0, 6, { 0 }},
|
|
||||||
{ "usub16", 0, 6, { 0 }},
|
|
||||||
{ "usat16", 0, 6, { 0 }},
|
|
||||||
{ "usada8", 0, 6, { 0 }},
|
|
||||||
{ "uqsubaddx", 0, 6, { 0 }},
|
|
||||||
{ "uqsub8", 0, 6, { 0 }},
|
|
||||||
{ "uqsub16", 0, 6, { 0 }},
|
|
||||||
{ "uqaddsubx", 0, 6, { 0 }},
|
|
||||||
{ "uqadd8", 0, 6, { 0 }},
|
|
||||||
{ "uqadd16", 0, 6, { 0 }},
|
|
||||||
{ "sxtab", 0, 6, { 0 }},
|
|
||||||
{ "uhsubaddx", 0, 6, { 0 }},
|
|
||||||
{ "uhsub8", 0, 6, { 0 }},
|
|
||||||
{ "uhsub16", 0, 6, { 0 }},
|
|
||||||
{ "uhaddsubx", 0, 6, { 0 }},
|
|
||||||
{ "uhadd8", 0, 6, { 0 }},
|
|
||||||
{ "uhadd16", 0, 6, { 0 }},
|
|
||||||
{ "uaddsubx", 0, 6, { 0 }},
|
|
||||||
{ "uadd8", 0, 6, { 0 }},
|
|
||||||
{ "uadd16", 0, 6, { 0 }},
|
|
||||||
{ "sxtah", 0, 6, { 0 }},
|
|
||||||
{ "sxtab16", 0, 6, { 0 }},
|
|
||||||
{ "qadd8", 0, 6, { 0 }},
|
|
||||||
{ "bxj", 0, 5, { 0 }},
|
|
||||||
{ "clz", 0, 3, { 0 }},
|
|
||||||
{ "uxtah", 0, 6, { 0 }},
|
|
||||||
{ "bx", 0, 2, { 0 }},
|
|
||||||
{ "rev", 0, 6, { 0 }},
|
|
||||||
{ "blx", 0, 3, { 0 }},
|
|
||||||
{ "revsh", 0, 6, { 0 }},
|
|
||||||
{ "qadd", 0, 4, { 0 }},
|
|
||||||
{ "qadd16", 0, 6, { 0 }},
|
|
||||||
{ "qaddsubx", 0, 6, { 0 }},
|
|
||||||
{ "ldrex", 0, 0, { 0 }},
|
|
||||||
{ "qdadd", 0, 4, { 0 }},
|
|
||||||
{ "qdsub", 0, 4, { 0 }},
|
|
||||||
{ "qsub", 0, 4, { 0 }},
|
|
||||||
{ "ldrexb", 0, 7, { 0 }},
|
|
||||||
{ "qsub8", 0, 6, { 0 }},
|
|
||||||
{ "qsub16", 0, 6, { 0 }},
|
|
||||||
{ "smuad", 0, 6, { 0 }},
|
|
||||||
{ "smmul", 0, 6, { 0 }},
|
|
||||||
{ "smusd", 0, 6, { 0 }},
|
|
||||||
{ "smlsd", 0, 6, { 0 }},
|
|
||||||
{ "smlsld", 0, 6, { 0 }},
|
|
||||||
{ "smmla", 0, 6, { 0 }},
|
|
||||||
{ "smmls", 0, 6, { 0 }},
|
|
||||||
{ "smlald", 0, 6, { 0 }},
|
|
||||||
{ "smlad", 0, 6, { 0 }},
|
|
||||||
{ "smlaw", 0, 4, { 0 }},
|
|
||||||
{ "smulw", 0, 4, { 0 }},
|
|
||||||
{ "pkhtb", 0, 6, { 0 }},
|
|
||||||
{ "pkhbt", 0, 6, { 0 }},
|
|
||||||
{ "smul", 0, 4, { 0 }},
|
|
||||||
{ "smlal", 0, 4, { 0 }},
|
|
||||||
{ "smla", 0, 4, { 0 }},
|
|
||||||
{ "mcrr", 0, 6, { 0 }},
|
|
||||||
{ "mrrc", 0, 6, { 0 }},
|
|
||||||
{ "cmp", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "tst", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "teq", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "cmn", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "smull", 0, 0, { 0 }},
|
|
||||||
{ "umull", 0, 0, { 0 }},
|
|
||||||
{ "umlal", 0, 0, { 0 }},
|
|
||||||
{ "smlal", 0, 0, { 0 }},
|
|
||||||
{ "mul", 0, 0, { 0 }},
|
|
||||||
{ "mla", 0, 0, { 0 }},
|
|
||||||
{ "ssat", 0, 6, { 0 }},
|
|
||||||
{ "usat", 0, 6, { 0 }},
|
|
||||||
{ "mrs", 0, 0, { 0 }},
|
|
||||||
{ "msr", 0, 0, { 0 }},
|
|
||||||
{ "and", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "bic", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "ldm", 0, 0, { 0 }},
|
|
||||||
{ "eor", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "add", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "rsb", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "rsc", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "sbc", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "adc", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "sub", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "orr", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "mvn", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "mov", 3, 0, { 4, 4, 0x00000001, 7, 7, 0x00000001, 25, 25, 0x00000000 }},
|
|
||||||
{ "stm", 0, 0, { 0 }},
|
|
||||||
{ "ldm", 0, 0, { 0 }},
|
|
||||||
{ "ldrsh", 0, 2, { 0 }},
|
|
||||||
{ "stm", 0, 0, { 0 }},
|
|
||||||
{ "ldm", 0, 0, { 0 }},
|
|
||||||
{ "ldrsb", 0, 2, { 0 }},
|
|
||||||
{ "strd", 0, 4, { 0 }},
|
|
||||||
{ "ldrh", 0, 0, { 0 }},
|
|
||||||
{ "strh", 0, 0, { 0 }},
|
|
||||||
{ "ldrd", 0, 4, { 0 }},
|
|
||||||
{ "strt", 0, 0, { 0 }},
|
|
||||||
{ "strbt", 0, 0, { 0 }},
|
|
||||||
{ "ldrbt", 0, 0, { 0 }},
|
|
||||||
{ "ldrt", 0, 0, { 0 }},
|
|
||||||
{ "mrc", 0, 6, { 0 }},
|
|
||||||
{ "mcr", 0, 0, { 0 }},
|
|
||||||
{ "msr", 0, 0, { 0 }},
|
|
||||||
{ "msr", 0, 0, { 0 }},
|
|
||||||
{ "msr", 0, 0, { 0 }},
|
|
||||||
{ "msr", 0, 0, { 0 }},
|
|
||||||
{ "msr", 0, 0, { 0 }},
|
|
||||||
{ "ldrb", 0, 0, { 0 }},
|
|
||||||
{ "strb", 0, 0, { 0 }},
|
|
||||||
{ "ldr", 0, 0, { 0 }},
|
|
||||||
{ "ldrcond", 1, 0, { 28, 31, 0x0000000e }},
|
|
||||||
{ "str", 0, 0, { 0 }},
|
|
||||||
{ "cdp", 0, 0, { 0 }},
|
|
||||||
{ "stc", 0, 0, { 0 }},
|
|
||||||
{ "ldc", 0, 0, { 0 }},
|
|
||||||
{ "ldrexd", 0, ARMV6K, { 0 }},
|
|
||||||
{ "strexd", 0, ARMV6K, { 0 }},
|
|
||||||
{ "ldrexh", 0, ARMV6K, { 0 }},
|
|
||||||
{ "strexh", 0, ARMV6K, { 0 }},
|
|
||||||
{ "nop", 0, ARMV6K, { 0 }},
|
|
||||||
{ "yield", 0, ARMV6K, { 0 }},
|
|
||||||
{ "wfe", 0, ARMV6K, { 0 }},
|
|
||||||
{ "wfi", 0, ARMV6K, { 0 }},
|
|
||||||
{ "sev", 0, ARMV6K, { 0 }},
|
|
||||||
{ "swi", 0, 0, { 0 }},
|
|
||||||
{ "bbl", 0, 0, { 0 }},
|
|
||||||
|
|
||||||
{ "bl_1_thumb", 0, INVALID, { 0 }}, // Should be table[-4]
|
|
||||||
{ "bl_2_thumb", 0, INVALID, { 0 }}, // Should be located at the end of the table[-3]
|
|
||||||
{ "blx_1_thumb", 0, INVALID, { 0 }}, // Should be located at table[-2]
|
|
||||||
{ "invalid", 0, INVALID, { 0 }}
|
|
||||||
};
|
|
||||||
|
|
||||||
ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) {
|
|
||||||
int n = 0;
|
|
||||||
int base = 0;
|
|
||||||
int instr_slots = sizeof(arm_instruction) / sizeof(InstructionSetEncodingItem);
|
|
||||||
ARMDecodeStatus ret = ARMDecodeStatus::FAILURE;
|
|
||||||
|
|
||||||
for (int i = 0; i < instr_slots; i++) {
|
|
||||||
n = arm_instruction[i].attribute_value;
|
|
||||||
base = 0;
|
|
||||||
|
|
||||||
// 3DS has no VFP3 support
|
|
||||||
if (arm_instruction[i].version == ARMVFP3)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
while (n) {
|
|
||||||
if (arm_instruction[i].content[base + 1] == 31 && arm_instruction[i].content[base] == 0) {
|
|
||||||
// clrex
|
|
||||||
if (instr != arm_instruction[i].content[base + 2]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (BITS(instr, arm_instruction[i].content[base], arm_instruction[i].content[base + 1]) != arm_instruction[i].content[base + 2]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
base += 3;
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All conditions are satisfied.
|
|
||||||
if (n == 0)
|
|
||||||
ret = ARMDecodeStatus::SUCCESS;
|
|
||||||
|
|
||||||
if (ret == ARMDecodeStatus::SUCCESS) {
|
|
||||||
n = arm_exclusion_code[i].attribute_value;
|
|
||||||
if (n != 0) {
|
|
||||||
base = 0;
|
|
||||||
while (n) {
|
|
||||||
if (BITS(instr, arm_exclusion_code[i].content[base], arm_exclusion_code[i].content[base + 1]) != arm_exclusion_code[i].content[base + 2]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
base += 3;
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All conditions are satisfied.
|
|
||||||
if (n == 0)
|
|
||||||
ret = ARMDecodeStatus::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == ARMDecodeStatus::SUCCESS) {
|
|
||||||
*idx = i;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
// Copyright 2012 Michael Kang, 2015 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
enum class ARMDecodeStatus {
|
|
||||||
SUCCESS,
|
|
||||||
FAILURE
|
|
||||||
};
|
|
||||||
|
|
||||||
ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx);
|
|
||||||
|
|
||||||
struct InstructionSetEncodingItem {
|
|
||||||
const char *name;
|
|
||||||
int attribute_value;
|
|
||||||
int version;
|
|
||||||
u32 content[21];
|
|
||||||
};
|
|
||||||
|
|
||||||
// ARM versions
|
|
||||||
enum {
|
|
||||||
INVALID = 0,
|
|
||||||
ARMALL,
|
|
||||||
ARMV4,
|
|
||||||
ARMV4T,
|
|
||||||
ARMV5T,
|
|
||||||
ARMV5TE,
|
|
||||||
ARMV5TEJ,
|
|
||||||
ARMV6,
|
|
||||||
ARM1176JZF_S,
|
|
||||||
ARMVFP2,
|
|
||||||
ARMVFP3,
|
|
||||||
ARMV6K,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const InstructionSetEncodingItem arm_instruction[];
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +0,0 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
struct ARMul_State;
|
|
||||||
|
|
||||||
unsigned InterpreterMainLoop(ARMul_State* state);
|
|
||||||
void InterpreterClearCache();
|
|
|
@ -1,48 +0,0 @@
|
||||||
/* Copyright (C)
|
|
||||||
* 2011 - Michael.Kang blackfin.kang@gmail.com
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the PC is being read, and if so, word-aligns it.
|
|
||||||
* Used with address calculations.
|
|
||||||
*
|
|
||||||
* @param cpu The ARM CPU state instance.
|
|
||||||
* @param Rn The register being read.
|
|
||||||
*
|
|
||||||
* @return If the PC is being read, then the word-aligned PC value is returned.
|
|
||||||
* If the PC is not being read, then the value stored in the register is returned.
|
|
||||||
*/
|
|
||||||
inline u32 CHECK_READ_REG15_WA(const ARMul_State* cpu, int Rn) {
|
|
||||||
return (Rn == 15) ? ((cpu->Reg[15] & ~0x3) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the PC. Used for data processing operations that use the PC.
|
|
||||||
*
|
|
||||||
* @param cpu The ARM CPU state instance.
|
|
||||||
* @param Rn The register being read.
|
|
||||||
*
|
|
||||||
* @return If the PC is being read, then the incremented PC value is returned.
|
|
||||||
* If the PC is not being read, then the values stored in the register is returned.
|
|
||||||
*/
|
|
||||||
inline u32 CHECK_READ_REG15(const ARMul_State* cpu, int Rn) {
|
|
||||||
return (Rn == 15) ? ((cpu->Reg[15] & ~0x1) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn];
|
|
||||||
}
|
|
|
@ -1,402 +0,0 @@
|
||||||
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
|
|
||||||
// ARM instruction, and using the existing ARM simulator.
|
|
||||||
|
|
||||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armsupp.h"
|
|
||||||
|
|
||||||
// Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field,
|
|
||||||
// with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions
|
|
||||||
// allows easier simulation of the special dual BL instruction.
|
|
||||||
|
|
||||||
ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size) {
|
|
||||||
ThumbDecodeStatus valid = ThumbDecodeStatus::UNINITIALIZED;
|
|
||||||
u32 tinstr = GetThumbInstruction(instr, addr);
|
|
||||||
|
|
||||||
*ainstr = 0xDEADC0DE; // Debugging to catch non updates
|
|
||||||
|
|
||||||
switch ((tinstr & 0xF800) >> 11) {
|
|
||||||
case 0: // LSL
|
|
||||||
case 1: // LSR
|
|
||||||
case 2: // ASR
|
|
||||||
*ainstr = 0xE1B00000 // base opcode
|
|
||||||
| ((tinstr & 0x1800) >> (11 - 5)) // shift type
|
|
||||||
|((tinstr & 0x07C0) << (7 - 6)) // imm5
|
|
||||||
|((tinstr & 0x0038) >> 3) // Rs
|
|
||||||
|((tinstr & 0x0007) << 12); // Rd
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // ADD/SUB
|
|
||||||
{
|
|
||||||
static const u32 subset[4] = {
|
|
||||||
0xE0900000, // ADDS Rd,Rs,Rn
|
|
||||||
0xE0500000, // SUBS Rd,Rs,Rn
|
|
||||||
0xE2900000, // ADDS Rd,Rs,#imm3
|
|
||||||
0xE2500000 // SUBS Rd,Rs,#imm3
|
|
||||||
};
|
|
||||||
// It is quicker indexing into a table, than performing switch or conditionals:
|
|
||||||
*ainstr = subset[(tinstr & 0x0600) >> 9] // base opcode
|
|
||||||
|((tinstr & 0x01C0) >> 6) // Rn or imm3
|
|
||||||
|((tinstr & 0x0038) << (16 - 3)) // Rs
|
|
||||||
|((tinstr & 0x0007) << (12 - 0)); // Rd
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4: // MOV
|
|
||||||
case 5: // CMP
|
|
||||||
case 6: // ADD
|
|
||||||
case 7: // SUB
|
|
||||||
{
|
|
||||||
static const u32 subset[4] = {
|
|
||||||
0xE3B00000, // MOVS Rd,#imm8
|
|
||||||
0xE3500000, // CMP Rd,#imm8
|
|
||||||
0xE2900000, // ADDS Rd,Rd,#imm8
|
|
||||||
0xE2500000, // SUBS Rd,Rd,#imm8
|
|
||||||
};
|
|
||||||
|
|
||||||
*ainstr = subset[(tinstr & 0x1800) >> 11] // base opcode
|
|
||||||
|((tinstr & 0x00FF) >> 0) // imm8
|
|
||||||
|((tinstr & 0x0700) << (16 - 8)) // Rn
|
|
||||||
|((tinstr & 0x0700) << (12 - 8)); // Rd
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8: // Arithmetic and high register transfers
|
|
||||||
|
|
||||||
// TODO: Since the subsets for both Format 4 and Format 5 instructions are made up of
|
|
||||||
// different ARM encodings, we could save the following conditional, and just have one
|
|
||||||
// large subset
|
|
||||||
|
|
||||||
if ((tinstr & (1 << 10)) == 0) {
|
|
||||||
enum otype {
|
|
||||||
t_norm,
|
|
||||||
t_shift,
|
|
||||||
t_neg,
|
|
||||||
t_mul
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
u32 opcode;
|
|
||||||
otype type;
|
|
||||||
} subset[16] = {
|
|
||||||
{ 0xE0100000, t_norm }, // ANDS Rd,Rd,Rs
|
|
||||||
{ 0xE0300000, t_norm }, // EORS Rd,Rd,Rs
|
|
||||||
{ 0xE1B00010, t_shift }, // MOVS Rd,Rd,LSL Rs
|
|
||||||
{ 0xE1B00030, t_shift }, // MOVS Rd,Rd,LSR Rs
|
|
||||||
{ 0xE1B00050, t_shift }, // MOVS Rd,Rd,ASR Rs
|
|
||||||
{ 0xE0B00000, t_norm }, // ADCS Rd,Rd,Rs
|
|
||||||
{ 0xE0D00000, t_norm }, // SBCS Rd,Rd,Rs
|
|
||||||
{ 0xE1B00070, t_shift }, // MOVS Rd,Rd,ROR Rs
|
|
||||||
{ 0xE1100000, t_norm }, // TST Rd,Rs
|
|
||||||
{ 0xE2700000, t_neg }, // RSBS Rd,Rs,#0
|
|
||||||
{ 0xE1500000, t_norm }, // CMP Rd,Rs
|
|
||||||
{ 0xE1700000, t_norm }, // CMN Rd,Rs
|
|
||||||
{ 0xE1900000, t_norm }, // ORRS Rd,Rd,Rs
|
|
||||||
{ 0xE0100090, t_mul }, // MULS Rd,Rd,Rs
|
|
||||||
{ 0xE1D00000, t_norm }, // BICS Rd,Rd,Rs
|
|
||||||
{ 0xE1F00000, t_norm } // MVNS Rd,Rs
|
|
||||||
};
|
|
||||||
|
|
||||||
*ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; // base
|
|
||||||
|
|
||||||
switch (subset[(tinstr & 0x03C0) >> 6].type) {
|
|
||||||
case t_norm:
|
|
||||||
*ainstr |= ((tinstr & 0x0007) << 16) // Rn
|
|
||||||
|((tinstr & 0x0007) << 12) // Rd
|
|
||||||
|((tinstr & 0x0038) >> 3); // Rs
|
|
||||||
break;
|
|
||||||
case t_shift:
|
|
||||||
*ainstr |= ((tinstr & 0x0007) << 12) // Rd
|
|
||||||
|((tinstr & 0x0007) >> 0) // Rm
|
|
||||||
|((tinstr & 0x0038) << (8 - 3)); // Rs
|
|
||||||
break;
|
|
||||||
case t_neg:
|
|
||||||
*ainstr |= ((tinstr & 0x0007) << 12) // Rd
|
|
||||||
|((tinstr & 0x0038) << (16 - 3)); // Rn
|
|
||||||
break;
|
|
||||||
case t_mul:
|
|
||||||
*ainstr |= ((tinstr & 0x0007) << 16) // Rd
|
|
||||||
|((tinstr & 0x0007) << 8) // Rs
|
|
||||||
|((tinstr & 0x0038) >> 3); // Rm
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
u32 Rd = ((tinstr & 0x0007) >> 0);
|
|
||||||
u32 Rs = ((tinstr & 0x0078) >> 3);
|
|
||||||
|
|
||||||
if (tinstr & (1 << 7))
|
|
||||||
Rd += 8;
|
|
||||||
|
|
||||||
switch ((tinstr & 0x03C0) >> 6) {
|
|
||||||
case 0x0: // ADD Rd,Rd,Rs
|
|
||||||
case 0x1: // ADD Rd,Rd,Hs
|
|
||||||
case 0x2: // ADD Hd,Hd,Rs
|
|
||||||
case 0x3: // ADD Hd,Hd,Hs
|
|
||||||
*ainstr = 0xE0800000 // base
|
|
||||||
| (Rd << 16) // Rn
|
|
||||||
|(Rd << 12) // Rd
|
|
||||||
|(Rs << 0); // Rm
|
|
||||||
break;
|
|
||||||
case 0x4: // CMP Rd,Rs
|
|
||||||
case 0x5: // CMP Rd,Hs
|
|
||||||
case 0x6: // CMP Hd,Rs
|
|
||||||
case 0x7: // CMP Hd,Hs
|
|
||||||
*ainstr = 0xE1500000 // base
|
|
||||||
| (Rd << 16) // Rn
|
|
||||||
|(Rs << 0); // Rm
|
|
||||||
break;
|
|
||||||
case 0x8: // MOV Rd,Rs
|
|
||||||
case 0x9: // MOV Rd,Hs
|
|
||||||
case 0xA: // MOV Hd,Rs
|
|
||||||
case 0xB: // MOV Hd,Hs
|
|
||||||
*ainstr = 0xE1A00000 // base
|
|
||||||
|(Rd << 12) // Rd
|
|
||||||
|(Rs << 0); // Rm
|
|
||||||
break;
|
|
||||||
case 0xC: // BX Rs
|
|
||||||
case 0xD: // BX Hs
|
|
||||||
*ainstr = 0xE12FFF10 // base
|
|
||||||
| ((tinstr & 0x0078) >> 3); // Rd
|
|
||||||
break;
|
|
||||||
case 0xE: // BLX
|
|
||||||
case 0xF: // BLX
|
|
||||||
*ainstr = 0xE1200030 // base
|
|
||||||
| (Rs << 0); // Rm
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 9: // LDR Rd,[PC,#imm8]
|
|
||||||
*ainstr = 0xE59F0000 // base
|
|
||||||
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|
|
||||||
|((tinstr & 0x00FF) << (2 - 0)); // off8
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 10:
|
|
||||||
case 11:
|
|
||||||
{
|
|
||||||
static const u32 subset[8] = {
|
|
||||||
0xE7800000, // STR Rd,[Rb,Ro]
|
|
||||||
0xE18000B0, // STRH Rd,[Rb,Ro]
|
|
||||||
0xE7C00000, // STRB Rd,[Rb,Ro]
|
|
||||||
0xE19000D0, // LDRSB Rd,[Rb,Ro]
|
|
||||||
0xE7900000, // LDR Rd,[Rb,Ro]
|
|
||||||
0xE19000B0, // LDRH Rd,[Rb,Ro]
|
|
||||||
0xE7D00000, // LDRB Rd,[Rb,Ro]
|
|
||||||
0xE19000F0 // LDRSH Rd,[Rb,Ro]
|
|
||||||
};
|
|
||||||
|
|
||||||
*ainstr = subset[(tinstr & 0xE00) >> 9] // base
|
|
||||||
|((tinstr & 0x0007) << (12 - 0)) // Rd
|
|
||||||
|((tinstr & 0x0038) << (16 - 3)) // Rb
|
|
||||||
|((tinstr & 0x01C0) >> 6); // Ro
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 12: // STR Rd,[Rb,#imm5]
|
|
||||||
case 13: // LDR Rd,[Rb,#imm5]
|
|
||||||
case 14: // STRB Rd,[Rb,#imm5]
|
|
||||||
case 15: // LDRB Rd,[Rb,#imm5]
|
|
||||||
{
|
|
||||||
static const u32 subset[4] = {
|
|
||||||
0xE5800000, // STR Rd,[Rb,#imm5]
|
|
||||||
0xE5900000, // LDR Rd,[Rb,#imm5]
|
|
||||||
0xE5C00000, // STRB Rd,[Rb,#imm5]
|
|
||||||
0xE5D00000 // LDRB Rd,[Rb,#imm5]
|
|
||||||
};
|
|
||||||
// The offset range defends on whether we are transferring a byte or word value:
|
|
||||||
*ainstr = subset[(tinstr & 0x1800) >> 11] // base
|
|
||||||
|((tinstr & 0x0007) << (12 - 0)) // Rd
|
|
||||||
|((tinstr & 0x0038) << (16 - 3)) // Rb
|
|
||||||
|((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); // off5
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 16: // STRH Rd,[Rb,#imm5]
|
|
||||||
case 17: // LDRH Rd,[Rb,#imm5]
|
|
||||||
*ainstr = ((tinstr & (1 << 11)) // base
|
|
||||||
? 0xE1D000B0 // LDRH
|
|
||||||
: 0xE1C000B0) // STRH
|
|
||||||
|((tinstr & 0x0007) << (12 - 0)) // Rd
|
|
||||||
|((tinstr & 0x0038) << (16 - 3)) // Rb
|
|
||||||
|((tinstr & 0x01C0) >> (6 - 1)) // off5, low nibble
|
|
||||||
|((tinstr & 0x0600) >> (9 - 8)); // off5, high nibble
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 18: // STR Rd,[SP,#imm8]
|
|
||||||
case 19: // LDR Rd,[SP,#imm8]
|
|
||||||
*ainstr = ((tinstr & (1 << 11)) // base
|
|
||||||
? 0xE59D0000 // LDR
|
|
||||||
: 0xE58D0000) // STR
|
|
||||||
|((tinstr & 0x0700) << (12 - 8)) // Rd
|
|
||||||
|((tinstr & 0x00FF) << 2); // off8
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 20: // ADD Rd,PC,#imm8
|
|
||||||
case 21: // ADD Rd,SP,#imm8
|
|
||||||
|
|
||||||
if ((tinstr & (1 << 11)) == 0) {
|
|
||||||
|
|
||||||
// NOTE: The PC value used here should by word aligned. We encode shift-left-by-2 in the
|
|
||||||
// rotate immediate field, so no shift of off8 is needed.
|
|
||||||
|
|
||||||
*ainstr = 0xE28F0F00 // base
|
|
||||||
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|
|
||||||
|(tinstr & 0x00FF); // off8
|
|
||||||
} else {
|
|
||||||
// We encode shift-left-by-2 in the rotate immediate field, so no shift of off8 is needed.
|
|
||||||
*ainstr = 0xE28D0F00 // base
|
|
||||||
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|
|
||||||
|(tinstr & 0x00FF); // off8
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 22:
|
|
||||||
case 23:
|
|
||||||
if ((tinstr & 0x0F00) == 0x0000) {
|
|
||||||
// NOTE: The instruction contains a shift left of 2 equivalent (implemented as ROR #30):
|
|
||||||
*ainstr = ((tinstr & (1 << 7)) // base
|
|
||||||
? 0xE24DDF00 // SUB
|
|
||||||
: 0xE28DDF00) // ADD
|
|
||||||
|(tinstr & 0x007F); // off7
|
|
||||||
} else if ((tinstr & 0x0F00) == 0x0e00) {
|
|
||||||
// BKPT
|
|
||||||
*ainstr = 0xEF000000 // base
|
|
||||||
| BITS(tinstr, 0, 3) // imm4 field;
|
|
||||||
| (BITS(tinstr, 4, 7) << 8); // beginning 4 bits of imm12
|
|
||||||
} else if ((tinstr & 0x0F00) == 0x0200) {
|
|
||||||
static const u32 subset[4] = {
|
|
||||||
0xE6BF0070, // SXTH
|
|
||||||
0xE6AF0070, // SXTB
|
|
||||||
0xE6FF0070, // UXTH
|
|
||||||
0xE6EF0070, // UXTB
|
|
||||||
};
|
|
||||||
|
|
||||||
*ainstr = subset[BITS(tinstr, 6, 7)] // base
|
|
||||||
| (BITS(tinstr, 0, 2) << 12) // Rd
|
|
||||||
| BITS(tinstr, 3, 5); // Rm
|
|
||||||
} else if ((tinstr & 0x0F00) == 0x600) {
|
|
||||||
if (BIT(tinstr, 5) == 0) {
|
|
||||||
// SETEND
|
|
||||||
*ainstr = 0xF1010000 // base
|
|
||||||
| (BIT(tinstr, 3) << 9); // endian specifier
|
|
||||||
} else {
|
|
||||||
// CPS
|
|
||||||
*ainstr = 0xF1080000 // base
|
|
||||||
| (BIT(tinstr, 0) << 6) // fiq bit
|
|
||||||
| (BIT(tinstr, 1) << 7) // irq bit
|
|
||||||
| (BIT(tinstr, 2) << 8) // abort bit
|
|
||||||
| (BIT(tinstr, 4) << 18); // enable bit
|
|
||||||
}
|
|
||||||
} else if ((tinstr & 0x0F00) == 0x0a00) {
|
|
||||||
static const u32 subset[4] = {
|
|
||||||
0xE6BF0F30, // REV
|
|
||||||
0xE6BF0FB0, // REV16
|
|
||||||
0, // undefined
|
|
||||||
0xE6FF0FB0, // REVSH
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t subset_index = BITS(tinstr, 6, 7);
|
|
||||||
|
|
||||||
if (subset_index == 2) {
|
|
||||||
valid = ThumbDecodeStatus::UNDEFINED;
|
|
||||||
} else {
|
|
||||||
*ainstr = subset[subset_index] // base
|
|
||||||
| (BITS(tinstr, 0, 2) << 12) // Rd
|
|
||||||
| BITS(tinstr, 3, 5); // Rm
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
static const u32 subset[4] = {
|
|
||||||
0xE92D0000, // STMDB sp!,{rlist}
|
|
||||||
0xE92D4000, // STMDB sp!,{rlist,lr}
|
|
||||||
0xE8BD0000, // LDMIA sp!,{rlist}
|
|
||||||
0xE8BD8000 // LDMIA sp!,{rlist,pc}
|
|
||||||
};
|
|
||||||
*ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] // base
|
|
||||||
|(tinstr & 0x00FF); // mask8
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 24: // STMIA
|
|
||||||
case 25: // LDMIA
|
|
||||||
if (tinstr & (1 << 11))
|
|
||||||
{
|
|
||||||
unsigned int base = 0xE8900000;
|
|
||||||
unsigned int rn = BITS(tinstr, 8, 10);
|
|
||||||
|
|
||||||
// Writeback
|
|
||||||
if ((tinstr & (1 << rn)) == 0)
|
|
||||||
base |= (1 << 21);
|
|
||||||
|
|
||||||
*ainstr = base // base (LDMIA)
|
|
||||||
| (rn << 16) // Rn
|
|
||||||
| (tinstr & 0x00FF); // Register list
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*ainstr = 0xE8A00000 // base (STMIA)
|
|
||||||
| (BITS(tinstr, 8, 10) << 16) // Rn
|
|
||||||
| (tinstr & 0x00FF); // Register list
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 26: // Bcc
|
|
||||||
case 27: // Bcc/SWI
|
|
||||||
if ((tinstr & 0x0F00) == 0x0F00) {
|
|
||||||
// Format 17 : SWI
|
|
||||||
*ainstr = 0xEF000000;
|
|
||||||
// Breakpoint must be handled specially.
|
|
||||||
if ((tinstr & 0x00FF) == 0x18)
|
|
||||||
*ainstr |= ((tinstr & 0x00FF) << 16);
|
|
||||||
// New breakpoint value. See gdb/arm-tdep.c
|
|
||||||
else if ((tinstr & 0x00FF) == 0xFE)
|
|
||||||
*ainstr |= 0x180000; // base |= BKPT mask
|
|
||||||
else
|
|
||||||
*ainstr |= (tinstr & 0x00FF);
|
|
||||||
} else if ((tinstr & 0x0F00) != 0x0E00)
|
|
||||||
valid = ThumbDecodeStatus::BRANCH;
|
|
||||||
else // UNDEFINED : cc=1110(AL) uses different format
|
|
||||||
valid = ThumbDecodeStatus::UNDEFINED;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 28: // B
|
|
||||||
valid = ThumbDecodeStatus::BRANCH;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 29:
|
|
||||||
if (tinstr & 0x1)
|
|
||||||
valid = ThumbDecodeStatus::UNDEFINED;
|
|
||||||
else
|
|
||||||
valid = ThumbDecodeStatus::BRANCH;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 30: // BL instruction 1
|
|
||||||
|
|
||||||
// There is no single ARM instruction equivalent for this Thumb instruction. To keep the
|
|
||||||
// simulation simple (from the user perspective) we check if the following instruction is
|
|
||||||
// the second half of this BL, and if it is we simulate it immediately
|
|
||||||
|
|
||||||
valid = ThumbDecodeStatus::BRANCH;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 31: // BL instruction 2
|
|
||||||
|
|
||||||
// There is no single ARM instruction equivalent for this instruction. Also, it should only
|
|
||||||
// ever be matched with the fmt19 "BL instruction 1" instruction. However, we do allow the
|
|
||||||
// simulation of it on its own, with undefined results if r14 is not suitably initialised.
|
|
||||||
|
|
||||||
valid = ThumbDecodeStatus::BRANCH;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*inst_size = 2;
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/* Copyright (C)
|
|
||||||
* 2011 - Michael.Kang blackfin.kang@gmail.com
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file arm_dyncom_thumb.h
|
|
||||||
* @brief The thumb dyncom
|
|
||||||
* @author Michael.Kang blackfin.kang@gmail.com
|
|
||||||
* @version 78.77
|
|
||||||
* @date 2011-11-07
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
enum class ThumbDecodeStatus {
|
|
||||||
UNDEFINED, // Undefined Thumb instruction
|
|
||||||
DECODED, // Instruction decoded to ARM equivalent
|
|
||||||
BRANCH, // Thumb branch (already processed)
|
|
||||||
UNINITIALIZED,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Translates a Thumb mode instruction into its ARM equivalent.
|
|
||||||
ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size);
|
|
||||||
|
|
||||||
inline u32 GetThumbInstruction(u32 instr, u32 address) {
|
|
||||||
// Normally you would need to handle instruction endianness,
|
|
||||||
// however, it is fixed to little-endian on the MPCore, so
|
|
||||||
// there's no need to check for this beforehand.
|
|
||||||
if ((address & 0x3) != 0)
|
|
||||||
return instr >> 16;
|
|
||||||
|
|
||||||
return instr & 0xFFFF;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,499 +0,0 @@
|
||||||
struct ARMul_State;
|
|
||||||
typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper);
|
|
||||||
|
|
||||||
enum class TransExtData {
|
|
||||||
COND = (1 << 0),
|
|
||||||
NON_BRANCH = (1 << 1),
|
|
||||||
DIRECT_BRANCH = (1 << 2),
|
|
||||||
INDIRECT_BRANCH = (1 << 3),
|
|
||||||
CALL = (1 << 4),
|
|
||||||
RET = (1 << 5),
|
|
||||||
END_OF_PAGE = (1 << 6),
|
|
||||||
THUMB = (1 << 7),
|
|
||||||
SINGLE_STEP = (1 << 8)
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(disable:4200)
|
|
||||||
#endif
|
|
||||||
struct arm_inst {
|
|
||||||
unsigned int idx;
|
|
||||||
unsigned int cond;
|
|
||||||
TransExtData br;
|
|
||||||
#ifdef __GNUC__
|
|
||||||
__extension__
|
|
||||||
#endif
|
|
||||||
char component[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct generic_arm_inst {
|
|
||||||
u32 Ra;
|
|
||||||
u32 Rm;
|
|
||||||
u32 Rn;
|
|
||||||
u32 Rd;
|
|
||||||
u8 op1;
|
|
||||||
u8 op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct adc_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct add_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct orr_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct and_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct eor_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bbl_inst {
|
|
||||||
unsigned int L;
|
|
||||||
int signed_immed_24;
|
|
||||||
unsigned int next_addr;
|
|
||||||
unsigned int jmp_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bx_inst {
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct blx_inst {
|
|
||||||
union {
|
|
||||||
s32 signed_immed_24;
|
|
||||||
u32 Rm;
|
|
||||||
} val;
|
|
||||||
unsigned int inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct clz_inst {
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rd;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cps_inst {
|
|
||||||
unsigned int imod0;
|
|
||||||
unsigned int imod1;
|
|
||||||
unsigned int mmod;
|
|
||||||
unsigned int A, I, F;
|
|
||||||
unsigned int mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct clrex_inst {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cpy_inst {
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rd;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bic_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sub_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct tst_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cmn_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct teq_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct stm_inst {
|
|
||||||
unsigned int inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bkpt_inst {
|
|
||||||
u32 imm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct stc_inst {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ldc_inst {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct swi_inst {
|
|
||||||
unsigned int num;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cmp_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mov_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mvn_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rev_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int op1;
|
|
||||||
unsigned int op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rsb_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rsc_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sbc_inst {
|
|
||||||
unsigned int I;
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int shifter_operand;
|
|
||||||
shtop_fp_t shtop_func;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mul_inst {
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct smul_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int x;
|
|
||||||
unsigned int y;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct umull_inst {
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int RdHi;
|
|
||||||
unsigned int RdLo;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct smlad_inst {
|
|
||||||
unsigned int m;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Ra;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int op1;
|
|
||||||
unsigned int op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct smla_inst {
|
|
||||||
unsigned int x;
|
|
||||||
unsigned int y;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int Rn;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct smlalxy_inst {
|
|
||||||
unsigned int x;
|
|
||||||
unsigned int y;
|
|
||||||
unsigned int RdLo;
|
|
||||||
unsigned int RdHi;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rn;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ssat_inst {
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int imm5;
|
|
||||||
unsigned int sat_imm;
|
|
||||||
unsigned int shift_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct umaal_inst {
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int RdHi;
|
|
||||||
unsigned int RdLo;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct umlal_inst {
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int RdHi;
|
|
||||||
unsigned int RdLo;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct smlal_inst {
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int RdHi;
|
|
||||||
unsigned int RdLo;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct smlald_inst {
|
|
||||||
unsigned int RdLo;
|
|
||||||
unsigned int RdHi;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int swap;
|
|
||||||
unsigned int op1;
|
|
||||||
unsigned int op2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mla_inst {
|
|
||||||
unsigned int S;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rs;
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mrc_inst {
|
|
||||||
unsigned int opcode_1;
|
|
||||||
unsigned int opcode_2;
|
|
||||||
unsigned int cp_num;
|
|
||||||
unsigned int crn;
|
|
||||||
unsigned int crm;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mcr_inst {
|
|
||||||
unsigned int opcode_1;
|
|
||||||
unsigned int opcode_2;
|
|
||||||
unsigned int cp_num;
|
|
||||||
unsigned int crn;
|
|
||||||
unsigned int crm;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mcrr_inst {
|
|
||||||
unsigned int opcode_1;
|
|
||||||
unsigned int cp_num;
|
|
||||||
unsigned int crm;
|
|
||||||
unsigned int rt;
|
|
||||||
unsigned int rt2;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mrs_inst {
|
|
||||||
unsigned int R;
|
|
||||||
unsigned int Rd;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct msr_inst {
|
|
||||||
unsigned int field_mask;
|
|
||||||
unsigned int R;
|
|
||||||
unsigned int inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pld_inst {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sxtb_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int rotate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sxtab_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned rotate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sxtah_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int rotate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sxth_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int rotate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uxtab_inst {
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int rotate;
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uxtah_inst {
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int rotate;
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uxth_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int rotate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cdp_inst {
|
|
||||||
unsigned int opcode_1;
|
|
||||||
unsigned int CRn;
|
|
||||||
unsigned int CRd;
|
|
||||||
unsigned int cp_num;
|
|
||||||
unsigned int opcode_2;
|
|
||||||
unsigned int CRm;
|
|
||||||
unsigned int inst;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uxtb_inst {
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int rotate;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct swp_inst {
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned int Rm;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct setend_inst {
|
|
||||||
unsigned int set_bigend;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct b_2_thumb {
|
|
||||||
unsigned int imm;
|
|
||||||
};
|
|
||||||
struct b_cond_thumb {
|
|
||||||
unsigned int imm;
|
|
||||||
unsigned int cond;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bl_1_thumb {
|
|
||||||
unsigned int imm;
|
|
||||||
};
|
|
||||||
struct bl_2_thumb {
|
|
||||||
unsigned int imm;
|
|
||||||
};
|
|
||||||
struct blx_1_thumb {
|
|
||||||
unsigned int imm;
|
|
||||||
unsigned int instr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pkh_inst {
|
|
||||||
unsigned int Rm;
|
|
||||||
unsigned int Rn;
|
|
||||||
unsigned int Rd;
|
|
||||||
unsigned char imm;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Floating point VFPv3 structures
|
|
||||||
#define VFP_INTERPRETER_STRUCT
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp"
|
|
||||||
#undef VFP_INTERPRETER_STRUCT
|
|
||||||
|
|
||||||
typedef void (*get_addr_fp_t)(ARMul_State *cpu, unsigned int inst, unsigned int &virt_addr);
|
|
||||||
|
|
||||||
struct ldst_inst {
|
|
||||||
unsigned int inst;
|
|
||||||
get_addr_fp_t get_addr;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef arm_inst* ARM_INST_PTR;
|
|
||||||
typedef ARM_INST_PTR (*transop_fp_t)(unsigned int, int);
|
|
||||||
|
|
||||||
extern const transop_fp_t arm_instruction_trans[];
|
|
||||||
extern const size_t arm_instruction_trans_len;
|
|
||||||
|
|
||||||
#define TRANS_CACHE_SIZE (64 * 1024 * 2000)
|
|
||||||
extern char trans_cache_buf[TRANS_CACHE_SIZE];
|
|
||||||
extern size_t trans_cache_buf_top;
|
|
|
@ -1,187 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
enum {
|
|
||||||
R0 = 0,
|
|
||||||
R1,
|
|
||||||
R2,
|
|
||||||
R3,
|
|
||||||
R4,
|
|
||||||
R5,
|
|
||||||
R6,
|
|
||||||
R7,
|
|
||||||
R8,
|
|
||||||
R9,
|
|
||||||
R10,
|
|
||||||
R11,
|
|
||||||
R12,
|
|
||||||
R13,
|
|
||||||
LR,
|
|
||||||
R15, //PC,
|
|
||||||
CPSR_REG,
|
|
||||||
SPSR_REG,
|
|
||||||
|
|
||||||
PHYS_PC,
|
|
||||||
R13_USR,
|
|
||||||
R14_USR,
|
|
||||||
R13_SVC,
|
|
||||||
R14_SVC,
|
|
||||||
R13_ABORT,
|
|
||||||
R14_ABORT,
|
|
||||||
R13_UNDEF,
|
|
||||||
R14_UNDEF,
|
|
||||||
R13_IRQ,
|
|
||||||
R14_IRQ,
|
|
||||||
R8_FIRQ,
|
|
||||||
R9_FIRQ,
|
|
||||||
R10_FIRQ,
|
|
||||||
R11_FIRQ,
|
|
||||||
R12_FIRQ,
|
|
||||||
R13_FIRQ,
|
|
||||||
R14_FIRQ,
|
|
||||||
SPSR_INVALID1,
|
|
||||||
SPSR_INVALID2,
|
|
||||||
SPSR_SVC,
|
|
||||||
SPSR_ABORT,
|
|
||||||
SPSR_UNDEF,
|
|
||||||
SPSR_IRQ,
|
|
||||||
SPSR_FIRQ,
|
|
||||||
MODE_REG, /* That is the cpsr[4 : 0], just for calculation easily */
|
|
||||||
BANK_REG,
|
|
||||||
EXCLUSIVE_TAG,
|
|
||||||
EXCLUSIVE_STATE,
|
|
||||||
EXCLUSIVE_RESULT,
|
|
||||||
|
|
||||||
MAX_REG_NUM,
|
|
||||||
};
|
|
||||||
|
|
||||||
// VFP system registers
|
|
||||||
enum VFPSystemRegister {
|
|
||||||
VFP_FPSID,
|
|
||||||
VFP_FPSCR,
|
|
||||||
VFP_FPEXC,
|
|
||||||
VFP_FPINST,
|
|
||||||
VFP_FPINST2,
|
|
||||||
VFP_MVFR0,
|
|
||||||
VFP_MVFR1,
|
|
||||||
|
|
||||||
// Not an actual register.
|
|
||||||
// All VFP system registers should be defined above this.
|
|
||||||
VFP_SYSTEM_REGISTER_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
enum CP15Register {
|
|
||||||
// c0 - Information registers
|
|
||||||
CP15_MAIN_ID,
|
|
||||||
CP15_CACHE_TYPE,
|
|
||||||
CP15_TCM_STATUS,
|
|
||||||
CP15_TLB_TYPE,
|
|
||||||
CP15_CPU_ID,
|
|
||||||
CP15_PROCESSOR_FEATURE_0,
|
|
||||||
CP15_PROCESSOR_FEATURE_1,
|
|
||||||
CP15_DEBUG_FEATURE_0,
|
|
||||||
CP15_AUXILIARY_FEATURE_0,
|
|
||||||
CP15_MEMORY_MODEL_FEATURE_0,
|
|
||||||
CP15_MEMORY_MODEL_FEATURE_1,
|
|
||||||
CP15_MEMORY_MODEL_FEATURE_2,
|
|
||||||
CP15_MEMORY_MODEL_FEATURE_3,
|
|
||||||
CP15_ISA_FEATURE_0,
|
|
||||||
CP15_ISA_FEATURE_1,
|
|
||||||
CP15_ISA_FEATURE_2,
|
|
||||||
CP15_ISA_FEATURE_3,
|
|
||||||
CP15_ISA_FEATURE_4,
|
|
||||||
|
|
||||||
// c1 - Control registers
|
|
||||||
CP15_CONTROL,
|
|
||||||
CP15_AUXILIARY_CONTROL,
|
|
||||||
CP15_COPROCESSOR_ACCESS_CONTROL,
|
|
||||||
|
|
||||||
// c2 - Translation table registers
|
|
||||||
CP15_TRANSLATION_BASE_TABLE_0,
|
|
||||||
CP15_TRANSLATION_BASE_TABLE_1,
|
|
||||||
CP15_TRANSLATION_BASE_CONTROL,
|
|
||||||
CP15_DOMAIN_ACCESS_CONTROL,
|
|
||||||
CP15_RESERVED,
|
|
||||||
|
|
||||||
// c5 - Fault status registers
|
|
||||||
CP15_FAULT_STATUS,
|
|
||||||
CP15_INSTR_FAULT_STATUS,
|
|
||||||
CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
|
|
||||||
CP15_INST_FSR,
|
|
||||||
|
|
||||||
// c6 - Fault Address registers
|
|
||||||
CP15_FAULT_ADDRESS,
|
|
||||||
CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
|
|
||||||
CP15_WFAR,
|
|
||||||
CP15_IFAR,
|
|
||||||
|
|
||||||
// c7 - Cache operation registers
|
|
||||||
CP15_WAIT_FOR_INTERRUPT,
|
|
||||||
CP15_PHYS_ADDRESS,
|
|
||||||
CP15_INVALIDATE_INSTR_CACHE,
|
|
||||||
CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
|
|
||||||
CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
|
|
||||||
CP15_FLUSH_PREFETCH_BUFFER,
|
|
||||||
CP15_FLUSH_BRANCH_TARGET_CACHE,
|
|
||||||
CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
|
|
||||||
CP15_INVALIDATE_DATA_CACHE,
|
|
||||||
CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
|
|
||||||
CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
|
|
||||||
CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
|
|
||||||
CP15_CLEAN_DATA_CACHE,
|
|
||||||
CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
|
|
||||||
CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
|
|
||||||
CP15_DATA_SYNC_BARRIER,
|
|
||||||
CP15_DATA_MEMORY_BARRIER,
|
|
||||||
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
|
|
||||||
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
|
|
||||||
CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
|
|
||||||
|
|
||||||
// c8 - TLB operations
|
|
||||||
CP15_INVALIDATE_ITLB,
|
|
||||||
CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
|
|
||||||
CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
|
|
||||||
CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
|
|
||||||
CP15_INVALIDATE_DTLB,
|
|
||||||
CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
|
|
||||||
CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
|
|
||||||
CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
|
|
||||||
CP15_INVALIDATE_UTLB,
|
|
||||||
CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
|
|
||||||
CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
|
|
||||||
CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
|
|
||||||
|
|
||||||
// c9 - Data cache lockdown register
|
|
||||||
CP15_DATA_CACHE_LOCKDOWN,
|
|
||||||
|
|
||||||
// c10 - TLB/Memory map registers
|
|
||||||
CP15_TLB_LOCKDOWN,
|
|
||||||
CP15_PRIMARY_REGION_REMAP,
|
|
||||||
CP15_NORMAL_REGION_REMAP,
|
|
||||||
|
|
||||||
// c13 - Thread related registers
|
|
||||||
CP15_PID,
|
|
||||||
CP15_CONTEXT_ID,
|
|
||||||
CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
|
|
||||||
CP15_THREAD_URO, // Thread ID register - User Read Only (Privileged R/W)
|
|
||||||
CP15_THREAD_PRW, // Thread ID register - Privileged R/W only.
|
|
||||||
|
|
||||||
// c15 - Performance and TLB lockdown registers
|
|
||||||
CP15_PERFORMANCE_MONITOR_CONTROL,
|
|
||||||
CP15_CYCLE_COUNTER,
|
|
||||||
CP15_COUNT_0,
|
|
||||||
CP15_COUNT_1,
|
|
||||||
CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
|
|
||||||
CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
|
|
||||||
CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
|
|
||||||
CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
|
|
||||||
CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
|
|
||||||
CP15_TLB_DEBUG_CONTROL,
|
|
||||||
|
|
||||||
// Skyeye defined
|
|
||||||
CP15_TLB_FAULT_ADDR,
|
|
||||||
CP15_TLB_FAULT_STATUS,
|
|
||||||
|
|
||||||
// Not an actual register.
|
|
||||||
// All registers should be defined above this.
|
|
||||||
CP15_REGISTER_COUNT,
|
|
||||||
};
|
|
|
@ -1,693 +0,0 @@
|
||||||
// Copyright 2015 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(disable : 4244)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/bit_util.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
|
||||||
|
|
||||||
ARMul_State::ARMul_State(PrivilegeMode initial_mode)
|
|
||||||
{
|
|
||||||
Reset();
|
|
||||||
ChangePrivilegeMode(initial_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMul_State::ChangePrivilegeMode(u32 new_mode)
|
|
||||||
{
|
|
||||||
if (Mode == new_mode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (new_mode != USERBANK) {
|
|
||||||
switch (Mode) {
|
|
||||||
case SYSTEM32MODE: // Shares registers with user mode
|
|
||||||
case USER32MODE:
|
|
||||||
Reg_usr[0] = Reg[13];
|
|
||||||
Reg_usr[1] = Reg[14];
|
|
||||||
break;
|
|
||||||
case IRQ32MODE:
|
|
||||||
Reg_irq[0] = Reg[13];
|
|
||||||
Reg_irq[1] = Reg[14];
|
|
||||||
Spsr[IRQBANK] = Spsr_copy;
|
|
||||||
break;
|
|
||||||
case SVC32MODE:
|
|
||||||
Reg_svc[0] = Reg[13];
|
|
||||||
Reg_svc[1] = Reg[14];
|
|
||||||
Spsr[SVCBANK] = Spsr_copy;
|
|
||||||
break;
|
|
||||||
case ABORT32MODE:
|
|
||||||
Reg_abort[0] = Reg[13];
|
|
||||||
Reg_abort[1] = Reg[14];
|
|
||||||
Spsr[ABORTBANK] = Spsr_copy;
|
|
||||||
break;
|
|
||||||
case UNDEF32MODE:
|
|
||||||
Reg_undef[0] = Reg[13];
|
|
||||||
Reg_undef[1] = Reg[14];
|
|
||||||
Spsr[UNDEFBANK] = Spsr_copy;
|
|
||||||
break;
|
|
||||||
case FIQ32MODE:
|
|
||||||
std::copy(Reg.begin() + 8, Reg.end() - 1, Reg_firq.begin());
|
|
||||||
Spsr[FIQBANK] = Spsr_copy;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (new_mode) {
|
|
||||||
case USER32MODE:
|
|
||||||
Reg[13] = Reg_usr[0];
|
|
||||||
Reg[14] = Reg_usr[1];
|
|
||||||
Bank = USERBANK;
|
|
||||||
break;
|
|
||||||
case IRQ32MODE:
|
|
||||||
Reg[13] = Reg_irq[0];
|
|
||||||
Reg[14] = Reg_irq[1];
|
|
||||||
Spsr_copy = Spsr[IRQBANK];
|
|
||||||
Bank = IRQBANK;
|
|
||||||
break;
|
|
||||||
case SVC32MODE:
|
|
||||||
Reg[13] = Reg_svc[0];
|
|
||||||
Reg[14] = Reg_svc[1];
|
|
||||||
Spsr_copy = Spsr[SVCBANK];
|
|
||||||
Bank = SVCBANK;
|
|
||||||
break;
|
|
||||||
case ABORT32MODE:
|
|
||||||
Reg[13] = Reg_abort[0];
|
|
||||||
Reg[14] = Reg_abort[1];
|
|
||||||
Spsr_copy = Spsr[ABORTBANK];
|
|
||||||
Bank = ABORTBANK;
|
|
||||||
break;
|
|
||||||
case UNDEF32MODE:
|
|
||||||
Reg[13] = Reg_undef[0];
|
|
||||||
Reg[14] = Reg_undef[1];
|
|
||||||
Spsr_copy = Spsr[UNDEFBANK];
|
|
||||||
Bank = UNDEFBANK;
|
|
||||||
break;
|
|
||||||
case FIQ32MODE:
|
|
||||||
std::copy(Reg_firq.begin(), Reg_firq.end(), Reg.begin() + 8);
|
|
||||||
Spsr_copy = Spsr[FIQBANK];
|
|
||||||
Bank = FIQBANK;
|
|
||||||
break;
|
|
||||||
case SYSTEM32MODE: // Shares registers with user mode.
|
|
||||||
Reg[13] = Reg_usr[0];
|
|
||||||
Reg[14] = Reg_usr[1];
|
|
||||||
Bank = SYSTEMBANK;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the mode bits in the APSR
|
|
||||||
Cpsr = (Cpsr & ~Mode) | new_mode;
|
|
||||||
Mode = new_mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Performs a reset
|
|
||||||
void ARMul_State::Reset()
|
|
||||||
{
|
|
||||||
VFPInit(this);
|
|
||||||
|
|
||||||
// Set stack pointer to the top of the stack
|
|
||||||
Reg[13] = 0x10000000;
|
|
||||||
Reg[15] = 0;
|
|
||||||
|
|
||||||
Cpsr = INTBITS | SVC32MODE;
|
|
||||||
Mode = SVC32MODE;
|
|
||||||
Bank = SVCBANK;
|
|
||||||
|
|
||||||
ResetMPCoreCP15Registers();
|
|
||||||
|
|
||||||
NresetSig = HIGH;
|
|
||||||
NfiqSig = HIGH;
|
|
||||||
NirqSig = HIGH;
|
|
||||||
NtransSig = (Mode & 3) ? HIGH : LOW;
|
|
||||||
abortSig = LOW;
|
|
||||||
|
|
||||||
NumInstrs = 0;
|
|
||||||
Emulate = RUN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resets certain MPCore CP15 values to their ARM-defined reset values.
|
|
||||||
void ARMul_State::ResetMPCoreCP15Registers()
|
|
||||||
{
|
|
||||||
// c0
|
|
||||||
CP15[CP15_MAIN_ID] = 0x410FB024;
|
|
||||||
CP15[CP15_TLB_TYPE] = 0x00000800;
|
|
||||||
CP15[CP15_PROCESSOR_FEATURE_0] = 0x00000111;
|
|
||||||
CP15[CP15_PROCESSOR_FEATURE_1] = 0x00000001;
|
|
||||||
CP15[CP15_DEBUG_FEATURE_0] = 0x00000002;
|
|
||||||
CP15[CP15_MEMORY_MODEL_FEATURE_0] = 0x01100103;
|
|
||||||
CP15[CP15_MEMORY_MODEL_FEATURE_1] = 0x10020302;
|
|
||||||
CP15[CP15_MEMORY_MODEL_FEATURE_2] = 0x01222000;
|
|
||||||
CP15[CP15_MEMORY_MODEL_FEATURE_3] = 0x00000000;
|
|
||||||
CP15[CP15_ISA_FEATURE_0] = 0x00100011;
|
|
||||||
CP15[CP15_ISA_FEATURE_1] = 0x12002111;
|
|
||||||
CP15[CP15_ISA_FEATURE_2] = 0x11221011;
|
|
||||||
CP15[CP15_ISA_FEATURE_3] = 0x01102131;
|
|
||||||
CP15[CP15_ISA_FEATURE_4] = 0x00000141;
|
|
||||||
|
|
||||||
// c1
|
|
||||||
CP15[CP15_CONTROL] = 0x00054078;
|
|
||||||
CP15[CP15_AUXILIARY_CONTROL] = 0x0000000F;
|
|
||||||
CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = 0x00000000;
|
|
||||||
|
|
||||||
// c2
|
|
||||||
CP15[CP15_TRANSLATION_BASE_TABLE_0] = 0x00000000;
|
|
||||||
CP15[CP15_TRANSLATION_BASE_TABLE_1] = 0x00000000;
|
|
||||||
CP15[CP15_TRANSLATION_BASE_CONTROL] = 0x00000000;
|
|
||||||
|
|
||||||
// c3
|
|
||||||
CP15[CP15_DOMAIN_ACCESS_CONTROL] = 0x00000000;
|
|
||||||
|
|
||||||
// c7
|
|
||||||
CP15[CP15_PHYS_ADDRESS] = 0x00000000;
|
|
||||||
|
|
||||||
// c9
|
|
||||||
CP15[CP15_DATA_CACHE_LOCKDOWN] = 0xFFFFFFF0;
|
|
||||||
|
|
||||||
// c10
|
|
||||||
CP15[CP15_TLB_LOCKDOWN] = 0x00000000;
|
|
||||||
CP15[CP15_PRIMARY_REGION_REMAP] = 0x00098AA4;
|
|
||||||
CP15[CP15_NORMAL_REGION_REMAP] = 0x44E048E0;
|
|
||||||
|
|
||||||
// c13
|
|
||||||
CP15[CP15_PID] = 0x00000000;
|
|
||||||
CP15[CP15_CONTEXT_ID] = 0x00000000;
|
|
||||||
CP15[CP15_THREAD_UPRW] = 0x00000000;
|
|
||||||
CP15[CP15_THREAD_URO] = 0x00000000;
|
|
||||||
CP15[CP15_THREAD_PRW] = 0x00000000;
|
|
||||||
|
|
||||||
// c15
|
|
||||||
CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = 0x00000000;
|
|
||||||
CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = 0x00000000;
|
|
||||||
CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = 0x00000000;
|
|
||||||
CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = 0x00000000;
|
|
||||||
CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static void CheckMemoryBreakpoint(u32 address, GDBStub::BreakpointType type)
|
|
||||||
//{
|
|
||||||
// if (GDBStub::g_server_enabled && GDBStub::CheckBreakpoint(address, type)) {
|
|
||||||
// LOG_DEBUG(Debug, "Found memory breakpoint @ %08x", address);
|
|
||||||
// GDBStub::Break(true);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
u8 ARMul_State::ReadMemory8(u32 address) const
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
|
||||||
|
|
||||||
return user_callbacks->MemoryRead8(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 ARMul_State::ReadMemory16(u32 address) const
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
|
||||||
|
|
||||||
u16 data = user_callbacks->MemoryRead16(address);
|
|
||||||
|
|
||||||
if (InBigEndianMode())
|
|
||||||
data = Dynarmic::Common::Swap16(data);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ARMul_State::ReadMemory32(u32 address) const
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
|
||||||
|
|
||||||
u32 data = user_callbacks->MemoryRead32(address);
|
|
||||||
|
|
||||||
if (InBigEndianMode())
|
|
||||||
data = Dynarmic::Common::Swap32(data);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 ARMul_State::ReadMemory64(u32 address) const
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
|
|
||||||
|
|
||||||
u64 data = user_callbacks->MemoryRead64(address);
|
|
||||||
|
|
||||||
if (InBigEndianMode())
|
|
||||||
data = Dynarmic::Common::Swap64(data);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMul_State::WriteMemory8(u32 address, u8 data)
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
|
|
||||||
|
|
||||||
user_callbacks->MemoryWrite8(address, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMul_State::WriteMemory16(u32 address, u16 data)
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
|
|
||||||
|
|
||||||
if (InBigEndianMode())
|
|
||||||
data = Dynarmic::Common::Swap16(data);
|
|
||||||
|
|
||||||
user_callbacks->MemoryWrite16(address, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMul_State::WriteMemory32(u32 address, u32 data)
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
|
|
||||||
|
|
||||||
if (InBigEndianMode())
|
|
||||||
data = Dynarmic::Common::Swap32(data);
|
|
||||||
|
|
||||||
user_callbacks->MemoryWrite32(address, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ARMul_State::WriteMemory64(u32 address, u64 data)
|
|
||||||
{
|
|
||||||
// CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
|
|
||||||
|
|
||||||
if (InBigEndianMode())
|
|
||||||
data = Dynarmic::Common::Swap64(data);
|
|
||||||
|
|
||||||
user_callbacks->MemoryWrite64(address, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Reads from the CP15 registers. Used with implementation of the MRC instruction.
|
|
||||||
// Note that since the 3DS does not have the hypervisor extensions, these registers
|
|
||||||
// are not implemented.
|
|
||||||
u32 ARMul_State::ReadCP15Register(u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) const
|
|
||||||
{
|
|
||||||
// Unprivileged registers
|
|
||||||
if (crn == 13 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
return CP15[CP15_THREAD_UPRW];
|
|
||||||
|
|
||||||
if (opcode_2 == 3)
|
|
||||||
return CP15[CP15_THREAD_URO];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InAPrivilegedMode())
|
|
||||||
{
|
|
||||||
if (crn == 0 && opcode_1 == 0)
|
|
||||||
{
|
|
||||||
if (crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_MAIN_ID];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_CACHE_TYPE];
|
|
||||||
|
|
||||||
if (opcode_2 == 3)
|
|
||||||
return CP15[CP15_TLB_TYPE];
|
|
||||||
|
|
||||||
if (opcode_2 == 5)
|
|
||||||
return CP15[CP15_CPU_ID];
|
|
||||||
}
|
|
||||||
else if (crm == 1)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_PROCESSOR_FEATURE_0];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_PROCESSOR_FEATURE_1];
|
|
||||||
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
return CP15[CP15_DEBUG_FEATURE_0];
|
|
||||||
|
|
||||||
if (opcode_2 == 4)
|
|
||||||
return CP15[CP15_MEMORY_MODEL_FEATURE_0];
|
|
||||||
|
|
||||||
if (opcode_2 == 5)
|
|
||||||
return CP15[CP15_MEMORY_MODEL_FEATURE_1];
|
|
||||||
|
|
||||||
if (opcode_2 == 6)
|
|
||||||
return CP15[CP15_MEMORY_MODEL_FEATURE_2];
|
|
||||||
|
|
||||||
if (opcode_2 == 7)
|
|
||||||
return CP15[CP15_MEMORY_MODEL_FEATURE_3];
|
|
||||||
}
|
|
||||||
else if (crm == 2)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_ISA_FEATURE_0];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_ISA_FEATURE_1];
|
|
||||||
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
return CP15[CP15_ISA_FEATURE_2];
|
|
||||||
|
|
||||||
if (opcode_2 == 3)
|
|
||||||
return CP15[CP15_ISA_FEATURE_3];
|
|
||||||
|
|
||||||
if (opcode_2 == 4)
|
|
||||||
return CP15[CP15_ISA_FEATURE_4];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 1 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_CONTROL];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_AUXILIARY_CONTROL];
|
|
||||||
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
return CP15[CP15_COPROCESSOR_ACCESS_CONTROL];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 2 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_TRANSLATION_BASE_TABLE_0];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_TRANSLATION_BASE_TABLE_1];
|
|
||||||
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
return CP15[CP15_TRANSLATION_BASE_CONTROL];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0)
|
|
||||||
return CP15[CP15_DOMAIN_ACCESS_CONTROL];
|
|
||||||
|
|
||||||
if (crn == 5 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_FAULT_STATUS];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_INSTR_FAULT_STATUS];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 6 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_FAULT_ADDRESS];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_WFAR];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 7 && opcode_1 == 0 && crm == 4 && opcode_2 == 0)
|
|
||||||
return CP15[CP15_PHYS_ADDRESS];
|
|
||||||
|
|
||||||
if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0)
|
|
||||||
return CP15[CP15_DATA_CACHE_LOCKDOWN];
|
|
||||||
|
|
||||||
if (crn == 10 && opcode_1 == 0)
|
|
||||||
{
|
|
||||||
if (crm == 0 && opcode_2 == 0)
|
|
||||||
return CP15[CP15_TLB_LOCKDOWN];
|
|
||||||
|
|
||||||
if (crm == 2)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_PRIMARY_REGION_REMAP];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_NORMAL_REGION_REMAP];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 13 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_PID];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_CONTEXT_ID];
|
|
||||||
|
|
||||||
if (opcode_2 == 4)
|
|
||||||
return CP15[CP15_THREAD_PRW];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crn == 15)
|
|
||||||
{
|
|
||||||
if (opcode_1 == 0 && crm == 12)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
return CP15[CP15_PERFORMANCE_MONITOR_CONTROL];
|
|
||||||
|
|
||||||
if (opcode_2 == 1)
|
|
||||||
return CP15[CP15_CYCLE_COUNTER];
|
|
||||||
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
return CP15[CP15_COUNT_0];
|
|
||||||
|
|
||||||
if (opcode_2 == 3)
|
|
||||||
return CP15[CP15_COUNT_1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opcode_1 == 5 && opcode_2 == 2)
|
|
||||||
{
|
|
||||||
if (crm == 5)
|
|
||||||
return CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS];
|
|
||||||
|
|
||||||
if (crm == 6)
|
|
||||||
return CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS];
|
|
||||||
|
|
||||||
if (crm == 7)
|
|
||||||
return CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opcode_1 == 7 && crm == 1 && opcode_2 == 0)
|
|
||||||
return CP15[CP15_TLB_DEBUG_CONTROL];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// LOG_ERROR(Core_ARM11, "MRC CRn=%u, CRm=%u, OP1=%u OP2=%u is not implemented. Returning zero.", crn, crm, opcode_1, opcode_2);
|
|
||||||
ASSERT_MSG(false, "MRC CRn={}, CRm={}, OP1={} OP2={} is not implemented. Returning zero.", crn, crm, opcode_1, opcode_2);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to the CP15 registers. Used with implementation of the MCR instruction.
|
|
||||||
// Note that since the 3DS does not have the hypervisor extensions, these registers
|
|
||||||
// are not implemented.
|
|
||||||
void ARMul_State::WriteCP15Register(u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2)
|
|
||||||
{
|
|
||||||
if (InAPrivilegedMode())
|
|
||||||
{
|
|
||||||
if (crn == 1 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_CONTROL] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_AUXILIARY_CONTROL] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_COPROCESSOR_ACCESS_CONTROL] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 2 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_TRANSLATION_BASE_TABLE_0] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_TRANSLATION_BASE_TABLE_1] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_TRANSLATION_BASE_CONTROL] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 3 && opcode_1 == 0 && crm == 0 && opcode_2 == 0)
|
|
||||||
{
|
|
||||||
CP15[CP15_DOMAIN_ACCESS_CONTROL] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 5 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_FAULT_STATUS] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_INSTR_FAULT_STATUS] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 6 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_FAULT_ADDRESS] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_WFAR] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 7 && opcode_1 == 0)
|
|
||||||
{
|
|
||||||
if (crm == 0 && opcode_2 == 4)
|
|
||||||
{
|
|
||||||
CP15[CP15_WAIT_FOR_INTERRUPT] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 4 && opcode_2 == 0)
|
|
||||||
{
|
|
||||||
// NOTE: Not entirely accurate. This should do permission checks.
|
|
||||||
//CP15[CP15_PHYS_ADDRESS] = Memory::VirtualToPhysicalAddress(value);
|
|
||||||
}
|
|
||||||
else if (crm == 5)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_INVALIDATE_INSTR_CACHE] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_INVALIDATE_INSTR_CACHE_USING_MVA] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_INVALIDATE_INSTR_CACHE_USING_INDEX] = value;
|
|
||||||
else if (opcode_2 == 6)
|
|
||||||
CP15[CP15_FLUSH_BRANCH_TARGET_CACHE] = value;
|
|
||||||
else if (opcode_2 == 7)
|
|
||||||
CP15[CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 6)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_INVALIDATE_DATA_CACHE] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 7 && opcode_2 == 0)
|
|
||||||
{
|
|
||||||
CP15[CP15_INVALIDATE_DATA_AND_INSTR_CACHE] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 10)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_CLEAN_DATA_CACHE] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_MVA] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 14)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (crn == 8 && opcode_1 == 0)
|
|
||||||
{
|
|
||||||
if (crm == 5)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_INVALIDATE_ITLB] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_INVALIDATE_ITLB_SINGLE_ENTRY] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH] = value;
|
|
||||||
else if (opcode_2 == 3)
|
|
||||||
CP15[CP15_INVALIDATE_ITLB_ENTRY_ON_MVA] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 6)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_INVALIDATE_DTLB] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_INVALIDATE_DTLB_SINGLE_ENTRY] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH] = value;
|
|
||||||
else if (opcode_2 == 3)
|
|
||||||
CP15[CP15_INVALIDATE_DTLB_ENTRY_ON_MVA] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 7)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_INVALIDATE_UTLB] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_INVALIDATE_UTLB_SINGLE_ENTRY] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH] = value;
|
|
||||||
else if (opcode_2 == 3)
|
|
||||||
CP15[CP15_INVALIDATE_UTLB_ENTRY_ON_MVA] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (crn == 9 && opcode_1 == 0 && crm == 0 && opcode_2 == 0)
|
|
||||||
{
|
|
||||||
CP15[CP15_DATA_CACHE_LOCKDOWN] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 10 && opcode_1 == 0)
|
|
||||||
{
|
|
||||||
if (crm == 0 && opcode_2 == 0)
|
|
||||||
{
|
|
||||||
CP15[CP15_TLB_LOCKDOWN] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 2)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_PRIMARY_REGION_REMAP] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_NORMAL_REGION_REMAP] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (crn == 13 && opcode_1 == 0 && crm == 0)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_PID] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_CONTEXT_ID] = value;
|
|
||||||
else if (opcode_2 == 3)
|
|
||||||
CP15[CP15_THREAD_URO] = value;
|
|
||||||
else if (opcode_2 == 4)
|
|
||||||
CP15[CP15_THREAD_PRW] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 15)
|
|
||||||
{
|
|
||||||
if (opcode_1 == 0 && crm == 12)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 0)
|
|
||||||
CP15[CP15_PERFORMANCE_MONITOR_CONTROL] = value;
|
|
||||||
else if (opcode_2 == 1)
|
|
||||||
CP15[CP15_CYCLE_COUNTER] = value;
|
|
||||||
else if (opcode_2 == 2)
|
|
||||||
CP15[CP15_COUNT_0] = value;
|
|
||||||
else if (opcode_2 == 3)
|
|
||||||
CP15[CP15_COUNT_1] = value;
|
|
||||||
}
|
|
||||||
else if (opcode_1 == 5)
|
|
||||||
{
|
|
||||||
if (crm == 4)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 2)
|
|
||||||
CP15[CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY] = value;
|
|
||||||
else if (opcode_2 == 4)
|
|
||||||
CP15[CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 5 && opcode_2 == 2)
|
|
||||||
{
|
|
||||||
CP15[CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 6 && opcode_2 == 2)
|
|
||||||
{
|
|
||||||
CP15[CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS] = value;
|
|
||||||
}
|
|
||||||
else if (crm == 7 && opcode_2 == 2)
|
|
||||||
{
|
|
||||||
CP15[CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (opcode_1 == 7 && crm == 1 && opcode_2 == 0)
|
|
||||||
{
|
|
||||||
CP15[CP15_TLB_DEBUG_CONTROL] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unprivileged registers
|
|
||||||
if (crn == 7 && opcode_1 == 0 && crm == 5 && opcode_2 == 4)
|
|
||||||
{
|
|
||||||
CP15[CP15_FLUSH_PREFETCH_BUFFER] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 7 && opcode_1 == 0 && crm == 10)
|
|
||||||
{
|
|
||||||
if (opcode_2 == 4)
|
|
||||||
CP15[CP15_DATA_SYNC_BARRIER] = value;
|
|
||||||
else if (opcode_2 == 5)
|
|
||||||
CP15[CP15_DATA_MEMORY_BARRIER] = value;
|
|
||||||
}
|
|
||||||
else if (crn == 13 && opcode_1 == 0 && crm == 0 && opcode_2 == 2)
|
|
||||||
{
|
|
||||||
CP15[CP15_THREAD_UPRW] = value;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,256 +0,0 @@
|
||||||
/* armdefs.h -- ARMulator common definitions: ARM6 Instruction Emulator.
|
|
||||||
Copyright (C) 1994 Advanced RISC Machines Ltd.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <dynarmic/A32/config.h>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/arm_regformat.h"
|
|
||||||
|
|
||||||
// Signal levels
|
|
||||||
enum {
|
|
||||||
LOW = 0,
|
|
||||||
HIGH = 1,
|
|
||||||
LOWHIGH = 1,
|
|
||||||
HIGHLOW = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cache types
|
|
||||||
enum {
|
|
||||||
NONCACHE = 0,
|
|
||||||
DATACACHE = 1,
|
|
||||||
INSTCACHE = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ARM privilege modes
|
|
||||||
enum PrivilegeMode {
|
|
||||||
USER32MODE = 16,
|
|
||||||
FIQ32MODE = 17,
|
|
||||||
IRQ32MODE = 18,
|
|
||||||
SVC32MODE = 19,
|
|
||||||
ABORT32MODE = 23,
|
|
||||||
UNDEF32MODE = 27,
|
|
||||||
SYSTEM32MODE = 31
|
|
||||||
};
|
|
||||||
|
|
||||||
// ARM privilege mode register banks
|
|
||||||
enum {
|
|
||||||
USERBANK = 0,
|
|
||||||
FIQBANK = 1,
|
|
||||||
IRQBANK = 2,
|
|
||||||
SVCBANK = 3,
|
|
||||||
ABORTBANK = 4,
|
|
||||||
UNDEFBANK = 5,
|
|
||||||
DUMMYBANK = 6,
|
|
||||||
SYSTEMBANK = 7
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hardware vector addresses
|
|
||||||
enum {
|
|
||||||
ARMResetV = 0,
|
|
||||||
ARMUndefinedInstrV = 4,
|
|
||||||
ARMSWIV = 8,
|
|
||||||
ARMPrefetchAbortV = 12,
|
|
||||||
ARMDataAbortV = 16,
|
|
||||||
ARMAddrExceptnV = 20,
|
|
||||||
ARMIRQV = 24,
|
|
||||||
ARMFIQV = 28,
|
|
||||||
ARMErrorV = 32, // This is an offset, not an address!
|
|
||||||
|
|
||||||
ARMul_ResetV = ARMResetV,
|
|
||||||
ARMul_UndefinedInstrV = ARMUndefinedInstrV,
|
|
||||||
ARMul_SWIV = ARMSWIV,
|
|
||||||
ARMul_PrefetchAbortV = ARMPrefetchAbortV,
|
|
||||||
ARMul_DataAbortV = ARMDataAbortV,
|
|
||||||
ARMul_AddrExceptnV = ARMAddrExceptnV,
|
|
||||||
ARMul_IRQV = ARMIRQV,
|
|
||||||
ARMul_FIQV = ARMFIQV
|
|
||||||
};
|
|
||||||
|
|
||||||
// Coprocessor status values
|
|
||||||
enum {
|
|
||||||
ARMul_FIRST = 0,
|
|
||||||
ARMul_TRANSFER = 1,
|
|
||||||
ARMul_BUSY = 2,
|
|
||||||
ARMul_DATA = 3,
|
|
||||||
ARMul_INTERRUPT = 4,
|
|
||||||
ARMul_DONE = 0,
|
|
||||||
ARMul_CANT = 1,
|
|
||||||
ARMul_INC = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
// Instruction condition codes
|
|
||||||
enum ConditionCode {
|
|
||||||
EQ = 0,
|
|
||||||
NE = 1,
|
|
||||||
CS = 2,
|
|
||||||
CC = 3,
|
|
||||||
MI = 4,
|
|
||||||
PL = 5,
|
|
||||||
VS = 6,
|
|
||||||
VC = 7,
|
|
||||||
HI = 8,
|
|
||||||
LS = 9,
|
|
||||||
GE = 10,
|
|
||||||
LT = 11,
|
|
||||||
GT = 12,
|
|
||||||
LE = 13,
|
|
||||||
AL = 14,
|
|
||||||
NV = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Flags for use with the APSR.
|
|
||||||
enum : u32 {
|
|
||||||
NBIT = (1U << 31U),
|
|
||||||
ZBIT = (1 << 30),
|
|
||||||
CBIT = (1 << 29),
|
|
||||||
VBIT = (1 << 28),
|
|
||||||
QBIT = (1 << 27),
|
|
||||||
JBIT = (1 << 24),
|
|
||||||
EBIT = (1 << 9),
|
|
||||||
ABIT = (1 << 8),
|
|
||||||
IBIT = (1 << 7),
|
|
||||||
FBIT = (1 << 6),
|
|
||||||
TBIT = (1 << 5),
|
|
||||||
|
|
||||||
// Masks for groups of bits in the APSR.
|
|
||||||
MODEBITS = 0x1F,
|
|
||||||
INTBITS = 0x1C0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Values for Emulate.
|
|
||||||
enum {
|
|
||||||
STOP = 0, // Stop
|
|
||||||
CHANGEMODE = 1, // Change mode
|
|
||||||
ONCE = 2, // Execute just one iteration
|
|
||||||
RUN = 3 // Continuous execution
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct ARMul_State final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ARMul_State(PrivilegeMode initial_mode);
|
|
||||||
|
|
||||||
void ChangePrivilegeMode(u32 new_mode);
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
// Reads/writes data in big/little endian format based on the
|
|
||||||
// state of the E (endian) bit in the APSR.
|
|
||||||
u8 ReadMemory8(u32 address) const;
|
|
||||||
u16 ReadMemory16(u32 address) const;
|
|
||||||
u32 ReadMemory32(u32 address) const;
|
|
||||||
u64 ReadMemory64(u32 address) const;
|
|
||||||
void WriteMemory8(u32 address, u8 data);
|
|
||||||
void WriteMemory16(u32 address, u16 data);
|
|
||||||
void WriteMemory32(u32 address, u32 data);
|
|
||||||
void WriteMemory64(u32 address, u64 data);
|
|
||||||
|
|
||||||
u32 ReadCP15Register(u32 crn, u32 opcode_1, u32 crm, u32 opcode_2) const;
|
|
||||||
void WriteCP15Register(u32 value, u32 crn, u32 opcode_1, u32 crm, u32 opcode_2);
|
|
||||||
|
|
||||||
// Exclusive memory access functions
|
|
||||||
bool IsExclusiveMemoryAccess(u32 address) const {
|
|
||||||
return exclusive_state && exclusive_tag == (address & RESERVATION_GRANULE_MASK);
|
|
||||||
}
|
|
||||||
void SetExclusiveMemoryAddress(u32 address) {
|
|
||||||
exclusive_tag = address & RESERVATION_GRANULE_MASK;
|
|
||||||
exclusive_state = true;
|
|
||||||
}
|
|
||||||
void UnsetExclusiveMemoryAddress() {
|
|
||||||
exclusive_tag = 0xFFFFFFFF;
|
|
||||||
exclusive_state = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whether or not the given CPU is in big endian mode (E bit is set)
|
|
||||||
bool InBigEndianMode() const {
|
|
||||||
return (Cpsr & (1 << 9)) != 0;
|
|
||||||
}
|
|
||||||
// Whether or not the given CPU is in a mode other than user mode.
|
|
||||||
bool InAPrivilegedMode() const {
|
|
||||||
return (Mode != USER32MODE);
|
|
||||||
}
|
|
||||||
// Note that for the 3DS, a Thumb instruction will only ever be
|
|
||||||
// two bytes in size. Thus we don't need to worry about ThumbEE
|
|
||||||
// or Thumb-2 where instructions can be 4 bytes in length.
|
|
||||||
u32 GetInstructionSize() const {
|
|
||||||
return TFlag ? 2 : 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<u32, 16> Reg{}; // The current register file
|
|
||||||
std::array<u32, 2> Reg_usr{};
|
|
||||||
std::array<u32, 2> Reg_svc{}; // R13_SVC R14_SVC
|
|
||||||
std::array<u32, 2> Reg_abort{}; // R13_ABORT R14_ABORT
|
|
||||||
std::array<u32, 2> Reg_undef{}; // R13 UNDEF R14 UNDEF
|
|
||||||
std::array<u32, 2> Reg_irq{}; // R13_IRQ R14_IRQ
|
|
||||||
std::array<u32, 7> Reg_firq{}; // R8---R14 FIRQ
|
|
||||||
std::array<u32, 7> Spsr{}; // The exception psr's
|
|
||||||
std::array<u32, CP15_REGISTER_COUNT> CP15{};
|
|
||||||
|
|
||||||
// FPSID, FPSCR, and FPEXC
|
|
||||||
std::array<u32, VFP_SYSTEM_REGISTER_COUNT> VFP{};
|
|
||||||
|
|
||||||
// VFPv2 and VFPv3-D16 has 16 doubleword registers (D0-D16 or S0-S31).
|
|
||||||
// VFPv3-D32/ASIMD may have up to 32 doubleword registers (D0-D31),
|
|
||||||
// and only 32 singleword registers are accessible (S0-S31).
|
|
||||||
std::array<u32, 64> ExtReg{};
|
|
||||||
|
|
||||||
u32 Emulate; // To start and stop emulation
|
|
||||||
u32 Cpsr; // The current PSR
|
|
||||||
u32 Spsr_copy;
|
|
||||||
u32 phys_pc;
|
|
||||||
|
|
||||||
u32 Mode; // The current mode
|
|
||||||
u32 Bank; // The current register bank
|
|
||||||
|
|
||||||
u32 NFlag, ZFlag, CFlag, VFlag, IFFlags; // Dummy flags for speed
|
|
||||||
unsigned int shifter_carry_out;
|
|
||||||
|
|
||||||
u32 TFlag; // Thumb state
|
|
||||||
|
|
||||||
unsigned long long NumInstrs; // The number of instructions executed
|
|
||||||
unsigned NumInstrsToExecute;
|
|
||||||
|
|
||||||
unsigned NresetSig; // Reset the processor
|
|
||||||
unsigned NfiqSig;
|
|
||||||
unsigned NirqSig;
|
|
||||||
|
|
||||||
unsigned abortSig;
|
|
||||||
unsigned NtransSig;
|
|
||||||
unsigned bigendSig;
|
|
||||||
unsigned syscallSig;
|
|
||||||
|
|
||||||
// TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per
|
|
||||||
// process for our purposes), not per ARMul_State (which tracks CPU core state).
|
|
||||||
std::unordered_map<u32, int> instruction_cache;
|
|
||||||
|
|
||||||
void ResetMPCoreCP15Registers();
|
|
||||||
|
|
||||||
// Defines a reservation granule of 2 words, which protects the first 2 words starting at the tag.
|
|
||||||
// This is the smallest granule allowed by the v7 spec, and is coincidentally just large enough to
|
|
||||||
// support LDR/STREXD.
|
|
||||||
static const u32 RESERVATION_GRANULE_MASK = 0xFFFFFFF8;
|
|
||||||
|
|
||||||
u32 exclusive_tag; // The address for which the local monitor is in exclusive access mode
|
|
||||||
bool exclusive_state;
|
|
||||||
|
|
||||||
Dynarmic::A32::UserCallbacks* user_callbacks;
|
|
||||||
};
|
|
|
@ -1,207 +0,0 @@
|
||||||
/* armsupp.c -- ARMulator support code: ARM6 Instruction Emulator.
|
|
||||||
Copyright (C) 1994 Advanced RISC Machines Ltd.
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
||||||
|
|
||||||
//#include "common/logging/log.h"
|
|
||||||
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/arm_regformat.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armsupp.h"
|
|
||||||
|
|
||||||
// Unsigned sum of absolute difference
|
|
||||||
u8 ARMul_UnsignedAbsoluteDifference(u8 left, u8 right)
|
|
||||||
{
|
|
||||||
if (left > right)
|
|
||||||
return left - right;
|
|
||||||
|
|
||||||
return right - left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add with carry, indicates if a carry-out or signed overflow occurred.
|
|
||||||
u32 AddWithCarry(u32 left, u32 right, u32 carry_in, bool* carry_out_occurred, bool* overflow_occurred)
|
|
||||||
{
|
|
||||||
u64 unsigned_sum = (u64)left + (u64)right + (u64)carry_in;
|
|
||||||
s64 signed_sum = (s64)(s32)left + (s64)(s32)right + (s64)carry_in;
|
|
||||||
u64 result = (unsigned_sum & 0xFFFFFFFF);
|
|
||||||
|
|
||||||
if (carry_out_occurred)
|
|
||||||
*carry_out_occurred = (result != unsigned_sum);
|
|
||||||
|
|
||||||
if (overflow_occurred)
|
|
||||||
*overflow_occurred = ((s64)(s32)result != signed_sum);
|
|
||||||
|
|
||||||
return (u32)result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute whether an addition of A and B, giving RESULT, overflowed.
|
|
||||||
bool AddOverflow(u32 a, u32 b, u32 result)
|
|
||||||
{
|
|
||||||
return ((NEG(a) && NEG(b) && POS(result)) ||
|
|
||||||
(POS(a) && POS(b) && NEG(result)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute whether a subtraction of A and B, giving RESULT, overflowed.
|
|
||||||
bool SubOverflow(u32 a, u32 b, u32 result)
|
|
||||||
{
|
|
||||||
return ((NEG(a) && POS(b) && POS(result)) ||
|
|
||||||
(POS(a) && NEG(b) && NEG(result)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the Q flag should be set as a result of overflow.
|
|
||||||
bool ARMul_AddOverflowQ(u32 a, u32 b)
|
|
||||||
{
|
|
||||||
u32 result = a + b;
|
|
||||||
if (((result ^ a) & (u32)0x80000000) && ((a ^ b) & (u32)0x80000000) == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8-bit signed saturated addition
|
|
||||||
u8 ARMul_SignedSaturatedAdd8(u8 left, u8 right)
|
|
||||||
{
|
|
||||||
u8 result = left + right;
|
|
||||||
|
|
||||||
if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) == 0) {
|
|
||||||
if (left & 0x80)
|
|
||||||
result = 0x80;
|
|
||||||
else
|
|
||||||
result = 0x7F;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8-bit signed saturated subtraction
|
|
||||||
u8 ARMul_SignedSaturatedSub8(u8 left, u8 right)
|
|
||||||
{
|
|
||||||
u8 result = left - right;
|
|
||||||
|
|
||||||
if (((result ^ left) & 0x80) && ((left ^ right) & 0x80) != 0) {
|
|
||||||
if (left & 0x80)
|
|
||||||
result = 0x80;
|
|
||||||
else
|
|
||||||
result = 0x7F;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16-bit signed saturated addition
|
|
||||||
u16 ARMul_SignedSaturatedAdd16(u16 left, u16 right)
|
|
||||||
{
|
|
||||||
u16 result = left + right;
|
|
||||||
|
|
||||||
if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) == 0) {
|
|
||||||
if (left & 0x8000)
|
|
||||||
result = 0x8000;
|
|
||||||
else
|
|
||||||
result = 0x7FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16-bit signed saturated subtraction
|
|
||||||
u16 ARMul_SignedSaturatedSub16(u16 left, u16 right)
|
|
||||||
{
|
|
||||||
u16 result = left - right;
|
|
||||||
|
|
||||||
if (((result ^ left) & 0x8000) && ((left ^ right) & 0x8000) != 0) {
|
|
||||||
if (left & 0x8000)
|
|
||||||
result = 0x8000;
|
|
||||||
else
|
|
||||||
result = 0x7FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8-bit unsigned saturated addition
|
|
||||||
u8 ARMul_UnsignedSaturatedAdd8(u8 left, u8 right)
|
|
||||||
{
|
|
||||||
u8 result = left + right;
|
|
||||||
|
|
||||||
if (result < left)
|
|
||||||
result = 0xFF;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16-bit unsigned saturated addition
|
|
||||||
u16 ARMul_UnsignedSaturatedAdd16(u16 left, u16 right)
|
|
||||||
{
|
|
||||||
u16 result = left + right;
|
|
||||||
|
|
||||||
if (result < left)
|
|
||||||
result = 0xFFFF;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8-bit unsigned saturated subtraction
|
|
||||||
u8 ARMul_UnsignedSaturatedSub8(u8 left, u8 right)
|
|
||||||
{
|
|
||||||
if (left <= right)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return left - right;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16-bit unsigned saturated subtraction
|
|
||||||
u16 ARMul_UnsignedSaturatedSub16(u16 left, u16 right)
|
|
||||||
{
|
|
||||||
if (left <= right)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return left - right;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signed saturation.
|
|
||||||
u32 ARMul_SignedSatQ(s32 value, u8 shift, bool* saturation_occurred)
|
|
||||||
{
|
|
||||||
const u32 max = (1 << shift) - 1;
|
|
||||||
const s32 top = (value >> shift);
|
|
||||||
|
|
||||||
if (top > 0) {
|
|
||||||
*saturation_occurred = true;
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
else if (top < -1) {
|
|
||||||
*saturation_occurred = true;
|
|
||||||
return ~max;
|
|
||||||
}
|
|
||||||
|
|
||||||
*saturation_occurred = false;
|
|
||||||
return (u32)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsigned saturation
|
|
||||||
u32 ARMul_UnsignedSatQ(s32 value, u8 shift, bool* saturation_occurred)
|
|
||||||
{
|
|
||||||
const u32 max = (1 << shift) - 1;
|
|
||||||
|
|
||||||
if (value < 0) {
|
|
||||||
*saturation_occurred = true;
|
|
||||||
return 0;
|
|
||||||
} else if ((u32)value > max) {
|
|
||||||
*saturation_occurred = true;
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
*saturation_occurred = false;
|
|
||||||
return (u32)value;
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Copyright 2014 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
#define BITS(s, a, b) ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1))
|
|
||||||
#define BIT(s, n) ((s >> (n)) & 1)
|
|
||||||
|
|
||||||
#define POS(i) ( (~(i)) >> 31 )
|
|
||||||
#define NEG(i) ( (i) >> 31 )
|
|
||||||
|
|
||||||
bool AddOverflow(u32, u32, u32);
|
|
||||||
bool SubOverflow(u32, u32, u32);
|
|
||||||
|
|
||||||
u32 AddWithCarry(u32, u32, u32, bool*, bool*);
|
|
||||||
bool ARMul_AddOverflowQ(u32, u32);
|
|
||||||
|
|
||||||
u8 ARMul_SignedSaturatedAdd8(u8, u8);
|
|
||||||
u8 ARMul_SignedSaturatedSub8(u8, u8);
|
|
||||||
u16 ARMul_SignedSaturatedAdd16(u16, u16);
|
|
||||||
u16 ARMul_SignedSaturatedSub16(u16, u16);
|
|
||||||
|
|
||||||
u8 ARMul_UnsignedSaturatedAdd8(u8, u8);
|
|
||||||
u16 ARMul_UnsignedSaturatedAdd16(u16, u16);
|
|
||||||
u8 ARMul_UnsignedSaturatedSub8(u8, u8);
|
|
||||||
u16 ARMul_UnsignedSaturatedSub16(u16, u16);
|
|
||||||
u8 ARMul_UnsignedAbsoluteDifference(u8, u8);
|
|
||||||
u32 ARMul_SignedSatQ(s32, u8, bool*);
|
|
||||||
u32 ARMul_UnsignedSatQ(s32, u8, bool*);
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* arch/arm/include/asm/vfp.h
|
|
||||||
*
|
|
||||||
* VFP register definitions.
|
|
||||||
* First, the standard VFP set.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// ARM11 MPCore FPSID Information
|
|
||||||
// Note that these are used as values and not as flags.
|
|
||||||
enum : u32 {
|
|
||||||
VFP_FPSID_IMPLMEN = 0x41, // Implementation code. Should be the same as cp15 0 c0 0
|
|
||||||
VFP_FPSID_SW = 0, // Software emulation bit value
|
|
||||||
VFP_FPSID_SUBARCH = 0x1, // Subarchitecture version number
|
|
||||||
VFP_FPSID_PARTNUM = 0x20, // Part number
|
|
||||||
VFP_FPSID_VARIANT = 0xB, // Variant number
|
|
||||||
VFP_FPSID_REVISION = 0x4 // Revision number
|
|
||||||
};
|
|
||||||
|
|
||||||
// FPEXC bits
|
|
||||||
enum : u32 {
|
|
||||||
FPEXC_EX = (1U << 31U),
|
|
||||||
FPEXC_EN = (1 << 30),
|
|
||||||
FPEXC_DEX = (1 << 29),
|
|
||||||
FPEXC_FP2V = (1 << 28),
|
|
||||||
FPEXC_VV = (1 << 27),
|
|
||||||
FPEXC_TFV = (1 << 26),
|
|
||||||
FPEXC_LENGTH_BIT = (8),
|
|
||||||
FPEXC_LENGTH_MASK = (7 << FPEXC_LENGTH_BIT),
|
|
||||||
FPEXC_IDF = (1 << 7),
|
|
||||||
FPEXC_IXF = (1 << 4),
|
|
||||||
FPEXC_UFF = (1 << 3),
|
|
||||||
FPEXC_OFF = (1 << 2),
|
|
||||||
FPEXC_DZF = (1 << 1),
|
|
||||||
FPEXC_IOF = (1 << 0),
|
|
||||||
FPEXC_TRAP_MASK = (FPEXC_IDF|FPEXC_IXF|FPEXC_UFF|FPEXC_OFF|FPEXC_DZF|FPEXC_IOF)
|
|
||||||
};
|
|
||||||
|
|
||||||
// FPSCR Flags
|
|
||||||
enum : u32 {
|
|
||||||
FPSCR_NFLAG = (1U << 31U), // Negative condition flag
|
|
||||||
FPSCR_ZFLAG = (1 << 30), // Zero condition flag
|
|
||||||
FPSCR_CFLAG = (1 << 29), // Carry condition flag
|
|
||||||
FPSCR_VFLAG = (1 << 28), // Overflow condition flag
|
|
||||||
|
|
||||||
FPSCR_QC = (1 << 27), // Cumulative saturation bit
|
|
||||||
FPSCR_AHP = (1 << 26), // Alternative half-precision control bit
|
|
||||||
FPSCR_DEFAULT_NAN = (1 << 25), // Default NaN mode control bit
|
|
||||||
FPSCR_FLUSH_TO_ZERO = (1 << 24), // Flush-to-zero mode control bit
|
|
||||||
FPSCR_RMODE_MASK = (3 << 22), // Rounding Mode bit mask
|
|
||||||
FPSCR_STRIDE_MASK = (3 << 20), // Vector stride bit mask
|
|
||||||
FPSCR_LENGTH_MASK = (7 << 16), // Vector length bit mask
|
|
||||||
|
|
||||||
FPSCR_IDE = (1 << 15), // Input Denormal exception trap enable.
|
|
||||||
FPSCR_IXE = (1 << 12), // Inexact exception trap enable
|
|
||||||
FPSCR_UFE = (1 << 11), // Undeflow exception trap enable
|
|
||||||
FPSCR_OFE = (1 << 10), // Overflow exception trap enable
|
|
||||||
FPSCR_DZE = (1 << 9), // Division by Zero exception trap enable
|
|
||||||
FPSCR_IOE = (1 << 8), // Invalid Operation exception trap enable
|
|
||||||
|
|
||||||
FPSCR_IDC = (1 << 7), // Input Denormal cumulative exception bit
|
|
||||||
FPSCR_IXC = (1 << 4), // Inexact cumulative exception bit
|
|
||||||
FPSCR_UFC = (1 << 3), // Undeflow cumulative exception bit
|
|
||||||
FPSCR_OFC = (1 << 2), // Overflow cumulative exception bit
|
|
||||||
FPSCR_DZC = (1 << 1), // Division by Zero cumulative exception bit
|
|
||||||
FPSCR_IOC = (1 << 0), // Invalid Operation cumulative exception bit
|
|
||||||
};
|
|
||||||
|
|
||||||
// FPSCR bit offsets
|
|
||||||
enum : u32 {
|
|
||||||
FPSCR_RMODE_BIT = 22,
|
|
||||||
FPSCR_STRIDE_BIT = 20,
|
|
||||||
FPSCR_LENGTH_BIT = 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
// FPSCR rounding modes
|
|
||||||
enum : u32 {
|
|
||||||
FPSCR_ROUND_NEAREST = (0 << 22),
|
|
||||||
FPSCR_ROUND_PLUSINF = (1 << 22),
|
|
||||||
FPSCR_ROUND_MINUSINF = (2 << 22),
|
|
||||||
FPSCR_ROUND_TOZERO = (3 << 22)
|
|
||||||
};
|
|
|
@ -1,166 +0,0 @@
|
||||||
/*
|
|
||||||
armvfp.c - ARM VFPv3 emulation unit
|
|
||||||
Copyright (C) 2003 Skyeye Develop Group
|
|
||||||
for help please send mail to <skyeye-developer@lists.gro.clinux.org>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Note: this file handles interface with arm core and vfp registers */
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp.h"
|
|
||||||
|
|
||||||
#define LOG_INFO(...) do{}while(0)
|
|
||||||
#define LOG_TRACE(...) do{}while(0)
|
|
||||||
|
|
||||||
void VFPInit(ARMul_State* state)
|
|
||||||
{
|
|
||||||
state->VFP[VFP_FPSID] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 |
|
|
||||||
VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION;
|
|
||||||
state->VFP[VFP_FPEXC] = 0;
|
|
||||||
state->VFP[VFP_FPSCR] = 0;
|
|
||||||
|
|
||||||
// ARM11 MPCore instruction register reset values.
|
|
||||||
state->VFP[VFP_FPINST] = 0xEE000A00;
|
|
||||||
state->VFP[VFP_FPINST2] = 0;
|
|
||||||
|
|
||||||
// ARM11 MPCore feature register values.
|
|
||||||
state->VFP[VFP_MVFR0] = 0x11111111;
|
|
||||||
state->VFP[VFP_MVFR1] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMOVBRS(ARMul_State* state, u32 to_arm, [[maybe_unused]] u32 t, u32 n, u32* value)
|
|
||||||
{
|
|
||||||
if (to_arm)
|
|
||||||
{
|
|
||||||
*value = state->ExtReg[n];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->ExtReg[n] = *value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMOVBRRD(ARMul_State* state, u32 to_arm, [[maybe_unused]] u32 t, [[maybe_unused]] u32 t2,
|
|
||||||
u32 n, u32* value1, u32* value2)
|
|
||||||
{
|
|
||||||
if (to_arm)
|
|
||||||
{
|
|
||||||
*value2 = state->ExtReg[n*2+1];
|
|
||||||
*value1 = state->ExtReg[n*2];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->ExtReg[n*2+1] = *value2;
|
|
||||||
state->ExtReg[n*2] = *value1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void VMOVBRRSS(ARMul_State* state, u32 to_arm, [[maybe_unused]] u32 t, [[maybe_unused]] u32 t2,
|
|
||||||
u32 n, u32* value1, u32* value2)
|
|
||||||
{
|
|
||||||
if (to_arm)
|
|
||||||
{
|
|
||||||
*value1 = state->ExtReg[n+0];
|
|
||||||
*value2 = state->ExtReg[n+1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->ExtReg[n+0] = *value1;
|
|
||||||
state->ExtReg[n+1] = *value2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMOVI(ARMul_State* state, u32 single, u32 d, u32 imm)
|
|
||||||
{
|
|
||||||
if (single)
|
|
||||||
{
|
|
||||||
state->ExtReg[d] = imm;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Check endian please */
|
|
||||||
state->ExtReg[d*2+1] = imm;
|
|
||||||
state->ExtReg[d*2] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void VMOVR(ARMul_State* state, u32 single, u32 d, u32 m)
|
|
||||||
{
|
|
||||||
if (single)
|
|
||||||
{
|
|
||||||
state->ExtReg[d] = state->ExtReg[m];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Check endian please */
|
|
||||||
state->ExtReg[d*2+1] = state->ExtReg[m*2+1];
|
|
||||||
state->ExtReg[d*2] = state->ExtReg[m*2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Miscellaneous functions */
|
|
||||||
s32 vfp_get_float(ARMul_State* state, unsigned int reg)
|
|
||||||
{
|
|
||||||
LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]", reg, state->ExtReg[reg]);
|
|
||||||
return state->ExtReg[reg];
|
|
||||||
}
|
|
||||||
|
|
||||||
void vfp_put_float(ARMul_State* state, s32 val, unsigned int reg)
|
|
||||||
{
|
|
||||||
LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]", reg, val);
|
|
||||||
state->ExtReg[reg] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 vfp_get_double(ARMul_State* state, unsigned int reg)
|
|
||||||
{
|
|
||||||
u64 result = ((u64) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2];
|
|
||||||
LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]", reg * 2 + 1, reg * 2, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
|
|
||||||
{
|
|
||||||
LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff));
|
|
||||||
state->ExtReg[reg*2] = (u32) (val & 0xffffffff);
|
|
||||||
state->ExtReg[reg*2+1] = (u32) (val>>32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Process bitmask of exception conditions. (from vfpmodule.c)
|
|
||||||
*/
|
|
||||||
void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr)
|
|
||||||
{
|
|
||||||
LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x", exceptions);
|
|
||||||
|
|
||||||
if (exceptions == VFP_EXCEPTION_ERROR) {
|
|
||||||
ASSERT_MSG(false, "unhandled bounce {:08x}", inst);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any of the status flags are set, update the FPSCR.
|
|
||||||
* Comparison instructions always return at least one of
|
|
||||||
* these flags set.
|
|
||||||
*/
|
|
||||||
if (exceptions & (FPSCR_NFLAG|FPSCR_ZFLAG|FPSCR_CFLAG|FPSCR_VFLAG))
|
|
||||||
fpscr &= ~(FPSCR_NFLAG|FPSCR_ZFLAG|FPSCR_CFLAG|FPSCR_VFLAG);
|
|
||||||
|
|
||||||
fpscr |= exceptions;
|
|
||||||
|
|
||||||
state->VFP[VFP_FPSCR] = fpscr;
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
vfp/vfp.h - ARM VFPv3 emulation unit - vfp interface
|
|
||||||
Copyright (C) 2003 Skyeye Develop Group
|
|
||||||
for help please send mail to <skyeye-developer@lists.gro.clinux.org>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
|
|
||||||
|
|
||||||
#define LOG_INFO(...) do{}while(0)
|
|
||||||
#define LOG_TRACE(...) do{}while(0)
|
|
||||||
|
|
||||||
#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested", __FUNCTION__);
|
|
||||||
#define CHECK_VFP_ENABLED
|
|
||||||
#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]);
|
|
||||||
|
|
||||||
void VFPInit(ARMul_State* state);
|
|
||||||
|
|
||||||
s32 vfp_get_float(ARMul_State* state, u32 reg);
|
|
||||||
void vfp_put_float(ARMul_State* state, s32 val, u32 reg);
|
|
||||||
u64 vfp_get_double(ARMul_State* state, u32 reg);
|
|
||||||
void vfp_put_double(ARMul_State* state, u64 val, u32 reg);
|
|
||||||
void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr);
|
|
||||||
u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
|
|
||||||
u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
|
|
||||||
|
|
||||||
void VMOVBRS(ARMul_State* state, u32 to_arm, u32 t, u32 n, u32* value);
|
|
||||||
void VMOVBRRD(ARMul_State* state, u32 to_arm, u32 t, u32 t2, u32 n, u32* value1, u32* value2);
|
|
||||||
void VMOVBRRSS(ARMul_State* state, u32 to_arm, u32 t, u32 t2, u32 n, u32* value1, u32* value2);
|
|
||||||
void VMOVI(ARMul_State* state, u32 single, u32 d, u32 imm);
|
|
||||||
void VMOVR(ARMul_State* state, u32 single, u32 d, u32 imm);
|
|
|
@ -1,448 +0,0 @@
|
||||||
/*
|
|
||||||
vfp/vfp.h - ARM VFPv3 emulation unit - SoftFloat lib helper
|
|
||||||
Copyright (C) 2003 Skyeye Develop Group
|
|
||||||
for help please send mail to <skyeye-developer@lists.gro.clinux.org>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The following code is derivative from Linux Android kernel vfp
|
|
||||||
* floating point support.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2004 ARM Limited.
|
|
||||||
* Written by Deep Blue Solutions Limited.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
|
||||||
* published by the Free Software Foundation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
|
|
||||||
|
|
||||||
#define LOG_INFO(...) do{}while(0)
|
|
||||||
#define LOG_TRACE(...) do{}while(0)
|
|
||||||
|
|
||||||
#define do_div(n, base) {n/=base;}
|
|
||||||
|
|
||||||
enum : u32 {
|
|
||||||
FOP_MASK = 0x00b00040,
|
|
||||||
FOP_FMAC = 0x00000000,
|
|
||||||
FOP_FNMAC = 0x00000040,
|
|
||||||
FOP_FMSC = 0x00100000,
|
|
||||||
FOP_FNMSC = 0x00100040,
|
|
||||||
FOP_FMUL = 0x00200000,
|
|
||||||
FOP_FNMUL = 0x00200040,
|
|
||||||
FOP_FADD = 0x00300000,
|
|
||||||
FOP_FSUB = 0x00300040,
|
|
||||||
FOP_FDIV = 0x00800000,
|
|
||||||
FOP_EXT = 0x00b00040
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FOP_TO_IDX(inst) ((inst & 0x00b00000) >> 20 | (inst & (1 << 6)) >> 4)
|
|
||||||
|
|
||||||
enum : u32 {
|
|
||||||
FEXT_MASK = 0x000f0080,
|
|
||||||
FEXT_FCPY = 0x00000000,
|
|
||||||
FEXT_FABS = 0x00000080,
|
|
||||||
FEXT_FNEG = 0x00010000,
|
|
||||||
FEXT_FSQRT = 0x00010080,
|
|
||||||
FEXT_FCMP = 0x00040000,
|
|
||||||
FEXT_FCMPE = 0x00040080,
|
|
||||||
FEXT_FCMPZ = 0x00050000,
|
|
||||||
FEXT_FCMPEZ = 0x00050080,
|
|
||||||
FEXT_FCVT = 0x00070080,
|
|
||||||
FEXT_FUITO = 0x00080000,
|
|
||||||
FEXT_FSITO = 0x00080080,
|
|
||||||
FEXT_FTOUI = 0x000c0000,
|
|
||||||
FEXT_FTOUIZ = 0x000c0080,
|
|
||||||
FEXT_FTOSI = 0x000d0000,
|
|
||||||
FEXT_FTOSIZ = 0x000d0080
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FEXT_TO_IDX(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
|
|
||||||
|
|
||||||
#define vfp_get_sd(inst) ((inst & 0x0000f000) >> 11 | (inst & (1 << 22)) >> 22)
|
|
||||||
#define vfp_get_dd(inst) ((inst & 0x0000f000) >> 12 | (inst & (1 << 22)) >> 18)
|
|
||||||
#define vfp_get_sm(inst) ((inst & 0x0000000f) << 1 | (inst & (1 << 5)) >> 5)
|
|
||||||
#define vfp_get_dm(inst) ((inst & 0x0000000f) | (inst & (1 << 5)) >> 1)
|
|
||||||
#define vfp_get_sn(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
|
|
||||||
#define vfp_get_dn(inst) ((inst & 0x000f0000) >> 16 | (inst & (1 << 7)) >> 3)
|
|
||||||
|
|
||||||
#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00)
|
|
||||||
|
|
||||||
inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
|
|
||||||
{
|
|
||||||
if (shift) {
|
|
||||||
if (shift < 32)
|
|
||||||
val = val >> shift | ((val << (32 - shift)) != 0);
|
|
||||||
else
|
|
||||||
val = val != 0;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
|
|
||||||
{
|
|
||||||
if (shift) {
|
|
||||||
if (shift < 64)
|
|
||||||
val = val >> shift | ((val << (64 - shift)) != 0);
|
|
||||||
else
|
|
||||||
val = val != 0;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u32 vfp_hi64to32jamming(u64 val)
|
|
||||||
{
|
|
||||||
u32 v;
|
|
||||||
u32 highval = val >> 32;
|
|
||||||
u32 lowval = val & 0xffffffff;
|
|
||||||
|
|
||||||
if (lowval >= 1)
|
|
||||||
v = highval | 1;
|
|
||||||
else
|
|
||||||
v = highval;
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
|
|
||||||
{
|
|
||||||
*resl = nl + ml;
|
|
||||||
*resh = nh + mh;
|
|
||||||
if (*resl < nl)
|
|
||||||
*resh += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
|
|
||||||
{
|
|
||||||
*resl = nl - ml;
|
|
||||||
*resh = nh - mh;
|
|
||||||
if (*resl > nl)
|
|
||||||
*resh -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
|
|
||||||
{
|
|
||||||
u32 nh, nl, mh, ml;
|
|
||||||
u64 rh, rma, rmb, rl;
|
|
||||||
|
|
||||||
nl = static_cast<u32>(n);
|
|
||||||
ml = static_cast<u32>(m);
|
|
||||||
rl = (u64)nl * ml;
|
|
||||||
|
|
||||||
nh = n >> 32;
|
|
||||||
rma = (u64)nh * ml;
|
|
||||||
|
|
||||||
mh = m >> 32;
|
|
||||||
rmb = (u64)nl * mh;
|
|
||||||
rma += rmb;
|
|
||||||
|
|
||||||
rh = (u64)nh * mh;
|
|
||||||
rh += ((u64)(rma < rmb) << 32) + (rma >> 32);
|
|
||||||
|
|
||||||
rma <<= 32;
|
|
||||||
rl += rma;
|
|
||||||
rh += (rl < rma);
|
|
||||||
|
|
||||||
*resl = rl;
|
|
||||||
*resh = rh;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void shift64left(u64* resh, u64* resl, u64 n)
|
|
||||||
{
|
|
||||||
*resh = n >> 63;
|
|
||||||
*resl = n << 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u64 vfp_hi64multiply64(u64 n, u64 m)
|
|
||||||
{
|
|
||||||
u64 rh, rl;
|
|
||||||
mul64to128(&rh, &rl, n, m);
|
|
||||||
return rh | (rl != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
|
|
||||||
{
|
|
||||||
u64 mh, ml, remh, reml, termh, terml, z;
|
|
||||||
|
|
||||||
if (nh >= m)
|
|
||||||
return ~0ULL;
|
|
||||||
mh = m >> 32;
|
|
||||||
if (mh << 32 <= nh) {
|
|
||||||
z = 0xffffffff00000000ULL;
|
|
||||||
} else {
|
|
||||||
z = nh;
|
|
||||||
do_div(z, mh);
|
|
||||||
z <<= 32;
|
|
||||||
}
|
|
||||||
mul64to128(&termh, &terml, m, z);
|
|
||||||
sub128(&remh, &reml, nh, nl, termh, terml);
|
|
||||||
ml = m << 32;
|
|
||||||
while ((s64)remh < 0) {
|
|
||||||
z -= 0x100000000ULL;
|
|
||||||
add128(&remh, &reml, remh, reml, mh, ml);
|
|
||||||
}
|
|
||||||
remh = (remh << 32) | (reml >> 32);
|
|
||||||
if (mh << 32 <= remh) {
|
|
||||||
z |= 0xffffffff;
|
|
||||||
} else {
|
|
||||||
do_div(remh, mh);
|
|
||||||
z |= remh;
|
|
||||||
}
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operations on unpacked elements
|
|
||||||
#define vfp_sign_negate(sign) (sign ^ 0x8000)
|
|
||||||
|
|
||||||
// Single-precision
|
|
||||||
struct vfp_single {
|
|
||||||
s16 exponent;
|
|
||||||
u16 sign;
|
|
||||||
u32 significand;
|
|
||||||
};
|
|
||||||
|
|
||||||
// VFP_SINGLE_MANTISSA_BITS - number of bits in the mantissa
|
|
||||||
// VFP_SINGLE_EXPONENT_BITS - number of bits in the exponent
|
|
||||||
// VFP_SINGLE_LOW_BITS - number of low bits in the unpacked significand
|
|
||||||
// which are not propagated to the float upon packing.
|
|
||||||
#define VFP_SINGLE_MANTISSA_BITS (23)
|
|
||||||
#define VFP_SINGLE_EXPONENT_BITS (8)
|
|
||||||
#define VFP_SINGLE_LOW_BITS (32 - VFP_SINGLE_MANTISSA_BITS - 2)
|
|
||||||
#define VFP_SINGLE_LOW_BITS_MASK ((1 << VFP_SINGLE_LOW_BITS) - 1)
|
|
||||||
|
|
||||||
// The bit in an unpacked float which indicates that it is a quiet NaN
|
|
||||||
#define VFP_SINGLE_SIGNIFICAND_QNAN (1 << (VFP_SINGLE_MANTISSA_BITS - 1 + VFP_SINGLE_LOW_BITS))
|
|
||||||
|
|
||||||
// Operations on packed single-precision numbers
|
|
||||||
#define vfp_single_packed_sign(v) ((v) & 0x80000000)
|
|
||||||
#define vfp_single_packed_negate(v) ((v) ^ 0x80000000)
|
|
||||||
#define vfp_single_packed_abs(v) ((v) & ~0x80000000)
|
|
||||||
#define vfp_single_packed_exponent(v) (((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1))
|
|
||||||
#define vfp_single_packed_mantissa(v) ((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1))
|
|
||||||
|
|
||||||
enum : u32 {
|
|
||||||
VFP_NUMBER = (1 << 0),
|
|
||||||
VFP_ZERO = (1 << 1),
|
|
||||||
VFP_DENORMAL = (1 << 2),
|
|
||||||
VFP_INFINITY = (1 << 3),
|
|
||||||
VFP_NAN = (1 << 4),
|
|
||||||
VFP_NAN_SIGNAL = (1 << 5),
|
|
||||||
|
|
||||||
VFP_QNAN = (VFP_NAN),
|
|
||||||
VFP_SNAN = (VFP_NAN|VFP_NAN_SIGNAL)
|
|
||||||
};
|
|
||||||
|
|
||||||
inline int vfp_single_type(const vfp_single* s)
|
|
||||||
{
|
|
||||||
int type = VFP_NUMBER;
|
|
||||||
if (s->exponent == 255) {
|
|
||||||
if (s->significand == 0)
|
|
||||||
type = VFP_INFINITY;
|
|
||||||
else if (s->significand & VFP_SINGLE_SIGNIFICAND_QNAN)
|
|
||||||
type = VFP_QNAN;
|
|
||||||
else
|
|
||||||
type = VFP_SNAN;
|
|
||||||
} else if (s->exponent == 0) {
|
|
||||||
if (s->significand == 0)
|
|
||||||
type |= VFP_ZERO;
|
|
||||||
else
|
|
||||||
type |= VFP_DENORMAL;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unpack a single-precision float. Note that this returns the magnitude
|
|
||||||
// of the single-precision float mantissa with the 1. if necessary,
|
|
||||||
// aligned to bit 30.
|
|
||||||
inline u32 vfp_single_unpack(vfp_single* s, s32 val, u32 fpscr)
|
|
||||||
{
|
|
||||||
u32 exceptions = 0;
|
|
||||||
s->sign = vfp_single_packed_sign(val) >> 16;
|
|
||||||
s->exponent = vfp_single_packed_exponent(val);
|
|
||||||
|
|
||||||
u32 significand = ((u32)val << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2;
|
|
||||||
if (s->exponent && s->exponent != 255)
|
|
||||||
significand |= 0x40000000;
|
|
||||||
s->significand = significand;
|
|
||||||
|
|
||||||
// If flush-to-zero mode is enabled, turn the denormal into zero.
|
|
||||||
// On a VFPv2 architecture, the sign of the zero is always positive.
|
|
||||||
if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_single_type(s) & VFP_DENORMAL) != 0) {
|
|
||||||
s->sign = 0;
|
|
||||||
s->exponent = 0;
|
|
||||||
s->significand = 0;
|
|
||||||
exceptions |= FPSCR_IDC;
|
|
||||||
}
|
|
||||||
return exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-pack a single-precision float. This assumes that the float is
|
|
||||||
// already normalised such that the MSB is bit 30, _not_ bit 31.
|
|
||||||
inline s32 vfp_single_pack(const vfp_single* s)
|
|
||||||
{
|
|
||||||
u32 val = (s->sign << 16) +
|
|
||||||
(s->exponent << VFP_SINGLE_MANTISSA_BITS) +
|
|
||||||
(s->significand >> VFP_SINGLE_LOW_BITS);
|
|
||||||
return (s32)val;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, const char* func);
|
|
||||||
|
|
||||||
// Double-precision
|
|
||||||
struct vfp_double {
|
|
||||||
s16 exponent;
|
|
||||||
u16 sign;
|
|
||||||
u64 significand;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define VFP_DOUBLE_MANTISSA_BITS (52)
|
|
||||||
#define VFP_DOUBLE_EXPONENT_BITS (11)
|
|
||||||
#define VFP_DOUBLE_LOW_BITS (64 - VFP_DOUBLE_MANTISSA_BITS - 2)
|
|
||||||
#define VFP_DOUBLE_LOW_BITS_MASK ((1 << VFP_DOUBLE_LOW_BITS) - 1)
|
|
||||||
|
|
||||||
// The bit in an unpacked double which indicates that it is a quiet NaN
|
|
||||||
#define VFP_DOUBLE_SIGNIFICAND_QNAN (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1 + VFP_DOUBLE_LOW_BITS))
|
|
||||||
|
|
||||||
// Operations on packed single-precision numbers
|
|
||||||
#define vfp_double_packed_sign(v) ((v) & (1ULL << 63))
|
|
||||||
#define vfp_double_packed_negate(v) ((v) ^ (1ULL << 63))
|
|
||||||
#define vfp_double_packed_abs(v) ((v) & ~(1ULL << 63))
|
|
||||||
#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
|
|
||||||
#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))
|
|
||||||
|
|
||||||
inline int vfp_double_type(const vfp_double* s)
|
|
||||||
{
|
|
||||||
int type = VFP_NUMBER;
|
|
||||||
if (s->exponent == 2047) {
|
|
||||||
if (s->significand == 0)
|
|
||||||
type = VFP_INFINITY;
|
|
||||||
else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN)
|
|
||||||
type = VFP_QNAN;
|
|
||||||
else
|
|
||||||
type = VFP_SNAN;
|
|
||||||
} else if (s->exponent == 0) {
|
|
||||||
if (s->significand == 0)
|
|
||||||
type |= VFP_ZERO;
|
|
||||||
else
|
|
||||||
type |= VFP_DENORMAL;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unpack a double-precision float. Note that this returns the magnitude
|
|
||||||
// of the double-precision float mantissa with the 1. if necessary,
|
|
||||||
// aligned to bit 62.
|
|
||||||
inline u32 vfp_double_unpack(vfp_double* s, s64 val, u32 fpscr)
|
|
||||||
{
|
|
||||||
u32 exceptions = 0;
|
|
||||||
s->sign = vfp_double_packed_sign(val) >> 48;
|
|
||||||
s->exponent = vfp_double_packed_exponent(val);
|
|
||||||
|
|
||||||
u64 significand = ((u64)val << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2;
|
|
||||||
if (s->exponent && s->exponent != 2047)
|
|
||||||
significand |= (1ULL << 62);
|
|
||||||
s->significand = significand;
|
|
||||||
|
|
||||||
// If flush-to-zero mode is enabled, turn the denormal into zero.
|
|
||||||
// On a VFPv2 architecture, the sign of the zero is always positive.
|
|
||||||
if ((fpscr & FPSCR_FLUSH_TO_ZERO) != 0 && (vfp_double_type(s) & VFP_DENORMAL) != 0) {
|
|
||||||
s->sign = 0;
|
|
||||||
s->exponent = 0;
|
|
||||||
s->significand = 0;
|
|
||||||
exceptions |= FPSCR_IDC;
|
|
||||||
}
|
|
||||||
return exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-pack a double-precision float. This assumes that the float is
|
|
||||||
// already normalised such that the MSB is bit 30, _not_ bit 31.
|
|
||||||
inline s64 vfp_double_pack(const vfp_double* s)
|
|
||||||
{
|
|
||||||
u64 val = ((u64)s->sign << 48) +
|
|
||||||
((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
|
|
||||||
(s->significand >> VFP_DOUBLE_LOW_BITS);
|
|
||||||
return (s64)val;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand);
|
|
||||||
|
|
||||||
// A special flag to tell the normalisation code not to normalise.
|
|
||||||
#define VFP_NAN_FLAG 0x100
|
|
||||||
|
|
||||||
// A bit pattern used to indicate the initial (unset) value of the
|
|
||||||
// exception mask, in case nothing handles an instruction. This
|
|
||||||
// doesn't include the NAN flag, which get masked out before
|
|
||||||
// we check for an error.
|
|
||||||
#define VFP_EXCEPTION_ERROR ((u32)-1 & ~VFP_NAN_FLAG)
|
|
||||||
|
|
||||||
// A flag to tell vfp instruction type.
|
|
||||||
// OP_SCALAR - This operation always operates in scalar mode
|
|
||||||
// OP_SD - The instruction exceptionally writes to a single precision result.
|
|
||||||
// OP_DD - The instruction exceptionally writes to a double precision result.
|
|
||||||
// OP_SM - The instruction exceptionally reads from a single precision operand.
|
|
||||||
enum : u32 {
|
|
||||||
OP_SCALAR = (1 << 0),
|
|
||||||
OP_SD = (1 << 1),
|
|
||||||
OP_DD = (1 << 1),
|
|
||||||
OP_SM = (1 << 2)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct op {
|
|
||||||
u32 (* const fn)(ARMul_State* state, int dd, int dn, int dm, u32 fpscr);
|
|
||||||
u32 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline u32 fls(u32 x)
|
|
||||||
{
|
|
||||||
int r = 32;
|
|
||||||
|
|
||||||
if (!x)
|
|
||||||
return 0;
|
|
||||||
if (!(x & 0xffff0000u)) {
|
|
||||||
x <<= 16;
|
|
||||||
r -= 16;
|
|
||||||
}
|
|
||||||
if (!(x & 0xff000000u)) {
|
|
||||||
x <<= 8;
|
|
||||||
r -= 8;
|
|
||||||
}
|
|
||||||
if (!(x & 0xf0000000u)) {
|
|
||||||
x <<= 4;
|
|
||||||
r -= 4;
|
|
||||||
}
|
|
||||||
if (!(x & 0xc0000000u)) {
|
|
||||||
x <<= 2;
|
|
||||||
r -= 2;
|
|
||||||
}
|
|
||||||
if (!(x & 0x80000000u)) {
|
|
||||||
x <<= 1;
|
|
||||||
r -= 1;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 vfp_double_multiply(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr);
|
|
||||||
u32 vfp_double_add(vfp_double* vdd, vfp_double* vdn, vfp_double *vdm, u32 fpscr);
|
|
||||||
u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, const char* func);
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -9,8 +9,6 @@
|
||||||
#include <dynarmic/A32/a32.h>
|
#include <dynarmic/A32/a32.h>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
|
||||||
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
|
|
||||||
#include "testenv.h"
|
#include "testenv.h"
|
||||||
|
|
||||||
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
template <typename InstructionType, u32 infinite_loop>
|
template <typename InstructionType_, u32 infinite_loop>
|
||||||
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
|
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
|
using InstructionType = InstructionType_;
|
||||||
using RegisterArray = std::array<u32, 16>;
|
using RegisterArray = std::array<u32, 16>;
|
||||||
using ExtRegsArray = std::array<u32, 64>;
|
using ExtRegsArray = std::array<u32, 64>;
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,4 @@
|
||||||
add_executable(dynarmic_tests
|
add_executable(dynarmic_tests
|
||||||
A32/fuzz_arm.cpp
|
|
||||||
A32/fuzz_thumb.cpp
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_dec.cpp
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_dec.h
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_run.h
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_thumb.h
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_trans.cpp
|
|
||||||
A32/skyeye_interpreter/dyncom/arm_dyncom_trans.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/arm_regformat.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/armstate.cpp
|
|
||||||
A32/skyeye_interpreter/skyeye_common/armstate.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/armsupp.cpp
|
|
||||||
A32/skyeye_interpreter/skyeye_common/armsupp.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/vfp.cpp
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/vfp.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp
|
|
||||||
A32/skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp
|
|
||||||
A32/test_arm_disassembler.cpp
|
A32/test_arm_disassembler.cpp
|
||||||
A32/test_thumb_instructions.cpp
|
A32/test_thumb_instructions.cpp
|
||||||
A32/testenv.h
|
A32/testenv.h
|
||||||
|
@ -39,6 +16,8 @@ add_executable(dynarmic_tests
|
||||||
|
|
||||||
if (DYNARMIC_TESTS_USE_UNICORN)
|
if (DYNARMIC_TESTS_USE_UNICORN)
|
||||||
target_sources(dynarmic_tests PRIVATE
|
target_sources(dynarmic_tests PRIVATE
|
||||||
|
A32/fuzz_arm.cpp
|
||||||
|
A32/fuzz_thumb.cpp
|
||||||
A64/fuzz_with_unicorn.cpp
|
A64/fuzz_with_unicorn.cpp
|
||||||
A64/verify_unicorn.cpp
|
A64/verify_unicorn.cpp
|
||||||
fuzz_util.cpp
|
fuzz_util.cpp
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
#include "A32/testenv.h"
|
#include "A32/testenv.h"
|
||||||
#include "a32_unicorn.h"
|
#include "a32_unicorn.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
@ -19,23 +20,31 @@
|
||||||
constexpr u32 BEGIN_ADDRESS = 0;
|
constexpr u32 BEGIN_ADDRESS = 0;
|
||||||
constexpr u32 END_ADDRESS = ~u32(0);
|
constexpr u32 END_ADDRESS = ~u32(0);
|
||||||
|
|
||||||
A32Unicorn::A32Unicorn(ArmTestEnv& testenv) : testenv(testenv) {
|
template <class TestEnvironment>
|
||||||
CHECKED(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc));
|
A32Unicorn<TestEnvironment>::A32Unicorn(TestEnvironment& testenv) : testenv{testenv} {
|
||||||
|
constexpr uc_mode open_mode = std::is_same_v<TestEnvironment, ArmTestEnv> ? UC_MODE_ARM : UC_MODE_THUMB;
|
||||||
|
|
||||||
|
CHECKED(uc_open(UC_ARCH_ARM, open_mode, &uc));
|
||||||
CHECKED(uc_hook_add(uc, &intr_hook, UC_HOOK_INTR, (void*)InterruptHook, this, BEGIN_ADDRESS, END_ADDRESS));
|
CHECKED(uc_hook_add(uc, &intr_hook, UC_HOOK_INTR, (void*)InterruptHook, this, BEGIN_ADDRESS, END_ADDRESS));
|
||||||
CHECKED(uc_hook_add(uc, &mem_invalid_hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, BEGIN_ADDRESS, END_ADDRESS));
|
CHECKED(uc_hook_add(uc, &mem_invalid_hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, this, BEGIN_ADDRESS, END_ADDRESS));
|
||||||
CHECKED(uc_hook_add(uc, &mem_write_prot_hook, UC_HOOK_MEM_WRITE, (void*)MemoryWriteHook, this, BEGIN_ADDRESS, END_ADDRESS));
|
CHECKED(uc_hook_add(uc, &mem_write_prot_hook, UC_HOOK_MEM_WRITE, (void*)MemoryWriteHook, this, BEGIN_ADDRESS, END_ADDRESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
A32Unicorn::~A32Unicorn() {
|
template <class TestEnvironment>
|
||||||
|
A32Unicorn<TestEnvironment>::~A32Unicorn() {
|
||||||
ClearPageCache();
|
ClearPageCache();
|
||||||
CHECKED(uc_hook_del(uc, intr_hook));
|
CHECKED(uc_hook_del(uc, intr_hook));
|
||||||
CHECKED(uc_hook_del(uc, mem_invalid_hook));
|
CHECKED(uc_hook_del(uc, mem_invalid_hook));
|
||||||
CHECKED(uc_close(uc));
|
CHECKED(uc_close(uc));
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::Run() {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::Run() {
|
||||||
|
// Thumb execution mode requires the LSB to be set to 1.
|
||||||
|
constexpr u64 mask = std::is_same_v<TestEnvironment, ArmTestEnv> ? 0 : 1;
|
||||||
|
|
||||||
while (testenv.ticks_left > 0) {
|
while (testenv.ticks_left > 0) {
|
||||||
CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1));
|
CHECKED(uc_emu_start(uc, GetPC() | mask, END_ADDRESS, 0, 1));
|
||||||
testenv.ticks_left--;
|
testenv.ticks_left--;
|
||||||
if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) {
|
if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) {
|
||||||
return;
|
return;
|
||||||
|
@ -43,63 +52,72 @@ void A32Unicorn::Run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 A32Unicorn::GetPC() const {
|
template <class TestEnvironment>
|
||||||
|
u32 A32Unicorn<TestEnvironment>::GetPC() const {
|
||||||
u32 pc;
|
u32 pc;
|
||||||
CHECKED(uc_reg_read(uc, UC_ARM_REG_PC, &pc));
|
CHECKED(uc_reg_read(uc, UC_ARM_REG_PC, &pc));
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::SetPC(u32 value) {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::SetPC(u32 value) {
|
||||||
CHECKED(uc_reg_write(uc, UC_ARM_REG_PC, &value));
|
CHECKED(uc_reg_write(uc, UC_ARM_REG_PC, &value));
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 A32Unicorn::GetSP() const {
|
template <class TestEnvironment>
|
||||||
|
u32 A32Unicorn<TestEnvironment>::GetSP() const {
|
||||||
u32 sp;
|
u32 sp;
|
||||||
CHECKED(uc_reg_read(uc, UC_ARM_REG_SP, &sp));
|
CHECKED(uc_reg_read(uc, UC_ARM_REG_SP, &sp));
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::SetSP(u32 value) {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::SetSP(u32 value) {
|
||||||
CHECKED(uc_reg_write(uc, UC_ARM_REG_SP, &value));
|
CHECKED(uc_reg_write(uc, UC_ARM_REG_SP, &value));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<int, A32Unicorn::num_gprs> gpr_ids{
|
constexpr std::array<int, Unicorn::A32::num_gprs> gpr_ids{
|
||||||
UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7,
|
UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7,
|
||||||
UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, UC_ARM_REG_R12, UC_ARM_REG_R13, UC_ARM_REG_R14, UC_ARM_REG_R15,
|
UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, UC_ARM_REG_R12, UC_ARM_REG_R13, UC_ARM_REG_R14, UC_ARM_REG_R15,
|
||||||
};
|
};
|
||||||
|
|
||||||
A32Unicorn::RegisterArray A32Unicorn::GetRegisters() const {
|
template <class TestEnvironment>
|
||||||
RegisterArray regs;
|
Unicorn::A32::RegisterArray A32Unicorn<TestEnvironment>::GetRegisters() const {
|
||||||
RegisterPtrArray ptrs;
|
Unicorn::A32::RegisterArray regs;
|
||||||
for (size_t i = 0; i < ptrs.size(); ++i)
|
Unicorn::A32::RegisterPtrArray ptrs;
|
||||||
|
for (size_t i = 0; i < ptrs.size(); ++i) {
|
||||||
ptrs[i] = ®s[i];
|
ptrs[i] = ®s[i];
|
||||||
|
}
|
||||||
|
|
||||||
CHECKED(uc_reg_read_batch(uc, const_cast<int*>(gpr_ids.data()),
|
CHECKED(uc_reg_read_batch(uc, const_cast<int*>(gpr_ids.data()),
|
||||||
reinterpret_cast<void**>(ptrs.data()), num_gprs));
|
reinterpret_cast<void**>(ptrs.data()), Unicorn::A32::num_gprs));
|
||||||
return regs;
|
return regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::SetRegisters(const RegisterArray& value) {
|
template <class TestEnvironment>
|
||||||
RegisterConstPtrArray ptrs;
|
void A32Unicorn<TestEnvironment>::SetRegisters(const RegisterArray& value) {
|
||||||
for (size_t i = 0; i < ptrs.size(); ++i)
|
Unicorn::A32::RegisterConstPtrArray ptrs;
|
||||||
|
for (size_t i = 0; i < ptrs.size(); ++i) {
|
||||||
ptrs[i] = &value[i];
|
ptrs[i] = &value[i];
|
||||||
|
}
|
||||||
|
|
||||||
CHECKED(uc_reg_write_batch(uc, const_cast<int*>(gpr_ids.data()),
|
CHECKED(uc_reg_write_batch(uc, const_cast<int*>(gpr_ids.data()),
|
||||||
reinterpret_cast<void**>(const_cast<u32**>(ptrs.data())), ptrs.size()));
|
reinterpret_cast<void**>(const_cast<u32**>(ptrs.data())), ptrs.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
using DoubleExtRegPtrArray = std::array<A32Unicorn::ExtRegArray::pointer, A32Unicorn::num_ext_regs/2>;
|
using DoubleExtRegPtrArray = std::array<Unicorn::A32::ExtRegArray::pointer, Unicorn::A32::num_ext_regs/2>;
|
||||||
using DoubleExtRegConstPtrArray = std::array<A32Unicorn::ExtRegArray::const_pointer, A32Unicorn::num_ext_regs/2>;
|
using DoubleExtRegConstPtrArray = std::array<Unicorn::A32::ExtRegArray::const_pointer, Unicorn::A32::num_ext_regs/2>;
|
||||||
|
|
||||||
constexpr std::array<int, A32Unicorn::num_ext_regs/2> double_ext_reg_ids{
|
constexpr std::array<int, Unicorn::A32::num_ext_regs/2> double_ext_reg_ids{
|
||||||
UC_ARM_REG_D0, UC_ARM_REG_D1, UC_ARM_REG_D2, UC_ARM_REG_D3, UC_ARM_REG_D4, UC_ARM_REG_D5, UC_ARM_REG_D6, UC_ARM_REG_D7,
|
UC_ARM_REG_D0, UC_ARM_REG_D1, UC_ARM_REG_D2, UC_ARM_REG_D3, UC_ARM_REG_D4, UC_ARM_REG_D5, UC_ARM_REG_D6, UC_ARM_REG_D7,
|
||||||
UC_ARM_REG_D8, UC_ARM_REG_D9, UC_ARM_REG_D10, UC_ARM_REG_D11, UC_ARM_REG_D12, UC_ARM_REG_D13, UC_ARM_REG_D14, UC_ARM_REG_D15,
|
UC_ARM_REG_D8, UC_ARM_REG_D9, UC_ARM_REG_D10, UC_ARM_REG_D11, UC_ARM_REG_D12, UC_ARM_REG_D13, UC_ARM_REG_D14, UC_ARM_REG_D15,
|
||||||
UC_ARM_REG_D16, UC_ARM_REG_D17, UC_ARM_REG_D18, UC_ARM_REG_D19, UC_ARM_REG_D20, UC_ARM_REG_D21, UC_ARM_REG_D22, UC_ARM_REG_D23,
|
UC_ARM_REG_D16, UC_ARM_REG_D17, UC_ARM_REG_D18, UC_ARM_REG_D19, UC_ARM_REG_D20, UC_ARM_REG_D21, UC_ARM_REG_D22, UC_ARM_REG_D23,
|
||||||
UC_ARM_REG_D24, UC_ARM_REG_D25, UC_ARM_REG_D26, UC_ARM_REG_D27, UC_ARM_REG_D28, UC_ARM_REG_D29, UC_ARM_REG_D30, UC_ARM_REG_D31,
|
UC_ARM_REG_D24, UC_ARM_REG_D25, UC_ARM_REG_D26, UC_ARM_REG_D27, UC_ARM_REG_D28, UC_ARM_REG_D29, UC_ARM_REG_D30, UC_ARM_REG_D31,
|
||||||
};
|
};
|
||||||
|
|
||||||
A32Unicorn::ExtRegArray A32Unicorn::GetExtRegs() const {
|
template <class TestEnvironment>
|
||||||
ExtRegArray ext_regs;
|
Unicorn::A32::ExtRegArray A32Unicorn<TestEnvironment>::GetExtRegs() const {
|
||||||
|
Unicorn::A32::ExtRegArray ext_regs;
|
||||||
DoubleExtRegPtrArray ptrs;
|
DoubleExtRegPtrArray ptrs;
|
||||||
for (size_t i = 0; i < ptrs.size(); ++i)
|
for (size_t i = 0; i < ptrs.size(); ++i)
|
||||||
ptrs[i] = &ext_regs[i*2];
|
ptrs[i] = &ext_regs[i*2];
|
||||||
|
@ -110,43 +128,69 @@ A32Unicorn::ExtRegArray A32Unicorn::GetExtRegs() const {
|
||||||
return ext_regs;
|
return ext_regs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::SetExtRegs(const ExtRegArray& value) {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::SetExtRegs(const ExtRegArray& value) {
|
||||||
DoubleExtRegConstPtrArray ptrs;
|
DoubleExtRegConstPtrArray ptrs;
|
||||||
for (size_t i = 0; i < ptrs.size(); ++i)
|
for (size_t i = 0; i < ptrs.size(); ++i) {
|
||||||
ptrs[i] = &value[i*2];
|
ptrs[i] = &value[i*2];
|
||||||
|
}
|
||||||
|
|
||||||
CHECKED(uc_reg_write_batch(uc, const_cast<int*>(double_ext_reg_ids.data()),
|
CHECKED(uc_reg_write_batch(uc, const_cast<int*>(double_ext_reg_ids.data()),
|
||||||
reinterpret_cast<void* const *>(const_cast<u32**>(ptrs.data())), ptrs.size()));
|
reinterpret_cast<void* const *>(const_cast<u32**>(ptrs.data())), ptrs.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 A32Unicorn::GetFpscr() const {
|
template <class TestEnvironment>
|
||||||
|
u32 A32Unicorn<TestEnvironment>::GetFpscr() const {
|
||||||
u32 fpsr;
|
u32 fpsr;
|
||||||
CHECKED(uc_reg_read(uc, UC_ARM_REG_FPSCR, &fpsr));
|
CHECKED(uc_reg_read(uc, UC_ARM_REG_FPSCR, &fpsr));
|
||||||
return fpsr;
|
return fpsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::SetFpscr(u32 value) {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::SetFpscr(u32 value) {
|
||||||
CHECKED(uc_reg_write(uc, UC_ARM_REG_FPSCR, &value));
|
CHECKED(uc_reg_write(uc, UC_ARM_REG_FPSCR, &value));
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 A32Unicorn::GetCpsr() const {
|
template <class TestEnvironment>
|
||||||
|
u32 A32Unicorn<TestEnvironment>::GetFpexc() const {
|
||||||
|
u32 value = 0;
|
||||||
|
CHECKED(uc_reg_read(uc, UC_ARM_REG_FPEXC, &value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::SetFpexc(u32 value) {
|
||||||
|
CHECKED(uc_reg_write(uc, UC_ARM_REG_FPEXC, &value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestEnvironment>
|
||||||
|
u32 A32Unicorn<TestEnvironment>::GetCpsr() const {
|
||||||
u32 pstate;
|
u32 pstate;
|
||||||
CHECKED(uc_reg_read(uc, UC_ARM_REG_CPSR, &pstate));
|
CHECKED(uc_reg_read(uc, UC_ARM_REG_CPSR, &pstate));
|
||||||
return pstate;
|
return pstate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::SetCpsr(u32 value) {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::SetCpsr(u32 value) {
|
||||||
CHECKED(uc_reg_write(uc, UC_ARM_REG_CPSR, &value));
|
CHECKED(uc_reg_write(uc, UC_ARM_REG_CPSR, &value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::ClearPageCache() {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::EnableFloatingPointAccess() {
|
||||||
|
const u32 new_fpexc = GetFpexc() | (1U << 30);
|
||||||
|
SetFpexc(new_fpexc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::ClearPageCache() {
|
||||||
for (const auto& page : pages) {
|
for (const auto& page : pages) {
|
||||||
CHECKED(uc_mem_unmap(uc, page->address, 4096));
|
CHECKED(uc_mem_unmap(uc, page->address, 4096));
|
||||||
}
|
}
|
||||||
pages.clear();
|
pages.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::DumpMemoryInformation() {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::DumpMemoryInformation() {
|
||||||
uc_mem_region* regions;
|
uc_mem_region* regions;
|
||||||
u32 count;
|
u32 count;
|
||||||
CHECKED(uc_mem_regions(uc, ®ions, &count));
|
CHECKED(uc_mem_regions(uc, ®ions, &count));
|
||||||
|
@ -158,7 +202,8 @@ void A32Unicorn::DumpMemoryInformation() {
|
||||||
CHECKED(uc_free(regions));
|
CHECKED(uc_free(regions));
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32Unicorn::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_data) {
|
template <class TestEnvironment>
|
||||||
|
void A32Unicorn<TestEnvironment>::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_data) {
|
||||||
auto* this_ = static_cast<A32Unicorn*>(user_data);
|
auto* this_ = static_cast<A32Unicorn*>(user_data);
|
||||||
|
|
||||||
u32 esr = 0;
|
u32 esr = 0;
|
||||||
|
@ -177,15 +222,17 @@ void A32Unicorn::InterruptHook(uc_engine* /*uc*/, u32 int_number, void* user_dat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 start_address, int size, u64 /*value*/, void* user_data) {
|
template <class TestEnvironment>
|
||||||
|
bool A32Unicorn<TestEnvironment>::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 start_address, int size, u64 /*value*/, void* user_data) {
|
||||||
auto* this_ = static_cast<A32Unicorn*>(user_data);
|
auto* this_ = static_cast<A32Unicorn*>(user_data);
|
||||||
|
|
||||||
const auto generate_page = [&](u32 base_address) {
|
const auto generate_page = [&](u32 base_address) {
|
||||||
// printf("generate_page(%x)\n", base_address);
|
// printf("generate_page(%x)\n", base_address);
|
||||||
|
|
||||||
const u32 permissions = [&]() -> u32 {
|
const u32 permissions = [&]() -> u32 {
|
||||||
if (base_address < this_->testenv.code_mem.size() * 4)
|
if (base_address < this_->testenv.code_mem.size() * sizeof(typename TestEnvironment::InstructionType)) {
|
||||||
return UC_PROT_READ | UC_PROT_EXEC;
|
return UC_PROT_READ | UC_PROT_EXEC;
|
||||||
|
}
|
||||||
return UC_PROT_READ;
|
return UC_PROT_READ;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
@ -220,7 +267,8 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 sta
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) {
|
template <class TestEnvironment>
|
||||||
|
bool A32Unicorn<TestEnvironment>::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 start_address, int size, u64 value, void* user_data) {
|
||||||
auto* this_ = static_cast<A32Unicorn*>(user_data);
|
auto* this_ = static_cast<A32Unicorn*>(user_data);
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
|
@ -242,3 +290,6 @@ bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 st
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template class A32Unicorn<ArmTestEnv>;
|
||||||
|
template class A32Unicorn<ThumbTestEnv>;
|
||||||
|
|
|
@ -15,17 +15,23 @@
|
||||||
|
|
||||||
#include "A32/testenv.h"
|
#include "A32/testenv.h"
|
||||||
|
|
||||||
|
namespace Unicorn::A32 {
|
||||||
|
static constexpr size_t num_gprs = 16;
|
||||||
|
static constexpr size_t num_ext_regs = 64;
|
||||||
|
|
||||||
|
using ExtRegArray = std::array<u32, num_ext_regs>;
|
||||||
|
using RegisterArray = std::array<u32, num_gprs>;
|
||||||
|
using RegisterPtrArray = std::array<RegisterArray::pointer, num_gprs>;
|
||||||
|
using RegisterConstPtrArray = std::array<RegisterArray::const_pointer, num_gprs>;
|
||||||
|
} // namespace Unicorn::A32
|
||||||
|
|
||||||
|
template <class TestEnvironment>
|
||||||
class A32Unicorn final {
|
class A32Unicorn final {
|
||||||
public:
|
public:
|
||||||
static constexpr size_t num_gprs = 16;
|
using ExtRegArray = Unicorn::A32::ExtRegArray;
|
||||||
using RegisterArray = std::array<u32, num_gprs>;
|
using RegisterArray = Unicorn::A32::RegisterArray;
|
||||||
using RegisterPtrArray = std::array<RegisterArray::pointer, num_gprs>;
|
|
||||||
using RegisterConstPtrArray = std::array<RegisterArray::const_pointer, num_gprs>;
|
|
||||||
|
|
||||||
static constexpr size_t num_ext_regs = 64;
|
explicit A32Unicorn(TestEnvironment& testenv);
|
||||||
using ExtRegArray = std::array<u32, num_ext_regs>;
|
|
||||||
|
|
||||||
explicit A32Unicorn(ArmTestEnv& testenv);
|
|
||||||
~A32Unicorn();
|
~A32Unicorn();
|
||||||
|
|
||||||
void Run();
|
void Run();
|
||||||
|
@ -45,9 +51,14 @@ public:
|
||||||
u32 GetFpscr() const;
|
u32 GetFpscr() const;
|
||||||
void SetFpscr(u32 value);
|
void SetFpscr(u32 value);
|
||||||
|
|
||||||
|
u32 GetFpexc() const;
|
||||||
|
void SetFpexc(u32 value);
|
||||||
|
|
||||||
u32 GetCpsr() const;
|
u32 GetCpsr() const;
|
||||||
void SetCpsr(u32 value);
|
void SetCpsr(u32 value);
|
||||||
|
|
||||||
|
void EnableFloatingPointAccess();
|
||||||
|
|
||||||
void ClearPageCache();
|
void ClearPageCache();
|
||||||
|
|
||||||
void DumpMemoryInformation();
|
void DumpMemoryInformation();
|
||||||
|
@ -62,7 +73,7 @@ private:
|
||||||
std::array<u8, 4096> data;
|
std::array<u8, 4096> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
ArmTestEnv& testenv;
|
TestEnvironment& testenv;
|
||||||
uc_engine* uc{};
|
uc_engine* uc{};
|
||||||
uc_hook intr_hook{};
|
uc_hook intr_hook{};
|
||||||
uc_hook mem_invalid_hook{};
|
uc_hook mem_invalid_hook{};
|
||||||
|
|
Loading…
Reference in a new issue