diff --git a/arm7tdmi/src/arm/arm_isa.rs b/arm7tdmi/src/arm/arm_isa.rs index d2eda03..057bb18 100644 --- a/arm7tdmi/src/arm/arm_isa.rs +++ b/arm7tdmi/src/arm/arm_isa.rs @@ -9,6 +9,7 @@ pub enum ArmError { UnknownInstructionFormat(u32), UndefinedConditionCode(u32), InvalidShiftType(u32), + InvalidHSBits(u32), } #[derive(Debug, Copy, Clone, PartialEq, Primitive)] @@ -81,6 +82,13 @@ pub enum ArmOpCode { MVN = 0b1111, } +#[derive(Debug, PartialEq, Primitive)] +pub enum ArmHalfwordTransferType { + UnsignedHalfwords = 0b01, + SignedByte = 0b10, + SignedHalfwords = 0b11, +} + #[derive(Debug, Copy, Clone, PartialEq)] pub struct ArmInstruction { pub cond: ArmCond, @@ -106,10 +114,20 @@ impl TryFrom<(u32, u32)> for ArmInstruction { Ok(BX) } else if (0x0e00_0000 & raw) == 0x0a00_0000 { Ok(B_BL) + } else if (0xe000_0010 & raw) == 0x0600_0000 { + Err(ArmError::UnknownInstructionFormat(raw)) + } else if (0x0fb0_0ff0 & raw) == 0x0100_0090 { + Ok(SWP) } else if (0x0fc0_00f0 & raw) == 0x0000_0090 { Ok(MUL_MLA) } else if (0x0f80_00f0 & raw) == 0x0080_0090 { Ok(MULL_MLAL) + } else if (0x0fbf_0fff & raw) == 0x010f_0000 { + Ok(MRS) + } else if (0x0fbf_fff0 & raw) == 0x0129_f000 { + Ok(MSR_REG) + } else if (0x0dbf_f000 & raw) == 0x0128_f000 { + Ok(MSR_FLAGS) } else if (0x0c00_0000 & raw) == 0x0400_0000 { Ok(LDR_STR) } else if (0x0e40_0F90 & raw) == 0x0000_0090 { @@ -118,16 +136,6 @@ impl TryFrom<(u32, u32)> for ArmInstruction { Ok(LDR_STR_HS_IMM) } else if (0x0e00_0000 & raw) == 0x0800_0000 { Ok(LDM_STM) - } else if (0x0fb0_0ff0 & raw) == 0x0100_0090 { - Ok(SWP) - } else if (0x0fbf_0fff & raw) == 0x010f_0000 { - Ok(MRS) - } else if (0x0fbf_fff0 & raw) == 0x0129_f000 { - Ok(MSR_REG) - } else if (0x0dbf_f000 & raw) == 0x0128_f000 { - Ok(MSR_FLAGS) - } else if (0x0fb0_0ff0 & raw) == 0x0100_0090 { - Ok(SWP) } else if (0x0c00_0000 & raw) == 0x0000_0000 { Ok(DP) } else { @@ -176,16 +184,20 @@ impl TryFrom for ArmShift { } #[derive(Debug, PartialEq)] -pub enum ArmInstructionShiftValue { - ImmediateValue(u32), +pub enum ArmShiftedValue { + ImmediateValue(i32), RotatedImmediate(u32, u32), - ShiftedRegister(usize, ArmShift), + ShiftedRegister { + reg: usize, + shift: ArmShift, + added: Option, + }, } -impl ArmInstructionShiftValue { +impl ArmShiftedValue { /// Decode operand2 as an immediate value pub fn decode_rotated_immediate(&self) -> Option { - if let ArmInstructionShiftValue::RotatedImmediate(immediate, rotate) = self { + if let ArmShiftedValue::RotatedImmediate(immediate, rotate) = self { return Some(immediate.rotate_right(*rotate) as i32); } None @@ -212,7 +224,19 @@ impl ArmInstruction { pub fn rm(&self) -> usize { self.raw.bit_range(0..4) as usize } - + + pub fn rs(&self) -> usize { + self.raw.bit_range(8..12) as usize + } + + pub fn rd_lo(&self) -> usize { + self.raw.bit_range(12..16) as usize + } + + pub fn rd_hi(&self) -> usize { + self.raw.bit_range(16..20) as usize + } + pub fn opcode(&self) -> Option { ArmOpCode::from_u32(self.raw.bit_range(21..25)) } @@ -221,18 +245,33 @@ impl ArmInstruction { ((((self.raw << 8) as i32) >> 8) << 2) + 8 } - pub fn is_load(&self) -> bool { + pub fn load_flag(&self) -> bool { self.raw.bit(20) } - pub fn is_set_cond(&self) -> bool { + pub fn set_cond_flag(&self) -> bool { self.raw.bit(20) } - pub fn is_write_back(&self) -> bool { + pub fn write_back_flag(&self) -> bool { self.raw.bit(21) } + pub fn accumulate_flag(&self) -> bool { + self.raw.bit(21) + } + + pub fn u_flag(&self) -> bool { + self.raw.bit(22) + } + + pub fn halfword_data_transfer_type(&self) -> Result { + match ArmHalfwordTransferType::from_u32((self.raw & 0b1100000) >> 5) { + Some(x) => Ok(x), + None => Err(ArmError::InvalidHSBits(self.raw)), + } + } + pub fn transfer_size(&self) -> usize { if self.raw.bit(22) { 1 @@ -245,43 +284,79 @@ impl ArmInstruction { self.raw.bit(22) } - pub fn is_spsr(&self) -> bool { + pub fn spsr_flag(&self) -> bool { self.raw.bit(22) } - pub fn is_ofs_added(&self) -> bool { + pub fn add_offset_flag(&self) -> bool { self.raw.bit(23) } - pub fn is_pre_indexing(&self) -> bool { + pub fn pre_index_flag(&self) -> bool { self.raw.bit(24) } - pub fn is_linked_branch(&self) -> bool { + pub fn link_flag(&self) -> bool { self.raw.bit(24) } - pub fn offset(&self) -> ArmInstructionShiftValue { + /// gets offset used by ldr/str instructions + pub fn ldr_str_offset(&self) -> ArmShiftedValue { let ofs = self.raw.bit_range(0..12); if self.raw.bit(25) { let rm = ofs & 0xf; let shift = ArmShift::try_from(ofs).unwrap(); - ArmInstructionShiftValue::ShiftedRegister(rm as usize, shift) + ArmShiftedValue::ShiftedRegister { + reg: rm as usize, + shift: shift, + added: Some(self.add_offset_flag()), + } } else { - ArmInstructionShiftValue::ImmediateValue(ofs) + let ofs = if self.add_offset_flag() { + ofs as i32 + } else { + -(ofs as i32) + }; + ArmShiftedValue::ImmediateValue(ofs) } } - pub fn operand2(&self) -> ArmInstructionShiftValue { + pub fn ldr_str_hs_offset(&self) -> Option { + match self.fmt { + ArmInstructionFormat::LDR_STR_HS_IMM => { + let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4); + let offset8 = if self.add_offset_flag() { + offset8 as i32 + } else { + -(offset8 as i32) + }; + Some(ArmShiftedValue::ImmediateValue(offset8)) + }, + ArmInstructionFormat::LDR_STR_HS_REG => { + Some(ArmShiftedValue::ShiftedRegister { + reg: (self.raw & 0xf) as usize, + shift: ArmShift::ImmediateShift(0, ArmShiftType::LSL), + added: Some(self.add_offset_flag()) + }) + }, + _ => None + } + } + + pub fn operand2(&self) -> ArmShiftedValue { let op2 = self.raw.bit_range(0..12); if self.raw.bit(25) { let immediate = op2 & 0xff; let rotate = 2 * op2.bit_range(8..12); - ArmInstructionShiftValue::RotatedImmediate(immediate, rotate) + ArmShiftedValue::RotatedImmediate(immediate, rotate) } else { let reg = op2 & 0xf; let shift = ArmShift::try_from(op2).unwrap(); // TODO error handling - ArmInstructionShiftValue::ShiftedRegister(reg as usize, shift) + ArmShiftedValue::ShiftedRegister { + reg: reg as usize, + shift: shift, + added: None, + } } } diff --git a/arm7tdmi/src/arm/display.rs b/arm7tdmi/src/arm/display.rs index c451099..92d532d 100644 --- a/arm7tdmi/src/arm/display.rs +++ b/arm7tdmi/src/arm/display.rs @@ -1,7 +1,7 @@ -use super::super::reg_string; +use super::super::{reg_string, REG_PC}; use super::arm_isa::{ - ArmCond, ArmInstruction, ArmInstructionFormat, ArmInstructionShiftValue, ArmOpCode, ArmShift, - ArmShiftType, + ArmCond, ArmInstruction, ArmInstructionFormat, ArmShiftedValue, ArmOpCode, ArmShift, + ArmShiftType, ArmHalfwordTransferType }; use std::fmt; @@ -64,6 +64,17 @@ impl fmt::Display for ArmShiftType { } } +impl fmt::Display for ArmHalfwordTransferType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ArmHalfwordTransferType::*; + match self { + UnsignedHalfwords => write!(f, "h"), + SignedHalfwords => write!(f, "sh"), + SignedByte => write!(f, "sb"), + } + } +} + fn is_shift(shift: &ArmShift) -> bool { if let ArmShift::ImmediateShift(val, typ) = shift { return !(*val == 0 && *typ == ArmShiftType::LSL); @@ -92,98 +103,127 @@ impl ArmInstruction { write!( f, "b{link}{cond}\t{ofs:#x}", - link = if self.is_linked_branch() { "l" } else { "" }, + link = if self.link_flag() { "l" } else { "" }, cond = self.cond, ofs = self.pc.wrapping_add(self.branch_offset() as u32) as u32 ) } + + fn set_cond_mark(&self) -> &str { + if self.set_cond_flag() { "s" } else { "" } + } fn fmt_data_processing(&self, f: &mut fmt::Formatter) -> fmt::Result { use ArmOpCode::*; let opcode = self.opcode().unwrap(); - let rd = reg_string(self.rd()); match opcode { - // {cond}{S} Rd, MOV | MVN => write!( f, - "{opcode}{cond}{S}\t{Rd}", + "{opcode}{S}{cond}\t{Rd}, ", opcode = opcode, cond = self.cond, - S = if self.is_set_cond() { "s" } else { "" }, - Rd = rd + S = self.set_cond_mark(), + Rd = reg_string(self.rd()) + ), + CMP | CMN | TEQ | TST => write!( + f, + "{opcode}{cond}\t{Rn}, ", + opcode = opcode, + cond = self.cond, + Rn = reg_string(self.rn()) ), - // {cond}{S} Rd,Rn, _ => write!( f, - "{opcode}{cond}\t{Rd}, {Rn}", + "{opcode}{S}{cond}\t{Rd}, {Rn}, ", opcode = opcode, cond = self.cond, - Rd = rd, + S = self.set_cond_mark(), + Rd = reg_string(self.rd()), Rn = reg_string(self.rn()) ), }?; let operand2 = self.operand2(); match operand2 { - ArmInstructionShiftValue::RotatedImmediate(_, _) => { - write!(f, ", #{:#x}", operand2.decode_rotated_immediate().unwrap()) + ArmShiftedValue::RotatedImmediate(_, _) => { + let value = operand2.decode_rotated_immediate().unwrap(); + write!(f, "#{}\t; {:#x}", value, value) } - ArmInstructionShiftValue::ShiftedRegister(reg, shift) => { - write!(f, ", {}", self.make_shifted_reg_string(reg, shift)) + ArmShiftedValue::ShiftedRegister{reg, shift, added: _} => { + write!(f, "{}", self.make_shifted_reg_string(reg, shift)) } _ => write!(f, "RegisterNotImpl"), } } - /// {cond}{B}{T} Rd,
+ fn auto_incremenet_mark(&self) -> &str { + if self.write_back_flag() { "!" } else { "" } + } + + fn fmt_rn_offset(&self, f: &mut fmt::Formatter, offset: ArmShiftedValue) -> fmt::Result { + write!(f, "[{Rn}", Rn = reg_string(self.rn()))?; + let (ofs_string, comment) = match offset { + ArmShiftedValue::ImmediateValue(value) => { + let value_for_commnet = if self.rn() == REG_PC { + value + (self.pc as i32) + 8 // account for pipelining + } else { + value + }; + ( + format!("#{}", value), + Some(format!("\t; {:#x}", value_for_commnet)), + ) + } + ArmShiftedValue::ShiftedRegister{reg, shift, added: Some(added)} => ( + format!("{}{}", if added { "" } else { "-" }, self.make_shifted_reg_string(reg, shift)), + None, + ), + _ => panic!("bad barrel shifter"), + }; + + if self.pre_index_flag() { + write!(f, ", {}]{}", ofs_string, self.auto_incremenet_mark())?; + } else { + write!(f, "], {}", ofs_string)?; + } + + if let Some(comment) = comment { + write!(f, "{}", comment) + } else { + Ok(()) + } + } + fn fmt_ldr_str(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "{mnem}{B}{cond}{T}\t{Rd}, [{Rn}", - mnem = if self.is_load() { "ldr" } else { "str" }, + "{mnem}{B}{cond}{T}\t{Rd}, ", + mnem = if self.load_flag() { "ldr" } else { "str" }, B = if self.transfer_size() == 1 { "b" } else { "" }, cond = self.cond, - T = if !self.is_pre_indexing() && self.is_write_back() { + T = if !self.pre_index_flag() && self.write_back_flag() { "t" } else { "" }, Rd = reg_string(self.rd()), - Rn = reg_string(self.rn()) )?; - let offset = self.offset(); - let auto_incremenet_mark = if self.is_write_back() { "!" } else { "" }; - let sign_mark = if self.is_ofs_added() { '+' } else { '-' }; - - let ofs_string = match offset { - ArmInstructionShiftValue::ImmediateValue(value) => format!("#{:+}", value), - ArmInstructionShiftValue::ShiftedRegister(reg, shift) => { - format!("{}{}", sign_mark, self.make_shifted_reg_string(reg, shift)) - } - _ => panic!("bad barrel shifter"), - }; - - if self.is_pre_indexing() { - write!(f, ", {}]{}", ofs_string, auto_incremenet_mark) - } else { - write!(f, "], {}", ofs_string) - } + self.fmt_rn_offset(f, self.ldr_str_offset()) } - /// {cond} Rn{!},{^} fn fmt_ldm_stm(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{mnem}{inc_dec}{pre_post}{cond}\t{Rn}{auto_inc}, {{", - mnem = if self.is_load() { "ldm" } else { "stm" }, - inc_dec = if self.is_ofs_added() { 'i' } else { 'd' }, - pre_post = if self.is_pre_indexing() { 'b' } else { 'a' }, + mnem = if self.load_flag() { "ldm" } else { "stm" }, + inc_dec = if self.add_offset_flag() { 'i' } else { 'd' }, + pre_post = if self.pre_index_flag() { 'b' } else { 'a' }, cond = self.cond, Rn = reg_string(self.rn()), - auto_inc = if self.is_write_back() { "!" } else { "" } + auto_inc = if self.write_back_flag() { "!" } else { "" } )?; let mut register_list = self.register_list().into_iter(); @@ -197,29 +237,98 @@ impl ArmInstruction { } /// MRS - transfer PSR contents to a register - /// MRS{cond} Rd, fn fmt_mrs(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "mrs{cond}\t{Rd}, {psr}", cond = self.cond, Rd = reg_string(self.rd()), - psr = if self.is_spsr() { "SPSR" } else { "CPSR" } + psr = if self.spsr_flag() { "SPSR" } else { "CPSR" } ) } /// MSR - transfer register contents to PSR - /// MSR{cond} ,Rm fn fmt_msr_reg(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "msr{cond}\t{psr}, {Rm}", cond = self.cond, - psr = if self.is_spsr() { "SPSR" } else { "CPSR" }, + psr = if self.spsr_flag() { "SPSR" } else { "CPSR" }, Rm = reg_string(self.rm()), - ) } + + fn fmt_mul_mla(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.accumulate_flag() { + write!( + f, + "mla{S}{cond}\t{Rd}, {Rm}, {Rs}, {Rn}", + S = self.set_cond_mark(), + cond = self.cond, + Rd = reg_string(self.rd()), + Rm = reg_string(self.rm()), + Rs = reg_string(self.rs()), + Rn = reg_string(self.rn()), + ) + } else { + write!( + f, + "mul{S}{cond}\t{Rd}, {Rm}, {Rs}", + S = self.set_cond_mark(), + cond = self.cond, + Rd = reg_string(self.rd()), + Rm = reg_string(self.rm()), + Rs = reg_string(self.rs()), + ) + } + } + + fn sign_mark(&self) -> &str { + if self.u_flag() { "s" } else { "u" } + } + + fn fmt_mull_mlal(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.accumulate_flag() { + write!( + f, + "{sign}mlal{S}{cond}\t{RdLo}, {RdHi}, {Rm}, {Rs}", + sign = self.sign_mark(), + S = self.set_cond_mark(), + cond = self.cond, + RdLo = reg_string(self.rd_lo()), + RdHi = reg_string(self.rd_hi()), + Rm = reg_string(self.rm()), + Rs = reg_string(self.rs()), + ) + } else { + write!( + f, + "{sign}mull{S}{cond}\t{RdLo}, {RdHi}, {Rm}", + sign = self.sign_mark(), + S = self.set_cond_mark(), + cond = self.cond, + RdLo = reg_string(self.rd_lo()), + RdHi = reg_string(self.rd_hi()), + Rm = reg_string(self.rm()) + ) + } + } + + fn fmt_ldr_str_hs(&self, f: &mut fmt::Formatter) -> fmt::Result{ + if let Ok(transfer_type) = self.halfword_data_transfer_type() { + write!( + f, + "{mnem}{type}{cond}\t{Rd}, ", + mnem = if self.load_flag() { "ldr" } else { "str" }, + cond = self.cond, + type = transfer_type, + Rd = reg_string(self.rd()), + )?; + self.fmt_rn_offset(f, self.ldr_str_hs_offset().unwrap()) + } else { + write!(f, "") + } + } } impl fmt::Display for ArmInstruction { @@ -233,6 +342,10 @@ impl fmt::Display for ArmInstruction { LDM_STM => self.fmt_ldm_stm(f), MRS => self.fmt_mrs(f), MSR_REG => self.fmt_msr_reg(f), + MUL_MLA => self.fmt_mul_mla(f), + MULL_MLAL => self.fmt_mull_mlal(f), + LDR_STR_HS_IMM => self.fmt_ldr_str_hs(f), + LDR_STR_HS_REG => self.fmt_ldr_str_hs(f), _ => write!(f, "({:?})", self), } } diff --git a/arm7tdmi/src/lib.rs b/arm7tdmi/src/lib.rs index da47a17..ac47331 100644 --- a/arm7tdmi/src/lib.rs +++ b/arm7tdmi/src/lib.rs @@ -6,6 +6,8 @@ extern crate bit; pub mod arm; +pub const REG_PC: usize = 15; + pub fn reg_string(reg: usize) -> &'static str { let reg_names = &[ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "fp", "ip", "sp", diff --git a/src/disassembler.rs b/src/disassembler.rs index 1444a75..dc92dae 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -55,7 +55,7 @@ fn main() { } }; let addr = (rdr.position() - 4) as u32; - print!("{:08x}: <{:08x}>\t", addr, value); + print!("{:8x}:\t{:08x} \t", addr, value); match ArmInstruction::try_from((value, addr)) { Ok(insn) => println!("{}", insn), Err(_) => println!("")