From 20cbf14809c1a46afa1dcc49a6c15465b715c152 Mon Sep 17 00:00:00 2001 From: Nav Date: Thu, 7 Sep 2023 23:31:29 +0100 Subject: [PATCH] AVR8 opcode decoder --- src/CMakeLists.txt | 1 + src/Helpers/FixedString.hpp | 14 + src/Services/Avr8InstructionService.cpp | 47 + src/Services/Avr8InstructionService.hpp | 70 + src/Services/BitsetService.hpp | 9 +- src/Targets/CMakeLists.txt | 1 + .../AVR/AVR8/OpcodeDecoder/Decoder.cpp | 218 +++ .../AVR/AVR8/OpcodeDecoder/Decoder.hpp | 56 + .../Exceptions/DecodeFailure.hpp | 23 + .../AVR/AVR8/OpcodeDecoder/Instruction.hpp | 69 + .../AVR/AVR8/OpcodeDecoder/Opcode.hpp | 773 ++++++++ .../AVR/AVR8/OpcodeDecoder/Opcodes.hpp | 1563 +++++++++++++++++ 12 files changed, 2839 insertions(+), 5 deletions(-) create mode 100644 src/Helpers/FixedString.hpp create mode 100644 src/Services/Avr8InstructionService.cpp create mode 100644 src/Services/Avr8InstructionService.hpp create mode 100644 src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp create mode 100644 src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp create mode 100644 src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp create mode 100644 src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp create mode 100644 src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp create mode 100644 src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b07e14b8..e04c5310 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/Helpers/FixedString.hpp b/src/Helpers/FixedString.hpp new file mode 100644 index 00000000..cf36d79b --- /dev/null +++ b/src/Helpers/FixedString.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +template +struct FixedString +{ + char value[length]; + + constexpr FixedString(const char (&str)[length]) { + std::copy_n(str, length, this->value); + } +}; diff --git a/src/Services/Avr8InstructionService.cpp b/src/Services/Avr8InstructionService.cpp new file mode 100644 index 00000000..ac3bb3b1 --- /dev/null +++ b/src/Services/Avr8InstructionService.cpp @@ -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 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(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; + } +} diff --git a/src/Services/Avr8InstructionService.hpp b/src/Services/Avr8InstructionService.hpp new file mode 100644 index 00000000..64ff13da --- /dev/null +++ b/src/Services/Avr8InstructionService.hpp @@ -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, 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 resolveProgramDestinationAddress( + const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Instruction& instruction, + Targets::TargetMemoryAddress instructionAddress, + const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder::InstructionMapping& instructions + ); + }; +} diff --git a/src/Services/BitsetService.hpp b/src/Services/BitsetService.hpp index cae9d36e..0a1890ca 100644 --- a/src/Services/BitsetService.hpp +++ b/src/Services/BitsetService.hpp @@ -1,10 +1,9 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include +#include namespace Services { diff --git a/src/Targets/CMakeLists.txt b/src/Targets/CMakeLists.txt index 2fb2b4d2..6e8f8044 100755 --- a/src/Targets/CMakeLists.txt +++ b/src/Targets/CMakeLists.txt @@ -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 ) diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp new file mode 100644 index 00000000..98a4eaa1 --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp @@ -0,0 +1,218 @@ +#include "Decoder.hpp" + +#include + +#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(*(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), + }); + } +} diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp new file mode 100644 index 00000000..bd2c9a65 --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +#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>; + + /** + * 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, 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( + const Targets::TargetMemoryBuffer::const_iterator&, + const Targets::TargetMemoryBuffer::const_iterator& + ) + >; + using OpcodeDecoders = std::array; + + static OpcodeDecoders opcodeDecoders(); + }; +} diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp new file mode 100644 index 00000000..f6d8d572 --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#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) + {} + }; +} diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp new file mode 100644 index 00000000..caeffeb7 --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include + +#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 data; + + std::optional sourceRegister; + std::optional destinationRegister; + + std::optional programWordAddress; + std::optional programWordAddressOffset; + bool canSkipNextInstruction; + + std::optional registerBitPosition; + std::optional statusRegisterBitPosition; + + std::optional ioSpaceAddress; + std::optional dataSpaceAddress; + + std::optional displacement; + + Instruction( + const std::string& name, + std::uint32_t opcode, + std::uint8_t byteSize, + bool canChangeProgramFlow, + std::optional data = std::nullopt, + std::optional sourceRegister = std::nullopt, + std::optional destinationRegister = std::nullopt, + std::optional programWordAddress = std::nullopt, + std::optional programWordAddressOffset = std::nullopt, + bool canSkipNextInstruction = false, + std::optional registerBitPosition = std::nullopt, + std::optional statusRegisterBitPosition = std::nullopt, + std::optional ioSpaceAddress = std::nullopt, + std::optional dataSpaceAddress = std::nullopt, + std::optional 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) + {} + }; +} diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp new file mode 100644 index 00000000..7a7ac892 --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp @@ -0,0 +1,773 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#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 + 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 bitFieldRanges; + + const std::uint8_t length; + const std::uint32_t mask; + + constexpr InstructionParameter( + const std::array& 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({bitFieldRange}) + ) + {} + }; + + template + struct RegisterParameter: public InstructionParameter + { + const std::uint8_t offset = 0; + const bool pair = false; + + constexpr RegisterParameter( + std::uint8_t offset, + bool pair, + const std::array& bitFieldRanges + ) + : InstructionParameter(bitFieldRanges) + , offset(offset) + , pair(pair) + {} + + constexpr RegisterParameter( + std::uint8_t offset, + bool pair, + const Services::BitsetService::BitFieldRange& bitFieldRange + ) + : InstructionParameter(bitFieldRange) + , offset(offset) + , pair(pair) + {} + + constexpr RegisterParameter( + const std::array& bitFieldRanges + ) + : InstructionParameter(bitFieldRanges) + {} + + constexpr RegisterParameter(const Services::BitsetService::BitFieldRange& bitFieldRange) + : InstructionParameter(bitFieldRange) + {} + }; + + template + struct IsRegisterParam: std::false_type {}; + + template + struct IsRegisterParam>: std::true_type {}; + + template + requires (std::is_same_v || std::is_base_of_v) + class OptionalInstructionParameter + { + public: + ParamType value; + + constexpr OptionalInstructionParameter(ParamType value) + : value(value) + {}; + + static constexpr bool hasValue() { + if constexpr (std::is_base_of_v) { + return true; + + } else { + return false; + } + } + }; + + /** + * Base for InstructionParameterMask(ParamType param, ParamPackType... params). + */ + template + 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 + 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::value + ) + && ( + !decltype(destinationRegisterParameter)::hasValue() + || IsRegisterParam::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 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(*(dataBegin + 1) << 8 | *dataBegin); + + if constexpr (wordSize == 2) { + opcode = (opcode << 16) | static_cast(*(dataBegin + 3) << 8 | *(dataBegin + 2)); + } + + if ((opcode & SelfType::opcodeMask()) != static_cast(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( + 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( + 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( + 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( + BitsetService::extractBitField(opcode, param.bitFieldRanges) + ); + + const auto signBitMask = static_cast(0x01 << (param.length - 1)); + if (addressOffset & signBitMask) { + // Sign extending required + addressOffset |= static_cast(-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( + 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(-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; + } + }; +} diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp new file mode 100644 index 00000000..499bab2b --- /dev/null +++ b/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp @@ -0,0 +1,1563 @@ +#pragma once + +#include + +#include "Opcode.hpp" +#include "src/Services/BitsetService.hpp" + +namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes +{ + using Adc = Opcode< + "ADC", + 0b0001110000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Add = Opcode< + "ADD", + 0b0000110000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Adiw = Opcode< + "ADIW", + 0b1001011000000000, + 1, + false, + std::nullopt, + RegisterParameter(24, true, {5, 2}), + InstructionParameter(std::to_array({ + {7, 2}, + {3, 4}, + })) + >; + + using And = Opcode< + "AND", + 0b0010000000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Andi = Opcode< + "ANDI", + 0b0111000000000000, + 1, + false, + std::nullopt, + RegisterParameter({7, 4}), + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Asr = Opcode< + "ASR", + 0b1001010000000101, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Bclr = Opcode< + "BCLR", + 0b1001010010001000, + 1, + false, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({6, 3}) + >; + + using Bld = Opcode< + "BLD", + 0b1111100000000000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}) + >; + + using Brbc = Opcode< + "BRBC", + 0b1111010000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}), + std::nullopt, + InstructionParameter({2, 3}) + >; + + using Brbs = Opcode< + "BRBS", + 0b1111000000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}), + std::nullopt, + InstructionParameter({2, 3}) + >; + + using Brcc = Opcode< + "BRCC", + 0b1111010000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brcs = Opcode< + "BRCS", + 0b1111000000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Break = Opcode< + "BREAK", + 0b1001010110011000, + 1, + false + >; + + using Breq = Opcode< + "BREQ", + 0b1111000000000001, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brge = Opcode< + "BRGE", + 0b1111010000000100, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brhc = Opcode< + "BRHC", + 0b1111010000000101, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brhs = Opcode< + "BRHS", + 0b1111000000000101, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brid = Opcode< + "BRID", + 0b1111010000000111, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brie = Opcode< + "BRIE", + 0b1111000000000111, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brlo = Opcode< + "BRLO", + 0b1111000000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brlt = Opcode< + "BRLT", + 0b1111000000000100, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brmi = Opcode< + "BRMI", + 0b1111000000000010, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brne = Opcode< + "BRNE", + 0b1111010000000001, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brpl = Opcode< + "BRPL", + 0b1111010000000010, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brsh = Opcode< + "BRSH", + 0b1111010000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brtc = Opcode< + "BRTC", + 0b1111010000000110, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brts = Opcode< + "BRTS", + 0b1111000000000110, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brvc = Opcode< + "BRVC", + 0b1111010000000011, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Brvs = Opcode< + "BRVS", + 0b1111000000000011, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({9, 7}) + >; + + using Bset = Opcode< + "BSET", + 0b1001010000001000, + 1, + false, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({6, 3}) + >; + + using Bst = Opcode< + "BST", + 0b1111101000000000, + 1, + false, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}) + >; + + using Call = Opcode< + "CALL", + 0b10010100000011100000000000000000, + 2, + true, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {24, 5}, + {16, 17}, + })) + >; + + using Cbi = Opcode< + "CBI", + 0b1001100000000000, + 1, + false, + std::nullopt, + RegisterParameter({7, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}) + >; + + using Cbr = Opcode< + "CBR", + 0b0111000000000000, + 1, + false, + std::nullopt, + RegisterParameter({7, 4}), + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Clc = Opcode< + "CLC", + 0b1001010010001000, + 1, + false + >; + + using Clh = Opcode< + "CLH", + 0b1001010011011000, + 1, + false + >; + + using Cli = Opcode< + "CLI", + 0b1001010011111000, + 1, + false + >; + + using Cln = Opcode< + "CLN", + 0b1001010010101000, + 1, + false + >; + + using Clr = Opcode< + "CLR", + 0b0010010000000000, + 1, + false, + std::nullopt, + RegisterParameter({9, 10}) + >; + + using Cls = Opcode< + "CLS", + 0b1001010011001000, + 1, + false + >; + + using Clt = Opcode< + "CLT", + 0b1001010011101000, + 1, + false + >; + + using Clv = Opcode< + "CLV", + 0b1001010010111000, + 1, + false + >; + + using Clz = Opcode< + "CLZ", + 0b1001010010011000, + 1, + false + >; + + using Com = Opcode< + "COM", + 0b1001010000000000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Cp = Opcode< + "CP", + 0b0001010000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Cpc = Opcode< + "CPC", + 0b0000010000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Cpi = Opcode< + "CPI", + 0b0011000000000000, + 1, + false, + RegisterParameter({7, 4}), + std::nullopt, + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Cpse = Opcode< + "CPSE", + 0b0001000000000000, + 1, + true, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + true + >; + + using Dec = Opcode< + "DEC", + 0b1001010000001010, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Des = Opcode< + "DES", + 0b1001010000001011, + 1, + false, + std::nullopt, + std::nullopt, + InstructionParameter({7, 4}) + >; + + using Eicall = Opcode< + "EICALL", + 0b1001010100011001, + 1, + true + >; + + using Eijmp = Opcode< + "EIJMP", + 0b1001010000011001, + 1, + true + >; + + using Elpm1 = Opcode< + "ELPM", + 0b1001010111011000, + 1, + false + >; + + using Elpm2 = Opcode< + "ELPM", + 0b1001000000000110, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Elpm3 = Opcode< + "ELPM", + 0b1001000000000111, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Eor = Opcode< + "EOR", + 0b0010010000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Fmul = Opcode< + "FMUL", + 0b0000001100001000, + 1, + false, + RegisterParameter({2, 3}), + RegisterParameter({6, 3}) + >; + + using Fmuls = Opcode< + "FMULS", + 0b0000001110000000, + 1, + false, + RegisterParameter({2, 3}), + RegisterParameter({6, 3}) + >; + + using Fmulsu = Opcode< + "FMULSU", + 0b0000001110001000, + 1, + false, + RegisterParameter({2, 3}), + RegisterParameter({6, 3}) + >; + + using Icall = Opcode< + "ICALL", + 0b1001010100001001, + 1, + true + >; + + using Ijmp = Opcode< + "IJMP", + 0b1001010000001001, + 1, + true + >; + + using In = Opcode< + "IN", + 0b1011000000000000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {10, 2}, + {3, 4} + })) + >; + + using Inc = Opcode< + "INC", + 0b1001010000000011, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Jmp = Opcode< + "JMP", + 0b10010100000011000000000000000000, + 2, + true, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {24, 5}, + {16, 17} + })) + >; + + using Lac = Opcode< + "LAC", + 0b1001001000000110, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Las = Opcode< + "LAS", + 0b1001001000000101, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Lat = Opcode< + "LAT", + 0b1001001000000111, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdX1 = Opcode< + "LD", + 0b1001000000001100, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdX2 = Opcode< + "LD", + 0b1001000000001101, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdX3 = Opcode< + "LD", + 0b1001000000001110, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdY1 = Opcode< + "LD", + 0b1000000000001000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdY2 = Opcode< + "LD", + 0b1001000000001001, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdY3 = Opcode< + "LD", + 0b1001000000001010, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LddY = Opcode< + "LDD", + 0b1000000000001000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {13, 1}, + {11, 2}, + {2, 3} + })) + >; + + using LdZ1 = Opcode< + "LD", + 0b1000000000000000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdZ2 = Opcode< + "LD", + 0b1001000000000001, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LdZ3 = Opcode< + "LD", + 0b1001000000000010, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using LddZ = Opcode< + "LDD", + 0b1000000000000000, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {13, 1}, + {11, 2}, + {2, 3} + })) + >; + + using Ldi = Opcode< + "LDI", + 0b1110000000000000, + 1, + false, + std::nullopt, + RegisterParameter(16, false, {7, 4}), + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4} + })) + >; + + using Lds1 = Opcode< + "LDS", + 0b10010000000000000000000000000000, + 2, + false, + std::nullopt, + RegisterParameter({24, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({15, 16}) + >; + + using Lds2 = Opcode< + "LDS", + 0b1010000000000000, + 1, + false, + std::nullopt, + RegisterParameter({7, 4}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {10, 3}, + {3, 4} + })) + >; + + using Lpm1 = Opcode< + "LPM", + 0b1001010111001000, + 1, + false + >; + + using Lpm2 = Opcode< + "LPM", + 0b1001000000000100, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Lpm3 = Opcode< + "LPM", + 0b1001000000000101, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Lsl = Opcode< + "LSL", + 0b0000110000000000, + 1, + false, + std::nullopt, + RegisterParameter({9, 10}) + >; + + using Lsr = Opcode< + "LSR", + 0b1001010000000110, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Mov = Opcode< + "MOV", + 0b0010110000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Movw = Opcode< + "MOVW", + 0b0000000100000000, + 1, + false, + RegisterParameter(0, true, {3, 4}), + RegisterParameter(0, true, {7, 4}) + >; + + using Mul = Opcode< + "MUL", + 0b1001110000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Muls = Opcode< + "MULS", + 0b0000001000000000, + 1, + false, + RegisterParameter({3, 4}), + RegisterParameter({7, 4}) + >; + + using Mulsu = Opcode< + "MULSU", + 0b0000001100000000, + 1, + false, + RegisterParameter({2, 3}), + RegisterParameter({6, 3}) + >; + + using Neg = Opcode< + "NEG", + 0b1001010000000001, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Nop = Opcode< + "NOP", + 0b0000000000000000, + 1, + false + >; + + using Or = Opcode< + "OR", + 0b0010100000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Ori = Opcode< + "ORI", + 0b0110000000000000, + 1, + false, + std::nullopt, + RegisterParameter({7, 4}), + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Out = Opcode< + "OUT", + 0b1011100000000000, + 1, + false, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {10, 2}, + {3, 4}, + })) + >; + + using Pop = Opcode< + "POP", + 0b1001000000001111, + 1, + false, + RegisterParameter({8, 5}) + >; + + using Push = Opcode< + "PUSH", + 0b1001001000001111, + 1, + false, + RegisterParameter({8, 5}) + >; + + using Rcall = Opcode< + "RCALL", + 0b1101000000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({11, 12}) + >; + + using Ret = Opcode< + "RET", + 0b1001010100001000, + 1, + true + >; + + using Reti = Opcode< + "RETI", + 0b1001010100011000, + 1, + true + >; + + using Rjmp = Opcode< + "RJMP", + 0b1100000000000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({11, 12}) + >; + + using Rol = Opcode< + "ROL", + 0b0001110000000000, + 1, + false, + std::nullopt, + RegisterParameter({9, 10}) + >; + + using Ror = Opcode< + "ROR", + 0b1001010000000111, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Sbc = Opcode< + "SBC", + 0b0000100000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Sbci = Opcode< + "SBCI", + 0b0100000000000000, + 1, + false, + std::nullopt, + RegisterParameter(16, false, {7, 4}), + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Sbi = Opcode< + "SBI", + 0b1001101000000000, + 1, + false, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}), + std::nullopt, + InstructionParameter({7, 5}) + >; + + using Sbic = Opcode< + "SBIC", + 0b1001100100000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}), + std::nullopt, + InstructionParameter({7, 5}), + std::nullopt, + std::nullopt, + true + >; + + using Sbis = Opcode< + "SBIS", + 0b1001101100000000, + 1, + true, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}), + std::nullopt, + InstructionParameter({7, 5}), + std::nullopt, + std::nullopt, + true + >; + + using Sbiw = Opcode< + "SBIW", + 0b1001011100000000, + 1, + false, + std::nullopt, + RegisterParameter(24, true, {5, 2}), + InstructionParameter(std::to_array({ + {7, 2}, + {3, 4}, + })) + >; + + using Sbr = Opcode< + "SBR", + 0b0110000000000000, + 1, + false, + std::nullopt, + RegisterParameter(16, true, {7, 4}), + InstructionParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Sbrc = Opcode< + "SBRC", + 0b1111110000000000, + 1, + true, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + true + >; + + using Sbrs = Opcode< + "SBRS", + 0b1111111000000000, + 1, + true, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({2, 3}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + true + >; + + using Sec = Opcode< + "SEC", + 0b1001010000001000, + 1, + false + >; + + using Seh = Opcode< + "SEH", + 0b1001010001011000, + 1, + false + >; + + using Sei = Opcode< + "SEI", + 0b1001010001111000, + 1, + false + >; + + using Sen = Opcode< + "SEN", + 0b1001010000101000, + 1, + false + >; + + using Ser = Opcode< + "SER", + 0b1110111100001111, + 1, + false, + std::nullopt, + RegisterParameter({7, 4}) + >; + + using Ses = Opcode< + "SES", + 0b1001010001001000, + 1, + false + >; + + using Set = Opcode< + "SET", + 0b1001010001101000, + 1, + false + >; + + using Sev = Opcode< + "SEV", + 0b1001010000111000, + 1, + false + >; + + using Sez = Opcode< + "SEZ", + 0b1001010000011000, + 1, + false + >; + + using Sleep = Opcode< + "SLEEP", + 0b1001010110001000, + 1, + false + >; + + using Spm1 = Opcode< + "SPM", + 0b1001010111101000, + 1, + false + >; + + using Spm2 = Opcode< + "SPM", + 0b1001010111111000, + 1, + false + >; + + using StX1 = Opcode< + "ST", + 0b1001001000001100, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StX2 = Opcode< + "ST", + 0b1001001000001101, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StX3 = Opcode< + "ST", + 0b1001001000001110, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StY1 = Opcode< + "ST", + 0b1000001000001000, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StY2 = Opcode< + "ST", + 0b1001001000001001, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StY3 = Opcode< + "ST", + 0b1001001000001010, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StdY = Opcode< + "STD", + 0b1000001000001000, + 1, + false, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {13, 1}, + {11, 2}, + {2, 3}, + })) + >; + + using StZ1 = Opcode< + "ST", + 0b1000001000000000, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StZ2 = Opcode< + "ST", + 0b1001001000000001, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StZ3 = Opcode< + "ST", + 0b1001001000000010, + 1, + false, + RegisterParameter({8, 5}) + >; + + using StdZ = Opcode< + "STD", + 0b1000001000000000, + 1, + false, + RegisterParameter({8, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter(std::to_array({ + {13, 1}, + {11, 2}, + {2, 3}, + })) + >; + + using Sts1 = Opcode< + "STS", + 0b10010010000000000000000000000000, + 2, + false, + RegisterParameter({24, 5}), + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + std::nullopt, + InstructionParameter({15, 16}) + >; + + using Sts2 = Opcode< + "STS", + 0b1010100000000000, + 1, + false, + RegisterParameter({7, 4}), + std::nullopt, + /* + * TODO: + * This is supposed to be a 7-bit data space address, but it requires additional parsing to be converted to an + * actual address. For that reason, we just treat it as a constant data param. + * + * We're not actually making use of data space addresses ATM, so it's not a big deal for now. Will sort it + * later. + */ + InstructionParameter(std::to_array({ + {10, 3}, + {3, 4}, + })) + >; + + using Sub = Opcode< + "SUB", + 0b0001100000000000, + 1, + false, + RegisterParameter(std::to_array({ + {9, 1}, + {3, 4}, + })), + RegisterParameter({8, 5}) + >; + + using Subi = Opcode< + "SUBI", + 0b0101000000000000, + 1, + false, + std::nullopt, + RegisterParameter({7, 4}), + RegisterParameter(std::to_array({ + {11, 4}, + {3, 4}, + })) + >; + + using Swap = Opcode< + "SWAP", + 0b1001010000000010, + 1, + false, + std::nullopt, + RegisterParameter({8, 5}) + >; + + using Tst = Opcode< + "TST", + 0b0010000000000000, + 1, + false, + std::nullopt, + RegisterParameter({9, 10}) + >; + + using Wdr = Opcode< + "WDR", + 0b1001010110101000, + 1, + false + >; + + using Xch = Opcode< + "XCH", + 0b1001001000000100, + 1, + false, + RegisterParameter({8, 5}) + >; +}