diff --git a/InstructionSets/ARM/OperationMapper.hpp b/InstructionSets/ARM/OperationMapper.hpp index 83c04a12a..c9598f2b2 100644 --- a/InstructionSets/ARM/OperationMapper.hpp +++ b/InstructionSets/ARM/OperationMapper.hpp @@ -16,7 +16,7 @@ enum class Model { ARM2, }; -enum class Operation { +enum class DataProcessingOperation { AND, /// Rd = Op1 AND Op2. EOR, /// Rd = Op1 EOR Op2. SUB, /// Rd = Op1 - Op2. @@ -33,7 +33,9 @@ enum class Operation { MOV, /// Rd = Op2 BIC, /// Rd = Op1 AND NOT Op2. MVN, /// Rd = NOT Op2. +}; +enum class Operation { MUL, /// Rd = Rm * Rs MLA, /// Rd = Rm * Rs + Rn B, /// Add offset to PC; programmer allows for PC being two words ahead. @@ -120,12 +122,17 @@ private: struct DataProcessingFlags { constexpr DataProcessingFlags(uint8_t flags) noexcept : flags_(flags) {} + /// @returns The operation to apply. + constexpr DataProcessingOperation operation() const { + return DataProcessingOperation((flags_ >> 21) & 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. - constexpr bool operand2_is_immediate() { return flag_bit<25>(flags_); } + constexpr bool operand2_is_immediate() const { return flag_bit<25>(flags_); } /// @c true if the status register should be updated; @c false otherwise. - constexpr bool set_condition_codes() { return flag_bit<20>(flags_); } + constexpr bool set_condition_codes() const { return flag_bit<20>(flags_); } private: uint8_t flags_; @@ -332,8 +339,7 @@ struct OperationMapper { // Data processing; cf. p.17. if constexpr (((partial >> 26) & 0b11) == 0b00) { - constexpr auto operation = Operation(int(Operation::AND) + ((partial >> 21) & 0xf)); - scheduler.template perform( + scheduler.template perform( condition, DataProcessing(instruction) ); @@ -444,7 +450,7 @@ struct SampleScheduler { // (1) Condition, indicating the condition code associated with this operation; and // (2) An operation-specific encapsulation of the operation code for decoding of fields that didn't // fit into the template parameters. - template void perform(Condition, DataProcessing); + template void perform(Condition, DataProcessing); template void perform(Condition, Multiply); template void perform(Condition, SingleDataTransfer); template void perform(Condition, BlockDataTransfer); diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm index 5e5533b2b..cbaef8417 100644 --- a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -15,8 +15,74 @@ using namespace InstructionSet::ARM; namespace { +template +void shift(uint32_t &source, uint32_t &carry, uint32_t amount) { + switch(type) { + case ShiftType::LogicalLeft: + if(amount > 32) { + source = carry = 0; + } else if(amount == 32) { + carry = source & 1; + source = 0; + } else { + carry = source & (0x8000'0000 >> amount); + source <<= amount; + } + break; + + default: break; + } +} + +void shift(ShiftType type, uint32_t &source, uint32_t &carry, uint32_t amount) { + switch(type) { + case ShiftType::LogicalLeft: shift(source, carry, amount); break; + case ShiftType::LogicalRight: shift(source, carry, amount); break; + case ShiftType::ArithmeticRight: shift(source, carry, amount); break; + case ShiftType::RotateRight: shift(source, carry, amount); break; + } +} + struct Scheduler { - template void perform(Condition, DataProcessing) {} + template void perform(Condition condition, DataProcessing fields) { + if(!status.test(condition)) { + return; + } + + constexpr DataProcessingFlags flags(f); + auto &destination = registers_[fields.destination()]; + const auto &operand1 = registers_[fields.operand1()]; + uint32_t operand2; + uint32_t rotate_carry = 0; + + if constexpr (flags.operand2_is_immediate()) { + if(!fields.rotate()) { + operand2 = fields.immediate(); + } else { + operand2 = fields.immediate() >> fields.rotate(); + operand2 |= fields.immediate() << (32 - fields.rotate()); + } + } else { + uint32_t shift_amount; + if(fields.shift_count_is_register()) { + shift_amount = registers_[fields.shift_register()]; + } else { + shift_amount = fields.shift_amount(); + } + + operand2 = registers_[fields.operand2()]; + shift(fields.shift_type(), operand2, rotate_carry, shift_amount); + } + + switch(flags.operation()) { + case DataProcessingOperation::AND: + destination = operand1 & operand2; + break; + + default: break; // ETC. + } + } + template void perform(Condition, Multiply) {} template void perform(Condition, SingleDataTransfer) {} template void perform(Condition, BlockDataTransfer) {} @@ -30,6 +96,11 @@ struct Scheduler { void software_interrupt(Condition) {} void unknown(uint32_t) {} + +private: + Status status; + + uint32_t registers_[16]; // TODO: register swaps with mode. }; }