aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-03-15 04:43:49 -0700
committeriximeow <me@iximeow.net>2020-03-15 04:43:49 -0700
commit0eef8df038ff3a83f31aeeb3c58d5bf652c400ec (patch)
treeac0b14aa49c8fba5f72bae4937fc2cf0b0a06a9d
parentf4f2c1befecfc841b2a7f5906492b94b16560b9c (diff)
armv7 support through neon, some still missing (mainly coproc instrs)
-rw-r--r--src/armv7.rs1728
-rw-r--r--test/armv7.rs228
2 files changed, 1679 insertions, 277 deletions
diff --git a/src/armv7.rs b/src/armv7.rs
index 20f0189..4b8c608 100644
--- a/src/armv7.rs
+++ b/src/armv7.rs
@@ -45,18 +45,20 @@ fn reg_name_colorize<C: fmt::Display, Y: YaxColors<C>>(reg: Reg, colors: &Y) ->
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) => {
+ Opcode::LDR => {
match self.operands {
- [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Reg { bits: 13 }, 4, true), Operand::Nothing, Operand::Nothing] => {
+ // TODO: should this be PostindexOffset?
+ [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Reg { bits: 13 }, 4, true, false), Operand::Nothing, Operand::Nothing] => {
ConditionedOpcode(Opcode::POP, self.s, self.condition).colorize(colors, out)?;
return write!(out, " {{{}}}", reg_name_colorize(Rt, colors));
},
_ => {}
}
},
- Opcode::STR(false, true, true) => {
+ Opcode::STR => {
match self.operands {
- [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Reg { bits: 13 }, 4, fale), Operand::Nothing, Operand::Nothing] => {
+ // TODO: should this be PreindexOffset?
+ [Operand::Reg(Rt), Operand::RegDerefPreindexOffset(Reg { bits: 13 }, 4, false, true), Operand::Nothing, Operand::Nothing] => {
ConditionedOpcode(Opcode::PUSH, self.s, self.condition).colorize(colors, out)?;
return write!(out, " {{{}}}", reg_name_colorize(Rt, colors));
},
@@ -89,71 +91,6 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
}
match self.opcode {
- Opcode::LDR(add, pre, wback) |
- Opcode::STR(add, pre, wback) |
- Opcode::STRB(add, pre, wback) |
- Opcode::LDRB(add, pre, wback) => {
- 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(*Rn, 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, pre, wback, colors);
- }
- // TODO: this might not be necessary
- [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)
- )?;
- return format_reg_imm_mem(out, Reg::from_u8(15), *imm, add, pre, wback, colors);
- },
- [Operand::Reg(Rn), Operand::Reg(Rd), Operand::RegShift(shift), Operand::Nothing] => {
- ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
- write!(
- out, " {}, ",
- reg_name_colorize(*Rn, colors)
- )?;
- return format_reg_shift_mem(out, *Rd, *shift, add, pre, wback, colors);
- }
- [Operand::Reg(Rn), Operand::RegDerefRegShift(shift), Operand::Nothing, Operand::Nothing] => {
- ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
- write!(
- out, " {}, ",
- reg_name_colorize(*Rn, colors)
- )?;
- write!(out, "[")?;
- format_shift(out, *shift, colors);
- write!(out, "]")?;
- return Ok(());
- }
- [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Rn, offset, add), 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, *add, pre, wback, 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) => {
@@ -173,10 +110,136 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3
Opcode::Incomplete(word) => {
write!(out, "incomplete: {:#x}", word)
},
+ Opcode::STC2L(coproc) => {
+ write!(out, "stc2l p{}", coproc)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ Ok(())
+ }
+ Opcode::STC2(coproc) => {
+ write!(out, "stc2 p{}", coproc)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ Ok(())
+ }
+ Opcode::LDC2(coproc) => {
+ write!(out, "ldc2 p{}", coproc)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ Ok(())
+ }
+ Opcode::LDC2L(coproc) => {
+ write!(out, "ldc2l p{}", coproc)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ Ok(())
+ }
+ Opcode::MRRC2(coproc, opc) => {
+ write!(out, "mrrc2 p{}, {}", coproc, opc)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ Ok(())
+ }
+ Opcode::MCRR2(coproc, opc) => {
+ write!(out, "mcrr2 p{}, {}", coproc, opc)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ Ok(())
+ }
+ Opcode::MRC2(coproc, opc1, opc2) => {
+ write!(out, "mrc2 p{}, {}", coproc, opc1)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ write!(out, ", {}", opc2)?;
+
+ Ok(())
+ }
+ Opcode::MCR2(coproc, opc1, opc2) => {
+ write!(out, "mcr2 p{}, {}", coproc, opc1)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ write!(out, ", {}", opc2)?;
+
+ Ok(())
+ }
+ Opcode::CDP2(coproc, opc1, opc2) => {
+ write!(out, "cdp2 p{}, {}", coproc, opc1)?;
+ let ops = self.operands.iter();
+ for op in ops {
+ if let Operand::Nothing = op {
+ break;
+ }
+ write!(out, ", ")?;
+ op.colorize(colors, out)?;
+ }
+
+ write!(out, ", {}", opc2)?;
+
+ Ok(())
+ }
_ => {
ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?;
let mut ops = self.operands.iter();
if let Some(first_op) = ops.next() {
+ if let Operand::Nothing = first_op {
+ return Ok(());
+ }
write!(out, " ")?;
first_op.colorize(colors, out)?;
} else {
@@ -226,6 +289,11 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::SBC |
Opcode::RSC |
+ Opcode::QADD |
+ Opcode::QSUB |
+ Opcode::QDADD |
+ Opcode::QDSUB |
+
Opcode::CLZ |
Opcode::MUL |
@@ -250,6 +318,12 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::CMP |
Opcode::CMN => { write!(out, "{}", colors.comparison_op(self)) },
+ Opcode::LDRSH |
+ Opcode::LDRSHT |
+ Opcode::LDRSB |
+ Opcode::LDRSBT |
+ Opcode::STRD |
+ Opcode::LDRD |
Opcode::LDREXH |
Opcode::STREXH |
Opcode::LDREXB |
@@ -266,24 +340,42 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::STM(false, true, _, _) |
Opcode::STM(true, false, _, _) |
Opcode::STM(true, true, _, _) |
- Opcode::LDR(_, _, _) |
- Opcode::STR(_, _, _) |
- Opcode::LDRH(_, _, _) |
- Opcode::STRH(_, _, _) |
- Opcode::LDRB(_, _, _) |
- Opcode::STRB(_, _, _) |
- Opcode::LDRT(_) |
- Opcode::STRT(_) |
- Opcode::LDRHT(_) |
- Opcode::STRHT(_) |
- Opcode::LDRBT(_) |
- Opcode::STRBT(_) |
+ Opcode::LDR |
+ Opcode::STR |
+ Opcode::LDRH |
+ Opcode::STRH |
+ Opcode::LDRB |
+ Opcode::STRB |
+ Opcode::LDRT |
+ Opcode::STRT |
+ Opcode::LDRHT |
+ Opcode::STRHT |
+ Opcode::LDRBT |
+ Opcode::STRBT |
Opcode::SWP |
Opcode::SWPB |
Opcode::MSR |
Opcode::MRS |
Opcode::MOV |
+ Opcode::MOVT |
Opcode::MVN => { write!(out, "{}", colors.data_op(self)) },
+
+ Opcode::SRS(_, _) |
+ Opcode::BKPT => { write!(out, "{}", colors.misc_op(self)) },
+
+ Opcode::ERET |
+ Opcode::RFE(_, _) |
+ Opcode::HVC |
+ Opcode::SMC |
+ Opcode::LDC2(_) |
+ Opcode::LDC2L(_) |
+ Opcode::STC2(_) |
+ Opcode::STC2L(_) |
+ Opcode::MCRR2(_, _) |
+ Opcode::MCR2(_, _, _) |
+ Opcode::MRRC2(_, _) |
+ Opcode::MRC2(_, _, _) |
+ Opcode::CDP2(_, _, _) => { write!(out, "{}", colors.platform_op(self)) },
}
}
}
@@ -291,6 +383,32 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
impl Display for Opcode {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
+ Opcode::LDRSH => { write!(f, "ldrsh") },
+ Opcode::LDRSHT => { write!(f, "ldrsht") },
+ Opcode::LDRSB => { write!(f, "ldrsb") },
+ Opcode::LDRSBT => { write!(f, "ldrsbt") },
+ Opcode::STRD => { write!(f, "strd") },
+ Opcode::LDRD => { write!(f, "ldrd") },
+ Opcode::LDC2(_) => { write!(f, "ldc2") },
+ Opcode::LDC2L(_) => { write!(f, "ldc2l") },
+ Opcode::STC2(_) => { write!(f, "stc2") },
+ Opcode::STC2L(_) => { write!(f, "stc2l") },
+ Opcode::MCRR2(_, _) => { write!(f, "mcrr2") },
+ Opcode::MCR2(_, _, _) => { write!(f, "mcr2") },
+ Opcode::MRRC2(_, _) => { write!(f, "mrrc2") },
+ Opcode::MRC2(_, _, _) => { write!(f, "mrc2") },
+ Opcode::CDP2(_, _, _) => { write!(f, "cdp2") },
+ Opcode::SRS(p, u) => { write!(f, "srs{}{}", if *u { "i" } else { "d" }, if *p { "b" } else { "a" }) },
+ Opcode::RFE(p, u) => { write!(f, "rfe{}{}", if *u { "i" } else { "d" }, if *p { "b" } else { "a" }) },
+ Opcode::ERET => { write!(f, "eret") },
+ Opcode::HVC => { write!(f, "hvc") },
+ Opcode::BKPT => { write!(f, "bkpt") },
+ Opcode::SMC => { write!(f, "smc") },
+ Opcode::MOVT => { write!(f, "movt") },
+ Opcode::QADD => { write!(f, "qadd") },
+ Opcode::QSUB => { write!(f, "qsub") },
+ Opcode::QDADD => { write!(f, "qdadd") },
+ Opcode::QDSUB => { write!(f, "qdsub") },
Opcode::Incomplete(word) => { write!(f, "incomplete: {:#x}", word) },
Opcode::Invalid => { write!(f, "invalid") },
Opcode::POP => { write!(f, "pop") },
@@ -341,18 +459,18 @@ impl Display for Opcode {
Opcode::STM(false, true, _, _) => { write!(f, "stmdb") },
Opcode::STM(true, false, _, _) => { write!(f, "stm") },
Opcode::STM(true, true, _, _) => { write!(f, "stmia") },
- Opcode::LDR(_, _, _) => { write!(f, "ldr") },
- Opcode::STR(_, _, _) => { write!(f, "str") },
- Opcode::LDRH(_, _, _) => { write!(f, "ldrh") },
- Opcode::STRH(_, _, _) => { write!(f, "strh") },
- Opcode::LDRB(_, _, _) => { write!(f, "ldrb") },
- Opcode::STRB(_, _, _) => { write!(f, "strb") },
- Opcode::LDRT(_) => { write!(f, "ldrt") },
- Opcode::STRT(_) => { write!(f, "strt") },
- Opcode::LDRHT(_) => { write!(f, "ldrht") },
- Opcode::STRHT(_) => { write!(f, "strht") },
- Opcode::LDRBT(_) => { write!(f, "ldrbt") },
- Opcode::STRBT(_) => { write!(f, "strbt") },
+ Opcode::LDR => { write!(f, "ldr") },
+ Opcode::STR => { write!(f, "str") },
+ Opcode::LDRH => { write!(f, "ldrh") },
+ Opcode::STRH => { write!(f, "strh") },
+ Opcode::LDRB => { write!(f, "ldrb") },
+ Opcode::STRB => { write!(f, "strb") },
+ Opcode::LDRT => { write!(f, "ldrt") },
+ Opcode::STRT => { write!(f, "strt") },
+ Opcode::LDRHT => { write!(f, "ldrht") },
+ Opcode::STRHT => { write!(f, "strht") },
+ Opcode::LDRBT => { write!(f, "ldrbt") },
+ Opcode::STRBT => { write!(f, "strbt") },
Opcode::SWP => { write!(f, "swp") },
Opcode::SWPB => { write!(f, "swpb") },
Opcode::MUL => { write!(f, "mul") },
@@ -383,6 +501,7 @@ impl Display for Opcode {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+#[allow(non_camel_case_types)]
pub enum Opcode {
Incomplete(u32),
Invalid,
@@ -434,18 +553,35 @@ pub enum Opcode {
STREX,
LDM(bool, bool, bool, bool),
STM(bool, bool, bool, bool),
- LDR(bool, bool, bool),
- STR(bool, bool, bool),
- LDRH(bool, bool, bool),
- STRH(bool, bool, bool),
- LDRB(bool, bool, bool),
- STRB(bool, bool, bool),
- LDRT(bool),
- STRT(bool),
- LDRHT(bool),
- STRHT(bool),
- LDRBT(bool),
- STRBT(bool),
+ LDR,
+ STR,
+ LDRH,
+ STRH,
+ LDRB,
+ STRB,
+ LDRSH,
+ LDRSHT,
+ LDRSB,
+ LDRSBT,
+ STRD,
+ LDRD,
+ LDC2(u8),
+ LDC2L(u8),
+ STC2(u8),
+ STC2L(u8),
+ MCRR2(u8, u8),
+ MCR2(u8, u8, u8),
+ MRRC2(u8, u8),
+ MRC2(u8, u8, u8),
+ CDP2(u8, u8, u8),
+ SRS(bool, bool),
+ RFE(bool, bool),
+ LDRT,
+ STRT,
+ LDRHT,
+ STRHT,
+ LDRBT,
+ STRBT,
SWP,
SWPB,
MUL,
@@ -461,6 +597,15 @@ pub enum Opcode {
SMLAL_halfword(bool, bool),
SMAL(bool, bool),
SMLAW(bool),
+ ERET,
+ BKPT,
+ HVC,
+ SMC,
+ MOVT,
+ QDSUB,
+ QDADD,
+ QSUB,
+ QADD,
}
static DATA_PROCESSING_OPCODES: [Opcode; 16] = [
@@ -520,6 +665,17 @@ pub enum ShiftStyle {
ROR = 3,
}
+impl Display for ShiftStyle {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ShiftStyle::LSL => write!(f, "lsl"),
+ ShiftStyle::LSR => write!(f, "lsr"),
+ ShiftStyle::ASR => write!(f, "asr"),
+ ShiftStyle::ROR => write!(f, "ror"),
+ }
+ }
+}
+
impl ShiftStyle {
fn from(bits: u8) -> ShiftStyle {
match bits {
@@ -568,6 +724,73 @@ pub struct Reg {
}
impl Reg {
+ #[allow(non_snake_case)]
+ fn from_sysm(R: bool, M: u8) -> Option<Operand> {
+/*
+ * Is one of:
+ * • <Rm>_<mode>, encoded with R==0.
+ * • ELR_hyp, encoded with R==0.
+ * • SPSR_<mode>, encoded with R==1.
+ * For a full description of the encoding of this field, see Encoding and use of Banked register
+ * transfer
+ * instructions on page B9-1959.
+ * */
+ if R == false {
+ [
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(8))),
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(9))),
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(10))),
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(11))),
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(12))),
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Usr, Reg::from_u8(14))),
+ None,
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(8))),
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(9))),
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(10))),
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(11))),
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(12))),
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Fiq, Reg::from_u8(14))),
+ None,
+ Some(Operand::BankedReg(Bank::Irq, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Irq, Reg::from_u8(14))),
+ Some(Operand::BankedReg(Bank::Svc, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Svc, Reg::from_u8(14))),
+ Some(Operand::BankedReg(Bank::Abt, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Abt, Reg::from_u8(14))),
+ Some(Operand::BankedReg(Bank::Und, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Und, Reg::from_u8(14))),
+ None,
+ None,
+ None,
+ None,
+ Some(Operand::BankedReg(Bank::Mon, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Mon, Reg::from_u8(14))),
+ Some(Operand::BankedReg(Bank::Hyp, Reg::from_u8(13))),
+ Some(Operand::BankedReg(Bank::Hyp, Reg::from_u8(14))),
+ ][M as usize]
+ } else {
+ if M == 0b01110 {
+ Some(Operand::BankedSPSR(Bank::Fiq))
+ } else if M == 0b10000 {
+ Some(Operand::BankedSPSR(Bank::Irq))
+ } else if M == 0b10010 {
+ Some(Operand::BankedSPSR(Bank::Svc))
+ } else if M == 0b10100 {
+ Some(Operand::BankedSPSR(Bank::Abt))
+ } else if M == 0b10110 {
+ Some(Operand::BankedSPSR(Bank::Und))
+ } else if M == 0b11100 {
+ Some(Operand::BankedSPSR(Bank::Mon))
+ } else if M == 0b11110 {
+ Some(Operand::BankedSPSR(Bank::Hyp))
+ } else {
+ None
+ }
+ }
+ }
+
pub fn from_u8(bits: u8) -> Reg {
if bits > 0b1111 {
panic!("register number out of range");
@@ -581,33 +804,218 @@ impl Reg {
}
}
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[repr(transparent)]
+pub struct CReg {
+ bits: u8
+}
+
+impl Display for CReg {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "c{}", self.bits)
+ }
+}
+
+impl CReg {
+ pub fn from_u8(bits: u8) -> CReg {
+ if bits > 0b1111 {
+ panic!("register number out of range");
+ }
+
+ CReg { bits }
+ }
+
+ pub fn number(&self) -> u8 {
+ self.bits
+ }
+}
+
+impl Display for StatusRegMask {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ StatusRegMask::CPSR_C => write!(f, "cpsr_c"),
+ StatusRegMask::CPSR_X => write!(f, "cpsr_x"),
+ StatusRegMask::CPSR_XC => write!(f, "cpsr_xc"),
+ StatusRegMask::APSR_G => write!(f, "apsr_g"),
+ StatusRegMask::CPSR_SC => write!(f, "cpsr_sc"),
+ StatusRegMask::CPSR_SX => write!(f, "cpsr_sx"),
+ StatusRegMask::CPSR_SXC => write!(f, "cpsr_sxc"),
+ StatusRegMask::APSR_NZCVQ => write!(f, "apsr_nzcvq"),
+ StatusRegMask::CPSR_FC => write!(f, "cpsr_fc"),
+ StatusRegMask::CPSR_FX => write!(f, "cpsr_fx"),
+ StatusRegMask::CPSR_FXC => write!(f, "cpsr_fxc"),
+ StatusRegMask::APSR_NZCVQG => write!(f, "apsr_nzcvqg"),
+ StatusRegMask::CPSR_FSC => write!(f, "cpsr_fsc"),
+ StatusRegMask::CPSR_FSX => write!(f, "cpsr_fsx"),
+ StatusRegMask::CPSR_FSXC => write!(f, "cpsr_fsxc"),
+ StatusRegMask::SPSR => write!(f, "spsr"),
+ StatusRegMask::SPSR_C => write!(f, "spsr_c"),
+ StatusRegMask::SPSR_X => write!(f, "spsr_x"),
+ StatusRegMask::SPSR_XC => write!(f, "spsr_xc"),
+ StatusRegMask::SPSR_S => write!(f, "spsr_s"),
+ StatusRegMask::SPSR_SC => write!(f, "spsr_sc"),
+ StatusRegMask::SPSR_SX => write!(f, "spsr_sx"),
+ StatusRegMask::SPSR_SXC => write!(f, "spsr_sxc"),
+ StatusRegMask::SPSR_F => write!(f, "spsr_f"),
+ StatusRegMask::SPSR_FC => write!(f, "spsr_fc"),
+ StatusRegMask::SPSR_FX => write!(f, "spsr_fx"),
+ StatusRegMask::SPSR_FXC => write!(f, "spsr_fxc"),
+ StatusRegMask::SPSR_FS => write!(f, "spsr_fs"),
+ StatusRegMask::SPSR_FSC => write!(f, "spsr_fsc"),
+ StatusRegMask::SPSR_FSX => write!(f, "spsr_fsx"),
+ StatusRegMask::SPSR_FSXC => write!(f, "spsr_fsxc"),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[allow(non_camel_case_types)]
+pub enum StatusRegMask {
+ // Note 0b0000 is unused (as is 0b10000)
+ CPSR_C = 0b0001,
+ CPSR_X = 0b0010,
+ CPSR_XC = 0b0011,
+ APSR_G = 0b0100,
+ CPSR_SC = 0b0101,
+ CPSR_SX = 0b0110,
+ CPSR_SXC = 0b0111,
+ APSR_NZCVQ = 0b1000,
+ CPSR_FC = 0b1001,
+ CPSR_FX = 0b1010,
+ CPSR_FXC = 0b1011,
+ APSR_NZCVQG = 0b1100,
+ CPSR_FSC = 0b1101,
+ CPSR_FSX = 0b1110,
+ CPSR_FSXC = 0b1111,
+ SPSR = 0b10000,
+ SPSR_C = 0b10001,
+ SPSR_X = 0b10010,
+ SPSR_XC = 0b10011,
+ SPSR_S = 0b10100,
+ SPSR_SC = 0b10101,
+ SPSR_SX = 0b10110,
+ SPSR_SXC = 0b10111,
+ SPSR_F = 0b11000,
+ SPSR_FC = 0b11001,
+ SPSR_FX = 0b11010,
+ SPSR_FXC = 0b11011,
+ SPSR_FS = 0b11100,
+ SPSR_FSC = 0b11101,
+ SPSR_FSX = 0b11110,
+ SPSR_FSXC = 0b11111,
+}
+
+impl StatusRegMask {
+ fn from_raw(raw: u8) -> StatusRegMask {
+ if raw == 0 {
+ panic!("invalid status reg mask value");
+ }
+ [
+ StatusRegMask::CPSR_C, // actually unreachable
+ StatusRegMask::CPSR_C,
+ StatusRegMask::CPSR_X,
+ StatusRegMask::CPSR_XC,
+ StatusRegMask::APSR_G,
+ StatusRegMask::CPSR_SC,
+ StatusRegMask::CPSR_SX,
+ StatusRegMask::CPSR_SXC,
+ StatusRegMask::APSR_NZCVQ,
+ StatusRegMask::CPSR_FC,
+ StatusRegMask::CPSR_FX,
+ StatusRegMask::CPSR_FXC,
+ StatusRegMask::APSR_NZCVQG,
+ StatusRegMask::CPSR_FSC,
+ StatusRegMask::CPSR_FSX,
+ StatusRegMask::CPSR_FSXC,
+ StatusRegMask::SPSR,
+ StatusRegMask::SPSR_C,
+ StatusRegMask::SPSR_X,
+ StatusRegMask::SPSR_XC,
+ StatusRegMask::SPSR_S,
+ StatusRegMask::SPSR_SC,
+ StatusRegMask::SPSR_SX,
+ StatusRegMask::SPSR_SXC,
+ StatusRegMask::SPSR_F,
+ StatusRegMask::SPSR_FC,
+ StatusRegMask::SPSR_FX,
+ StatusRegMask::SPSR_FXC,
+ StatusRegMask::SPSR_FS,
+ StatusRegMask::SPSR_FSC,
+ StatusRegMask::SPSR_FSX,
+ StatusRegMask::SPSR_FSXC,
+ ][raw as usize]
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Operand {
Reg(Reg),
+ RegWBack(Reg, bool),
RegList(u16),
RegDeref(Reg),
RegDisp(Reg, i16),
RegShift(RegShift),
- RegDerefRegShift(RegShift),
- RegDerefPostindexOffset(Reg, u16, bool), // add/sub
- RegDerefPreindexOffset(Reg, u16, bool), // add/sub
- RegDerefPostindexReg(Reg, Reg, bool),
- RegDerefPreindexReg(Reg, Reg, bool),
+ RegDerefPostindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback
+ RegDerefPreindexRegShift(Reg, RegShift, bool, bool), // add/sub, wback
+ RegDerefPostindexOffset(Reg, u16, bool, bool), // add/sub, wback
+ RegDerefPreindexOffset(Reg, u16, bool, bool), // add/sub, wback
+ RegDerefPostindexReg(Reg, Reg, bool, bool), // add/sub, wback
+ RegDerefPreindexReg(Reg, Reg, bool, bool), // add/sub, wback
Imm12(u16),
Imm32(u32),
BranchOffset(i32),
- ASPR,
- CSPR,
+ BranchThumbOffset(i32),
+ Coprocessor(u8),
+ CoprocOption(u8),
+ CReg(CReg),
+ APSR,
+ CPSR,
SPSR,
+ BankedReg(Bank, Reg),
+ BankedSPSR(Bank),
+ StatusRegMask(StatusRegMask),
Nothing,
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Bank {
+ Usr,
+ Fiq,
+ Irq,
+ Svc,
+ Abt,
+ Und,
+ Mon,
+ Hyp,
+}
+
+impl Display for Bank {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Bank::Usr => write!(f, "usr"),
+ Bank::Fiq => write!(f, "fiq"),
+ Bank::Irq => write!(f, "irq"),
+ Bank::Svc => write!(f, "svc"),
+ Bank::Abt => write!(f, "abt"),
+ Bank::Und => write!(f, "und"),
+ Bank::Mon => write!(f, "mon"),
+ Bank::Hyp => write!(f, "hyp"),
+ }
+ }
+}
+
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::BankedReg(bank, reg) => {
+ write!(f, "{}_{}", reg_name_colorize(*reg, colors), bank)
+ },
+ Operand::BankedSPSR(bank) => {
+ write!(f, "spsr_{}", bank)
+ },
Operand::Reg(reg) => {
write!(f, "{}", reg_name_colorize(*reg, colors))
}
@@ -620,22 +1028,23 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Operand::RegShift(shift) => {
format_shift(f, *shift, colors)
}
- Operand::RegDerefRegShift(shift) => {
- write!(f, "[")?;
- format_shift(f, *shift, colors)?;
- write!(f, "]")
+ Operand::RegDerefPostindexRegShift(reg, shift, add, wback) => {
+ format_reg_shift_mem(f, *reg, *shift, *add, false, *wback, colors)
}
- Operand::RegDerefPostindexOffset(reg, offs, add) => {
- write!(f, "[{}, {}{:#x}]", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, offs)
+ Operand::RegDerefPreindexRegShift(reg, shift, add, wback) => {
+ format_reg_shift_mem(f, *reg, *shift, *add, true, *wback, colors)
}
- Operand::RegDerefPreindexOffset(reg, offs, add) => {
- write!(f, "[{}], {}{:#x}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, offs)
+ Operand::RegDerefPostindexOffset(reg, offs, add, wback) => {
+ format_reg_imm_mem(f, *reg, *offs, *add, false, *wback, colors)
}
- Operand::RegDerefPostindexReg(reg, offsreg, add) => {
- write!(f, "[{}, {}{}]", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors))
+ Operand::RegDerefPreindexOffset(reg, offs, add, wback) => {
+ format_reg_imm_mem(f, *reg, *offs, *add, true, *wback, colors)
}
- Operand::RegDerefPreindexReg(reg, offsreg, add) => {
- write!(f, "[{}], {}{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors))
+ Operand::RegDerefPostindexReg(reg, offsreg, add, wback) => {
+ write!(f, "[{}], {}{}{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors), if *wback { "!" } else { "" })
+ }
+ Operand::RegDerefPreindexReg(reg, offsreg, add, wback) => {
+ write!(f, "[{}, {}{}]{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors), if *wback { "!" } else { "" })
}
Operand::Imm12(imm) => {
write!(f, "{:#x}", imm)
@@ -645,13 +1054,39 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
}
Operand::BranchOffset(imm) => {
if *imm < 0 {
- write!(f, " $-{:#x}", (-*imm) * 4)
+ write!(f, "$-{:#x}", (-*imm) * 4)
+ } else {
+ write!(f, "$+{:#x}", *imm * 4)
+ }
+ }
+ Operand::BranchThumbOffset(imm) => {
+ if *imm < 0 {
+ write!(f, "$-{:#x}", (-*imm) * 2)
} else {
- write!(f, " $+{:#x}", *imm * 4)
+ write!(f, "$+{:#x}", *imm * 2)
}
}
- Operand::ASPR => { write!(f, "aspr") },
- Operand::CSPR => { write!(f, "cspr") },
+ Operand::Coprocessor(num) => {
+ write!(f, "p{}", num)
+ }
+ Operand::CoprocOption(num) => {
+ write!(f, "{{{:#x}}}", num)
+ }
+ Operand::RegWBack(reg, wback) => {
+ if *wback {
+ write!(f, "{}!", reg_name_colorize(*reg, colors))
+ } else {
+ write!(f, "{}", reg_name_colorize(*reg, colors))
+ }
+ }
+ Operand::CReg(creg) => {
+ write!(f, "{}", creg)
+ }
+ Operand::StatusRegMask(mask) => {
+ write!(f, "{}", mask)
+ }
+ Operand::APSR => { write!(f, "apsr") },
+ Operand::CPSR => { write!(f, "cpsr") },
Operand::SPSR => { write!(f, "spsr") },
Operand::Nothing => { panic!("tried to print Nothing operand") },
}
@@ -672,6 +1107,8 @@ pub enum DecodeError {
InvalidOpcode,
InvalidOperand,
Incomplete,
+ Nonconforming,
+ Undefined,
}
impl fmt::Display for DecodeError {
@@ -681,6 +1118,8 @@ impl fmt::Display for DecodeError {
DecodeError::InvalidOpcode => write!(f, "invalid opcode"),
DecodeError::InvalidOperand => write!(f, "invalid operand"),
DecodeError::Incomplete => write!(f, "incomplete decoder"),
+ DecodeError::Nonconforming => write!(f, "invalid reserved bits"),
+ DecodeError::Undefined => write!(f, "undefined encoding"),
}
}
}
@@ -736,24 +1175,16 @@ 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, shift: RegShift, colors: &Y) -> Result<(), fmt::Error> {
- fn shift_tpe_to_str(tpe: ShiftStyle) -> &'static str {
- match tpe {
- ShiftStyle::LSL => "lsl",
- ShiftStyle::LSR => "lsr",
- ShiftStyle::ASR => "asr",
- ShiftStyle::ROR => "ror",
- }
- }
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())
+ write!(f, "{}, {} {}", reg_name_colorize(imm_shift.shiftee(), colors), 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))
+ write!(f, "{}, {} {}", reg_name_colorize(reg_shift.shiftee(), colors), reg_shift.stype(), reg_name_colorize(reg_shift.shifter(), colors))
},
}
}
@@ -774,7 +1205,7 @@ fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut
write!(f, "]")
},
(false, true) => {
- unreachable!("I don't know how to render an operand with pre==false and wback==true, this seems like it should be LDRT");
+ unreachable!("preindex with writeback is not an ARM addressing mode. this is a decoder bug.");
},
(false, false) => {
write!(f, "[{}], {}", reg_name_colorize(Rd, colors), op)?;
@@ -790,16 +1221,16 @@ fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T
match (pre, wback) {
(true, true) => {
- write!(f, "[{}, #{}{:#x}]!", reg_name_colorize(Rn, colors), op, imm * 4)
+ write!(f, "[{}, {}{:#x}]!", reg_name_colorize(Rn, colors), op, imm)
},
(true, false) => {
- write!(f, "[{}, #{}{:#x}]", reg_name_colorize(Rn, colors), op, imm * 4)
+ write!(f, "[{}, {}{:#x}]", reg_name_colorize(Rn, colors), op, imm)
},
(false, true) => {
- unreachable!("I don't know how to render an operand with pre==false and wback==true, this seems like it should be LDRT");
+ unreachable!("I don't know how to render an operand with postindex and wback==true, this seems like it should be LDRT");
},
(false, false) => {
- write!(f, "[{}], #{}{:#x}", reg_name_colorize(Rn, colors), op, imm * 4)
+ write!(f, "[{}], {}{:#x}", reg_name_colorize(Rn, colors), op, imm)
}
}
} else {
@@ -904,9 +1335,159 @@ impl ConditionCode {
}
}
-#[derive(Default, Debug)]
+#[derive(Debug, Copy, Clone)]
+#[allow(dead_code)]
+enum DecodeMode {
+ User,
+ FIQ,
+ IRQ,
+ Supervisor,
+ Monitor,
+ Abort,
+ Hyp,
+ Undefined,
+ System,
+ /// Catch-all mode to try decoding all ARM instructions. Some instructions are `UNDEFINED` or
+ /// `UNPREDICTABLE` in some modes, but `Any` will attempt to decode all.
+ Any,
+}
+
+impl Default for DecodeMode {
+ fn default() -> Self {
+ DecodeMode::Any
+ }
+}
+
+impl DecodeMode {
+ fn is_user(&self) -> bool {
+ match self {
+ DecodeMode::Any |
+ DecodeMode::User => true,
+ _ => false
+ }
+ }
+ #[allow(dead_code)]
+ fn is_supervisor(&self) -> bool {
+ match self {
+ DecodeMode::Any |
+ DecodeMode::Supervisor => true,
+ _ => false
+ }
+ }
+ fn is_hyp(&self) -> bool {
+ match self {
+ DecodeMode::Any |
+ DecodeMode::Hyp => true,
+ _ => false
+ }
+ }
+ fn is_system(&self) -> bool {
+ match self {
+ DecodeMode::Any |
+ DecodeMode::System => true,
+ _ => false
+ }
+ }
+ fn is_any(&self) -> bool {
+ match self {
+ DecodeMode::Any => true,
+ _ => false,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+#[allow(non_camel_case_types)]
+enum ARMVersion {
+ v4,
+ v5,
+ v6,
+ v6t2,
+ v7,
+ v7ve,
+ v7vese,
+ Any,
+}
+
+impl Default for ARMVersion {
+ fn default() -> Self {
+ ARMVersion::Any
+ }
+}
+
+#[derive(Debug)]
pub struct InstDecoder {
- system_mode: bool
+ mode: DecodeMode,
+ version: ARMVersion,
+ should_is_must: bool,
+}
+
+impl Default for InstDecoder {
+ fn default() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::Any,
+ should_is_must: true,
+ }
+ }
+}
+
+impl InstDecoder {
+ pub fn armv4() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v4,
+ should_is_must: true,
+ }
+ }
+
+ pub fn armv5() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v5,
+ should_is_must: true,
+ }
+ }
+
+ pub fn armv6() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v6,
+ should_is_must: true,
+ }
+ }
+
+ pub fn armv6t2() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v6t2,
+ should_is_must: true,
+ }
+ }
+
+ pub fn armv7() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v7,
+ should_is_must: true,
+ }
+ }
+
+ pub fn armv7ve() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v7ve,
+ should_is_must: true,
+ }
+ }
+
+ pub fn armv7vese() -> Self {
+ Self {
+ mode: DecodeMode::Any,
+ version: ARMVersion::v7vese,
+ should_is_must: true,
+ }
+ }
}
#[allow(non_snake_case)]
@@ -936,21 +1517,73 @@ impl Decoder<Instruction> for InstDecoder {
};
if cond == 0b1111 {
- // do unconditional instruction stuff
+ // unconditional instructions, section A5.7/page A5-214
inst.condition = ConditionCode::AL;
let op1 = (word >> 20) as u8;
- if op1 > 0x80 {
+ if op1 >= 0b1000_0000 {
match (op1 >> 5) & 0b11 {
0b00 => {
- inst.opcode = Opcode::Incomplete(word);
+ match op1 & 0b101 {
+ 0b000 |
+ 0b101 => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ 0b100 => {
+ // SRS (see table A5.7, op1 = 0b100xx1x0, page A5-214)
+ if !self.mode.is_any() && self.mode.is_hyp() {
+ return Err(DecodeError::Undefined);
+ }
+ if self.should_is_must {
+ if word & 0x000fffe0 != 0x000d0500 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let puxw = (word >> 21) & 0b1111;
+ let P = puxw & 0b1000 != 0;
+ let U = puxw & 0b0100 != 0;
+ let W = puxw & 0b0001 != 0;
+ inst.opcode = Opcode::SRS(P, U);
+ inst.operands = [
+ Operand::RegWBack(Reg::from_u8(13), W),
+ Operand::Imm32(word & 0b1111),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ },
+ 0b001 => {
+ // RFE (see table A5.7, op1 = 0b100xx0x1, page A5-214)
+ if !self.mode.is_any() && self.mode.is_hyp() {
+ return Err(DecodeError::Undefined);
+ }
+ if self.should_is_must {
+ if word & 0xffff != 0x0a00 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let puxw = (word >> 21) & 0b1111;
+ let P = puxw & 0b1000 != 0;
+ let U = puxw & 0b0100 != 0;
+ let W = puxw & 0b0001 != 0;
+ inst.opcode = Opcode::RFE(P, U);
+ inst.operands = [
+ Operand::RegWBack(Reg::from_u8((word >> 16) as u8 & 0b1111), W),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ _ => {
+ unreachable!("op1 mask is 0b101 but somehow we got an invalid pattern");
+ }
+ }
}
0b01 => {
inst.opcode = Opcode::BLX;
- let operand = ((word & 0xffffff) as i32) << 8 >> 6;
+ let operand = ((word & 0xffffff) as i32) << 8 >> 7;
inst.operands = [
- Operand::BranchOffset(
+ Operand::BranchThumbOffset(
operand | (
- ((word >> 23) & 0b10) as i32
+ ((word >> 24) & 0b1) as i32
)
),
Operand::Nothing,
@@ -959,16 +1592,142 @@ impl Decoder<Instruction> for InstDecoder {
];
}
0b10 => {
- inst.opcode = Opcode::Incomplete(word);
+ // op1=0b110xxxxx, see table A5-23
+ if (word >> 20) & 0b11010 == 0b00000 {
+ // the `not 11000x0{0,1}` cases in table A5-23, MCRR or MRRC
+ // but first check that bit 2 of op1 is in fact 1:
+ if (word >> 20) & 0b00100 != 0 {
+ // actually MCRR or MRRC
+ let CRm = word as u8 & 0b1111;
+ let opc1 = (word >> 4) as u8 & 0b1111;
+ let coproc = (word >> 8) as u8 & 0b1111;
+ if coproc & 0b1110 == 0b1010 {
+ // TODO: `UNDEFINED`
+ return Err(DecodeError::InvalidOpcode);
+ }
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let Rt2 = (word >> 16) as u8 & 0b1111;
+ if Rt == 15 || Rt2 == 15 || Rt == Rt2 {
+ // TODO: actually `UNPREDICTABLE`
+ return Err(DecodeError::InvalidOperand);
+ }
+ if (word >> 20) & 0b00001 != 0 {
+ inst.opcode = Opcode::MRRC2(coproc, opc1);
+ } else {
+ inst.opcode = Opcode::MCRR2(coproc, opc1);
+ }
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::Reg(Reg::from_u8(Rt2)),
+ Operand::CReg(CReg::from_u8(CRm)),
+ Operand::Nothing,
+ ];
+ } else {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ } else {
+ // STC or LDC
+ let pudw = (word >> 21) as u8 & 0b1111;
+ let Rn = (word >> 16) as u8 & 0b1111;
+ let CRd = (word >> 12) as u8 & 0b1111;
+ let coproc = (word >> 8) as u8 & 0b1111;
+ let imm8 = word & 0b11111111;
+
+ if coproc & 0b1110 == 0b1010 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+
+ if (word >> 20) & 0b00001 == 0 {
+ // op=110xxxx0, STC
+ // page A8-663
+
+ if pudw & 0b0010 != 0 {
+ inst.opcode = Opcode::STC2L(coproc);
+ } else {
+ inst.opcode = Opcode::STC2(coproc);
+ }
+ } else {
+ // op=110xxxx1, LDC
+ // page A8-393
+
+ if pudw & 0b0010 != 0 {
+ inst.opcode = Opcode::LDC2L(coproc);
+ } else {
+ inst.opcode = Opcode::LDC2(coproc);
+ }
+ }
+
+ let P = pudw & 0b1000 != 0;
+ let U = pudw & 0b0100 != 0;
+ let W = pudw & 0b0001 != 0;
+
+ inst.operands = [
+ Operand::CReg(CReg::from_u8(CRd)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, W)
+ } else {
+ if W {
+ // preindex has no wback
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), (imm8 << 2) as u16, U, false)
+ } else {
+ Operand::RegDeref(Reg::from_u8(Rn))
+ }
+ },
+ if !P && !W {
+ // TODO: not sure what ldc2{l}'s <option> field really means?
+ // at this point the invalid encoding and mrrc/mcrr forms have
+ // been tested, so..
+ debug_assert!(U);
+ Operand::CoprocOption(imm8 as u8)
+ } else {
+ Operand::Nothing
+ },
+ Operand::Nothing,
+ ];
+ }
}
0b11 => {
- inst.opcode = Opcode::Incomplete(word);
+ // operands are shared between cdp2 and mcr2/mrc2, but Rt is repurposed as
+ // CRd
+ let CRm = word as u8 & 0b1111;
+ let opc2 = (word >> 5) as u8 & 0b111;
+ let coproc = (word >> 8) as u8 & 0b1111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let CRn = (word >> 16) as u8 & 0b1111;
+
+ if (word >> 4) & 1 == 0 {
+ // CDP2, page A8-356
+ let opc1 = (word >> 20) as u8 & 0b1111;
+ inst.opcode = Opcode::CDP2(coproc, opc1, opc2);
+ inst.operands = [
+ Operand::CReg(CReg::from_u8(Rt)),
+ Operand::CReg(CReg::from_u8(CRn)),
+ Operand::CReg(CReg::from_u8(CRm)),
+ Operand::Nothing,
+ ];
+ } else {
+ // MCR2/MRC2, page A8-477/A8-493
+ let opc1 = (word >> 21) as u8 & 0b111;
+ if (word >> 20) & 1 == 0 {
+ inst.opcode = Opcode::MCR2(coproc, opc1, opc2);
+ } else {
+ inst.opcode = Opcode::MRC2(coproc, opc1, opc2);
+ }
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::CReg(CReg::from_u8(CRn)),
+ Operand::CReg(CReg::from_u8(CRm)),
+ Operand::Nothing,
+ ];
+ }
}
_ => {
unreachable!();
}
}
} else {
+ // op1=0xxxxxxx, "Memory hints, Advanced SIMD instructions, and miscellaneous
+ // instructions on pge A5-215"
inst.opcode = Opcode::Incomplete(word);
}
return Ok(());
@@ -1243,16 +2002,18 @@ impl Decoder<Instruction> for InstDecoder {
0b00000 => {
// STRHT or STRH
if !P && W { // flags == 0b0x010
- inst.opcode = Opcode::STRHT(U);
+ inst.opcode = Opcode::STRHT;
} else {
- inst.opcode = Opcode::STRH(U, P, W);
+ inst.opcode = Opcode::STRH;
}
inst.operands = [
Operand::Reg(Reg::from_u8(Rd)),
if P {
- Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U)
+ Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U, W)
} else {
- Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U)
+ // either this is !P && W, so STRHT, and no wback,
+ // or this is !W, and no wback
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U, false)
},
Operand::Nothing,
Operand::Nothing,
@@ -1261,16 +2022,18 @@ impl Decoder<Instruction> for InstDecoder {
0b00001 => {
// LDRHT or LDRH
if !P && W { // flags == 0b0x011
- inst.opcode = Opcode::LDRHT(U);
+ inst.opcode = Opcode::LDRHT;
} else {
- inst.opcode = Opcode::LDRH(U, P, W);
+ inst.opcode = Opcode::LDRH;
}
inst.operands = [
Operand::Reg(Reg::from_u8(Rd)),
if P {
- Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U)
+ Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U, W)
} else {
- Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U)
+ // either this is !P && W, so LDRHT, and no wback,
+ // or this is !W, and no wback
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(LoOffset), U, false)
},
Operand::Nothing,
Operand::Nothing,
@@ -1279,17 +2042,19 @@ impl Decoder<Instruction> for InstDecoder {
0b00100 => {
// STRHT or STRH
if !P && W { // flags == 0b0x110
- inst.opcode = Opcode::STRHT(U);
+ inst.opcode = Opcode::STRHT;
} else {
- inst.opcode = Opcode::STRH(U, P, W);
+ inst.opcode = Opcode::STRH;
}
let imm = (HiOffset << 4) as u16 | LoOffset as u16;
inst.operands = [
Operand::Reg(Reg::from_u8(Rd)),
if P {
- Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U)
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U, W)
} else {
- Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U)
+ // either this is !P && W, so STRHT, and no wback,
+ // or this is !W, and no wback
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U, false)
},
Operand::Nothing,
Operand::Nothing,
@@ -1298,17 +2063,19 @@ impl Decoder<Instruction> for InstDecoder {
0b00101 => {
// LDRHT or LDRH
if !P && W { // flags == 0b0x111
- inst.opcode = Opcode::LDRHT(U);
+ inst.opcode = Opcode::LDRHT;
} else {
- inst.opcode = Opcode::LDRH(U, P, W);
+ inst.opcode = Opcode::LDRH;
}
let imm = (HiOffset << 4) as u16 | LoOffset as u16;
inst.operands = [
Operand::Reg(Reg::from_u8(Rd)),
if P {
- Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U)
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U, W)
} else {
- Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U)
+ // either this is !P && W, so LDRHT, and no wback,
+ // or this is !W, and no wback
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U, false)
},
Operand::Nothing,
Operand::Nothing,
@@ -1323,14 +2090,251 @@ impl Decoder<Instruction> for InstDecoder {
0b10 => {
// |c o n d|0 0 0 x|x x x x x x x x x x x x x x x x|1 1 0 1|x x x x|
// page A5-201
- inst.opcode = Opcode::Incomplete(word);
- return Ok(());
+ let P = flags & 0b10000 != 0;
+ let U = flags & 0b01000 != 0;
+ let W = flags & 0b00010 != 0;
+
+ match flags & 0b00101 {
+ 0b00000 => {
+ // LDRD or invalid
+ if !P && W { // flags == 0b0x010
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ inst.opcode = Opcode::LDRD;
+ }
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 == 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let Rm = word as u8 & 0b1111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ if Rt & 1 != 0 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ if Rt == 15 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ let Rn = (word >> 16) as u8 & 0b1111;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::Reg(Reg::from_u8(Rt + 1)),
+ if P {
+ Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, W)
+ } else {
+ // either !P & W (invalid) or !W
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, false)
+ },
+ Operand::Nothing,
+ ];
+ },
+ 0b00001 => {
+ // LDRSB or LDRSBT
+ if !P && W { // flags == 0b0x010
+ inst.opcode = Opcode::LDRSBT;
+ } else {
+ inst.opcode = Opcode::LDRSB;
+ }
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 == 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let Rm = word as u8 & 0b1111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let Rn = (word >> 16) as u8 & 0b1111;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ if P {
+ Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, W)
+ } else {
+ // either !P & W (ldrsbt) or !W
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, false)
+ },
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ },
+ 0b00100 => {
+ // LDRD (immediate)
+ if !P && W { // flags == 0b0x110
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ inst.opcode = Opcode::LDRD;
+ }
+ let Rt = (word >> 12) as u8 & 0b1111;
+ if Rt & 1 != 0 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ if Rt == 14 || Rn == 15 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ let Rn = (word >> 16) as u8 & 0b1111;
+ let imm = (HiOffset << 4) as u16 | LoOffset as u16;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::Reg(Reg::from_u8(Rt + 1)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U, W)
+ } else {
+ // either !P & W (invalid) or !W
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U, false)
+ },
+ Operand::Nothing,
+ ];
+ },
+ 0b00101 => {
+ // LDRSB or LDRSBT
+ if !P && W { // flags == 0b0x010
+ inst.opcode = Opcode::LDRSBT;
+ } else {
+ inst.opcode = Opcode::LDRSB;
+ }
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 == 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let Rn = (word >> 16) as u8 & 0b1111;
+ let imm = (HiOffset << 4) as u16 | LoOffset as u16;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U, W)
+ } else {
+ // either !P & W (ldrsbt) or !W
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U, false)
+ },
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ },
+ _ => { unreachable!("impossible bit pattern"); }
+ }
}
0b11 => {
// |c o n d|0 0 0 x|x x x x x x x x x x x x x x x x|1 1 1 1|x x x x|
// page A5-201
- inst.opcode = Opcode::Incomplete(word);
- return Ok(());
+ let P = flags & 0b10000 != 0;
+ let U = flags & 0b01000 != 0;
+ let W = flags & 0b00010 != 0;
+
+ match flags & 0b00101 {
+ 0b00000 => {
+ // STRD or invalid
+ if !P && W { // flags == 0b0x010
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ inst.opcode = Opcode::STRD;
+ }
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 == 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let Rm = word as u8 & 0b1111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ if Rt & 1 != 0 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ let Rn = (word >> 16) as u8 & 0b1111;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::Reg(Reg::from_u8(Rt + 1)),
+ if P {
+ Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, W)
+ } else {
+ // either !P & W (invalid) or !W
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, false)
+ },
+ Operand::Nothing,
+ ];
+ },
+ 0b00001 => {
+ // LDRSH or LDRSHT
+ if !P && W { // flags == 0b0x010
+ inst.opcode = Opcode::LDRSHT;
+ } else {
+ inst.opcode = Opcode::LDRSH;
+ }
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 == 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let Rm = word as u8 & 0b1111;
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let Rn = (word >> 16) as u8 & 0b1111;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ if P {
+ Operand::RegDerefPreindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, W)
+ } else {
+ // either !P & W (ldrsht) or !W
+ Operand::RegDerefPostindexReg(Reg::from_u8(Rn), Reg::from_u8(Rm), U, false)
+ },
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ },
+ 0b00100 => {
+ // STRD (immediate)
+ if !P && W { // flags == 0b0x110
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ inst.opcode = Opcode::STRD;
+ }
+ let Rt = (word >> 12) as u8 & 0b1111;
+ if Rt & 1 != 0 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ if Rt == 15 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ let Rn = (word >> 16) as u8 & 0b1111;
+ let imm = (HiOffset << 4) as u16 | LoOffset as u16;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::Reg(Reg::from_u8(Rt + 1)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U, W)
+ } else {
+ // either !P & W (invalid) or !W
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U, false)
+ },
+ Operand::Nothing,
+ ];
+ },
+ 0b00101 => {
+ // LDRSH or LDRSHT
+ if !P && W { // flags == 0b0x010
+ inst.opcode = Opcode::LDRSHT;
+ } else {
+ inst.opcode = Opcode::LDRSH;
+ }
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 == 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ let Rt = (word >> 12) as u8 & 0b1111;
+ let Rn = (word >> 16) as u8 & 0b1111;
+ let imm = (HiOffset << 4) as u16 | LoOffset as u16;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm, U, W)
+ } else {
+ // either !P & W (ldrsht) or !W
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm, U, false)
+ },
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ },
+ _ => { unreachable!("impossible bit pattern"); }
+ }
}
_ => { unreachable!(); }
}
@@ -1350,61 +2354,77 @@ impl Decoder<Instruction> for InstDecoder {
// misc instructions (page A5-194)
match op2 {
0b000 => {
- if word & 0b1000000000 != 0 {
+ // test B bit (bit 9)
+ if word & 0b10_0000_0000 != 0 {
// TODO: ARMv7VE flag
- let _M = (((word >> 9) & 1) << 4) | ((word >> 16) & 0x0f);
- let _R = (word >> 22) & 1;
+ let SYSm = (((word >> 9) & 1) << 4) as u8 | ((word >> 16) & 0x0f) as u8;
+ let R = (word >> 22) & 1;
if opcode & 0b01 == 0b01 {
+ if self.should_is_must {
+ if word & 0b1111_1100_0000_0000 != 0xf000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ }
inst.opcode = Opcode::MSR;
+ inst.operands[0] = Reg::from_sysm(R != 0, SYSm).expect("from_sysm works");
inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
- return Err(DecodeError::Incomplete);
+ inst.operands[2] = Operand::Nothing;
+ inst.operands[3] = Operand::Nothing;
} else {
+ if self.should_is_must {
+ if word & 0b0000_1100_0000_1111 != 0x0000 {
+ return Err(DecodeError::InvalidOperand);
+ }
+ }
inst.opcode = Opcode::MRS;
inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111));
- return Err(DecodeError::Incomplete);
+ inst.operands[1] = Reg::from_sysm(R != 0, SYSm).expect("from_sysm works");
+ inst.operands[2] = Operand::Nothing;
+ inst.operands[3] = Operand::Nothing;
}
} else {
match opcode & 0b11 {
0b00 |
0b10 => {
inst.opcode = Opcode::MRS;
- /*
- let src = if self.system_mode {
+ let src = if self.mode.is_system() {
let R = (word >> 22) & 1 != 0;
if R {
Operand::SPSR
} else {
- Operand::CSPR
+ Operand::CPSR
}
} else {
- Operand::ASPR
+ Operand::APSR
};
- */
inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111));
- // TODO:
- return Err(DecodeError::Incomplete);
+ inst.operands[1] = src;
+ inst.operands[2] = Operand::Nothing;
+ inst.operands[3] = Operand::Nothing;
}
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
+ if mask & 0b11 == 0 {
+ if self.mode.is_user() {
+ inst.operands[0] = Operand::StatusRegMask(StatusRegMask::from_raw(mask as u8));
+ inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
+ inst.operands[2] = Operand::Nothing;
+ inst.operands[3] = Operand::Nothing;
} 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);
+ // MSR with op == 01, op == xx00, but not
+ // user mode
+ return Err(DecodeError::InvalidOperand);
+ }
} else {
- if mask & 0b11 == 0 {
+ if self.mode.is_system() {
+ // bit 22 is the high bit of opcode, so..
+ let R = (word >> 22) as u8 & 1;
+ inst.operands[0] = Operand::StatusRegMask(StatusRegMask::from_raw((R << 4) | mask as u8));
inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
- // TODO:
- // inst.operands[0] = /* derived from mask >> 2 */
- return Err(DecodeError::Incomplete);
+ inst.operands[2] = Operand::Nothing;
+ inst.operands[3] = Operand::Nothing;
} else {
// MSR with op == 01, op != xx00
return Err(DecodeError::InvalidOperand);
@@ -1412,14 +2432,18 @@ impl Decoder<Instruction> for InstDecoder {
}
},
0b11 => {
- if !self.system_mode {
+ if !self.mode.is_system() {
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);
+ let mask = (word >> 16) & 0b1111;
+ // bit 22 is the high bit of opcode, so..
+ let R = (word >> 22) & 1;
+ inst.operands[0] = Operand::StatusRegMask(StatusRegMask::from_raw((R << 4) as u8 | mask as u8));
+ inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111));
+ inst.operands[2] = Operand::Nothing;
+ inst.operands[3] = Operand::Nothing;
}
_ => {
unreachable!();
@@ -1475,18 +2499,121 @@ impl Decoder<Instruction> for InstDecoder {
},
0b101 => {
// TODO: "Saturating addition and subtraction" page A5-200
- return Err(DecodeError::Incomplete);
+ match (word >> 21) & 0b11 {
+ 0b00 => {
+ inst.opcode = Opcode::QADD;
+ },
+ 0b01 => {
+ inst.opcode = Opcode::QSUB;
+ },
+ 0b10 => {
+ inst.opcode = Opcode::QDADD;
+ },
+ 0b11 => {
+ inst.opcode = Opcode::QDSUB;
+ },
+ _ => {
+ unreachable!("bit pattern masked by 0b11 but large value observed");
+ }
+ }
+
+ if self.should_is_must {
+ if (word >> 8) & 0b1111 != 0 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(((word >> 12) & 0b1111) as u8)),
+ Operand::Reg(Reg::from_u8((word & 0b1111) as u8)),
+ Operand::Reg(Reg::from_u8(((word >> 16) & 0b1111) as u8)),
+ Operand::Nothing
+ ];
}
0b110 => {
- // TODO: "ERET" page B9-1968
- return Err(DecodeError::Incomplete);
+ if (word >> 21) & 0b11 != 0b11 {
+ return Err(DecodeError::InvalidOpcode);
+ }
+
+ // "ERET" page B9-1968, from A5.2.12, page A5-205
+ if self.should_is_must {
+ if word & 0b1111_1111_1111_1111 != 0b0000_0000_0000_0110_1110 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ inst.opcode = Opcode::ERET;
+ inst.operands = [
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
},
0b111 => {
- // TODO: "BKPT, HVC, SMC" from table A5-14/page A5-203
- return Err(DecodeError::Incomplete);
+ // "BKPT, HVC, SMC" from table A5-14/page A5-205
+ match (word >> 21) & 0b11 {
+ 0b00 => {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ 0b01 => {
+ // BKPT
+ if self.should_is_must {
+ if (word >> 28) & 0b1111 != 0b1110 {
+ // "BKPT must be encoded with AL condition"
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ inst.opcode = Opcode::BKPT;
+ let immlo = word & 0x0f;
+ let imm = ((word >> 4) & 0xfff0) | immlo;
+ inst.operands = [
+ Operand::Imm32(imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ 0b10 => {
+ // HVC
+ if self.should_is_must {
+ if (word >> 28) & 0b1111 != 0b1110 {
+ // "HVC must be encoded with AL condition"
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ inst.opcode = Opcode::HVC;
+ let immlo = word & 0x0f;
+ let imm = ((word >> 4) & 0xfff0) | immlo;
+ inst.operands = [
+ Operand::Imm32(imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ 0b11 => {
+ // SMC
+ if self.should_is_must {
+ if word & 0xf_ff_ff_70 == 0x1_60_00_70 {
+ // "SMC must be encoded with AL condition"
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ inst.opcode = Opcode::SMC;
+ let immlo = word & 0x0f;
+ let imm = ((word >> 4) & 0xfff0) | immlo;
+ inst.operands = [
+ Operand::Imm32(imm),
+ Operand::Nothing,
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ _ => { unreachable!("impossible bit pattern"); }
+ }
},
_ => {
- unreachable!();
+ unreachable!("op2 is a three bit field, got an invalid pattern");
}
}
} else {
@@ -1592,9 +2719,10 @@ impl Decoder<Instruction> for InstDecoder {
if shift_spec & 0xff0 == 0 {
if (0b1101 & opcode) == 0b1101 {
- if Rn != 0 {
- // this is really a "should be zero" situation
- return Err(DecodeError::Incomplete);
+ if self.should_is_must {
+ if Rn != 0 {
+ return Err(DecodeError::Nonconforming);
+ }
}
// MOV or MVN
inst.operands = [
@@ -1612,9 +2740,11 @@ impl Decoder<Instruction> for InstDecoder {
];
}
} else {
- if opcode == 0b1101 && Rn != 0 {
- // Rn "should" be zero
- return Err(DecodeError::Incomplete);
+ if self.should_is_must {
+ if opcode == 0b1101 && Rn != 0 {
+ // Rn "should" be zero
+ return Err(DecodeError::Nonconforming);
+ }
}
inst.operands = [
@@ -1672,8 +2802,69 @@ impl Decoder<Instruction> for InstDecoder {
// 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(());
+ match opcode & 0b0011 {
+ 0b00 => {
+ // 16-bit immediate load, MOV (immediate) on page A8-485
+ inst.opcode = Opcode::MOV;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(((word >> 12) & 0b1111) as u8)),
+ Operand::Imm32(((word >> 4) & 0xf000) | (word & 0xfff)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ 0b10 => {
+ // High halfword 16-bit immediate load, MOVT on page A8-492
+ inst.opcode = Opcode::MOVT;
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(((word >> 12) & 0b1111) as u8)),
+ Operand::Imm32(((word >> 4) & 0xf000) | (word & 0xfff)),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ }
+ 0b01 => {
+ // MSR (immediate), and hints on page A5-204, op==0
+ let op1 = (word >> 16) & 0b1111;
+ match op1 {
+ 0b0000 => {
+
+ },
+ _ => {
+ // Move to Special register, Application level MSR (immediate) on
+ // page A8-499
+ if self.should_is_must {
+ if (word >> 12) & 0b1111 != 0b1111 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ inst.operands = [
+ Operand::StatusRegMask(StatusRegMask::from_raw(op1 as u8)),
+ Operand::Imm32(word & 0xfff),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ inst.opcode = Opcode::MSR;
+ },
+ }
+ }
+ 0b11 => {
+ // MSR (immediate), and hints on page A5-204, op==1
+ if self.should_is_must {
+ if (word >> 12) & 0b1111 != 0b1111 {
+ return Err(DecodeError::Nonconforming);
+ }
+ }
+ inst.operands = [
+ Operand::StatusRegMask(StatusRegMask::from_raw((word >> 16) as u8 & 0b1111 | 0b10000)),
+ Operand::Imm32(word & 0xfff),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
+ inst.opcode = Opcode::MSR;
+ }
+ _ => { unreachable!("impossible bit pattern"); }
+ }
} else {
// Data-processing (immediate)
// Page A5-197
@@ -1685,7 +2876,7 @@ impl Decoder<Instruction> for InstDecoder {
let (Rn, Rd, imm) = {
let rot = word & 0x00000f00;
- let imm = (word & 0x000000ff);
+ 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)
};
@@ -1734,12 +2925,18 @@ impl Decoder<Instruction> for InstDecoder {
0x111 -> LDRBT
*/
inst.opcode = match op {
- 0b010 => Opcode::STRT(add),
- 0b011 => Opcode::LDRT(add),
- 0b110 => Opcode::STRBT(add),
- 0b111 => Opcode::LDRBT(add),
+ 0b010 => Opcode::STRT,
+ 0b011 => Opcode::LDRT,
+ 0b110 => Opcode::STRBT,
+ 0b111 => Opcode::LDRBT,
_ => { unreachable!(); }
};
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16, add, false),
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
} else {
/*
xx0x0 not 0x010 -> STR (imm)
@@ -1747,59 +2944,71 @@ impl Decoder<Instruction> for InstDecoder {
xx1x0 not 0x110 -> STRB (imm)
xx1x1 not 0x111 -> LDRB (imm)
*/
- let pre = (op & 0b10000) != 0;
- let wback = (op & 0b00010) != 0;
+ let P = (op & 0b10000) != 0;
+ let W = (op & 0b00010) != 0;
let op = op & 0b00101;
- inst.opcode = if !pre && wback {
+ inst.opcode = if !P && W {
// Table A5-15
// strt, ldrt, strbt, ldrbt
match op {
- 0b000 => Opcode::STRT(add),
- 0b001 => Opcode::LDRT(add),
- 0b100 => Opcode::STRBT(add),
- 0b101 => Opcode::LDRBT(add),
+ 0b000 => Opcode::STRT,
+ 0b001 => Opcode::LDRT,
+ 0b100 => Opcode::STRBT,
+ 0b101 => Opcode::LDRBT,
_ => { unreachable!("bad bit pattern, table A5-15"); }
}
} else {
match op {
- 0b000 => Opcode::STR(add, pre, wback),
+ 0b000 => Opcode::STR,
0b001 => {
if Rn == 0b1111 {
inst.operands = [
Operand::Reg(Reg::from_u8(Rt)),
- Operand::RegDisp(Reg::from_u8(Rn), imm as u16 as i16),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm as u16, add, W)
+ } else {
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16, add, W)
+ },
Operand::Nothing,
Operand::Nothing,
];
- inst.opcode = Opcode::LDR(add, pre, wback);
+ inst.opcode = Opcode::LDR;
return Ok(());
}
- Opcode::LDR(add, pre, wback)
+ Opcode::LDR
},
- 0b100 => Opcode::STRB(add, pre, wback),
+ 0b100 => Opcode::STRB,
0b101 => {
if Rn == 0b1111 {
inst.operands = [
Operand::Reg(Reg::from_u8(Rt)),
- Operand::RegDisp(Reg::from_u8(Rn), imm as u16 as i16),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm as u16, add, W)
+ } else {
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16, add, W)
+ },
Operand::Nothing,
Operand::Nothing,
];
- inst.opcode = Opcode::LDRB(add, pre, wback);
+ inst.opcode = Opcode::LDRB;
return Ok(());
}
- Opcode::LDRB(add, pre, wback)
+ Opcode::LDRB
},
_ => { unreachable!(); }
}
};
+ inst.operands = [
+ Operand::Reg(Reg::from_u8(Rt)),
+ if P {
+ Operand::RegDerefPreindexOffset(Reg::from_u8(Rn), imm as u16, add, W)
+ } else {
+ Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16, add, W)
+ },
+ Operand::Nothing,
+ Operand::Nothing,
+ ];
}
- inst.operands = [
- Operand::Reg(Reg::from_u8(Rt)),
- Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16, add),
- Operand::Nothing,
- Operand::Nothing,
- ];
},
0b011 => {
// page A5-192 to distinguish the following:
@@ -1808,12 +3017,15 @@ impl Decoder<Instruction> for InstDecoder {
// |c o n d|0 1 1|x x x x|x|x x x x|x x x x|x x x x x|x x|1|x x x x|
// using language from A5-206: A == 1 and B == 1
// so this is media instructions (A5-207)
+ return Err(DecodeError::Incomplete);
} else {
// |c o n d|0 1 1|x x x x|x|x x x x|x x x x|x x x x x|x x|0|x x x x|
// instructions here are A == 1, B == 0 in A5-206
let op = ((word >> 20) & 0x1f) as u8;
- let add = (op & 0b01000) != 0;
+ let P = (op & 0b10000) != 0;
+ let U = (op & 0b01000) != 0;
+ let W = (op & 0b00010) != 0;
/*
xx0x0 not 0x010 -> STR (register)
0x010 -> STRT
@@ -1835,10 +3047,10 @@ impl Decoder<Instruction> for InstDecoder {
0x111 -> LDRBT
*/
inst.opcode = match op {
- 0b010 => Opcode::STRT(add),
- 0b011 => Opcode::LDRT(add),
- 0b110 => Opcode::STRBT(add),
- 0b111 => Opcode::LDRBT(add),
+ 0b010 => Opcode::STRT,
+ 0b011 => Opcode::LDRT,
+ 0b110 => Opcode::STRBT,
+ 0b111 => Opcode::LDRBT,
_ => { unreachable!(); }
};
} else {
@@ -1848,14 +3060,12 @@ impl Decoder<Instruction> for InstDecoder {
xx1x0 not 0x110 -> STRB (imm)
xx1x1 not 0x111 -> LDRB (imm)
*/
- let pre = (op & 0b10000) != 0;
- let wback = (op & 0b00010) != 0;
let op = op & 0b00101;
inst.opcode = match op {
- 0b000 => Opcode::STR(add, pre, wback),
- 0b001 => Opcode::LDR(add, pre, wback),
- 0b100 => Opcode::STRB(add, pre, wback),
- 0b101 => Opcode::LDRB(add, pre, wback),
+ 0b000 => Opcode::STR,
+ 0b001 => Opcode::LDR,
+ 0b100 => Opcode::STRB,
+ 0b101 => Opcode::LDRB,
_ => { unreachable!(); }
};
}
@@ -1865,9 +3075,14 @@ impl Decoder<Instruction> for InstDecoder {
let Rt = (word & 0xf) as u8;
(Rt, shift)
};
+ let Rn = (word >> 16) as u8 & 0b1111;
inst.operands = [
Operand::Reg(Reg::from_u8(Rt)),
- Operand::RegDerefRegShift(RegShift::from_raw(shift)),
+ if P {
+ Operand::RegDerefPreindexRegShift(Reg::from_u8(Rn), RegShift::from_raw(shift), U, W)
+ } else {
+ Operand::RegDerefPostindexRegShift(Reg::from_u8(Rn), RegShift::from_raw(shift), U, false)
+ },
Operand::Nothing,
Operand::Nothing,
];
@@ -1919,6 +3134,7 @@ impl Decoder<Instruction> for InstDecoder {
0b110 | 0b111 => {
// coprocessor instructions and supervisor call
// page A5-213
+ // low bit of 0b110 or 0b111 corresponds to high bit of op1
inst.opcode = Opcode::Incomplete(word);
return Ok(());
},
diff --git a/test/armv7.rs b/test/armv7.rs
index 3931e15..865aeb5 100644
--- a/test/armv7.rs
+++ b/test/armv7.rs
@@ -1,8 +1,34 @@
use yaxpeax_arch::{Arch, Decoder, LengthedInstruction};
-use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, Operand, Opcode, Reg, RegShift};
+use yaxpeax_arm::armv7::{ARMv7, Instruction, ConditionCode, DecodeError, Operand, Opcode, Reg, RegShift};
+
+type InstDecoder = <ARMv7 as Arch>::Decoder;
+
+fn test_invalid_under(decoder: &InstDecoder, data: [u8; 4]) {
+ match decoder.decode(data.to_vec()) {
+ Err(_) => { },
+ Ok(inst) => {
+ panic!(
+ "unexpected successful decode for {:02x}{:02x}{:02x}{:02x}\ngot: {}",
+ data[0], data[1], data[2], data[3],
+ inst
+ );
+ }
+ }
+}
+
+fn test_display_under(decoder: &InstDecoder, data: [u8; 4], expected: &'static str) {
+ let instr = decoder.decode(data.to_vec()).unwrap_or_else(|_| panic!("failed to decode {:02x}{:02x}{:02x}{:02x}", data[0], data[1], data[2], data[3]));
+ let displayed = format!("{}", instr);
+ assert!(
+ displayed == expected,
+ "decode error for {:02x}{:02x}{:02x}{:02x}:\n displayed: {}\n expected: {}\n",
+ data[0], data[1], data[2], data[3],
+ displayed, expected
+ );
+}
fn test_decode(data: [u8; 4], expected: Instruction) {
- let instr = <ARMv7 as Arch>::Decoder::default().decode(data.to_vec()).unwrap();
+ let instr = InstDecoder::default().decode(data.to_vec()).unwrap();
assert!(
instr == expected,
"decode error for {:02x}{:02x}{:02x}{:02x}:\n decoded: {:?}\n expected: {:?}\n",
@@ -11,8 +37,52 @@ fn test_decode(data: [u8; 4], expected: Instruction) {
);
}
+fn test_invalid(data: [u8; 4]) {
+ test_invalid_under(&InstDecoder::default(), data);
+}
+
+fn test_all(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv4(), data, expected);
+ test_display_under(&InstDecoder::armv5(), data, expected);
+ test_display_under(&InstDecoder::armv6(), data, expected);
+ test_display_under(&InstDecoder::armv7(), data, expected);
+}
+fn test_armv5(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv5(), data, expected);
+// test_invalid_under(&InstDecoder::armv4(), data);
+}
+fn test_armv6(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv6(), data, expected);
+// test_invalid_under(&InstDecoder::armv5(), data);
+}
+fn test_armv6t2(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv6t2(), data, expected);
+// test_invalid_under(&InstDecoder::armv6(), data);
+}
+#[allow(dead_code)]
+fn test_armv7(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv7(), data, expected);
+// test_invalid_under(&InstDecoder::armv6(), data);
+}
+fn test_armv7ve(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv7ve(), data, expected);
+// test_invalid_under(&InstDecoder::armv7(), data, expected);
+}
+fn test_arm_security_extensions(data: [u8; 4], expected: &'static str) {
+ test_display_under(&InstDecoder::armv7vese(), data, expected);
+// test_invalid_under(&InstDecoder::armv7ve(), data, expected);
+}
+
+fn test_nonconformant(data: [u8; 4]) {
+ let result = InstDecoder::default().decode(data.to_vec());
+ assert!(
+ result == Err(DecodeError::Nonconforming),
+ "got bad result: {:?} from {:#x?}", result, data
+ );
+}
+
fn test_display(data: [u8; 4], expected: &'static str) {
- let instr = <ARMv7 as Arch>::Decoder::default().decode(data.to_vec()).unwrap();
+ let instr = InstDecoder::default().decode(data.to_vec()).unwrap();
let text = format!("{}", instr);
assert!(
text == expected,
@@ -29,10 +99,10 @@ fn test_decode_str_ldr() {
[0x24, 0xc0, 0x9f, 0xe5],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::LDR(true, true, false),
+ opcode: Opcode::LDR,
operands: [
Operand::Reg(Reg::from_u8(12)),
- Operand::RegDisp(Reg::from_u8(15), 0x24),
+ Operand::RegDerefPreindexOffset(Reg::from_u8(15), 0x24, true, false),
Operand::Nothing,
Operand::Nothing,
],
@@ -43,10 +113,10 @@ fn test_decode_str_ldr() {
[0x10, 0x00, 0x9f, 0xe5],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::LDR(true, true, false),
+ opcode: Opcode::LDR,
operands: [
Operand::Reg(Reg::from_u8(0)),
- Operand::RegDisp(Reg::from_u8(15), 0x10),
+ Operand::RegDerefPreindexOffset(Reg::from_u8(15), 0x10, true, false),
Operand::Nothing,
Operand::Nothing,
],
@@ -57,10 +127,10 @@ fn test_decode_str_ldr() {
[0x04, 0x20, 0x2d, 0xe5],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::STR(false, true, true),
+ opcode: Opcode::STR,
operands: [
Operand::Reg(Reg::from_u8(2)),
- Operand::RegDerefPostindexOffset(Reg::from_u8(13), 4, false),
+ Operand::RegDerefPreindexOffset(Reg::from_u8(13), 4, false, true),
Operand::Nothing,
Operand::Nothing,
],
@@ -71,10 +141,10 @@ fn test_decode_str_ldr() {
[0x04, 0x00, 0x2d, 0xe5],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::STR(false, true, true),
+ opcode: Opcode::STR,
operands: [
Operand::Reg(Reg::from_u8(0)),
- Operand::RegDerefPostindexOffset(Reg::from_u8(13), 4, false),
+ Operand::RegDerefPreindexOffset(Reg::from_u8(13), 4, false, true),
Operand::Nothing,
Operand::Nothing,
],
@@ -85,10 +155,10 @@ fn test_decode_str_ldr() {
[0x14, 0x30, 0x9f, 0xe5],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::LDR(true, true, false),
+ opcode: Opcode::LDR,
operands: [
Operand::Reg(Reg::from_u8(3)),
- Operand::RegDisp(Reg::from_u8(15), 0x14),
+ Operand::RegDerefPreindexOffset(Reg::from_u8(15), 0x14, true, false),
Operand::Nothing,
Operand::Nothing,
],
@@ -99,16 +169,40 @@ fn test_decode_str_ldr() {
[0x14, 0x20, 0x9f, 0xe5],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::LDR(true, true, false),
+ opcode: Opcode::LDR,
operands: [
Operand::Reg(Reg::from_u8(2)),
- Operand::RegDisp(Reg::from_u8(15), 0x14),
+ Operand::RegDerefPreindexOffset(Reg::from_u8(15), 0x14, true, false),
Operand::Nothing,
Operand::Nothing,
],
s: false
}
);
+ test_all([0x10, 0x00, 0x7f, 0xe5], "ldrb r0, [pc, -0x10]!");
+ test_all([0x10, 0x00, 0x3f, 0xe5], "ldr r0, [pc, -0x10]!");
+ test_all([0x10, 0x00, 0x7f, 0xe4], "ldrbt r0, [pc], -0x10");
+ test_all([0x10, 0x00, 0x3f, 0xe4], "ldrt r0, [pc], -0x10");
+ test_all([0x10, 0x00, 0x4f, 0xe4], "strb r0, [pc], -0x10");
+ // Extra load/store instructions A5.2.8, page A5-201
+ test_all([0xbb, 0x38, 0xa5, 0xe1], "strh r3, [r5, fp]!");
+ test_all([0xbb, 0x38, 0xb5, 0xe1], "ldrh r3, [r5, fp]!");
+ test_all([0xbb, 0x38, 0xe5, 0xe1], "strh r3, [r5, 0x8b]!");
+ test_all([0xbb, 0x38, 0xf5, 0xe1], "ldrh r3, [r5, 0x8b]!");
+ test_armv5([0xdb, 0x48, 0xa6, 0xe1], "ldrd r4, r5, [r6, fp]!");
+ test_invalid([0xdb, 0x38, 0xa5, 0xe1]);
+ test_all([0xdb, 0x38, 0xb5, 0xe1], "ldrsb r3, [r5, fp]!");
+ test_armv5([0xdb, 0x48, 0xe6, 0xe1], "ldrd r4, r5, [r6, 0x8b]!");
+ test_invalid([0xdb, 0x38, 0xe5, 0xe1]);
+ test_all([0xdb, 0x38, 0xf5, 0xe1], "ldrsb r3, [r5, 0x8b]!");
+ test_invalid([0xfb, 0x38, 0xa5, 0xe1]);
+ test_all([0xfb, 0x48, 0xa6, 0xe1], "strd r4, r5, [r6, fp]!");
+ test_all([0xfb, 0x38, 0xb5, 0xe1], "ldrsh r3, [r5, fp]!");
+ test_invalid([0xfb, 0x38, 0xe5, 0xe1]);
+ test_all([0xfb, 0x48, 0xe6, 0xe1], "strd r4, r5, [r6, 0x8b]!");
+ test_all([0xfb, 0x38, 0xf5, 0xe1], "ldrsh r3, [r5, 0x8b]!");
+ test_all([0xfb, 0x38, 0xff, 0xe1], "ldrsh r3, [pc, 0x8b]!");
+
}
#[test]
@@ -169,6 +263,7 @@ fn test_data_imm() {
#[test]
fn test_decode_misc() {
+ test_armv5([0x32, 0xff, 0x2f, 0xe1], "blx r2");
test_display(
[0x13, 0x5f, 0x6f, 0xe1],
"clz r5, r3"
@@ -187,8 +282,49 @@ fn test_decode_misc() {
);
test_display(
[0xe8, 0x10, 0x9f, 0xe5],
- "ldr r1, [pc, #0x3a0]"
- );
+ "ldr r1, [pc, 0xe8]"
+ );
+ // https://www.raspberrypi.org/forums/viewtopic.php?p=967759&sid=25fa58d95208c0c76b579012ca693380#p967759
+ // it looks like gcc toolchains older than 6.1(?) don't support -march=armv7e
+ test_armv7ve([0x6e, 0x00, 0x60, 0xe1], "eret");
+ test_armv5([0x76, 0x00, 0x23, 0xe1], "bkpt 0x3006");
+ // ARMv7VE only, capstone (not-next) doesn't have this, no secondary confirmation yet
+ test_armv7ve([0x76, 0x00, 0x43, 0xe1], "hvc 0x3006");
+ test_arm_security_extensions([0x76, 0x00, 0x63, 0xe1], "smc 0x3006");
+ test_all([0x6e, 0xf0, 0x28, 0xe3], "msr apsr_nzcvq, 0x6e");
+ test_all([0x6e, 0xf0, 0x24, 0xe3], "msr apsr_g, 0x6e");
+ test_all([0x6e, 0xf0, 0x2c, 0xe3], "msr apsr_nzcvqg, 0x6e");
+
+ test_all([0x6e, 0xf0, 0x21, 0xe3], "msr cpsr_c, 0x6e");
+ test_all([0x6e, 0xf0, 0x22, 0xe3], "msr cpsr_x, 0x6e");
+ test_all([0x6e, 0xf0, 0x23, 0xe3], "msr cpsr_xc, 0x6e");
+ test_all([0x6e, 0xf0, 0x25, 0xe3], "msr cpsr_sc, 0x6e");
+ test_all([0x6e, 0xf0, 0x26, 0xe3], "msr cpsr_sx, 0x6e");
+ test_all([0x6e, 0xf0, 0x27, 0xe3], "msr cpsr_sxc, 0x6e");
+ test_all([0x6e, 0xf0, 0x29, 0xe3], "msr cpsr_fc, 0x6e");
+ test_all([0x6e, 0xf0, 0x2a, 0xe3], "msr cpsr_fx, 0x6e");
+ test_all([0x6e, 0xf0, 0x2b, 0xe3], "msr cpsr_fxc, 0x6e");
+ test_all([0x6e, 0xf0, 0x2d, 0xe3], "msr cpsr_fsc, 0x6e");
+ test_all([0x6e, 0xf0, 0x2e, 0xe3], "msr cpsr_fsx, 0x6e");
+ test_all([0x6e, 0xf0, 0x2f, 0xe3], "msr cpsr_fsxc, 0x6e");
+ test_all([0x6e, 0xf0, 0x60, 0xe3], "msr spsr, 0x6e");
+ test_all([0x6e, 0xf0, 0x61, 0xe3], "msr spsr_c, 0x6e");
+ test_all([0x6e, 0xf0, 0x62, 0xe3], "msr spsr_x, 0x6e");
+ test_all([0x6e, 0xf0, 0x63, 0xe3], "msr spsr_xc, 0x6e");
+ test_all([0x6e, 0xf0, 0x64, 0xe3], "msr spsr_s, 0x6e");
+ test_all([0x6e, 0xf0, 0x65, 0xe3], "msr spsr_sc, 0x6e");
+ test_all([0x6e, 0xf0, 0x66, 0xe3], "msr spsr_sx, 0x6e");
+ test_all([0x6e, 0xf0, 0x67, 0xe3], "msr spsr_sxc, 0x6e");
+ test_all([0x6e, 0xf0, 0x68, 0xe3], "msr spsr_f, 0x6e");
+ test_all([0x6e, 0xf0, 0x69, 0xe3], "msr spsr_fc, 0x6e");
+ test_all([0x6e, 0xf0, 0x6a, 0xe3], "msr spsr_fx, 0x6e");
+ test_all([0x6e, 0xf0, 0x6b, 0xe3], "msr spsr_fxc, 0x6e");
+ test_all([0x6e, 0xf0, 0x6c, 0xe3], "msr spsr_fs, 0x6e");
+ test_all([0x6e, 0xf0, 0x6d, 0xe3], "msr spsr_fsc, 0x6e");
+ test_all([0x6e, 0xf0, 0x6e, 0xe3], "msr spsr_fsx, 0x6e");
+ test_all([0x6e, 0xf0, 0x6f, 0xe3], "msr spsr_fsxc, 0x6e");
+ test_armv6t2([0x45, 0x67, 0x01, 0xe3], "mov r6, 0x1745");
+ test_armv6t2([0x45, 0x67, 0x41, 0xe3], "movt r6, 0x1745");
}
#[test]
@@ -197,10 +333,10 @@ fn test_decode_pop() {
[0x04, 0x10, 0x9d, 0xe4],
Instruction {
condition: ConditionCode::AL,
- opcode: Opcode::LDR(true, false, false),
+ opcode: Opcode::LDR,
operands: [
Operand::Reg(Reg::from_u8(1)),
- Operand::RegDerefPostindexOffset(Reg::from_u8(13), 0x4, true),
+ Operand::RegDerefPostindexOffset(Reg::from_u8(13), 0x4, true, false),
Operand::Nothing,
Operand::Nothing,
],
@@ -265,6 +401,8 @@ fn test_decode_mov() {
s: false
}
);
+ test_display([0x0d, 0x20, 0xa0, 0xe1], "mov r2, sp");
+ test_nonconformant([0x0d, 0x20, 0xa1, 0xe1]);
test_decode(
[0x00, 0xb0, 0xa0, 0xe3],
Instruction {
@@ -335,6 +473,54 @@ fn test_decode_arithmetic() {
}
#[test]
+fn test_unconditional() {
+ test_armv6([0x00, 0x0a, 0x15, 0xf8], "rfeda r5");
+ test_nonconformant([0x10, 0x0a, 0x15, 0xf8]);
+ test_armv6([0x00, 0x0a, 0x1f, 0xf8], "rfeda pc");
+ test_armv6([0x00, 0x0a, 0xb5, 0xf8], "rfeia r5!");
+ test_armv6([0x00, 0x0a, 0xb5, 0xf9], "rfeib r5!");
+ test_armv6([0x0f, 0x05, 0x4d, 0xf8], "srsda sp, 0xf");
+ test_nonconformant([0xff, 0x05, 0xed, 0xf8]);
+ test_armv6([0x0f, 0x05, 0x4d, 0xf8], "srsda sp, 0xf");
+ test_armv6([0x0f, 0x05, 0xed, 0xf9], "srsib sp!, 0xf");
+ test_armv6([0x0f, 0x05, 0xed, 0xf8], "srsia sp!, 0xf");
+ test_armv5([0x01, 0x02, 0x03, 0xfb], "blx $+0xc0806");
+ test_armv5([0x01, 0x02, 0x03, 0xfa], "blx $+0xc0804");
+ test_armv5([0x12, 0x34, 0xcf, 0xfc], "stc2l p4, c3, [pc], {0x12}");
+ test_armv5([0x12, 0x34, 0xdf, 0xfc], "ldc2l p4, c3, [pc], {0x12}");
+ test_armv5([0x34, 0x78, 0xff, 0xfc], "ldc2l p8, c7, [pc], 0xd0");
+ test_invalid([0x34, 0x78, 0x1f, 0xfc]);
+ test_armv5([0x34, 0x78, 0x9f, 0xfc], "ldc2 p8, c7, [pc], {0x34}");
+ test_armv5([0x34, 0x78, 0xbf, 0xfc], "ldc2 p8, c7, [pc], 0xd0");
+ test_armv5([0x34, 0x78, 0xbf, 0xfd], "ldc2 p8, c7, [pc, 0xd0]!");
+ test_armv5([0x34, 0x78, 0x9f, 0xfd], "ldc2 p8, c7, [pc, 0xd0]");
+ test_armv5([0x34, 0x78, 0x1f, 0xfd], "ldc2 p8, c7, [pc, -0xd0]");
+ test_armv5([0x34, 0x78, 0xdf, 0xfc], "ldc2l p8, c7, [pc], {0x34}");
+ test_armv6([0x34, 0x78, 0x5a, 0xfc], "mrrc2 p8, 3, r7, r10, c4");
+ // Rt/Rt2 may not be r15
+ test_invalid([0x34, 0x78, 0x5f, 0xfc]);
+ test_armv6([0x34, 0x78, 0x4a, 0xfc], "mcrr2 p8, 3, r7, r10, c4");
+ // Rt/Rt2 may not be r15
+ test_invalid([0x34, 0x78, 0x4f, 0xfc]);
+ test_armv5([0x34, 0x78, 0x4f, 0xfe], "mcr2 p8, 2, r7, c15, c4, 1");
+ test_armv5([0x34, 0x78, 0x5f, 0xfe], "mrc2 p8, 2, r7, c15, c4, 1");
+ test_armv5([0x24, 0x78, 0x5f, 0xfe], "cdp2 p8, 5, c7, c15, c4, 1");
+ test_armv5([0x24, 0x78, 0x4f, 0xfe], "cdp2 p8, 4, c7, c15, c4, 1");
+}
+
+#[test]
+fn test_saturating_addsub() {
+ test_armv5([0x50, 0x10, 0x64, 0xe1], "qdsub r1, r0, r4");
+ test_nonconformant([0x50, 0x14, 0x64, 0xe1]);
+ test_armv5([0x50, 0x10, 0x44, 0xe1], "qdadd r1, r0, r4");
+ test_nonconformant([0x50, 0x14, 0x44, 0xe1]);
+ test_armv5([0x50, 0x10, 0x24, 0xe1], "qsub r1, r0, r4");
+ test_nonconformant([0x50, 0x14, 0x24, 0xe1]);
+ test_armv5([0x50, 0x10, 0x04, 0xe1], "qadd r1, r0, r4");
+ test_nonconformant([0x50, 0x14, 0x04, 0xe1]);
+}
+
+#[test]
fn test_decode_mul() {
test_decode(
[0x9c, 0x7d, 0x0b, 0x00],
@@ -448,7 +634,7 @@ static INSTRUCTION_BYTES: [u8; 4 * 60] = [
fn test_decode_span() {
let mut i = 0u32;
while i < INSTRUCTION_BYTES.len() as u32 {
- let instr = <ARMv7 as Arch>::Decoder::default().decode(INSTRUCTION_BYTES[(i as usize)..].iter().cloned()).unwrap();
+ let instr = InstDecoder::default().decode(INSTRUCTION_BYTES[(i as usize)..].iter().cloned()).unwrap();
println!(
"Decoded {:02x}{:02x}{:02x}{:02x}: {}", //{:?}\n {}",
INSTRUCTION_BYTES[i as usize],
@@ -532,7 +718,7 @@ pub fn bench_60000_instrs(b: &mut Bencher) {
b.iter(|| {
for i in (0..1000) {
let mut iter = INSTRUCTION_BYTES.iter().map(|x| *x);
- let decoder = <ARMv7 as Arch>::Decoder::default();
+ let decoder = InstDecoder::default();
let mut result = Instruction::default();
loop {
match decoder.decode_into(&mut result, &mut iter) {