diff options
author | iximeow <me@iximeow.net> | 2019-01-16 03:48:46 -0800 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2020-01-12 17:14:49 -0800 |
commit | ef683581fbf5e9aaa4b157acb72caf193cb64b08 (patch) | |
tree | 27f6451bfb8baf220d96ed756f16e5742b3a3de6 |
initial commit
-rw-r--r-- | Cargo.toml | 17 | ||||
-rw-r--r-- | src/lib.rs | 450 |
2 files changed, 467 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e1ecdf4 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] + +name = "yaxpeax-pic17" +version = "0.0.1" +authors = [ "iximeow <me@iximeow.net>" ] +license = "0BSD" +repository = "" +description = """ +A rust pic17 decoder +""" + +[dependencies] +yaxpeax-arch = { path = "../../yaxpeax-arch" } + +[[test]] +name = "test" +path = "test/test.rs" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6ec1b01 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,450 @@ +extern crate yaxpeax_arch; + +use yaxpeax_arch::{Arch, Decodable, LengthedInstruction}; + +use std::fmt::{Display, Formatter}; + +#[derive(Debug, Copy, Clone)] +pub struct Instruction { + pub opcode: Opcode, + pub operands: [Operand; 2] +} + +pub struct PIC17; +impl Arch for PIC17 { + type Address = u16; + type Instruction = Instruction; + type Operand = Operand; +} + +impl Display for Instruction { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + write!(f, "{}", self.opcode)?; + match self.operands[0] { + Operand::Nothing => return Ok(()), + x @ _ => { + write!(f, " {}", x)?; + } + }; + match self.operands[1] { + Operand::Nothing => return Ok(()), + x @ _ => { + write!(f, ", {}", x)?; + } + }; + Ok(()) + } +} + +impl LengthedInstruction for Instruction { + type Unit = <PIC17 as Arch>::Address; + fn len(&self) -> Self::Unit { + match self.opcode { + _ => 2 + } + } +} + +impl Instruction { + pub fn blank() -> Instruction { + Instruction { + opcode: Opcode::NOP, + operands: [Operand::Nothing, Operand::Nothing] + } + } + + pub fn is_call(&self) -> bool { + match self.opcode { + Opcode::CALL | Opcode::LCALL => { true }, + _ => { false } + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Opcode { + Invalid(u8, u8), + NOP, + RETURN, + SLEEP, + CLRWDT, + RETFIE, + MOVWF, + SUBWFB, + SUBWF, + DECF, + IORWF, + ANDWF, + XORWF, + ADDWF, + ADDWFC, + COMF, + INCF, + DECFSZ, + RRCF, + RLCF, + SWAPF, + INCFSZ, + RRNCF, + RLNCF, + INFSNZ, + DCFSNZ, + CLRF, + SETF, + NEGW, + DAW, + BTG, + CPFSLT, + CPFSEQ, + CPFSGT, + MULWF, + TSTFSZ, + BSF, + BCF, + BTFSS, + BTFSC, + MOVFP, + MOVPF, + MOVLW, + ADDLW, + SUBLW, + IORLW, + XORLW, + ANDLW, + RETLW, + LCALL, + MOVLB, + MOVLR, + MULLW, + TLRDL, + TLRDH, + TLWTL, + TLWTH, + TABLRDL, + TABLRDLI, + TABLRDH, + TABLRDHI, + TABLWTL, + TABLWTLI, + TABLWTH, + TABLWTHI, + GOTO, + CALL +} + +impl Display for Opcode { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + match self { + Opcode::Invalid(a, b) => { write!(f, "invalid({:02x}{:02x})", a, b) }, + Opcode::NOP => { write!(f, "nop") }, + Opcode::RETURN => { write!(f, "return") }, + Opcode::SLEEP => { write!(f, "sleep") }, + Opcode::CLRWDT => { write!(f, "clrwdt") }, + Opcode::RETFIE => { write!(f, "retfie") }, + Opcode::MOVFP => { write!(f, "movfp") }, + Opcode::MOVPF => { write!(f, "movpf") }, + Opcode::TLRDL => { write!(f, "tlrdl") }, + Opcode::TLRDH => { write!(f, "tlrdh") }, + Opcode::TLWTL => { write!(f, "tlwtl") }, + Opcode::TLWTH => { write!(f, "tlwth") }, + Opcode::TABLRDL => { write!(f, "tablrdl") }, + Opcode::TABLRDLI => { write!(f, "tablrdli") }, + Opcode::TABLRDH => { write!(f, "tablrdh") }, + Opcode::TABLRDHI => { write!(f, "tablrdhi") }, + Opcode::TABLWTL => { write!(f, "tablwtl") }, + Opcode::TABLWTLI => { write!(f, "tablwtli") }, + Opcode::TABLWTH => { write!(f, "tablwth") }, + Opcode::TABLWTHI => { write!(f, "tablwthi") }, + Opcode::MOVWF => { write!(f, "movwf") }, + Opcode::SUBWFB => { write!(f, "subwfb") }, + Opcode::SUBWF => { write!(f, "subwf") }, + Opcode::DECF => { write!(f, "decf") }, + Opcode::IORWF => { write!(f, "iorwf") }, + Opcode::ANDWF => { write!(f, "andwf") }, + Opcode::XORWF => { write!(f, "xorwf") }, + Opcode::ADDWF => { write!(f, "addwf") }, + Opcode::ADDWFC => { write!(f, "addwfc") }, + Opcode::COMF => { write!(f, "comf") }, + Opcode::INCF => { write!(f, "incf") }, + Opcode::DECFSZ => { write!(f, "decfsz") }, + Opcode::RRCF => { write!(f, "rrcf") }, + Opcode::RLCF => { write!(f, "rlcf") }, + Opcode::SWAPF => { write!(f, "swapf") }, + Opcode::INCFSZ => { write!(f, "incfsz") }, + Opcode::RRNCF => { write!(f, "rrncf") }, + Opcode::RLNCF => { write!(f, "rlncf") }, + Opcode::INFSNZ => { write!(f, "infsnz") }, + Opcode::DCFSNZ => { write!(f, "dcfsnz") }, + Opcode::CLRF => { write!(f, "clrf") }, + Opcode::SETF => { write!(f, "setf") }, + Opcode::NEGW => { write!(f, "negw") }, + Opcode::DAW => { write!(f, "daw") }, + Opcode::BTG => { write!(f, "btg") }, + Opcode::CPFSLT => { write!(f, "cpfslt") }, + Opcode::CPFSEQ => { write!(f, "cpfseq") }, + Opcode::CPFSGT => { write!(f, "cpfsgt") }, + Opcode::MULWF => { write!(f, "mulwf") }, + Opcode::TSTFSZ => { write!(f, "tstfsz") }, + Opcode::BSF => { write!(f, "bsf") }, + Opcode::BCF => { write!(f, "bcf") }, + Opcode::BTFSS => { write!(f, "btfss") }, + Opcode::BTFSC => { write!(f, "btfsc") }, + Opcode::MOVLW => { write!(f, "movlw") }, + Opcode::ADDLW => { write!(f, "addlw") }, + Opcode::SUBLW => { write!(f, "sublw") }, + Opcode::IORLW => { write!(f, "iorlw") }, + Opcode::XORLW => { write!(f, "xorlw") }, + Opcode::ANDLW => { write!(f, "andlw") }, + Opcode::RETLW => { write!(f, "retlw") }, + Opcode::LCALL => { write!(f, "lcall") }, + Opcode::MOVLB => { write!(f, "movlb") }, + Opcode::MOVLR => { write!(f, "movlr") }, + Opcode::MULLW => { write!(f, "mullw") }, + Opcode::GOTO => { write!(f, "goto") }, + Opcode::CALL => { write!(f, "call") } + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Operand { + ImmediateU8(u8), + ImmediateU32(u32), + File(u8), + W, + Nothing +} + +impl Operand { + pub fn file_value(&self) -> u8 { + match self { + Operand::File(f) => *f, + _ => { unreachable!() } + } + } + pub fn imm8_value(&self) -> u8 { + match self { + Operand::ImmediateU8(i) => *i, + _ => { unreachable!() } + } + } + pub fn imm32_value(&self) -> u32 { + match self { + Operand::ImmediateU32(i) => *i, + _ => { unreachable!() } + } + } +} + +impl Display for Operand { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + match self { + Operand::ImmediateU8(imm) => { + write!(f, "#0x{:x}", imm) + }, + Operand::ImmediateU32(imm) => { + write!(f, "#0x{:x}", imm) + }, + Operand::W => { + write!(f, "W") + }, + Operand::File(file) => { + write!(f, "[banked 0x{:x}]", file) + }, + Operand::Nothing => { + write!(f, "<No Operand>") + } + } + } +} + +impl Decodable for Instruction { + fn decode<'a, T: IntoIterator<Item=&'a u8>>(bytes: T) -> Option<Self> { + let mut blank = Instruction::blank(); + match blank.decode_into(bytes) { + Some(_) => Some(blank), + None => None + } + } + fn decode_into<'a, T: IntoIterator<Item=&'a u8>>(&mut self, bytes: T) -> Option<()> { + let mut bytes_iter = bytes.into_iter(); + let word: Vec<&'a u8> = bytes_iter.by_ref().take(2).collect(); + if word.len() != 2 { + return None; + } + + match *word[1] { + 0x00 => { + self.operands = [Operand::Nothing, Operand::Nothing]; + match *word[0] { + 0x00 => { + self.opcode = Opcode::NOP; + Some(()) + }, + 0b00000010 => { + self.opcode = Opcode::RETURN; + Some(()) + }, + 0b00000011 => { + self.opcode = Opcode::SLEEP; + Some(()) + }, + 0b00000100 => { + self.opcode = Opcode::CLRWDT; + Some(()) + }, + 0b00000101 => { + self.opcode = Opcode::RETFIE; + Some(()) + }, + _ => { + self.opcode = Opcode::Invalid(*word[1], *word[0]); + None + } + } + }, + 0x01 => { + self.opcode = Opcode::MOVWF; + self.operands = [Operand::File(*word[0]), Operand::Nothing]; + Some(()) + }, + x if x < 0x30 => { + // TODO: consume + let d = x & 0x01 == 0x01; + self.opcode = [ + Opcode::SUBWFB, + Opcode::SUBWF, + Opcode::DECF, + Opcode::IORWF, + Opcode::ANDWF, + Opcode::XORWF, + Opcode::ADDWF, + Opcode::ADDWFC, + Opcode::COMF, + Opcode::INCF, + Opcode::DECFSZ, + Opcode::RRCF, + Opcode::RLCF, + Opcode::SWAPF, + Opcode::INCFSZ, + Opcode::RRNCF, + Opcode::RLNCF, + Opcode::INFSNZ, + Opcode::DCFSNZ, + Opcode::CLRF, + Opcode::SETF, + Opcode::NEGW, + Opcode::DAW + ][(x >> 1) as usize - 1]; + self.operands[0] = Operand::File(*word[0]); + self.operands[1] = if d { + Operand::File(*word[0]) + } else { + Operand::W + }; + Some(()) + }, + x if x < 0x40 => { + self.operands = [Operand::File(*word[0]), Operand::Nothing]; + self.opcode = match *word[1] { + 0x30 => Opcode::CPFSLT, + 0x31 => Opcode::CPFSEQ, + 0x32 => Opcode::CPFSGT, + 0x33 => Opcode::MULWF, + 0x34 => Opcode::TSTFSZ, + 0x35 => { Opcode::Invalid(*word[1], *word[0]); return None }, + 0x36 => { Opcode::Invalid(*word[1], *word[0]); return None }, + 0x37 => { Opcode::Invalid(*word[1], *word[0]); return None }, + 0x38 => { self.operands[1] = Operand::ImmediateU8(0); Opcode::BTG }, + 0x39 => { self.operands[1] = Operand::ImmediateU8(1); Opcode::BTG }, + 0x3a => { self.operands[1] = Operand::ImmediateU8(2); Opcode::BTG }, + 0x3b => { self.operands[1] = Operand::ImmediateU8(3); Opcode::BTG }, + 0x3c => { self.operands[1] = Operand::ImmediateU8(4); Opcode::BTG }, + 0x3d => { self.operands[1] = Operand::ImmediateU8(5); Opcode::BTG }, + 0x3e => { self.operands[1] = Operand::ImmediateU8(6); Opcode::BTG }, + 0x3f => { self.operands[1] = Operand::ImmediateU8(7); Opcode::BTG }, + _ => { unreachable!(); } + }; + Some(()) + }, + x if x < 0x60 => { + self.opcode = Opcode::MOVPF; + self.operands[0] = Operand::File((*word[1]) & 0x1f); + self.operands[1] = Operand::File(*word[0]); + Some(()) + }, + x if x < 0x80 => { + self.opcode = Opcode::MOVFP; + self.operands[0] = Operand::File(*word[0]); + self.operands[1] = Operand::File((*word[1]) & 0x1f); + Some(()) + }, + x if x < 0xa0 => { + self.opcode = [ + Opcode::BSF, + Opcode::BCF, + Opcode::BTFSS, + Opcode::BTFSC, + ][(((*word[1]) >> 3) & 0x3) as usize]; + self.operands[0] = Operand::File(*word[0]); + self.operands[1] = Operand::ImmediateU8((*word[1]) & 0x7); + Some(()) + }, + x if x < 0xb0 => { + self.opcode = [ + Opcode::TLRDL, + Opcode::TLRDL, + Opcode::TLRDH, + Opcode::TLRDH, + Opcode::TLWTL, + Opcode::TLWTL, + Opcode::TLWTH, + Opcode::TLWTH, + Opcode::TABLRDL, + Opcode::TABLRDLI, + Opcode::TABLRDH, + Opcode::TABLRDHI, + Opcode::TABLWTL, + Opcode::TABLWTLI, + Opcode::TABLWTH, + Opcode::TABLWTHI + ][(x & 0x0f) as usize]; + self.operands = [Operand::File(*word[0]), Operand::Nothing]; + Some(()) + }, + x if x < 0xc0 => { + self.opcode = [ + Opcode::MOVLW, + Opcode::ADDLW, + Opcode::SUBLW, + Opcode::IORLW, + Opcode::XORLW, + Opcode::ANDLW, + Opcode::RETLW, + Opcode::LCALL, + Opcode::MOVLB, // BSR only gets low four... + Opcode::Invalid(*word[1], *word[0]), + Opcode::MOVLR, // These are weird ones. The Bank Select + Opcode::MOVLR, // Register only gets the high four bits. + Opcode::MULLW, + Opcode::Invalid(*word[1], *word[0]), + Opcode::Invalid(*word[1], *word[0]), + Opcode::Invalid(*word[1], *word[0]) + ][((*word[1]) & 0x0f) as usize]; + self.operands = [Operand::ImmediateU8(*word[0]), Operand::Nothing]; + Some(()) + }, + x if x < 0xe0 => { + self.opcode = Opcode::GOTO; + self.operands = [Operand::ImmediateU32(*word[0] as u32 | (((*word[1] as u32) & 0x1f) << 8)), Operand::Nothing]; + Some(()) + }, + _ => { + self.opcode = Opcode::CALL; + self.operands = [Operand::ImmediateU32(*word[0] as u32 | (((*word[1] as u32) & 0x1f) << 8)), Operand::Nothing]; + Some(()) + } + } + } +} + |