summaryrefslogtreecommitdiff
path: root/arch/msp430/src
diff options
context:
space:
mode:
authorJoe Rozner <joe@deadbytes.net>2024-08-20 21:55:05 -0700
committerAlexander Taylor <alex@vector35.com>2024-08-22 19:35:30 -0400
commitf038c7c9a9646c2a126d693eab7d297cd25565e3 (patch)
tree51b1366dabb78fad2e2a7cc0518b3604620f66d8 /arch/msp430/src
parent2a9d07d7fefe5a6d7f7a482397060a96782142ac (diff)
Add msp430 architecture
Diffstat (limited to 'arch/msp430/src')
-rw-r--r--arch/msp430/src/architecture.rs763
-rw-r--r--arch/msp430/src/flag.rs153
-rw-r--r--arch/msp430/src/lib.rs55
-rw-r--r--arch/msp430/src/lift.rs670
-rw-r--r--arch/msp430/src/register.rs130
5 files changed, 1771 insertions, 0 deletions
diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs
new file mode 100644
index 00000000..ab392a38
--- /dev/null
+++ b/arch/msp430/src/architecture.rs
@@ -0,0 +1,763 @@
+use crate::flag::{Flag, FlagClass, FlagGroup, FlagWrite};
+use crate::lift::lift_instruction;
+use crate::register::Register;
+
+use binaryninja::{
+ architecture::{
+ Architecture, BranchInfo, CoreArchitecture, CustomArchitectureHandle, FlagCondition,
+ InstructionInfo, UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo,
+ },
+ disassembly::{InstructionTextToken, InstructionTextTokenContents},
+ llil::{LiftedExpr, Lifter},
+ Endianness,
+};
+
+use msp430_asm::{
+ emulate::Emulated, instruction::Instruction, jxx::Jxx, operand::Operand,
+ single_operand::SingleOperand, two_operand::TwoOperand,
+};
+
+use log::error;
+
+const MIN_MNEMONIC: usize = 9;
+
+pub struct Msp430 {
+ handle: CoreArchitecture,
+ custom_handle: CustomArchitectureHandle<Msp430>,
+}
+
+impl Msp430 {
+ pub fn new(handle: CoreArchitecture, custom_handle: CustomArchitectureHandle<Msp430>) -> Self {
+ Msp430 {
+ handle,
+ custom_handle,
+ }
+ }
+}
+
+impl Architecture for Msp430 {
+ type Handle = CustomArchitectureHandle<Self>;
+ type RegisterStackInfo = UnusedRegisterStackInfo<Self::Register>;
+ type RegisterStack = UnusedRegisterStack<Self::Register>;
+ type Register = Register;
+ type RegisterInfo = Register;
+ type Flag = Flag;
+ type FlagWrite = FlagWrite;
+ type FlagClass = FlagClass;
+ type FlagGroup = FlagGroup;
+ type Intrinsic = UnusedIntrinsic;
+
+ fn endianness(&self) -> Endianness {
+ Endianness::LittleEndian
+ }
+
+ fn address_size(&self) -> usize {
+ 2 // 16 bit
+ }
+
+ fn default_integer_size(&self) -> usize {
+ 2 // 16 bit integers
+ }
+
+ fn instruction_alignment(&self) -> usize {
+ 2
+ }
+
+ fn max_instr_len(&self) -> usize {
+ 6
+ }
+
+ fn opcode_display_len(&self) -> usize {
+ self.max_instr_len()
+ }
+
+ fn associated_arch_by_addr(&self, _addr: &mut u64) -> CoreArchitecture {
+ self.handle
+ }
+
+ fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo> {
+ match msp430_asm::decode(data) {
+ Ok(inst) => {
+ let mut info = InstructionInfo::new(inst.size(), 0);
+
+ match inst {
+ Instruction::Jnz(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jz(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jlo(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jc(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jn(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jge(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jl(inst) => {
+ info.add_branch(
+ BranchInfo::True(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ info.add_branch(
+ BranchInfo::False(addr + inst.size() as u64),
+ Some(self.handle),
+ );
+ }
+ Instruction::Jmp(inst) => {
+ info.add_branch(
+ BranchInfo::Unconditional(offset_to_absolute(addr, inst.offset())),
+ Some(self.handle),
+ );
+ }
+ Instruction::Br(inst) => match inst.destination() {
+ Some(Operand::RegisterDirect(_)) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Some(Operand::Indexed(_)) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Some(Operand::Absolute(value)) => info.add_branch(
+ BranchInfo::Unconditional(*value as u64),
+ Some(self.handle),
+ ),
+ Some(Operand::Symbolic(offset)) => info.add_branch(
+ BranchInfo::Unconditional((addr as i64 + *offset as i64) as u64),
+ Some(self.handle),
+ ),
+ Some(Operand::Immediate(addr)) => info
+ .add_branch(BranchInfo::Unconditional(*addr as u64), Some(self.handle)),
+ Some(Operand::Constant(_)) => {
+ info.add_branch(BranchInfo::Unconditional(addr), Some(self.handle))
+ }
+ Some(Operand::RegisterIndirect(_))
+ | Some(Operand::RegisterIndirectAutoIncrement(_)) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ None => {}
+ },
+ Instruction::Call(inst) => match inst.source() {
+ Operand::RegisterDirect(_) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Operand::Indexed(_) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ Operand::Absolute(value) => {
+ info.add_branch(BranchInfo::Call(*value as u64), Some(self.handle))
+ }
+ Operand::Symbolic(offset) => info.add_branch(
+ BranchInfo::Call((addr as i64 + *offset as i64) as u64),
+ Some(self.handle),
+ ),
+ Operand::Immediate(addr) => {
+ info.add_branch(BranchInfo::Call(*addr as u64), Some(self.handle))
+ }
+ Operand::Constant(_) => {
+ info.add_branch(BranchInfo::Call(addr), Some(self.handle))
+ }
+ Operand::RegisterIndirect(_)
+ | Operand::RegisterIndirectAutoIncrement(_) => {
+ info.add_branch(BranchInfo::Indirect, Some(self.handle))
+ }
+ },
+ Instruction::Reti(_) => {
+ info.add_branch(BranchInfo::FunctionReturn, Some(self.handle));
+ }
+ Instruction::Ret(_) => {
+ info.add_branch(BranchInfo::FunctionReturn, Some(self.handle));
+ }
+ _ => {}
+ }
+
+ Some(info)
+ }
+ Err(_) => None,
+ }
+ }
+
+ fn instruction_text(
+ &self,
+ data: &[u8],
+ addr: u64,
+ ) -> Option<(usize, Vec<InstructionTextToken>)> {
+ match msp430_asm::decode(data) {
+ Ok(inst) => {
+ let tokens = generate_tokens(&inst, addr);
+ if tokens.is_empty() {
+ None
+ } else {
+ Some((inst.size(), tokens))
+ }
+ }
+ Err(_) => None,
+ }
+ }
+
+ fn instruction_llil(
+ &self,
+ data: &[u8],
+ addr: u64,
+ il: &mut Lifter<Self>,
+ ) -> Option<(usize, bool)> {
+ match msp430_asm::decode(data) {
+ Ok(inst) => {
+ lift_instruction(&inst, addr, il);
+ Some((inst.size(), true))
+ }
+ Err(_) => None,
+ }
+ }
+
+ fn flags_required_for_flag_condition(
+ &self,
+ condition: FlagCondition,
+ _class: Option<Self::FlagClass>,
+ ) -> Vec<Self::Flag> {
+ match condition {
+ FlagCondition::LLFC_UGE => vec![Flag::C],
+ FlagCondition::LLFC_ULT => vec![Flag::C],
+ FlagCondition::LLFC_SGE => vec![Flag::N, Flag::V],
+ FlagCondition::LLFC_SLT => vec![Flag::N, Flag::V],
+ FlagCondition::LLFC_E => vec![Flag::Z],
+ FlagCondition::LLFC_NE => vec![Flag::Z],
+ FlagCondition::LLFC_NEG => vec![Flag::N],
+ FlagCondition::LLFC_POS => vec![Flag::N],
+ _ => vec![],
+ }
+ }
+
+ fn flag_group_llil<'a>(
+ &self,
+ _group: Self::FlagGroup,
+ _il: &'a mut Lifter<Self>,
+ ) -> Option<LiftedExpr<'a, Self>> {
+ None
+ }
+
+ fn registers_all(&self) -> Vec<Self::Register> {
+ vec![
+ Register::Pc,
+ Register::Sp,
+ Register::Sr,
+ Register::Cg,
+ Register::R4,
+ Register::R5,
+ Register::R6,
+ Register::R7,
+ Register::R8,
+ Register::R9,
+ Register::R10,
+ Register::R11,
+ Register::R12,
+ Register::R13,
+ Register::R14,
+ Register::R15,
+ ]
+ }
+
+ fn registers_full_width(&self) -> Vec<Self::Register> {
+ vec![
+ Register::Pc,
+ Register::Sp,
+ Register::Sr,
+ Register::Cg,
+ Register::R4,
+ Register::R5,
+ Register::R6,
+ Register::R7,
+ Register::R8,
+ Register::R9,
+ Register::R10,
+ Register::R11,
+ Register::R12,
+ Register::R13,
+ Register::R14,
+ Register::R15,
+ ]
+ }
+
+ fn registers_global(&self) -> Vec<Self::Register> {
+ Vec::new()
+ }
+
+ fn registers_system(&self) -> Vec<Self::Register> {
+ Vec::new()
+ }
+
+ fn flags(&self) -> Vec<Self::Flag> {
+ vec![Flag::C, Flag::Z, Flag::N, Flag::V]
+ }
+
+ fn flag_write_types(&self) -> Vec<Self::FlagWrite> {
+ vec![
+ FlagWrite::All,
+ FlagWrite::Nz,
+ FlagWrite::Nvz,
+ FlagWrite::Cnz,
+ ]
+ }
+
+ fn flag_classes(&self) -> Vec<Self::FlagClass> {
+ Vec::new()
+ }
+
+ fn flag_groups(&self) -> Vec<Self::FlagGroup> {
+ Vec::new()
+ }
+
+ fn stack_pointer_reg(&self) -> Option<Self::Register> {
+ Some(Register::Sp)
+ }
+
+ fn link_reg(&self) -> Option<Self::Register> {
+ None
+ }
+
+ fn register_from_id(&self, id: u32) -> Option<Self::Register> {
+ match id.try_into() {
+ Ok(register) => Some(register),
+ Err(_) => None,
+ }
+ }
+
+ fn flag_from_id(&self, id: u32) -> Option<Self::Flag> {
+ match id.try_into() {
+ Ok(flag) => Some(flag),
+ Err(_) => {
+ error!("invalid flag id {}", id);
+ None
+ }
+ }
+ }
+
+ fn flag_write_from_id(&self, id: u32) -> Option<Self::FlagWrite> {
+ match id.try_into() {
+ Ok(flag_write) => Some(flag_write),
+ Err(_) => {
+ error!("invalid flag write id {}", id);
+ None
+ }
+ }
+ }
+
+ fn flag_class_from_id(&self, _: u32) -> Option<Self::FlagClass> {
+ None
+ }
+
+ fn flag_group_from_id(&self, _: u32) -> Option<Self::FlagGroup> {
+ None
+ }
+
+ fn handle(&self) -> Self::Handle {
+ self.custom_handle
+ }
+}
+
+impl AsRef<CoreArchitecture> for Msp430 {
+ fn as_ref(&self) -> &CoreArchitecture {
+ &self.handle
+ }
+}
+
+fn generate_tokens(inst: &Instruction, addr: u64) -> Vec<InstructionTextToken> {
+ match inst {
+ Instruction::Rrc(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Swpb(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Rra(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Sxt(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Push(inst) => generate_single_operand_tokens(inst, addr, false),
+ Instruction::Call(inst) => generate_single_operand_tokens(inst, addr, true),
+ Instruction::Reti(_) => vec![InstructionTextToken::new(
+ "reti",
+ InstructionTextTokenContents::Instruction,
+ )],
+
+ // Jxx instructions
+ Instruction::Jnz(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jz(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jlo(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jc(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jn(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jge(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jl(inst) => generate_jxx_tokens(inst, addr),
+ Instruction::Jmp(inst) => generate_jxx_tokens(inst, addr),
+
+ // two operand instructions
+ Instruction::Mov(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Add(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Addc(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Subc(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Sub(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Cmp(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Dadd(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Bit(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Bic(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Bis(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::Xor(inst) => generate_two_operand_tokens(inst, addr),
+ Instruction::And(inst) => generate_two_operand_tokens(inst, addr),
+
+ // emulated
+ Instruction::Adc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Br(inst) => generate_emulated_tokens(inst, addr, true),
+ Instruction::Clr(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Clrc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Clrn(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Clrz(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Dadc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Dec(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Decd(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Dint(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Eint(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Inc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Incd(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Inv(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Nop(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Pop(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Ret(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Rla(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Rlc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Sbc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Setc(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Setn(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Setz(inst) => generate_emulated_tokens(inst, addr, false),
+ Instruction::Tst(inst) => generate_emulated_tokens(inst, addr, false),
+ }
+}
+
+fn generate_single_operand_tokens(
+ inst: &impl SingleOperand,
+ addr: u64,
+ call: bool,
+) -> Vec<InstructionTextToken> {
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, call));
+
+ res
+}
+
+fn generate_jxx_tokens(inst: &impl Jxx, addr: u64) -> Vec<InstructionTextToken> {
+ let fixed_addr = offset_to_absolute(addr, inst.offset());
+
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ res.push(InstructionTextToken::new(
+ &format!("0x{fixed_addr:4x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(fixed_addr),
+ ));
+
+ res
+}
+
+fn generate_two_operand_tokens(inst: &impl TwoOperand, addr: u64) -> Vec<InstructionTextToken> {
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ res.extend_from_slice(&generate_operand_tokens(inst.source(), addr, false));
+ res.push(InstructionTextToken::new(
+ ", ",
+ InstructionTextTokenContents::OperandSeparator,
+ ));
+ res.extend_from_slice(&generate_operand_tokens(inst.destination(), addr, false));
+
+ res
+}
+
+fn generate_emulated_tokens(
+ inst: &impl Emulated,
+ addr: u64,
+ call: bool,
+) -> Vec<InstructionTextToken> {
+ let mut res = vec![InstructionTextToken::new(
+ inst.mnemonic(),
+ InstructionTextTokenContents::Instruction,
+ )];
+
+ if inst.mnemonic().len() < MIN_MNEMONIC {
+ let padding = " ".repeat(MIN_MNEMONIC - inst.mnemonic().len());
+ res.push(InstructionTextToken::new(
+ &padding,
+ InstructionTextTokenContents::Text,
+ ))
+ }
+
+ if inst.destination().is_some() {
+ res.extend_from_slice(&generate_operand_tokens(
+ &inst.destination().unwrap(),
+ addr,
+ call,
+ ))
+ }
+
+ res
+}
+
+fn generate_operand_tokens(source: &Operand, addr: u64, call: bool) -> Vec<InstructionTextToken> {
+ match source {
+ Operand::RegisterDirect(r) => match r {
+ 0 => vec![InstructionTextToken::new(
+ "pc",
+ InstructionTextTokenContents::Register,
+ )],
+ 1 => vec![InstructionTextToken::new(
+ "sp",
+ InstructionTextTokenContents::Register,
+ )],
+ 2 => vec![InstructionTextToken::new(
+ "sr",
+ InstructionTextTokenContents::Register,
+ )],
+ 3 => vec![InstructionTextToken::new(
+ "cg",
+ InstructionTextTokenContents::Register,
+ )],
+ _ => vec![InstructionTextToken::new(
+ &format!("r{r}"),
+ InstructionTextTokenContents::Register,
+ )],
+ },
+ Operand::Indexed((r, i)) => match r {
+ 0 => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("pc", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ 1 => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("sp", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ 2 => {
+ let num_text = if *i >= 0 {
+ &format!("{i:#x}")
+ } else {
+ &format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("sr", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ 3 => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new("cg", InstructionTextTokenContents::Register),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ _ => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+ vec![
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ InstructionTextToken::new("(", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(
+ &format!("r{r}"),
+ InstructionTextTokenContents::Register,
+ ),
+ InstructionTextToken::new(")", InstructionTextTokenContents::Text),
+ ]
+ }
+ },
+ Operand::RegisterIndirect(r) => {
+ let r_text = if *r == 1 {
+ "sp".into()
+ } else {
+ format!("r{r}")
+ };
+
+ vec![
+ InstructionTextToken::new("@", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(&r_text, InstructionTextTokenContents::Register),
+ ]
+ }
+ Operand::RegisterIndirectAutoIncrement(r) => {
+ let r_text = if *r == 1 {
+ "sp".into()
+ } else {
+ format!("r{r}")
+ };
+
+ vec![
+ InstructionTextToken::new("@", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(&r_text, InstructionTextTokenContents::Register),
+ InstructionTextToken::new("+", InstructionTextTokenContents::Text),
+ ]
+ }
+ Operand::Symbolic(i) => {
+ let val = (addr as i64 + *i as i64) as u64;
+ vec![InstructionTextToken::new(
+ &format!("{val:#x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(val),
+ )]
+ }
+ Operand::Immediate(i) => {
+ if call {
+ vec![InstructionTextToken::new(
+ &format!("{i:#x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(*i as u64),
+ )]
+ } else {
+ vec![InstructionTextToken::new(
+ &format!("{i:#x}"),
+ InstructionTextTokenContents::PossibleAddress(*i as u64),
+ )]
+ }
+ }
+ Operand::Absolute(a) => {
+ if call {
+ vec![InstructionTextToken::new(
+ &format!("{a:#x}"),
+ InstructionTextTokenContents::CodeRelativeAddress(*a as u64),
+ )]
+ } else {
+ vec![InstructionTextToken::new(
+ &format!("{a:#x}"),
+ InstructionTextTokenContents::PossibleAddress(*a as u64),
+ )]
+ }
+ }
+ Operand::Constant(i) => {
+ let num_text = if *i >= 0 {
+ format!("{i:#x}")
+ } else {
+ format!("-{:#x}", -i)
+ };
+
+ vec![
+ InstructionTextToken::new("#", InstructionTextTokenContents::Text),
+ InstructionTextToken::new(
+ &num_text,
+ InstructionTextTokenContents::Integer(*i as u64),
+ ),
+ ]
+ }
+ }
+}
+
+pub(crate) fn offset_to_absolute(addr: u64, offset: i16) -> u64 {
+ // add + 2 to addr to get past the jxx instruction which is always 2 bytes
+ ((addr + 2) as i64 + ((offset * 2) as i64)) as u64
+}
diff --git a/arch/msp430/src/flag.rs b/arch/msp430/src/flag.rs
new file mode 100644
index 00000000..115fe866
--- /dev/null
+++ b/arch/msp430/src/flag.rs
@@ -0,0 +1,153 @@
+use binaryninja::architecture;
+use binaryninja::architecture::FlagRole;
+
+use std::borrow::Cow;
+use std::collections::HashMap;
+
+// NOTE: GIE, CPUOFF, OSCOFF, SG0, and SG1 not implemented as it's not clear how they would be used
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Flag {
+ C,
+ Z,
+ N,
+ V,
+}
+
+impl architecture::Flag for Flag {
+ type FlagClass = FlagClass;
+
+ fn name(&self) -> Cow<str> {
+ match self {
+ Self::C => "c".into(),
+ Self::Z => "z".into(),
+ Self::N => "n".into(),
+ Self::V => "v".into(),
+ }
+ }
+
+ fn role(&self, _class: Option<Self::FlagClass>) -> architecture::FlagRole {
+ match self {
+ Self::C => FlagRole::CarryFlagRole,
+ Self::Z => FlagRole::ZeroFlagRole,
+ Self::N => FlagRole::NegativeSignFlagRole,
+ Self::V => FlagRole::OverflowFlagRole,
+ }
+ }
+
+ fn id(&self) -> u32 {
+ match self {
+ Self::C => 0,
+ Self::Z => 1,
+ Self::N => 2,
+ Self::V => 8,
+ }
+ }
+}
+
+impl TryFrom<u32> for Flag {
+ type Error = ();
+ fn try_from(flag: u32) -> Result<Self, Self::Error> {
+ match flag {
+ 0 => Ok(Self::C),
+ 1 => Ok(Self::Z),
+ 2 => Ok(Self::N),
+ 8 => Ok(Self::V),
+ _ => Err(()),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct FlagClass {}
+
+impl architecture::FlagClass for FlagClass {
+ fn name(&self) -> Cow<str> {
+ unimplemented!()
+ }
+
+ fn id(&self) -> u32 {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum FlagGroup {}
+
+impl architecture::FlagGroup for FlagGroup {
+ type FlagType = Flag;
+ type FlagClass = FlagClass;
+
+ fn name(&self) -> Cow<str> {
+ unimplemented!()
+ }
+
+ fn id(&self) -> u32 {
+ unimplemented!()
+ }
+
+ fn flags_required(&self) -> Vec<Self::FlagType> {
+ unimplemented!()
+ }
+
+ fn flag_conditions(&self) -> HashMap<Self::FlagClass, architecture::FlagCondition> {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum FlagWrite {
+ All,
+ Nz,
+ Nvz,
+ Cnz,
+}
+
+impl architecture::FlagWrite for FlagWrite {
+ type FlagType = Flag;
+ type FlagClass = FlagClass;
+
+ fn name(&self) -> Cow<str> {
+ match self {
+ Self::All => "*".into(),
+ Self::Nz => "nz".into(),
+ Self::Nvz => "nvz".into(),
+ Self::Cnz => "cnz".into(),
+ }
+ }
+
+ fn class(&self) -> Option<Self::FlagClass> {
+ None
+ }
+
+ fn id(&self) -> u32 {
+ match self {
+ Self::All => 1,
+ Self::Nz => 2,
+ Self::Nvz => 3,
+ Self::Cnz => 4,
+ }
+ }
+
+ fn flags_written(&self) -> Vec<Self::FlagType> {
+ match self {
+ Self::All => vec![Flag::C, Flag::N, Flag::V, Flag::Z],
+ Self::Nz => vec![Flag::N, Flag::Z],
+ Self::Nvz => vec![Flag::N, Flag::V, Flag::Z],
+ Self::Cnz => vec![Flag::C, Flag::N, Flag::Z],
+ }
+ }
+}
+
+impl TryFrom<u32> for FlagWrite {
+ type Error = ();
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ match value {
+ 1 => Ok(Self::All),
+ 2 => Ok(Self::Nz),
+ 3 => Ok(Self::Nvz),
+ 4 => Ok(Self::Cnz),
+ _ => Err(()),
+ }
+ }
+}
diff --git a/arch/msp430/src/lib.rs b/arch/msp430/src/lib.rs
new file mode 100644
index 00000000..ffa89bad
--- /dev/null
+++ b/arch/msp430/src/lib.rs
@@ -0,0 +1,55 @@
+extern crate binaryninja;
+extern crate log;
+extern crate msp430_asm;
+
+use binaryninja::{
+ architecture::ArchitectureExt,
+ callingconvention,
+ custombinaryview::{BinaryViewType, BinaryViewTypeExt},
+ Endianness,
+};
+
+mod architecture;
+mod flag;
+mod lift;
+mod register;
+
+use architecture::Msp430;
+
+#[no_mangle]
+#[allow(non_snake_case)]
+pub extern "C" fn CorePluginInit() -> bool {
+ binaryninja::logger::init(log::LevelFilter::Info).unwrap();
+ let arch = binaryninja::architecture::register_architecture(
+ "msp430",
+ |custom_handle, handle| Msp430::new(handle, custom_handle),
+ );
+
+ // we may need to introduce additional calling conventions here to
+ // support additional ABIs. MSPGCC's calling convention (what
+ // microcorruption seems to use) has some differences between the EABI
+ // calling convention though GCC has since added support for the EABI
+ // calling convention according to
+ // https://www.ti.com/lit/an/slaa664/slaa664.pdf?ts=1613210655081. MSPGCC
+ // appears to be a legacy calling convention while EABI is the newer
+ // standardized one that is compatible with TI's compiler
+ let default = callingconvention::ConventionBuilder::new(arch)
+ .is_eligible_for_heuristics(true)
+ .int_arg_registers(&["r15", "r14", "r13", "r12"])
+ .return_int_reg("r15")
+ .return_hi_int_reg("r14")
+ .register("default");
+ callingconvention::ConventionBuilder::new(arch)
+ .is_eligible_for_heuristics(true)
+ .return_int_reg("r15")
+ .return_hi_int_reg("r14")
+ .register("stack");
+
+ arch.set_default_calling_convention(&default);
+
+ if let Ok(bv) = BinaryViewType::by_name("ELF") {
+ bv.register_arch(105, Endianness::LittleEndian, arch);
+ }
+
+ true
+}
diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs
new file mode 100644
index 00000000..85d204e1
--- /dev/null
+++ b/arch/msp430/src/lift.rs
@@ -0,0 +1,670 @@
+use crate::architecture::offset_to_absolute;
+use crate::flag::{Flag, FlagWrite};
+use crate::register::Register;
+use crate::Msp430;
+
+use binaryninja::{
+ architecture::FlagCondition,
+ llil::{Label, LiftedNonSSA, Lifter, Mutable, NonSSA},
+};
+
+use msp430_asm::emulate::Emulated;
+use msp430_asm::instruction::Instruction;
+use msp430_asm::jxx::Jxx;
+use msp430_asm::operand::{Operand, OperandWidth};
+use msp430_asm::single_operand::SingleOperand;
+use msp430_asm::two_operand::TwoOperand;
+
+use log::info;
+
+macro_rules! auto_increment {
+ ($src:expr, $il:ident) => {
+ if let Operand::RegisterIndirectAutoIncrement(r) = $src {
+ $il.set_reg(
+ 2,
+ Register::try_from(*r as u32).unwrap(),
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, 2),
+ ),
+ )
+ .append();
+ }
+ };
+}
+
+macro_rules! one_operand {
+ ($source:expr, $il:ident, $op:ident) => {
+ match $source {
+ Operand::RegisterDirect(r) => $il
+ .set_reg(2, Register::try_from(*r as u32).unwrap(), $op)
+ .append(),
+ Operand::Indexed((r, offset)) => $il
+ .store(
+ 2,
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, *offset as u64),
+ ),
+ $op,
+ )
+ .append(),
+ Operand::Symbolic(offset) => $il
+ .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op)
+ .append(),
+ Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(),
+ Operand::Immediate(_) => $op.append(),
+ Operand::RegisterIndirect(r) => $il
+ .store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op)
+ .append(),
+ Operand::RegisterIndirectAutoIncrement(r) => {
+ $il.store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op)
+ .append();
+ $il.set_reg(
+ 2,
+ Register::try_from(*r as u32).unwrap(),
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, 2),
+ ),
+ )
+ .append()
+ }
+ _ => {
+ unreachable!()
+ }
+ };
+ };
+}
+
+macro_rules! two_operand {
+ ($destination:expr, $il:ident, $op:ident) => {
+ match $destination {
+ Operand::RegisterDirect(r) => $il
+ .set_reg(2, Register::try_from(*r as u32).unwrap(), $op)
+ .append(),
+ Operand::Indexed((r, offset)) => $il
+ .store(
+ 2,
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, *offset as u64),
+ ),
+ $op,
+ )
+ .append(),
+ Operand::Symbolic(offset) => $il
+ .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op)
+ .append(),
+ Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(),
+ _ => {
+ unreachable!()
+ }
+ };
+ };
+}
+
+macro_rules! emulated {
+ ($inst:ident, $il:ident, $op:ident) => {
+ match $inst.destination() {
+ Some(Operand::RegisterDirect(r)) => $il
+ .set_reg(2, Register::try_from(*r as u32).unwrap(), $op)
+ .append(),
+ Some(Operand::Indexed((r, offset))) => $il
+ .store(
+ 2,
+ $il.add(
+ 2,
+ $il.reg(2, Register::try_from(*r as u32).unwrap()),
+ $il.const_int(2, *offset as u64),
+ ),
+ $op,
+ )
+ .append(),
+ Some(Operand::Symbolic(offset)) => $il
+ .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op)
+ .append(),
+ Some(Operand::Absolute(val)) => $il.store(2, $il.const_ptr(*val as u64), $op).append(),
+ _ => {
+ unreachable!()
+ }
+ };
+ };
+}
+
+macro_rules! conditional_jump {
+ ($addr:ident, $inst:ident, $cond:ident, $il:ident) => {
+ let true_addr = offset_to_absolute($addr, $inst.offset());
+ let false_addr = $addr + $inst.size() as u64;
+ let mut new_true = Label::new();
+ let mut new_false = Label::new();
+
+ let true_label = $il.label_for_address(true_addr);
+ let false_label = $il.label_for_address(false_addr);
+
+ $il.if_expr(
+ $cond,
+ true_label.unwrap_or_else(|| &new_true),
+ false_label.unwrap_or_else(|| &new_false),
+ )
+ .append();
+
+ if true_label.is_none() {
+ $il.mark_label(&mut new_true);
+ }
+
+ $il.goto(true_label.unwrap_or_else(|| &new_true)).append();
+
+ if false_label.is_none() {
+ $il.mark_label(&mut new_false);
+ }
+ };
+}
+
+pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter<Msp430>) {
+ match inst {
+ Instruction::Rrc(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(inst.source(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.rrc(size, dest, src).with_flag_write(FlagWrite::All))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.rrc(size, dest, src).with_flag_write(FlagWrite::All)
+ }
+ };
+ one_operand!(inst.source(), il, op);
+ }
+ Instruction::Swpb(inst) => {
+ let src = lift_source_operand(inst.source(), 2, il);
+ let op = il.rol(2, src, il.const_int(2, 8));
+ one_operand!(inst.source(), il, op);
+ }
+ Instruction::Rra(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(inst.source(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz)
+ }
+ };
+ one_operand!(inst.source(), il, op);
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ }
+ Instruction::Sxt(inst) => {
+ // source is always 1 byte and instruction is always 2 bytes for sxt because we're sign
+ // extending the low byte into the high and the result is always 2 bytes
+ let src = lift_source_operand(inst.source(), 1, il);
+ let op = il.sx(2, src).with_flag_write(FlagWrite::Nz);
+ one_operand!(inst.source(), il, op);
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ }
+ Instruction::Push(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = lift_source_operand(inst.source(), size, il);
+ il.push(2, src).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Call(inst) => {
+ // TODO: verify the special autoincrement behavior Josh implemented?
+ let src = if let Operand::Immediate(src) = inst.source() {
+ il.const_ptr(*src as u64)
+ } else {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+
+ lift_source_operand(inst.source(), size, il)
+ };
+ il.call(src).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Reti(_) => {
+ il.set_reg(2, Register::Sr, il.pop(2))
+ .with_flag_write(FlagWrite::All)
+ .append();
+ il.ret(il.pop(2)).append();
+ }
+
+ // Jxx instructions
+ Instruction::Jnz(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_NE);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jz(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_E);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jlo(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_ULT);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jc(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_UGE);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jn(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_NEG);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jge(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_SGE);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jl(inst) => {
+ let cond = il.flag_cond(FlagCondition::LLFC_SLT);
+ conditional_jump!(addr, inst, cond, il);
+ }
+ Instruction::Jmp(inst) => {
+ let fixed_addr = offset_to_absolute(addr, inst.offset());
+ let label = il.label_for_address(fixed_addr);
+ match label {
+ Some(label) => {
+ il.goto(label).append();
+ }
+ None => {
+ il.jump(il.const_ptr(fixed_addr)).append();
+ }
+ }
+ }
+
+ // two operand instructions
+ Instruction::Mov(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = match inst.operand_width() {
+ OperandWidth::Byte => il
+ .sx(2, lift_source_operand(inst.source(), size, il))
+ .build(),
+ OperandWidth::Word => lift_source_operand(inst.source(), size, il),
+ };
+ two_operand!(inst.destination(), il, src);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Add(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.add(size, src, dest).with_flag_write(FlagWrite::All))
+ }
+ OperandWidth::Word => il.add(size, src, dest).with_flag_write(FlagWrite::All),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Addc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Subc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Sub(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.sub(size, src, dest).with_flag_write(FlagWrite::All))
+ }
+ OperandWidth::Word => il.sub(size, src, dest).with_flag_write(FlagWrite::All),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Cmp(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ il.sub(size, dest, src)
+ .with_flag_write(FlagWrite::All)
+ .append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Dadd(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Bit(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ il.and(size, src, dest)
+ .with_flag_write(FlagWrite::Nz)
+ .append();
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Bic(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => il.sx(2, il.and(size, il.not(size, src), dest)),
+ OperandWidth::Word => il.and(size, il.not(size, src), dest),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Bis(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => il.sx(2, il.or(size, src, dest)),
+ OperandWidth::Word => il.or(size, src, dest),
+ };
+ two_operand!(inst.destination(), il, op);
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::Xor(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz))
+ }
+ OperandWidth::Word => il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz),
+ };
+ two_operand!(inst.destination(), il, op);
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ auto_increment!(inst.source(), il);
+ }
+ Instruction::And(inst) => {
+ let size = width_to_size(inst.operand_width());
+ let src = lift_source_operand(inst.source(), size, il);
+ let dest = lift_source_operand(inst.destination(), size, il);
+ let op = match inst.operand_width() {
+ OperandWidth::Byte => {
+ il.sx(2, il.and(size, src, dest).with_flag_write(FlagWrite::Nz))
+ }
+ OperandWidth::Word => il.and(size, src, dest).with_flag_write(FlagWrite::Nz),
+ };
+ two_operand!(inst.destination(), il, op);
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ auto_increment!(inst.source(), il);
+ }
+
+ // emulated
+ Instruction::Adc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Br(inst) => {
+ let dest = if let Some(Operand::Immediate(dest)) = inst.destination() {
+ if let Some(label) = il.label_for_address(*dest as u64) {
+ il.goto(label).append();
+ return;
+ } else {
+ il.const_ptr(*dest as u64)
+ }
+ } else {
+ lift_source_operand(&inst.destination().unwrap(), 2, il)
+ };
+
+ il.jump(dest).append();
+ }
+ Instruction::Clr(inst) => {
+ let op = il.const_int(2, 0);
+ emulated!(inst, il, op);
+ }
+ Instruction::Clrc(_) => {
+ // TODO: should we lift clearing the C bit in the SR register as well?
+ il.set_flag(Flag::C, il.const_int(0, 0)).append();
+ }
+ Instruction::Clrn(_) => {
+ // TODO: should we lift clearing the N bit in the SR register as well?
+ il.set_flag(Flag::N, il.const_int(0, 0)).append();
+ }
+ Instruction::Clrz(_) => {
+ // TODO: should we lift clearing the Z bit in the SR register as well?
+ il.set_flag(Flag::Z, il.const_int(0, 0)).append();
+ }
+ Instruction::Dadc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Dec(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.sub(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .sub(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Decd(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.sub(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .sub(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Dint(_) => {
+ // If GIE flag is ever exposed this should clear it
+ }
+ Instruction::Eint(_) => {
+ // If GIE flag is ever exposed this should set it
+ }
+ Instruction::Inc(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.add(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .add(size, dest, il.const_int(size, 1))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Incd(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => il.sx(
+ 2,
+ il.add(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ ),
+ Some(OperandWidth::Word) | None => il
+ .add(size, dest, il.const_int(size, 2))
+ .with_flag_write(FlagWrite::All),
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Inv(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.not(size, dest).with_flag_write(FlagWrite::Nvz))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.not(size, dest).with_flag_write(FlagWrite::Nvz)
+ }
+ };
+ emulated!(inst, il, op);
+ il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append();
+ }
+ Instruction::Nop(_) => {
+ il.nop().append();
+ }
+ Instruction::Pop(inst) => {
+ if let Some(Operand::RegisterDirect(r)) = inst.destination() {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ il.set_reg(size, Register::try_from(*r as u32).unwrap(), il.pop(2))
+ .append();
+ } else {
+ info!("pop: invalid destination operand");
+ }
+ }
+ Instruction::Ret(_) => {
+ il.ret(il.pop(2)).append();
+ }
+ Instruction::Rla(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.rol(size, dest, src).with_flag_write(FlagWrite::All))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.rol(size, dest, src).with_flag_write(FlagWrite::All)
+ }
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Rlc(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let src = il.const_int(size, 1);
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ let op = match inst.operand_width() {
+ Some(OperandWidth::Byte) => {
+ il.sx(2, il.rlc(size, dest, src).with_flag_write(FlagWrite::All))
+ }
+ Some(OperandWidth::Word) | None => {
+ il.rlc(size, dest, src).with_flag_write(FlagWrite::All)
+ }
+ };
+ emulated!(inst, il, op);
+ }
+ Instruction::Sbc(_) => {
+ il.unimplemented().append();
+ }
+ Instruction::Setc(_) => {
+ // TODO: should we lift setting the C bit in the SR register as well?
+ il.set_flag(Flag::C, il.const_int(0, 1)).append();
+ }
+ Instruction::Setn(_) => {
+ // TODO: should we lift setting the N bit in the SR register as well?
+ il.set_flag(Flag::N, il.const_int(0, 1)).append();
+ }
+ Instruction::Setz(_) => {
+ // TODO: should we lift setting the Z bit in the SR register as well?
+ il.set_flag(Flag::Z, il.const_int(0, 1)).append();
+ }
+ Instruction::Tst(inst) => {
+ let size = match inst.operand_width() {
+ Some(width) => width_to_size(width),
+ None => 2,
+ };
+ let dest = lift_source_operand(&inst.destination().unwrap(), size, il);
+ il.sub(size, dest, il.const_int(size, 0))
+ .with_flag_write(FlagWrite::Nz)
+ .append();
+ il.set_flag(Flag::V, il.const_int(0, 0)).append();
+ il.set_flag(Flag::C, il.const_int(0, 1)).append();
+ }
+ }
+}
+
+fn lift_source_operand<'a>(
+ operand: &Operand,
+ size: usize,
+ il: &'a Lifter<Msp430>,
+) -> binaryninja::llil::Expression<
+ 'a,
+ Msp430,
+ Mutable,
+ NonSSA<LiftedNonSSA>,
+ binaryninja::llil::ValueExpr,
+> {
+ match operand {
+ Operand::RegisterDirect(r) => il.reg(size, Register::try_from(*r as u32).unwrap()),
+ Operand::Indexed((r, offset)) => il
+ .load(
+ size,
+ il.add(
+ 2,
+ il.reg(2, Register::try_from(*r as u32).unwrap()),
+ il.const_int(2, *offset as u64),
+ ),
+ )
+ .build(),
+ // should we add offset to addr here rather than lifting to the register since we know where PC is?
+ Operand::Symbolic(offset) => il
+ .load(
+ size,
+ il.add(2, il.reg(2, Register::Pc), il.const_int(2, *offset as u64)),
+ )
+ .build(),
+ Operand::Absolute(addr) => il.load(size, il.const_ptr(*addr as u64)).build(),
+ // these are the same, we need to autoincrement in a separate il instruction
+ Operand::RegisterIndirect(r) | Operand::RegisterIndirectAutoIncrement(r) => il
+ .load(size, il.reg(2, Register::try_from(*r as u32).unwrap()))
+ .build(),
+ Operand::Immediate(val) => il.const_int(size, *val as u64),
+ Operand::Constant(val) => il.const_int(size, *val as u64),
+ }
+}
+
+fn width_to_size(width: &OperandWidth) -> usize {
+ match width {
+ OperandWidth::Byte => 1,
+ OperandWidth::Word => 2,
+ }
+}
diff --git a/arch/msp430/src/register.rs b/arch/msp430/src/register.rs
new file mode 100644
index 00000000..20a5dff8
--- /dev/null
+++ b/arch/msp430/src/register.rs
@@ -0,0 +1,130 @@
+use binaryninja::architecture;
+use binaryninja::architecture::ImplicitRegisterExtend;
+
+use std::borrow::Cow;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Register {
+ Pc,
+ Sp,
+ Sr,
+ Cg,
+ R4,
+ R5,
+ R6,
+ R7,
+ R8,
+ R9,
+ R10,
+ R11,
+ R12,
+ R13,
+ R14,
+ R15,
+}
+
+impl TryFrom<u32> for Register {
+ type Error = ();
+
+ fn try_from(id: u32) -> Result<Self, Self::Error> {
+ // TODO: we should return separate errors if the id is between 0x7fff_ffff and 0xffff_ffff
+ // vs outside of that range. Temporary registers have have the high bit set which we
+ // shouldn't get, unless there is a bug in core. An id that isn't within that range but we
+ // don't handle is a bug in the architecture.
+ match id {
+ 0 => Ok(Self::Pc),
+ 1 => Ok(Self::Sp),
+ 2 => Ok(Self::Sr),
+ 3 => Ok(Self::Cg),
+ 4 => Ok(Self::R4),
+ 5 => Ok(Self::R5),
+ 6 => Ok(Self::R6),
+ 7 => Ok(Self::R7),
+ 8 => Ok(Self::R8),
+ 9 => Ok(Self::R9),
+ 10 => Ok(Self::R10),
+ 11 => Ok(Self::R11),
+ 12 => Ok(Self::R12),
+ 13 => Ok(Self::R13),
+ 14 => Ok(Self::R14),
+ 15 => Ok(Self::R15),
+ _ => Err(()),
+ }
+ }
+}
+
+impl architecture::Register for Register {
+ type InfoType = Self;
+
+ fn name(&self) -> Cow<'_, str> {
+ match self {
+ Self::Pc => "pc".into(),
+ Self::Sp => "sp".into(),
+ Self::Sr => "sr".into(),
+ Self::Cg => "cg".into(),
+ Self::R4
+ | Self::R5
+ | Self::R6
+ | Self::R7
+ | Self::R8
+ | Self::R9
+ | Self::R10
+ | Self::R11
+ | Self::R12
+ | Self::R13
+ | Self::R14
+ | Self::R15 => format!("r{}", self.id()).into(),
+ }
+ }
+
+ fn info(&self) -> Self::InfoType {
+ *self
+ }
+
+ fn id(&self) -> u32 {
+ match self {
+ Self::Pc => 0,
+ Self::Sp => 1,
+ Self::Sr => 2,
+ Self::Cg => 3,
+ Self::R4 => 4,
+ Self::R5 => 5,
+ Self::R6 => 6,
+ Self::R7 => 7,
+ Self::R8 => 8,
+ Self::R9 => 9,
+ Self::R10 => 10,
+ Self::R11 => 11,
+ Self::R12 => 12,
+ Self::R13 => 13,
+ Self::R14 => 14,
+ Self::R15 => 15,
+ }
+ }
+}
+
+impl architecture::RegisterInfo for Register {
+ type RegType = Self;
+
+ fn parent(&self) -> Option<Self::RegType> {
+ None
+ }
+
+ fn size(&self) -> usize {
+ 2
+ }
+
+ fn offset(&self) -> usize {
+ 0
+ }
+
+ fn implicit_extend(&self) -> ImplicitRegisterExtend {
+ ImplicitRegisterExtend::NoExtend
+ }
+}
+
+impl From<Register> for binaryninja::llil::Register<Register> {
+ fn from(register: Register) -> Self {
+ binaryninja::llil::Register::ArchReg(register)
+ }
+}