diff --git a/InstructionSets/ARM/Registers.hpp b/InstructionSets/ARM/Registers.hpp index d893b8b36..02123203e 100644 --- a/InstructionSets/ARM/Registers.hpp +++ b/InstructionSets/ARM/Registers.hpp @@ -59,24 +59,14 @@ struct Registers { overflow_flag_ = value; } - /// @returns The program counter address only, optionally with a post-increment. - template - uint32_t pc() { - const uint32_t result = pc_; - if constexpr (increment) { - pc_ = (pc_ + 4) & ConditionCode::Address; - } - return result; - } - void begin_irq() { interrupt_flags_ |= ConditionCode::IRQDisable; } void begin_fiq() { interrupt_flags_ |= ConditionCode::FIQDisable; } /// @returns The full PC + status bits. - uint32_t get() const { + uint32_t status(uint32_t offset) const { return uint32_t(mode_) | - pc_ | + ((active[15] + offset) & ConditionCode::Address) | (negative_flag_ & ConditionCode::Negative) | (zero_result_ ? 0 : ConditionCode::Zero) | (carry_flag_ ? ConditionCode::Carry : 0) | @@ -84,6 +74,10 @@ struct Registers { interrupt_flags_; } + uint32_t pc(uint32_t offset) const { + return (active[15] + offset) & ConditionCode::Address; + } + bool test(Condition condition) { const auto ne = [&]() -> bool { return zero_result_; @@ -129,8 +123,9 @@ struct Registers { } } + uint32_t active[16]; // TODO: register swaps with mode. + private: - uint32_t pc_ = 0; Mode mode_ = Mode::Supervisor; uint32_t zero_result_ = 0; diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm index 5b9d6c0bc..483130e21 100644 --- a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -106,7 +106,7 @@ void shift(ShiftType type, uint32_t &source, uint32_t amount, uint32_t *carry) { struct Scheduler { bool should_schedule(Condition condition) { - return status.test(condition); + return registers_.test(condition); } template void perform(DataProcessing fields) { @@ -146,10 +146,23 @@ struct Scheduler { // or Rm. constexpr DataProcessingFlags flags(f); - auto &destination = registers_[fields.destination()]; - const uint32_t operand1 = registers_[fields.operand1()]; + const bool shift_by_register = !flags.operand2_is_immediate() && fields.shift_count_is_register(); + + auto &destination = registers_.active[fields.destination()]; + + // When R15 appears in either of the Rn or Rs positions it will give the value + // of the PC alone, with the PSR bits replaced by zeroes. ... + // + // If the shift amount is specified in the instruction, the PC will be 8 bytes ahead. + // If a register is used to specify the shift amount, the PC will be ... 12 bytes ahead + // when used as Rn or Rm. + const uint32_t operand1 = + (fields.operand1() == 15) ? + registers_.pc(shift_by_register ? 12 : 8) : + registers_.active[fields.operand1()]; + uint32_t operand2; - uint32_t rotate_carry = status.c(); + uint32_t rotate_carry = registers_.c(); // Populate carry from the shift only if it'll be used. constexpr bool shift_sets_carry = is_logical(flags.operation()) && flags.set_condition_codes(); @@ -163,12 +176,30 @@ struct Scheduler { } else { uint32_t shift_amount; if(fields.shift_count_is_register()) { - shift_amount = registers_[fields.shift_register()]; + // When R15 appears in either of the Rn or Rs positions it will give the value + // of the PC alone, with the PSR bits replaced by zeroes. ... + // + // If a register is used to specify the shift amount, the + // PC will be 8 bytes ahead when used as Rs. + shift_amount = + fields.shift_register() == 15 ? + registers_.pc(8) : + registers_.active[fields.shift_register()]; } else { shift_amount = fields.shift_amount(); } - operand2 = registers_[fields.operand2()]; + // When R15 appears in the Rm position it will give the value of the PC together + // with the PSR flags to the barrel shifter. ... + // + // If the shift amount is specified in the instruction, the PC will be 8 bytes ahead. + // If a register is used to specify the shift amount, the PC will be ... 12 bytes ahead + // when used as Rn or Rm. + if(fields.operand2() == 15) { + operand2 = registers_.status(shift_by_register ? 12 : 8); + } else { + operand2 = registers_.active[fields.operand2()]; + } shift(fields.shift_type(), operand2, shift_amount, &rotate_carry); } @@ -193,12 +224,12 @@ struct Scheduler { conditions = operand1 + operand2; if constexpr (flags.operation() == DataProcessingOperation::ADC) { - conditions += status.c(); + conditions += registers_.c(); } if constexpr (flags.set_condition_codes()) { - status.set_c(Numeric::carried_out(operand1, operand2, conditions)); - status.set_v(Numeric::overflow(operand1, operand2, conditions)); + registers_.set_c(Numeric::carried_out(operand1, operand2, conditions)); + registers_.set_v(Numeric::overflow(operand1, operand2, conditions)); } if constexpr (!is_comparison(flags.operation())) { @@ -212,12 +243,12 @@ struct Scheduler { conditions = operand1 - operand2; if constexpr (flags.operation() == DataProcessingOperation::SBC) { - conditions -= status.c(); + conditions -= registers_.c(); } if constexpr (flags.set_condition_codes()) { - status.set_c(Numeric::carried_out(operand1, operand2, conditions)); - status.set_v(Numeric::overflow(operand1, operand2, conditions)); + registers_.set_c(Numeric::carried_out(operand1, operand2, conditions)); + registers_.set_v(Numeric::overflow(operand1, operand2, conditions)); } if constexpr (!is_comparison(flags.operation())) { @@ -230,12 +261,12 @@ struct Scheduler { conditions = operand2 - operand1; if constexpr (flags.operation() == DataProcessingOperation::RSC) { - conditions -= status.c(); + conditions -= registers_.c(); } if constexpr (flags.set_condition_codes()) { - status.set_c(Numeric::carried_out(operand2, operand1, conditions)); - status.set_v(Numeric::overflow(operand2, operand1, conditions)); + registers_.set_c(Numeric::carried_out(operand2, operand1, conditions)); + registers_.set_v(Numeric::overflow(operand2, operand1, conditions)); } destination = conditions; @@ -244,12 +275,12 @@ struct Scheduler { // Set N and Z in a unified way. if constexpr (flags.set_condition_codes()) { - status.set_nz(conditions); + registers_.set_nz(conditions); } // Set C from the barrel shifter if applicable. if constexpr (shift_sets_carry) { - status.set_c(rotate_carry); + registers_.set_c(rotate_carry); } // TODO: If register 15 was in use as a destination, write back and clean up. @@ -270,9 +301,7 @@ struct Scheduler { void unknown(uint32_t) {} private: - Registers status; - - uint32_t registers_[16]; // TODO: register swaps with mode. + Registers registers_; }; }