diff --git a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm index b257ea977..06f43b535 100644 --- a/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm +++ b/OSBindings/Mac/Clock SignalTests/ARMDecoderTests.mm @@ -26,12 +26,62 @@ void shift(uint32_t &source, uint32_t amount, uint32_t *carry = nullptr) { } else if(amount == 32) { if constexpr (set_carry) *carry = source & 1; source = 0; - } else { - if constexpr (set_carry) *carry = source & (0x8000'0000 >> amount); + } else if(amount > 0) { + if constexpr (set_carry) *carry = source & (0x8000'0000 >> (amount - 1)); source <<= amount; } break; + case ShiftType::LogicalRight: + if(amount > 32) { + if constexpr (set_carry) *carry = 0; + source = 0; + } else if(amount == 32) { + if constexpr (set_carry) *carry = source & 0x8000'0000; + source = 0; + } else if(amount > 0) { + if constexpr (set_carry) *carry = source & (1 << (amount - 1)); + source >>= amount; + } else { + // A logical shift right by '0' is treated as a shift by 32; + // assemblers are supposed to map LSR #0 to LSL #0. + if constexpr (set_carry) *carry = source & 0x8000'0000; + source = 0; + } + break; + + case ShiftType::ArithmeticRight: { + const uint32_t sign = (source & 0x8000'0000) ? 0xffff'ffff : 0x0000'0000; + + if(amount >= 32) { + if constexpr (set_carry) *carry = sign; + source = sign; + } else if(amount > 0) { + if constexpr (set_carry) *carry = source & (1 << (amount - 1)); + source = (source >> amount) | (sign << (32 - amount)); + } else { + // As per logical right, an arithmetic shift of '0' is + // treated as a shift by 32. + if constexpr (set_carry) *carry = source & 0x8000'0000; + source = sign; + } + } break; + + case ShiftType::RotateRight: { + if(amount == 32) { + if constexpr (set_carry) *carry = source & 0x8000'0000; + } else if(amount == 0) { + // Rotate right by 0 is treated as a rotate right by 1 through carry. + const uint32_t high = *carry << 31; + if constexpr (set_carry) *carry = source & 1; + source = (source >> 1) | high; + } else { + amount &= 31; + if constexpr (set_carry) *carry = source & (1 << (amount - 1)); + source = (source >> amount) | (source << (32 - amount)); + } + } break; + default: break; } } @@ -54,7 +104,6 @@ void shift(ShiftType type, uint32_t &source, uint32_t amount, uint32_t *carry) { } } - struct Scheduler { bool should_schedule(Condition condition) { return status.test(condition); @@ -67,7 +116,7 @@ struct Scheduler { auto &destination = registers_[fields.destination()]; const uint32_t operand1 = registers_[fields.operand1()]; uint32_t operand2; - uint32_t rotate_carry = 0; + uint32_t rotate_carry = status.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();