Reorganisation, Import Skyeye, This is a mess

This commit is contained in:
MerryMage 2016-07-04 17:22:11 +08:00
parent be1c1de552
commit d743adf518
55 changed files with 14939 additions and 591 deletions

View file

@ -5,7 +5,7 @@ project(dynarmic)
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON) option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
# Compiler flags # Compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14 -Wall -Werror -Wextra -pedantic -Wfatal-errors -Wno-unused-parameter -static-libgcc -static-libstdc++") add_compile_options(--std=c++14 -Wall -Werror -Wextra -pedantic -Wfatal-errors -Wno-unused-parameter -static-libgcc -static-libstdc++)
# Arch detection # Arch detection
include(CheckSymbolExists) include(CheckSymbolExists)
@ -56,3 +56,4 @@ enable_testing(true) # Enables unit-testing.
# Dynarmic project files # Dynarmic project files
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(tests)

View file

@ -1,6 +1,52 @@
include_directories(.) include_directories(.)
add_subdirectory(backend_x64) set(SRCS
add_subdirectory(common) backend_x64/emit_x64.cpp
add_subdirectory(frontend_arm) backend_x64/interface_x64.cpp
add_subdirectory(tests) backend_x64/reg_alloc.cpp
backend_x64/routines.cpp
common/logging/log.cpp
common/memory_util.cpp
common/string_util.cpp
common/x64/abi.cpp
common/x64/cpu_detect.cpp
common/x64/emitter.cpp
frontend/disassembler_arm.cpp
frontend/ir/ir.cpp
frontend/ir_emitter.cpp
)
set(HEADERS
backend_x64/emit_x64.h
backend_x64/jitstate.h
backend_x64/reg_alloc.h
backend_x64/routines.h
common/assert.h
common/bit_set.h
common/bit_util.h
common/code_block.h
common/common_types.h
common/logging/log.h
common/memory_util.h
common/mp.h
common/scope_exit.h
common/string_util.h
common/x64/abi.h
common/x64/cpu_detect.h
common/x64/emitter.h
frontend/arm_types.h
frontend/decoder/arm.h
frontend/decoder/decoder_detail.h
frontend/decoder/thumb1.h
frontend/disassembler_arm.h
frontend/frontend_arm.h
frontend/ir/ir.h
frontend/ir/opcodes.h
frontend/ir_emitter.h
frontend/translate_thumb.h
interface/interface.h
)
source_group(dynarmic FILES ${SRCS} ${HEADERS})
add_library(dynarmic STATIC ${SRCS} ${HEADERS})
set_target_properties(dynarmic PROPERTIES LINKER_LANGUAGE CXX)

View file

@ -1,18 +0,0 @@
set(SRCS
emit_x64.cpp
reg_alloc.cpp
routines.cpp
)
set(HEADERS
../interface/interface.h
emit_x64.h
jitstate.h
reg_alloc.h
routines.h
)
source_group(frontend_x64 FILES ${SRCS} ${HEADERS})
add_library(dynarmic_backend_x64 STATIC ${SRCS} ${HEADERS})
target_link_libraries(dynarmic_backend_x64 dynarmic_common)
set_target_properties(dynarmic_backend_x64 PROPERTIES LINKER_LANGUAGE CXX)

View file

@ -22,7 +22,7 @@ namespace BackendX64 {
// Mapping from opcode to Emit* member function. // Mapping from opcode to Emit* member function.
const static std::map<IR::Opcode, void (EmitX64::*)(IR::Value*)> emit_fns { const static std::map<IR::Opcode, void (EmitX64::*)(IR::Value*)> emit_fns {
#define OPCODE(name, type, ...) { IR::Opcode::name, &EmitX64::Emit##name }, #define OPCODE(name, type, ...) { IR::Opcode::name, &EmitX64::Emit##name },
#include "frontend_arm/ir/opcodes.inc" #include "frontend/ir/opcodes.inc"
#undef OPCODE #undef OPCODE
}; };
@ -33,7 +33,7 @@ static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) {
return iter == uses.end() ? nullptr : reinterpret_cast<IR::Inst*>(iter->get()); return iter == uses.end() ? nullptr : reinterpret_cast<IR::Inst*>(iter->get());
} }
CodePtr EmitX64::Emit(Dynarmic::IR::Block block) { CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block block) {
code->INT3(); code->INT3();
CodePtr code_ptr = code->GetCodePtr(); CodePtr code_ptr = code->GetCodePtr();
@ -98,6 +98,8 @@ void EmitX64::EmitGetNFlag(IR::Value* value_) {
X64Reg result = reg_alloc.DefRegister(value); X64Reg result = reg_alloc.DefRegister(value);
// TODO: Flag optimization
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr))); code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(31)); code->SHR(32, R(result), Imm8(31));
} }
@ -107,6 +109,8 @@ void EmitX64::EmitSetNFlag(IR::Value* value_) {
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get()); X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
// TODO: Flag optimization
code->SHL(32, R(to_store), Imm8(31)); code->SHL(32, R(to_store), Imm8(31));
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 31))); code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 31)));
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store)); code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
@ -117,6 +121,8 @@ void EmitX64::EmitGetZFlag(IR::Value* value_) {
X64Reg result = reg_alloc.DefRegister(value); X64Reg result = reg_alloc.DefRegister(value);
// TODO: Flag optimization
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr))); code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(30)); code->SHR(32, R(result), Imm8(30));
code->AND(32, R(result), Imm32(1)); code->AND(32, R(result), Imm32(1));
@ -127,6 +133,8 @@ void EmitX64::EmitSetZFlag(IR::Value* value_) {
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get()); X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
// TODO: Flag optimization
code->SHL(32, R(to_store), Imm8(30)); code->SHL(32, R(to_store), Imm8(30));
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 30))); code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 30)));
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store)); code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
@ -137,6 +145,8 @@ void EmitX64::EmitGetCFlag(IR::Value* value_) {
X64Reg result = reg_alloc.DefRegister(value); X64Reg result = reg_alloc.DefRegister(value);
// TODO: Flag optimization
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr))); code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(29)); code->SHR(32, R(result), Imm8(29));
code->AND(32, R(result), Imm32(1)); code->AND(32, R(result), Imm32(1));
@ -147,6 +157,8 @@ void EmitX64::EmitSetCFlag(IR::Value* value_) {
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get()); X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
// TODO: Flag optimization
code->SHL(32, R(to_store), Imm8(29)); code->SHL(32, R(to_store), Imm8(29));
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 29))); code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 29)));
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store)); code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
@ -157,6 +169,8 @@ void EmitX64::EmitGetVFlag(IR::Value* value_) {
X64Reg result = reg_alloc.DefRegister(value); X64Reg result = reg_alloc.DefRegister(value);
// TODO: Flag optimization
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr))); code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(28)); code->SHR(32, R(result), Imm8(28));
code->AND(32, R(result), Imm32(1)); code->AND(32, R(result), Imm32(1));
@ -167,6 +181,8 @@ void EmitX64::EmitSetVFlag(IR::Value* value_) {
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get()); X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
// TODO: Flag optimization
code->SHL(32, R(to_store), Imm8(28)); code->SHL(32, R(to_store), Imm8(28));
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 28))); code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 28)));
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store)); code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
@ -179,6 +195,8 @@ void EmitX64::EmitGetCarryFromOp(IR::Value*) {
void EmitX64::EmitLeastSignificantByte(IR::Value* value_) { void EmitX64::EmitLeastSignificantByte(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_); auto value = reinterpret_cast<IR::Inst*>(value_);
// TODO: Flag optimization
reg_alloc.UseDefRegister(value->GetArg(0).get(), value); reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
} }
@ -187,6 +205,8 @@ void EmitX64::EmitMostSignificantBit(IR::Value* value_) {
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
// TODO: Flag optimization
code->SHL(32, R(result), Imm8(31)); code->SHL(32, R(result), Imm8(31));
} }
@ -195,6 +215,8 @@ void EmitX64::EmitIsZero(IR::Value* value_) {
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
// TODO: Flag optimization
code->TEST(32, R(result), R(result)); code->TEST(32, R(result), R(result));
code->SETcc(CCFlags::CC_E, R(result)); code->SETcc(CCFlags::CC_E, R(result));
code->MOVZX(32, 8, result, R(result)); code->MOVZX(32, 8, result, R(result));
@ -303,7 +325,62 @@ void EmitX64::EmitLogicalShiftRight(IR::Value* value_) {
} }
} }
void EmitX64::EmitArithmeticShiftRight(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp);
if (!carry_inst) {
X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX});
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
//X64Reg zero = reg_alloc.ScratchRegister();
// The 32-bit x64 SAR instruction masks the shift count by 0x1F before performing the shift.
// ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros.
// TODO: Optimize this.
code->CMP(8, R(shift), Imm8(31));
auto Rs_gt31 = code->J_CC(CC_A);
// if (Rs & 0xFF <= 31) {
code->SAR(32, R(result), R(shift));
auto jmp_to_end = code->J();
// } else {
code->SetJumpTarget(Rs_gt31);
code->SAR(32, R(result), Imm8(31)); // Verified.
// }
code->SetJumpTarget(jmp_to_end);
} else {
inhibit_emission.insert(carry_inst);
X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX});
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst);
// TODO: Optimize this.
code->CMP(8, R(shift), Imm8(31));
auto Rs_gt31 = code->J_CC(CC_A);
// if (Rs & 0xFF == 0) goto end;
code->TEST(8, R(shift), R(shift));
auto Rs_zero = code->J_CC(CC_Z);
// if (Rs & 0xFF <= 31) {
code->SAR(32, R(result), R(CL));
code->SETcc(CC_C, R(carry));
auto jmp_to_end = code->J();
// } else if (Rs & 0xFF > 31) {
code->SetJumpTarget(Rs_gt31);
code->SAR(32, R(result), Imm8(31)); // Verified.
code->BT(32, R(result), Imm8(31));
code->SETcc(CC_C, R(carry));
// }
code->SetJumpTarget(jmp_to_end);
code->SetJumpTarget(Rs_zero);
}
}
void EmitX64::EmitReturnToDispatch() { void EmitX64::EmitReturnToDispatch() {
// TODO: Update cycle counts
code->JMP(routines->RunCodeReturnAddress(), true); code->JMP(routines->RunCodeReturnAddress(), true);
} }

View file

@ -11,7 +11,7 @@
#include "backend_x64/reg_alloc.h" #include "backend_x64/reg_alloc.h"
#include "backend_x64/routines.h" #include "backend_x64/routines.h"
#include "common/x64/emitter.h" #include "common/x64/emitter.h"
#include "frontend_arm/ir/ir.h" #include "frontend/ir/ir.h"
#include "interface/interface.h" #include "interface/interface.h"
namespace Dynarmic { namespace Dynarmic {
@ -21,7 +21,8 @@ class EmitX64 final {
public: public:
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb) : code(code), reg_alloc(code), routines(routines), cb(cb) {} EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb) : code(code), reg_alloc(code), routines(routines), cb(cb) {}
CodePtr Emit(IR::Block ir); CodePtr Emit(Arm::LocationDescriptor descriptor, IR::Block ir);
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor);
void EmitImmU1(IR::Value* value); void EmitImmU1(IR::Value* value);
void EmitImmU8(IR::Value* value); void EmitImmU8(IR::Value* value);
@ -43,6 +44,7 @@ public:
void EmitIsZero(IR::Value* value); void EmitIsZero(IR::Value* value);
void EmitLogicalShiftLeft(IR::Value* value); void EmitLogicalShiftLeft(IR::Value* value);
void EmitLogicalShiftRight(IR::Value* value); void EmitLogicalShiftRight(IR::Value* value);
void EmitArithmeticShiftRight(IR::Value* value);
void EmitReturnToDispatch(); void EmitReturnToDispatch();

View file

@ -0,0 +1,102 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <memory>
#include "backend_x64/emit_x64.h"
#include "backend_x64/jitstate.h"
#include "backend_x64/routines.h"
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/scope_exit.h"
#include "frontend/arm_types.h"
#include "interface/interface.h"
namespace Dynarmic {
using namespace BackendX64;
struct BlockOfCode : Gen::XCodeBlock {
BlockOfCode() {
AllocCodeSpace(128 * 1024 * 1024);
}
};
struct Jit::Impl {
Impl(UserCallbacks callbacks) : emitter(&block_of_code, &routines, callbacks) {}
JitState jit_state{};
Routines routines{};
BlockOfCode block_of_code{};
EmitX64 emitter;
size_t Execute(size_t cycle_count) {
u32 pc = jit_state.Reg[15];
bool TFlag = Common::Bit<5>(jit_state.Cpsr);
bool EFlag = Common::Bit<9>(jit_state.Cpsr);
Arm::LocationDescriptor descriptor{pc, TFlag, EFlag};
CodePtr code_ptr = GetBasicBlock(descriptor);
return routines.RunCode(&jit_state, code_ptr, cycle_count);
}
private:
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
CodePtr code_ptr = emitter.GetBasicBlock(descriptor);
if (code_ptr)
return code_ptr;
IR::Block ir_block = IR::Block({0, false, false}); // TODO: Do this.
return emitter.Emit(descriptor, ir_block);
}
};
Jit::Jit(UserCallbacks callbacks) : callbacks(callbacks), impl(std::make_unique<Impl>(callbacks)) {}
Jit::~Jit() {}
size_t Jit::Run(size_t cycle_count) {
ASSERT(!is_executing);
is_executing = true;
SCOPE_EXIT({ this->is_executing = false; });
halt_requested = false;
size_t cycles_executed = 0;
while (cycles_executed < cycle_count && !halt_requested) {
cycles_executed += impl->Execute(cycle_count - cycles_executed);
}
return cycles_executed;
}
void Jit::ClearCache(bool poison_memory) {
ASSERT(!is_executing);
}
void Jit::HaltExecution() {
ASSERT(is_executing);
halt_requested = true;
// TODO: Uh do other stuff to JitState pls.
}
std::array<u32, 16>& Jit::Regs() {
return impl->jit_state.Reg;
}
std::array<u32, 16> Jit::Regs() const {
return impl->jit_state.Reg;
}
u32& Jit::Cpsr() {
return impl->jit_state.Cpsr;
}
u32 Jit::Cpsr() const {
return impl->jit_state.Cpsr;
}
} // namespace Dynarmic

View file

@ -15,15 +15,15 @@ namespace BackendX64 {
constexpr size_t SpillCount = 32; constexpr size_t SpillCount = 32;
struct JitState { struct JitState {
u32 Cpsr; u32 Cpsr = 0;
std::array<u32, 16> Reg{}; // Current register file. std::array<u32, 16> Reg{}; // Current register file.
// TODO: Mode-specific register sets unimplemented. // TODO: Mode-specific register sets unimplemented.
std::array<u32, SpillCount> Spill{}; // Spill. std::array<u32, SpillCount> Spill{}; // Spill.
// For internal use (See: Routines::RunCode) // For internal use (See: Routines::RunCode)
u64 save_host_RSP; u64 save_host_RSP = 0;
s64 cycles_remaining; s64 cycles_remaining = 0;
}; };
using CodePtr = const u8*; using CodePtr = const u8*;

View file

@ -14,22 +14,23 @@
namespace Dynarmic { namespace Dynarmic {
namespace BackendX64 { namespace BackendX64 {
// TODO: Just turn this into a function that indexes a std::array.
const static std::map<HostLoc, Gen::X64Reg> hostloc_to_x64 = { const static std::map<HostLoc, Gen::X64Reg> hostloc_to_x64 = {
{ HostLoc::RAX, Gen::RAX }, { HostLoc::RAX, Gen::RAX },
{ HostLoc::RBX, Gen::RBX }, { HostLoc::RBX, Gen::RBX },
{ HostLoc::RCX, Gen::RCX }, { HostLoc::RCX, Gen::RCX },
{ HostLoc::RDX, Gen::RDX }, { HostLoc::RDX, Gen::RDX },
{ HostLoc::RSI, Gen::RSI }, { HostLoc::RSI, Gen::RSI },
{ HostLoc::RDI, Gen::RDI }, { HostLoc::RDI, Gen::RDI },
{ HostLoc::RBP, Gen::RBP }, { HostLoc::RBP, Gen::RBP },
{ HostLoc::RSP, Gen::RSP }, { HostLoc::RSP, Gen::RSP },
{ HostLoc::R8, Gen::R8 }, { HostLoc::R8, Gen::R8 },
{ HostLoc::R9, Gen::R9 }, { HostLoc::R9, Gen::R9 },
{ HostLoc::R10, Gen::R10 }, { HostLoc::R10, Gen::R10 },
{ HostLoc::R11, Gen::R11 }, { HostLoc::R11, Gen::R11 },
{ HostLoc::R12, Gen::R12 }, { HostLoc::R12, Gen::R12 },
{ HostLoc::R13, Gen::R13 }, { HostLoc::R13, Gen::R13 },
{ HostLoc::R14, Gen::R14 }, { HostLoc::R14, Gen::R14 },
}; };
static Gen::OpArg SpillToOpArg(HostLoc loc) { static Gen::OpArg SpillToOpArg(HostLoc loc) {

View file

@ -11,7 +11,7 @@
#include "backend_x64/jitstate.h" #include "backend_x64/jitstate.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/x64/emitter.h" #include "common/x64/emitter.h"
#include "frontend_arm/ir/ir.h" #include "frontend/ir/ir.h"
namespace Dynarmic { namespace Dynarmic {
namespace BackendX64 { namespace BackendX64 {

View file

@ -1,28 +0,0 @@
set(SRCS
logging/log.cpp
memory_util.cpp
string_util.cpp
x64/abi.cpp
x64/cpu_detect.cpp
x64/emitter.cpp
)
set(HEADERS
assert.h
bit_set.h
bit_util.h
code_block.h
common_types.h
logging/log.h
memory_util.h
mp.h
scope_exit.h
string_util.h
x64/abi.h
x64/cpu_detect.h
x64/emitter.h
)
source_group(common FILES ${SRCS} ${HEADERS})
add_library(dynarmic_common STATIC ${SRCS} ${HEADERS})
set_target_properties(dynarmic_common PROPERTIES LINKER_LANGUAGE CXX)

View file

@ -32,7 +32,7 @@ constexpr T Bits(const T value) {
/// Extracts a single bit at bit_position from value of type T. /// Extracts a single bit at bit_position from value of type T.
template<size_t bit_position, typename T> template<size_t bit_position, typename T>
constexpr T Bit(const T value) { constexpr bool Bit(const T value) {
static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T"); static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
return (value >> bit_position) & 1; return (value >> bit_position) & 1;
@ -44,8 +44,8 @@ inline T SignExtend(const T value) {
static_assert(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T"); static_assert(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T");
constexpr T mask = static_cast<T>(1ULL << bit_count) - 1; constexpr T mask = static_cast<T>(1ULL << bit_count) - 1;
const T signbit = Bit<bit_count - 1>(value); const bool signbit = Bit<bit_count - 1, T>(value);
if (signbit != 0) { if (signbit) {
return value | ~mask; return value | ~mask;
} }
return value; return value;

View file

@ -34,6 +34,7 @@ enum class Class : ClassType {
Log, Log,
Common, Common,
Common_Memory, Common_Memory,
Core_ARM11,
Debug, Debug,
Count ///< Total number of logging classes Count ///< Total number of logging classes
}; };

View file

@ -16,7 +16,7 @@
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend_arm/decoder/decoder_detail.h" #include "frontend/decoder/decoder_detail.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {

View file

@ -0,0 +1,165 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <array>
#include <functional>
#include <tuple>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
namespace Dynarmic {
namespace Arm {
template <typename Visitor>
struct Thumb1Matcher {
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::thumb1_UDF), &Visitor::thumb1_UDF>::return_type;
Thumb1Matcher(const char* const name, u16 mask, u16 expect, std::function<CallRetT(Visitor&, u16)> fn)
: name(name), mask(mask), expect(expect), fn(fn) {}
/// Gets the name of this type of instruction.
const char* GetName() const {
return name;
}
/**
* Tests to see if the instruction is this type of instruction.
* @param instruction The instruction to test
* @returns true if the instruction is
*/
bool Matches(u16 instruction) const {
return (instruction & mask) == expect;
}
/**
* Calls the corresponding instruction handler on visitor for this type of instruction.
* @param v The visitor to use
* @param instruction The instruction to decode.
*/
CallRetT call(Visitor& v, u16 instruction) const {
assert(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
u16 mask, expect;
std::function<CallRetT(Visitor&, u16)> fn;
};
template <typename V>
static const std::array<Thumb1Matcher<V>, 6> g_thumb1_instruction_table {{
#define INST(fn, name, bitstring) detail::detail<Thumb1Matcher, u16, 16>::GetMatcher<decltype(fn), fn>(name, bitstring)
// Shift (immediate), add, subtract, move and compare instructions
{ INST(&V::thumb1_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd") },
{ INST(&V::thumb1_LSR_imm, "LSR (imm)", "00001vvvvvmmmddd") },
{ INST(&V::thumb1_ASR_imm, "ASR (imm)", "00010vvvvvmmmddd") },
//{ INST(&V::thumb1_ADD_rrr, "ADD (rrr)", "0001100mmmnnnddd") },
//{ INST(&V::thumb1_SUB_rrr, "SUB (rrr)", "0001101mmmnnnddd") },
//{ INST(&V::thumb1_ADD_rri, "ADD (rri)", "0001110mmmnnnddd") },
//{ INST(&V::thumb1_SUB_rri, "SUB (rri)", "0001111mmmnnnddd") },
//{ INST(&V::thumb1_MOV_ri, "MOV (ri)", "00100dddvvvvvvvv") },
//{ INST(&V::thumb1_CMP_ri, "CMP (ri)", "00101dddvvvvvvvv") },
//{ INST(&V::thumb1_ADD_ri, "ADD (ri)", "00110dddvvvvvvvv") },
//{ INST(&V::thumb1_SUB_ri, "SUB (ri)", "00111dddvvvvvvvv") },
// Data-processing instructions
// { INST(&V::thumb1_AND_reg, "AND (reg)", "0100000000mmmddd") },
// { INST(&V::thumb1_EOR_reg, "EOR (reg)", "0100000001mmmddd") },
{ INST(&V::thumb1_LSL_reg, "LSL (reg)", "0100000010mmmddd") },
{ INST(&V::thumb1_LSR_reg, "LSR (reg)", "0100000011sssddd") },
{ INST(&V::thumb1_ASR_reg, "ASR (reg)", "0100000100sssddd") },
//{ INST(&V::thumb1_ADCS_rr, "ADCS (rr)", "0100000101mmmddd") },
//{ INST(&V::thumb1_SBCS_rr, "SBCS (rr)", "0100000110mmmddd") },
//{ INST(&V::thumb1_RORS_rr, "RORS (rr)", "0100000111sssddd") },
//{ INST(&V::thumb1_TST_rr, "TST (rr)", "0100001000mmmnnn") },
//{ INST(&V::thumb1_NEGS_rr, "NEGS (rr)", "0100001001mmmddd") },
//{ INST(&V::thumb1_CMP_rr, "CMP (rr)", "0100001010mmmnnn") },
//{ INST(&V::thumb1_CMN_rr, "CMN (rr)", "0100001011mmmnnn") },
//{ INST(&V::thumb1_ORRS_rr, "ORRS (rr)", "0100001100mmmddd") },
//{ INST(&V::thumb1_MULS_rr, "MULS (rr)", "0100001101mmmddd") },
//{ INST(&V::thumb1_BICS_rr, "BICS (rr)", "0100001110mmmddd") },
//{ INST(&V::thumb1_MVNS_rr, "MVNS (rr)", "0100001111mmmddd") },
// Special data instructions
//{ INST(&V::thumb1_ADD_high, "ADD (high)", "01000100dmmmmddd") }, // v4T, Low regs: v6T2
//{ INST(&V::thumb1_CMP_high, "CMP (high)", "01000101dmmmmddd") }, // v4T
//{ INST(&V::thumb1_MOV_high, "MOV (high)", "01000110dmmmmddd") }, // v4T, Low regs: v6
// Store/Load single data item instructions
//{ INST(&V::thumb1_LDR_lit, "LDR (literal)", "01001dddvvvvvvvv") },
//{ INST(&V::thumb1_STR_rrr, "STR (rrr)", "0101000mmmnnnddd") },
//{ INST(&V::thumb1_STRH_rrr, "STRH (rrr)", "0101001mmmnnnddd") },
//{ INST(&V::thumb1_STRB_rrr, "STRB (rrr)", "0101010mmmnnnddd") },
//{ INST(&V::thumb1_LDRSB_rrr, "LDRSB (rrr)", "0101011mmmnnnddd") },
//{ INST(&V::thumb1_LDR_rrr, "LDR (rrr)", "0101100mmmnnnddd") },
//{ INST(&V::thumb1_LDRH_rrr, "LDRH (rrr)", "0101101mmmnnnddd") },
//{ INST(&V::thumb1_LDRB_rrr, "LDRB (rrr)", "0101110mmmnnnddd") },
//{ INST(&V::thumb1_LDRSH_rrr, "LDRSH (rrr)", "0101111mmmnnnddd") },
//{ INST(&V::thumb1_STRH_rri, "STRH (rri)", "10000vvvvvnnnddd") },
//{ INST(&V::thumb1_LDRH_rri, "LDRH (rri)", "10001vvvvvnnnddd") },
//{ INST(&V::thumb1_STR_sp, "STR (SP)", "10010dddvvvvvvvv") },
//{ INST(&V::thumb1_LDR_sp, "LDR (SP)", "10011dddvvvvvvvv") },
// Generate relative address instruction
//{ INST(&V::thumb1_ADR, "ADR", "10100dddvvvvvvvv") },
//{ INST(&V::thumb1_ADD_sp, "ADD (relative to SP)", "10101dddvvvvvvvv") },
// Miscellaneous 16-bit instructions
//{ INST(&V::thumb1_ADD_spsp, "ADD (imm to SP)", "101100000vvvvvvv") }, // v4T
//{ INST(&V::thumb1_SUB_spsp, "SUB (imm from SP)", "101100001vvvvvvv") }, // v4T
//{ INST(&V::thumb1_SXTH, "SXTH", "1011001000mmmddd") }, // v6
//{ INST(&V::thumb1_SXTB, "SXTB", "1011001001mmmddd") }, // v6
//{ INST(&V::thumb1_UXTH, "UXTH", "1011001010mmmddd") }, // v6
//{ INST(&V::thumb1_UXTB, "UXTB", "1011001011mmmddd") }, // v6
//{ INST(&V::thumb1_PUSH, "PUSH", "1011010rxxxxxxxx") }, // v4T
//{ INST(&V::thumb1_POP, "POP", "1011110rxxxxxxxx") }, // v4T
//{ INST(&V::thumb1_SETEND, "SETEND", "101101100101x000") }, // v6
//{ INST(&V::thumb1_CPS, "CPS", "10110110011m0aif") }, // v6
//{ INST(&V::thumb1_REV, "REV", "1011101000nnnddd") }, // v6
//{ INST(&V::thumb1_REV16, "REV16", "1011101001nnnddd") }, // v6
//{ INST(&V::thumb1_REVSH, "REVSH", "1011101011nnnddd") }, // v6
//{ INST(&V::thumb1_BKPT, "BKPT", "10111110xxxxxxxx") }, // v5
// Store/Load multiple registers
//{ INST(&V::thumb1_STMIA, "STMIA", "11000nnnxxxxxxxx") },
//{ INST(&V::thumb1_LDMIA, "LDMIA", "11001nnnxxxxxxxx") },
// Branch instructions
//{ INST(&V::thumb1_BX, "BX (reg)", "010001110mmmm000") }, // v4T
//{ INST(&V::thumb1_BLX, "BLX (reg)", "010001111mmmm000") }, // v5T
//{ INST(&V::thumb1_UDF, "UDF", "11011110--------") },
//{ INST(&V::thumb1_SWI, "SWI", "11011111xxxxxxxx") },
//{ INST(&V::thumb1_B_cond, "B (cond)", "1101ccccxxxxxxxx") },
//{ INST(&V::thumb1_B_imm, "B (imm)", "11100xxxxxxxxxxx") },
//{ INST(&V::thumb1_BLX_suffix, "BLX (imm, suffix)", "11101xxxxxxxxxx0") },
//{ INST(&V::thumb1_BLX_prefix, "BL/BLX (imm, prefix)", "11110xxxxxxxxxxx") },
//{ INST(&V::thumb1_BL_suffix, "BL (imm, suffix)", "11111xxxxxxxxxxx") },
#undef INST
}};
template<typename Visitor>
boost::optional<const Thumb1Matcher<Visitor>&> DecodeThumb1(u16 instruction) {
const auto& table = g_thumb1_instruction_table<Visitor>;
auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
assert(std::count_if(table.begin(), table.end(), matches_instruction) <= 1);
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? boost::make_optional<const Thumb1Matcher<Visitor>&>(*iter) : boost::none;
}
} // namespace Arm
} // namespace Dynarmic

View file

@ -9,8 +9,8 @@
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "frontend_arm/arm_types.h" #include "frontend/arm_types.h"
#include "frontend_arm/decoder/arm.h" #include "frontend/decoder/arm.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {
@ -512,8 +512,6 @@ public:
std::string arm_RFE() { return "ice"; } std::string arm_RFE() { return "ice"; }
std::string arm_SETEND(bool E) { return "ice"; } std::string arm_SETEND(bool E) { return "ice"; }
std::string arm_SRS() { return "ice"; } std::string arm_SRS() { return "ice"; }
}; };
std::string DisassembleArm(u32 instruction) { std::string DisassembleArm(u32 instruction) {

View file

@ -8,7 +8,7 @@
#include <map> #include <map>
#include "common/assert.h" #include "common/assert.h"
#include "frontend_arm/ir/ir.h" #include "frontend/ir/ir.h"
namespace Dynarmic { namespace Dynarmic {
namespace IR { namespace IR {

View file

@ -13,8 +13,8 @@
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend_arm/arm_types.h" #include "frontend/arm_types.h"
#include "frontend_arm/ir/opcodes.h" #include "frontend/ir/opcodes.h"
namespace Dynarmic { namespace Dynarmic {
namespace IR { namespace IR {

View file

@ -27,3 +27,4 @@ OPCODE(MostSignificantBit, T::U1, T::U32
OPCODE(IsZero, T::U1, T::U32 ) OPCODE(IsZero, T::U1, T::U32 )
OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 ) OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 )
OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 ) OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 )
OPCODE(ArithmeticShiftRight, T::U32, T::U32, T::U8, T::U1 )

View file

@ -67,6 +67,11 @@ IREmitter::ResultAndCarry IREmitter::LogicalShiftRight(IR::ValuePtr value_in, IR
return {result, carry_out}; return {result, carry_out};
} }
IREmitter::ResultAndCarry IREmitter::ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
auto result = Inst(IR::Opcode::ArithmeticShiftRight, {value_in, shift_amount, carry_in});
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
return {result, carry_out};
}
IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args) { IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args) {
auto inst = std::make_shared<IR::Inst>(op); auto inst = std::make_shared<IR::Inst>(op);

View file

@ -6,9 +6,9 @@
#pragma once #pragma once
#include "frontend_arm/arm_types.h" #include "frontend/arm_types.h"
#include "frontend_arm/ir/ir.h" #include "frontend/ir/ir.h"
#include "frontend_arm/ir/opcodes.h" #include "frontend/ir/opcodes.h"
namespace Dynarmic { namespace Dynarmic {
namespace Arm { namespace Arm {
@ -40,6 +40,7 @@ public:
ResultAndCarry LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in); ResultAndCarry LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in); ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
ResultAndCarry ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
private: private:
IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args); IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args);

View file

@ -0,0 +1,88 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include "frontend/arm_types.h"
#include "frontend/ir_emitter.h"
namespace Dynarmic {
namespace Arm {
class TranslatorVisitor {
public:
IREmitter ir;
void thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5;
// LSLS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_LSR_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5 != 0 ? imm5 : 32;
// LSRS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_ASR_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5 != 0 ? imm5 : 32;
// ASRS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
auto result = ir.ArithmeticShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_LSL_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// LSLS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto apsr_c = ir.GetCFlag();
auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(n), shift_n, apsr_c);
ir.SetRegister(d, result_carry.result);
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
ir.SetZFlag(ir.IsZero(result_carry.result));
ir.SetCFlag(result_carry.carry);
}
void thumb1_LSR_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// LSRS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto cpsr_c = ir.GetCFlag();
auto result = ir.LogicalShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_ASR_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// ASRS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto cpsr_c = ir.GetCFlag();
auto result = ir.ArithmeticShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_UDF() {}
};
} // namespace Arm
} // namepsace Dynarmic

View file

@ -1,23 +0,0 @@
set(SRCS
arm_disassembler.cpp
ir/ir.cpp
ir_emitter.cpp
)
set(HEADERS
arm_disassembler.h
arm_types.h
decoder/arm.h
decoder/decoder_detail.h
decoder/thumb1.h
frontend_arm.h
ir/ir.h
ir/opcodes.h
ir_emitter.h
translate_thumb.h
)
source_group(frontend_arm FILES ${SRCS} ${HEADERS})
add_library(dynarmic_frontend_arm STATIC ${SRCS} ${HEADERS})
target_link_libraries(dynarmic_frontend_arm dynarmic_common)
set_target_properties(dynarmic_frontend_arm PROPERTIES LINKER_LANGUAGE CXX)

View file

@ -1,166 +0,0 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <array>
#include <functional>
#include <tuple>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "frontend_arm/decoder/decoder_detail.h"
namespace Dynarmic {
namespace Arm {
template <typename Visitor>
struct Thumb1Matcher {
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::thumb1_UDF), &Visitor::thumb1_UDF>::return_type;
Thumb1Matcher(const char* const name, u16 mask, u16 expect, std::function<CallRetT(Visitor&, u16)> fn)
: name(name), mask(mask), expect(expect), fn(fn) {}
/// Gets the name of this type of instruction.
const char* GetName() const {
return name;
}
/**
* Tests to see if the instruction is this type of instruction.
* @param instruction The instruction to test
* @returns true if the instruction is
*/
bool Matches(u16 instruction) const {
return (instruction & mask) == expect;
}
/**
* Calls the corresponding instruction handler on visitor for this type of instruction.
* @param v The visitor to use
* @param instruction The instruction to decode.
*/
CallRetT call(Visitor& v, u16 instruction) const {
assert(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
u16 mask, expect;
std::function<CallRetT(Visitor&, u16)> fn;
};
template <typename V>
static const std::array<Thumb1Matcher<V>, 2> g_thumb1_instruction_table {{
#define INST(fn, name, bitstring) detail::detail<Thumb1Matcher, u16, 16>::GetMatcher<decltype(fn), fn>(name, bitstring)
// Shift (immediate), add, subtract, move and compare instructions
{ INST(&V::thumb1_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd") },
{ INST(&V::thumb1_LSR_imm, "LSR (imm)", "00001vvvvvmmmddd") },
/*
{ INST(&V::thumb1_ASR_rri, "ASR (rri)", "00010vvvvvmmmddd") },
{ INST(&V::thumb1_ADD_rrr, "ADD (rrr)", "0001100mmmnnnddd") },
{ INST(&V::thumb1_SUB_rrr, "SUB (rrr)", "0001101mmmnnnddd") },
{ INST(&V::thumb1_ADD_rri, "ADD (rri)", "0001110mmmnnnddd") },
{ INST(&V::thumb1_SUB_rri, "SUB (rri)", "0001111mmmnnnddd") },
{ INST(&V::thumb1_MOV_ri, "MOV (ri)", "00100dddvvvvvvvv") },
{ INST(&V::thumb1_CMP_ri, "CMP (ri)", "00101dddvvvvvvvv") },
{ INST(&V::thumb1_ADD_ri, "ADD (ri)", "00110dddvvvvvvvv") },
{ INST(&V::thumb1_SUB_ri, "SUB (ri)", "00111dddvvvvvvvv") },
// Data-processing instructions
{ INST(&V::thumb1_ANDS_rr, "ANDS (rr)", "0100000000mmmddd") },
{ INST(&V::thumb1_EORS_rr, "EORS (rr)", "0100000001mmmddd") },
{ INST(&V::thumb1_LSLS_reg, "LSLS (reg)", "0100000010mmmddd") },
{ INST(&V::thumb1_LSRS_rr, "LSRS (rr)", "0100000011sssddd") },
{ INST(&V::thumb1_ASRS_rr, "ASRS (rr)", "0100000100sssddd") },
{ INST(&V::thumb1_ADCS_rr, "ADCS (rr)", "0100000101mmmddd") },
{ INST(&V::thumb1_SBCS_rr, "SBCS (rr)", "0100000110mmmddd") },
{ INST(&V::thumb1_RORS_rr, "RORS (rr)", "0100000111sssddd") },
{ INST(&V::thumb1_TST_rr, "TST (rr)", "0100001000mmmnnn") },
{ INST(&V::thumb1_NEGS_rr, "NEGS (rr)", "0100001001mmmddd") },
{ INST(&V::thumb1_CMP_rr, "CMP (rr)", "0100001010mmmnnn") },
{ INST(&V::thumb1_CMN_rr, "CMN (rr)", "0100001011mmmnnn") },
{ INST(&V::thumb1_ORRS_rr, "ORRS (rr)", "0100001100mmmddd") },
{ INST(&V::thumb1_MULS_rr, "MULS (rr)", "0100001101mmmddd") },
{ INST(&V::thumb1_BICS_rr, "BICS (rr)", "0100001110mmmddd") },
{ INST(&V::thumb1_MVNS_rr, "MVNS (rr)", "0100001111mmmddd") },
// Special data instructions
{ INST(&V::thumb1_ADD_high, "ADD (high)", "01000100dmmmmddd") }, // v4T, Low regs: v6T2
{ INST(&V::thumb1_CMP_high, "CMP (high)", "01000101dmmmmddd") }, // v4T
{ INST(&V::thumb1_MOV_high, "MOV (high)", "01000110dmmmmddd") }, // v4T, Low regs: v6
// Store/Load single data item instructions
{ INST(&V::thumb1_LDR_lit, "LDR (literal)", "01001dddvvvvvvvv") },
{ INST(&V::thumb1_STR_rrr, "STR (rrr)", "0101000mmmnnnddd") },
{ INST(&V::thumb1_STRH_rrr, "STRH (rrr)", "0101001mmmnnnddd") },
{ INST(&V::thumb1_STRB_rrr, "STRB (rrr)", "0101010mmmnnnddd") },
{ INST(&V::thumb1_LDRSB_rrr, "LDRSB (rrr)", "0101011mmmnnnddd") },
{ INST(&V::thumb1_LDR_rrr, "LDR (rrr)", "0101100mmmnnnddd") },
{ INST(&V::thumb1_LDRH_rrr, "LDRH (rrr)", "0101101mmmnnnddd") },
{ INST(&V::thumb1_LDRB_rrr, "LDRB (rrr)", "0101110mmmnnnddd") },
{ INST(&V::thumb1_LDRSH_rrr, "LDRSH (rrr)", "0101111mmmnnnddd") },
{ INST(&V::thumb1_STRH_rri, "STRH (rri)", "10000vvvvvnnnddd") },
{ INST(&V::thumb1_LDRH_rri, "LDRH (rri)", "10001vvvvvnnnddd") },
{ INST(&V::thumb1_STR_sp, "STR (SP)", "10010dddvvvvvvvv") },
{ INST(&V::thumb1_LDR_sp, "LDR (SP)", "10011dddvvvvvvvv") },
// Generate relative address instruction
{ INST(&V::thumb1_ADR, "ADR", "10100dddvvvvvvvv") },
{ INST(&V::thumb1_ADD_sp, "ADD (relative to SP)", "10101dddvvvvvvvv") },
// Miscellaneous 16-bit instructions
{ INST(&V::thumb1_ADD_spsp, "ADD (imm to SP)", "101100000vvvvvvv") }, // v4T
{ INST(&V::thumb1_SUB_spsp, "SUB (imm from SP)", "101100001vvvvvvv") }, // v4T
{ INST(&V::thumb1_SXTH, "SXTH", "1011001000mmmddd") }, // v6
{ INST(&V::thumb1_SXTB, "SXTB", "1011001001mmmddd") }, // v6
{ INST(&V::thumb1_UXTH, "UXTH", "1011001010mmmddd") }, // v6
{ INST(&V::thumb1_UXTB, "UXTB", "1011001011mmmddd") }, // v6
{ INST(&V::thumb1_PUSH, "PUSH", "1011010rxxxxxxxx") }, // v4T
{ INST(&V::thumb1_POP, "POP", "1011110rxxxxxxxx") }, // v4T
{ INST(&V::thumb1_SETEND, "SETEND", "101101100101x000") }, // v6
{ INST(&V::thumb1_CPS, "CPS", "10110110011m0aif") }, // v6
{ INST(&V::thumb1_REV, "REV", "1011101000nnnddd") }, // v6
{ INST(&V::thumb1_REV16, "REV16", "1011101001nnnddd") }, // v6
{ INST(&V::thumb1_REVSH, "REVSH", "1011101011nnnddd") }, // v6
{ INST(&V::thumb1_BKPT, "BKPT", "10111110xxxxxxxx") }, // v5
// Store/Load multiple registers
{ INST(&V::thumb1_STMIA, "STMIA", "11000nnnxxxxxxxx") },
{ INST(&V::thumb1_LDMIA, "LDMIA", "11001nnnxxxxxxxx") },
// Branch instructions
{ INST(&V::thumb1_BX, "BX (reg)", "010001110mmmm000") }, // v4T
{ INST(&V::thumb1_BLX, "BLX (reg)", "010001111mmmm000") }, // v5T
{ INST(&V::thumb1_UDF, "UDF", "11011110--------") },
{ INST(&V::thumb1_SWI, "SWI", "11011111xxxxxxxx") },
{ INST(&V::thumb1_B_cond, "B (cond)", "1101ccccxxxxxxxx") },
{ INST(&V::thumb1_B_imm, "B (imm)", "11100xxxxxxxxxxx") },
{ INST(&V::thumb1_BLX_suffix, "BLX (imm, suffix)", "11101xxxxxxxxxx0") },
{ INST(&V::thumb1_BLX_prefix, "BL/BLX (imm, prefix)", "11110xxxxxxxxxxx") },
{ INST(&V::thumb1_BL_suffix, "BL (imm, suffix)", "11111xxxxxxxxxxx") },*/
#undef INST
}};
template<typename Visitor>
boost::optional<const Thumb1Matcher<Visitor>&> DecodeThumb1(u16 instruction) {
const auto& table = g_thumb1_instruction_table<Visitor>;
auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
assert(std::count_if(table.begin(), table.end(), matches_instruction) <= 1);
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? boost::make_optional<const Thumb1Matcher<Visitor>&>(*iter) : boost::none;
}
} // namespace Arm
} // namespace Dynarmic

View file

@ -1,293 +0,0 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include "frontend_arm/arm_types.h"
#include "frontend_arm/ir_emitter.h"
namespace Dynarmic {
namespace Arm {
class TranslatorVisitor {
public:
IREmitter ir;
void thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5;
// LSLS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_LSR_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5 != 0 ? imm5 : 32;
// LSRS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
void thumb1_ASR_rri(Imm5 imm5, Reg m, Reg d) {
ir.Unimplemented();
}
void thumb1_ADD_rrr(Reg m, Reg n, Reg d) {
ir.Unimplemented();
}
void thumb1_SUB_rrr(Reg m, Reg n, Reg d) {
ir.Unimplemented();
}
void thumb1_ADD_rri() {
ir.Unimplemented();
}
void thumb1_SUB_rri() {
ir.Unimplemented();
}
void thumb1_MOV_ri() {
ir.Unimplemented();
}
void thumb1_CMP_ri() {
ir.Unimplemented();
}
void thumb1_ADD_ri() {
ir.Unimplemented();
}
void thumb1_SUB_ri() {
ir.Unimplemented();
}
void thumb1_ANDS_rr() {
ir.Unimplemented();
}
void thumb1_EORS_rr() {
ir.Unimplemented();
}
void thumb1_LSLS_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// LSLS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto apsr_c = ir.GetCFlag();
auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(d), shift_n, apsr_c);
ir.SetRegister(d, result_carry.result);
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
ir.SetZFlag(ir.IsZero(result_carry.result));
ir.SetCFlag(result_carry.carry);
}
void thumb1_LSRS_rr() {
ir.Unimplemented();
}
void thumb1_ASRS_rr() {
ir.Unimplemented();
}
void thumb1_ADCS_rr() {
ir.Unimplemented();
}
void thumb1_SBCS_rr() {
ir.Unimplemented();
}
void thumb1_RORS_rr() {
ir.Unimplemented();
}
void thumb1_TST_rr() {
ir.Unimplemented();
}
void thumb1_NEGS_rr() {
ir.Unimplemented();
}
void thumb1_CMP_rr() {
ir.Unimplemented();
}
void thumb1_CMN_rr() {
ir.Unimplemented();
}
void thumb1_ORRS_rr() {
ir.Unimplemented();
}
void thumb1_MULS_rr() {
ir.Unimplemented();
}
void thumb1_BICS_rr() {
ir.Unimplemented();
}
void thumb1_MVNS_rr() {
ir.Unimplemented();
}
void thumb1_ADD_high() {
ir.Unimplemented();
}
void thumb1_CMP_high() {
ir.Unimplemented();
}
void thumb1_MOV_high() {
ir.Unimplemented();
}
void thumb1_LDR_lit() {
ir.Unimplemented();
}
void thumb1_STR_rrr() {
ir.Unimplemented();
}
void thumb1_STRH_rrr() {
ir.Unimplemented();
}
void thumb1_STRB_rrr() {
ir.Unimplemented();
}
void thumb1_LDRSB_rrr() {
ir.Unimplemented();
}
void thumb1_LDR_rrr() {
ir.Unimplemented();
}
void thumb1_LDRH_rrr() {
ir.Unimplemented();
}
void thumb1_LDRB_rrr() {
ir.Unimplemented();
}
void thumb1_LDRSH_rrr() {
ir.Unimplemented();
}
void thumb1_STRH_rri() {
ir.Unimplemented();
}
void thumb1_LDRH_rri() {
ir.Unimplemented();
}
void thumb1_STR_sp() {
ir.Unimplemented();
}
void thumb1_LDR_sp() {
ir.Unimplemented();
}
void thumb1_ADR() {
ir.Unimplemented();
}
void thumb1_ADD_sp() {
ir.Unimplemented();
}
void thumb1_ADD_spsp() {
ir.Unimplemented();
}
void thumb1_SUB_spsp() {
ir.Unimplemented();
}
void thumb1_SXTH() {
ir.Unimplemented();
}
void thumb1_SXTB() {
ir.Unimplemented();
}
void thumb1_UXTH() {
ir.Unimplemented();
}
void thumb1_UXTB() {
ir.Unimplemented();
}
void thumb1_PUSH() {
ir.Unimplemented();
}
void thumb1_POP() {
ir.Unimplemented();
}
void thumb1_SETEND() {
ir.Unimplemented();
}
void thumb1_CPS() {
ir.Unimplemented();
}
void thumb1_REV() {
ir.Unimplemented();
}
void thumb1_REV16() {
ir.Unimplemented();
}
void thumb1_REVSH() {
ir.Unimplemented();
}
void thumb1_BKPT() {
ir.Unimplemented();
}
void thumb1_STMIA() {
ir.Unimplemented();
}
void thumb1_LDMIA() {
ir.Unimplemented();
}
void thumb1_BX() {
ir.Unimplemented();
}
void thumb1_BLX() {
ir.Unimplemented();
}
void thumb1_BEQ() {
ir.Unimplemented();
}
void thumb1_BNE() {
ir.Unimplemented();
}
void thumb1_BCS() {
ir.Unimplemented();
}
void thumb1_BCC() {
ir.Unimplemented();
}
void thumb1_BMI() {
ir.Unimplemented();
}
void thumb1_BPL() {
ir.Unimplemented();
}
void thumb1_BVS() {
ir.Unimplemented();
}
void thumb1_BVC() {
ir.Unimplemented();
}
void thumb1_BHI() {
ir.Unimplemented();
}
void thumb1_BLS() {
ir.Unimplemented();
}
void thumb1_BGE() {
ir.Unimplemented();
}
void thumb1_BLT() {
ir.Unimplemented();
}
void thumb1_BGT() {
ir.Unimplemented();
}
void thumb1_BLE() {
ir.Unimplemented();
}
void thumb1_UDF() {
ir.Unimplemented();
}
void thumb1_SWI() {
ir.Unimplemented();
}
void thumb1_B() {
ir.Unimplemented();
}
void thumb1_BLX_suffix() {
ir.Unimplemented();
}
void thumb1_BLX_prefix() {
ir.Unimplemented();
}
void thumb1_BL_suffix() {
ir.Unimplemented();
}
};
} // namespace Arm
} // namepsace Dynarmic

View file

@ -6,10 +6,15 @@
#pragma once #pragma once
#include <memory>
#include "common/common_types.h" #include "common/common_types.h"
namespace Dynarmic { namespace Dynarmic {
class Jit;
/// These function pointers may be inserted into compiled code.
struct UserCallbacks { struct UserCallbacks {
u8 (*MemoryRead8)(u32 vaddr); u8 (*MemoryRead8)(u32 vaddr);
u16 (*MemoryRead16)(u32 vaddr); u16 (*MemoryRead16)(u32 vaddr);
@ -21,9 +26,62 @@ struct UserCallbacks {
void (*MemoryWrite32)(u32 vaddr, u32 value); void (*MemoryWrite32)(u32 vaddr, u32 value);
void (*MemoryWrite64)(u32 vaddr, u64 value); void (*MemoryWrite64)(u32 vaddr, u64 value);
void (*InterpreterFallback)(u32 pc, void* jit_state); bool (*IsReadOnlyMemory)(u32 vaddr);
bool (*SoftwareInterrupt)(u32 swi); void (*InterpreterFallback)(u32 pc, Jit* jit);
bool (*CallSVC)(u32 swi);
};
class Jit final {
public:
explicit Jit(Dynarmic::UserCallbacks callbacks);
~Jit();
/**
* Runs the emulated CPU for about cycle_count cycles.
* Cannot be recursively called.
* @param cycle_count Estimated number of cycles to run the CPU for.
* @returns Actual cycle count.
*/
size_t Run(size_t cycle_count);
/**
* Clears the code cache of all compiled code.
* Cannot be called from a callback.
* @param poison_memory If true, poisons memory to crash if any stray code pointers are called.
*/
void ClearCache(bool poison_memory = true);
/**
* Stops execution in Jit::Run.
* Can only be called from a callback.
*/
void HaltExecution();
/// View and modify registers.
std::array<u32, 16>& Regs();
std::array<u32, 16> Regs() const;
/// View and modify CPSR.
u32& Cpsr();
u32 Cpsr() const;
/**
* Returns true if Jit::Run was called but hasn't returned yet.
* i.e.: We're in a callback.
*/
bool IsExecuting() const {
return is_executing;
}
private:
bool halt_requested = false;
bool is_executing = false;
Dynarmic::UserCallbacks callbacks;
struct Impl;
std::unique_ptr<Impl> impl;
}; };
} // namespace Dynarmic } // namespace Dynarmic

View file

@ -1,14 +0,0 @@
set(SRCS
arm/fuzz_thumb.cpp
arm/test_arm_disassembler.cpp
arm/test_thumb_instructions.cpp
main.cpp
)
set(HEADERS
)
source_group(tests FILES ${SRCS} ${HEADERS})
add_executable(dynarmic_tests ${SRCS})
target_link_libraries(dynarmic_tests dynarmic_common dynarmic_frontend_arm dynarmic_backend_x64)
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)

35
tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,35 @@
include_directories(.)
set(SRCS
arm/fuzz_thumb.cpp
arm/test_arm_disassembler.cpp
arm/test_thumb_instructions.cpp
main.cpp
skyeye_interpreter/dyncom/arm_dyncom_dec.cpp
skyeye_interpreter/dyncom/arm_dyncom_interpreter.cpp
skyeye_interpreter/dyncom/arm_dyncom_thumb.cpp
skyeye_interpreter/skyeye_common/armstate.cpp
skyeye_interpreter/skyeye_common/armsupp.cpp
skyeye_interpreter/skyeye_common/vfp/vfp.cpp
skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp
skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp
skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp
)
set(HEADERS
skyeye_interpreter/dyncom/arm_dyncom_dec.h
skyeye_interpreter/dyncom/arm_dyncom_interpreter.h
skyeye_interpreter/dyncom/arm_dyncom_run.h
skyeye_interpreter/dyncom/arm_dyncom_thumb.h
skyeye_interpreter/skyeye_common/armstate.h
skyeye_interpreter/skyeye_common/armsupp.h
skyeye_interpreter/skyeye_common/arm_regformat.h
skyeye_interpreter/skyeye_common/vfp/asm_vfp.h
skyeye_interpreter/skyeye_common/vfp/vfp.h
skyeye_interpreter/skyeye_common/vfp/vfp_helper.h
)
source_group(dynarmic_tests FILES ${SRCS} ${HEADERS})
add_executable(dynarmic_tests ${SRCS})
target_link_libraries(dynarmic_tests dynarmic)
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)

View file

@ -4,3 +4,5 @@
* General Public License version 2 or any later version. * General Public License version 2 or any later version.
*/ */
#include "interface/interface.h"

View file

@ -6,7 +6,7 @@
#include <catch.hpp> #include <catch.hpp>
#include "frontend_arm/arm_disassembler.h" #include "frontend/disassembler_arm.h"
TEST_CASE( "Disassemble branch instructions", "[arm][disassembler]" ) { TEST_CASE( "Disassemble branch instructions", "[arm][disassembler]" ) {
REQUIRE(Dynarmic::Arm::DisassembleArm(0xEAFFFFFE) == "b +#0"); REQUIRE(Dynarmic::Arm::DisassembleArm(0xEAFFFFFE) == "b +#0");

View file

@ -8,8 +8,8 @@
#include "backend_x64/emit_x64.h" #include "backend_x64/emit_x64.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "frontend_arm/decoder/thumb1.h" #include "frontend/decoder/thumb1.h"
#include "frontend_arm/translate_thumb.h" #include "frontend/translate_thumb.h"
struct TinyBlockOfCode : Gen::XCodeBlock { struct TinyBlockOfCode : Gen::XCodeBlock {
TinyBlockOfCode() { TinyBlockOfCode() {
@ -25,10 +25,10 @@ void RunSingleThumbInstruction(u16 thumb_instruction, Dynarmic::BackendX64::JitS
TinyBlockOfCode block_of_code; TinyBlockOfCode block_of_code;
Dynarmic::BackendX64::Routines routines; Dynarmic::BackendX64::Routines routines;
Dynarmic::UserCallbacks callbacks; Dynarmic::UserCallbacks callbacks{};
Dynarmic::BackendX64::EmitX64 emitter(&block_of_code, &routines, callbacks); Dynarmic::BackendX64::EmitX64 emitter(&block_of_code, &routines, callbacks);
Dynarmic::BackendX64::CodePtr code = emitter.Emit(visitor.ir.block); Dynarmic::BackendX64::CodePtr code = emitter.Emit({0, true, false}, visitor.ir.block);
routines.RunCode(jit_state_ptr, code, 1); routines.RunCode(jit_state_ptr, code, 1);
} }

View file

@ -0,0 +1,466 @@
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_dec.h"
#include "tests/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;
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

@ -0,0 +1,39 @@
// 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

@ -0,0 +1,10 @@
// 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

@ -0,0 +1,48 @@
/* 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 "tests/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

@ -0,0 +1,393 @@
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
// ARM instruction, and using the existing ARM simulator.
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
#include "tests/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[3] = {
0xE6BF0F30, // REV
0xE6BF0FB0, // REV16
0xE6FF0FB0, // REVSH
};
*ainstr = subset[BITS(tinstr, 6, 7)] // 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

@ -0,0 +1,49 @@
/* 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;
}

View file

@ -0,0 +1,187 @@
#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

@ -0,0 +1,670 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/logging/log.h"
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
namespace Common {
inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);}
inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);}
}
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;
}
u8 ARMul_State::ReadMemory8(u32 address) const
{
return (*user_callbacks.MemoryRead8)(address);
}
u16 ARMul_State::ReadMemory16(u32 address) const
{
u16 data = (*user_callbacks.MemoryRead16)(address);
if (InBigEndianMode())
data = Common::swap16(data);
return data;
}
u32 ARMul_State::ReadMemory32(u32 address) const
{
u32 data = (*user_callbacks.MemoryRead32)(address);
if (InBigEndianMode())
data = Common::swap32(data);
return data;
}
u64 ARMul_State::ReadMemory64(u32 address) const
{
u64 data = (*user_callbacks.MemoryRead64)(address);
if (InBigEndianMode())
data = Common::swap64(data);
return data;
}
void ARMul_State::WriteMemory8(u32 address, u8 data)
{
(*user_callbacks.MemoryWrite8)(address, data);
}
void ARMul_State::WriteMemory16(u32 address, u16 data)
{
if (InBigEndianMode())
data = Common::swap16(data);
(*user_callbacks.MemoryWrite16)(address, data);
}
void ARMul_State::WriteMemory32(u32 address, u32 data)
{
if (InBigEndianMode())
data = Common::swap32(data);
(*user_callbacks.MemoryWrite32)(address, data);
}
void ARMul_State::WriteMemory64(u32 address, u64 data)
{
if (InBigEndianMode())
data = 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);
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.
// TODO: Implement this maybe.
// 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

@ -0,0 +1,255 @@
/* 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 "common/common_types.h"
#include "interface/interface.h"
#include "tests/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::UserCallbacks user_callbacks;
};

View file

@ -0,0 +1,207 @@
/* 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 "tests/skyeye_interpreter/skyeye_common/arm_regformat.h"
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
#include "tests/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

@ -0,0 +1,32 @@
// 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

@ -0,0 +1,83 @@
/*
* 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

@ -0,0 +1,162 @@
/*
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/common_types.h"
#include "common/logging/log.h"
#include "tests/skyeye_interpreter/skyeye_common/armstate.h"
#include "tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
#include "tests/skyeye_interpreter/skyeye_common/vfp/vfp.h"
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, 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, u32 t, 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, u32 t, 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) {
LOG_CRITICAL(Core_ARM11, "unhandled bounce %x", inst);
exit(-1);
}
/*
* 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

@ -0,0 +1,43 @@
/*
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 "tests/skyeye_interpreter/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
#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

@ -0,0 +1,450 @@
/*
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 "tests/skyeye_interpreter/skyeye_common/armstate.h"
#include "tests/skyeye_interpreter/skyeye_common/vfp/asm_vfp.h"
#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 void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
{
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;
*fpscr |= FPSCR_IDC;
}
}
// 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, u32 exceptions, const char* func);
// Double-precision
struct vfp_double {
s16 exponent;
u16 sign;
u64 significand;
};
// VFP_REG_ZERO is a special register number for vfp_get_double
// which returns (double)0.0. This is useful for the compare with
// zero instructions.
#ifdef CONFIG_VFPv3
#define VFP_REG_ZERO 32
#else
#define VFP_REG_ZERO 16
#endif
#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 void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
{
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;
*fpscr |= FPSCR_IDC;
}
}
// 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, u32 exceptions, 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