diff options
-rw-r--r-- | Cargo.toml | 17 | ||||
-rw-r--r-- | src/display.rs | 123 | ||||
-rw-r--r-- | src/lib.rs | 315 |
3 files changed, 455 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..58e3a3c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] + +name = "yaxpeax-msp430" +version = "0.0.1" +authors = [ "iximeow <me@iximeow.net>" ] +license = "0BSD" +repository = "msp430-rs" +description = """ +A rust msp430 decoder (specifically the microcorruption flavor) +""" + +[dependencies] +yaxpeax-arch = { path = "../../yaxpeax-arch" } + +[[test]] +name = "test" +path = "test/test.rs" diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..7864248 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,123 @@ +use std; +use std::fmt::{Display, Formatter}; + +use ::{Operand, Opcode, Instruction, Width}; + +impl Display for Instruction { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + write!(f, "{}", self.opcode)?; + match self.op_width { + Width::B => { write!(f, ".b")? }, + Width::W => { } + }; + 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 Display for Opcode { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + match self { + Opcode::Invalid(a) => { write!(f, "invalid({:04x})", a) }, + Opcode::RRC => { write!(f, "rrc") }, + Opcode::SWPB => { write!(f, "swpb") }, + Opcode::RRA => { write!(f, "rra") }, + Opcode::SXT => { write!(f, "sxt") }, + Opcode::PUSH => { write!(f, "push") }, + Opcode::CALL => { write!(f, "call") }, + Opcode::RETI => { write!(f, "reti") }, + Opcode::JNE => { write!(f, "jne") }, + Opcode::JEQ => { write!(f, "jeq") }, + Opcode::JNC => { write!(f, "jnc") }, + Opcode::JC => { write!(f, "jc") }, + Opcode::JN => { write!(f, "jn") }, + Opcode::JGE => { write!(f, "jge") }, + Opcode::JL => { write!(f, "jl") }, + Opcode::JMP => { write!(f, "jmp") }, + Opcode::MOV => { write!(f, "mov") }, + Opcode::ADD => { write!(f, "add") }, + Opcode::ADDC => { write!(f, "addc") }, + Opcode::SUBC => { write!(f, "subc") }, + Opcode::SUB => { write!(f, "sub") }, + Opcode::CMP => { write!(f, "cmp") }, + Opcode::DADD => { write!(f, "dadd") }, + Opcode::BIT => { write!(f, "bit") }, + Opcode::BIC => { write!(f, "bic") }, + Opcode::BIS => { write!(f, "bis") }, + Opcode::XOR => { write!(f, "xor") }, + Opcode::AND => { write!(f, "and") } + } + } +} + + +impl Display for Operand { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + fn signed_hex(num: i16) -> String { + if num >= 0 { + format!("+{:#x}", num) + } else { + format!("-{:#x}", -num) + } + } + match self { + Operand::Register(reg) => { + write!(f, "R{}", reg) + }, + Operand::Indexed(reg, offset) => { + write!(f, "{}(R{})", signed_hex(*offset as i16), reg) + }, + Operand::RegisterIndirect(reg) => { + write!(f, "@R{}", reg) + }, + Operand::IndirectAutoinc(reg) => { + write!(f, "@R{}+", reg) + }, + Operand::Offset(offset) => { + write!(f, "${}", signed_hex(*offset as i16)) + }, + Operand::Symbolic(offset) => { + write!(f, "{}(PC)", signed_hex(*offset as i16)) + }, + Operand::Immediate(imm) => { + write!(f, "#0x{:x}", imm) + }, + Operand::Absolute(offset) => { + write!(f, "&0x{:x}", offset) + }, + Operand::Const4 => { + write!(f, "4") + }, + Operand::Const8 => { + write!(f, "8") + }, + Operand::Const0 => { + write!(f, "0") + }, + Operand::Const1 => { + write!(f, "1") + }, + Operand::Const2 => { + write!(f, "2") + }, + Operand::ConstNeg1 => { + write!(f, "-1") + }, + Operand::Nothing => { + write!(f, "<No Operand>") + } + } + } +} + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f46a7d5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,315 @@ +extern crate yaxpeax_arch; + +use yaxpeax_arch::{Arch, Decodable, LengthedInstruction}; + +mod display; + +pub struct MSP430; +impl Arch for MSP430 { + type Address = u16; + type Instruction = Instruction; + type Operand = Operand; +} + +#[derive(Debug, Copy, Clone)] +pub struct Instruction { + pub opcode: Opcode, + pub op_width: Width, + pub operands: [Operand; 2] +} + +#[derive(Debug, Copy, Clone)] +pub enum Width { + W, B +} + +impl Instruction { + pub fn blank() -> Instruction { + Instruction { + opcode: Opcode::Invalid(0xffff), + op_width: Width::W, + operands: [Operand::Nothing, Operand::Nothing] + } + } +} + +impl LengthedInstruction for Instruction { + type Unit = <MSP430 as Arch>::Address; + fn len(&self) -> Self::Unit { + let mut size = 2; + match self.operands[0] { + Operand::Indexed(_, _) | + Operand::Symbolic(_) | + Operand::Immediate(_) | + Operand::Absolute(_) => { size += 2; }, + _ => {} + }; + match self.operands[1] { + Operand::Indexed(_, _) | + Operand::Symbolic(_) | + Operand::Immediate(_) | + Operand::Absolute(_) => { size += 2; }, + _ => {} + }; + size + } +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum Opcode { + Invalid(u16), + RRC, + SWPB, + RRA, + SXT, + PUSH, + CALL, + RETI, + JNE, + JEQ, + JNC, + JC, + JN, + JGE, + JL, + JMP, + MOV, + ADD, + ADDC, + SUBC, + SUB, + CMP, + DADD, + BIT, + BIC, + BIS, + XOR, + AND +} + +#[derive(Debug, Copy, Clone)] +pub enum Operand { + Register(u8), + Indexed(u8, u16), + RegisterIndirect(u8), + IndirectAutoinc(u8), + Symbolic(u16), + Immediate(u16), + Absolute(u16), + Offset(i16), + Const4, + Const8, + Const0, + Const1, + Const2, + ConstNeg1, + Nothing +} + +impl Decodable for Instruction { + fn decode<'a, T: IntoIterator<Item=&'a u8>>(bytes: T) -> Option<Self> { + let mut instr = Instruction::blank(); + match instr.decode_into(bytes) { + Some(_) => Some(instr), + 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(); + + let fullword = match word[..] { + [] | [_] => { return None; }, + [low, high] => (*high as u16) << 8 | (*low as u16), + _ => unreachable!() + }; + + fn decode_operand<'a, T: Iterator<Item=&'a u8>>(bytes: &mut T, reg: u8, mode: u8, oper: &mut Operand) -> bool { + *oper = match reg { + 0 => { + if mode == 0 { + Operand::Register(reg) + } else if mode == 1 { + let next = match bytes.take(2).collect::<Vec<&u8>>()[..] { + [] | [_] => { return false; }, + [low, high] => { ((*high as u16) << 8) | (*low as u16) }, + _ => { unreachable!() } + }; + Operand::Symbolic(next) + } else if mode == 2 { + Operand::RegisterIndirect(reg) + } else if mode == 3 { + let next = match bytes.take(2).collect::<Vec<&u8>>()[..] { + [] | [_] => { return false; }, + [low, high] => { ((*high as u16) << 8) | (*low as u16) }, + _ => { unreachable!() } + }; + Operand::Immediate(next) + } else { + return false; + } + }, + 2 => { + match mode { + 0 => { Operand::Register(reg) }, + 1 => { + let next = match bytes.take(2).collect::<Vec<&u8>>()[..] { + [] | [_] => { return false; }, + [low, high] => { ((*high as u16) << 8) | (*low as u16) }, + _ => { unreachable!() } + }; + Operand::Absolute(next) + }, + 2 => { Operand::Const8 }, + 3 => { Operand::Const4 }, + _ => { unreachable!() } + } + }, + 3 => { + match mode { + 0 => { Operand::Const0 }, + 1 => { Operand::Const1 }, + 2 => { Operand::Const2 }, + 3 => { Operand::ConstNeg1 }, + _ => { unreachable!() } + } + }, + _ => { + match mode { + 0 => { Operand::Register(reg) }, + 1 => { + let next = match bytes.take(2).collect::<Vec<&u8>>()[..] { + [] | [_] => { return false; }, + [low, high] => { ((*high as u16) << 8) | (*low as u16) }, + _ => { unreachable!() } + }; + Operand::Indexed(reg, next) + }, + 2 => { Operand::RegisterIndirect(reg) }, + 3 => { Operand::IndirectAutoinc(reg) }, + _ => { unreachable!() } + } + } + }; + return true; + } + + self.op_width = Width::W; + + match fullword { + /* + instrword if instrword < 0x1000 => { + // MSP430X instructions go here + self.opcode = Opcode::Invalid(instrword); + self.operands[0] = Operand::Nothing; + self.operands[1] = Operand::Nothing; + return None; + }, */ + instrword if instrword < 0x2000 => { + // microcorruption msp430 is non-standard and accepts invalid instructions.. + let (opcode_idx, operands) = ((instrword & 0x0380) >> 7, instrword & 0x7f); + match opcode_idx { + x if x < 6 => { + self.opcode = [ + Opcode::RRC, + Opcode::SWPB, + Opcode::RRA, + Opcode::SXT, + Opcode::PUSH, + Opcode::CALL + ][x as usize]; + self.op_width = if operands & 0b01000000 == 0 { + Width::W + } else { + if x == 1 || x == 3 || x == 5 { + self.opcode = Opcode::Invalid(instrword); + return None; + } + Width:: B + }; + #[allow(non_snake_case)] + let (As, source) = ( + ((instrword & 0x0030) >> 4) as u8, + (instrword & 0x000f) as u8 + ); + if !decode_operand(&mut bytes_iter, source, As, &mut self.operands[0]) { + self.opcode = Opcode::Invalid(instrword); + return None; + }; + self.operands[1] = Operand::Nothing; + Some(()) + }, + 6 => { + if operands == 0 { + self.opcode = Opcode::RETI; + self.operands[0] = Operand::Nothing; + self.operands[1] = Operand::Nothing; + Some(()) + } else { + self.opcode = Opcode::Invalid(instrword); + return None; + } + } + 7 => { + self.opcode = Opcode::Invalid(instrword); + return None; + } + _ => { + unreachable!(); + } + } + }, + instrword if instrword < 0x4000 => { + let (opcode_idx, offset) = ((instrword & 0x1c00) >> 10, instrword & 0x3ff); + self.opcode = [ + Opcode::JNE, + Opcode::JEQ, + Opcode::JNC, + Opcode::JC, + Opcode::JN, + Opcode::JGE, + Opcode::JL, + Opcode::JMP + ][opcode_idx as usize]; + self.operands[0] = Operand::Offset(((offset as i16) << 6) >> 6); + self.operands[1] = Operand::Nothing; + Some(()) + }, + instrword @ _ => { + let (opcode_idx, operands) = ((instrword & 0xf000) >> 12, instrword & 0x0fff); + self.opcode = [ + Opcode::MOV, + Opcode::ADD, + Opcode::ADDC, + Opcode::SUBC, + Opcode::SUB, + Opcode::CMP, + Opcode::DADD, + Opcode::BIT, + Opcode::BIC, + Opcode::BIS, + Opcode::XOR, + Opcode::AND + ][(opcode_idx - 4) as usize]; + self.op_width = if operands & 0b01000000 == 0 { Width::W } else { Width:: B }; + #[allow(non_snake_case)] + let (source, Ad, As, dest) = ( + ((instrword & 0x0f00) >> 8) as u8, + ((instrword & 0x0080) >> 7) as u8, + ((instrword & 0x0030) >> 4) as u8, + (instrword & 0x000f) as u8 + ); + if !decode_operand(&mut bytes_iter, source, As, &mut self.operands[0]) { + self.opcode = Opcode::Invalid(instrword); + return None; + } + if !decode_operand(&mut bytes_iter, dest, Ad, &mut self.operands[1]) { + self.opcode = Opcode::Invalid(instrword); + return None; + } + Some(()) + } + } + } +} + |