diff options
-rw-r--r-- | src/armv7.rs | 1728 | ||||
-rw-r--r-- | test/armv7.rs | 228 |
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) { |