First Commit

This commit is contained in:
MerryMage 2016-07-01 21:01:06 +08:00
commit 65df15633d
55 changed files with 18405 additions and 0 deletions

58
CMakeLists.txt Normal file
View file

@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.5)
project(dynarmic)
# Dynarmic project options
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
# 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++")
# Arch detection
include(CheckSymbolExists)
function(detect_architecture symbol arch)
if (NOT DEFINED ARCHITECTURE)
set(CMAKE_REQUIRED_QUIET 1)
check_symbol_exists("${symbol}" "" ARCHITECTURE_${arch})
unset(CMAKE_REQUIRED_QUIET)
# The output variable needs to be unique across invocations otherwise
# CMake's crazy scope rules will keep it defined
if (ARCHITECTURE_${arch})
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
add_definitions(-DARCHITECTURE_${arch}=1)
endif()
endif()
endfunction()
if (MSVC)
detect_architecture("_M_AMD64" x86_64)
detect_architecture("_M_IX86" x86)
detect_architecture("_M_ARM" ARM)
else()
detect_architecture("__x86_64__" x86_64)
detect_architecture("__i386__" x86)
detect_architecture("__arm__" ARM)
endif()
if (NOT DEFINED ARCHITECTURE)
set(ARCHITECTURE "GENERIC")
set(ARCHITECTURE_GENERIC 1)
add_definitions(-DARCHITECTURE_GENERIC=1)
endif()
message(STATUS "Target architecture: ${ARCHITECTURE}")
# Include Boost
if(DYNARMIC_USE_SYSTEM_BOOST)
find_package(Boost 1.57.0 REQUIRED)
else()
if(NOT Boost_INCLUDE_DIRS)
message(FATAL_ERROR "Please provide a path to a boost installation using Boost_INCLUDE_DIRS")
endif()
endif()
include_directories(${Boost_INCLUDE_DIRS})
# Include Catch
include_directories(externals/catch)
enable_testing(true) # Enables unit-testing.
# Dynarmic project files
add_subdirectory(src)

27
README.md Normal file
View file

@ -0,0 +1,27 @@
Dynarmic
========
An (eventual) dynamic recompiler for ARM6K. The code is a mess.
A lot of optimization work can be done (it currently produces bad code, worse than that non-IR JIT).
Plans
-----
### Near-term
* Actually finish the translators off
* Get everything working
* Redundant Get/Set elimination
* Handle immediates properly
* Allow ARM flags to be stored in host flags
### Medium-term
* Undo strength-reduction (invert some common ARM idioms)
* Other optimizations
### Long-term
* ARMv7A support
* ARMv5 support

10509
externals/catch/catch.hpp vendored Normal file

File diff suppressed because it is too large Load diff

7
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,7 @@
include_directories(.)
add_subdirectory(backend_x64)
add_subdirectory(common)
add_subdirectory(daggen)
add_subdirectory(frontend_arm)
add_subdirectory(tests)

View file

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

@ -0,0 +1,311 @@
/* 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 <map>
#include <unordered_map>
#include "backend_x64/emit_x64.h"
#include "common/x64/emitter.h"
// TODO: More optimal use of immediates.
// TODO: Have ARM flags in host flags and not have them use up GPR registers unless necessary.
// TODO: Actually implement that proper instruction selector you've always wanted to sweetheart.
using namespace Gen;
namespace Dynarmic {
namespace BackendX64 {
// Mapping from opcode to Emit* member function.
const static std::map<IR::Opcode, void (EmitX64::*)(IR::Value*)> emit_fns {
#define OPCODE(name, type, ...) { IR::Opcode::name, &EmitX64::Emit##name },
#include "frontend_arm/ir/opcodes.inc"
#undef OPCODE
};
static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) {
// Gets first found use.
auto uses = inst->GetUses();
auto iter = std::find_if(uses.begin(), uses.end(), [opcode](const auto& use){ return use->GetOpcode() == opcode; });
return iter == uses.end() ? nullptr : reinterpret_cast<IR::Inst*>(iter->get());
}
CodePtr EmitX64::Emit(Dynarmic::IR::Block block) {
code->INT3();
CodePtr code_ptr = code->GetCodePtr();
// Call Emit* member function for each instruction.
for (const auto& value : block.instructions) {
if (inhibit_emission.count(value.get()) != 0)
continue;
(this->*emit_fns.at(value->GetOpcode()))(value.get());
reg_alloc.EndOfAllocScope();
}
EmitReturnToDispatch();
return code_ptr;
}
void EmitX64::EmitImmU1(IR::Value*) {
ASSERT_MSG(0, "Unimplemented");
}
void EmitX64::EmitImmU8(IR::Value* value_) {
auto value = reinterpret_cast<IR::ImmU8*>(value_);
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), Imm32(value->value));
}
void EmitX64::EmitImmU32(IR::Value* value_) {
auto value = reinterpret_cast<IR::ImmU32*>(value_);
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), Imm32(value->value));
}
void EmitX64::EmitImmRegRef(IR::Value*) {
return; // No need to do anything.
}
void EmitX64::EmitGetRegister(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
auto regref = reinterpret_cast<IR::ImmRegRef*>(value->GetArg(0).get());
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Reg) + static_cast<size_t>(regref->value) * sizeof(u32)));
}
void EmitX64::EmitSetRegister(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
auto regref = reinterpret_cast<IR::ImmRegRef*>(value->GetArg(0).get());
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(1).get());
code->MOV(32, MDisp(R15, offsetof(JitState, Reg) + static_cast<size_t>(regref->value) * sizeof(u32)), R(to_store));
}
void EmitX64::EmitGetNFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(31));
}
void EmitX64::EmitSetNFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
code->SHL(32, R(to_store), Imm8(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));
}
void EmitX64::EmitGetZFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(30));
code->AND(32, R(result), Imm32(1));
}
void EmitX64::EmitSetZFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
code->SHL(32, R(to_store), Imm8(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));
}
void EmitX64::EmitGetCFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(29));
code->AND(32, R(result), Imm32(1));
}
void EmitX64::EmitSetCFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
code->SHL(32, R(to_store), Imm8(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));
}
void EmitX64::EmitGetVFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg result = reg_alloc.DefRegister(value);
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
code->SHR(32, R(result), Imm8(28));
code->AND(32, R(result), Imm32(1));
}
void EmitX64::EmitSetVFlag(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(0).get());
code->SHL(32, R(to_store), Imm8(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));
}
void EmitX64::EmitGetCarryFromOp(IR::Value*) {
ASSERT_MSG(0, "should never happen");
}
void EmitX64::EmitLeastSignificantByte(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
}
void EmitX64::EmitMostSignificantBit(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
code->SHL(32, R(result), Imm8(31));
}
void EmitX64::EmitIsZero(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value);
code->TEST(32, R(result), R(result));
code->SETcc(CCFlags::CC_E, R(result));
code->MOVZX(32, 8, result, R(result));
}
void EmitX64::EmitLogicalShiftLeft(IR::Value* value_) {
auto value = reinterpret_cast<IR::Inst*>(value_);
auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp);
// TODO: Consider using BMI2 instructions like SHLX when arm-in-host flags is implemented.
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 SHL 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.
code->SHL(32, R(result), R(shift));
code->XOR(32, R(zero), R(zero));
code->CMP(8, R(shift), Imm8(32));
code->CMOVcc(32, result, R(zero), CC_NB);
} 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(32));
auto Rs_gt32 = code->J_CC(CC_A);
auto Rs_eq32 = code->J_CC(CC_E);
// if (Rs & 0xFF < 32) {
code->BT(32, R(carry), Imm8(0)); // Set the carry flag for correct behaviour in the case when Rs & 0xFF == 0
code->SHL(32, R(result), R(shift));
code->SETcc(CC_C, R(carry));
auto jmp_to_end_1 = code->J();
// } else if (Rs & 0xFF > 32) {
code->SetJumpTarget(Rs_gt32);
code->XOR(32, R(result), R(result));
code->XOR(32, R(carry), R(carry));
auto jmp_to_end_2 = code->J();
// } else if (Rs & 0xFF == 32) {
code->SetJumpTarget(Rs_eq32);
code->MOV(32, R(carry), R(result));
code->AND(32, R(carry), Imm8(1));
code->XOR(32, R(result), R(result));
// }
code->SetJumpTarget(jmp_to_end_1);
code->SetJumpTarget(jmp_to_end_2);
}
}
void EmitX64::EmitLogicalShiftRight(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 SHR 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.
code->SHR(32, R(result), R(shift));
code->XOR(32, R(zero), R(zero));
code->CMP(8, R(shift), Imm8(32));
code->CMOVcc(32, result, R(zero), CC_NB);
} 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(32, R(shift), Imm8(32));
auto Rs_gt32 = code->J_CC(CC_A);
auto Rs_eq32 = code->J_CC(CC_E);
// if (Rs & 0xFF == 0) goto end;
code->TEST(32, R(shift), R(shift));
auto Rs_zero = code->J_CC(CC_Z);
// if (Rs & 0xFF < 32) {
code->SHR(32, R(result), R(shift));
code->SETcc(CC_C, R(carry));
auto jmp_to_end_1 = code->J();
// } else if (Rs & 0xFF > 32) {
code->SetJumpTarget(Rs_gt32);
code->MOV(32, R(result), Imm32(0));
code->MOV(8, R(carry), Imm8(0));
auto jmp_to_end_2 = code->J();
// } else if (Rs & 0xFF == 32) {
code->SetJumpTarget(Rs_eq32);
code->BT(32, R(result), Imm8(31));
code->SETcc(CC_C, R(carry));
code->MOV(32, R(result), Imm32(0));
// }
code->SetJumpTarget(jmp_to_end_1);
code->SetJumpTarget(jmp_to_end_2);
code->SetJumpTarget(Rs_zero);
}
}
void EmitX64::EmitReturnToDispatch() {
code->JMP(routines->RunCodeReturnAddress(), true);
}
} // namespace BackendX64
} // namespace Dynarmic

View file

@ -0,0 +1,58 @@
/* 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 <set>
#include "backend_x64/reg_alloc.h"
#include "backend_x64/routines.h"
#include "common/x64/emitter.h"
#include "frontend_arm/ir/ir.h"
#include "interface/interface.h"
namespace Dynarmic {
namespace BackendX64 {
class EmitX64 final {
public:
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb) : code(code), reg_alloc(code), routines(routines), cb(cb) {}
CodePtr Emit(IR::Block ir);
void EmitImmU1(IR::Value* value);
void EmitImmU8(IR::Value* value);
void EmitImmU32(IR::Value* value);
void EmitImmRegRef(IR::Value* value);
void EmitGetRegister(IR::Value* value);
void EmitSetRegister(IR::Value* value);
void EmitGetNFlag(IR::Value* value);
void EmitSetNFlag(IR::Value* value);
void EmitGetZFlag(IR::Value* value);
void EmitSetZFlag(IR::Value* value);
void EmitGetCFlag(IR::Value* value);
void EmitSetCFlag(IR::Value* value);
void EmitGetVFlag(IR::Value* value);
void EmitSetVFlag(IR::Value* value);
void EmitGetCarryFromOp(IR::Value* value);
void EmitLeastSignificantByte(IR::Value* value);
void EmitMostSignificantBit(IR::Value* value);
void EmitIsZero(IR::Value* value);
void EmitLogicalShiftLeft(IR::Value* value);
void EmitLogicalShiftRight(IR::Value* value);
void EmitReturnToDispatch();
private:
std::set<IR::Value*> inhibit_emission;
Gen::XEmitter* code;
RegAlloc reg_alloc;
Routines* routines;
UserCallbacks cb;
};
} // namespace BackendX64
} // namespace Dynarmic

View file

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

View file

@ -0,0 +1,204 @@
/* 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 <algorithm>
#include <map>
#include "backend_x64/reg_alloc.h"
#include "common/assert.h"
#include "common/x64/emitter.h"
namespace Dynarmic {
namespace BackendX64 {
const static std::map<HostLoc, Gen::X64Reg> hostloc_to_x64 = {
{ HostLoc::RAX, Gen::RAX },
{ HostLoc::RBX, Gen::RBX },
{ HostLoc::RCX, Gen::RCX },
{ HostLoc::RDX, Gen::RDX },
{ HostLoc::RSI, Gen::RSI },
{ HostLoc::RDI, Gen::RDI },
{ HostLoc::RBP, Gen::RBP },
{ HostLoc::RSP, Gen::RSP },
{ HostLoc::R8, Gen::R8 },
{ HostLoc::R9, Gen::R9 },
{ HostLoc::R10, Gen::R10 },
{ HostLoc::R11, Gen::R11 },
{ HostLoc::R12, Gen::R12 },
{ HostLoc::R13, Gen::R13 },
{ HostLoc::R14, Gen::R14 },
};
static Gen::OpArg SpillToOpArg(HostLoc loc) {
ASSERT(HostLocIsSpill(loc));
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
return Gen::MDisp(Gen::R15, offsetof(JitState, Spill) + i * sizeof(u32));
}
Gen::X64Reg RegAlloc::DefRegister(IR::Value* def_value, std::initializer_list<HostLoc> desired_locations) {
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
ASSERT_MSG(remaining_uses.find(def_value) == remaining_uses.end(), "def_value has already been defined");
ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined");
HostLoc location = SelectARegister(desired_locations);
if (IsRegisterOccupied(location)) {
SpillRegister(location);
}
// Update state
hostloc_state[location] = HostLocState::Def;
hostloc_to_value[location] = def_value;
remaining_uses[def_value] = def_value->NumUses();
return hostloc_to_x64.at(location);
}
Gen::X64Reg RegAlloc::UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations) {
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
ASSERT_MSG(remaining_uses.find(def_value) == remaining_uses.end(), "def_value has already been defined");
ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined");
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
// TODO: Optimize the case when this is the last use_value use.
Gen::X64Reg use_reg = UseRegister(use_value);
Gen::X64Reg def_reg = DefRegister(def_value, desired_locations);
code->MOV(32, Gen::R(def_reg), Gen::R(use_reg));
return def_reg;
}
Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations) {
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)");
HostLoc current_location = ValueLocations(use_value).front();
auto iter = std::find(desired_locations.begin(), desired_locations.end(), current_location);
if (iter != desired_locations.end()) {
ASSERT(hostloc_state[current_location] == HostLocState::Idle || hostloc_state[current_location] == HostLocState::Use);
// Update state
hostloc_state[current_location] = HostLocState::Use;
remaining_uses[use_value]--;
return hostloc_to_x64.at(current_location);
}
HostLoc new_location = SelectARegister(desired_locations);
if (HostLocIsSpill(current_location)) {
if (IsRegisterOccupied(new_location)) {
SpillRegister(new_location);
}
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), SpillToOpArg(current_location));
hostloc_state[new_location] = HostLocState::Use;
hostloc_to_value[new_location] = use_value;
remaining_uses[use_value]--;
} else if (HostLocIsRegister(current_location)) {
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
code->XCHG(32, Gen::R(hostloc_to_x64.at(current_location)), Gen::R(hostloc_to_x64.at(new_location)));
hostloc_state[new_location] = HostLocState::Use;
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
remaining_uses[use_value]--;
} else {
ASSERT_MSG(0, "Invalid current_location");
}
return hostloc_to_x64.at(new_location);
}
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
HostLoc location = SelectARegister(desired_locations);
if (IsRegisterOccupied(location)) {
SpillRegister(location);
}
// Update state
hostloc_state[location] = HostLocState::Scratch;
return hostloc_to_x64.at(location);
}
HostLoc RegAlloc::SelectARegister(std::initializer_list<HostLoc> desired_locations) const {
std::vector<HostLoc> candidates = desired_locations;
// Find all locations that have not been allocated..
auto allocated_locs = std::partition(candidates.begin(), candidates.end(), [this](auto loc){
return !this->IsRegisterAllocated(loc);
});
candidates.erase(allocated_locs, candidates.end());
ASSERT_MSG(!candidates.empty(), "All candidate registers have already been allocated");
// Selects the best location out of the available locations.
// TODO: Actually do LRU or something. Currently we just try to pick something without a value if possible.
std::partition(candidates.begin(), candidates.end(), [this](auto loc){
return !this->IsRegisterOccupied(loc);
});
return candidates.front();
}
std::vector<HostLoc> RegAlloc::ValueLocations(IR::Value* value) const {
std::vector<HostLoc> locations;
for (const auto& iter : hostloc_to_value)
if (iter.second == value)
locations.emplace_back(iter.first);
return locations;
}
bool RegAlloc::IsRegisterOccupied(HostLoc loc) const {
return hostloc_to_value.find(loc) != hostloc_to_value.end() && hostloc_to_value.at(loc) != nullptr;
}
bool RegAlloc::IsRegisterAllocated(HostLoc loc) const {
return hostloc_state.find(loc) != hostloc_state.end() && hostloc_state.at(loc) != HostLocState::Idle;
}
void RegAlloc::SpillRegister(HostLoc loc) {
ASSERT_MSG(HostLocIsRegister(loc), "Only registers can be spilled");
ASSERT_MSG(hostloc_state[loc] == HostLocState::Idle, "Allocated registers cannot be spilled");
ASSERT_MSG(IsRegisterOccupied(loc), "There is no need to spill unoccupied registers");
ASSERT_MSG(!IsRegisterAllocated(loc), "Registers that have been allocated must not be spilt");
HostLoc new_loc = FindFreeSpill();
code->MOV(32, SpillToOpArg(new_loc), Gen::R(hostloc_to_x64.at(loc)));
hostloc_to_value[new_loc] = hostloc_to_value[loc];
hostloc_to_value[loc] = nullptr;
}
HostLoc RegAlloc::FindFreeSpill() const {
for (size_t i = 0; i < SpillCount; i++)
if (!IsRegisterOccupied(HostLocSpill(i)))
return HostLocSpill(i);
ASSERT_MSG(0, "All spill locations are full");
}
void RegAlloc::EndOfAllocScope() {
hostloc_state.clear();
for (auto& iter : hostloc_to_value)
if (iter.second && remaining_uses[iter.second] == 0)
iter.second = nullptr;
}
} // namespace BackendX64
} // namespace Dynarmic

View file

@ -0,0 +1,99 @@
/* 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 <map>
#include "backend_x64/jitstate.h"
#include "common/common_types.h"
#include "common/x64/emitter.h"
#include "frontend_arm/ir/ir.h"
namespace Dynarmic {
namespace BackendX64 {
enum class HostLoc {
RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14,
CF, PF, AF, ZF, SF, OF,
FirstSpill,
};
enum class HostLocState {
Idle, Def, Use, Scratch
};
inline bool HostLocIsRegister(HostLoc reg) {
return reg >= HostLoc::RAX && reg <= HostLoc::R14;
}
inline bool HostLocIsFlag(HostLoc reg) {
return reg >= HostLoc::CF && reg <= HostLoc::OF;
}
inline HostLoc HostLocSpill(size_t i) {
ASSERT_MSG(i < SpillCount, "Invalid spill");
return static_cast<HostLoc>(static_cast<int>(HostLoc::FirstSpill) + i);
}
inline bool HostLocIsSpill(HostLoc reg) {
return reg >= HostLoc::FirstSpill && reg <= HostLocSpill(SpillCount - 1);
}
constexpr std::initializer_list<HostLoc> hostloc_any_register = {
HostLoc::RAX,
HostLoc::RBX,
HostLoc::RCX,
HostLoc::RDX,
HostLoc::RSI,
HostLoc::RDI,
HostLoc::RBP,
HostLoc::RSP,
HostLoc::R8,
HostLoc::R9,
HostLoc::R10,
HostLoc::R11,
HostLoc::R12,
HostLoc::R13,
HostLoc::R14
};
class RegAlloc final {
public:
RegAlloc(Gen::XEmitter* code) : code(code) {}
/// Late-def
Gen::X64Reg DefRegister(IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
/// Early-use, Late-def
Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
/// Early-use
Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
/// Early-def, Late-use, single-use
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
// TODO: Values in host flags
void EndOfAllocScope();
private:
HostLoc SelectARegister(std::initializer_list<HostLoc> desired_locations) const;
std::vector<HostLoc> ValueLocations(IR::Value* value) const;
bool IsRegisterOccupied(HostLoc loc) const;
bool IsRegisterAllocated(HostLoc loc) const;
void SpillRegister(HostLoc loc);
HostLoc FindFreeSpill() const;
Gen::XEmitter* code = nullptr;
using mapping_map_t = std::map<HostLoc, IR::Value*>;
mapping_map_t hostloc_to_value;
std::map<HostLoc, HostLocState> hostloc_state;
std::map<IR::Value*, size_t> remaining_uses;
};
} // namespace BackendX64
} // namespace Dynarmic

View file

@ -0,0 +1,61 @@
/* 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 <limits>
#include "backend_x64/jitstate.h"
#include "backend_x64/routines.h"
#include "common/x64/abi.h"
using namespace Gen;
namespace Dynarmic {
namespace BackendX64 {
Routines::Routines() {
AllocCodeSpace(1024);
GenRunCode();
GenReturnFromRunCode();
}
size_t Routines::RunCode(JitState* jit_state, CodePtr basic_block, size_t cycles_to_run) const {
ASSERT(cycles_to_run <= std::numeric_limits<decltype(jit_state->cycles_remaining)>::max());
jit_state->cycles_remaining = cycles_to_run;
run_code(jit_state, basic_block);
return cycles_to_run - jit_state->cycles_remaining; // Return number of cycles actually run.
}
CodePtr Routines::RunCodeReturnAddress() const {
return return_from_run_code;
}
void Routines::GenRunCode() {
run_code = reinterpret_cast<RunCodeFuncType>(this->GetCodePtr());
// This serves two purposes:
// 1. It saves all the registers we as a callee need to save.
// 2. It aligns the stack so that the code the JIT emits can assume
// that the stack is appropriately aligned for CALLs.
ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
MOV(64, R(R15), R(ABI_PARAM1));
MOV(64, MDisp(R15, offsetof(JitState, save_host_RSP)), R(RSP));
JMPptr(R(ABI_PARAM2));
}
void Routines::GenReturnFromRunCode() {
return_from_run_code = this->GetCodePtr();
MOV(64, R(RSP), MDisp(R15, offsetof(JitState, save_host_RSP)));
ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8);
RET();
}
} // namespace BackendX64
} // namespace Dynarmic

View file

@ -0,0 +1,33 @@
/* 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 "backend_x64/jitstate.h"
#include "common/common_types.h"
#include "common/x64/emitter.h"
namespace Dynarmic {
namespace BackendX64 {
class Routines final : private Gen::XCodeBlock {
public:
Routines();
size_t RunCode(JitState* jit_state, CodePtr basic_block, size_t cycles_to_run) const;
CodePtr RunCodeReturnAddress() const;
private:
using RunCodeFuncType = void(*)(JitState*, CodePtr);
RunCodeFuncType run_code;
void GenRunCode();
CodePtr return_from_run_code;
void GenReturnFromRunCode();
};
} // namespace BackendX64
} // namespace Dynarmic

20
src/common/CMakeLists.txt Normal file
View file

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

53
src/common/assert.h Normal file
View file

@ -0,0 +1,53 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstdlib>
#include "common/logging/log.h"
// For asserts we'd like to keep all the junk executed when an assert happens away from the
// important code in the function. One way of doing this is to put all the relevant code inside a
// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
// template that calls the lambda. This seems to generate an extra instruction at the call-site
// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
// enough for our purposes.
template <typename Fn>
#if defined(_MSC_VER)
__declspec(noinline, noreturn)
#elif defined(__GNUC__)
__attribute__((noinline, noreturn, cold))
#endif
static void assert_noinline_call(const Fn& fn) {
fn();
exit(1); // Keeps GCC's mouth shut about this actually returning
}
#define ASSERT(_a_) \
do if (!(_a_)) { assert_noinline_call([] { \
LOG_CRITICAL(Debug, "Assertion Failed!"); \
throw ""; \
}); } while (0)
#define ASSERT_MSG(_a_, ...) \
do if (!(_a_)) { assert_noinline_call([&] { \
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
throw ""; \
}); } while (0)
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
#ifdef _DEBUG
#define DEBUG_ASSERT(_a_) ASSERT(_a_)
#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
#else // not debug
#define DEBUG_ASSERT(_a_)
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
#endif
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
#define UNIMPLEMENTED_MSG(_a_, ...) ASSERT_MSG(false, _a_, __VA_ARGS__)

190
src/common/bit_set.h Normal file
View file

@ -0,0 +1,190 @@
// This file is under the public domain.
#pragma once
#include <cstddef>
#ifdef _WIN32
#include <intrin.h>
#endif
#include <initializer_list>
#include <new>
#include <type_traits>
#include "common/common_types.h"
// namespace avoids conflict with OS X Carbon; don't use BitSet<T> directly
namespace Common {
// Helper functions:
#ifdef _WIN32
template <typename T>
static inline int CountSetBits(T v)
{
// from https://graphics.stanford.edu/~seander/bithacks.html
// GCC has this built in, but MSVC's intrinsic will only emit the actual
// POPCNT instruction, which we're not depending on
v = v - ((v >> 1) & (T)~(T)0/3);
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);
v = (v + (v >> 4)) & (T)~(T)0/255*15;
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8;
}
static inline int LeastSignificantSetBit(uint8_t val)
{
unsigned long index;
_BitScanForward(&index, val);
return (int)index;
}
static inline int LeastSignificantSetBit(uint16_t val)
{
unsigned long index;
_BitScanForward(&index, val);
return (int)index;
}
static inline int LeastSignificantSetBit(uint32_t val)
{
unsigned long index;
_BitScanForward(&index, val);
return (int)index;
}
static inline int LeastSignificantSetBit(uint64_t val)
{
unsigned long index;
_BitScanForward64(&index, val);
return (int)index;
}
#else
static inline int CountSetBits(uint8_t val) { return __builtin_popcount(val); }
static inline int CountSetBits(uint16_t val) { return __builtin_popcount(val); }
static inline int CountSetBits(uint32_t val) { return __builtin_popcount(val); }
static inline int CountSetBits(uint64_t val) { return __builtin_popcountll(val); }
static inline int LeastSignificantSetBit(uint8_t val) { return __builtin_ctz(val); }
static inline int LeastSignificantSetBit(uint16_t val) { return __builtin_ctz(val); }
static inline int LeastSignificantSetBit(uint32_t val) { return __builtin_ctz(val); }
static inline int LeastSignificantSetBit(uint64_t val) { return __builtin_ctzll(val); }
#endif
// Similar to std::bitset, this is a class which encapsulates a bitset, i.e.
// using the set bits of an integer to represent a set of integers. Like that
// class, it acts like an array of bools:
// BitSet32 bs;
// bs[1] = true;
// but also like the underlying integer ([0] = least significant bit):
// BitSet32 bs2 = ...;
// bs = (bs ^ bs2) & BitSet32(0xffff);
// The following additional functionality is provided:
// - Construction using an initializer list.
// BitSet bs { 1, 2, 4, 8 };
// - Efficiently iterating through the set bits:
// for (int i : bs)
// [i is the *index* of a set bit]
// (This uses the appropriate CPU instruction to find the next set bit in one
// operation.)
// - Counting set bits using .Count() - see comment on that method.
// TODO: use constexpr when MSVC gets out of the Dark Ages
template <typename IntTy>
class BitSet
{
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
public:
// A reference to a particular bit, returned from operator[].
class Ref
{
public:
Ref(Ref&& other) : m_bs(other.m_bs), m_mask(other.m_mask) {}
Ref(BitSet* bs, IntTy mask) : m_bs(bs), m_mask(mask) {}
operator bool() const { return (m_bs->m_val & m_mask) != 0; }
bool operator=(bool set)
{
m_bs->m_val = (m_bs->m_val & ~m_mask) | (set ? m_mask : 0);
return set;
}
private:
BitSet* m_bs;
IntTy m_mask;
};
// A STL-like iterator is required to be able to use range-based for loops.
class Iterator
{
public:
Iterator(const Iterator& other) : m_val(other.m_val), m_bit(other.m_bit) {}
Iterator(IntTy val, int bit) : m_val(val), m_bit(bit) {}
Iterator& operator=(Iterator other) { new (this) Iterator(other); return *this; }
int operator*() { return m_bit; }
Iterator& operator++()
{
if (m_val == 0)
{
m_bit = -1;
}
else
{
int bit = LeastSignificantSetBit(m_val);
m_val &= ~(1 << bit);
m_bit = bit;
}
return *this;
}
Iterator operator++(int _)
{
Iterator other(*this);
++*this;
return other;
}
bool operator==(Iterator other) const { return m_bit == other.m_bit; }
bool operator!=(Iterator other) const { return m_bit != other.m_bit; }
private:
IntTy m_val;
int m_bit;
};
BitSet() : m_val(0) {}
explicit BitSet(IntTy val) : m_val(val) {}
BitSet(std::initializer_list<int> init)
{
m_val = 0;
for (int bit : init)
m_val |= (IntTy)1 << bit;
}
static BitSet AllTrue(size_t count)
{
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
}
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
const Ref operator[](size_t bit) const { return (*const_cast<BitSet*>(this))[bit]; }
bool operator==(BitSet other) const { return m_val == other.m_val; }
bool operator!=(BitSet other) const { return m_val != other.m_val; }
bool operator<(BitSet other) const { return m_val < other.m_val; }
bool operator>(BitSet other) const { return m_val > other.m_val; }
BitSet operator|(BitSet other) const { return BitSet(m_val | other.m_val); }
BitSet operator&(BitSet other) const { return BitSet(m_val & other.m_val); }
BitSet operator^(BitSet other) const { return BitSet(m_val ^ other.m_val); }
BitSet operator~() const { return BitSet(~m_val); }
BitSet& operator|=(BitSet other) { return *this = *this | other; }
BitSet& operator&=(BitSet other) { return *this = *this & other; }
BitSet& operator^=(BitSet other) { return *this = *this ^ other; }
operator uint32_t() = delete;
operator bool() { return m_val != 0; }
// Warning: Even though on modern CPUs this is a single fast instruction,
// Dolphin's official builds do not currently assume POPCNT support on x86,
// so slower explicit bit twiddling is generated. Still should generally
// be faster than a loop.
unsigned int Count() const { return CountSetBits(m_val); }
Iterator begin() const { Iterator it(m_val, 0); return ++it; }
Iterator end() const { return Iterator(m_val, -1); }
IntTy m_val;
};
} // Common
typedef Common::BitSet<uint8_t> BitSet8;
typedef Common::BitSet<uint16_t> BitSet16;
typedef Common::BitSet<uint32_t> BitSet32;
typedef Common::BitSet<uint64_t> BitSet64;

55
src/common/bit_util.h Normal file
View file

@ -0,0 +1,55 @@
/* 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 <climits>
#include "common/common_types.h"
namespace Dynarmic {
namespace Common {
/// The size of a type in terms of bits
template<typename T>
constexpr size_t BitSize() {
return sizeof(T) * CHAR_BIT;
}
/// Extract bits [begin_bit, end_bit] inclusive from value of type T.
template<size_t begin_bit, size_t end_bit, typename T>
constexpr T Bits(const T value) {
static_assert(begin_bit < end_bit,
"invalid bit range (position of beginning bit cannot be greater than that of end bit)");
static_assert(begin_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
static_assert(end_bit < BitSize<T>(), "begin_bit must be smaller than size of T");
return (value >> begin_bit) & ((1 << (end_bit - begin_bit + 1)) - 1);
}
/// Extracts a single bit at bit_position from value of type T.
template<size_t bit_position, typename T>
constexpr T Bit(const T value) {
static_assert(bit_position < BitSize<T>(), "bit_position must be smaller than size of T");
return (value >> bit_position) & 1;
}
/// Sign-extends a value that has NBits bits to the full bitwidth of type T.
template<size_t bit_count, typename T>
inline T SignExtend(const T value) {
static_assert(bit_count <= BitSize<T>(), "bit_count larger than bitsize of T");
constexpr T mask = static_cast<T>(1ULL << bit_count) - 1;
const T signbit = Bit<bit_count - 1>(value);
if (signbit != 0) {
return value | ~mask;
}
return value;
}
} // namespace Common
} // namespace Dynarmic

92
src/common/code_block.h Normal file
View file

@ -0,0 +1,92 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include "common/common_types.h"
#include "common/memory_util.h"
// Everything that needs to generate code should inherit from this.
// You get memory management for free, plus, you can use all emitter functions without
// having to prefix them with gen-> or something similar.
// Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {}
template<class T> class CodeBlock : public T
{
private:
// A privately used function to set the executable RAM space to something invalid.
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction
virtual void PoisonMemory() = 0;
protected:
u8 *region;
size_t region_size;
public:
CodeBlock() : region(nullptr), region_size(0) {}
virtual ~CodeBlock() { if (region) FreeCodeSpace(); }
CodeBlock(const CodeBlock&) = delete;
CodeBlock& operator=(const CodeBlock&) = delete;
// Call this before you generate any code.
void AllocCodeSpace(int size)
{
region_size = size;
region = (u8*)AllocateExecutableMemory(region_size);
T::SetCodePtr(region);
}
// Always clear code space with breakpoints, so that if someone accidentally executes
// uninitialized, it just breaks into the debugger.
void ClearCodeSpace()
{
PoisonMemory();
ResetCodePtr();
}
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace()
{
#ifdef __SYMBIAN32__
ResetExecutableMemory(region);
#else
FreeMemoryPages(region, region_size);
#endif
region = nullptr;
region_size = 0;
}
bool IsInSpace(const u8 *ptr)
{
return (ptr >= region) && (ptr < (region + region_size));
}
// Cannot currently be undone. Will write protect the entire code region.
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
void WriteProtect()
{
WriteProtectMemory(region, region_size, true);
}
void ResetCodePtr()
{
T::SetCodePtr(region);
}
size_t GetSpaceLeft() const
{
return region_size - (T::GetCodePtr() - region);
}
u8 *GetBasePtr() {
return region;
}
size_t GetOffset(const u8 *ptr) const {
return ptr - region;
}
};

29
src/common/common_types.h Normal file
View file

@ -0,0 +1,29 @@
/* 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 <cstddef>
#include <cstdint>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using uptr = std::uintptr_t;
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using sptr = std::intptr_t;
using size_t = std::size_t;
using f32 = float;
using f64 = double;
static_assert(sizeof(f32) == sizeof(u32), "f32 must be 32 bits wide");
static_assert(sizeof(f64) == sizeof(u64), "f64 must be 64 bits wide");

View file

@ -0,0 +1,21 @@
/* 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 <cstdarg>
#include <cstdio>
#include "common/logging/log.h"
namespace Log {
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr, const char* function, const char* format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
} // namespace Log

68
src/common/logging/log.h Normal file
View file

@ -0,0 +1,68 @@
// 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"
namespace Log {
/// Specifies the severity or level of detail of the log message.
enum class Level : u8 {
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
/// pollute logs.
Debug, ///< Less detailed debugging information.
Info, ///< Status information from important points during execution.
Warning, ///< Minor or potential problems found during execution of a task.
Error, ///< Major problems found during execution of a task that prevent it from being
/// completed.
Critical, ///< Major problems during execution that threathen the stability of the entire
/// application.
Count ///< Total number of logging levels
};
typedef u8 ClassType;
/**
* Specifies the sub-system that generated the log message.
*
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in backend.cpp.
*/
enum class Class : ClassType {
Log,
Common,
Common_Memory,
Debug,
Count ///< Total number of logging classes
};
/// Logs a message to the global logger.
void LogMessage(Class log_class, Level log_level,
const char* filename, unsigned int line_nr, const char* function,
#ifdef _MSC_VER
_Printf_format_string_
#endif
const char* format, ...)
#ifdef __GNUC__
__attribute__((format(gnu_printf, 6, 7)))
#endif
;
} // namespace Log
#define LOG_GENERIC(log_class, log_level, ...) \
::Log::LogMessage(log_class, log_level, __FILE__, __LINE__, __func__, __VA_ARGS__)
#ifdef _DEBUG
#define LOG_TRACE( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Trace, __VA_ARGS__)
#else
#define LOG_TRACE( log_class, ...) (void(0))
#endif
#define LOG_DEBUG( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Debug, __VA_ARGS__)
#define LOG_INFO( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Info, __VA_ARGS__)
#define LOG_WARNING( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Warning, __VA_ARGS__)
#define LOG_ERROR( log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
#define LOG_CRITICAL(log_class, ...) LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)

192
src/common/memory_util.cpp Normal file
View file

@ -0,0 +1,192 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "common/memory_util.h"
#ifdef _WIN32
#include <windows.h>
#include <psapi.h>
#include "common/string_util.h"
#else
#include <cstdlib>
#include <sys/mman.h>
#endif
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
#include <unistd.h>
#define PAGE_MASK (getpagesize() - 1)
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
#endif
// Generic function to get last error message.
// Call directly after the command or use the error num.
// This function might change the error code.
const char* GetLastErrorMsg()
{
static const size_t buff_size = 255;
#ifdef _WIN32
static thread_local char err_str[buff_size] = {};
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
err_str, buff_size, nullptr);
#else
static __thread char err_str[buff_size] = {};
// Thread safe (XSI-compliant)
strerror_r(errno, err_str, buff_size);
#endif
return err_str;
}
// This is purposely not a full wrapper for virtualalloc/mmap, but it
// provides exactly the primitive operations that Dolphin needs.
void* AllocateExecutableMemory(size_t size, bool low)
{
#if defined(_WIN32)
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
static char* map_hint = nullptr;
#if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
// This OS has no flag to enforce allocation below the 4 GB boundary,
// but if we hint that we want a low address it is very likely we will
// get one.
// An older version of this code used MAP_FIXED, but that has the side
// effect of discarding already mapped pages that happen to be in the
// requested virtual memory range (such as the emulated RAM, sometimes).
if (low && (!map_hint))
map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */
#endif
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE
#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
| (low ? MAP_32BIT : 0)
#endif
, -1, 0);
#endif /* defined(_WIN32) */
#ifdef _WIN32
if (ptr == nullptr)
{
#else
if (ptr == MAP_FAILED)
{
ptr = nullptr;
#endif
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
}
#if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT)
else
{
if (low)
{
map_hint += size;
map_hint = (char*)round_page(map_hint); /* round up to the next page */
}
}
#endif
#if EMU_ARCH_BITS == 64
if ((u64)ptr >= 0x80000000 && low == true)
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
#endif
return ptr;
}
void* AllocateMemoryPages(size_t size)
{
#ifdef _WIN32
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
#else
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED)
ptr = nullptr;
#endif
if (ptr == nullptr)
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
return ptr;
}
void* AllocateAlignedMemory(size_t size,size_t alignment)
{
#ifdef _WIN32
void* ptr = _aligned_malloc(size,alignment);
#else
void* ptr = nullptr;
#ifdef ANDROID
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size) != 0)
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
#endif
#endif
if (ptr == nullptr)
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
return ptr;
}
void FreeMemoryPages(void* ptr, size_t size)
{
if (ptr)
{
#ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE))
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n%s", GetLastErrorMsg());
#else
munmap(ptr, size);
#endif
}
}
void FreeAlignedMemory(void* ptr)
{
if (ptr)
{
#ifdef _WIN32
_aligned_free(ptr);
#else
free(ptr);
#endif
}
}
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
{
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n%s", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
#endif
}
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
{
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n%s", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
#endif
}
std::string MemUsage()
{
return "";
}

19
src/common/memory_util.h Normal file
View file

@ -0,0 +1,19 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string>
void* AllocateExecutableMemory(size_t size, bool low = true);
void* AllocateMemoryPages(size_t size);
void FreeMemoryPages(void* ptr, size_t size);
void* AllocateAlignedMemory(size_t size,size_t alignment);
void FreeAlignedMemory(void* ptr);
void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
std::string MemUsage();
inline int GetPageSize() { return 4096; }

26
src/common/mp.h Normal file
View file

@ -0,0 +1,26 @@
/* 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 "common/common_types.h"
namespace Dynarmic {
namespace mp {
template<typename MemFnT, MemFnT fn>
struct MemFnInfo;
/// This struct provides information about a member function pointer.
template<typename T, typename ReturnT, typename ...Args, ReturnT (T::*fn)(Args...)>
struct MemFnInfo<ReturnT (T::*)(Args...), fn> {
using class_type = T;
using return_type = ReturnT;
static constexpr size_t args_count = sizeof...(Args);
};
} // namespace mp
} // namespace Dynarmic

View file

@ -0,0 +1,36 @@
/* 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 <cstdio>
#include <cstdarg>
#include <string>
#include "common/string_util.h"
namespace Dynarmic {
namespace Common {
std::string StringFromFormat(const char* format, ...) {
va_list args;
va_start(args, format);
int size = std::vsnprintf(nullptr, 0, format, args);
va_end(args);
std::string str;
str.resize(size+1);
va_start(args, format);
std::vsnprintf(&str[0], str.size(), format, args);
va_end(args);
str.resize(size);
return str;
}
} // namespace Common
} // namespace Dynarmic

17
src/common/string_util.h Normal file
View file

@ -0,0 +1,17 @@
/* 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 <string>
namespace Dynarmic {
namespace Common {
std::string StringFromFormat(const char* format, ...) __attribute__((format(gnu_printf, 1, 2)));
} // namespace Common
} // namespace Dynarmic

363
src/common/x64/abi.cpp Normal file
View file

@ -0,0 +1,363 @@
// Copyright (C) 2003 Dolphin Project.
// 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, version 2.0 or later versions.
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "abi.h"
#include "emitter.h"
using namespace Gen;
// Shared code between Win64 and Unix64
void XEmitter::ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size, size_t* shadowp, size_t* subtractionp, size_t* xmm_offsetp) {
size_t shadow = 0;
#if defined(_WIN32)
shadow = 0x20;
#endif
int count = (mask & ABI_ALL_GPRS).Count();
rsp_alignment -= count * 8;
size_t subtraction = 0;
int fpr_count = (mask & ABI_ALL_FPRS).Count();
if (fpr_count) {
// If we have any XMMs to save, we must align the stack here.
subtraction = rsp_alignment & 0xf;
}
subtraction += 16 * fpr_count;
size_t xmm_base_subtraction = subtraction;
subtraction += needed_frame_size;
subtraction += shadow;
// Final alignment.
rsp_alignment -= subtraction;
subtraction += rsp_alignment & 0xf;
*shadowp = shadow;
*subtractionp = subtraction;
*xmm_offsetp = subtraction - xmm_base_subtraction;
}
size_t XEmitter::ABI_PushRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) {
size_t shadow, subtraction, xmm_offset;
ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset);
for (int r : mask & ABI_ALL_GPRS)
PUSH((X64Reg)r);
if (subtraction)
SUB(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction));
for (int x : mask & ABI_ALL_FPRS) {
MOVAPD(MDisp(RSP, (int)xmm_offset), (X64Reg)(x - 16));
xmm_offset += 16;
}
return shadow;
}
void XEmitter::ABI_PopRegistersAndAdjustStack(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size) {
size_t shadow, subtraction, xmm_offset;
ABI_CalculateFrameSize(mask, rsp_alignment, needed_frame_size, &shadow, &subtraction, &xmm_offset);
for (int x : mask & ABI_ALL_FPRS) {
MOVAPD((X64Reg) (x - 16), MDisp(RSP, (int)xmm_offset));
xmm_offset += 16;
}
if (subtraction)
ADD(64, R(RSP), subtraction >= 0x80 ? Imm32((u32)subtraction) : Imm8((u8)subtraction));
for (int r = 15; r >= 0; r--) {
if (mask[r])
POP((X64Reg)r);
}
}
// Common functions
void XEmitter::ABI_CallFunction(const void *func) {
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionC16(const void *func, u16 param1) {
MOV(32, R(ABI_PARAM1), Imm32((u32)param1));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionCC16(const void *func, u32 param1, u16 param2) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32((u32)param2));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionC(const void *func, u32 param1) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionCC(const void *func, u32 param1, u32 param2) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionCCC(const void *func, u32 param1, u32 param2, u32 param3) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(32, R(ABI_PARAM3), Imm32(param3));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionCCP(const void *func, u32 param1, u32 param2, void *param3) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(64, R(ABI_PARAM3), ImmPtr(param3));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionCCCP(const void *func, u32 param1, u32 param2, u32 param3, void *param4) {
MOV(32, R(ABI_PARAM1), Imm32(param1));
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(32, R(ABI_PARAM3), Imm32(param3));
MOV(64, R(ABI_PARAM4), ImmPtr(param4));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionP(const void *func, void *param1) {
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionPA(const void *func, void *param1, const Gen::OpArg &arg2) {
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
if (!arg2.IsSimpleReg(ABI_PARAM2))
MOV(32, R(ABI_PARAM2), arg2);
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionPAA(const void *func, void *param1, const Gen::OpArg &arg2, const Gen::OpArg &arg3) {
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
if (!arg2.IsSimpleReg(ABI_PARAM2))
MOV(32, R(ABI_PARAM2), arg2);
if (!arg3.IsSimpleReg(ABI_PARAM3))
MOV(32, R(ABI_PARAM3), arg3);
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionPPC(const void *func, void *param1, void *param2, u32 param3) {
MOV(64, R(ABI_PARAM1), ImmPtr(param1));
MOV(64, R(ABI_PARAM2), ImmPtr(param2));
MOV(32, R(ABI_PARAM3), Imm32(param3));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
// Pass a register as a parameter.
void XEmitter::ABI_CallFunctionR(const void *func, X64Reg reg1) {
if (reg1 != ABI_PARAM1)
MOV(32, R(ABI_PARAM1), R(reg1));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
// Pass two registers as parameters.
void XEmitter::ABI_CallFunctionRR(const void *func, X64Reg reg1, X64Reg reg2) {
if (reg2 != ABI_PARAM1) {
if (reg1 != ABI_PARAM1)
MOV(64, R(ABI_PARAM1), R(reg1));
if (reg2 != ABI_PARAM2)
MOV(64, R(ABI_PARAM2), R(reg2));
} else {
if (reg2 != ABI_PARAM2)
MOV(64, R(ABI_PARAM2), R(reg2));
if (reg1 != ABI_PARAM1)
MOV(64, R(ABI_PARAM1), R(reg1));
}
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionAC(const void *func, const Gen::OpArg &arg1, u32 param2)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(32, R(ABI_PARAM1), arg1);
MOV(32, R(ABI_PARAM2), Imm32(param2));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionACC(const void *func, const Gen::OpArg &arg1, u32 param2, u32 param3)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(32, R(ABI_PARAM1), arg1);
MOV(32, R(ABI_PARAM2), Imm32(param2));
MOV(64, R(ABI_PARAM3), Imm64(param3));
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionA(const void *func, const Gen::OpArg &arg1)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(32, R(ABI_PARAM1), arg1);
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}
void XEmitter::ABI_CallFunctionAA(const void *func, const Gen::OpArg &arg1, const Gen::OpArg &arg2)
{
if (!arg1.IsSimpleReg(ABI_PARAM1))
MOV(32, R(ABI_PARAM1), arg1);
if (!arg2.IsSimpleReg(ABI_PARAM2))
MOV(32, R(ABI_PARAM2), arg2);
u64 distance = u64(func) - (u64(code) + 5);
if (distance >= 0x0000000080000000ULL
&& distance < 0xFFFFFFFF80000000ULL) {
// Far call
MOV(64, R(RAX), ImmPtr(func));
CALLptr(R(RAX));
} else {
CALL(func);
}
}

59
src/common/x64/abi.h Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "common/bit_set.h"
#include "emitter.h"
// x64 ABI:s, and helpers to help follow them when JIT-ing code.
// All convensions return values in EAX (+ possibly EDX).
// Windows 64-bit
// * 4-reg "fastcall" variant, very new-skool stack handling
// * Callee moves stack pointer, to make room for shadow regs for the biggest function _it itself calls_
// * Parameters passed in RCX, RDX, ... further parameters are MOVed into the allocated stack space.
// Scratch: RAX RCX RDX R8 R9 R10 R11
// Callee-save: RBX RSI RDI RBP R12 R13 R14 R15
// Parameters: RCX RDX R8 R9, further MOV-ed
// Linux 64-bit
// * 6-reg "fastcall" variant, old skool stack handling (parameters are pushed)
// Scratch: RAX RCX RDX RSI RDI R8 R9 R10 R11
// Callee-save: RBX RBP R12 R13 R14 R15
// Parameters: RDI RSI RDX RCX R8 R9
#define ABI_ALL_FPRS BitSet32(0xffff0000)
#define ABI_ALL_GPRS BitSet32(0x0000ffff)
#ifdef _WIN32 // 64-bit Windows - the really exotic calling convention
#define ABI_PARAM1 RCX
#define ABI_PARAM2 RDX
#define ABI_PARAM3 R8
#define ABI_PARAM4 R9
// xmm0-xmm15 use the upper 16 bits in the functions that push/pop registers.
#define ABI_ALL_CALLER_SAVED \
(BitSet32 { RAX, RCX, RDX, R8, R9, R10, R11, \
XMM0+16, XMM1+16, XMM2+16, XMM3+16, XMM4+16, XMM5+16 })
#else //64-bit Unix / OS X
#define ABI_PARAM1 RDI
#define ABI_PARAM2 RSI
#define ABI_PARAM3 RDX
#define ABI_PARAM4 RCX
#define ABI_PARAM5 R8
#define ABI_PARAM6 R9
// TODO: Avoid pushing all 16 XMM registers when possible. Most functions we call probably
// don't actually clobber them.
#define ABI_ALL_CALLER_SAVED \
(BitSet32 { RAX, RCX, RDX, RDI, RSI, R8, R9, R10, R11 } | \
ABI_ALL_FPRS)
#endif // WIN32
#define ABI_ALL_CALLEE_SAVED (~ABI_ALL_CALLER_SAVED)
#define ABI_RETURN RAX

View file

@ -0,0 +1,187 @@
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <string>
#include <thread>
#include "common/common_types.h"
#include "cpu_detect.h"
namespace Common {
#ifndef _MSC_VER
#ifdef __FreeBSD__
#include <sys/types.h>
#include <machine/cpufunc.h>
#endif
static inline void __cpuidex(int info[4], int function_id, int subfunction_id) {
#ifdef __FreeBSD__
// Despite the name, this is just do_cpuid() with ECX as second input.
cpuid_count((u_int)function_id, (u_int)subfunction_id, (u_int*)info);
#else
info[0] = function_id; // eax
info[2] = subfunction_id; // ecx
__asm__(
"cpuid"
: "=a" (info[0]),
"=b" (info[1]),
"=c" (info[2]),
"=d" (info[3])
: "a" (function_id),
"c" (subfunction_id)
);
#endif
}
static inline void __cpuid(int info[4], int function_id) {
return __cpuidex(info, function_id, 0);
}
#define _XCR_XFEATURE_ENABLED_MASK 0
static u64 _xgetbv(u32 index) {
u32 eax, edx;
__asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
return ((u64)edx << 32) | eax;
}
#endif // ifndef _MSC_VER
// Detects the various CPU features
static CPUCaps Detect() {
CPUCaps caps = {};
caps.num_cores = std::thread::hardware_concurrency();
// Assumes the CPU supports the CPUID instruction. Those that don't would likely not support
// Citra at all anyway
int cpu_id[4];
memset(caps.brand_string, 0, sizeof(caps.brand_string));
// Detect CPU's CPUID capabilities and grab CPU string
__cpuid(cpu_id, 0x00000000);
u32 max_std_fn = cpu_id[0]; // EAX
std::memcpy(&caps.brand_string[0], &cpu_id[1], sizeof(int));
std::memcpy(&caps.brand_string[4], &cpu_id[3], sizeof(int));
std::memcpy(&caps.brand_string[8], &cpu_id[2], sizeof(int));
__cpuid(cpu_id, 0x80000000);
u32 max_ex_fn = cpu_id[0];
if (!strcmp(caps.brand_string, "GenuineIntel"))
caps.vendor = CPUVendor::INTEL;
else if (!strcmp(caps.brand_string, "AuthenticAMD"))
caps.vendor = CPUVendor::AMD;
else
caps.vendor = CPUVendor::OTHER;
// Set reasonable default brand string even if brand string not available
strcpy(caps.cpu_string, caps.brand_string);
// Detect family and other miscellaneous features
if (max_std_fn >= 1) {
__cpuid(cpu_id, 0x00000001);
if ((cpu_id[3] >> 25) & 1) caps.sse = true;
if ((cpu_id[3] >> 26) & 1) caps.sse2 = true;
if ((cpu_id[2]) & 1) caps.sse3 = true;
if ((cpu_id[2] >> 9) & 1) caps.ssse3 = true;
if ((cpu_id[2] >> 19) & 1) caps.sse4_1 = true;
if ((cpu_id[2] >> 20) & 1) caps.sse4_2 = true;
if ((cpu_id[2] >> 22) & 1) caps.movbe = true;
if ((cpu_id[2] >> 25) & 1) caps.aes = true;
if ((cpu_id[3] >> 24) & 1) {
caps.fxsave_fxrstor = true;
}
// AVX support requires 3 separate checks:
// - Is the AVX bit set in CPUID?
// - Is the XSAVE bit set in CPUID?
// - XGETBV result has the XCR bit set.
if (((cpu_id[2] >> 28) & 1) && ((cpu_id[2] >> 27) & 1)) {
if ((_xgetbv(_XCR_XFEATURE_ENABLED_MASK) & 0x6) == 0x6) {
caps.avx = true;
if ((cpu_id[2] >> 12) & 1)
caps.fma = true;
}
}
if (max_std_fn >= 7) {
__cpuidex(cpu_id, 0x00000007, 0x00000000);
// Can't enable AVX2 unless the XSAVE/XGETBV checks above passed
if ((cpu_id[1] >> 5) & 1)
caps.avx2 = caps.avx;
if ((cpu_id[1] >> 3) & 1)
caps.bmi1 = true;
if ((cpu_id[1] >> 8) & 1)
caps.bmi2 = true;
}
}
caps.flush_to_zero = caps.sse;
if (max_ex_fn >= 0x80000004) {
// Extract CPU model string
__cpuid(cpu_id, 0x80000002);
std::memcpy(caps.cpu_string, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000003);
std::memcpy(caps.cpu_string + 16, cpu_id, sizeof(cpu_id));
__cpuid(cpu_id, 0x80000004);
std::memcpy(caps.cpu_string + 32, cpu_id, sizeof(cpu_id));
}
if (max_ex_fn >= 0x80000001) {
// Check for more features
__cpuid(cpu_id, 0x80000001);
if (cpu_id[2] & 1) caps.lahf_sahf_64 = true;
if ((cpu_id[2] >> 5) & 1) caps.lzcnt = true;
if ((cpu_id[2] >> 16) & 1) caps.fma4 = true;
if ((cpu_id[3] >> 29) & 1) caps.long_mode = true;
}
return caps;
}
const CPUCaps& GetCPUCaps() {
static CPUCaps caps = Detect();
return caps;
}
std::string GetCPUCapsString() {
auto caps = GetCPUCaps();
std::string sum(caps.cpu_string);
sum += " (";
sum += caps.brand_string;
sum += ")";
if (caps.sse) sum += ", SSE";
if (caps.sse2) {
sum += ", SSE2";
if (!caps.flush_to_zero) sum += " (without DAZ)";
}
if (caps.sse3) sum += ", SSE3";
if (caps.ssse3) sum += ", SSSE3";
if (caps.sse4_1) sum += ", SSE4.1";
if (caps.sse4_2) sum += ", SSE4.2";
if (caps.avx) sum += ", AVX";
if (caps.avx2) sum += ", AVX2";
if (caps.bmi1) sum += ", BMI1";
if (caps.bmi2) sum += ", BMI2";
if (caps.fma) sum += ", FMA";
if (caps.aes) sum += ", AES";
if (caps.movbe) sum += ", MOVBE";
if (caps.long_mode) sum += ", 64-bit support";
return sum;
}
} // namespace Common

View file

@ -0,0 +1,66 @@
// Copyright 2013 Dolphin Emulator Project / 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
namespace Common {
/// x86/x64 CPU vendors that may be detected by this module
enum class CPUVendor {
INTEL,
AMD,
OTHER,
};
/// x86/x64 CPU capabilities that may be detected by this module
struct CPUCaps {
CPUVendor vendor;
char cpu_string[0x21];
char brand_string[0x41];
int num_cores;
bool sse;
bool sse2;
bool sse3;
bool ssse3;
bool sse4_1;
bool sse4_2;
bool lzcnt;
bool avx;
bool avx2;
bool bmi1;
bool bmi2;
bool fma;
bool fma4;
bool aes;
// Support for the FXSAVE and FXRSTOR instructions
bool fxsave_fxrstor;
bool movbe;
// This flag indicates that the hardware supports some mode in which denormal inputs and outputs
// are automatically set to (signed) zero.
bool flush_to_zero;
// Support for LAHF and SAHF instructions in 64-bit mode
bool lahf_sahf_64;
bool long_mode;
};
/**
* Gets the supported capabilities of the host CPU
* @return Reference to a CPUCaps struct with the detected host CPU capabilities
*/
const CPUCaps& GetCPUCaps();
/**
* Gets a string summary of the name and supported capabilities of the host CPU
* @return String summary
*/
std::string GetCPUCapsString();
} // namespace Common

2013
src/common/x64/emitter.cpp Normal file

File diff suppressed because it is too large Load diff

1048
src/common/x64/emitter.h Normal file

File diff suppressed because it is too large Load diff

11
src/daggen/CMakeLists.txt Normal file
View file

@ -0,0 +1,11 @@
set(SRCS
main.cpp
print_tuples.h)
set(HEADERS
)
source_group(daggen FILES ${SRCS} ${HEADERS})
add_executable(dynarmic_daggen ${SRCS})
target_link_libraries(dynarmic_daggen dynarmic_common)
set_target_properties(dynarmic_daggen PROPERTIES LINKER_LANGUAGE CXX)

137
src/daggen/main.cpp Normal file
View file

@ -0,0 +1,137 @@
/* 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 <tuple>
#include <vector>
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/include/std_tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
#include "common/common_types.h"
#include "frontend_arm/ir/opcodes.h"
#include "print_tuples.h"
using Opcode = Dynarmic::IR::Opcode;
struct Inst;
struct Ref;
using Needle = boost::variant<Inst, Ref>;
struct Inst {
Opcode opcode;
std::vector<Needle> children;
std::vector<Needle> parents;
};
struct Ref {
std::string name;
};
int main(int argc, char** argv) {
std::string input{R"(
tile TestTile
in [ a (REGISTER), b (REGISTER) ]
out [ o?(REGISTER) ]
match
o: ()
endmatch
code
code
endcode
endtile
)"};
std::string out_input = "out(REGISTER)";
std::string ident_input = "out";
using Iterator = decltype(input.begin());
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using namespace qi::labels;
using TileIn = std::tuple<std::string, std::string>;
using TileOut = std::tuple<std::string, bool, std::string>;
using TileEntry = std::tuple<std::string, std::vector<TileIn>, std::vector<TileOut>, std::string, std::string>;
qi::rule<Iterator, std::string()> identifier;
identifier %=
qi::lexeme[ascii::alpha >> *ascii::alnum];
qi::rule<Iterator, std::string()> needle_name;
needle_name %=
qi::lexeme['$' >> *ascii::alnum];
qi::rule<Iterator, std::string()> until_closeparen;
until_closeparen %=
qi::lexeme[*(qi::char_ - ')')];
qi::rule<Iterator, TileIn(), ascii::space_type> in;
in %=
(identifier >> '(' >> until_closeparen >> ')');
qi::rule<Iterator, bool()> question_mark;
question_mark =
('?' >> qi::attr(true)) | qi::attr(false);
qi::rule<Iterator, TileOut(), ascii::space_type> out;
out %=
(identifier >> question_mark >> '(' >> until_closeparen >> ')');
qi::rule<Iterator, TileEntry(), ascii::space_type> entry;
entry %= (
qi::lit("tile") >> identifier >>
qi::lit("in") >> '[' >> (in % ',') >> ']' >>
qi::lit("out") >> '[' >> (out % ',') >> ']' >>
qi::lit("match") >>
qi::lexeme[*(qi::char_ - qi::lit("endmatch"))] >>
qi::lit("endmatch") >>
qi::lit("code") >>
qi::lexeme[*(qi::char_ - qi::lit("endcode"))] >>
qi::lit("endcode") >>
qi::lit("endtile")
);
auto first = input.begin(), last = input.end();
TileEntry te;
bool r = qi::phrase_parse(first, last, entry, ascii::space, te);
if (first != last || !r) {
std::cout << "Fail!" << std::endl;
std::cout << te << std::endl;
} else {
std::cout << te << std::endl;
}
first = out_input.begin(), last = out_input.end();
TileOut to;
r = qi::phrase_parse(first, last, out, ascii::space, to);
if (first != last || !r) {
std::cout << "Fail!" << std::endl;
std::cout << to << std::endl;
} else {
std::cout << to << std::endl;
}
first = ident_input.begin(), last = ident_input.end();
std::string test_ident;
r = qi::phrase_parse(first, last, identifier, ascii::space, test_ident);
if (first != last || !r) {
std::cout << "Fail!" << std::endl;
std::cout << test_ident << std::endl;
} else {
std::cout << test_ident << std::endl;
}
return 0;
}

48
src/daggen/print_tuples.h Normal file
View file

@ -0,0 +1,48 @@
/* 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
template<typename Type, unsigned N, unsigned Last>
struct tuple_printer {
static void print(std::ostream& out, const Type& value) {
out << std::get<N>(value) << ", ";
tuple_printer<Type, N + 1, Last>::print(out, value);
}
};
template<typename Type, unsigned N>
struct tuple_printer<Type, N, N> {
static void print(std::ostream& out, const Type& value) {
out << std::get<N>(value);
}
};
template<typename... Types>
std::ostream& operator<<(std::ostream& out, const std::tuple<Types...>& value) {
out << "(";
tuple_printer<std::tuple<Types...>, 0, sizeof...(Types) - 1>::print(out, value);
out << ")";
return out;
}
template<typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& value) {
out << "{";
bool first = true;
for (const auto& v : value) {
if (!first) {
out << ", ";
} else {
first = false;
}
out << v;
}
out << "}";
return out;
}

View file

@ -0,0 +1,22 @@
set(SRCS
arm_disassembler.cpp
)
set(HEADERS
arm_disassembler.h
arm_types.h
decoder/arm.h
decoder/decoder_detail.h
decoder/thumb1.h
frontend_arm.h
ir/ir.cpp
ir/ir.h
ir_emitter.cpp
ir_emitter.h
translate_thumb.h
ir/opcodes.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

@ -0,0 +1,526 @@
/* 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 <cstdlib>
#include <string>
#include "common/bit_util.h"
#include "common/string_util.h"
#include "frontend_arm/arm_types.h"
#include "frontend_arm/decoder/arm.h"
namespace Dynarmic {
namespace Arm {
class DisassemblerVisitor {
public:
const char* CondStr(Cond cond) {
switch (cond) {
case Cond::EQ:
return "eq";
case Cond::NE:
return "ne";
case Cond::CS:
return "cs";
case Cond::CC:
return "cc";
case Cond::MI:
return "mi";
case Cond::PL:
return "pl";
case Cond::VS:
return "vs";
case Cond::VC:
return "vc";
case Cond::HI:
return "hi";
case Cond::LS:
return "ls";
case Cond::GE:
return "ge";
case Cond::LT:
return "lt";
case Cond::GT:
return "gt";
case Cond::LE:
return "le";
case Cond::AL:
return "";
case Cond::NV:
break;
}
assert(false);
return "<internal error>";
}
template<typename T>
const char* SignStr(T value) {
return value >= 0 ? "+" : "-";
}
const char* RegStr(Reg reg) {
switch (reg) {
case Reg::R0:
return "r0";
case Reg::R1:
return "r1";
case Reg::R2:
return "r2";
case Reg::R3:
return "r3";
case Reg::R4:
return "r4";
case Reg::R5:
return "r5";
case Reg::R6:
return "r6";
case Reg::R7:
return "r7";
case Reg::R8:
return "r8";
case Reg::R9:
return "r9";
case Reg::R10:
return "r10";
case Reg::R11:
return "r11";
case Reg::R12:
return "r12";
case Reg::R13:
return "sp";
case Reg::R14:
return "lr";
case Reg::R15:
return "pc";
case Reg::INVALID_REG:
break;
}
assert(false);
return "<internal error>";
}
u32 rotr(u32 x, int shift) {
shift &= 31;
if (!shift) return x;
return (x >> shift) | (x << (32 - shift));
}
u32 ArmExpandImm(int rotate, Imm8 imm8) {
return rotr(static_cast<u32>(imm8), rotate*2);
}
std::string ShiftStr(ShiftType shift, Imm5 imm5) {
switch (shift) {
case ShiftType::LSL:
if (imm5 == 0) return "";
return Common::StringFromFormat(", lsl #%hhu", imm5);
case ShiftType::LSR:
if (imm5 == 0) return ", lsr #32";
return Common::StringFromFormat(", lsr #%hhu", imm5);
case ShiftType::ASR:
if (imm5 == 0) return ", asr #32";
return Common::StringFromFormat(", asr #%hhu", imm5);
case ShiftType::ROR:
if (imm5 == 0) return ", rrx";
return Common::StringFromFormat(", ror #%hhu", imm5);
}
assert(false);
return "<internal error>";
}
std::string RsrStr(Reg s, ShiftType shift, Reg m) {
switch (shift){
case ShiftType::LSL:
return Common::StringFromFormat("%s, LSL %s", RegStr(m), RegStr(s));
case ShiftType::LSR:
return Common::StringFromFormat("%s, LSR %s", RegStr(m), RegStr(s));
case ShiftType::ASR:
return Common::StringFromFormat("%s, ASR %s", RegStr(m), RegStr(s));
case ShiftType::ROR:
return Common::StringFromFormat("%s, ROR %s", RegStr(m), RegStr(s));
}
assert(false);
return "<internal error>";
}
// Branch instructions
std::string arm_B(Cond cond, Imm24 imm24) {
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
return Common::StringFromFormat("b%s %s#%i", CondStr(cond), SignStr(offset), abs(offset));
}
std::string arm_BL(Cond cond, Imm24 imm24) {
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
return Common::StringFromFormat("bl%s %s#%i", CondStr(cond), SignStr(offset), abs(offset));
}
std::string arm_BLX_imm(bool H, Imm24 imm24) {
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8 + (H ? 2 : 0);
return Common::StringFromFormat("blx %s#%i", SignStr(offset), abs(offset));
}
std::string arm_BLX_reg(Cond cond, Reg m) {
return Common::StringFromFormat("blx%s %s", CondStr(cond), RegStr(m));
}
std::string arm_BX(Cond cond, Reg m) {
return Common::StringFromFormat("bx%s %s", CondStr(cond), RegStr(m));
}
std::string arm_BXJ(Cond cond, Reg m) {
return Common::StringFromFormat("bxj%s %s", CondStr(cond), RegStr(m));
}
// Coprocessor instructions
std::string arm_CDP() { return "<unimplemented>"; }
std::string arm_LDC() { return "<unimplemented>"; }
std::string arm_MCR() { return "<unimplemented>"; }
std::string arm_MCRR() { return "<unimplemented>"; }
std::string arm_MRC() { return "<unimplemented>"; }
std::string arm_MRRC() { return "<unimplemented>"; }
std::string arm_STC() { return "<unimplemented>"; }
// Data processing instructions
std::string arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("adc%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("adc%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_ADC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("adc%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_ADD_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("add%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_ADD_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("add%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_ADD_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("add%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_AND_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("and%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_AND_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("and%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_AND_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("and%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_BIC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("bic%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("bic%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_BIC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("bic%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_CMN_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
return Common::StringFromFormat("cmn%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_CMN_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("cmn%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_CMN_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("cmn%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_CMP_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
return Common::StringFromFormat("cmp%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_CMP_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("cmp%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_CMP_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("cmp%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_EOR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("eor%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_EOR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("eor%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_EOR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("eor%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_MOV_imm(Cond cond, bool S, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("mov%s%s %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), ArmExpandImm(rotate, imm8));
}
std::string arm_MOV_reg(Cond cond, bool S, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("mov%s%s %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_MOV_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("mov%s%s %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RsrStr(s, shift, m).c_str());
}
std::string arm_MVN_imm(Cond cond, bool S, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("mvn%s%s %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), ArmExpandImm(rotate, imm8));
}
std::string arm_MVN_reg(Cond cond, bool S, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("mvn%s%s %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_MVN_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("mvn%s%s %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RsrStr(s, shift, m).c_str());
}
std::string arm_ORR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("orr%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_ORR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("orr%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_ORR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("orr%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_RSB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("rsb%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("rsb%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_RSB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("rsb%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_RSC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("rsc%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_RSC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("rsc%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_RSC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("rsc%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_SBC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("sbc%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_SBC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("sbc%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_SBC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("sbc%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_SUB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
return Common::StringFromFormat("sub%s%s %s, %s, #%i", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_SUB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("sub%s%s %s, %s, %s%s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_SUB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("sub%s%s %s, %s, %s", CondStr(cond), S ? "s" : "", RegStr(d), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_TEQ_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
return Common::StringFromFormat("teq%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_TEQ_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("teq%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_TEQ_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("teq%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
}
std::string arm_TST_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
return Common::StringFromFormat("tst%s %s, #%i", CondStr(cond), RegStr(n), ArmExpandImm(rotate, imm8));
}
std::string arm_TST_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
return Common::StringFromFormat("tst%s %s, %s%s", CondStr(cond), RegStr(n), RegStr(m), ShiftStr(shift, imm5).c_str());
}
std::string arm_TST_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
return Common::StringFromFormat("tst%s %s, %s", CondStr(cond), RegStr(n), RsrStr(s, shift, m).c_str());
}
// Exception generation instructions
std::string arm_BKPT(Cond cond, Imm12 imm12, Imm4 imm4) { return "ice"; }
std::string arm_SVC(Cond cond, Imm24 imm24) { return "ice"; }
std::string arm_UDF() { return "ice"; }
// Extension functions
std::string arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_SXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_SXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_SXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_SXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_SXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_UXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_UXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_UXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_UXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_UXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
std::string arm_UXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) { return "ice"; }
// Hint instructions
std::string arm_PLD() { return "<unimplemented>"; }
std::string arm_SEV() { return "<unimplemented>"; }
std::string arm_WFE() { return "<unimplemented>"; }
std::string arm_WFI() { return "<unimplemented>"; }
std::string arm_YIELD() { return "<unimplemented>"; }
// Load/Store instructions
std::string arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
std::string arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
std::string arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
std::string arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
std::string arm_LDRBT() { return "ice"; }
std::string arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
std::string arm_LDRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
std::string arm_LDRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_LDRHT() { return "ice"; }
std::string arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
std::string arm_LDRSB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_LDRSBT() { return "ice"; }
std::string arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
std::string arm_LDRSH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_LDRSHT() { return "ice"; }
std::string arm_LDRT() { return "ice"; }
std::string arm_STR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
std::string arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
std::string arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12) { return "ice"; }
std::string arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) { return "ice"; }
std::string arm_STRBT() { return "ice"; }
std::string arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
std::string arm_STRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_STRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm4 imm8a, Imm4 imm8b) { return "ice"; }
std::string arm_STRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_STRHT() { return "ice"; }
std::string arm_STRT() { return "ice"; }
// Load/Store multiple instructions
std::string arm_LDM(Cond cond, bool P, bool U, bool W, Reg n, RegList list) { return "ice"; }
std::string arm_LDM_usr() { return "ice"; }
std::string arm_LDM_eret() { return "ice"; }
std::string arm_STM(Cond cond, bool P, bool U, bool W, Reg n, RegList list) { return "ice"; }
std::string arm_STM_usr() { return "ice"; }
// Miscellaneous instructions
std::string arm_CLZ(Cond cond, Reg d, Reg m) { return "ice"; }
std::string arm_NOP() { return "ice"; }
std::string arm_SEL(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
// Unsigned sum of absolute difference functions
std::string arm_USAD8(Cond cond, Reg d, Reg m, Reg n) { return "ice"; }
std::string arm_USADA8(Cond cond, Reg d, Reg a, Reg m, Reg n) { return "ice"; }
// Packing instructions
std::string arm_PKHBT(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { return "ice"; }
std::string arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { return "ice"; }
// Reversal instructions
std::string arm_REV(Cond cond, Reg d, Reg m) { return "ice"; }
std::string arm_REV16(Cond cond, Reg d, Reg m) { return "ice"; }
std::string arm_REVSH(Cond cond, Reg d, Reg m) { return "ice"; }
// Saturation instructions
std::string arm_SSAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n) { return "ice"; }
std::string arm_SSAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; }
std::string arm_USAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n) { return "ice"; }
std::string arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) { return "ice"; }
// Multiply (Normal) instructions
std::string arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) { return "ice"; }
std::string arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) { return "ice"; }
// Multiply (Long) instructions
std::string arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
std::string arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
std::string arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
std::string arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
std::string arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) { return "ice"; }
// Multiply (Halfword) instructions
std::string arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) { return "ice"; }
std::string arm_SMLAxy(Cond cond, Reg d, Reg a, Reg m, bool M, bool N, Reg n) { return "ice"; }
std::string arm_SMULxy(Cond cond, Reg d, Reg m, bool M, bool N, Reg n) { return "ice"; }
// Multiply (word by halfword) instructions
std::string arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { return "ice"; }
std::string arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n) { return "ice"; }
// Multiply (Most significant word) instructions
std::string arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) { return "ice"; }
std::string arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) { return "ice"; }
std::string arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n) { return "ice"; }
// Multiply (Dual) instructions
std::string arm_SMLAD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { return "ice"; }
std::string arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) { return "ice"; }
std::string arm_SMLSD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) { return "ice"; }
std::string arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) { return "ice"; }
std::string arm_SMUAD(Cond cond, Reg d, Reg m, bool M, Reg n) { return "ice"; }
std::string arm_SMUSD(Cond cond, Reg d, Reg m, bool M, Reg n) { return "ice"; }
// Parallel Add/Subtract (Modulo arithmetic) instructions
std::string arm_SADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_USAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_USUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_USUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
// Parallel Add/Subtract (Saturating) instructions
std::string arm_QADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UQADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UQADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UQASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UQSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UQSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UQSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
// Parallel Add/Subtract (Halving) instructions
std::string arm_SHADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SHADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SHASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SHSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SHSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SHSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UHADD8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UHADD16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UHASX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UHSAX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UHSUB8(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_UHSUB16(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
// Saturated Add/Subtract instructions
std::string arm_QADD(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QSUB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QDADD(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_QDSUB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
// Synchronization Primitive instructions
std::string arm_CLREX() { return "ice"; }
std::string arm_LDREX(Cond cond, Reg n, Reg d) { return "ice"; }
std::string arm_LDREXB(Cond cond, Reg n, Reg d) { return "ice"; }
std::string arm_LDREXD(Cond cond, Reg n, Reg d) { return "ice"; }
std::string arm_LDREXH(Cond cond, Reg n, Reg d) { return "ice"; }
std::string arm_STREX(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_STREXB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_STREXD(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_STREXH(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SWP(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
std::string arm_SWPB(Cond cond, Reg n, Reg d, Reg m) { return "ice"; }
// Status register access instructions
std::string arm_CPS() { return "ice"; }
std::string arm_MRS() { return "ice"; }
std::string arm_MSR() { return "ice"; }
std::string arm_RFE() { return "ice"; }
std::string arm_SETEND(bool E) { return "ice"; }
std::string arm_SRS() { return "ice"; }
};
std::string DisassembleArm(u32 instruction) {
DisassemblerVisitor visitor;
auto decoder = DecodeArm<DisassemblerVisitor>(instruction);
return !decoder ? "UNKNOWN" : decoder->call(visitor, instruction);
}
} // namespace Arm
} // namespace Dynarmic

View file

@ -0,0 +1,19 @@
/* 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 <string>
#include "common/common_types.h"
namespace Dynarmic {
namespace Arm {
std::string DisassembleArm(u32 instruction);
} // namespace Arm
} // namespace Dynarmic

View file

@ -0,0 +1,76 @@
/* 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 <cassert>
#include <tuple>
#include <type_traits>
#include "common/common_types.h"
namespace Dynarmic {
namespace Arm {
enum class Cond {
EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV
};
enum class Reg {
R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15,
SP = R13,
LR = R14,
PC = R15,
INVALID_REG = 99
};
inline Reg operator+(Reg reg, int number) {
assert(reg != Reg::INVALID_REG);
int new_reg = static_cast<int>(reg) + number;
assert(new_reg >= 0 && new_reg <= 15);
return static_cast<Reg>(new_reg);
}
using Imm4 = u32;
using Imm5 = u32;
using Imm8 = u32;
using Imm11 = u32;
using Imm12 = u32;
using Imm24 = u32;
using RegList = u16;
enum class ShiftType {
LSL,
LSR,
ASR,
ROR ///< RRX falls under this too
};
enum class SignExtendRotation {
ROR_0, ///< ROR #0 or omitted
ROR_8, ///< ROR #8
ROR_16, ///< ROR #16
ROR_24 ///< ROR #24
};
struct LocationDescriptor {
LocationDescriptor(u32 arm_pc, bool TFlag, bool EFlag, Cond cond = Cond::AL)
: arm_pc(arm_pc), TFlag(TFlag), EFlag(EFlag), cond(cond) {}
u32 arm_pc;
bool TFlag; ///< Thumb / ARM
bool EFlag; ///< Big / Little Endian
Cond cond;
bool operator == (const LocationDescriptor& o) const {
return std::tie(arm_pc, TFlag, EFlag, cond) == std::tie(o.arm_pc, o.TFlag, o.EFlag, o.cond);
}
};
} // namespace Arm
} // namespace Dynarmic

View file

@ -0,0 +1,351 @@
/* 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.
*
* Original version of table by Lioncash.
*/
#pragma once
#include <algorithm>
#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 ArmMatcher {
using CallRetT = typename mp::MemFnInfo<decltype(&Visitor::arm_UDF), &Visitor::arm_UDF>::return_type;
ArmMatcher(const char* const name, u32 mask, u32 expect, std::function<CallRetT(Visitor&, u32)> 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(u32 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, u32 instruction) const {
assert(Matches(instruction));
return fn(v, instruction);
}
private:
const char* name;
u32 mask, expect;
std::function<CallRetT(Visitor&, u32)> fn;
};
template <typename V>
static const std::array<ArmMatcher<V>, 221> g_arm_instruction_table = {{
#define INST(fn, name, bitstring) detail::detail<ArmMatcher, u32, 32>::GetMatcher<decltype(fn), fn>(name, bitstring)
// Branch instructions
{ INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") }, // v5
{ INST(&V::arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") }, // v5
{ INST(&V::arm_B, "B", "cccc1010vvvvvvvvvvvvvvvvvvvvvvvv") }, // all
{ INST(&V::arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv") }, // all
{ INST(&V::arm_BX, "BX", "cccc000100101111111111110001mmmm") }, // v4T
{ INST(&V::arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm") }, // v5J
// Coprocessor instructions
{ INST(&V::arm_CDP, "CDP2", "11111110-------------------1----") }, // v5
{ INST(&V::arm_CDP, "CDP", "----1110-------------------0----") }, // v2
{ INST(&V::arm_LDC, "LDC2", "1111110----1--------------------") }, // v5
{ INST(&V::arm_LDC, "LDC", "----110----1--------------------") }, // v2
{ INST(&V::arm_MCR, "MCR2", "----1110---0---------------1----") }, // v5
{ INST(&V::arm_MCR, "MCR", "----1110---0---------------1----") }, // v2
{ INST(&V::arm_MCRR, "MCRR2", "111111000100--------------------") }, // v6
{ INST(&V::arm_MCRR, "MCRR", "----11000100--------------------") }, // v5E
{ INST(&V::arm_MRC, "MRC2", "11111110---1---------------1----") }, // v5
{ INST(&V::arm_MRC, "MRC", "----1110---1---------------1----") }, // v2
{ INST(&V::arm_MRRC, "MRRC2", "111111000101--------------------") }, // v6
{ INST(&V::arm_MRRC, "MRRC", "----11000101--------------------") }, // v5E
{ INST(&V::arm_STC, "STC2", "1111110----0--------------------") }, // v5
{ INST(&V::arm_STC, "STC", "----110----0--------------------") }, // v2
// Data Processing instructions
{ INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_ADC_reg, "ADC (reg)", "cccc0000101Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_ADC_rsr, "ADC (rsr)", "cccc0000101Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_ADD_reg, "ADD (reg)", "cccc0000100Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_ADD_rsr, "ADD (rsr)", "cccc0000100Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_AND_imm, "AND (imm)", "cccc0010000Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_AND_reg, "AND (reg)", "cccc0000000Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_AND_rsr, "AND (rsr)", "cccc0000000Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_BIC_imm, "BIC (imm)", "cccc0011110Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_BIC_reg, "BIC (reg)", "cccc0001110Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_BIC_rsr, "BIC (rsr)", "cccc0001110Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_CMN_imm, "CMN (imm)", "cccc00110111nnnn0000rrrrvvvvvvvv") }, // all
{ INST(&V::arm_CMN_reg, "CMN (reg)", "cccc00010111nnnn0000vvvvvrr0mmmm") }, // all
{ INST(&V::arm_CMN_rsr, "CMN (rsr)", "cccc00010111nnnn0000ssss0rr1mmmm") }, // all
{ INST(&V::arm_CMP_imm, "CMP (imm)", "cccc00110101nnnn0000rrrrvvvvvvvv") }, // all
{ INST(&V::arm_CMP_reg, "CMP (reg)", "cccc00010101nnnn0000vvvvvrr0mmmm") }, // all
{ INST(&V::arm_CMP_rsr, "CMP (rsr)", "cccc00010101nnnn0000ssss0rr1mmmm") }, // all
{ INST(&V::arm_EOR_imm, "EOR (imm)", "cccc0010001Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_EOR_reg, "EOR (reg)", "cccc0000001Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_EOR_rsr, "EOR (rsr)", "cccc0000001Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_MOV_imm, "MOV (imm)", "cccc0011101S0000ddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_MOV_reg, "MOV (reg)", "cccc0001101S0000ddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_MOV_rsr, "MOV (rsr)", "cccc0001101S0000ddddssss0rr1mmmm") }, // all
{ INST(&V::arm_MVN_imm, "MVN (imm)", "cccc0011111S0000ddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_MVN_reg, "MVN (reg)", "cccc0001111S0000ddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_MVN_rsr, "MVN (rsr)", "cccc0001111S0000ddddssss0rr1mmmm") }, // all
{ INST(&V::arm_ORR_imm, "ORR (imm)", "cccc0011100Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_ORR_reg, "ORR (reg)", "cccc0001100Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_ORR_rsr, "ORR (rsr)", "cccc0001100Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_RSB_imm, "RSB (imm)", "cccc0010011Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_RSB_reg, "RSB (reg)", "cccc0000011Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_RSB_rsr, "RSB (rsr)", "cccc0000011Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_RSC_imm, "RSC (imm)", "cccc0010111Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_RSC_reg, "RSC (reg)", "cccc0000111Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_RSC_rsr, "RSC (rsr)", "cccc0000111Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv") }, // all
{ INST(&V::arm_SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm") }, // all
{ INST(&V::arm_SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm") }, // all
{ INST(&V::arm_TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv") }, // all
{ INST(&V::arm_TEQ_reg, "TEQ (reg)", "cccc00010011nnnn0000vvvvvrr0mmmm") }, // all
{ INST(&V::arm_TEQ_rsr, "TEQ (rsr)", "cccc00010011nnnn0000ssss0rr1mmmm") }, // all
{ INST(&V::arm_TST_imm, "TST (imm)", "cccc00110001nnnn0000rrrrvvvvvvvv") }, // all
{ INST(&V::arm_TST_reg, "TST (reg)", "cccc00010001nnnn0000vvvvvrr0mmmm") }, // all
{ INST(&V::arm_TST_rsr, "TST (rsr)", "cccc00010001nnnn0000ssss0rr1mmmm") }, // all
// Exception Generating instructions
{ INST(&V::arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv") }, // v5
{ INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv") }, // all
{ INST(&V::arm_UDF, "UDF", "111001111111------------1111----") }, // all
// Extension instructions
{ INST(&V::arm_SXTB, "SXTB", "cccc011010101111ddddrr000111mmmm") }, // v6
{ INST(&V::arm_SXTB16, "SXTB16", "cccc011010001111ddddrr000111mmmm") }, // v6
{ INST(&V::arm_SXTH, "SXTH", "cccc011010111111ddddrr000111mmmm") }, // v6
{ INST(&V::arm_SXTAB, "SXTAB", "cccc01101010nnnnddddrr000111mmmm") }, // v6
{ INST(&V::arm_SXTAB16, "SXTAB16", "cccc01101000nnnnddddrr000111mmmm") }, // v6
{ INST(&V::arm_SXTAH, "SXTAH", "cccc01101011nnnnddddrr000111mmmm") }, // v6
{ INST(&V::arm_UXTB, "UXTB", "cccc011011101111ddddrr000111mmmm") }, // v6
{ INST(&V::arm_UXTB16, "UXTB16", "cccc011011001111ddddrr000111mmmm") }, // v6
{ INST(&V::arm_UXTH, "UXTH", "cccc011011111111ddddrr000111mmmm") }, // v6
{ INST(&V::arm_UXTAB, "UXTAB", "cccc01101110nnnnddddrr000111mmmm") }, // v6
{ INST(&V::arm_UXTAB16, "UXTAB16", "cccc01101100nnnnddddrr000111mmmm") }, // v6
{ INST(&V::arm_UXTAH, "UXTAH", "cccc01101111nnnnddddrr000111mmmm") }, // v6
// Hint instructions
{ INST(&V::arm_PLD, "PLD", "111101---101----1111------------") }, // v5E
{ INST(&V::arm_SEV, "SEV", "----0011001000001111000000000100") }, // v6K
{ INST(&V::arm_WFE, "WFE", "----0011001000001111000000000010") }, // v6K
{ INST(&V::arm_WFI, "WFI", "----0011001000001111000000000011") }, // v6K
{ INST(&V::arm_YIELD, "YIELD", "----0011001000001111000000000001") }, // v6K
// Synchronization Primitive instructions
{ INST(&V::arm_CLREX, "CLREX", "11110101011111111111000000011111") }, // v6K
{ INST(&V::arm_LDREX, "LDREX", "cccc00011001nnnndddd111110011111") }, // v6
{ INST(&V::arm_LDREXB, "LDREXB", "cccc00011101nnnndddd111110011111") }, // v6K
{ INST(&V::arm_LDREXD, "LDREXD", "cccc00011011nnnndddd111110011111") }, // v6K
{ INST(&V::arm_LDREXH, "LDREXH", "cccc00011111nnnndddd111110011111") }, // v6K
{ INST(&V::arm_STREX, "STREX", "cccc00011000nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_STREXB, "STREXB", "cccc00011100nnnndddd11111001mmmm") }, // v6K
{ INST(&V::arm_STREXD, "STREXD", "cccc00011010nnnndddd11111001mmmm") }, // v6K
{ INST(&V::arm_STREXH, "STREXH", "cccc00011110nnnndddd11111001mmmm") }, // v6K
{ INST(&V::arm_SWP, "SWP", "cccc00010000nnnndddd00001001mmmm") }, // v2S (v6: Deprecated)
{ INST(&V::arm_SWPB, "SWPB", "cccc00010100nnnndddd00001001mmmm") }, // v2S (v6: Deprecated)
// Load/Store instructions
{ INST(&V::arm_LDR_imm, "LDR (imm)", "cccc010pu0w1nnnnddddvvvvvvvvvvvv") },
{ INST(&V::arm_LDR_reg, "LDR (reg)", "cccc011pu0w1nnnnddddvvvvvrr0mmmm") },
{ INST(&V::arm_LDRB_imm, "LDRB (imm)", "cccc010pu1w1nnnnddddvvvvvvvvvvvv") },
{ INST(&V::arm_LDRB_reg, "LDRB (reg)", "cccc011pu1w1nnnnddddvvvvvrr0mmmm") },
{ INST(&V::arm_LDRBT, "LDRBT (A1)", "----0100-111--------------------") },
{ INST(&V::arm_LDRBT, "LDRBT (A2)", "----0110-111---------------0----") },
{ INST(&V::arm_LDRD_imm, "LDRD (imm)", "cccc000pu1w0nnnnddddvvvv1101vvvv") }, // v5E
{ INST(&V::arm_LDRD_reg, "LDRD (reg)", "cccc000pu0w0nnnndddd00001101mmmm") }, // v5E
{ INST(&V::arm_LDRH_imm, "LDRH (imm)", "cccc000pu1w1nnnnddddvvvv1011vvvv") },
{ INST(&V::arm_LDRH_reg, "LDRH (reg)", "cccc000pu0w1nnnndddd00001011mmmm") },
{ INST(&V::arm_LDRHT, "LDRHT (A1)", "----0000-111------------1011----") },
{ INST(&V::arm_LDRHT, "LDRHT (A2)", "----0000-011--------00001011----") },
{ INST(&V::arm_LDRSB_imm, "LDRSB (imm)", "cccc000pu1w1nnnnddddvvvv1101vvvv") },
{ INST(&V::arm_LDRSB_reg, "LDRSB (reg)", "cccc000pu0w1nnnndddd00001101mmmm") },
{ INST(&V::arm_LDRSBT, "LDRSBT (A1)", "----0000-111------------1101----") },
{ INST(&V::arm_LDRSBT, "LDRSBT (A2)", "----0000-011--------00001101----") },
{ INST(&V::arm_LDRSH_imm, "LDRSH (imm)", "cccc000pu1w1nnnnddddvvvv1111vvvv") },
{ INST(&V::arm_LDRSH_reg, "LDRSH (reg)", "cccc000pu0w1nnnndddd00001111mmmm") },
{ INST(&V::arm_LDRSHT, "LDRSHT (A1)", "----0000-111------------1111----") },
{ INST(&V::arm_LDRSHT, "LDRSHT (A2)", "----0000-011--------00001111----") },
{ INST(&V::arm_LDRT, "LDRT (A1)", "----0100-011--------------------") },
{ INST(&V::arm_LDRT, "LDRT (A2)", "----0110-011---------------0----") },
{ INST(&V::arm_STR_imm, "STR (imm)", "cccc010pu0w0nnnnddddvvvvvvvvvvvv") },
{ INST(&V::arm_STR_reg, "STR (reg)", "cccc011pu0w0nnnnddddvvvvvrr0mmmm") },
{ INST(&V::arm_STRB_imm, "STRB (imm)", "cccc010pu1w0nnnnddddvvvvvvvvvvvv") },
{ INST(&V::arm_STRB_reg, "STRB (reg)", "cccc011pu1w0nnnnddddvvvvvrr0mmmm") },
{ INST(&V::arm_STRBT, "STRBT (A1)", "----0100-110--------------------") },
{ INST(&V::arm_STRBT, "STRBT (A2)", "----0110-110---------------0----") },
{ INST(&V::arm_STRD_imm, "STRD (imm)", "cccc000pu1w0nnnnddddvvvv1111vvvv") }, // v5E
{ INST(&V::arm_STRD_reg, "STRD (reg)", "cccc000pu0w0nnnndddd00001111mmmm") }, // v5E
{ INST(&V::arm_STRH_imm, "STRH (imm)", "cccc000pu1w0nnnnddddvvvv1011vvvv") },
{ INST(&V::arm_STRH_reg, "STRH (reg)", "cccc000pu0w0nnnndddd00001011mmmm") },
{ INST(&V::arm_STRHT, "STRHT (A1)", "----0000-110------------1011----") },
{ INST(&V::arm_STRHT, "STRHT (A2)", "----0000-010--------00001011----") },
{ INST(&V::arm_STRT, "STRT (A1)", "----0100-010--------------------") },
{ INST(&V::arm_STRT, "STRT (A2)", "----0110-010---------------0----") },
// Load/Store Multiple instructions
{ INST(&V::arm_LDM, "LDM", "cccc100pu0w1nnnnxxxxxxxxxxxxxxxx") }, // all
{ INST(&V::arm_LDM_usr, "LDM (usr reg)", "----100--101--------------------") }, // all
{ INST(&V::arm_LDM_eret, "LDM (exce ret)", "----100--1-1----1---------------") }, // all
{ INST(&V::arm_STM, "STM", "cccc100pu0w0nnnnxxxxxxxxxxxxxxxx") }, // all
{ INST(&V::arm_STM_usr, "STM (usr reg)", "----100--100--------------------") }, // all
// Miscellaneous instructions
{ INST(&V::arm_CLZ, "CLZ", "cccc000101101111dddd11110001mmmm") }, // v5
{ INST(&V::arm_NOP, "NOP", "----001100100000111100000000----") }, // v6K
{ INST(&V::arm_SEL, "SEL", "cccc01101000nnnndddd11111011mmmm") }, // v6
// Unsigned Sum of Absolute Differences instructions
{ INST(&V::arm_USAD8, "USAD8", "cccc01111000dddd1111mmmm0001nnnn") }, // v6
{ INST(&V::arm_USADA8, "USADA8", "cccc01111000ddddaaaammmm0001nnnn") }, // v6
// Packing instructions
{ INST(&V::arm_PKHBT, "PKHBT", "cccc01101000nnnnddddvvvvv001mmmm") }, // v6K
{ INST(&V::arm_PKHTB, "PKHTB", "cccc01101000nnnnddddvvvvv101mmmm") }, // v6K
// Reversal instructions
{ INST(&V::arm_REV, "REV", "cccc011010111111dddd11110011mmmm") }, // v6
{ INST(&V::arm_REV16, "REV16", "cccc011010111111dddd11111011mmmm") }, // v6
{ INST(&V::arm_REVSH, "REVSH", "cccc011011111111dddd11111011mmmm") }, // v6
// Saturation instructions
{ INST(&V::arm_SSAT, "SSAT", "cccc0110101vvvvvddddvvvvvr01nnnn") }, // v6
{ INST(&V::arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn") }, // v6
{ INST(&V::arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn") }, // v6
{ INST(&V::arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn") }, // v6
// Multiply (Normal) instructions
{ INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") }, // v2
{ INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") }, // v2
// Multiply (Long) instructions
{ INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn") }, // v3M
{ INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn") }, // v3M
{ INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn") }, // v6
{ INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn") }, // v3M
{ INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn") }, // v3M
// Multiply (Halfword) instructions
{ INST(&V::arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn") }, // v5xP
{ INST(&V::arm_SMLAxy, "SMLAXY", "cccc00010000ddddaaaammmm1xy0nnnn") }, // v5xP
{ INST(&V::arm_SMULxy, "SMULXY", "cccc00010110dddd0000mmmm1xy0nnnn") }, // v5xP
// Multiply (Word by Halfword) instructions
{ INST(&V::arm_SMLAWy, "SMLAWY", "cccc00010010ddddaaaammmm1y00nnnn") }, // v5xP
{ INST(&V::arm_SMULWy, "SMULWY", "cccc00010010dddd0000mmmm1y10nnnn") }, // v5xP
// Multiply (Most Significant Word) instructions
{ INST(&V::arm_SMMUL, "SMMUL", "cccc01110101dddd1111mmmm00R1nnnn") }, // v6
{ INST(&V::arm_SMMLA, "SMMLA", "cccc01110101ddddaaaammmm00R1nnnn") }, // v6
{ INST(&V::arm_SMMLS, "SMMLS", "cccc01110101ddddaaaammmm11R1nnnn") }, // v6
// Multiply (Dual) instructions
{ INST(&V::arm_SMLAD, "SMLAD", "cccc01110000ddddaaaammmm00M1nnnn") }, // v6
{ INST(&V::arm_SMLALD, "SMLALD", "cccc01110100ddddaaaammmm00M1nnnn") }, // v6
{ INST(&V::arm_SMLSD, "SMLSD", "cccc01110000ddddaaaammmm01M1nnnn") }, // v6
{ INST(&V::arm_SMLSLD, "SMLSLD", "cccc01110100ddddaaaammmm01M1nnnn") }, // v6
{ INST(&V::arm_SMUAD, "SMUAD", "cccc01110000dddd1111mmmm00M1nnnn") }, // v6
{ INST(&V::arm_SMUSD, "SMUSD", "cccc01110000dddd1111mmmm01M1nnnn") }, // v6
// Parallel Add/Subtract (Modulo) instructions
{ INST(&V::arm_SADD8, "SADD8", "cccc01100001nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_SADD16, "SADD16", "cccc01100001nnnndddd11110001mmmm") }, // v6
{ INST(&V::arm_SASX, "SASX", "cccc01100001nnnndddd11110011mmmm") }, // v6
{ INST(&V::arm_SSAX, "SSAX", "cccc01100001nnnndddd11110101mmmm") }, // v6
{ INST(&V::arm_SSUB8, "SSUB8", "cccc01100001nnnndddd11111111mmmm") }, // v6
{ INST(&V::arm_SSUB16, "SSUB16", "cccc01100001nnnndddd11110111mmmm") }, // v6
{ INST(&V::arm_UADD8, "UADD8", "cccc01100101nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_UADD16, "UADD16", "cccc01100101nnnndddd11110001mmmm") }, // v6
{ INST(&V::arm_UASX, "UASX", "cccc01100101nnnndddd11110011mmmm") }, // v6
{ INST(&V::arm_USAX, "USAX", "cccc01100101nnnndddd11110101mmmm") }, // v6
{ INST(&V::arm_USUB8, "USUB8", "cccc01100101nnnndddd11111111mmmm") }, // v6
{ INST(&V::arm_USUB16, "USUB16", "cccc01100101nnnndddd11110111mmmm") }, // v6
// Parallel Add/Subtract (Saturating) instructions
{ INST(&V::arm_QADD8, "QADD8", "cccc01100010nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_QADD16, "QADD16", "cccc01100010nnnndddd11110001mmmm") }, // v6
{ INST(&V::arm_QASX, "QASX", "cccc01100010nnnndddd11110011mmmm") }, // v6
{ INST(&V::arm_QSAX, "QSAX", "cccc01100010nnnndddd11110101mmmm") }, // v6
{ INST(&V::arm_QSUB8, "QSUB8", "cccc01100010nnnndddd11111111mmmm") }, // v6
{ INST(&V::arm_QSUB16, "QSUB16", "cccc01100010nnnndddd11110111mmmm") }, // v6
{ INST(&V::arm_UQADD8, "UQADD8", "cccc01100110nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_UQADD16, "UQADD16", "cccc01100110nnnndddd11110001mmmm") }, // v6
{ INST(&V::arm_UQASX, "UQASX", "cccc01100110nnnndddd11110011mmmm") }, // v6
{ INST(&V::arm_UQSAX, "UQSAX", "cccc01100110nnnndddd11110101mmmm") }, // v6
{ INST(&V::arm_UQSUB8, "UQSUB8", "cccc01100110nnnndddd11111111mmmm") }, // v6
{ INST(&V::arm_UQSUB16, "UQSUB16", "cccc01100110nnnndddd11110111mmmm") }, // v6
// Parallel Add/Subtract (Halving) instructions
{ INST(&V::arm_SHADD8, "SHADD8", "cccc01100011nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_SHADD16, "SHADD16", "cccc01100011nnnndddd11110001mmmm") }, // v6
{ INST(&V::arm_SHASX, "SHASX", "cccc01100011nnnndddd11110011mmmm") }, // v6
{ INST(&V::arm_SHSAX, "SHSAX", "cccc01100011nnnndddd11110101mmmm") }, // v6
{ INST(&V::arm_SHSUB8, "SHSUB8", "cccc01100011nnnndddd11111111mmmm") }, // v6
{ INST(&V::arm_SHSUB16, "SHSUB16", "cccc01100011nnnndddd11110111mmmm") }, // v6
{ INST(&V::arm_UHADD8, "UHADD8", "cccc01100111nnnndddd11111001mmmm") }, // v6
{ INST(&V::arm_UHADD16, "UHADD16", "cccc01100111nnnndddd11110001mmmm") }, // v6
{ INST(&V::arm_UHASX, "UHASX", "cccc01100111nnnndddd11110011mmmm") }, // v6
{ INST(&V::arm_UHSAX, "UHSAX", "cccc01100111nnnndddd11110101mmmm") }, // v6
{ INST(&V::arm_UHSUB8, "UHSUB8", "cccc01100111nnnndddd11111111mmmm") }, // v6
{ INST(&V::arm_UHSUB16, "UHSUB16", "cccc01100111nnnndddd11110111mmmm") }, // v6
// Saturated Add/Subtract instructions
{ INST(&V::arm_QADD, "QADD", "cccc00010000nnnndddd00000101mmmm") }, // v5xP
{ INST(&V::arm_QSUB, "QSUB", "cccc00010010nnnndddd00000101mmmm") }, // v5xP
{ INST(&V::arm_QDADD, "QDADD", "cccc00010100nnnndddd00000101mmmm") }, // v5xP
{ INST(&V::arm_QDSUB, "QDSUB", "cccc00010110nnnndddd00000101mmmm") }, // v5xP
// Status Register Access instructions
{ INST(&V::arm_CPS, "CPS", "111100010000---00000000---0-----") }, // v6
{ INST(&V::arm_SETEND, "SETEND", "1111000100000001000000e000000000") }, // v6
{ INST(&V::arm_MRS, "MRS", "----00010-00--------00--00000000") }, // v3
{ INST(&V::arm_MSR, "MSR", "----00-10-10----1111------------") }, // v3
{ INST(&V::arm_RFE, "RFE", "----0001101-0000---------110----") }, // v6
{ INST(&V::arm_SRS, "SRS", "0000011--0-00000000000000001----") }, // v6
#undef INST
}};
template<typename Visitor>
boost::optional<const ArmMatcher<Visitor>&> DecodeArm(u32 instruction) {
const auto& table = g_arm_instruction_table<Visitor>;
const auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? boost::make_optional<const ArmMatcher<Visitor>&>(*iter) : boost::none;
}
} // namespace Arm
} // namespace Dynarmic

View file

@ -0,0 +1,132 @@
/* 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 <algorithm>
#include <array>
#include <cassert>
#include <tuple>
#include "common/mp.h"
namespace Dynarmic {
namespace Arm {
namespace detail {
/**
* Helper functions for the decoders.
* @tparam MatcherT The type of the matcher. (ARM: ArmMatcher, Thumb1: Thumb1Matcher)
* @tparam InstructionT The type that represents an instruction. (ARM: u32, Thumb1: u16)
* @tparam instruction_bit_size Bit-size for an instruction. (ARM: 32, Thumb1: 16)
*/
template<template<typename> class MatcherT, typename InstructionT, size_t instruction_bit_size>
struct detail {
private:
/**
* Generates the mask and the expected value after masking from a given bitstring.
* A '0' in a bitstring indicates that a zero must be present at that bit position.
* A '1' in a bitstring indicates that a one must be present at that bit position.
*/
static auto GetMaskAndExpect(const char* const bitstring) {
InstructionT mask = 0, expect = 0;
for (size_t i = 0; i < instruction_bit_size; i++) {
const size_t bit_position = instruction_bit_size - i - 1;
switch (bitstring[i]) {
case '0':
mask |= 1 << bit_position;
break;
case '1':
expect |= 1 << bit_position;
mask |= 1 << bit_position;
break;
}
}
return std::make_tuple(mask, expect);
}
/**
* Generates the masks and shifts for each argument.
* A '-' in a bitstring indicates that we don't care about that value.
* An argument is specified by a continuous string of the same character.
*/
template<size_t N>
static auto GetArgInfo(const char* const bitstring) {
std::array<InstructionT, N> masks = {};
std::array<size_t, N> shifts = {};
size_t arg_index = 0;
char ch = 0;
for (size_t i = 0; i < instruction_bit_size; i++) {
const size_t bit_position = instruction_bit_size - i - 1;
if (bitstring[i] == '0' || bitstring[i] == '1' || bitstring[i] == '-') {
if (ch != 0) {
ch = 0;
arg_index++;
}
} else {
if (ch == 0) {
ch = bitstring[i];
} else if (ch != bitstring[i]) {
ch = bitstring[i];
arg_index++;
}
assert(arg_index < N);
masks[arg_index] |= 1 << bit_position;
shifts[arg_index] = bit_position;
}
}
assert(std::all_of(masks.begin(), masks.end(), [](auto m){ return m != 0; }));
return std::make_tuple(masks, shifts);
}
/**
* This struct's Make member function generates a lambda which decodes an instruction based on
* the provided arg_masks and arg_shifts. The Visitor member function to call is provided as a
* template argument.
*/
template<typename FnT, FnT fn>
struct VisitorCaller;
template<typename Visitor, typename ...Args, typename CallRetT, CallRetT (Visitor::*fn)(Args...)>
struct VisitorCaller<CallRetT(Visitor::*)(Args...), fn> {
template<size_t ...iota>
static auto Make(std::integer_sequence<size_t, iota...>,
const std::array<InstructionT, sizeof...(iota)> arg_masks,
const std::array<size_t, sizeof...(iota)> arg_shifts) {
return [arg_masks, arg_shifts](Visitor& v, InstructionT instruction) {
(void)instruction;
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
};
}
};
public:
/**
* Creates a matcher that can match and parse instructions based on bitstring.
* See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
*/
template<typename FnT, FnT fn>
static auto GetMatcher(const char* const name, const char* const bitstring) {
using Visitor = typename mp::MemFnInfo<FnT, fn>::class_type;
constexpr size_t args_count = mp::MemFnInfo<FnT, fn>::args_count;
using Iota = std::make_index_sequence<args_count>;
const auto mask_and_expect = GetMaskAndExpect(bitstring);
const auto arg_info = GetArgInfo<args_count>(bitstring);
const auto proxy_fn = VisitorCaller<FnT, fn>::Make(Iota(), std::get<0>(arg_info), std::get<1>(arg_info));
return MatcherT<Visitor>(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn);
}
};
} // namespace detail
} // namespace Arm
} // namespace Dynarmic

View file

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

@ -0,0 +1,15 @@
/* 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
namespace Dynarmic {
namespace Arm {
} // namespace Arm
} // namespace Dynarmic

130
src/frontend_arm/ir/ir.cpp Normal file
View file

@ -0,0 +1,130 @@
/* 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 <algorithm>
#include <map>
#include "common/assert.h"
#include "frontend_arm/ir/ir.h"
namespace Dynarmic {
namespace IR {
// Opcode information
namespace OpcodeInfo {
using T = Dynarmic::IR::Type;
struct Meta {
const char* name;
Type type;
std::vector<Type> arg_types;
};
static const std::map<Opcode, Meta> opcode_info {{
#define OPCODE(name, type, ...) { Opcode::name, { #name, type, { __VA_ARGS__ } } },
#include "opcodes.inc"
#undef OPCODE
}};
} // namespace OpcodeInfo
Type GetTypeOf(Opcode op) {
return OpcodeInfo::opcode_info.at(op).type;
}
size_t GetNumArgsOf(Opcode op) {
return OpcodeInfo::opcode_info.at(op).arg_types.size();
}
Type GetArgTypeOf(Opcode op, size_t arg_index) {
return OpcodeInfo::opcode_info.at(op).arg_types.at(arg_index);
}
const char* GetNameOf(Opcode op) {
return OpcodeInfo::opcode_info.at(op).name;
}
// Value class member definitions
void Value::ReplaceUsesWith(ValuePtr replacement) {
for (const auto& use : uses) {
use.use_owner.lock()->ReplaceUseOfXWithY(use.value.lock(), replacement);
}
assert(uses.empty());
}
std::vector<ValuePtr> Value::GetUses() const {
std::vector<ValuePtr> result(uses.size());
std::transform(uses.begin(), uses.end(), result.begin(), [](const auto& use){ return use.use_owner.lock(); });
return result;
}
void Value::AddUse(ValuePtr owner) {
// There can be multiple uses from the same owner.
uses.push_back({ shared_from_this(), owner });
}
void Value::RemoveUse(ValuePtr owner) {
// Remove only one use.
auto iter = std::find_if(uses.begin(), uses.end(), [&owner](auto use) { return use.use_owner.lock() == owner; });
ASSERT_MSG(iter != uses.end(), "RemoveUse without associated AddUse. Bug in use management code.");
uses.erase(iter);
}
void Value::ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) {
// This should never be called. Use management is incorrect if this is ever called.
ASSERT_MSG(false, "This Value type doesn't use any values. Bug in use management code.");
}
// Inst class member definitions
Inst::Inst(Opcode op_) : Value(op_) {
args.resize(GetNumArgsOf(op));
}
void Inst::SetArg(size_t index, ValuePtr value) {
auto this_ = shared_from_this();
if (auto prev_value = args.at(index).lock()) {
prev_value->RemoveUse(this_);
}
ASSERT(value->GetType() == GetArgTypeOf(op, index));
args.at(index) = value;
value->AddUse(this_);
}
ValuePtr Inst::GetArg(size_t index) const {
ASSERT_MSG(!args.at(index).expired(), "This should never happen. All Values should be owned by a MicroBlock.");
return args.at(index).lock();
}
void Inst::AssertValid() {
ASSERT(std::all_of(args.begin(), args.end(), [](const auto& arg) { return !arg.expired(); }));
}
void Inst::ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) {
bool has_use = false;
auto this_ = shared_from_this();
// Note that there may be multiple uses of x.
for (auto& arg : args) {
if (arg.lock() == x) {
arg = y;
has_use = true;
x->RemoveUse(this_);
y->AddUse(this_);
}
}
ASSERT_MSG(has_use, "This Inst doesn't have x. Bug in use management code.");
}
} // namespace IR
} // namespace Dynarmic

168
src/frontend_arm/ir/ir.h Normal file
View file

@ -0,0 +1,168 @@
/* 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 <list>
#include <memory>
#include <vector>
#include <boost/variant.hpp>
#include "common/common_types.h"
#include "frontend_arm/arm_types.h"
#include "frontend_arm/ir/opcodes.h"
namespace Dynarmic {
namespace IR {
// ARM JIT Microinstruction Intermediate Representation
//
// This intermediate representation is an SSA IR. It is designed primarily for analysis,
// though it can be lowered into a reduced form for interpretation. Each IR node (Value)
// is a microinstruction of an idealised ARM CPU. The choice of microinstructions is made
// not based on any existing microarchitecture but on ease of implementation.
//
// A basic block is represented as an IR::Block.
enum class Type {
Void = 1 << 0,
RegRef = 1 << 1,
Opaque = 1 << 2,
U1 = 1 << 3,
U8 = 1 << 4,
U16 = 1 << 5,
U32 = 1 << 6,
U64 = 1 << 7,
};
Type GetTypeOf(Opcode op);
size_t GetNumArgsOf(Opcode op);
Type GetArgTypeOf(Opcode op, size_t arg_index);
const char* GetNameOf(Opcode op);
// Type declarations
/// Base class for microinstructions to derive from.
class Value;
using ValuePtr = std::shared_ptr<Value>;
using ValueWeakPtr = std::weak_ptr<Value>;
class Value : public std::enable_shared_from_this<Value> {
public:
virtual ~Value() = default;
bool HasUses() const { return !uses.empty(); }
bool HasOneUse() const { return uses.size() == 1; }
bool HasManyUses() const { return uses.size() > 1; }
/// Replace all uses of this Value with `replacement`.
void ReplaceUsesWith(ValuePtr replacement);
/// Get the microop this microinstruction represents.
Opcode GetOpcode() const { return op; }
/// Get the type this instruction returns.
Type GetType() const { return GetTypeOf(op); }
/// Get the number of arguments this instruction has.
size_t NumArgs() const { return GetNumArgsOf(op); }
/// Get the number of uses this instruction has.
size_t NumUses() const { return uses.size(); }
std::vector<ValuePtr> GetUses() const;
intptr_t GetTag() const { return tag; }
void SetTag(intptr_t tag_) { tag = tag_; }
protected:
friend class Inst;
Value(Opcode op_) : op(op_) {}
void AddUse(ValuePtr owner);
void RemoveUse(ValuePtr owner);
virtual void ReplaceUseOfXWithY(ValuePtr x, ValuePtr y);
private:
Opcode op;
struct Use {
/// The instruction which is being used.
ValueWeakPtr value;
/// The instruction which is using `value`.
ValueWeakPtr use_owner;
};
std::list<Use> uses;
intptr_t tag;
};
/// Representation of a u8 immediate.
class ImmU8 final : public Value {
public:
explicit ImmU8(u8 value_) : Value(Opcode::ImmU8), value(value_) {}
~ImmU8() override = default;
const u8 value; ///< Literal value to load
};
/// Representation of a u32 immediate.
class ImmU32 final : public Value {
public:
explicit ImmU32(u32 value_) : Value(Opcode::ImmU32), value(value_) {}
~ImmU32() override = default;
const u32 value; ///< Literal value to load
};
/// Representation of a GPR reference.
class ImmRegRef final : public Value {
public:
explicit ImmRegRef(Arm::Reg value_) : Value(Opcode::ImmRegRef), value(value_) {}
~ImmRegRef() override = default;
const Arm::Reg value; ///< Literal value to load
};
/**
* A representation of a microinstruction. A single ARM/Thumb instruction may be
* converted into zero or more microinstructions.
*/
class Inst final : public Value {
public:
explicit Inst(Opcode op);
~Inst() override = default;
/// Set argument number `index` to `value`.
void SetArg(size_t index, ValuePtr value);
/// Get argument number `index`.
ValuePtr GetArg(size_t index) const;
void AssertValid();
protected:
void ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) override;
private:
std::vector<ValueWeakPtr> args;
};
/**
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
* Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to
* the microinstructions. This only matters before chaining is done in order to correctly
* order memory accesses.
*/
class Block final {
public:
explicit Block(const Arm::LocationDescriptor& location) : location(location) {}
Arm::LocationDescriptor location;
std::list<ValuePtr> instructions;
};
} // namespace IR
} // namespace Dynarmic

View file

@ -0,0 +1,24 @@
/* 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 "common/common_types.h"
namespace Dynarmic {
namespace IR {
enum class Opcode {
#define OPCODE(name, type, ...) name,
#include "opcodes.inc"
#undef OPCODE
NUM_OPCODE
};
constexpr size_t OpcodeCount = static_cast<size_t>(Opcode::NUM_OPCODE);
} // namespace Arm
} // namespace Dynarmic

View file

@ -0,0 +1,29 @@
// opcode name, return type, arg1 type, arg2 type, arg3 type, ...
// Immediate values
OPCODE(ImmU1, T::U1, )
OPCODE(ImmU8, T::U8, )
OPCODE(ImmU32, T::U32, )
OPCODE(ImmRegRef, T::RegRef, )
// ARM Context getters/setters
OPCODE(GetRegister, T::U32, T::RegRef )
OPCODE(SetRegister, T::Void, T::RegRef, T::U32 )
OPCODE(GetNFlag, T::U1, )
OPCODE(SetNFlag, T::Void, T::U1 )
OPCODE(GetZFlag, T::U1, )
OPCODE(SetZFlag, T::Void, T::U1 )
OPCODE(GetCFlag, T::U1, )
OPCODE(SetCFlag, T::Void, T::U1 )
OPCODE(GetVFlag, T::U1, )
OPCODE(SetVFlag, T::Void, T::U1 )
// Pseudo-operation, handled specially at final emit
OPCODE(GetCarryFromOp, T::U1, T::U32 )
// Calculations
OPCODE(LeastSignificantByte, T::U8, T::U32 )
OPCODE(MostSignificantBit, T::U1, T::U32 )
OPCODE(IsZero, T::U1, T::U32 )
OPCODE(LogicalShiftLeft, T::U32, T::U32, T::U8, T::U1 )
OPCODE(LogicalShiftRight, T::U32, T::U32, T::U8, T::U1 )

View file

@ -0,0 +1,97 @@
/* 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 "ir_emitter.h"
namespace Dynarmic {
namespace Arm {
void IREmitter::Unimplemented() {
}
IR::ValuePtr IREmitter::Imm8(u8 i) {
auto imm8 = std::make_shared<IR::ImmU8>(i);
AddToBlock(imm8);
return imm8;
}
IR::ValuePtr IREmitter::GetRegister(Dynarmic::Arm::Reg reg) {
return Inst(IR::Opcode::GetRegister, { RegRef(reg) });
}
void IREmitter::SetRegister(const Reg reg, IR::ValuePtr value) {
Inst(IR::Opcode::SetRegister, { RegRef(reg), value });
}
IR::ValuePtr IREmitter::GetCFlag() {
return Inst(IR::Opcode::GetCFlag, {});
}
void IREmitter::SetNFlag(IR::ValuePtr value) {
Inst(IR::Opcode::SetNFlag, {value});
}
void IREmitter::SetZFlag(IR::ValuePtr value) {
Inst(IR::Opcode::SetZFlag, {value});
}
void IREmitter::SetCFlag(IR::ValuePtr value) {
Inst(IR::Opcode::SetCFlag, {value});
}
IR::ValuePtr IREmitter::LeastSignificantByte(IR::ValuePtr value) {
return Inst(IR::Opcode::LeastSignificantByte, {value});
}
IR::ValuePtr IREmitter::MostSignificantBit(IR::ValuePtr value) {
return Inst(IR::Opcode::MostSignificantBit, {value});
}
IR::ValuePtr IREmitter::IsZero(IR::ValuePtr value) {
return Inst(IR::Opcode::IsZero, {value});
}
IREmitter::ResultAndCarry IREmitter::LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
auto result = Inst(IR::Opcode::LogicalShiftLeft, {value_in, shift_amount, carry_in});
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
return {result, carry_out};
}
IREmitter::ResultAndCarry IREmitter::LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
auto result = Inst(IR::Opcode::LogicalShiftRight, {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) {
auto inst = std::make_shared<IR::Inst>(op);
assert(args.size() == inst->NumArgs());
std::for_each(args.begin(), args.end(), [&inst, op, index = size_t(0)](const auto& v) mutable {
assert(IR::GetArgTypeOf(op, index) == v->GetType());
inst->SetArg(index, v);
index++;
});
AddToBlock(inst);
return inst;
}
IR::ValuePtr IREmitter::RegRef(Reg reg) {
auto regref = std::make_shared<IR::ImmRegRef>(reg);
AddToBlock(regref);
return regref;
}
void IREmitter::AddToBlock(IR::ValuePtr value) {
block.instructions.emplace_back(value);
}
} // namespace Arm
} // namespace Dynarmic

View file

@ -0,0 +1,51 @@
/* 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/ir.h"
#include "frontend_arm/ir/opcodes.h"
namespace Dynarmic {
namespace Arm {
class IREmitter {
public:
IR::Block block = Dynarmic::IR::Block({0, false, false});
struct ResultAndCarry {
IR::ValuePtr result;
IR::ValuePtr carry;
};
void Unimplemented();
IR::ValuePtr Imm8(u8 value);
IR::ValuePtr GetRegister(Reg source_reg);
void SetRegister(const Reg dest_reg, IR::ValuePtr value);
IR::ValuePtr GetCFlag();
void SetNFlag(IR::ValuePtr value);
void SetZFlag(IR::ValuePtr value);
void SetCFlag(IR::ValuePtr value);
IR::ValuePtr LeastSignificantByte(IR::ValuePtr value);
IR::ValuePtr MostSignificantBit(IR::ValuePtr value);
IR::ValuePtr IsZero(IR::ValuePtr value);
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);
private:
IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args);
IR::ValuePtr RegRef(Reg reg);
void AddToBlock(IR::ValuePtr value);
};
} // namespace Arm
} // namespace Dynarmic

View file

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

29
src/interface/interface.h Normal file
View file

@ -0,0 +1,29 @@
/* 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 "common/common_types.h"
namespace Dynarmic {
struct UserCallbacks {
u8 (*MemoryRead8)(u32 vaddr);
u16 (*MemoryRead16)(u32 vaddr);
u32 (*MemoryRead32)(u32 vaddr);
u64 (*MemoryRead64)(u32 vaddr);
void (*MemoryWrite8)(u32 vaddr, u8 value);
void (*MemoryWrite16)(u32 vaddr, u16 value);
void (*MemoryWrite32)(u32 vaddr, u32 value);
void (*MemoryWrite64)(u32 vaddr, u64 value);
void (*InterpreterFallback)(u32 pc, void* jit_state);
bool (*SoftwareInterrupt)(u32 swi);
};
} // namespace Dynarmic

13
src/tests/CMakeLists.txt Normal file
View file

@ -0,0 +1,13 @@
set(SRCS
arm/test_arm_disassembler.cpp
main.cpp
arm/test_thumb_instructions.cpp
arm/fuzz_thumb.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)

View file

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

View file

@ -0,0 +1,24 @@
/* 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 <catch.hpp>
#include "frontend_arm/arm_disassembler.h"
TEST_CASE( "Disassemble branch instructions", "[arm][disassembler]" ) {
REQUIRE(Dynarmic::Arm::DisassembleArm(0xEAFFFFFE) == "b +#0");
REQUIRE(Dynarmic::Arm::DisassembleArm(0xEB000008) == "bl +#40");
REQUIRE(Dynarmic::Arm::DisassembleArm(0xFBFFFFFE) == "blx +#2");
REQUIRE(Dynarmic::Arm::DisassembleArm(0xFAFFFFFF) == "blx +#4");
REQUIRE(Dynarmic::Arm::DisassembleArm(0xFBE1E7FE) == "blx -#7888894");
REQUIRE(Dynarmic::Arm::DisassembleArm(0xE12FFF3D) == "blx sp");
REQUIRE(Dynarmic::Arm::DisassembleArm(0x312FFF13) == "bxcc r3");
REQUIRE(Dynarmic::Arm::DisassembleArm(0x012FFF29) == "bxjeq r9");
}
TEST_CASE( "Disassemble data processing instructions", "[arm][disassembler]" ) {
REQUIRE(Dynarmic::Arm::DisassembleArm(0xE2853004) == "add r3, r5, #4");
}

View file

@ -0,0 +1,59 @@
/* 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 <catch.hpp>
#include "backend_x64/emit_x64.h"
#include "common/common_types.h"
#include "frontend_arm/decoder/thumb1.h"
#include "frontend_arm/translate_thumb.h"
struct TinyBlockOfCode : Gen::XCodeBlock {
TinyBlockOfCode() {
AllocCodeSpace(256);
}
};
void RunSingleThumbInstruction(u16 thumb_instruction, Dynarmic::BackendX64::JitState* jit_state_ptr) {
Dynarmic::Arm::TranslatorVisitor visitor;
auto decoder = Dynarmic::Arm::DecodeThumb1<Dynarmic::Arm::TranslatorVisitor>(thumb_instruction);
REQUIRE(!!decoder);
decoder->call(visitor, thumb_instruction);
TinyBlockOfCode block_of_code;
Dynarmic::BackendX64::Routines routines;
Dynarmic::UserCallbacks callbacks;
Dynarmic::BackendX64::EmitX64 emitter(&block_of_code, &routines, callbacks);
Dynarmic::BackendX64::CodePtr code = emitter.Emit(visitor.ir.block);
routines.RunCode(jit_state_ptr, code, 1);
}
TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
Dynarmic::BackendX64::JitState jit_state;
jit_state.Reg[0] = 1;
jit_state.Reg[1] = 2;
jit_state.Cpsr = 0;
RunSingleThumbInstruction(0x0088, &jit_state);
REQUIRE( jit_state.Reg[0] == 8 );
REQUIRE( jit_state.Reg[1] == 2 );
REQUIRE( jit_state.Cpsr == 0 );
}
TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
Dynarmic::BackendX64::JitState jit_state;
jit_state.Reg[0] = 1;
jit_state.Reg[1] = 0xFFFFFFFF;
jit_state.Cpsr = 0;
RunSingleThumbInstruction(0x07C8, &jit_state);
REQUIRE( jit_state.Reg[0] == 0x80000000 );
REQUIRE( jit_state.Reg[1] == 0xffffffff );
REQUIRE( jit_state.Cpsr == 0x20000000 );
}

8
src/tests/main.cpp Normal file
View file

@ -0,0 +1,8 @@
/* 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.
*/
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#include <catch.hpp>