From 0eef8df038ff3a83f31aeeb3c58d5bf652c400ec Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 15 Mar 2020 04:43:49 -0700 Subject: armv7 support through neon, some still missing (mainly coproc instrs) --- src/armv7.rs | 1728 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 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>(reg: Reg, colors: &Y) -> impl > ShowContextual 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 > ShowContextual { - 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 > ShowContextual { 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 > Colorize> Colorize { 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 > Colorize { 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 > Colorize 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 { +/* + * Is one of: + * • _, encoded with R==0. + * • ELR_hyp, encoded with R==0. + * • SPSR_, 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 > Colorize 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 > Colorize { 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::RegDerefPreindexRegShift(reg, shift, add, wback) => { + format_reg_shift_mem(f, *reg, *shift, *add, true, *wback, colors) } - Operand::RegDerefPostindexOffset(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::RegDerefPreindexOffset(reg, offs, add) => { - write!(f, "[{}], {}{:#x}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, offs) + Operand::RegDerefPreindexOffset(reg, offs, add, wback) => { + format_reg_imm_mem(f, *reg, *offs, *add, true, *wback, colors) } - Operand::RegDerefPostindexReg(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) => { - write!(f, "[{}], {}{}", reg_name_colorize(*reg, colors), if *add { "" } else { "-" }, reg_name_colorize(*offsreg, colors)) + 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 > Colorize { if *imm < 0 { - write!(f, " $-{:#x}", (-*imm) * 4) + write!(f, "$-{:#x}", (-*imm) * 4) } else { - write!(f, " $+{:#x}", *imm * 4) + write!(f, "$+{:#x}", *imm * 4) } } - Operand::ASPR => { write!(f, "aspr") }, - Operand::CSPR => { write!(f, "cspr") }, + Operand::BranchThumbOffset(imm) => { + if *imm < 0 { + write!(f, "$-{:#x}", (-*imm) * 2) + } else { + write!(f, "$+{:#x}", *imm * 2) + } + } + 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>(f: &mut T, m #[allow(non_snake_case)] fn format_shift>(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>(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>(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 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 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