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] } #[derive(Debug)] 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 = ::Address; fn min_size() -> Self::Unit { 2 } 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, "") } } } } impl Decodable for Instruction { fn decode>(bytes: T) -> Option { let mut blank = Instruction::blank(); match blank.decode_into(bytes) { Some(_) => Some(blank), None => None } } fn decode_into>(&mut self, bytes: T) -> Option<()> { let mut bytes_iter = bytes.into_iter(); let word: Vec = 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(()) } } } }