diff options
author | iximeow <me@iximeow.net> | 2020-02-17 18:33:11 -0800 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2020-02-17 18:33:11 -0800 |
commit | 61541d356e6c9d378a85697778685b410fcbee1b (patch) | |
tree | b590df784a1219d94d18f211785bd20876d7cfb4 /src | |
parent | 685ce723ddf0bd9dcc0998f44a73f5ae07fdc0c5 (diff) |
entirely replace armv7 operands
Operands was an enum covering all forms of operands and data for each
operand for all instructions. this is hard to iterate, and hard to work
with when a single operand is the one of interest, so it's now replaced
by an array of `Operand` enum instances like other architectures.
in the course of this change, several forms of decoding are broken,
while adding support for some earlier-unsupported multiplies and
instructions like msr and clz
also clearly note which document the comments mentioning page
numbers/figures is referencing
Diffstat (limited to 'src')
-rw-r--r-- | src/armv7.rs | 1016 |
1 files changed, 781 insertions, 235 deletions
diff --git a/src/armv7.rs b/src/armv7.rs index 50cde07..eb86faa 100644 --- a/src/armv7.rs +++ b/src/armv7.rs @@ -1,3 +1,7 @@ +/// Manual references in this crate, both figure and page numbers, are with respect to the document +/// `DDI0406C_d_armv7ar_arm.pdf` +/// `sha256: 294668ae6480133b32d85e9567cc77c5eb0e1232decdf42cac7ab480e884f6e0` + //#[cfg(feature="use-serde")] //use serde::{Serialize, Deserialize}; @@ -5,24 +9,46 @@ use std::fmt::{self, Display, Formatter}; use yaxpeax_arch::{Arch, Colorize, Decoder, LengthedInstruction, NoColors, ShowContextual, YaxColors}; -pub struct ConditionedOpcode(pub Opcode, pub ConditionCode); +pub struct ConditionedOpcode(pub Opcode, pub bool, pub ConditionCode); impl Display for ConditionedOpcode { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!(f, "{}{}", self.0, self.1) + write!(f, "{}{}{}", self.0, if self.1 { "s" } else { "" }, self.2) } } pub struct NoContext; +fn reg_name_colorize<C: fmt::Display, Y: YaxColors<C>>(reg: Reg, colors: &Y) -> impl fmt::Display { + match reg.number() { + 0 => colors.register("r0"), + 1 => colors.register("r1"), + 2 => colors.register("r2"), + 3 => colors.register("r3"), + 4 => colors.register("r4"), + 5 => colors.register("r5"), + 6 => colors.register("r6"), + 7 => colors.register("r7"), + 8 => colors.register("r8"), + 9 => colors.register("sb"), + 10 => colors.register("r10"), + 11 => colors.register("fp"), + 12 => colors.register("ip"), + 13 => colors.register("sp"), + 14 => colors.register("lr"), + 15 => colors.program_counter("pc"), + _ => { unreachable!(); } + } +} + #[allow(non_snake_case)] impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u32, NoContext, Color, T, Y> for Instruction { fn contextualize(&self, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { match self.opcode { Opcode::LDR(true, false, false) => { match self.operands { - Operands::TwoRegImm(13, Rt, 4) => { - ConditionedOpcode(Opcode::POP, self.condition).colorize(colors, out)?; + [Operand::Reg(Rt), Operand::RegDisp(Reg { bits: 13 }, 4), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(Opcode::POP, self.s, self.condition).colorize(colors, out)?; return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); }, _ => {} @@ -30,8 +56,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3 }, Opcode::STR(false, true, true) => { match self.operands { - Operands::TwoRegImm(13, Rt, 4) => { - ConditionedOpcode(Opcode::PUSH, self.condition).colorize(colors, out)?; + [Operand::Reg(Rt), Operand::RegDisp(Reg { bits: 13 }, 4), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(Opcode::PUSH, self.s, self.condition).colorize(colors, out)?; return write!(out, " {{{}}}", reg_name_colorize(Rt, colors)); }, _ => {} @@ -40,8 +66,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3 Opcode::LDM(true, false, true, _usermode) => { // TODO: what indicates usermode in the ARM syntax? match self.operands { - Operands::RegRegList(13, list) => { - ConditionedOpcode(Opcode::POP, self.condition).colorize(colors, out)?; + [Operand::Reg(Reg { bits: 13 }), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(Opcode::POP, self.s, self.condition).colorize(colors, out)?; write!(out, " ")?; return format_reg_list(out, list, colors); } @@ -51,8 +77,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3 Opcode::STM(false, true, true, _usermode) => { // TODO: what indicates usermode in the ARM syntax? match self.operands { - Operands::RegRegList(13, list) => { - ConditionedOpcode(Opcode::PUSH, self.condition).colorize(colors, out)?; + [Operand::Reg(Reg { bits: 13 }), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(Opcode::PUSH, self.s, self.condition).colorize(colors, out)?; write!(out, " ")?; return format_reg_list(out, list, colors); } @@ -67,41 +93,62 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3 Opcode::STR(add, pre, wback) | Opcode::STRB(add, pre, wback) | Opcode::LDRB(add, pre, wback) => { - match self.operands { - Operands::TwoRegImm(Rn, Rt, imm) => { - ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?; + match &self.operands { + [Operand::Reg(Rn), Operand::Reg(Rt), Operand::Imm12(imm), Operand::Nothing] => { + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; + write!( + out, " {}, ", + reg_name_colorize(*Rt, colors), + )?; + return format_reg_imm_mem(out, *Rn, *imm, add, pre, wback, colors); + } + [Operand::Reg(Rn), Operand::RegDisp(Rt, offset), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; write!( out, " {}, ", - reg_name_colorize(Rt, colors), + reg_name_colorize(*Rn, colors), )?; - return format_reg_imm_mem(out, Rn, imm, add, pre, wback, colors); + let (add, offset) = if *offset < 0 { + (false, -*offset as u16) + } else { + (true, *offset as u16) + }; + return format_reg_imm_mem(out, *Rt, offset, add, true, false, colors); } // TODO: this might not be necessary - Operands::RegImm(Rt, imm) => { - ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?; + [Operand::Reg(Rt), Operand::Imm12(imm), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; write!( out, " {}, ", - reg_name_colorize(Rt, colors) + reg_name_colorize(*Rt, colors) )?; - return format_reg_imm_mem(out, 15, imm, add, pre, wback, colors); + return format_reg_imm_mem(out, Reg::from_u8(15), *imm, add, pre, wback, colors); }, - Operands::ThreeOperandWithShift(Rd, Rn, Rm, shift) => { - ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?; + [Operand::Reg(Rd), Operand::Reg(Rn), Operand::RegShift(shift), Operand::Nothing] => { + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; write!( out, " {}, ", - reg_name_colorize(Rn, colors) + reg_name_colorize(*Rn, colors) )?; - return format_reg_shift_mem(out, Rd, Rm, shift, add, pre, wback, colors); + return format_reg_shift_mem(out, *Rd, *shift, add, pre, wback, colors); } - _ => { unreachable!(); } + [Operand::Reg(Rt), Operand::RegDerefPostindexOffset(Rn, offset), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; + write!( + out, " {}, ", + reg_name_colorize(*Rt, colors) + )?; + return format_reg_imm_mem(out, *Rt, *offset, true, false, true, colors); + } + o => { println!("other str/ldr operands: {:?}", o); unreachable!(); } } } // TODO: [add, pre, usermode] Opcode::STM(_add, _pre, wback, _usermode) | Opcode::LDM(_add, _pre, wback, _usermode) => { match self.operands { - Operands::RegRegList(Rr, list) => { - ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?; + [Operand::Reg(Rr), Operand::RegList(list), Operand::Nothing, Operand::Nothing] => { + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; write!( out, " {}{}, ", reg_name_colorize(Rr, colors), @@ -116,55 +163,23 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u3 write!(out, "incomplete: {:#x}", word) }, _ => { - ConditionedOpcode(self.opcode, self.condition).colorize(colors, out)?; - match self.operands { - Operands::RegisterList(list) => { - write!(out, " ")?; - format_reg_list(out, list, colors)?; - }, - Operands::OneOperand(a) => { - write!(out, " {}", reg_name_colorize(a, colors))?; - }, - Operands::TwoOperand(a, b) => { - write!(out, " {}, {}", reg_name_colorize(a, colors), reg_name_colorize(b, colors))?; - }, - Operands::RegImm(a, imm) => { - write!(out, " {}, {:#x}", reg_name_colorize(a, colors), imm)?; - }, - Operands::RegRegList(r, list) => { - write!(out, " {}, ", reg_name_colorize(r, colors))?; - format_reg_list(out, list, colors)?; - }, - Operands::TwoRegImm(_a, _b, _imm) => { - // TODO: - write!(out, " <unimplemented>")?; - }, - Operands::ThreeOperand(a, b, c) => { - write!(out, " {}, {}, {}", reg_name_colorize(a, colors), reg_name_colorize(b, colors), reg_name_colorize(c, colors))?; - }, - Operands::ThreeOperandImm(_a, _b, _imm) => { - // TODO: - write!(out, " <unimplemented>")?; - }, - Operands::ThreeOperandWithShift(a, b, c, shift) => { - write!(out, " {}, {}, ", reg_name_colorize(a, colors), reg_name_colorize(b, colors))?; - format_shift(out, c, shift, colors)?; - }, - Operands::MulThreeRegs(a, b, c) => { - write!(out, " {}, {}, {}", reg_name_colorize(a, colors), reg_name_colorize(b, colors), reg_name_colorize(c, colors))?; - }, - Operands::MulFourRegs(_a, _b, _c, _d) => { - // TODO: - write!(out, " <unimplemented>")?; - }, - Operands::BranchOffset(imm) => { - if imm < 0 { - write!(out, " $-{:#x}", (-imm) * 4)?; - } else { - write!(out, " $+{:#x}", imm * 4)?; - } + ConditionedOpcode(self.opcode, self.s, self.condition).colorize(colors, out)?; + let mut ops = self.operands.iter(); + if let Some(first_op) = ops.next() { + write!(out, " ")?; + first_op.colorize(colors, out)?; + } else { + return Ok(()); + } + + for op in ops { + if let Operand::Nothing = op { + break; } - }; + write!(out, ", ")?; + op.colorize(colors, out)?; + } + Ok(()) } } @@ -200,6 +215,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color Opcode::SBC | Opcode::RSC | + Opcode::CLZ | + Opcode::MUL | Opcode::MLA | Opcode::UMAAL | @@ -207,7 +224,12 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color Opcode::UMULL | Opcode::UMLAL | Opcode::SMULL | - Opcode::SMLAL => { write!(out, "{}", colors.arithmetic_op(self)) }, + Opcode::SMUL(_, _) | + Opcode::SMAL(_, _) | + Opcode::SMLA(_, _) | + Opcode::SMLAW(_) | + Opcode::SMLAL | + Opcode::SMLAL_halfword(_, _) => { write!(out, "{}", colors.arithmetic_op(self)) }, Opcode::PUSH | Opcode::POP => { write!(out, "{}", colors.stack_op(self)) }, @@ -243,6 +265,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color Opcode::STRBT(_) | Opcode::SWP | Opcode::SWPB | + Opcode::MSR | + Opcode::MRS | Opcode::MOV | Opcode::MVN => { write!(out, "{}", colors.data_op(self)) }, } @@ -261,6 +285,7 @@ impl Display for Opcode { Opcode::BLX => { write!(f, "blx") }, Opcode::BX => { write!(f, "bx") }, Opcode::BXJ => { write!(f, "bxj") }, + Opcode::CLZ => { write!(f, "clz") }, Opcode::AND => { write!(f, "and") }, Opcode::EOR => { write!(f, "eor") }, Opcode::SUB => { write!(f, "sub") }, @@ -275,6 +300,8 @@ impl Display for Opcode { Opcode::CMN => { write!(f, "cmn") }, Opcode::ORR => { write!(f, "orr") }, Opcode::MOV => { write!(f, "mov") }, + Opcode::MSR => { write!(f, "msr") }, + Opcode::MRS => { write!(f, "mrs") }, Opcode::BIC => { write!(f, "bic") }, Opcode::MVN => { write!(f, "mvn") }, Opcode::LSL => { write!(f, "lsl") }, @@ -316,7 +343,22 @@ impl Display for Opcode { Opcode::UMULL => { write!(f, "umull") }, Opcode::UMLAL => { write!(f, "umlal") }, Opcode::SMULL => { write!(f, "smull") }, - Opcode::SMLAL => { write!(f, "smlal") } + Opcode::SMLA(first, second) => { + write!(f, "smla{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) + } + Opcode::SMLAL => { write!(f, "smlal") }, + Opcode::SMLAL_halfword(first, second) => { + write!(f, "smlal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) + } + Opcode::SMUL(first, second) => { + write!(f, "smul{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) + }, + Opcode::SMAL(first, second) => { + write!(f, "smal{}{}", if *first { "t" } else { "b" }, if *second { "t" } else { "b" }) + }, + Opcode::SMLAW(second) => { + write!(f, "smlaw{}", if *second { "t" } else { "b" }) + }, } } } @@ -360,6 +402,9 @@ pub enum Opcode { RRX, ROR, ADR, + MSR, + MRS, + CLZ, LDREXH, STREXH, LDREXB, @@ -387,7 +432,12 @@ pub enum Opcode { UMULL, UMLAL, SMULL, - SMLAL + SMUL(bool, bool), + SMLA(bool, bool), + SMLAL, + SMLAL_halfword(bool, bool), + SMAL(bool, bool), + SMLAW(bool), } static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ @@ -410,32 +460,178 @@ static DATA_PROCESSING_OPCODES: [Opcode; 16] = [ ]; #[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum ShiftSpec { - Immediate(u8), - Register(u8) +#[repr(transparent)] +pub struct RegShift { + data: u16 +} + +impl RegShift { + fn into_shift(&self) -> RegShiftStyle { + if self.data & 0b1000 == 0 { + RegShiftStyle::RegImm(RegImmShift { data: self.data }) + } else { + RegShiftStyle::RegReg(RegRegShift { data: self.data }) + } + } + + fn from(data: u16) -> Self { + RegShift { data } + } +} + +pub enum RegShiftStyle { + RegImm(RegImmShift), + RegReg(RegRegShift), +} + +#[repr(transparent)] +pub struct RegRegShift { + data: u16 +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum ShiftStyle { + LSL = 0, + LSR = 1, + ASR = 2, + ROR = 3, +} + +impl ShiftStyle { + fn from(bits: u8) -> ShiftStyle { + match bits { + 0b00 => ShiftStyle::LSL, + 0b01 => ShiftStyle::LSR, + 0b10 => ShiftStyle::ASR, + 0b11 => ShiftStyle::ROR, + _ => unreachable!("bad ShiftStyle index") + } + } +} + +impl RegRegShift { + pub fn shifter(&self) -> Reg { + Reg::from_u8((self.data >> 8) as u8 & 0b1111) + } + pub fn stype(&self) -> ShiftStyle { + ShiftStyle::from((self.data >> 5) as u8 & 0b11) + } + pub fn shiftee(&self) -> Reg { + Reg::from_u8(self.data as u8 & 0b1111) + } +} + +#[repr(transparent)] +pub struct RegImmShift { + data: u16 +} + +impl RegImmShift { + pub fn imm(&self) -> u8 { + (self.data >> 7) as u8 & 0b11111 + } + pub fn stype(&self) -> ShiftStyle { + ShiftStyle::from((self.data >> 5) as u8 & 0b11) + } + pub fn shiftee(&self) -> Reg { + Reg::from_u8(self.data as u8 & 0b1111) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(transparent)] +pub struct Reg { + bits: u8 +} + +impl Reg { + pub fn from_u8(bits: u8) -> Reg { + if bits > 0b1111 { + panic!("register number out of range"); + } + + Reg { bits } + } + + pub fn number(&self) -> u8 { + self.bits + } } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum Operands { - RegisterList(u16), - OneOperand(u8), - TwoOperand(u8, u8), - RegImm(u8, u32), - RegRegList(u8, u16), - TwoRegImm(u8, u8, u32), - ThreeOperand(u8, u8, u8), - ThreeOperandImm(u8, u8, u16), - ThreeOperandWithShift(u8, u8, u8, ShiftSpec), - MulThreeRegs(u8, u8, u8), - MulFourRegs(u8, u8, u8, u8), - BranchOffset(i32) +pub enum Operand { + Reg(Reg), + RegList(u16), + RegDeref(Reg), + RegDisp(Reg, i16), + RegShift(RegShift), + RegDerefRegShift(RegShift), + RegDerefPostindexOffset(Reg, u16), + RegDerefPostindexReg(Reg, Reg), + Imm12(u16), + Imm32(u32), + BranchOffset(i32), + ASPR, + CSPR, + SPSR, + Nothing, +} + +impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color, Y> for Operand { + fn colorize(&self, colors: &Y, f: &mut T) -> fmt::Result { + match self { + Operand::RegList(list) => { + format_reg_list(f, *list, colors) + } + Operand::Reg(reg) => { + write!(f, "{}", reg_name_colorize(*reg, colors)) + } + Operand::RegDeref(reg) => { + write!(f, "[{}]", reg_name_colorize(*reg, colors)) + } + Operand::RegDisp(reg, imm) => { + write!(f, "[{} + {:#x}]", reg_name_colorize(*reg, colors), imm) + } + Operand::RegShift(shift) => { + format_shift(f, *shift, colors) + } + Operand::RegDerefRegShift(shift) => { + write!(f, "[")?; + format_shift(f, *shift, colors)?; + write!(f, "]") + } + Operand::RegDerefPostindexOffset(reg, offs) => { + write!(f, "[{}, {:#x}]", reg_name_colorize(*reg, colors), offs) + } + Operand::RegDerefPostindexReg(reg, offsreg) => { + write!(f, "[{}, {}]", reg_name_colorize(*reg, colors), reg_name_colorize(*offsreg, colors)) + } + Operand::Imm12(imm) => { + write!(f, "{:#x}", imm) + } + Operand::Imm32(imm) => { + write!(f, "{:#x}", imm) + } + Operand::BranchOffset(imm) => { + if *imm < 0 { + write!(f, " $-{:#x}", (-*imm) * 4) + } else { + write!(f, " $+{:#x}", *imm * 4) + } + } + Operand::ASPR => { write!(f, "aspr") }, + Operand::CSPR => { write!(f, "cspr") }, + Operand::SPSR => { write!(f, "spsr") }, + Operand::Nothing => { panic!("tried to print Nothing operand") }, + } + } } #[derive(Debug, PartialEq, Eq)] pub struct Instruction { pub condition: ConditionCode, pub opcode: Opcode, - pub operands: Operands, + pub operands: [Operand; 4], pub s: bool } @@ -474,7 +670,7 @@ impl Default for Instruction { Instruction { condition: ConditionCode::AL, opcode: Opcode::Invalid, - operands: Operands::BranchOffset(0), + operands: [Operand::Nothing, Operand::Nothing, Operand::Nothing, Operand::Nothing], s: false } } @@ -499,7 +695,7 @@ fn format_reg_list<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, m } else { tail = true; } - write!(f, "{}", reg_name_colorize(i, colors))?; + write!(f, "{}", reg_name_colorize(Reg::from_u8(i), colors))?; } i += 1; list >>= 1; @@ -508,46 +704,42 @@ fn format_reg_list<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, m } #[allow(non_snake_case)] -fn format_shift<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rm: u8, shift: ShiftSpec, colors: &Y) -> Result<(), fmt::Error> { - fn shift_tpe_to_str(tpe: u8) -> &'static str { +fn format_shift<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, shift: RegShift, colors: &Y) -> Result<(), fmt::Error> { + fn shift_tpe_to_str(tpe: ShiftStyle) -> &'static str { match tpe { - 0b00 => "lsl", - 0b01 => "lsr", - 0b10 => "asr", - 0b11 => "ror", - _ => { unreachable!(); } + ShiftStyle::LSL => "lsl", + ShiftStyle::LSR => "lsr", + ShiftStyle::ASR => "asr", + ShiftStyle::ROR => "ror", } } - match shift { - ShiftSpec::Immediate(0) => { - write!(f, "{}", reg_name_colorize(Rm, colors)) - }, - ShiftSpec::Immediate(v) => { - let tpe = v & 0x3; - let imm = v >> 2; - write!(f, "{}, {} {}", reg_name_colorize(Rm, colors), shift_tpe_to_str(tpe), imm) - }, - ShiftSpec::Register(v) => { - let tpe = v & 0x3; - let Rs = v >> 3; - write!(f, "{}, {} {}", reg_name_colorize(Rm, colors), shift_tpe_to_str(tpe), reg_name_colorize(Rs, colors)) + match shift.into_shift() { + RegShiftStyle::RegImm(imm_shift) => { + if imm_shift.imm() == 0 && imm_shift.stype() == ShiftStyle::LSL { + write!(f, "{}", reg_name_colorize(imm_shift.shiftee(), colors)) + } else { + write!(f, "{}, {} {}", reg_name_colorize(imm_shift.shiftee(), colors), shift_tpe_to_str(imm_shift.stype()), imm_shift.imm()) + } + } + RegShiftStyle::RegReg(reg_shift) => { + write!(f, "{}, {} {}", reg_name_colorize(reg_shift.shiftee(), colors), shift_tpe_to_str(reg_shift.stype()), reg_name_colorize(reg_shift.shifter(), colors)) }, } } #[allow(non_snake_case)] -fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rd: u8, Rm: u8, shift: ShiftSpec, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { +fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rd: Reg, shift: RegShift, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { let op = if add { "" } else { "-" }; match (pre, wback) { (true, true) => { write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?; - format_shift(f, Rm, shift, colors)?; + format_shift(f, shift, colors)?; write!(f, "]!") }, (true, false) => { write!(f, "[{}, {}", reg_name_colorize(Rd, colors), op)?; - format_shift(f, Rm, shift, colors)?; + format_shift(f, shift, colors)?; write!(f, "]") }, (false, true) => { @@ -555,13 +747,13 @@ fn format_reg_shift_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut }, (false, false) => { write!(f, "[{}], {}", reg_name_colorize(Rd, colors), op)?; - format_shift(f, Rm, shift, colors) + format_shift(f, shift, colors) } } } #[allow(non_snake_case)] -fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rn: u8, imm: u32, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { +fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T, Rn: Reg, imm: u16, add: bool, pre: bool, wback: bool, colors: &Y) -> Result<(), fmt::Error> { if imm != 0 { let op = if add { "" } else { "-" }; @@ -596,27 +788,6 @@ fn format_reg_imm_mem<T: fmt::Write, C: fmt::Display, Y: YaxColors<C>>(f: &mut T } } } -fn reg_name_colorize<C: fmt::Display, Y: YaxColors<C>>(num: u8, colors: &Y) -> impl fmt::Display { - match num { - 0 => colors.register("r0"), - 1 => colors.register("r1"), - 2 => colors.register("r2"), - 3 => colors.register("r3"), - 4 => colors.register("r4"), - 5 => colors.register("r5"), - 6 => colors.register("r6"), - 7 => colors.register("r7"), - 8 => colors.register("r8"), - 9 => colors.register("sb"), - 10 => colors.register("r10"), - 11 => colors.register("fp"), - 12 => colors.register("ip"), - 13 => colors.register("sp"), - 14 => colors.register("lr"), - 15 => colors.program_counter("pc"), - _ => { unreachable!(); } - } -} impl Display for Instruction { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { @@ -703,7 +874,9 @@ impl ConditionCode { } #[derive(Default, Debug)] -pub struct InstDecoder {} +pub struct InstDecoder { + system_mode: bool +} #[allow(non_snake_case)] impl Decoder<Instruction> for InstDecoder { @@ -743,11 +916,16 @@ impl Decoder<Instruction> for InstDecoder { 0b01 => { inst.opcode = Opcode::BLX; let operand = ((word & 0xffffff) as i32) << 8 >> 6; - inst.operands = Operands::BranchOffset( - operand | ( - ((word >> 23) & 0b10) as i32 - ) - ); + inst.operands = [ + Operand::BranchOffset( + operand | ( + ((word >> 23) & 0b10) as i32 + ) + ), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; } 0b10 => { inst.opcode = Opcode::Incomplete(word); @@ -801,11 +979,21 @@ impl Decoder<Instruction> for InstDecoder { match op { 0b000 => { inst.opcode = Opcode::MUL; - inst.operands = Operands::MulThreeRegs(R[3], R[0], R[1]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + Operand::Nothing, + ]; }, 0b001 => { inst.opcode = Opcode::MLA; - inst.operands = Operands::MulFourRegs(R[3], R[0], R[1], R[2]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + Operand::Reg(Reg::from_u8(R[2])), + ]; }, 0b010 => { if s { @@ -813,7 +1001,12 @@ impl Decoder<Instruction> for InstDecoder { return Err(DecodeError::InvalidOpcode); } inst.opcode = Opcode::UMAAL; - inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[2])), + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + ]; }, 0b011 => { if s { @@ -821,23 +1014,48 @@ impl Decoder<Instruction> for InstDecoder { return Err(DecodeError::InvalidOpcode); } inst.opcode = Opcode::MLS; - inst.operands = Operands::MulFourRegs(R[3], R[0], R[1], R[2]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + Operand::Reg(Reg::from_u8(R[2])), + ]; } 0b100 => { inst.opcode = Opcode::UMULL; - inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[2])), + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + ]; } 0b101 => { inst.opcode = Opcode::UMLAL; - inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[2])), + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + ]; } 0b110 => { inst.opcode = Opcode::SMULL; - inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[2])), + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + ]; } 0b111 => { inst.opcode = Opcode::SMLAL; - inst.operands = Operands::MulFourRegs(R[2], R[3], R[0], R[1]); + inst.operands = [ + Operand::Reg(Reg::from_u8(R[2])), + Operand::Reg(Reg::from_u8(R[3])), + Operand::Reg(Reg::from_u8(R[0])), + Operand::Reg(Reg::from_u8(R[1])), + ]; } _ => { unreachable!(format!("mul upcode: {:x}", op)) } } @@ -859,16 +1077,20 @@ impl Decoder<Instruction> for InstDecoder { let flags = (word & 0x1f) as u8; (flags, Rn, Rd, HiOffset, op, LoOffset) }; - println!("{:032b}", word); - println!(" {:05b}|{:04b}|{:04b}|{:04b}|1{:02b}1|{:04b}", flags, Rn, Rd, HiOffset, op, LoOffset); match op { 0b00 => { // |c o n d|0 0 0 1|x x x x x x x x x x x x x x x x|1 0 0 1|x x x x| // this is swp or {ld,st}ex, conditional on bit 23 + // see page A5-203 match flags { 0b10000 => { inst.opcode = Opcode::SWP; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + ]; }, 0b10001 | 0b10010 | 0b10011 => { inst.opcode = Opcode::Invalid; @@ -876,43 +1098,98 @@ impl Decoder<Instruction> for InstDecoder { } 0b10100 => { inst.opcode = Opcode::SWPB; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + ]; }, 0b10101 | 0b10110 | 0b10111 => { inst.opcode = Opcode::Invalid; return Err(DecodeError::InvalidOpcode); } 0b11000 => { + // TODO: flag v6 inst.opcode = Opcode::STREX; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + ]; } 0b11001 => { + // TODO: flag v6 inst.opcode = Opcode::LDREX; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + Operand::Nothing, + ]; } 0b11010 => { inst.opcode = Opcode::STREXD; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + if LoOffset == 0b1110 || (LoOffset & 1 == 1) || Rd == 15 || Rn == 15 { + // TODO: "unpredictable" + return Err(DecodeError::InvalidOperand); + } + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::Reg(Reg::from_u8(LoOffset + 1)), + Operand::RegDeref(Reg::from_u8(Rn)), + ]; } 0b11011 => { inst.opcode = Opcode::LDREXD; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + if LoOffset == 0b1110 || (LoOffset & 1 == 1) || Rn == 15 { + // TODO: "unpredictable" + return Err(DecodeError::InvalidOperand); + } + inst.operands = [ + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::Reg(Reg::from_u8(LoOffset + 1)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + ]; } 0b11100 => { inst.opcode = Opcode::STREXB; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + ]; } 0b11101 => { inst.opcode = Opcode::LDREXB; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + Operand::Nothing, + ]; } 0b11110 => { inst.opcode = Opcode::STREXH; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + ]; } 0b11111 => { inst.opcode = Opcode::LDREXH; - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(LoOffset)), + Operand::RegDeref(Reg::from_u8(Rn)), + Operand::Nothing, + Operand::Nothing, + ]; } _ => { /* @@ -935,24 +1212,44 @@ impl Decoder<Instruction> for InstDecoder { 0b00010 => { // inst.opcode = Opcode::STRHT_sub; inst.opcode = Opcode::Incomplete(word); - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::RegDerefPostindexReg(Reg::from_u8(Rd), Reg::from_u8(LoOffset)), + Operand::Nothing, + Operand::Nothing, + ]; } 0b01010 => { // inst.opcode = Opcode::STRHT_add; inst.opcode = Opcode::Incomplete(word); - inst.operands = Operands::ThreeOperand(Rn, Rd, LoOffset); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::RegDerefPostindexReg(Reg::from_u8(Rd), Reg::from_u8(LoOffset)), + Operand::Nothing, + Operand::Nothing, + ]; } 0b00110 => { // inst.opcode = Opcode::STRHT_sub; inst.opcode = Opcode::Incomplete(word); let imm = (HiOffset << 4) as u16 | LoOffset as u16; - inst.operands = Operands::ThreeOperandImm(Rn, Rd, imm); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::RegDerefPostindexOffset(Reg::from_u8(Rd), imm), + Operand::Nothing, + Operand::Nothing, + ]; } 0b01110 => { // inst.opcode = Opcode::STRHT_add; inst.opcode = Opcode::Incomplete(word); let imm = (HiOffset << 4) as u16 | LoOffset as u16; - inst.operands = Operands::ThreeOperandImm(Rn, Rd, imm); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::RegDerefPostindexOffset(Reg::from_u8(Rd), imm), + Operand::Nothing, + Operand::Nothing, + ]; } _ => { return Err(DecodeError::Incomplete); @@ -979,9 +1276,10 @@ impl Decoder<Instruction> for InstDecoder { } } else { // we know this is data processing with imm or reg shift, OR - // misc instructions in Figure A3-4 + // misc instructions in Figure A5-4 if s == false && opcode >= 0b1000 && opcode < 0b1100 { + // Data-processing and miscellaneous instructions on page A5-194 let op2 = ((word >> 4) & 0x0f) as u8; // the instruction looks like // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x| @@ -991,34 +1289,140 @@ impl Decoder<Instruction> for InstDecoder { // misc instructions (page A5-194) match op2 { 0b000 => { - + if word & 0b1000000000 != 0 { + // TODO: ARMv7VE flag + let _M = (((word >> 9) & 1) << 4) | ((word >> 16) & 0x0f); + let _R = (word >> 22) & 1; + + if opcode & 0b01 == 0b01 { + inst.opcode = Opcode::MSR; + inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111)); + return Err(DecodeError::Incomplete); + } else { + inst.opcode = Opcode::MRS; + inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)); + return Err(DecodeError::Incomplete); + } + } else { + match opcode & 0b11 { + 0b00 | + 0b10 => { + inst.opcode = Opcode::MRS; + /* + let src = if self.system_mode { + let R = (word >> 22) & 1 != 0; + if R { + Operand::SPSR + } else { + Operand::CSPR + } + } else { + Operand::ASPR + }; + */ + inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)); + // TODO: + return Err(DecodeError::Incomplete); + } + 0b01 => { + inst.opcode = Opcode::MSR; + let mask = (word >> 16) & 0b1111; + if self.system_mode { + let R = (word >> 22) & 1 != 0; + let _dest = if R { + Operand::SPSR + } else { + Operand::CSPR + }; + // inst.operands[0] = /* masked write to dest */ + inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111)); + // TODO: + return Err(DecodeError::Incomplete); + } else { + if mask & 0b11 == 0 { + inst.operands[1] = Operand::Reg(Reg::from_u8(word as u8 & 0b1111)); + // TODO: + // inst.operands[0] = /* derived from mask >> 2 */ + return Err(DecodeError::Incomplete); + } else { + // MSR with op == 01, op != xx00 + return Err(DecodeError::InvalidOperand); + } + } + }, + 0b11 => { + if !self.system_mode { + return Err(DecodeError::InvalidOperand); + } + + inst.opcode = Opcode::MSR; + inst.operands[0] = Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)); + // TODO: + return Err(DecodeError::Incomplete); + } + _ => { + unreachable!(); + } + } + } }, 0b001 => { - + match opcode & 0b11 { + 0b01 => { + inst.opcode = Opcode::BX; + inst.operands = [ + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } + 0b11 => { + inst.opcode = Opcode::CLZ; + inst.operands = [ + Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Nothing, + Operand::Nothing, + ]; + } + _ => { + return Err(DecodeError::InvalidOpcode); + } + } }, 0b010 => { - + return Err(DecodeError::InvalidOpcode); }, 0b011 => { if opcode & 0b11 == 0b01 { inst.opcode = Opcode::BLX; - inst.operands = Operands::OneOperand((word & 0x0f) as u8); + inst.operands = [ + Operand::Reg(Reg::from_u8(word as u8 & 0x0f)), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; return Ok(()); } else { return Err(DecodeError::InvalidOpcode); } }, 0b100 => { - inst.opcode = Opcode::Incomplete(word); - return Ok(()); + // no row for op2 == 0b100 in table A5-14 (page A5-203) + return Err(DecodeError::InvalidOpcode); }, 0b101 => { - + // TODO: "Saturating addition and subtraction" page A5-200 + return Err(DecodeError::Incomplete); } 0b110 => { + // TODO: "ERET" page B9-1968 + return Err(DecodeError::Incomplete); }, 0b111 => { - + // TODO: "BKPT, HVC, SMC" from table A5-14/page A5-203 + return Err(DecodeError::Incomplete); }, _ => { unreachable!(); @@ -1026,11 +1430,84 @@ impl Decoder<Instruction> for InstDecoder { } } else { // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x|1|x x|x|x x x x| - // multiply and multiply-accumulate - inst.opcode = Opcode::Incomplete(word); - return Ok(()); + // Halfword multiply and multiply accumulate on page A5-200 + match (word >> 21) & 0b11 { + 0b00 => { + let Rn_b = ((word >> 6) & 1) == 0; + let Rm_b = ((word >> 5) & 1) == 0; + inst.opcode = Opcode::SMLA(Rn_b, Rm_b); + inst.operands = [ + Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)), + ]; + return Ok(()); + }, + 0b01 => { + if word & 0b10000 == 0 { + // SMLAWB, SMLAWT page A8-631 + let Rm_b = ((word >> 5) & 1) == 0; + inst.opcode = Opcode::SMLAW(Rm_b); + inst.operands = [ + Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)), + ]; + return Ok(()); + } else { + // SMULWB, SMULWT page A8-649 + let Rm_b = ((word >> 5) & 1) == 0; + inst.opcode = Opcode::SMLAW(Rm_b); + inst.operands = [ + Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)), + Operand::Nothing, + ]; + return Ok(()); + } + } + 0b10 => { + let Rn_b = ((word >> 6) & 1) == 0; + let Rm_b = ((word >> 5) & 1) == 0; + inst.opcode = Opcode::SMLAL_halfword(Rn_b, Rm_b); + inst.operands = [ + Operand::Reg(Reg::from_u8((word >> 12) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)), + ]; + if inst.operands[0] == inst.operands[1] { + // TODO: this is actually "UNPREDICTABLE" (A8-627) + return Err(DecodeError::InvalidOperand); + } + return Ok(()); + } + 0b11 => { + let Rn_b = ((word >> 6) & 1) == 0; + let Rm_b = ((word >> 5) & 1) == 0; + inst.opcode = Opcode::SMUL(Rn_b, Rm_b); + inst.operands = [ + Operand::Reg(Reg::from_u8((word >> 16) as u8 & 0b1111)), + Operand::Reg(Reg::from_u8(word as u8 & 0b1111)), + Operand::Reg(Reg::from_u8((word >> 8) as u8 & 0b1111)), + Operand::Nothing, + ]; + if inst.operands[0] == inst.operands[1] { + // TODO: this is actually "UNPREDICTABLE" (A8-627) + return Err(DecodeError::InvalidOperand); + } + return Ok(()); + } + _ => { + unreachable!(); + } + } } } else { + // `Table A5-3 Data-processing (register) instructions`, not op=10xx0. if opcode >= 16 { unreachable!(); } @@ -1040,45 +1517,63 @@ impl Decoder<Instruction> for InstDecoder { // at this point we know this is a data processing instruction // either immediate shift or register shift if word & 0b00010000 == 0 { - // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|0|x x x x| + // |c o n d|0 0 0|x x x x|0|x x x x|x x x x|x x x x x|x x|0|x x x x| // interpret the operands as // | Rn | Rd | shift amount | shift | 0 | Rm | let (Rn, Rd, shift_spec, Rm) = { let Rm = (word & 0x0f) as u8; - let word = word >> 5; - let shift_spec = (word & 0x7f) as u8; - let word = word >> 7; + let shift_spec = (word & 0xfff) as u16; + let word = word >> 12; let Rd = (word & 0x0f) as u8; let Rn = ((word >> 4) & 0x0f) as u8; (Rn, Rd, shift_spec, Rm) }; - if shift_spec == 0 { + if shift_spec & 0xff0 == 0 { if (0b1101 & opcode) == 0b1101 { + if Rn != 0 { + // this is really a "should be zero" situation + return Err(DecodeError::Incomplete); + } // MOV or MVN - inst.operands = Operands::TwoOperand(Rd, Rm); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(Rm)), + Operand::Nothing, + Operand::Nothing + ]; } else { - inst.operands = Operands::ThreeOperand(Rd, Rn, Rm); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(Rn)), + Operand::Reg(Reg::from_u8(Rm)), + Operand::Nothing + ]; } } else { - /* - * TODO: look at how this interacts with mov and mvn - */ - inst.operands = Operands::ThreeOperandWithShift(Rd, Rn, Rm, ShiftSpec::Immediate(shift_spec)); + if opcode == 0b1101 && Rn != 0 { + // Rn "should" be zero + return Err(DecodeError::Incomplete); + } + + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(Rn)), + Operand::RegShift(RegShift::from(shift_spec)), + Operand::Nothing + ]; } } else { // known 0 because it and bit 5 are not both 1 --v // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x 0|x x|1|x x x x| // interpret the operands as // | Rn | Rd | Rs | 0 | shift | 1 | Rm | - let (Rn, Rd, shift_spec, Rm) = { - let Rm = (word & 0x0f) as u8; - let word = word >> 5; - let shift_spec = (word & 0x7f) as u8; - let word = word >> 7; + let (Rn, Rd, shift_spec) = { + let shift_spec = (word & 0xfff) as u16; + let word = word >> 12; let Rd = (word & 0x0f) as u8; let Rn = ((word >> 4) & 0x0f) as u8; - (Rn, Rd, shift_spec, Rm) + (Rn, Rd, shift_spec) }; // page A5-200 indicates that saturating add and subtract should be // here? @@ -1087,7 +1582,13 @@ impl Decoder<Instruction> for InstDecoder { inst.opcode = Opcode::Invalid; return Err(DecodeError::InvalidOpcode); } else { - inst.operands = Operands::ThreeOperandWithShift(Rd, Rn, Rm, ShiftSpec::Register(shift_spec)); + // TODO: unsure about this RegShift... + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(Rn)), + Operand::RegShift(RegShift::from(shift_spec)), + Operand::Nothing, + ]; } } } @@ -1106,34 +1607,46 @@ impl Decoder<Instruction> for InstDecoder { }; if s == false && opcode >= 0b1000 && opcode < 0b1100 { // the instruction looks like - // |c o n d|0 0 0|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x| - // misc instructions (page A5-194) + // |c o n d|0 0 1|1 0 x x|0|x x x x|x x x x|x x x x x|x x|x|x x x x| + // which means 16-bit immediate load, high half immediate load, or MSR (immediate) + // + hints. + // See table A5-2, page A5-194. inst.opcode = Opcode::Incomplete(word); return Ok(()); } else { + // Data-processing (immediate) + // Page A5-197 if opcode >= 16 { unreachable!(); } inst.opcode = DATA_PROCESSING_OPCODES[opcode as usize]; inst.set_s(s); - let (Rn, imm) = { - let imm = word & 0x0000ffff; - let word = word >> 16; - ((word & 0x0f) as u8, imm) + let (Rn, Rd, imm) = { + let rot = word & 0x00000f00; + let imm = (word & 0x000000ff); + let imm = (imm as u32).rotate_right(2 * (rot >> 8)); + ((word >> 16) as u8 & 0x0f, (word >> 12) as u8 & 0x0f, imm) }; if (opcode == 0b0010 || opcode == 0b0100) && Rn == 0b1111 { inst.opcode = Opcode::ADR; } match opcode { 0b1101 => { - inst.operands = Operands::RegImm( - ((word >> 12) & 0xf) as u8, - (word & 0x0fff) as u32 - ); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Imm32(imm), + Operand::Nothing, + Operand::Nothing, + ]; } _ => { - inst.operands = Operands::RegImm(Rn, imm); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rd)), + Operand::Reg(Reg::from_u8(Rn)), + Operand::Imm32(imm), + Operand::Nothing, + ]; } } @@ -1141,6 +1654,9 @@ impl Decoder<Instruction> for InstDecoder { /* ... */ } 0b010 => { + // Load/store word and unsigned byte + // A5.3 + // Page A5-206 let Rn = ((word >> 16) & 0x0f) as u8; let op = ((word >> 20) & 0x1f) as u8; let add = (op & 0b01000) != 0; @@ -1177,7 +1693,12 @@ impl Decoder<Instruction> for InstDecoder { 0b000 => Opcode::STR(add, pre, wback), 0b001 => { if Rn == 0b1111 { - inst.operands = Operands::RegImm(Rt, imm.into()); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rt)), + Operand::RegDisp(Reg::from_u8(Rn), imm as u16 as i16), + Operand::Nothing, + Operand::Nothing, + ]; inst.opcode = Opcode::LDR(add, pre, wback); return Ok(()); } @@ -1186,7 +1707,12 @@ impl Decoder<Instruction> for InstDecoder { 0b100 => Opcode::STRB(add, pre, wback), 0b101 => { if Rn == 0b1111 { - inst.operands = Operands::RegImm(Rt, imm.into()); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rt)), + Operand::RegDisp(Reg::from_u8(Rn), imm as u16 as i16), + Operand::Nothing, + Operand::Nothing, + ]; inst.opcode = Opcode::LDRB(add, pre, wback); return Ok(()); } @@ -1195,7 +1721,12 @@ impl Decoder<Instruction> for InstDecoder { _ => { unreachable!(); } }; } - inst.operands = Operands::TwoRegImm(Rn, Rt, imm.into()); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rt)), + Operand::RegDerefPostindexOffset(Reg::from_u8(Rn), imm as u16), + Operand::Nothing, + Operand::Nothing, + ]; }, 0b011 => { // page A5-192 to distinguish the following: @@ -1220,7 +1751,7 @@ impl Decoder<Instruction> for InstDecoder { xx1x1 not 0x111 -> LDRB (register) 0x111 -> LDRBT */ - let Rn = ((word >> 16) & 0x0f) as u8; + let _Rn = ((word >> 16) & 0x0f) as u8; if (op & 0b10010) == 0b00010 { let op = op & 0b00111; // |c o n d|0 1 1|0 x x 1 x|x x x x x x x x x x x x x x x|0|x x x x| @@ -1255,15 +1786,18 @@ impl Decoder<Instruction> for InstDecoder { _ => { unreachable!(); } }; } - let (Rt, Rm, shift) = { - let Rm = (word & 0xf) as u8; - let word = word >> 5; - let shift = (word & 0x7f) as u8; - let word = word >> 7; + let (Rt, shift) = { + let shift = (word & 0xfff) as u16; + let word = word >> 12; let Rt = (word & 0xf) as u8; - (Rt, Rm, shift) + (Rt, shift) }; - inst.operands = Operands::ThreeOperandWithShift(Rn, Rt, Rm, ShiftSpec::Immediate(shift)); + inst.operands = [ + Operand::Reg(Reg::from_u8(Rt)), + Operand::RegDerefRegShift(RegShift::from(shift)), + Operand::Nothing, + Operand::Nothing, + ]; } return Ok(()); }, @@ -1277,24 +1811,36 @@ impl Decoder<Instruction> for InstDecoder { let pre = (op & 0b010000) != 0; let usermode = (op & 0b000100) != 0; inst.opcode = if (op & 1) == 0 { - Opcode::STM(add, pre, wback, usermode) - } else { - Opcode::LDM(add, pre, wback, usermode) - }; - inst.operands = Operands::RegRegList( - ((word >> 16) & 0xf) as u8, - (word & 0xffff) as u16 - ); + Opcode::STM(add, pre, wback, usermode) + } else { + Opcode::LDM(add, pre, wback, usermode) + }; + inst.operands = [ + Operand::Reg(Reg::from_u8(((word >> 16) & 0xf) as u8)), + Operand::RegList((word & 0xffff) as u16), + Operand::Nothing, + Operand::Nothing, + ]; } else if op < 0b110000 { // 10xxxx // the + 1 is to compensate for an architecturally-defined initial offset inst.opcode = Opcode::B; - inst.operands = Operands::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32); + inst.operands = [ + Operand::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; } else { // 11xxxx // the + 1 is to compensate for an architecturally-defined initial offset inst.opcode = Opcode::BL; - inst.operands = Operands::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32); + inst.operands = [ + Operand::BranchOffset(((word & 0x00ffff) + 1) as i16 as i32), + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; } }, 0b110 | 0b111 => { @@ -1322,7 +1868,7 @@ impl Arch for ARMv7 { type Instruction = Instruction; type DecodeError = DecodeError; type Decoder = InstDecoder; - type Operand = Operands; + type Operand = Operand; } |