diff --git a/InstructionSets/ARM/Registers.hpp b/InstructionSets/ARM/Registers.hpp index 8d27026c5..2a95ee78b 100644 --- a/InstructionSets/ARM/Registers.hpp +++ b/InstructionSets/ARM/Registers.hpp @@ -204,21 +204,6 @@ struct Registers { std::array active; - private: - Mode mode_ = Mode::Supervisor; - - uint32_t zero_result_ = 0; - uint32_t negative_flag_ = 0; - uint32_t interrupt_flags_ = 0; - uint32_t carry_flag_ = 0; - uint32_t overflow_flag_ = 0; - - // Various shadow registers. - std::array user_registers_; - std::array fiq_registers_; - std::array irq_registers_; - std::array supervisor_registers_; - void set_mode(Mode target_mode) { if(mode_ == target_mode) { return; @@ -266,6 +251,23 @@ struct Registers { mode_ = target_mode; } + + private: + Mode mode_ = Mode::Supervisor; + + uint32_t zero_result_ = 0; + uint32_t negative_flag_ = 0; + uint32_t interrupt_flags_ = 0; + uint32_t carry_flag_ = 0; + uint32_t overflow_flag_ = 0; + + // Various shadow registers. + std::array user_registers_; + std::array fiq_registers_; + std::array irq_registers_; + std::array supervisor_registers_; + + }; } diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm index ee97f7113..89621f7a4 100644 --- a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -360,32 +360,57 @@ struct Scheduler { template void perform(BlockDataTransfer transfer) { constexpr BlockDataTransferFlags flags(f); - uint32_t address = transfer.base() == 15 ? - registers_.pc_status(8) : - registers_.active[transfer.base()]; - constexpr uint32_t step = flags.add_offset() ? 4 : -4; - - // TODO: forcing transfer of the user bank. // TODO: inclusion of the base in the register list. // TODO: data aborts. + uint32_t address = transfer.base() == 15 ? + registers_.pc_status(8) : + registers_.active[transfer.base()]; + + const uint16_t list = transfer.register_list(); + + // Writes are always from lowest address to highest; asking for storage downward + // just results in predecrementation of the address. + if constexpr (!flags.add_offset()) { + uint32_t total = ((list & 0xa) >> 1) + (list & 0x5); + total = ((list & 0xc) >> 2) + (list & 0x3); + address -= total * 4; + } + + [[maybe_unused]] uint32_t final_address = address; + + bool completed_all_visits = true; const auto visit = [&](uint32_t &value) { - if constexpr (flags.pre_index()) { - address += step; + if constexpr (flags.pre_index() == flags.add_offset()) { + address += 4; } if constexpr (flags.operation() == BlockDataTransferFlags::Operation::STM) { bus_.template read(address, value, registers_.mode(), false); } else { - bus_.template write(address, value, registers_.mode(), false); + completed_all_visits &= bus_.template write(address, value, registers_.mode(), false); } - if constexpr (!flags.pre_index()) { - address += step; + if constexpr (!flags.pre_index() != flags.add_offset()) { + address += 4; } }; - const uint16_t list = transfer.register_list(); + // Handle forcing transfer of the user bank. + Mode original_mode = registers_.mode(); + const bool adopt_user_mode = + ( + flags.operation() == BlockDataTransferFlags::Operation::STM && + flags.load_psr() + ) || + ( + flags.operation() == BlockDataTransferFlags::Operation::LDM && + !(list & (1 << 15)) + ); + if(adopt_user_mode) { + registers_.set_mode(Mode::User); + } + for(int c = 0; c < 15; c++) { if(list & (1 << c)) { visit(registers_.active[c]); @@ -400,21 +425,25 @@ struct Scheduler { } else { visit(value); registers_.set_pc(value); - if constexpr (flags.load_psr()) { - // TODO: [T]he PSR will be overwritten by the corresponding bits of the loaded value. - // In user mode, however, the I, F, M0 and M1 bits are protected from change - // ... The mode at the start of the instruction determines whether these bits - // are protected. + registers_.set_status(value); } } } if constexpr (flags.write_back_address()) { if(transfer.base() != 15) { - registers_.active[transfer.base()] = address; + if constexpr (flags.add_offset()) { + registers_.active[transfer.base()] = address; + } else { + registers_.active[transfer.base()] = final_address; + } } } + + if(adopt_user_mode) { + registers_.set_mode(original_mode); + } } void software_interrupt() {