summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml17
-rw-r--r--src/lib.rs450
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(())
+ }
+ }
+ }
+}
+