aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrond <grond@grondhaus.net>2026-06-24 14:11:18 -0700
committeriximeow <me@iximeow.net>2026-07-28 02:53:24 +0000
commit44f91bf710081d98b465252487a6b168b2f2597e (patch)
tree4a345f7574fc83ccfa7d4fe7a757e39eacf2eb93
parent40743d84ffc8bbe0a4e24b9af4d65d58a1367b23 (diff)
Implement the RRX immediate shift type
-rw-r--r--CHANGELOG1
-rw-r--r--src/armv7.rs39
-rw-r--r--src/armv7/display.rs33
-rw-r--r--tests/armv7/mod.rs5
4 files changed, 55 insertions, 23 deletions
diff --git a/CHANGELOG b/CHANGELOG
index deb6edd..2daae7f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,7 @@
on the opcode (rather than distinct opcodes)
several fixes from @Grond66:
+* ARMv7: support the RRX rotate mode.
* ARMv7: added `armv7::InstDecoder::in_thumb_mode` to read back if an ARMv7
decoder is operating in thumb mode.
* ARMv7: fix incorrect handling of registers lsr or asr by an immediate 32.
diff --git a/src/armv7.rs b/src/armv7.rs
index f911388..ada486a 100644
--- a/src/armv7.rs
+++ b/src/armv7.rs
@@ -331,6 +331,10 @@ pub enum ShiftStyle {
ASR = 2,
/// rotate-right, filling with bits shifted out of the value.
ROR = 3,
+ /// shift right by one, populating the new most-significant bit from the carry flag.
+ ///
+ /// only possible on immediate shifts.
+ RRX = 4,
}
impl Display for ShiftStyle {
@@ -345,7 +349,7 @@ impl Display for ShiftStyle {
}
impl ShiftStyle {
- fn from(bits: u8) -> ShiftStyle {
+ fn from_bits(bits: u8) -> ShiftStyle {
match bits {
0b00 => ShiftStyle::LSL,
0b01 => ShiftStyle::LSR,
@@ -357,10 +361,11 @@ impl ShiftStyle {
fn name(&self) -> &'static [u8; 3] {
match self {
- ShiftStyle::LSL => &[b'l', b's', b'l'],
- ShiftStyle::LSR => &[b'l', b's', b'r'],
- ShiftStyle::ASR => &[b'a', b's', b'r'],
- ShiftStyle::ROR => &[b'r', b'o', b'r'],
+ ShiftStyle::LSL => b"lsl",
+ ShiftStyle::LSR => b"lsr",
+ ShiftStyle::ASR => b"asr",
+ ShiftStyle::ROR => b"ror",
+ ShiftStyle::RRX => b"rrx",
}
}
}
@@ -372,7 +377,7 @@ impl RegRegShift {
}
/// the way in which this register is shifted.
pub fn stype(&self) -> ShiftStyle {
- ShiftStyle::from((self.data >> 5) as u8 & 0b11)
+ ShiftStyle::from_bits((self.data >> 5) as u8 & 0b11)
}
/// the general-purpose register to be shifted.
pub fn shiftee(&self) -> Reg {
@@ -387,9 +392,14 @@ pub struct RegImmShift {
}
impl RegImmShift {
+ /// The shift immediate, without any interpretation
+ fn imm_raw(&self) -> u8 {
+ (self.data >> 7) as u8 & 0b11111
+ }
+
/// the immediate this register is shifted by.
pub fn imm(&self) -> u8 {
- let raw = (self.data >> 7) as u8 & 0b11111;
+ let raw = self.imm_raw();
// in the ARMv7m reference,
// `Instruction Details` ->
// `Shifts applied to a register` ->
@@ -403,12 +413,18 @@ impl RegImmShift {
// > ASR #<n> type = 0b10
// > If <n> < 32, immediate = <n>.
// > If <n> == 32, immediate = 0.
+ // > ...
+ // > ROR #<n> type = 0b11, immediate = <n>.
+ // > RRX type = 0b11, immediate = 0.
//
// so we have to fix this up here.
if raw == 0 {
let stype = self.stype();
if stype == ShiftStyle::LSR || stype == ShiftStyle::ASR {
return 32;
+ } else if stype == ShiftStyle::ROR {
+ // this is actually RRX, which rotates by exactly one bit.
+ return 1;
}
}
raw
@@ -416,8 +432,15 @@ impl RegImmShift {
/// the way in which this register is shifted.
pub fn stype(&self) -> ShiftStyle {
- ShiftStyle::from((self.data >> 5) as u8 & 0b11)
+ let stype = ShiftStyle::from_bits((self.data >> 5) as u8 & 0b11);
+
+ if self.imm_raw() == 0 && stype == ShiftStyle::ROR {
+ ShiftStyle::RRX
+ } else {
+ stype
+ }
}
+
/// the general-purpose register to be shifted.
pub fn shiftee(&self) -> Reg {
Reg::from_u8(self.data as u8 & 0b1111)
diff --git a/src/armv7/display.rs b/src/armv7/display.rs
index 5ceedd6..29d3ed6 100644
--- a/src/armv7/display.rs
+++ b/src/armv7/display.rs
@@ -97,22 +97,25 @@ impl<T: DisplaySink> DisplayingOperandVisitor<'_, T> {
match shift.into_shift() {
RegShiftStyle::RegImm(imm_shift) => {
self.f.write_reg(imm_shift.shiftee().number())?;
- if imm_shift.imm() != 0 || imm_shift.stype() != ShiftStyle::LSL {
+ let stype = imm_shift.stype();
+ if imm_shift.imm() != 0 || stype != ShiftStyle::LSL {
self.f.write_fixed_size(", ")?;
- self.emit_shift_type(imm_shift.stype())?;
- self.f.write_char(' ')?;
- let sh = imm_shift.imm();
- if sh >= 30 {
- self.f.write_char('3')?;
- self.f.write_char((sh - 30 + 0x30) as char)?;
- } else if sh >= 20 {
- self.f.write_char('2')?;
- self.f.write_char((sh - 20 + 0x30) as char)?;
- } else if sh >= 10 {
- self.f.write_char('1')?;
- self.f.write_char((sh - 10 + 0x30) as char)?;
- } else {
- self.f.write_char((sh + 0x30) as char)?;
+ self.emit_shift_type(stype)?;
+ if stype != ShiftStyle::RRX {
+ self.f.write_char(' ')?;
+ let sh = imm_shift.imm();
+ if sh >= 30 {
+ self.f.write_char('3')?;
+ self.f.write_char((sh - 30 + 0x30) as char)?;
+ } else if sh >= 20 {
+ self.f.write_char('2')?;
+ self.f.write_char((sh - 20 + 0x30) as char)?;
+ } else if sh >= 10 {
+ self.f.write_char('1')?;
+ self.f.write_char((sh - 10 + 0x30) as char)?;
+ } else {
+ self.f.write_char((sh + 0x30) as char)?;
+ }
}
}
}
diff --git a/tests/armv7/mod.rs b/tests/armv7/mod.rs
index 9a9e718..632bd3e 100644
--- a/tests/armv7/mod.rs
+++ b/tests/armv7/mod.rs
@@ -707,6 +707,11 @@ fn test_register_shift_rotate() {
// applied shift is 32.
test_armv6([0x21, 0x00, 0x20, 0x00], "eoreq r0, r0, r1, lsr 32");
test_armv6([0x41, 0x00, 0x20, 0x00], "eoreq r0, r0, r1, asr 32");
+
+ // When the argument of an immediate ROR shift is 0, it actually specifies an entirely
+ // different shift mode called RRX which only shifts by one to the right and populates the MSB
+ // with the carry flag.
+ test_all([0x62, 0x00, 0x01, 0xe0], "and r0, r1, r2, rrx");
}
static INSTRUCTION_BYTES: [u8; 4 * 60] = [