AVR8 opcode decoder

This commit is contained in:
Nav
2023-09-07 23:31:29 +01:00
parent d7891b386b
commit 20cbf14809
12 changed files with 2839 additions and 5 deletions

View File

@@ -9,6 +9,7 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Services/PathService.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Services/ProcessService.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Services/StringService.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Services/Avr8InstructionService.cpp
# Helpers & other
${CMAKE_CURRENT_SOURCE_DIR}/Logger/Logger.cpp

View File

@@ -0,0 +1,14 @@
#pragma once
#include <cstddef>
#include <algorithm>
template <std::size_t length>
struct FixedString
{
char value[length];
constexpr FixedString(const char (&str)[length]) {
std::copy_n(str, length, this->value);
}
};

View File

@@ -0,0 +1,47 @@
#include "Avr8InstructionService.hpp"
namespace Services
{
using Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder;
Decoder::InstructionMapping Avr8InstructionService::fetchInstructions(
const Targets::TargetMemoryAddressRange& addressRange,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
) {
const auto programMemory = targetControllerService.readMemory(
targetDescriptor.programMemoryType,
addressRange.startAddress,
addressRange.endAddress - addressRange.startAddress
);
return Decoder::decode(addressRange.startAddress, programMemory);
}
std::optional<Targets::TargetMemoryAddress> Avr8InstructionService::resolveProgramDestinationAddress(
const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Instruction& instruction,
Targets::TargetMemoryAddress instructionAddress,
const Decoder::InstructionMapping& instructions
) {
assert(instruction.canChangeProgramFlow);
if (instruction.programWordAddress.has_value()) {
return *(instruction.programWordAddress) * 2;
}
if (instruction.programWordAddressOffset.has_value()) {
return static_cast<std::int64_t>(instructionAddress) + (*(instruction.programWordAddressOffset) * 2) + 2;
}
if (instruction.canSkipNextInstruction) {
const auto subsequentInstructionAddress = instructionAddress + instruction.byteSize;
const auto subsequentInstructionIt = instructions.find(subsequentInstructionAddress);
if (subsequentInstructionIt != instructions.end() && subsequentInstructionIt->second.has_value()) {
return subsequentInstructionAddress + subsequentInstructionIt->second->byteSize;
}
}
return std::nullopt;
}
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include "TargetControllerService.hpp"
#include "src/Targets/TargetDescriptor.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp"
#include "src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp"
namespace Services
{
class Avr8InstructionService
{
public:
/**
* Fetches opcodes from the target's program memory and attempts to decodes them.
*
* @param addressRange
* @param targetDescriptor
* @param targetControllerService
*
* @return
* A mapping of std::optional<Instruction>, mapped by their byte address in program memory. The address will
* map to std::nullopt if we failed to decode the opcode at that address.
*/
static Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder::InstructionMapping fetchInstructions(
const Targets::TargetMemoryAddressRange& addressRange,
const Targets::TargetDescriptor& targetDescriptor,
TargetControllerService& targetControllerService
);
/**
* For instructions that can change program flow, this function will attempt to figure out where, in program
* memory, the instruction may jump to.
*
* This isn't possible for instructions that perform an indirect jump, where they jump to some address stored
* in some register.
*
* This function should only be called on instructions that can change program flow
* (Instruction::canChangeProgramFlow == true).
*
* @param instruction
* The subject instruction.
*
* @param instructionAddress
* The byte address of the subject instruction.
*
* @param instructions
* A reference of the InstructionMapping from which the subject instruction was taken, which should, ideally,
* contain the subsequent instruction.
*
* We need access to the subsequent instruction in order to resolve the destination of instructions that skip
* the subsequent instruction. For example, the SBIC instruction will skip the subsequent instruction, if a
* bit in an I/O register is cleared. We can't figure out where instructions like SBIC will jump to, if we
* don't know the size of the subsequent instruction (that will be skipped).
*
* If `instructions` does not contain the subsequent instruction, we'll just return std::nullopt for
* instructions that skip the subsequent instruction.
*
* @return
* The destination byte address the subject instruction may jump to, if we were able to resolve it.
* Otherwise, std::nullopt.
*/
static std::optional<Targets::TargetMemoryAddress> resolveProgramDestinationAddress(
const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Instruction& instruction,
Targets::TargetMemoryAddress instructionAddress,
const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder::InstructionMapping& instructions
);
};
}

View File

@@ -1,10 +1,9 @@
#pragma once
#include <string>
#include <bitset>
#include <limits>
#include <vector>
#include <numeric>
#include <cstdint>
#include <cstddef>
#include <array>
#include <type_traits>
namespace Services
{

View File

@@ -7,4 +7,5 @@ target_sources(
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/Avr8TargetConfig.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/PhysicalInterface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/TargetDescription/TargetDescriptionFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp
)

View File

@@ -0,0 +1,218 @@
#include "Decoder.hpp"
#include <iterator>
#include "Opcodes.hpp"
#include "Exceptions/DecodeFailure.hpp"
namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder
{
Decoder::InstructionMapping Decoder::decode(
Targets::TargetMemoryAddress startByteAddress,
const TargetMemoryBuffer& data,
bool throwOnFailure
) {
auto output = Decoder::InstructionMapping();
static const auto decoders = Decoder::opcodeDecoders();
auto instructionByteAddress = startByteAddress;
auto dataIt = data.begin();
const auto dataEndIt = data.end();
while(dataIt != dataEndIt) {
auto opcodeMatched = false;
for (const auto& decoder : decoders) {
auto instruction = decoder(dataIt, dataEndIt);
if (instruction.has_value()) {
const auto instructionSize = instruction->byteSize;
output.insert(std::pair(instructionByteAddress, std::move(*instruction)));
dataIt += instructionSize;
instructionByteAddress += instructionSize;
opcodeMatched = true;
break;
}
}
if (!opcodeMatched) {
if (throwOnFailure) {
throw Exceptions::DecodeFailure(
instructionByteAddress,
std::distance(dataIt, dataEndIt) >= 2
? static_cast<std::uint32_t>(*(dataIt + 1) << 8) | *dataIt
: *dataIt
);
}
output.insert(std::pair(instructionByteAddress, std::nullopt));
if (std::distance(dataIt, dataEndIt) < 2) {
break;
}
dataIt += 2;
instructionByteAddress += 2;
}
}
return output;
}
Decoder::OpcodeDecoders Decoder::opcodeDecoders() {
/*
* The decoders will be used in the order given here.
*
* I've used the same order that is used in the AVR implementation of GDB.
*/
return Decoder::OpcodeDecoders({
std::bind(&Opcodes::Clc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Clh::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cli::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cln::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cls::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Clt::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Clv::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Clz::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sec::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Seh::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sei::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sen::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ses::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Set::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sev::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sez::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Bclr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Bset::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Icall::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ijmp::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lpm1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lpm2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lpm3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Elpm1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Elpm2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Elpm3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Nop::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ret::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Reti::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sleep::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Break::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Wdr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Spm1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Spm2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Adc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Add::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::And::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cp::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cpc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cpse::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Eor::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Mov::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Mul::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Or::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sub::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Clr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lsl::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Rol::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Tst::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Andi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cbr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ldi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ser::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ori::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cpi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbci::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Subi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbrc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbrs::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Bld::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Bst::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::In::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Out::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Adiw::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbiw::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Cbi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbic::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sbis::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brcc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brcs::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Breq::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brge::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brhc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brhs::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brid::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brie::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brlo::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brlt::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brmi::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brne::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brpl::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brsh::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brtc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brts::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brvc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brvs::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brbc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Brbs::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Rcall::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Rjmp::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Call::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Jmp::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Asr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Com::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Dec::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Inc::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lsr::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Neg::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Pop::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Push::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Ror::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Swap::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Xch::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Las::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lac::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lat::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Movw::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Muls::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Mulsu::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Fmul::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Fmuls::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Fmulsu::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sts1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Sts2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lds1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Lds2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LddY::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LddZ::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdX1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdX2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdX3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdY1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdY2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdY3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdZ1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdZ2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::LdZ3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StdY::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StdZ::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StX1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StX2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StX3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StY1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StY2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StY3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StZ1::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StZ2::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::StZ3::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Eicall::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Eijmp::decode, std::placeholders::_1, std::placeholders::_2),
std::bind(&Opcodes::Des::decode, std::placeholders::_1, std::placeholders::_2),
});
}
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include <cstdint>
#include <map>
#include <array>
#include <functional>
#include <optional>
#include "Instruction.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Services/BitsetService.hpp"
namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder
{
class Decoder
{
public:
using InstructionMapping = std::map<Targets::TargetMemoryAddress, std::optional<Instruction>>;
/**
* Attempts to decode AVR8 opcodes.
*
* @param startByteAddress
* The start (byte) address of the memory given via the `data` param. This is used to calculate the address
* of each instruction.
*
* @param data
* The opcodes to decode. This is expected to be in LSB form, which is how opcodes are stored in AVR program
* memory.
*
* @param throwOnFailure
* If true, this function will throw a DecodeFailure exception, upon the first decode failure.
*
* @return
* A mapping of std::optional<Instruction>, by their byte address. std::nullopt will be used for decode
* failures (assuming `throwOnFailure` is false)
*/
static InstructionMapping decode(
Targets::TargetMemoryAddress startByteAddress,
const Targets::TargetMemoryBuffer& data,
bool throwOnFailure = false
);
private:
using OpcodeDecoderFunction = std::function<
std::optional<Instruction>(
const Targets::TargetMemoryBuffer::const_iterator&,
const Targets::TargetMemoryBuffer::const_iterator&
)
>;
using OpcodeDecoders = std::array<Decoder::OpcodeDecoderFunction, 144>;
static OpcodeDecoders opcodeDecoders();
};
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
#include "src/Exceptions/Exception.hpp"
#include "src/Targets/TargetMemory.hpp"
namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Exceptions
{
class DecodeFailure: public ::Exceptions::Exception
{
public:
Targets::TargetMemoryAddress byteAddress;
std::uint32_t opcode;
explicit DecodeFailure(Targets::TargetMemoryAddress byteAddress, std::uint32_t opcode)
: ::Exceptions::Exception("Failed to decode AVR opcode")
, byteAddress(byteAddress)
, opcode(opcode)
{}
};
}

View File

@@ -0,0 +1,69 @@
#pragma once
#include <string>
#include <cstdint>
#include <optional>
#include "src/Targets/TargetMemory.hpp"
namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder
{
struct Instruction
{
const std::string& name;
std::uint32_t opcode;
std::uint8_t byteSize;
bool canChangeProgramFlow;
std::optional<std::uint32_t> data;
std::optional<std::uint8_t> sourceRegister;
std::optional<std::uint8_t> destinationRegister;
std::optional<Targets::TargetMemoryAddress> programWordAddress;
std::optional<std::int16_t> programWordAddressOffset;
bool canSkipNextInstruction;
std::optional<std::uint8_t> registerBitPosition;
std::optional<std::uint8_t> statusRegisterBitPosition;
std::optional<Targets::TargetMemoryAddress> ioSpaceAddress;
std::optional<Targets::TargetMemoryAddress> dataSpaceAddress;
std::optional<std::uint8_t> displacement;
Instruction(
const std::string& name,
std::uint32_t opcode,
std::uint8_t byteSize,
bool canChangeProgramFlow,
std::optional<std::uint32_t> data = std::nullopt,
std::optional<std::uint8_t> sourceRegister = std::nullopt,
std::optional<std::uint8_t> destinationRegister = std::nullopt,
std::optional<Targets::TargetMemoryAddress> programWordAddress = std::nullopt,
std::optional<std::int16_t> programWordAddressOffset = std::nullopt,
bool canSkipNextInstruction = false,
std::optional<std::uint8_t> registerBitPosition = std::nullopt,
std::optional<std::uint8_t> statusRegisterBitPosition = std::nullopt,
std::optional<Targets::TargetMemoryAddress> ioSpaceAddress = std::nullopt,
std::optional<Targets::TargetMemoryAddress> dataSpaceAddress = std::nullopt,
std::optional<std::uint8_t> displacement = std::nullopt
)
: name(name)
, opcode(opcode)
, byteSize(byteSize)
, canChangeProgramFlow(canChangeProgramFlow)
, data(data)
, sourceRegister(sourceRegister)
, destinationRegister(destinationRegister)
, programWordAddress(programWordAddress)
, programWordAddressOffset(programWordAddressOffset)
, canSkipNextInstruction(canSkipNextInstruction)
, registerBitPosition(registerBitPosition)
, statusRegisterBitPosition(statusRegisterBitPosition)
, ioSpaceAddress(ioSpaceAddress)
, dataSpaceAddress(dataSpaceAddress)
, displacement(displacement)
{}
};
}

View File

@@ -0,0 +1,773 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <type_traits>
#include <iterator>
#include <array>
#include <cassert>
#include <optional>
#include "Instruction.hpp"
#include "src/Helpers/FixedString.hpp"
#include "src/Targets/TargetMemory.hpp"
#include "src/Services/BitsetService.hpp"
namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder
{
struct InstructionParameterBase
{};
template<std::size_t bitFieldRangeCount = 1>
struct InstructionParameter: public InstructionParameterBase
{
/*
* Not all instruction parameters are encoded in a consecutive manner. Some are scattered all over the place.
*
* For example, the CALL instruction accepts a single address parameter, but the 32-bit opcode looks
* like this: 0b1001010kkkkk111kkkkkkkkkkkkkkkkk. Where the 'k's are the address parameter bits, which are not
* encoded consecutively.
*
* There is one variation of the LD instruction that is even worse, where a single parameter is scattered in
* three different locations of the opcode.
*
* Because parameter bits can be scattered, we must accept a set of BitFieldRanges, as opposed to just one.
*/
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount> bitFieldRanges;
const std::uint8_t length;
const std::uint32_t mask;
constexpr InstructionParameter(
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>& bitFieldRanges
)
: bitFieldRanges(bitFieldRanges)
, length(Services::BitsetService::totalBitRangeLength(bitFieldRanges))
, mask(Services::BitsetService::setBitField(std::uint32_t{0}, bitFieldRanges))
{}
constexpr InstructionParameter(const Services::BitsetService::BitFieldRange& bitFieldRange)
: InstructionParameter(
std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>({bitFieldRange})
)
{}
};
template<std::size_t bitFieldRangeCount = 1>
struct RegisterParameter: public InstructionParameter<bitFieldRangeCount>
{
const std::uint8_t offset = 0;
const bool pair = false;
constexpr RegisterParameter(
std::uint8_t offset,
bool pair,
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>& bitFieldRanges
)
: InstructionParameter<bitFieldRangeCount>(bitFieldRanges)
, offset(offset)
, pair(pair)
{}
constexpr RegisterParameter(
std::uint8_t offset,
bool pair,
const Services::BitsetService::BitFieldRange& bitFieldRange
)
: InstructionParameter<bitFieldRangeCount>(bitFieldRange)
, offset(offset)
, pair(pair)
{}
constexpr RegisterParameter(
const std::array<Services::BitsetService::BitFieldRange, bitFieldRangeCount>& bitFieldRanges
)
: InstructionParameter<bitFieldRangeCount>(bitFieldRanges)
{}
constexpr RegisterParameter(const Services::BitsetService::BitFieldRange& bitFieldRange)
: InstructionParameter<bitFieldRangeCount>(bitFieldRange)
{}
};
template<typename>
struct IsRegisterParam: std::false_type {};
template<std::size_t bitFieldRangeCount>
struct IsRegisterParam<RegisterParameter<bitFieldRangeCount>>: std::true_type {};
template<typename ParamType>
requires (std::is_same_v<ParamType, std::nullopt_t> || std::is_base_of_v<InstructionParameterBase, ParamType>)
class OptionalInstructionParameter
{
public:
ParamType value;
constexpr OptionalInstructionParameter(ParamType value)
: value(value)
{};
static constexpr bool hasValue() {
if constexpr (std::is_base_of_v<InstructionParameterBase, ParamType>) {
return true;
} else {
return false;
}
}
};
/**
* Base for InstructionParameterMask(ParamType param, ParamPackType... params).
*/
template<typename ParamType>
static constexpr std::uint32_t InstructionParameterMask(ParamType param) {
if constexpr (!decltype(param)::hasValue()) {
return 0;
} else {
return param.value.mask;
}
}
/**
* Returns a combined (ORed) mask of the given params.
*/
template<typename ParamType, typename... ParamPackType>
static constexpr std::uint32_t InstructionParameterMask(ParamType param, ParamPackType... params) {
if constexpr (!decltype(param)::hasValue()) {
return InstructionParameterMask(params...);
} else {
return param.value.mask | InstructionParameterMask(params...);
}
}
/**
* This template class is used to describe a particular AVR8 instruction opcode.
*
* It provides decoding abilities, as well as compile-time error checks.
*
* @tparam instructionName
* The name of the AVR8 instruction, in the form of a string literal.
*
* @tparam expectedOpcode
* The expected opcode of the instruction, in the form of a binary literal, with all parameter bits cleared (which
* is enforced by template constraints).
*
* For example, consider the AVR8 OR instruction opcode: 0b001010rdddddrrrr, where 'r' and 'd' are source and
* destination register parameters, respectively. The expectedOpcode value would be 0b0010100000000000.
*
* @tparam wordSize
* The word size of the instruction. AVR8 instructions are either 1 or 2 words.
*
* @tparam canChangeProgramFlow
* Whether the instruction **can** change program flow.
*
* If it can change the program counter to anything other than PC + 1, this should be set to true.
*
* @tparam sourceRegisterParameter
* If the instruction encodes a source register parameter (like the OR instruction), this should be provided here.
* Otherwise, std::nullopt.
*
* Register parameters have some additional information we need (e.g. offsets, or if they should be interpreted as
* register pairs). For this reason, register parameters should be provided as instances of RegisterParameter (as
* opposed to instances of InstructionParameter).
*
* @tparam destinationRegisterParameter
* If the instruction encodes a destination register parameter (like the OR instruction), this should be provided
* here. Otherwise, std::nullopt.
*
* This should be an instance of RegisterParameter. See above.
*
* @tparam dataParameter
* If the instruction encodes a constant data parameter (like the ADIW instruction), this should be provided here.
* Otherwise, std::nullopt.
*
* @tparam programAddressParameter
* If the instruction encodes a constant program word address parameter (like the CALL instruction), this should
* be provided here. Otherwise, std::nullopt.
*
* If this parameter is provided, Opcode::canChangeProgramFlow must be set to true. This is enforced by template
* constraints.
*
* @tparam programAddressOffsetParameter
* If the instruction encodes a constant program word address offset parameter (like the RJMP instruction), this
* should be provided here. Otherwise, std::nullopt.
*
* If this parameter is provided, Opcode::canChangeProgramFlow must be set to true. This is enforced by template
* constraints.
*
* @tparam registerBitPositionParameter
* If the instruction encodes a register bit position parameter (like the SBIS instruction), this should be
* provided here. Otherwise, std::nullopt.
*
* @tparam statusRegisterBitPositionParameter
* If the instruction encodes a status register bit position parameter (like the BRBS instruction), this should be
* provided here. Otherwise, std::nullopt.
*
* @tparam ioSpaceAddressParameter
* If the instruction encodes an I/O space parameter (like the IN instruction), this should be provided here.
* Otherwise, std::nullopt.
*
* @tparam dataSpaceAddressParameter
* If the instruction encodes a data space address parameter (like the STS instruction), this should be provided
* here. Otherwise, std::nullopt.
*
* @tparam displacementParameter
* If the instruction encodes a displacement parameter (like the STD instruction), this should be provided here.
* Otherwise, std::nullopt.
*
* @tparam canSkipNextInstruction
* Whether the instruction can skip the following instruction.
*
* If this is set to true, Opcode::canChangeProgramFlow must also be set to true. This is enforced by template
* constraints.
*/
template <
FixedString instructionName,
std::uint32_t expectedOpcode,
int wordSize,
bool canChangeProgramFlow,
OptionalInstructionParameter sourceRegisterParameter = std::nullopt,
OptionalInstructionParameter destinationRegisterParameter = std::nullopt,
OptionalInstructionParameter dataParameter = std::nullopt,
OptionalInstructionParameter programAddressParameter = std::nullopt,
OptionalInstructionParameter programAddressOffsetParameter = std::nullopt,
OptionalInstructionParameter registerBitPositionParameter = std::nullopt,
OptionalInstructionParameter statusRegisterBitPositionParameter = std::nullopt,
OptionalInstructionParameter ioSpaceAddressParameter = std::nullopt,
OptionalInstructionParameter dataSpaceAddressParameter = std::nullopt,
OptionalInstructionParameter displacementParameter = std::nullopt,
bool canSkipNextInstruction = false
>
requires
(wordSize == 1 || wordSize == 2)
&& (wordSize == 2 || expectedOpcode <= 0x0000FFFF)
/*
* All parameter bits in expectedOpcode should be cleared. We enforce this here.
*/
&& (
!decltype(sourceRegisterParameter)::hasValue()
|| ((expectedOpcode & sourceRegisterParameter.value.mask) == 0)
)
&& (
!decltype(destinationRegisterParameter)::hasValue()
|| ((expectedOpcode & destinationRegisterParameter.value.mask) == 0)
)
&& (
!decltype(dataParameter)::hasValue()
|| ((expectedOpcode & dataParameter.value.mask) == 0)
)
&& (
!decltype(programAddressParameter)::hasValue()
|| ((expectedOpcode & programAddressParameter.value.mask) == 0)
)
&& (
!decltype(programAddressOffsetParameter)::hasValue()
|| ((expectedOpcode & programAddressOffsetParameter.value.mask) == 0)
)
&& (
!decltype(registerBitPositionParameter)::hasValue()
|| ((expectedOpcode & registerBitPositionParameter.value.mask) == 0)
)
&& (
!decltype(statusRegisterBitPositionParameter)::hasValue()
|| ((expectedOpcode & statusRegisterBitPositionParameter.value.mask) == 0)
)
&& (
!decltype(ioSpaceAddressParameter)::hasValue()
|| ((expectedOpcode & ioSpaceAddressParameter.value.mask) == 0)
)
&& (
!decltype(dataSpaceAddressParameter)::hasValue()
|| ((expectedOpcode & dataSpaceAddressParameter.value.mask) == 0)
)
&& (
!decltype(displacementParameter)::hasValue()
|| ((expectedOpcode & displacementParameter.value.mask) == 0)
)
/*
* We need additional info for register parameters, which should be provided via the RegisterParameter type.
*/
&& (
!decltype(sourceRegisterParameter)::hasValue()
|| IsRegisterParam<decltype(sourceRegisterParameter.value)>::value
)
&& (
!decltype(destinationRegisterParameter)::hasValue()
|| IsRegisterParam<decltype(destinationRegisterParameter.value)>::value
)
/*
* Parameters should not collide (share opcode bits) with others. We detect any collisions here.
*/
&& (
!decltype(sourceRegisterParameter)::hasValue()
|| (InstructionParameterMask(
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & sourceRegisterParameter.value.mask) == 0
)
&& (
!decltype(destinationRegisterParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & destinationRegisterParameter.value.mask) == 0
)
&& (
!decltype(dataParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & dataParameter.value.mask) == 0
)
&& (
!decltype(programAddressParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & programAddressParameter.value.mask) == 0
)
&& (
!decltype(programAddressOffsetParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & programAddressOffsetParameter.value.mask) == 0
)
&& (
!decltype(registerBitPositionParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & registerBitPositionParameter.value.mask) == 0
)
&& (
!decltype(statusRegisterBitPositionParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter
) & statusRegisterBitPositionParameter.value.mask) == 0
)
&& (
!decltype(ioSpaceAddressParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
dataSpaceAddressParameter,
displacementParameter
) & ioSpaceAddressParameter.value.mask) == 0
)
&& (
!decltype(dataSpaceAddressParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
displacementParameter
) & dataSpaceAddressParameter.value.mask) == 0
)
&& (
!decltype(displacementParameter)::hasValue()
|| (InstructionParameterMask(
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter
) & displacementParameter.value.mask) == 0
)
/*
* Instructions that encode a program address/offset, or skip the next instruction, can change program flow
* and should be marked as such. This is enforced here.
*/
&& (!decltype(programAddressParameter)::hasValue() || canChangeProgramFlow)
&& (!decltype(programAddressOffsetParameter)::hasValue() || canChangeProgramFlow)
&& (!canSkipNextInstruction || canChangeProgramFlow)
class Opcode
{
using SelfType = Opcode<
instructionName,
expectedOpcode,
wordSize,
canChangeProgramFlow,
sourceRegisterParameter,
destinationRegisterParameter,
dataParameter,
programAddressParameter,
programAddressOffsetParameter,
registerBitPositionParameter,
statusRegisterBitPositionParameter,
ioSpaceAddressParameter,
dataSpaceAddressParameter,
displacementParameter,
canSkipNextInstruction
>;
using OpcodeDataType = std::conditional_t<(wordSize > 1), std::uint32_t, std::uint16_t>;
/**
* We hold a single instance of the instruction name here, as a const static member, and then pass it by
* reference to any new instances of the OpcodeDecoder::Instruction struct (which holds a reference).
*/
static const inline std::string name = instructionName.value;
public:
/**
* Attempts to match and decode the first opcode that resides between the two iterators.
*
* @param dataBegin
* An iterator addressing some byte in the target's program memory. Keep in mind that AVR opcodes are stored
* in LSB form, so this iterator should address the least significant byte of the opcode.
*
* @param dataEnd
* An iterator addressing the byte at which decoding should stop, or just the end of the program memory.
*
* @return
* An instance of the OpcodeDecoder::Instruction struct, if the opcode was successfully matched and decoded.
* Otherwise, std::nullopt.
*/
static std::optional<Instruction> decode(
const Targets::TargetMemoryBuffer::const_iterator& dataBegin,
const Targets::TargetMemoryBuffer::const_iterator& dataEnd
) {
using Services::BitsetService;
constexpr auto byteSize = wordSize * 2;
if (std::distance(dataBegin, dataEnd) < byteSize) {
// There isn't enough data to hold this instruction.
return std::nullopt;
}
auto opcode = static_cast<OpcodeDataType>(*(dataBegin + 1) << 8 | *dataBegin);
if constexpr (wordSize == 2) {
opcode = (opcode << 16) | static_cast<OpcodeDataType>(*(dataBegin + 3) << 8 | *(dataBegin + 2));
}
if ((opcode & SelfType::opcodeMask()) != static_cast<OpcodeDataType>(expectedOpcode)) {
// Opcode mismatch
return std::nullopt;
}
auto output = Instruction(
SelfType::name,
opcode,
byteSize,
canChangeProgramFlow
);
if constexpr (decltype(sourceRegisterParameter)::hasValue()) {
constexpr auto param = sourceRegisterParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::sourceRegister)::value_type) * 8,
"Invalid instruction sourceRegisterParameter - param length exceeds Instruction::sourceRegister size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction sourceRegisterParameter - parameter length exceeds available bits"
);
auto value = BitsetService::extractBitField(opcode, param.bitFieldRanges);
if constexpr (param.pair) {
value = value * 2;
}
output.sourceRegister = static_cast<decltype(Instruction::sourceRegister)::value_type>(
value + param.offset
);
}
if constexpr (decltype(destinationRegisterParameter)::hasValue()) {
constexpr auto param = destinationRegisterParameter.value;
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction destinationRegisterParameter - parameter length exceeds available bits"
);
auto value = BitsetService::extractBitField(opcode, param.bitFieldRanges);
if constexpr (param.pair) {
value = value * 2;
}
output.destinationRegister = static_cast<decltype(Instruction::destinationRegister)::value_type>(
value + param.offset
);
}
if constexpr (decltype(dataParameter)::hasValue()) {
constexpr auto param = dataParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::data)::value_type) * 8,
"Invalid instruction dataParameter - param length exceeds Instruction::data size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction dataParameter - parameter length exceeds available bits"
);
output.data = static_cast<decltype(Instruction::data)::value_type>(
BitsetService::extractBitField(opcode, param.bitFieldRanges)
);
}
if constexpr (decltype(programAddressParameter)::hasValue()) {
constexpr auto param = programAddressParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::programWordAddress)::value_type) * 8,
"Invalid instruction programAddressParameter - param length exceeds "
"Instruction::destinationWordAddress size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction programAddressParameter - parameter length exceeds available bits"
);
output.programWordAddress = BitsetService::extractBitField(opcode, param.bitFieldRanges);
}
if constexpr (decltype(programAddressOffsetParameter)::hasValue()) {
constexpr auto param = programAddressOffsetParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::programWordAddressOffset)::value_type) * 8,
"Invalid instruction programAddressOffsetParameter - param length exceeds "
"Instruction::destinationWordAddressOffset size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction programAddressOffsetParameter - param length exceeds available bits"
);
auto addressOffset = static_cast<std::int16_t>(
BitsetService::extractBitField(opcode, param.bitFieldRanges)
);
const auto signBitMask = static_cast<std::int16_t>(0x01 << (param.length - 1));
if (addressOffset & signBitMask) {
// Sign extending required
addressOffset |= static_cast<std::int16_t>(-1LL) << param.length;
}
output.programWordAddressOffset = addressOffset;
}
if constexpr (decltype(registerBitPositionParameter)::hasValue()) {
constexpr auto param = registerBitPositionParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::registerBitPosition)::value_type) * 8,
"Invalid instruction registerBitPositionParameter - param length exceeds "
"Instruction::registerBitPosition size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction registerBitPositionParameter - parameter length exceeds available bits"
);
output.registerBitPosition = static_cast<decltype(Instruction::registerBitPosition)::value_type>(
BitsetService::extractBitField(opcode, param.bitFieldRanges)
);
}
if constexpr (decltype(statusRegisterBitPositionParameter)::hasValue()) {
constexpr auto param = statusRegisterBitPositionParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::statusRegisterBitPosition)::value_type) * 8,
"Invalid instruction statusRegisterBitPositionParameter - param length exceeds "
"Instruction::statusRegisterBitPosition size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction statusRegisterBitPositionParameter - parameter length exceeds available bits"
);
output.statusRegisterBitPosition = static_cast<
decltype(Instruction::statusRegisterBitPosition)::value_type
>(
BitsetService::extractBitField(opcode, param.bitFieldRanges)
);
}
if constexpr (decltype(ioSpaceAddressParameter)::hasValue()) {
constexpr auto param = ioSpaceAddressParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::ioSpaceAddress)::value_type) * 8,
"Invalid instruction ioSpaceAddressParameter - param length exceeds Instruction::ioSpaceAddress "
"size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction ioSpaceAddressParameter - parameter length exceeds available bits"
);
output.ioSpaceAddress = BitsetService::extractBitField(opcode, param.bitFieldRanges);
}
if constexpr (decltype(dataSpaceAddressParameter)::hasValue()) {
constexpr auto param = dataSpaceAddressParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::dataSpaceAddress)::value_type) * 8,
"Invalid instruction dataSpaceAddressParameter - param length exceeds Instruction::dataSpaceAddress "
"size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction dataSpaceAddressParameter - parameter length exceeds available bits"
);
output.dataSpaceAddress = BitsetService::extractBitField(opcode, param.bitFieldRanges);
}
if constexpr (decltype(displacementParameter)::hasValue()) {
constexpr auto param = displacementParameter.value;
static_assert(
param.length <= sizeof(decltype(Instruction::displacement)::value_type) * 8,
"Invalid instruction displacementParameter - param length exceeds Instruction::displacement size"
);
static_assert(
((param.bitFieldRanges.begin())->startPosition + 1) >= param.length,
"Invalid instruction displacementParameter - parameter length exceeds available bits"
);
output.displacement = BitsetService::extractBitField(opcode, param.bitFieldRanges);
}
output.canSkipNextInstruction = canSkipNextInstruction;
return output;
}
private:
static OpcodeDataType opcodeMask() {
using Services::BitsetService;
auto opcodeMask = static_cast<OpcodeDataType>(-1LL);
if constexpr (decltype(sourceRegisterParameter)::hasValue()) {
opcodeMask &= ~(sourceRegisterParameter.value.mask);
}
if constexpr (decltype(destinationRegisterParameter)::hasValue()) {
opcodeMask &= ~(destinationRegisterParameter.value.mask);
}
if constexpr (decltype(dataParameter)::hasValue()) {
opcodeMask &= ~(dataParameter.value.mask);
}
if constexpr (decltype(programAddressParameter)::hasValue()) {
opcodeMask &= ~(programAddressParameter.value.mask);
}
if constexpr (decltype(programAddressOffsetParameter)::hasValue()) {
opcodeMask &= ~(programAddressOffsetParameter.value.mask);
}
if constexpr (decltype(registerBitPositionParameter)::hasValue()) {
opcodeMask &= ~(registerBitPositionParameter.value.mask);
}
if constexpr (decltype(statusRegisterBitPositionParameter)::hasValue()) {
opcodeMask &= ~(statusRegisterBitPositionParameter.value.mask);
}
if constexpr (decltype(ioSpaceAddressParameter)::hasValue()) {
opcodeMask &= ~(ioSpaceAddressParameter.value.mask);
}
if constexpr (decltype(dataSpaceAddressParameter)::hasValue()) {
opcodeMask &= ~(dataSpaceAddressParameter.value.mask);
}
if constexpr (decltype(displacementParameter)::hasValue()) {
opcodeMask &= ~(displacementParameter.value.mask);
}
return opcodeMask;
}
};
}

File diff suppressed because it is too large Load Diff