diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04a808be..5a0394ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ set(HEADERS frontend/arm/types.h frontend/decoder/arm.h frontend/decoder/decoder_detail.h + frontend/decoder/matcher.h frontend/decoder/thumb16.h frontend/decoder/thumb32.h frontend/decoder/vfp2.h diff --git a/src/frontend/decoder/arm.h b/src/frontend/decoder/arm.h index a0a171c4..a46d65be 100644 --- a/src/frontend/decoder/arm.h +++ b/src/frontend/decoder/arm.h @@ -16,60 +16,19 @@ #include "common/common_types.h" #include "frontend/decoder/decoder_detail.h" +#include "frontend/decoder/matcher.h" namespace Dynarmic { namespace Arm { template -struct ArmMatcher { - using CallRetT = mp::return_type_t; - - ArmMatcher(const char* const name, u32 mask, u32 expect, std::function 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); - } - - u32 GetMask() const { - return mask; - } - - u32 GetExpect() const { - return expect; - } - -private: - const char* name; - u32 mask, expect; - std::function fn; -}; +using ArmMatcher = Matcher; template std::vector> GetArmDecodeTable() { std::vector> table = { -#define INST(fn, name, bitstring) detail::detail::GetMatcher(fn, name, bitstring) +#define INST(fn, name, bitstring) detail::detail>::GetMatcher(fn, name, bitstring) // Branch instructions INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), // v5 diff --git a/src/frontend/decoder/decoder_detail.h b/src/frontend/decoder/decoder_detail.h index dfd22e32..fafe8db9 100644 --- a/src/frontend/decoder/decoder_detail.h +++ b/src/frontend/decoder/decoder_detail.h @@ -11,6 +11,7 @@ #include #include "common/assert.h" +#include "common/bit_util.h" #include "common/mp.h" namespace Dynarmic { @@ -19,22 +20,25 @@ namespace detail { /** * Helper functions for the decoders. - * @tparam MatcherT The type of the matcher. (ARM: ArmMatcher, Thumb16: Thumb16Matcher) - * @tparam InstructionT The type that represents an instruction. (ARM: u32, Thumb16: u16) - * @tparam instruction_bit_size Bit-size for an instruction. (ARM: 32, Thumb16: 16) + * + * @tparam MatcherT The type of the Matcher to use. */ -template class MatcherT, typename InstructionT, size_t instruction_bit_size> +template struct detail { private: + using opcode_type = typename MatcherT::opcode_type; + + static constexpr size_t opcode_bitsize = Common::BitSize(); + /** * 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; + opcode_type mask = 0, expect = 0; + for (size_t i = 0; i < opcode_bitsize; i++) { + const size_t bit_position = opcode_bitsize - i - 1; switch (bitstring[i]) { case '0': mask |= 1 << bit_position; @@ -58,13 +62,13 @@ private: */ template static auto GetArgInfo(const char* const bitstring) { - std::array masks = {}; + std::array masks = {}; std::array 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; + for (size_t i = 0; i < opcode_bitsize; i++) { + const size_t bit_position = opcode_bitsize - i - 1; if (bitstring[i] == '0' || bitstring[i] == '1' || bitstring[i] == '-') { if (ch != 0) { @@ -107,9 +111,9 @@ private: template static auto Make(std::integer_sequence, CallRetT (Visitor::* const fn)(Args...), - const std::array arg_masks, + const std::array arg_masks, const std::array arg_shifts) { - return [fn, arg_masks, arg_shifts](Visitor& v, InstructionT instruction) { + return [fn, arg_masks, arg_shifts](Visitor& v, opcode_type instruction) { (void)instruction; return (v.*fn)(static_cast((instruction & arg_masks[iota]) >> arg_shifts[iota])...); }; @@ -126,7 +130,6 @@ public: */ template static auto GetMatcher(FnT fn, const char* const name, const char* const bitstring) { - using Visitor = typename mp::FunctionInfo::class_type; constexpr size_t args_count = mp::FunctionInfo::args_count; using Iota = std::make_index_sequence; @@ -134,7 +137,7 @@ public: const auto arg_info = GetArgInfo(bitstring); const auto proxy_fn = VisitorCaller::Make(Iota(), fn, std::get<0>(arg_info), std::get<1>(arg_info)); - return MatcherT(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn); + return MatcherT(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn); } }; diff --git a/src/frontend/decoder/matcher.h b/src/frontend/decoder/matcher.h new file mode 100644 index 00000000..bf92bea0 --- /dev/null +++ b/src/frontend/decoder/matcher.h @@ -0,0 +1,78 @@ +/* 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 + +#include "common/assert.h" + +namespace Dynarmic { +namespace Arm { + +/** + * Generic instruction handling construct. + * + * @tparam Visitor An arbitrary visitor type that will be passed through + * to the function being handled. This type must be the + * type of the first parameter in a handler function. + * + * @tparam OpcodeType Type representing an opcode. This must be the + * type of the second parameter in a handler function. + */ +template +class Matcher { +public: + using opcode_type = OpcodeType; + using handler_return_type = typename Visitor::instruction_return_type; + using handler_function = std::function; + + Matcher(const char* const name, opcode_type mask, opcode_type expected, handler_function func) + : name{name}, mask{mask}, expected{expected}, fn{std::move(func)} {} + + /// Gets the name of this type of instruction. + const char* GetName() const { + return name; + } + + /// Gets the mask for this instruction. + opcode_type GetMask() const { + return mask; + } + + /// Gets the expected value after masking for this instruction. + opcode_type GetExpected() const { + return expected; + } + + /** + * Tests to see if the given instruction is the instruction this matcher represents. + * @param instruction The instruction to test + * @returns true if the given instruction matches. + */ + bool Matches(opcode_type instruction) const { + return (instruction & mask) == expected; + } + + /** + * Calls the corresponding instruction handler on visitor for this type of instruction. + * @param v The visitor to use + * @param instruction The instruction to decode. + */ + handler_return_type call(Visitor& v, opcode_type instruction) const { + ASSERT(Matches(instruction)); + return fn(v, instruction); + } + +private: + const char* name; + opcode_type mask; + opcode_type expected; + handler_function fn; +}; + +} // namespace Arm +} // namespace Dynarmic diff --git a/src/frontend/decoder/thumb16.h b/src/frontend/decoder/thumb16.h index 18863ed7..24335da9 100644 --- a/src/frontend/decoder/thumb16.h +++ b/src/frontend/decoder/thumb16.h @@ -6,60 +6,25 @@ #pragma once -#include -#include #include #include #include "common/common_types.h" #include "frontend/decoder/decoder_detail.h" +#include "frontend/decoder/matcher.h" namespace Dynarmic { namespace Arm { template -struct Thumb16Matcher { - using CallRetT = mp::return_type_t; - - Thumb16Matcher(const char* const name, u16 mask, u16 expect, std::function 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 fn; -}; +using Thumb16Matcher = Matcher; template boost::optional&> DecodeThumb16(u16 instruction) { const static std::vector> table = { -#define INST(fn, name, bitstring) detail::detail::GetMatcher(fn, name, bitstring) +#define INST(fn, name, bitstring) detail::detail>::GetMatcher(fn, name, bitstring) // Shift (immediate), add, subtract, move and compare instructions INST(&V::thumb16_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd"), diff --git a/src/frontend/decoder/thumb32.h b/src/frontend/decoder/thumb32.h index cb2552d8..843988c8 100644 --- a/src/frontend/decoder/thumb32.h +++ b/src/frontend/decoder/thumb32.h @@ -6,60 +6,25 @@ #pragma once -#include -#include #include #include #include "common/common_types.h" #include "frontend/decoder/decoder_detail.h" +#include "frontend/decoder/matcher.h" namespace Dynarmic { namespace Arm { template -struct Thumb32Matcher { - using CallRetT = mp::return_type_t; - - Thumb32Matcher(const char* const name, u32 mask, u32 expect, std::function 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 fn; -}; +using Thumb32Matcher = Matcher; template boost::optional&> DecodeThumb32(u32 instruction) { const static std::vector> table = { -#define INST(fn, name, bitstring) detail::detail::GetMatcher(fn, name, bitstring) +#define INST(fn, name, bitstring) detail::detail>::GetMatcher(fn, name, bitstring) // Branch instructions INST(&V::thumb32_BL_imm, "BL (imm)", "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T diff --git a/src/frontend/decoder/vfp2.h b/src/frontend/decoder/vfp2.h index de48d6bb..b44f285e 100644 --- a/src/frontend/decoder/vfp2.h +++ b/src/frontend/decoder/vfp2.h @@ -6,60 +6,25 @@ #pragma once -#include -#include #include #include #include "common/common_types.h" #include "frontend/decoder/decoder_detail.h" +#include "frontend/decoder/matcher.h" namespace Dynarmic { namespace Arm { template -struct VFP2Matcher { - using CallRetT = mp::return_type_t; - - VFP2Matcher(const char* const name, u32 mask, u32 expect, std::function 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 fn; -}; +using VFP2Matcher = Matcher; template boost::optional&> DecodeVFP2(u32 instruction) { const static std::vector> table = { -#define INST(fn, name, bitstring) detail::detail::GetMatcher(fn, name, bitstring) +#define INST(fn, name, bitstring) detail::detail>::GetMatcher(fn, name, bitstring) // cccc1110________----101-__-0---- diff --git a/src/frontend/disassembler/disassembler_arm.cpp b/src/frontend/disassembler/disassembler_arm.cpp index 260d7f79..b7261b03 100644 --- a/src/frontend/disassembler/disassembler_arm.cpp +++ b/src/frontend/disassembler/disassembler_arm.cpp @@ -21,6 +21,8 @@ namespace Arm { class DisassemblerVisitor { public: + using instruction_return_type = std::string; + u32 rotr(u32 x, int shift) { shift &= 31; if (!shift) return x; diff --git a/src/frontend/disassembler/disassembler_thumb.cpp b/src/frontend/disassembler/disassembler_thumb.cpp index 3fd237c9..c34afef2 100644 --- a/src/frontend/disassembler/disassembler_thumb.cpp +++ b/src/frontend/disassembler/disassembler_thumb.cpp @@ -20,6 +20,8 @@ namespace Arm { class DisassemblerVisitor { public: + using instruction_return_type = std::string; + std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) { return fmt::format("lsls {}, {}, #{}", d, m, imm5); } diff --git a/src/frontend/translate/translate_arm/translate_arm.h b/src/frontend/translate/translate_arm/translate_arm.h index da5b97a3..9981e8de 100644 --- a/src/frontend/translate/translate_arm/translate_arm.h +++ b/src/frontend/translate/translate_arm/translate_arm.h @@ -24,6 +24,8 @@ enum class ConditionalState { }; struct ArmTranslatorVisitor final { + using instruction_return_type = bool; + explicit ArmTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) { ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode"); } diff --git a/src/frontend/translate/translate_thumb.cpp b/src/frontend/translate/translate_thumb.cpp index ebe9cd73..908392cc 100644 --- a/src/frontend/translate/translate_thumb.cpp +++ b/src/frontend/translate/translate_thumb.cpp @@ -21,6 +21,8 @@ namespace Arm { namespace { struct ThumbTranslatorVisitor final { + using instruction_return_type = bool; + explicit ThumbTranslatorVisitor(IR::LocationDescriptor descriptor) : ir(descriptor) { ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode"); }