2016-07-01 14:01:06 +01:00
|
|
|
/* 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 <tuple>
|
|
|
|
|
2016-08-06 17:17:58 +01:00
|
|
|
#include "common/assert.h"
|
2016-09-17 09:48:18 +01:00
|
|
|
#include "common/bit_util.h"
|
2016-07-01 14:01:06 +01:00
|
|
|
#include "common/mp.h"
|
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace Arm {
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper functions for the decoders.
|
2016-09-17 09:48:18 +01:00
|
|
|
*
|
|
|
|
* @tparam MatcherT The type of the Matcher to use.
|
2016-07-01 14:01:06 +01:00
|
|
|
*/
|
2016-09-17 09:48:18 +01:00
|
|
|
template<class MatcherT>
|
2016-07-01 14:01:06 +01:00
|
|
|
struct detail {
|
|
|
|
private:
|
2016-12-23 11:10:02 +00:00
|
|
|
using opcode_type = typename MatcherT::opcode_type;
|
|
|
|
using visitor_type = typename MatcherT::visitor_type;
|
2016-09-17 09:48:18 +01:00
|
|
|
|
|
|
|
static constexpr size_t opcode_bitsize = Common::BitSize<opcode_type>();
|
|
|
|
|
2016-07-01 14:01:06 +01:00
|
|
|
/**
|
|
|
|
* 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) {
|
2016-09-17 09:48:18 +01:00
|
|
|
opcode_type mask = 0, expect = 0;
|
|
|
|
for (size_t i = 0; i < opcode_bitsize; i++) {
|
|
|
|
const size_t bit_position = opcode_bitsize - i - 1;
|
2016-07-01 14:01:06 +01:00
|
|
|
switch (bitstring[i]) {
|
|
|
|
case '0':
|
|
|
|
mask |= 1 << bit_position;
|
|
|
|
break;
|
|
|
|
case '1':
|
|
|
|
expect |= 1 << bit_position;
|
|
|
|
mask |= 1 << bit_position;
|
|
|
|
break;
|
2016-07-04 14:37:50 +01:00
|
|
|
default:
|
|
|
|
// Ignore
|
|
|
|
break;
|
2016-07-01 14:01:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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) {
|
2016-09-17 09:48:18 +01:00
|
|
|
std::array<opcode_type, N> masks = {};
|
2016-07-01 14:01:06 +01:00
|
|
|
std::array<size_t, N> shifts = {};
|
|
|
|
size_t arg_index = 0;
|
|
|
|
char ch = 0;
|
|
|
|
|
2016-09-17 09:48:18 +01:00
|
|
|
for (size_t i = 0; i < opcode_bitsize; i++) {
|
|
|
|
const size_t bit_position = opcode_bitsize - i - 1;
|
2016-07-01 14:01:06 +01:00
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2016-08-06 17:17:58 +01:00
|
|
|
ASSERT(arg_index < N);
|
2016-07-01 14:01:06 +01:00
|
|
|
masks[arg_index] |= 1 << bit_position;
|
|
|
|
shifts[arg_index] = bit_position;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-06 17:17:58 +01:00
|
|
|
ASSERT(std::all_of(masks.begin(), masks.end(), [](auto m){ return m != 0; }));
|
2016-07-01 14:01:06 +01:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2016-08-06 17:17:58 +01:00
|
|
|
template<typename FnT>
|
2016-07-01 14:01:06 +01:00
|
|
|
struct VisitorCaller;
|
|
|
|
|
2016-07-18 10:28:17 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(push)
|
2016-08-12 18:17:31 +01:00
|
|
|
#pragma warning(disable:4800) // forcing value to bool 'true' or 'false' (performance warning)
|
2016-07-18 10:28:17 +01:00
|
|
|
#endif
|
2016-08-06 17:17:58 +01:00
|
|
|
template<typename Visitor, typename ...Args, typename CallRetT>
|
|
|
|
struct VisitorCaller<CallRetT(Visitor::*)(Args...)> {
|
2016-07-01 14:01:06 +01:00
|
|
|
template<size_t ...iota>
|
|
|
|
static auto Make(std::integer_sequence<size_t, iota...>,
|
2016-08-06 17:17:58 +01:00
|
|
|
CallRetT (Visitor::* const fn)(Args...),
|
2016-09-17 09:48:18 +01:00
|
|
|
const std::array<opcode_type, sizeof...(iota)> arg_masks,
|
2016-07-01 14:01:06 +01:00
|
|
|
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
2016-12-23 11:10:02 +00:00
|
|
|
static_assert(std::is_same<visitor_type, Visitor>::value, "Member function is not from Matcher's Visitor");
|
2016-09-17 09:48:18 +01:00
|
|
|
return [fn, arg_masks, arg_shifts](Visitor& v, opcode_type instruction) {
|
2016-07-01 14:01:06 +01:00
|
|
|
(void)instruction;
|
|
|
|
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
2016-12-23 11:33:40 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename Visitor, typename ...Args, typename CallRetT>
|
|
|
|
struct VisitorCaller<CallRetT(Visitor::*)(Args...) const> {
|
|
|
|
template<size_t ...iota>
|
|
|
|
static auto Make(std::integer_sequence<size_t, iota...>,
|
|
|
|
CallRetT (Visitor::* const fn)(Args...) const,
|
|
|
|
const std::array<opcode_type, sizeof...(iota)> arg_masks,
|
|
|
|
const std::array<size_t, sizeof...(iota)> arg_shifts) {
|
|
|
|
static_assert(std::is_same<visitor_type, const Visitor>::value, "Member function is not from Matcher's Visitor");
|
|
|
|
return [fn, arg_masks, arg_shifts](const Visitor& v, opcode_type instruction) {
|
|
|
|
(void)instruction;
|
|
|
|
return (v.*fn)(static_cast<Args>((instruction & arg_masks[iota]) >> arg_shifts[iota])...);
|
2016-07-01 14:01:06 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
2016-07-18 10:28:17 +01:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
2016-07-01 14:01:06 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* Creates a matcher that can match and parse instructions based on bitstring.
|
|
|
|
* See also: GetMaskAndExpect and GetArgInfo for format of bitstring.
|
|
|
|
*/
|
2016-08-06 17:17:58 +01:00
|
|
|
template<typename FnT>
|
|
|
|
static auto GetMatcher(FnT fn, const char* const name, const char* const bitstring) {
|
2016-08-16 17:40:04 +01:00
|
|
|
constexpr size_t args_count = mp::FunctionInfo<FnT>::args_count;
|
2016-07-01 14:01:06 +01:00
|
|
|
using Iota = std::make_index_sequence<args_count>;
|
|
|
|
|
|
|
|
const auto mask_and_expect = GetMaskAndExpect(bitstring);
|
|
|
|
const auto arg_info = GetArgInfo<args_count>(bitstring);
|
2016-08-06 17:17:58 +01:00
|
|
|
const auto proxy_fn = VisitorCaller<FnT>::Make(Iota(), fn, std::get<0>(arg_info), std::get<1>(arg_info));
|
2016-07-01 14:01:06 +01:00
|
|
|
|
2016-09-17 09:48:18 +01:00
|
|
|
return MatcherT(name, std::get<0>(mask_and_expect), std::get<1>(mask_and_expect), proxy_fn);
|
2016-07-01 14:01:06 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
} // namespace Arm
|
|
|
|
} // namespace Dynarmic
|