diff options
author | iximeow <me@iximeow.net> | 2020-03-15 04:43:49 -0700 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2020-03-15 04:43:49 -0700 |
commit | 0eef8df038ff3a83f31aeeb3c58d5bf652c400ec (patch) | |
tree | ac0b14aa49c8fba5f72bae4937fc2cf0b0a06a9d /src | |
parent | f4f2c1befecfc841b2a7f5906492b94b16560b9c (diff) |
armv7 support through neon, some still missing (mainly coproc instrs)
Diffstat (limited to 'src')
-rw-r--r-- | src/armv7.rs | 1728 |
1 files changed, 1472 insertions, 256 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(()); }, |