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(()) +            } +        } +    } +} + | 
