summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2019-01-16 03:48:46 -0800
committeriximeow <me@iximeow.net>2020-01-12 17:07:34 -0800
commita60c29bf3bf9c29422a78cdfa09bcb9ba6107db0 (patch)
tree752ed43462e42be64bbdfd599c25ff91262a9e31
initial commit
-rw-r--r--Cargo.toml17
-rw-r--r--src/display.rs123
-rw-r--r--src/lib.rs315
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(())
+ }
+ }
+ }
+}
+