Merge branch 'fuzz'

This commit is contained in:
Lioncash 2019-04-20 04:19:37 -04:00 committed by MerryMage
commit a77ca35ec3
28 changed files with 232 additions and 14670 deletions

View file

@ -31,19 +31,18 @@
#include "ir_opt/passes.h"
#include "rand_int.h"
#include "testenv.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
#include "unicorn_emu/a32_unicorn.h"
using Dynarmic::Common::Bits;
static Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
namespace {
Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv* testenv) {
Dynarmic::A32::UserConfig user_config;
user_config.enable_fast_dispatch = false;
user_config.callbacks = testenv;
return user_config;
}
namespace {
struct InstructionGenerator final {
public:
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;
std::function<bool(u32)> is_valid;
};
} // namespace
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) {
return interp.Reg == jit.Regs()
&& interp.ExtReg == jit.ExtRegs()
&& interp.Cpsr == jit.Cpsr()
//&& interp.VFP[VFP_FPSCR] == jit.Fpscr()
&& interp_write_records == jit_write_records;
bool DoesBehaviorMatch(const A32Unicorn<ArmTestEnv>& uni, const Dynarmic::A32::Jit& jit,
const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
return uni.GetRegisters() == jit.Regs() &&
uni.GetExtRegs() == jit.ExtRegs() &&
uni.GetCpsr() == jit.Cpsr() &&
// 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) {
@ -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
// Prepare test subjects
ARMul_State interp{USER32MODE};
interp.user_callbacks = &test_env;
A32Unicorn<ArmTestEnv> uni{test_env};
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
for (size_t run_number = 0; run_number < run_count; run_number++) {
interp.instruction_cache.clear();
InterpreterClearCache();
uni.ClearPageCache();
jit.ClearCache();
// Setup initial state
u32 initial_cpsr = 0x000001D0;
const u32 initial_cpsr = 0x000001D0;
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;
ArmTestEnv::ExtRegsArray initial_extregs;
std::generate(initial_extregs.begin(), initial_extregs.end(),
[]{ 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();
interp.Cpsr = initial_cpsr;
interp.Reg = initial_regs;
interp.ExtReg = initial_extregs;
interp.VFP[VFP_FPSCR] = initial_fpscr;
uni.SetCpsr(initial_cpsr);
uni.SetRegisters(initial_regs);
uni.SetExtRegs(initial_extregs);
uni.SetFpscr(initial_fpscr);
uni.EnableFloatingPointAccess();
jit.Reset();
jit.SetCpsr(initial_cpsr);
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");
for (int i = 0; i <= 15; i++) {
auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
for (size_t i = 0; i < initial_regs.size(); i++) {
const auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
printf("%4s: %08x\n", reg, initial_regs[i]);
}
printf("CPSR: %08x\n", initial_cpsr);
printf("FPSCR:%08x\n", initial_fpscr);
for (int i = 0; i <= 63; i++) {
printf("S%3i: %08x\n", i, initial_extregs[i]);
for (size_t i = 0; i < initial_extregs.size(); i++) {
printf("S%3zu: %08x\n", i, initial_extregs[i]);
}
printf("\nFinal Register Listing: \n");
printf(" interp jit\n");
for (int i = 0; i <= 15; i++) {
auto reg = Dynarmic::A32::RegToString(static_cast<Dynarmic::A32::Reg>(i));
printf("%4s: %08x %08x %s\n", reg, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
printf(" unicorn jit\n");
const auto uni_registers = uni.GetRegisters();
for (size_t i = 0; i < uni_registers.size(); 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("FPSCR:%08x %08x %s\n", interp.VFP[VFP_FPSCR], jit.Fpscr(), interp.VFP[VFP_FPSCR] != jit.Fpscr() ? "*" : "");
for (int i = 0; i <= 63; i++) {
printf("S%3i: %08x %08x %s\n", i, interp.ExtReg[i], jit.ExtRegs()[i], interp.ExtReg[i] != jit.ExtRegs()[i] ? "*" : "");
printf("CPSR: %08x %08x %s\n", uni.GetCpsr(), jit.Cpsr(), uni.GetCpsr() != jit.Cpsr() ? "*" : "");
printf("FPSCR:%08x %08x %s\n", uni.GetFpscr(), jit.Fpscr(), uni.GetFpscr() != jit.Fpscr() ? "*" : "");
const auto uni_ext_regs = uni.GetExtRegs();
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");
for (auto& record : interp_write_records) {
for (const auto& record : interp_write_records) {
printf("[%08x] = %02x\n", record.first, record.second);
}
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);
}
@ -209,12 +208,14 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
// Run interpreter
test_env.modified_memory.clear();
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
InterpreterMainLoop(&interp);
test_env.ticks_left = instructions_to_execute_count;
uni.Run();
interp_write_records = test_env.modified_memory;
{
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
const bool T = Dynarmic::Common::Bit<5>(uni.GetCpsr());
const u32 mask = T ? 0xFFFFFFFE : 0xFFFFFFFC;
const u32 new_pc = uni.GetPC() & mask;
uni.SetPC(new_pc);
}
// Run jit
@ -223,9 +224,10 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
jit.Run();
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]" ) {
// This was a randomized test-case that was failing.

View file

@ -27,8 +27,7 @@
#include "ir_opt/passes.h"
#include "rand_int.h"
#include "testenv.h"
#include "A32/skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
#include "A32/skyeye_interpreter/skyeye_common/armstate.h"
#include "unicorn_emu/a32_unicorn.h"
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {
Dynarmic::A32::UserConfig user_config;
@ -64,7 +63,7 @@ public:
u16 inst;
do {
u16 random = RandInt<u16>(0, 0xFFFF);
const u16 random = RandInt<u16>(0, 0xFFFF);
inst = bits | (random & ~mask);
} while (!is_valid(inst));
@ -78,46 +77,52 @@ private:
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) {
const auto interp_regs = interp.Reg;
static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const Dynarmic::A32::Jit& jit,
const WriteRecords& interp_write_records, const WriteRecords& jit_write_records) {
const auto interp_regs = uni.GetRegisters();
const auto jit_regs = jit.Regs();
return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end())
&& interp.Cpsr == jit.Cpsr()
&& interp_write_records == jit_write_records;
return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) &&
uni.GetCpsr() == jit.Cpsr() &&
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) {
interp.instruction_cache.clear();
InterpreterClearCache();
uni.ClearPageCache();
jit.ClearCache();
// Setup initial state
interp.Cpsr = 0x000001F0;
interp.Reg = initial_regs;
uni.SetCpsr(0x000001F0);
uni.SetRegisters(initial_regs);
jit.SetCpsr(0x000001F0);
jit.Regs() = initial_regs;
// Run interpreter
test_env.modified_memory.clear();
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
InterpreterMainLoop(&interp);
auto interp_write_records = test_env.modified_memory;
{
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
}
test_env.ticks_left = instructions_to_execute_count;
uni.SetPC(uni.GetPC() | 1);
uni.Run();
const bool uni_code_memory_modified = test_env.code_mem_modified_by_guest;
const auto interp_write_records = test_env.modified_memory;
// Run jit
test_env.code_mem_modified_by_guest = false;
test_env.modified_memory.clear();
test_env.ticks_left = instructions_to_execute_count;
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
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("\nInstruction Listing: \n");
@ -126,24 +131,25 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, ARMul_State&
}
printf("\nInitial Register Listing: \n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x\n", i, initial_regs[i]);
for (size_t i = 0; i < initial_regs.size(); i++) {
printf("%4zu: %08x\n", i, initial_regs[i]);
}
printf("\nFinal Register Listing: \n");
printf(" interp jit\n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
printf(" unicorn jit\n");
const auto uni_registers = uni.GetRegisters();
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");
for (auto& record : interp_write_records) {
printf("\nUnicorn Write Records:\n");
for (const auto& record : interp_write_records) {
printf("[%08x] = %02x\n", record.first, record.second);
}
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);
}
@ -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) {
ThumbTestEnv test_env;
// Prepare memory
// Prepare memory.
test_env.code_mem.resize(instruction_count + 1);
test_env.code_mem.back() = 0xE7FE; // b +#0
// Prepare test subjects
ARMul_State interp{USER32MODE};
interp.user_callbacks = &test_env;
A32Unicorn uni{test_env};
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
for (size_t run_number = 0; run_number < run_count; run_number++) {
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;
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]") {
const std::array<ThumbInstGen, 25> instructions = {{
const std::array instructions = {
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <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
ThumbInstGen("10111100xxxxxxxx", // POP (P = 0)
[](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
}};
#endif
};
auto instruction_select = [&]() -> u16 {
const auto instruction_select = [&]() -> u16 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
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]") {
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
[](u16 inst){
u32 Rm = Dynarmic::Common::Bits<3, 6>(inst);
const u32 Rm = Dynarmic::Common::Bits<3, 6>(inst);
return Rm != 15;
}),
#endif
ThumbInstGen("1010oxxxxxxxxxxx"), // add to pc/sp
ThumbInstGen("11100xxxxxxxxxxx"), // B
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
[](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.
}),
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);
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;
// Prepare test subjects
ARMul_State interp{USER32MODE};
interp.user_callbacks = &test_env;
A32Unicorn<ThumbTestEnv> uni{test_env};
Dynarmic::A32::Jit jit{GetUserConfig(&test_env)};
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
};
RunInstance(1, test_env, interp, jit, initial_regs, 5, 5);
RunInstance(1, test_env, uni, jit, initial_regs, 5, 5);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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();

View file

@ -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];
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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,
};

View file

@ -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;
}
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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*);

View file

@ -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)
};

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -9,8 +9,6 @@
#include <dynarmic/A32/a32.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"
static Dynarmic::A32::UserConfig GetUserConfig(ThumbTestEnv* testenv) {

View file

@ -17,9 +17,10 @@
#include "common/assert.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 {
public:
using InstructionType = InstructionType_;
using RegisterArray = std::array<u32, 16>;
using ExtRegsArray = std::array<u32, 64>;

View file

@ -1,27 +1,4 @@
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_thumb_instructions.cpp
A32/testenv.h
@ -39,6 +16,8 @@ add_executable(dynarmic_tests
if (DYNARMIC_TESTS_USE_UNICORN)
target_sources(dynarmic_tests PRIVATE
A32/fuzz_arm.cpp
A32/fuzz_thumb.cpp
A64/fuzz_with_unicorn.cpp
A64/verify_unicorn.cpp
fuzz_util.cpp

View file

@ -4,6 +4,7 @@
* General Public License version 2 or any later version.
*/
#include <type_traits>
#include "A32/testenv.h"
#include "a32_unicorn.h"
#include "common/assert.h"
@ -19,23 +20,31 @@
constexpr u32 BEGIN_ADDRESS = 0;
constexpr u32 END_ADDRESS = ~u32(0);
A32Unicorn::A32Unicorn(ArmTestEnv& testenv) : testenv(testenv) {
CHECKED(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc));
template <class TestEnvironment>
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, &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));
}
A32Unicorn::~A32Unicorn() {
template <class TestEnvironment>
A32Unicorn<TestEnvironment>::~A32Unicorn() {
ClearPageCache();
CHECKED(uc_hook_del(uc, intr_hook));
CHECKED(uc_hook_del(uc, mem_invalid_hook));
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) {
CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1));
CHECKED(uc_emu_start(uc, GetPC() | mask, END_ADDRESS, 0, 1));
testenv.ticks_left--;
if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) {
return;
@ -43,63 +52,72 @@ void A32Unicorn::Run() {
}
}
u32 A32Unicorn::GetPC() const {
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetPC() const {
u32 pc;
CHECKED(uc_reg_read(uc, UC_ARM_REG_PC, &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));
}
u32 A32Unicorn::GetSP() const {
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetSP() const {
u32 sp;
CHECKED(uc_reg_read(uc, UC_ARM_REG_SP, &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));
}
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_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 {
RegisterArray regs;
RegisterPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i)
template <class TestEnvironment>
Unicorn::A32::RegisterArray A32Unicorn<TestEnvironment>::GetRegisters() const {
Unicorn::A32::RegisterArray regs;
Unicorn::A32::RegisterPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i) {
ptrs[i] = &regs[i];
}
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;
}
void A32Unicorn::SetRegisters(const RegisterArray& value) {
RegisterConstPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i)
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetRegisters(const RegisterArray& value) {
Unicorn::A32::RegisterConstPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i) {
ptrs[i] = &value[i];
}
CHECKED(uc_reg_write_batch(uc, const_cast<int*>(gpr_ids.data()),
reinterpret_cast<void**>(const_cast<u32**>(ptrs.data())), ptrs.size()));
}
using DoubleExtRegPtrArray = std::array<A32Unicorn::ExtRegArray::pointer, A32Unicorn::num_ext_regs/2>;
using DoubleExtRegConstPtrArray = std::array<A32Unicorn::ExtRegArray::const_pointer, A32Unicorn::num_ext_regs/2>;
using DoubleExtRegPtrArray = std::array<Unicorn::A32::ExtRegArray::pointer, Unicorn::A32::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_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_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 {
ExtRegArray ext_regs;
template <class TestEnvironment>
Unicorn::A32::ExtRegArray A32Unicorn<TestEnvironment>::GetExtRegs() const {
Unicorn::A32::ExtRegArray ext_regs;
DoubleExtRegPtrArray ptrs;
for (size_t i = 0; i < ptrs.size(); ++i)
ptrs[i] = &ext_regs[i*2];
@ -110,43 +128,69 @@ A32Unicorn::ExtRegArray A32Unicorn::GetExtRegs() const {
return ext_regs;
}
void A32Unicorn::SetExtRegs(const ExtRegArray& value) {
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::SetExtRegs(const ExtRegArray& value) {
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];
}
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()));
}
u32 A32Unicorn::GetFpscr() const {
template <class TestEnvironment>
u32 A32Unicorn<TestEnvironment>::GetFpscr() const {
u32 fpsr;
CHECKED(uc_reg_read(uc, UC_ARM_REG_FPSCR, &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));
}
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;
CHECKED(uc_reg_read(uc, UC_ARM_REG_CPSR, &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));
}
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) {
CHECKED(uc_mem_unmap(uc, page->address, 4096));
}
pages.clear();
}
void A32Unicorn::DumpMemoryInformation() {
template <class TestEnvironment>
void A32Unicorn<TestEnvironment>::DumpMemoryInformation() {
uc_mem_region* regions;
u32 count;
CHECKED(uc_mem_regions(uc, &regions, &count));
@ -158,7 +202,8 @@ void A32Unicorn::DumpMemoryInformation() {
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);
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);
const auto generate_page = [&](u32 base_address) {
// printf("generate_page(%x)\n", base_address);
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;
}();
@ -220,7 +267,8 @@ bool A32Unicorn::UnmappedMemoryHook(uc_engine* uc, uc_mem_type /*type*/, u32 sta
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);
switch (size) {
@ -242,3 +290,6 @@ bool A32Unicorn::MemoryWriteHook(uc_engine* /*uc*/, uc_mem_type /*type*/, u32 st
return true;
}
template class A32Unicorn<ArmTestEnv>;
template class A32Unicorn<ThumbTestEnv>;

View file

@ -15,17 +15,23 @@
#include "A32/testenv.h"
class A32Unicorn final {
public:
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
static constexpr size_t num_ext_regs = 64;
using ExtRegArray = std::array<u32, num_ext_regs>;
template <class TestEnvironment>
class A32Unicorn final {
public:
using ExtRegArray = Unicorn::A32::ExtRegArray;
using RegisterArray = Unicorn::A32::RegisterArray;
explicit A32Unicorn(ArmTestEnv& testenv);
explicit A32Unicorn(TestEnvironment& testenv);
~A32Unicorn();
void Run();
@ -45,9 +51,14 @@ public:
u32 GetFpscr() const;
void SetFpscr(u32 value);
u32 GetFpexc() const;
void SetFpexc(u32 value);
u32 GetCpsr() const;
void SetCpsr(u32 value);
void EnableFloatingPointAccess();
void ClearPageCache();
void DumpMemoryInformation();
@ -62,7 +73,7 @@ private:
std::array<u8, 4096> data;
};
ArmTestEnv& testenv;
TestEnvironment& testenv;
uc_engine* uc{};
uc_hook intr_hook{};
uc_hook mem_invalid_hook{};