diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index 8e1ede1f2..febe92584 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -11,10 +11,151 @@ #include "Model.hpp" #include "Operation.hpp" +#include "../../Reflection/Dispatcher.hpp" + #include namespace InstructionSet::ARM { +enum class ShiftType { + LogicalLeft = 0b00, + LogicalRight = 0b01, + ArithmeticRight = 0b10, + RotateRight = 0b11, +}; + + +static constexpr int FlagsStartBit = 20; + +template +constexpr bool flag_bit(uint8_t flags) { + static_assert(position >= 20 && position < 28); + return flags & (1 << (position - FlagsStartBit)); +} + +// +// Data processing (i.e. AND to MVN). +// +struct DataProcessingFlags { + constexpr DataProcessingFlags(uint8_t flags) noexcept : flags_(flags) {} + + /// @returns @c true if operand 2 is defined by the @c rotate() and @c immediate() fields; + /// @c false if it is defined by the @c shift_*() and @c operand2() fields. + constexpr bool operand2_is_immediate() { return flag_bit<25>(flags_); } + + constexpr bool set_condition_codes() { return flag_bit<20>(flags_); } + +private: + uint8_t flags_; +}; + +struct DataProcessing { + constexpr DataProcessing(uint32_t opcode) noexcept : opcode_(opcode) {} + + /// The destination register index. i.e. Rd. + int destination() const { return (opcode_ >> 12) & 0xf; } + + /// The operand 1 register index. i.e. Rn. + int operand1() const { return (opcode_ >> 16) & 0xf; } + + + // + // Register values for operand 2. + // + + /// The operand 2 register index if @c operand2_is_immediate() is @c false; meaningless otherwise. + int operand2() const { return opcode_ & 0xf; } + /// The type of shift to apply to operand 2 if @c operand2_is_immediate() is @c false; meaningless otherwise. + ShiftType shift_type() const { return ShiftType((opcode_ >> 5) & 3); } + /// @returns @c true if the amount to shift by should be taken from a register; @c false if it is an immediate value. + bool shift_count_is_register() const { return opcode_ & (1 << 4); } + /// The shift amount register index if @c shift_count_is_register() is @c true; meaningless otherwise. + int shift_register() const { return (opcode_ >> 8) & 0xf; } + /// The amount to shift by if @c shift_count_is_register() is @c false; meaningless otherwise. + int shift_amount() const { return (opcode_ >> 7) & 0x1f; } + + // + // Immediate values for operand 2. + // + + /// An 8-bit value to rotate right @c rotate() places if @c operand2_is_immediate() is @c true; meaningless otherwise. + int immediate() const { return opcode_ & 0xff; } + /// The number of bits to rotate @c immediate() by to the right if @c operand2_is_immediate() is @c true; meaningless otherwise. + int rotate() const { return (opcode_ >> 7) & 0x1e; } + +private: + uint32_t opcode_; +}; + +// +// MUL and MLA. +// +struct MultiplyFlags { + constexpr MultiplyFlags(uint8_t flags) noexcept : flags_(flags) {} + + constexpr bool set_condition_codes() { return flag_bit<20>(flags_); } + +private: + uint8_t flags_; +}; + +struct Multiply { + constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {} + + /// The destination register index. i.e. 'Rd'. + int destination() const { return (opcode_ >> 16) & 0xf; } + + /// The accumulator register index for multiply-add. i.e. 'Rn'. + int accumulator() const { return (opcode_ >> 12) & 0xf; } + + /// The multiplicand register index. i.e. 'Rs'. + int multiplicand() const { return (opcode_ >> 8) & 0xf; } + + /// The multiplier register index. i.e. 'Rm'. + int multiplier() const { return opcode_ & 0xf; } + +private: + uint32_t opcode_; +}; + + +struct OperationMapper { + template void dispatch(uint32_t instruction, SchedulerT &scheduler) { + constexpr auto partial = static_cast(i << 20); + + // Data processing; cf. p.17. + if constexpr (((partial >> 26) & 0b11) == 0b00) { + constexpr auto operation = Operation(int(Operation::AND) + ((partial >> 21) & 0xf)); + constexpr auto flags = DataProcessingFlags(i); + scheduler.template data_processing( + DataProcessing(instruction) + ); + return; + } + + // Multiply and multiply-accumulate (MUL, MLA); cf. p.23. + if(((partial >> 22) & 0b111'111) == 0b000'000) { + if(((instruction >> 4) & 0b1111) != 0b1001) { + scheduler.unknown(instruction); + } else { + constexpr bool is_mla = partial & (1 << 21); + constexpr auto flags = MultiplyFlags(i); + scheduler.template multiply( + Multiply(instruction) + ); + } + + return; + } + } +}; + +template void dispatch(uint32_t instruction, SchedulerT &scheduler) { + OperationMapper mapper; + Reflection::dispatch(mapper, (instruction >> 20) & 0xff, instruction, scheduler); +} + +/* template using OperationTable = std::array; @@ -113,4 +254,6 @@ constexpr Operation operation(uint32_t opcode) { return op; } +*/ + } diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp index c9b8fec0f..d242f041d 100644 --- a/InstructionSets/ARM/Instruction.hpp +++ b/InstructionSets/ARM/Instruction.hpp @@ -14,7 +14,7 @@ namespace InstructionSet::ARM { -enum class ShiftType { +/*enum class ShiftType { LogicalLeft = 0b00, LogicalRight = 0b01, ArithmeticRight = 0b10, @@ -140,7 +140,7 @@ class Instruction { private: uint32_t opcode_; -}; +};*/ // TODO: do MUL and MLA really transpose Rd and Rn as per the data sheet? // ARM: Assembly Language Programming by Cockerell thinks not. diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index 4c78b06c6..e9adcf2a2 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -44,39 +44,7 @@ enum class Operation { /// Rd = NOT Op2. MVN, - /// Rd = Op1 AND Op2. - ANDS, - /// Rd = Op1 EOR Op2. - EORS, - /// Rd = Op1 - Op2. - SUBS, - /// Rd = Op2 - Op1. - RSBS, - /// Rd = Op1 + Op2. - ADDS, - /// Rd = Op1 + Ord2 + C. - ADCS, - /// Rd = Op1 - Op2 + C. - SBCS, - /// Rd = Op2 - Op1 + C. - RSCS, - /// Set condition codes on Op1 AND Op2. - TSTS, - /// Set condition codes on Op1 EOR Op2. - TEQS, - /// Set condition codes on Op1 - Op2. - CMPS, - /// Set condition codes on Op1 + Op2. - CMNS, - /// Rd = Op1 OR Op2. - ORRS, - /// Rd = Op2 - MOVS, - /// Rd = Op1 AND NOT Op2. - BICS, - /// Rd = NOT Op2. - MVNS, - + MUL, MLA, B, BL, LDR, STR, @@ -87,21 +55,8 @@ enum class Operation { CoprocessorDataTransfer, Undefined, - - // These are kept at the end for a minor decoding win; they can be only partially decoded - // with the table-based decoder used elsewhere so a special case checks more bits upon - // a MUL or MLA and keeping these at the end of the enum allows a single conditional to - // determine whether the extra decoding is needed. - // - // See is_multiply below. - MUL, MLA, - MULS, MLAS, }; -constexpr bool is_multiply(Operation op) { - return op >= Operation::MUL; -} - enum class Condition { EQ, NE, CS, CC, MI, PL, VS, VC,