aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/armv7.rs1016
-rw-r--r--test/armv7.rs168
2 files changed, 934 insertions, 250 deletions
diff --git a/src/armv7.rs b/src/armv7.rs
index 50cde07..eb86faa 100644
--- a/src/armv7.rs
+++ b/src/armv7.rs
@@ -1,3 +1,7 @@
+/// Manual references in this crate, both figure and page numbers, are with respect to the document
+/// `DDI0406C_d_armv7ar_arm.pdf`
+/// `sha256: 294668ae6480133b32d85e9567cc77c5eb0e1232decdf42cac7ab480e884f6e0`
+
//#[cfg(feature="use-serde")]
//use serde::{Serialize, Deserialize};
@@ -5,24 +9,46 @@ use std::fmt::{self, Display, Formatter};
use yaxpeax_arch::{Arch, Colorize, Decoder, LengthedInstruction, NoColors, ShowContextual, YaxColors};
-pub struct ConditionedOpcode(pub Opcode, pub ConditionCode);
+pub struct ConditionedOpcode(pub Opcode, pub bool, pub ConditionCode);
impl Display for ConditionedOpcode {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
- write!(f, "{}{}", self.0, self.1)
+ write!(f, "{}{}{}", self.0, if self.1 { "s" } else { "" }, self.2)
}
}
pub struct NoContext;
+fn reg_name_colorize<C: fmt::Display, Y: YaxColors<C>>(reg: Reg, colors: &Y) -> impl fmt::Display {
+ match reg.number() {
+ 0 => colors.register("r0"),
+ 1 => colors.register("r1"),
+ 2 => colors.register("r2"),
+ 3 => colors.register("r3"),
+ 4 => colors.register("r4"),
+ 5 => colors.register("r5"),
+ 6 => colors.register("r6"),
+ 7 => colors.register("r7"),
+ 8 => colors.register("r8"),
+ 9 => colors.register("sb"),
+ 10 => colors.register("r10"),
+ 11 => colors.register("fp"),
+ 12 => colors.register("ip"),
+ 13 => colors.register("sp"),
+ 14 => colors.register("lr"),
+ 15 => colors.program_counter("pc"),
+ _ => { unreachable!(); }
+ }
+}
+
#[allow(non_snake_case)]
impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u32, NoContext, Color, T, Y> for Instruction {
fn contextualize(&self, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
match self.opcode {
Opcode::LDR(true, false, false) => {
match self.operands {
- Operands::TwoRegImm(13, Rt, 4) => {
- ConditionedOpcode(Opcode::POP, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Rt), Operand::RegDisp(Reg { bits: 13 }, 4), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(Opcode::POP, self.s, self.condition).colorize(colors, out)?;
return write!(out, " {{{}}}", reg_name_colorize(Rt, colors));
},
_ => {}
@@ -30,8 +56,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
},
Opcode::STR(false, true, true) => {
match self.operands {
- Operands::TwoRegImm(13, Rt, 4) => {
- ConditionedOpcode(Opcode::PUSH, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Rt), Operand::RegDisp(Reg { bits: 13 }, 4), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(Opcode::PUSH, self.s, self.condition).colorize(colors, out)?;
return write!(out, " {{{}}}", reg_name_colorize(Rt, colors));
},
_ => {}
@@ -40,8 +66,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
Opcode::LDM(true, false, true, _usermode) => {
// TODO: what indicates usermode in the ARM syntax?
match self.operands {
- Operands::RegRegList(13, list) => {
- ConditionedOpcode(Opcode::POP, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Reg { bits: 13 }), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(Opcode::POP, self.s, self.condition).colorize(colors, out)?;
write!(out, " ")?;
return format_reg_list(out, list, colors);
}
@@ -51,8 +77,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
Opcode::STM(false, true, true, _usermode) => {
// TODO: what indicates usermode in the ARM syntax?
match self.operands {
- Operands::RegRegList(13, list) => {
- ConditionedOpcode(Opcode::PUSH, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Reg { bits: 13 }), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(Opcode::PUSH, self.s, self.condition).colorize(colors, out)?;
write!(out, " ")?;
return format_reg_list(out, list, colors);
}
@@ -67,41 +93,62 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
Opcode::STR(add, pre, wback) |
Opcode::STRB(add, pre, wback) |
Opcode::LDRB(add, pre, wback) => {
- match self.operands {
- Operands::TwoRegImm(Rn, Rt, imm) => {
- ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?;
+ match &self.operands {
+ [Operand::Reg(Rn), Operand::Reg(Rt), Operand::Imm12(imm), Operand::Nothing] => {
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
+ write!(
+ out, " {}, ",
+ reg_name_colorize(*Rt, colors),
+ )?;
+ return format_reg_imm_mem(out, *Rn, *imm, add, pre, wback, colors);
+ }
+ [Operand::Reg(Rn), Operand::RegDisp(Rt, offset), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
write!(
out, " {}, ",
- reg_name_colorize(Rt, colors),
+ reg_name_colorize(*Rn, colors),
)?;
- return format_reg_imm_mem(out, Rn, imm, add, pre, wback, colors);
+ let (add, offset) = if *offset < 0 {
+ (false, -*offset as u16)
+ } else {
+ (true, *offset as u16)
+ };
+ return format_reg_imm_mem(out, *Rt, offset, add, true, false, colors);
}
// TODO: this might not be necessary
- Operands::RegImm(Rt, imm) => {
- ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Rt), Operand::Imm12(imm), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
write!(
out, " {}, ",
- reg_name_colorize(Rt, colors)
+ reg_name_colorize(*Rt, colors)
)?;
- return format_reg_imm_mem(out, 15, imm, add, pre, wback, colors);
+ return format_reg_imm_mem(out, Reg::from_u8(15), *imm, add, pre, wback, colors);
},
- Operands::ThreeOperandWithShift(Rd, Rn, Rm, shift) => {
- ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Rd), Operand::Reg(Rn), Operand::RegShift(shift), Operand::Nothing] => {
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
write!(
out, " {}, ",
- reg_name_colorize(Rn, colors)
+ reg_name_colorize(*Rn, colors)
)?;
- return format_reg_shift_mem(out, Rd, Rm, shift, add, pre, wback, colors);
+ return format_reg_shift_mem(out, *Rd, *shift, add, pre, wback, colors);
}
- _ => { unreachable!(); }
+ [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Rn, offset), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
+ write!(
+ out, " {}, ",
+ reg_name_colorize(*Rt, colors)
+ )?;
+ return format_reg_imm_mem(out, *Rt, *offset, true, false, true, colors);
+ }
+ o => { println!("other str/ldr operands: {:?}", o); unreachable!(); }
}
}
// TODO: [add, pre, usermode]
Opcode::STM(_add, _pre, wback, _usermode) |
Opcode::LDM(_add, _pre, wback, _usermode) => {
match self.operands {
- Operands::RegRegList(Rr, list) => {
- ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?;
+ [Operand::Reg(Rr), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => {
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
write!(
out, " {}{}, ",
reg_name_colorize(Rr, colors),
@@ -116,55 +163,23 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
write!(out, "incomplete: {:#x}", word)
},
_ => {
- ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?;
- match self.operands {
- Operands::RegisterList(list) => {
- write!(out, " ")?;
- format_reg_list(out, list, colors)?;
- },
- Operands::OneOperand(a) => {
- write!(out, " {}", reg_name_colorize(a, colors))?;
- },
- Operands::TwoOperand(a, b) => {
- write!(out, " {}, {}", reg_name_colorize(a, colors), reg_name_colorize(b, colors))?;
- },
- Operands::RegImm(a, imm) => {
- write!(out, " {}, {:#x}", reg_name_colorize(a, colors), imm)?;
- },
- Operands::RegRegList(r, list) => {
- write!(out, " {}, ", reg_name_colorize(r, colors))?;
- format_reg_list(out, list, colors)?;
- },
- Operands::TwoRegImm(_a, _b, _imm) => {
- // TODO:
- write!(out, " <unimplemented>")?;
- },
- Operands::ThreeOperand(a, b, c) => {
- write!(out, " {}, {}, {}", reg_name_colorize(a, colors), reg_name_colorize(b, colors), reg_name_colorize(c, colors))?;
- },
- Operands::ThreeOperandImm(_a, _b, _imm) => {
- // TODO:
- write!(out, " <unimplemented>")?;
- },
- Operands::ThreeOperandWithShift(a, b, c, shift) => {
- write!(out, " {}, {}, ", reg_name_colorize(a, colors), reg_name_colorize(b, colors))?;
- format_shift(out, c, shift, colors)?;
- },
- Operands::MulThreeRegs(a, b, c) => {
- write!(out, " {}, {}, {}", reg_name_colorize(a, colors), reg_name_colorize(b, colors), reg_name_colorize(c, colors))?;
- },
- Operands::MulFourRegs(_a, _b, _c, _d) => {
- // TODO:
- write!(out, " <unimplemented>")?;
- },
- Operands::BranchOffset(imm) => {
- if imm < 0 {
- write!(out, " $-{:#x}", (-imm) * 4)?;
- } else {
- write!(out, " $+{:#x}", imm * 4)?;
- }
+ ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
+ let mut ops = self.operands.iter();
+ if let Some(first_op) = ops.next() {
+ write!(out, " ")?;
+ first_op.colorize(colors, out)?;
+ } else {
+ return Ok(());
+ }
+
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
}
- };
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
Ok(())
}
}
@@ -200,6 +215,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::SBC |
Opcode::RSC |
+ Opcode::CLZ |
+
Opcode::MUL |
Opcode::MLA |
Opcode::UMAAL |
@@ -207,7 +224,12 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::UMULL |
Opcode::UMLAL |
Opcode::SMULL |
- Opcode::SMLAL => { write!(out, "{}", colors.arithmetic_op(self)) },
+ Opcode::SMUL(_, _) |
+ Opcode::SMAL(_, _) |
+ Opcode::SMLA(_, _) |
+ Opcode::SMLAW(_) |
+ Opcode::SMLAL |
+ Opcode::SMLAL_halfword(_, _) => { write!(out, "{}", colors.arithmetic_op(self)) },
Opcode::PUSH |
Opcode::POP => { write!(out, "{}", colors.stack_op(self)) },
@@ -243,6 +265,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::STRBT(_) |
Opcode::SWP |
Opcode::SWPB |
+ Opcode::MSR |
+ Opcode::MRS |
Opcode::MOV |
Opcode::MVN => { write!(out, "{}", colors.data_op(self)) },
}
@@ -261,6 +285,7 @@ impl Display for Opcode {
Opcode::BLX => { write!(f, "blx") },
Opcode::BX => { write!(f, "bx") },
Opcode::BXJ => { write!(f, "bxj") },
+ Opcode::CLZ => { write!(f, "clz") },
Opcode::AND => { write!(f, "and") },
Opcode::EOR => { write!(f, "eor") },
Opcode::SUB => { write!(f, "sub") },
@@ -275,6 +300,8 @@ impl Display for Opcode {
Opcode::CMN => { write!(f, "cmn") },
Opcode::ORR => { write!(f, "orr") },
Opcode::MOV => { write!(f, "mov") },
+ Opcode::MSR => { write!(f, "msr") },
+ Opcode::MRS => { write!(f, "mrs") },
Opcode::BIC => { write!(f, "bic") },
Opcode::MVN => { write!(f, "mvn") },
Opcode::LSL => { write!(f, "lsl") },
@@ -316,7 +343,22 @@ impl Display for Opcode {
Opcode::UMULL => { write!(f, "umull") },
Opcode::UMLAL => { write!(f, "umlal") },
Opcode::SMULL => { write!(f, "smull") },
- Opcode::SMLAL => { write!(f, "smlal") }
+ Opcode::SMLA(first, second) => {
+ write!(f, "smla{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" })
+ }
+ Opcode::SMLAL => { write!(f, "smlal") },
+ Opcode::SMLAL_halfword(first, second) => {
+ write!(f, "smlal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" })
+ }
+ Opcode::SMUL(first, second) => {
+ write!(f, "smul{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" })
+ },
+ Opcode::SMAL(first, second) => {
+ write!(f, "smal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" })
+ },
+ Opcode::SMLAW(second) => {
+ write!(f, "smlaw{}", if *second { "t" } else { "b" })
+ },
}
}
}
@@ -360,6 +402,9 @@ pub enum Opcode {
RRX,
ROR,
ADR,
+ MSR,
+ MRS,
+ CLZ,
LDREXH,
STREXH,
LDREXB,
@@ -387,7 +432,12 @@ pub enum Opcode {
UMULL,
UMLAL,
SMULL,
- SMLAL
+ SMUL(bool, bool),
+ SMLA(bool, bool),
+ SMLAL,
+ SMLAL_halfword(bool, bool),
+ SMAL(bool, bool),
+ SMLAW(bool),
}
static DATA_PROCESSING_OPCODES: [Opcode; 16] = [
@@ -410,32 +460,178 @@ static DATA_PROCESSING_OPCODES: [Opcode; 16] = [
];
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub enum ShiftSpec {
- Immediate(u8),
- Register(u8)
+#[repr(transparent)]
+pub struct RegShift {
+ data: u16
+}
+
+impl RegShift {
+ fn into_shift(&self) -> RegShiftStyle {
+ if self.data & 0b1000 == 0 {
+ RegShiftStyle::RegImm(RegImmShift { data: self.data })
+ } else {
+ RegShiftStyle::RegReg(RegRegShift { data: self.data })
+ }
+ }
+
+ fn from(data: u16) -> Self {
+ RegShift { data }
+ }
+}
+
+pub enum RegShiftStyle {
+ RegImm(RegImmShift),
+ RegReg(RegRegShift),
+}
+
+#[repr(transparent)]
+pub struct RegRegShift {
+ data: u16
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub enum ShiftStyle {
+ LSL = 0,
+ LSR = 1,
+ ASR = 2,
+ ROR = 3,
+}
+
+impl ShiftStyle {
+ fn from(bits: u8) -> ShiftStyle {
+ match bits {
+ 0b00 => ShiftStyle::LSL,
+ 0b01 => ShiftStyle::LSR,
+ 0b10 => ShiftStyle::ASR,
+ 0b11 => ShiftStyle::ROR,
+ _ => unreachable!("bad ShiftStyle index")
+ }
+ }
+}
+
+impl RegRegShift {
+ pub fn shifter(&self) -> Reg {
+ Reg::from_u8((self.data >> 8) as u8 & 0b1111)
+ }
+ pub fn stype(&self) -> ShiftStyle {
+ ShiftStyle::from((self.data >> 5) as u8 & 0b11)
+ }
+ pub fn shiftee(&self) -> Reg {
+ Reg::from_u8(self.data as u8 & 0b1111)
+ }
+}
+
+#[repr(transparent)]
+pub struct RegImmShift {
+ data: u16
+}
+
+impl RegImmShift {
+ pub fn imm(&self) -> u8 {
+ (self.data >> 7) as u8 & 0b11111
+ }
+ pub fn stype(&self) -> ShiftStyle {
+ ShiftStyle::from((self.data >> 5) as u8 & 0b11)
+ }
+ pub fn shiftee(&self) -> Reg {
+ Reg::from_u8(self.data as u8 & 0b1111)
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct Reg {
+ bits: u8
+}
+
+impl Reg {
+ pub fn from_u8(bits: u8) -> Reg {
+ if bits > 0b1111 {
+ panic!("register number out of range");
+ }
+
+ Reg { bits }
+ }
+
+ pub fn number(&self) -> u8 {
+ self.bits
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum Operands {
- RegisterList(u16),
- OneOperand(u8),
- TwoOperand(u8, u8),
- RegImm(u8, u32),
- RegRegList(u8, u16),
- TwoRegImm(u8, u8, u32),
- ThreeOperand(u8, u8, u8),
- ThreeOperandImm(u8, u8, u16),
- ThreeOperandWithShift(u8, u8, u8, ShiftSpec),
- MulThreeRegs(u8, u8, u8),
- MulFourRegs(u8, u8, u8, u8),
- BranchOffset(i32)
+pub enum Operand {
+ Reg(Reg),
+ RegList(u16),
+ RegDeref(Reg),
+ RegDisp(Reg, i16),
+ RegShift(RegShift),
+ RegDerefRegShift(RegShift),
+ RegDerefPostindexOffset(Reg, u16),
+ RegDerefPostindexReg(Reg, Reg),
+ Imm12(u16),
+ Imm32(u32),
+ BranchOffset(i32),
+ ASPR,
+ CSPR,
+ SPSR,
+ Nothing,
+}
+
+impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color, Y> for Operand {
+ fn colorize(&self, colors: &Y, f: &mut T) -> fmt::Result {
+ match self {
+ Operand::RegList(list) => {
+ format_reg_list(f, *list, colors)
+ }
+ Operand::Reg(reg) => {
+ write!(f, "{}", reg_name_colorize(*reg, colors))
+ }
+ Operand::RegDeref(reg) => {
+ write!(f, "[{}]", reg_name_colorize(*reg, colors))
+ }
+ Operand::RegDisp(reg, imm) => {
+ write!(f, "[{} + {:#x}]", reg_name_colorize(*reg, colors), imm)
+ }
+ Operand::RegShift(shift) => {
+ format_shift(f, *shift, colors)
+ }
+ Operand::RegDerefRegShift(shift) => {
+ write!(f, "[")?;
+ format_shift(f, *shift, colors)?;
+ write!(f, "]")
+ }
+ Operand::RegDerefPostindexOffset(reg, offs) => {
+ write!(f, "[{}, {:#x}]", reg_name_colorize(*reg, colors), offs)
+ }
+ Operand::RegDerefPostindexReg(reg, offsreg) => {
+ write!(f, "[{}, {}]", reg_name_colorize(*reg, colors), reg_name_colorize(*offsreg, colors))
+ }
+ Operand::Imm12(imm) => {
+ write!(f, "{:#x}", imm)
+ }
+ Operand::Imm32(imm) => {
+ write!(f, "{:#x}", imm)
+ }
+ Operand::BranchOffset(imm) => {
+ if *imm < 0 {
+ write!(f, " $-{:#x}", (-*imm) * 4)
+ } else {
+ write!(f, " $+{:#x}", *imm * 4)
+ }
+ }
+ Operand::ASPR => { write!(f, "aspr") },
+ Operand::CSPR => { write!(f, "cspr") },
+ Operand::SPSR => { write!(f, "spsr") },
+ Operand::Nothing => { panic!("tried to print Nothing operand") },
+ }
+ }
}
#[derive(Debug, PartialEq, Eq)]
pub struct Instruction {
pub condition: ConditionCode,
pub opcode: Opcode,
- pub operands: Operands,
+ pub operands: [Operand; 4],
pub s: bool
}
@@ -474,7 +670,7 @@ impl Default for Instruction {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::Invalid,
- operands: Operands::BranchOffset(0),
+ operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing],
s: false
}
}
@@ -499,7 +695,7 @@ fn format_reg_list<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, m
} else {
tail = true;
}
- write!(f, "{}", reg_name_colorize(i, colors))?;
+ write!(f, "{}", reg_name_colorize(Reg::from_u8(i), colors))?;
}
i += 1;
list >>= 1;
@@ -508,46 +704,42 @@ fn format_reg_list<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, m
}
#[allow(non_snake_case)]
-fn format_shift<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rm: u8, shift: ShiftSpec, colors: &Y) -> Result<(), fmt::Error> {
- fn shift_tpe_to_str(tpe: u8) -> &'static str {
+fn format_shift<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, shift: RegShift, colors: &Y) -> Result<(), fmt::Error> {
+ fn shift_tpe_to_str(tpe: ShiftStyle) -> &'static str {
match tpe {
- 0b00 => "lsl",
- 0b01 => "lsr",
- 0b10 => "asr",
- 0b11 => "ror",
- _ => { unreachable!(); }
+ ShiftStyle::LSL => "lsl",
+ ShiftStyle::LSR => "lsr",
+ ShiftStyle::ASR => "asr",
+ ShiftStyle::ROR => "ror",
}
}
- match shift {
- ShiftSpec::Immediate(0) => {
- write!(f, "{}", reg_name_colorize(Rm, colors))
- },
- ShiftSpec::Immediate(v) => {
- let tpe = v & 0x3;
- let imm = v >> 2;
- write!(f, "{}, {} {}", reg_name_colorize(Rm, colors), shift_tpe_to_str(tpe), imm)
- },
- ShiftSpec::Register(v) => {
- let tpe = v & 0x3;
- let Rs = v >> 3;
- write!(f, "{}, {} {}", reg_name_colorize(Rm, colors), shift_tpe_to_str(tpe), reg_name_colorize(Rs, colors))
+ match shift.into_shift() {
+ RegShiftStyle::RegImm(imm_shift) => {
+ if imm_shift.imm() == 0 && imm_shift.stype() == ShiftStyle::LSL {
+ write!(f, "{}", reg_name_colorize(imm_shift.shiftee(), colors))
+ } else {
+ write!(f, "{}, {} {}", reg_name_colorize(imm_shift.shiftee(), colors), shift_tpe_to_str(imm_shift.stype()), imm_shift.imm())
+ }
+ }
+ RegShiftStyle::RegReg(reg_shift) => {
+ write!(f, "{}, {} {}", reg_name_colorize(reg_shift.shiftee(), colors), shift_tpe_to_str(reg_shift.stype()), reg_name_colorize(reg_shift.shifter(), colors))
},
}
}
#[allow(non_snake_case)]
-fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rd: u8, Rm: u8, shift: ShiftSpec, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> {
+fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rd: Reg, shift: RegShift, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> {
let op = if add { "" } else { "-" };
match (pre, wback) {
(true, true) => {
write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?;
- format_shift(f, Rm, shift, colors)?;
+ format_shift(f, shift, colors)?;
write!(f, "]!")
},
(true, false) => {
write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?;
- format_shift(f, Rm, shift, colors)?;
+ format_shift(f, shift, colors)?;
write!(f, "]")
},
(false, true) => {
@@ -555,13 +747,13 @@ fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut
},
(false, false) => {
write!(f, "[{}], {}", reg_name_colorize(Rd, colors), op)?;
- format_shift(f, Rm, shift, colors)
+ format_shift(f, shift, colors)
}
}
}
#[allow(non_snake_case)]
-fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rn: u8, imm: u32, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> {
+fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rn: Reg, imm: u16, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> {
if imm != 0 {
let op = if add { "" } else { "-" };
@@ -596,27 +788,6 @@ fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T
}
}
}
-fn reg_name_colorize<C: fmt::Display, Y: YaxColors<C>>(num: u8, colors: &Y) -> impl fmt::Display {
- match num {
- 0 => colors.register("r0"),
- 1 => colors.register("r1"),
- 2 => colors.register("r2"),
- 3 => colors.register("r3"),
- 4 => colors.register("r4"),
- 5 => colors.register("r5"),
- 6 => colors.register("r6"),
- 7 => colors.register("r7"),
- 8 => colors.register("r8"),
- 9 => colors.register("sb"),
- 10 => colors.register("r10"),
- 11 => colors.register("fp"),
- 12 => colors.register("ip"),
- 13 => colors.register("sp"),
- 14 => colors.register("lr"),
- 15 => colors.program_counter("pc"),
- _ => { unreachable!(); }
- }
-}
impl Display for Instruction {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
@@ -703,7 +874,9 @@ impl ConditionCode {
}
#[derive(Default, Debug)]
-pub struct InstDecoder {}
+pub struct InstDecoder {
+ system_mode: bool
+}
#[allow(non_snake_case)]
impl Decoder<Instruction> for InstDecoder {
@@ -743,11 +916,16 @@ impl Decoder<Instruction> for InstDecoder {
0b01 => {
inst.opcode = Opcode::BLX;
let operand = ((word & 0xffffff) as i32) << 8 >> 6;
- inst.operands = Operands::BranchOffset(
- operand | (
- ((word >> 23) & 0b10) as i32
- )
- );
+ inst.operands = [
+ Operand::BranchOffset(
+ operand | (
+ ((word >> 23) & 0b10) as i32
+ )
+ ),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
0b10 => {
inst.opcode = Opcode::Incomplete(word);
@@ -801,11 +979,21 @@ impl Decoder<Instruction> for InstDecoder {
match op {
0b000 => {
inst.opcode = Opcode::MUL;
- inst.operands = Operands::MulThreeRegs(R[3], R[0], R[1]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ Operand::Nothing,
+ ];
},
0b001 => {
inst.opcode = Opcode::MLA;
- inst.operands = Operands::MulFourRegs(R[3], R[0], R[1], R[2]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ Operand::Reg(Reg::from_u8(R[2])),
+ ];
},
0b010 => {
if s {
@@ -813,7 +1001,12 @@ impl Decoder<Instruction> for InstDecoder {
return Err(DecodeError::InvalidOpcode);
}
inst.opcode = Opcode::UMAAL;
- inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[2])),
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ ];
},
0b011 => {
if s {
@@ -821,23 +1014,48 @@ impl Decoder<Instruction> for InstDecoder {
return Err(DecodeError::InvalidOpcode);
}
inst.opcode = Opcode::MLS;
- inst.operands = Operands::MulFourRegs(R[3], R[0], R[1], R[2]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ Operand::Reg(Reg::from_u8(R[2])),
+ ];
}
0b100 => {
inst.opcode = Opcode::UMULL;
- inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[2])),
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ ];
}
0b101 => {
inst.opcode = Opcode::UMLAL;
- inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[2])),
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ ];
}
0b110 => {
inst.opcode = Opcode::SMULL;
- inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[2])),
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ ];
}
0b111 => {
inst.opcode = Opcode::SMLAL;
- inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(R[2])),
+ Operand::Reg(Reg::from_u8(R[3])),
+ Operand::Reg(Reg::from_u8(R[0])),
+ Operand::Reg(Reg::from_u8(R[1])),
+ ];
}
_ => { unreachable!(format!("mul upcode: {:x}", op)) }
}
@@ -859,16 +1077,20 @@ impl Decoder<Instruction> for InstDecoder {
let flags = (word & 0x1f) as u8;
(flags, Rn, Rd, HiOffset, op, LoOffset)
};
- println!("{:032b}", word);
- println!(" {:05b}|{:04b}|{:04b}|{:04b}|1{:02b}1|{:04b}", flags, Rn, Rd, HiOffset, op, LoOffset);
match op {
0b00 => {
// |c o n d|0 0 0 1|x x x x x x x x x x x x x x x x|1 0 0 1|x x x x|
// this is swp or {ld,st}ex, conditional on bit 23
+ // see page A5-203
match flags {
0b10000 => {
inst.opcode = Opcode::SWP;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ ];
},
0b10001 | 0b10010 | 0b10011 => {
inst.opcode = Opcode::Invalid;
@@ -876,43 +1098,98 @@ impl Decoder<Instruction> for InstDecoder {
}
0b10100 => {
inst.opcode = Opcode::SWPB;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ ];
},
0b10101 | 0b10110 | 0b10111 => {
inst.opcode = Opcode::Invalid;
return Err(DecodeError::InvalidOpcode);
}
0b11000 => {
+ // TODO: flag v6
inst.opcode = Opcode::STREX;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ ];
}
0b11001 => {
+ // TODO: flag v6
inst.opcode = Opcode::LDREX;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
0b11010 => {
inst.opcode = Opcode::STREXD;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ if LoOffset == 0b1110 || (LoOffset & 1 == 1) || Rd == 15 || Rn == 15 {
+ // TODO: "unpredictable"
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::Reg(Reg::from_u8(LoOffset + 1)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ ];
}
0b11011 => {
inst.opcode = Opcode::LDREXD;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ if LoOffset == 0b1110 || (LoOffset & 1 == 1) || Rn == 15 {
+ // TODO: "unpredictable"
+ return Err(DecodeError::InvalidOperand);
+ }
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::Reg(Reg::from_u8(LoOffset + 1)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ ];
}
0b11100 => {
inst.opcode = Opcode::STREXB;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ ];
}
0b11101 => {
inst.opcode = Opcode::LDREXB;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
0b11110 => {
inst.opcode = Opcode::STREXH;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ ];
}
0b11111 => {
inst.opcode = Opcode::LDREXH;
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(LoOffset)),
+ Operand::RegDeref(Reg::from_u8(Rn)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
_ => {
/*
@@ -935,24 +1212,44 @@ impl Decoder<Instruction> for InstDecoder {
0b00010 => {
// inst.opcode = Opcode::STRHT_sub;
inst.opcode = Opcode::Incomplete(word);
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rd), Reg::from_u8(LoOffset)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
0b01010 => {
// inst.opcode = Opcode::STRHT_add;
inst.opcode = Opcode::Incomplete(word);
- inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rd), Reg::from_u8(LoOffset)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
0b00110 => {
// inst.opcode = Opcode::STRHT_sub;
inst.opcode = Opcode::Incomplete(word);
let imm = (HiOffset << 4) as u16 | LoOffset as u16;
- inst.operands = Operands::ThreeOperandImm(Rn, Rd, imm);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rd), imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
0b01110 => {
// inst.opcode = Opcode::STRHT_add;
inst.opcode = Opcode::Incomplete(word);
let imm = (HiOffset << 4) as u16 | LoOffset as u16;
- inst.operands = Operands::ThreeOperandImm(Rn, Rd, imm);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rd), imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
_ => {
return Err(DecodeError::Incomplete);
@@ -979,9 +1276,10 @@ impl Decoder<Instruction> for InstDecoder {
}
} else {
// we know this is data processing with imm or reg shift, OR
- // misc instructions in Figure A3-4
+ // misc instructions in Figure A5-4
if s == false && opcode >= 0b1000 && opcode < 0b1100 {
+ // Data-processing and miscellaneous instructions on page A5-194
let op2 = ((word >> 4) & 0x0f) as u8;
// the instruction looks like
// |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x|
@@ -991,34 +1289,140 @@ impl Decoder<Instruction> for InstDecoder {
// misc instructions (page A5-194)
match op2 {
0b000 => {
-
+ if word & 0b1000000000 != 0 {
+ // TODO: ARMv7VE flag
+ let _M = (((word >> 9) & 1) << 4) | ((word >> 16) & 0x0f);
+ let _R = (word >> 22) & 1;
+
+ if opcode & 0b01 == 0b01 {
+ inst.opcode = Opcode::MSR;
+ inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
+ return Err(DecodeError::Incomplete);
+ } else {
+ inst.opcode = Opcode::MRS;
+ inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111));
+ return Err(DecodeError::Incomplete);
+ }
+ } else {
+ match opcode & 0b11 {
+ 0b00 |
+ 0b10 => {
+ inst.opcode = Opcode::MRS;
+ /*
+ let src = if self.system_mode {
+ let R = (word >> 22) & 1 != 0;
+ if R {
+ Operand::SPSR
+ } else {
+ Operand::CSPR
+ }
+ } else {
+ Operand::ASPR
+ };
+ */
+ inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111));
+ // TODO:
+ return Err(DecodeError::Incomplete);
+ }
+ 0b01 => {
+ inst.opcode = Opcode::MSR;
+ let mask = (word >> 16) & 0b1111;
+ if self.system_mode {
+ let R = (word >> 22) & 1 != 0;
+ let _dest = if R {
+ Operand::SPSR
+ } else {
+ Operand::CSPR
+ };
+ // inst.operands[0] = /* masked write to dest */
+ inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
+ // TODO:
+ return Err(DecodeError::Incomplete);
+ } else {
+ if mask & 0b11 == 0 {
+ inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
+ // TODO:
+ // inst.operands[0] = /* derived from mask >> 2 */
+ return Err(DecodeError::Incomplete);
+ } else {
+ // MSR with op == 01, op != xx00
+ return Err(DecodeError::InvalidOperand);
+ }
+ }
+ },
+ 0b11 => {
+ if !self.system_mode {
+ return Err(DecodeError::InvalidOperand);
+ }
+
+ inst.opcode = Opcode::MSR;
+ inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111));
+ // TODO:
+ return Err(DecodeError::Incomplete);
+ }
+ _ => {
+ unreachable!();
+ }
+ }
+ }
},
0b001 => {
-
+ match opcode & 0b11 {
+ 0b01 => {
+ inst.opcode = Opcode::BX;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ 0b11 => {
+ inst.opcode = Opcode::CLZ;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ _ => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
},
0b010 => {
-
+ return Err(DecodeError::InvalidOpcode);
},
0b011 => {
if opcode & 0b11 == 0b01 {
inst.opcode = Opcode::BLX;
- inst.operands = Operands::OneOperand((word & 0x0f) as u8);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(word as u8 & 0x0f)),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
return Ok(());
} else {
return Err(DecodeError::InvalidOpcode);
}
},
0b100 => {
- inst.opcode = Opcode::Incomplete(word);
- return Ok(());
+ // no row for op2 == 0b100 in table A5-14 (page A5-203)
+ return Err(DecodeError::InvalidOpcode);
},
0b101 => {
-
+ // TODO: "Saturating addition and subtraction" page A5-200
+ return Err(DecodeError::Incomplete);
}
0b110 => {
+ // TODO: "ERET" page B9-1968
+ return Err(DecodeError::Incomplete);
},
0b111 => {
-
+ // TODO: "BKPT, HVC, SMC" from table A5-14/page A5-203
+ return Err(DecodeError::Incomplete);
},
_ => {
unreachable!();
@@ -1026,11 +1430,84 @@ impl Decoder<Instruction> for InstDecoder {
}
} else {
// |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x|1|x x|x|x x x x|
- // multiply and multiply-accumulate
- inst.opcode = Opcode::Incomplete(word);
- return Ok(());
+ // Halfword multiply and multiply accumulate on page A5-200
+ match (word >> 21) & 0b11 {
+ 0b00 => {
+ let Rn_b = ((word >> 6) & 1) == 0;
+ let Rm_b = ((word >> 5) & 1) == 0;
+ inst.opcode = Opcode::SMLA(Rn_b, Rm_b);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)),
+ ];
+ return Ok(());
+ },
+ 0b01 => {
+ if word & 0b10000 == 0 {
+ // SMLAWB, SMLAWT page A8-631
+ let Rm_b = ((word >> 5) & 1) == 0;
+ inst.opcode = Opcode::SMLAW(Rm_b);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)),
+ ];
+ return Ok(());
+ } else {
+ // SMULWB, SMULWT page A8-649
+ let Rm_b = ((word >> 5) & 1) == 0;
+ inst.opcode = Opcode::SMLAW(Rm_b);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)),
+ Operand::Nothing,
+ ];
+ return Ok(());
+ }
+ }
+ 0b10 => {
+ let Rn_b = ((word >> 6) & 1) == 0;
+ let Rm_b = ((word >> 5) & 1) == 0;
+ inst.opcode = Opcode::SMLAL_halfword(Rn_b, Rm_b);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)),
+ ];
+ if inst.operands[0] == inst.operands[1] {
+ // TODO: this is actually "UNPREDICTABLE" (A8-627)
+ return Err(DecodeError::InvalidOperand);
+ }
+ return Ok(());
+ }
+ 0b11 => {
+ let Rn_b = ((word >> 6) & 1) == 0;
+ let Rm_b = ((word >> 5) & 1) == 0;
+ inst.opcode = Opcode::SMUL(Rn_b, Rm_b);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8(word as u8 & 0b1111)),
+ Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)),
+ Operand::Nothing,
+ ];
+ if inst.operands[0] == inst.operands[1] {
+ // TODO: this is actually "UNPREDICTABLE" (A8-627)
+ return Err(DecodeError::InvalidOperand);
+ }
+ return Ok(());
+ }
+ _ => {
+ unreachable!();
+ }
+ }
}
} else {
+ // `Table A5-3 Data-processing (register) instructions`, not op=10xx0.
if opcode >= 16 {
unreachable!();
}
@@ -1040,45 +1517,63 @@ impl Decoder<Instruction> for InstDecoder {
// at this point we know this is a data processing instruction
// either immediate shift or register shift
if word & 0b00010000 == 0 {
- // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|0|x x x x|
+ // |c o n d|0 0 0|x x x x|0|x x x x|x x x x|x x x x x|x x|0|x x x x|
// interpret the operands as
// | Rn | Rd | shift amount | shift | 0 | Rm |
let (Rn, Rd, shift_spec, Rm) = {
let Rm = (word & 0x0f) as u8;
- let word = word >> 5;
- let shift_spec = (word & 0x7f) as u8;
- let word = word >> 7;
+ let shift_spec = (word & 0xfff) as u16;
+ let word = word >> 12;
let Rd = (word & 0x0f) as u8;
let Rn = ((word >> 4) & 0x0f) as u8;
(Rn, Rd, shift_spec, Rm)
};
- if shift_spec == 0 {
+ if shift_spec & 0xff0 == 0 {
if (0b1101 & opcode) == 0b1101 {
+ if Rn != 0 {
+ // this is really a "should be zero" situation
+ return Err(DecodeError::Incomplete);
+ }
// MOV or MVN
- inst.operands = Operands::TwoOperand(Rd, Rm);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(Rm)),
+ Operand::Nothing,
+ Operand::Nothing
+ ];
} else {
- inst.operands = Operands::ThreeOperand(Rd, Rn, Rm);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(Rn)),
+ Operand::Reg(Reg::from_u8(Rm)),
+ Operand::Nothing
+ ];
}
} else {
- /*
- * TODO: look at how this interacts with mov and mvn
- */
- inst.operands = Operands::ThreeOperandWithShift(Rd, Rn, Rm, ShiftSpec::Immediate(shift_spec));
+ if opcode == 0b1101 && Rn != 0 {
+ // Rn "should" be zero
+ return Err(DecodeError::Incomplete);
+ }
+
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(Rn)),
+ Operand::RegShift(RegShift::from(shift_spec)),
+ Operand::Nothing
+ ];
}
} else {
// known 0 because it and bit 5 are not both 1 --v
// |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x 0|x x|1|x x x x|
// interpret the operands as
// | Rn | Rd | Rs | 0 | shift | 1 | Rm |
- let (Rn, Rd, shift_spec, Rm) = {
- let Rm = (word & 0x0f) as u8;
- let word = word >> 5;
- let shift_spec = (word & 0x7f) as u8;
- let word = word >> 7;
+ let (Rn, Rd, shift_spec) = {
+ let shift_spec = (word & 0xfff) as u16;
+ let word = word >> 12;
let Rd = (word & 0x0f) as u8;
let Rn = ((word >> 4) & 0x0f) as u8;
- (Rn, Rd, shift_spec, Rm)
+ (Rn, Rd, shift_spec)
};
// page A5-200 indicates that saturating add and subtract should be
// here?
@@ -1087,7 +1582,13 @@ impl Decoder<Instruction> for InstDecoder {
inst.opcode = Opcode::Invalid;
return Err(DecodeError::InvalidOpcode);
} else {
- inst.operands = Operands::ThreeOperandWithShift(Rd, Rn, Rm, ShiftSpec::Register(shift_spec));
+ // TODO: unsure about this RegShift...
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(Rn)),
+ Operand::RegShift(RegShift::from(shift_spec)),
+ Operand::Nothing,
+ ];
}
}
}
@@ -1106,34 +1607,46 @@ impl Decoder<Instruction> for InstDecoder {
};
if s == false && opcode >= 0b1000 && opcode < 0b1100 {
// the instruction looks like
- // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x|
- // misc instructions (page A5-194)
+ // |c o n d|0 0 1|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x|
+ // which means 16-bit immediate load, high half immediate load, or MSR (immediate)
+ // + hints.
+ // See table A5-2, page A5-194.
inst.opcode = Opcode::Incomplete(word);
return Ok(());
} else {
+ // Data-processing (immediate)
+ // Page A5-197
if opcode >= 16 {
unreachable!();
}
inst.opcode = DATA_PROCESSING_OPCODES[opcode as usize];
inst.set_s(s);
- let (Rn, imm) = {
- let imm = word & 0x0000ffff;
- let word = word >> 16;
- ((word & 0x0f) as u8, imm)
+ let (Rn, Rd, imm) = {
+ let rot = word & 0x00000f00;
+ let imm = (word & 0x000000ff);
+ let imm = (imm as u32).rotate_right(2 * (rot >> 8));
+ ((word >> 16) as u8 & 0x0f, (word >> 12) as u8 & 0x0f, imm)
};
if (opcode == 0b0010 || opcode == 0b0100) && Rn == 0b1111 {
inst.opcode = Opcode::ADR;
}
match opcode {
0b1101 => {
- inst.operands = Operands::RegImm(
- ((word >> 12) & 0xf) as u8,
- (word & 0x0fff) as u32
- );
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Imm32(imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
_ => {
- inst.operands = Operands::RegImm(Rn, imm);
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rd)),
+ Operand::Reg(Reg::from_u8(Rn)),
+ Operand::Imm32(imm),
+ Operand::Nothing,
+ ];
}
}
@@ -1141,6 +1654,9 @@ impl Decoder<Instruction> for InstDecoder {
/* ... */
}
0b010 => {
+ // Load/store word and unsigned byte
+ // A5.3
+ // Page A5-206
let Rn = ((word >> 16) & 0x0f) as u8;
let op = ((word >> 20) & 0x1f) as u8;
let add = (op & 0b01000) != 0;
@@ -1177,7 +1693,12 @@ impl Decoder<Instruction> for InstDecoder {
0b000 => Opcode::STR(add, pre, wback),
0b001 => {
if Rn == 0b1111 {
- inst.operands = Operands::RegImm(Rt, imm.into());
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::RegDisp(Reg::from_u8(Rn), imm as u16 as i16),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
inst.opcode = Opcode::LDR(add, pre, wback);
return Ok(());
}
@@ -1186,7 +1707,12 @@ impl Decoder<Instruction> for InstDecoder {
0b100 => Opcode::STRB(add, pre, wback),
0b101 => {
if Rn == 0b1111 {
- inst.operands = Operands::RegImm(Rt, imm.into());
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::RegDisp(Reg::from_u8(Rn), imm as u16 as i16),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
inst.opcode = Opcode::LDRB(add, pre, wback);
return Ok(());
}
@@ -1195,7 +1721,12 @@ impl Decoder<Instruction> for InstDecoder {
_ => { unreachable!(); }
};
}
- inst.operands = Operands::TwoRegImm(Rn, Rt, imm.into());
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
},
0b011 => {
// page A5-192 to distinguish the following:
@@ -1220,7 +1751,7 @@ impl Decoder<Instruction> for InstDecoder {
xx1x1 not 0x111 -> LDRB (register)
0x111 -> LDRBT
*/
- let Rn = ((word >> 16) & 0x0f) as u8;
+ let _Rn = ((word >> 16) & 0x0f) as u8;
if (op & 0b10010) == 0b00010 {
let op = op & 0b00111;
// |c o n d|0 1 1|0 x x 1 x|x x x x x x x x x x x x x x x|0|x x x x|
@@ -1255,15 +1786,18 @@ impl Decoder<Instruction> for InstDecoder {
_ => { unreachable!(); }
};
}
- let (Rt, Rm, shift) = {
- let Rm = (word & 0xf) as u8;
- let word = word >> 5;
- let shift = (word & 0x7f) as u8;
- let word = word >> 7;
+ let (Rt, shift) = {
+ let shift = (word & 0xfff) as u16;
+ let word = word >> 12;
let Rt = (word & 0xf) as u8;
- (Rt, Rm, shift)
+ (Rt, shift)
};
- inst.operands = Operands::ThreeOperandWithShift(Rn, Rt, Rm, ShiftSpec::Immediate(shift));
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::RegDerefRegShift(RegShift::from(shift)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
return Ok(());
},
@@ -1277,24 +1811,36 @@ impl Decoder<Instruction> for InstDecoder {
let pre = (op & 0b010000) != 0;
let usermode = (op & 0b000100) != 0;
inst.opcode = if (op & 1) == 0 {
- Opcode::STM(add, pre, wback, usermode)
- } else {
- Opcode::LDM(add, pre, wback, usermode)
- };
- inst.operands = Operands::RegRegList(
- ((word >> 16) & 0xf) as u8,
- (word & 0xffff) as u16
- );
+ Opcode::STM(add, pre, wback, usermode)
+ } else {
+ Opcode::LDM(add, pre, wback, usermode)
+ };
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(((word >> 16) & 0xf) as u8)),
+ Operand::RegList((word & 0xffff) as u16),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
} else if op < 0b110000 {
// 10xxxx
// the + 1 is to compensate for an architecturally-defined initial offset
inst.opcode = Opcode::B;
- inst.operands = Operands::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32);
+ inst.operands = [
+ Operand::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
} else {
// 11xxxx
// the + 1 is to compensate for an architecturally-defined initial offset
inst.opcode = Opcode::BL;
- inst.operands = Operands::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32);
+ inst.operands = [
+ Operand::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
},
0b110 | 0b111 => {
@@ -1322,7 +1868,7 @@ impl Arch for ARMv7 {
type Instruction = Instruction;
type DecodeError = DecodeError;
type Decoder = InstDecoder;
- type Operand = Operands;
+ type Operand = Operand;
}
diff --git a/test/armv7.rs b/test/armv7.rs
index 2a7ebe0..59f6000 100644
--- a/test/armv7.rs
+++ b/test/armv7.rs
@@ -1,5 +1,5 @@
use yaxpeax_arch::{Arch, Decoder, LengthedInstruction};
-use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, Operands, Opcode, ShiftSpec};
+use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, Operand, Opcode, Reg};
fn test_decode(data: [u8; 4], expected: Instruction) {
let instr = <ARMv7 as Arch>::Decoder::default().decode(data.to_vec()).unwrap();
@@ -30,7 +30,12 @@ fn test_decode_str_ldr() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::LDR(true, true, false),
- operands: Operands::RegImm(12, 0x24),
+ operands: [
+ Operand::Reg(Reg::from_u8(12)),
+ Operand::RegDisp(Reg::from_u8(15), 0x24),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -39,7 +44,12 @@ fn test_decode_str_ldr() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::LDR(true, true, false),
- operands: Operands::RegImm(0, 0x10),
+ operands: [
+ Operand::Reg(Reg::from_u8(0)),
+ Operand::RegDisp(Reg::from_u8(15), 0x10),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -48,7 +58,12 @@ fn test_decode_str_ldr() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::STR(false, true, true),
- operands: Operands::TwoRegImm(13, 2, 4),
+ operands: [
+ Operand::Reg(Reg::from_u8(2)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(13), 4),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -57,7 +72,12 @@ fn test_decode_str_ldr() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::STR(false, true, true),
- operands: Operands::TwoRegImm(13, 0, 4),
+ operands: [
+ Operand::Reg(Reg::from_u8(0)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(13), 4),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -66,7 +86,12 @@ fn test_decode_str_ldr() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::LDR(true, true, false),
- operands: Operands::RegImm(3, 0x14),
+ operands: [
+ Operand::Reg(Reg::from_u8(3)),
+ Operand::RegDisp(Reg::from_u8(15), 0x14),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -75,15 +100,84 @@ fn test_decode_str_ldr() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::LDR(true, true, false),
- operands: Operands::RegImm(2, 0x14),
+ operands: [
+ Operand::Reg(Reg::from_u8(2)),
+ Operand::RegDisp(Reg::from_u8(15), 0x14),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
}
#[test]
+fn test_synchronization() {
+ test_display(
+ [0x94, 0x8f, 0x8a, 0xe1],
+ "strex r8, r4, [r10]"
+ );
+ test_display(
+ [0x9f, 0x8f, 0x9a, 0xe1],
+ "ldrex r8, [r10]"
+ );
+ test_display(
+ [0x94, 0x2f, 0xa4, 0xe1],
+ "strexd r2, r4, r5, [r4]"
+ );
+ test_display(
+ [0x9f, 0x2f, 0xb4, 0xe1],
+ "ldrexd r2, r3, [r4]"
+ );
+ test_display(
+ [0x9f, 0x2f, 0xc4, 0xe1],
+ "strexb r2, pc, [r4]"
+ );
+ test_display(
+ [0x9f, 0x2f, 0xd4, 0xe1],
+ "ldreb r2, [r4]"
+ );
+ test_display(
+ [0x9f, 0x2f, 0xe4, 0xe1],
+ "strexh r2, pc, [r4]"
+ );
+ test_display(
+ [0x9f, 0x2f, 0xf4, 0xe1],
+ "ldrexh r2, [r4]"
+ );
+}
+
+#[test]
+fn test_str() {
+ test_display(
+ [0xb5, 0x53, 0x68, 0xe0],
+ "strht r5, [r8], -0x35"
+ );
+}
+
+#[test]
+fn test_data_imm() {
+ test_display(
+ [0x12, 0x34, 0xa0, 0xe3],
+ "mov r3, 0x12000000"
+ );
+ test_display(
+ [0x12, 0x44, 0x9c, 0xe3],
+ "orrs r4, ip, 0x12000000"
+ );
+}
+
+#[test]
fn test_decode_misc() {
test_display(
+ [0x13, 0x5f, 0x6f, 0xe1],
+ "clz r5, r3"
+ );
+ test_display(
+ [0xc8, 0xac, 0x0b, 0xe1],
+ "smlabt fp, r8, ip, r10"
+ );
+ test_display(
[0x32, 0xff, 0x2f, 0xe1],
"blx r2"
);
@@ -104,7 +198,12 @@ fn test_decode_pop() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::LDR(true, false, false),
- operands: Operands::TwoRegImm(13, 1, 4),
+ operands: [
+ Operand::Reg(Reg::from_u8(1)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(13), 0x4),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -117,7 +216,12 @@ fn test_decode_pop() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::STM(false, true, true, false),
- operands: Operands::RegRegList(13, 16624),
+ operands: [
+ Operand::Reg(Reg::from_u8(13)),
+ Operand::RegList(16624),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -130,7 +234,12 @@ fn test_decode_pop() {
Instruction {
condition: ConditionCode::NE,
opcode: Opcode::LDM(true, false, true, false),
- operands: Operands::RegRegList(13, 33008),
+ operands: [
+ Operand::Reg(Reg::from_u8(13)),
+ Operand::RegList(33008),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -147,7 +256,12 @@ fn test_decode_mov() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::MOV,
- operands: Operands::TwoOperand(2, 13),
+ operands: [
+ Operand::Reg(Reg::from_u8(2)),
+ Operand::Reg(Reg::from_u8(13)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -156,7 +270,12 @@ fn test_decode_mov() {
Instruction {
condition: ConditionCode::AL,
opcode: Opcode::MOV,
- operands: Operands::RegImm(11, 0),
+ operands: [
+ Operand::Reg(Reg::from_u8(11)),
+ Operand::Imm32(0),
+ Operand::Nothing,
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -164,6 +283,7 @@ fn test_decode_mov() {
#[test]
fn test_decode_arithmetic() {
+ /*
test_decode(
[0x18, 0x1d, 0x00, 0x00],
Instruction {
@@ -175,10 +295,12 @@ fn test_decode_arithmetic() {
s: false
}
);
+ */
test_display(
[0x18, 0x1d, 0x00, 0x00],
"andeq r1, r0, r8, lsl sp",
);
+ /*
test_decode(
[0x03, 0x30, 0x8f, 0xe0],
Instruction {
@@ -215,6 +337,7 @@ fn test_decode_arithmetic() {
s: false
}
);
+ */
}
#[test]
@@ -224,7 +347,12 @@ fn test_decode_mul() {
Instruction {
condition: ConditionCode::EQ,
opcode: Opcode::MUL,
- operands: Operands::MulThreeRegs(11, 12, 13),
+ operands: [
+ Operand::Reg(Reg::from_u8(11)),
+ Operand::Reg(Reg::from_u8(12)),
+ Operand::Reg(Reg::from_u8(13)),
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -233,7 +361,12 @@ fn test_decode_mul() {
Instruction {
condition: ConditionCode::EQ,
opcode: Opcode::MUL,
- operands: Operands::MulThreeRegs(9, 0, 9),
+ operands: [
+ Operand::Reg(Reg::from_u8(9)),
+ Operand::Reg(Reg::from_u8(0)),
+ Operand::Reg(Reg::from_u8(9)),
+ Operand::Nothing,
+ ],
s: false
}
);
@@ -242,7 +375,12 @@ fn test_decode_mul() {
Instruction {
condition: ConditionCode::EQ,
opcode: Opcode::MUL,
- operands: Operands::MulThreeRegs(9, 4, 9),
+ operands: [
+ Operand::Reg(Reg::from_u8(9)),
+ Operand::Reg(Reg::from_u8(4)),
+ Operand::Reg(Reg::from_u8(9)),
+ Operand::Nothing,
+ ],
s: false
}
);