diff --git a/InstructionSets/ARM/Decoder.hpp b/InstructionSets/ARM/Decoder.hpp index c9f1a7b7c..8e1ede1f2 100644 --- a/InstructionSets/ARM/Decoder.hpp +++ b/InstructionSets/ARM/Decoder.hpp @@ -31,14 +31,23 @@ constexpr OperationTable operation_table() { // Data processing; cf. p.17. if(((opcode >> 26) & 0b11) == 0b00) { - result[c] = Operation((c >> 21) & 0xf); + const auto operation = (c >> 21) & 0xf; + if((opcode >> 20) & 1) { + result[c] = Operation(int(Operation::ANDS) + operation); + } else { + result[c] = Operation(int(Operation::AND) + operation); + } continue; } // Multiply and multiply-accumulate (MUL, MLA); cf. p.23. if(((opcode >> 22) & 0b111'111) == 0b000'000) { - result[c] = - ((opcode >> 21) & 1) ? Operation::MLA : Operation::MUL; + switch((opcode >> 20) & 3) { + case 0: result[c] = Operation::MUL; break; + case 1: result[c] = Operation::MULS; break; + case 2: result[c] = Operation::MLA; break; + case 3: result[c] = Operation::MLAS; break; + } continue; } @@ -91,8 +100,11 @@ constexpr Operation operation(uint32_t opcode) { // MUL and MLA have an extra constraint that doesn't fit the neat // 256-entry table format as above. + // + // Hope: most instructions aren't MUL/MLA so relying on the branch predictor + // here is fine. if( - (op == Operation::MUL || op == Operation::MLA) + is_multiply(op) && ((opcode >> 4) & 0b1111) != 0b1001 ) { return Operation::Undefined; diff --git a/InstructionSets/ARM/Instruction.hpp b/InstructionSets/ARM/Instruction.hpp index e26d5c585..c9b8fec0f 100644 --- a/InstructionSets/ARM/Instruction.hpp +++ b/InstructionSets/ARM/Instruction.hpp @@ -26,60 +26,117 @@ class Instruction { public: constexpr Instruction(uint32_t opcode) noexcept : opcode_(opcode) {} - Condition condition() const { return Condition(opcode_ >> 28); } - Operation operation() const { + constexpr Condition condition() const { return Condition(opcode_ >> 28); } + constexpr Operation operation() const { return InstructionSet::ARM::operation(opcode_); } // // B and BL. // + struct Branch { + constexpr Branch(uint32_t opcode) noexcept : opcode_(opcode) {} - /// Provides a 26-bit offset to add to the program counter for B and BL. - uint32_t b_offset() const { return (opcode_ & 0xff'ffff) << 2; } + /// Provides a 26-bit offset to add to the program counter for B and BL. + uint32_t offset() const { return (opcode_ & 0xff'ffff) << 2; } + + private: + uint32_t opcode_; + }; + Branch branch() const { return Branch(opcode_); } // // Data processing (i.e. AND to MVN). // + struct DataProcessing { + constexpr DataProcessing(uint32_t opcode) noexcept : opcode_(opcode) {} - /// @returns @c true if this operation should set condition codes; @c false otherwise. - /// @note Valid for data processing and multiply/multiply-accumulate. - bool set_condition_codes() const { return opcode_ & (1 << 20); } - // TODO: could build this into the Operation? + /// The destination register index. + int destination() const { return (opcode_ >> 12) & 0xf; } - /// The destination register index. - int destination() const { return (opcode_ >> 12) & 0xf; } + /// The operand 1 register index. + int operand1() const { return (opcode_ >> 16) & 0xf; } - /// The operand 1 register index. - int operand1() const { return (opcode_ >> 16) & 0xf; } + /// @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. + bool operand2_is_immediate() const { return opcode_ & (1 << 25); } - /// @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. - bool operand2_is_immediate() const { return opcode_ & (1 << 25); } + // + // 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 if @c operand2_is_immediate() is @c true; meaningless otherwise. + int rotate() const { return (opcode_ >> 7) & 0x1e; } + + private: + uint32_t opcode_; + }; + DataProcessing data_processing() const { return DataProcessing(opcode_); } // - // Register values for operand 2. + // MUL and MLA. // + struct Multiply { + constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {} - /// 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; } + /// 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_; + }; + Multiply multiply() const { return Multiply(opcode_); } // - // Immediate values for operand 2. + // LDR and STR. // + struct SingleDataTransfer { + constexpr SingleDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {} - /// 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 if @c operand2_is_immediate() is @c true; meaningless otherwise. - int rotate() const { return (opcode_ >> 7) & 0x1e; } + /// The destination register index. i.e. 'Rd' for LDR. + int destination() const { return (opcode_ >> 12) & 0xf; } + + /// The destination register index. i.e. 'Rd' for STR. + int source() const { return (opcode_ >> 12) & 0xf; } + + /// The base register index. i.e. 'Rn'. + int base() const { return (opcode_ >> 16) & 0xf; } + + /// + int offset() const { return opcode_ & 0xfff; } + + // TODO: P, U, B, W, L, I. + + private: + uint32_t opcode_; + + }; private: uint32_t opcode_; diff --git a/InstructionSets/ARM/Operation.hpp b/InstructionSets/ARM/Operation.hpp index af06081a3..4c78b06c6 100644 --- a/InstructionSets/ARM/Operation.hpp +++ b/InstructionSets/ARM/Operation.hpp @@ -44,8 +44,40 @@ 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, + B, BL, - MUL, MLA, LDR, STR, LDM, STM, @@ -55,8 +87,21 @@ 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,