aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-02-17 18:33:11 -0800
committeriximeow <me@iximeow.net>2020-02-17 18:33:11 -0800
commit61541d356e6c9d378a85697778685b410fcbee1b (patch)
treeb590df784a1219d94d18f211785bd20876d7cfb4
parent685ce723ddf0bd9dcc0998f44a73f5ae07fdc0c5 (diff)
entirely replace armv7 operands
Operands was an enum covering all forms of operands and data for each operand for all instructions. this is hard to iterate, and hard to work with when a single operand is the one of interest, so it's now replaced by an array of `Operand` enum instances like other architectures. in the course of this change, several forms of decoding are broken, while adding support for some earlier-unsupported multiplies and instructions like msr and clz also clearly note which document the comments mentioning page numbers/figures is referencing
-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
}
);