diff --git a/Cargo.lock b/Cargo.lock index 6e58fa0..5a83e04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,99 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "arm7tdmi" +version = "0.1.0" +dependencies = [ + "bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "enum-primitive-derive" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustboyadvance" version = "0.1.0" +dependencies = [ + "arm7tdmi 0.1.0", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" +"checksum bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b645c5c09a7d4035949cfce1a915785aaad6f17800c35fda8a8c311c491f284" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum enum-primitive-derive 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b90e520ec62c1864c8c78d637acbfe8baf5f63240f2fb8165b8325c07812dd" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" diff --git a/Cargo.toml b/Cargo.toml index b9ec50b..42b7b08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,3 +5,9 @@ authors = ["Michel Heily "] edition = "2018" [dependencies] +byteorder = "*" +arm7tdmi = {path = "arm7tdmi"} + +[[bin]] +name = "disassembler" +path = "src/disassembler.rs" \ No newline at end of file diff --git a/arm7tdmi/Cargo.toml b/arm7tdmi/Cargo.toml index 6168832..bb55de7 100644 --- a/arm7tdmi/Cargo.toml +++ b/arm7tdmi/Cargo.toml @@ -5,3 +5,6 @@ authors = ["Michel Heily "] edition = "2018" [dependencies] +enum-primitive-derive = "^0.1" +num-traits = "^0.1" +bit = "^0.1" \ No newline at end of file diff --git a/arm7tdmi/src/arm/arm_isa.rs b/arm7tdmi/src/arm/arm_isa.rs new file mode 100644 index 0000000..d2eda03 --- /dev/null +++ b/arm7tdmi/src/arm/arm_isa.rs @@ -0,0 +1,298 @@ +use crate::bit::BitIndex; +use crate::num_traits::FromPrimitive; +use std::convert::TryFrom; + +pub use super::display; + +#[derive(Debug, PartialEq)] +pub enum ArmError { + UnknownInstructionFormat(u32), + UndefinedConditionCode(u32), + InvalidShiftType(u32), +} + +#[derive(Debug, Copy, Clone, PartialEq, Primitive)] +pub enum ArmCond { + Equal = 0b0000, + NotEqual = 0b0001, + UnsignedHigherOrSame = 0b0010, + UnsignedLower = 0b0011, + Negative = 0b0100, + PositiveOrZero = 0b0101, + Overflow = 0b0110, + NoOverflow = 0b0111, + UnsignedHigher = 0b1000, + UnsignedLowerOrSame = 0b1001, + GreaterOrEqual = 0b1010, + LessThan = 0b1011, + GreaterThan = 0b1100, + LessThanOrEqual = 0b1101, + Always = 0b1110, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +#[allow(non_camel_case_types)] +pub enum ArmInstructionFormat { + // Branch and Exchange + BX, + // Branch /w Link + B_BL, + // Multiply and Multiply-Accumulate + MUL_MLA, + // Multiply Long and Multiply-Accumulate Long + MULL_MLAL, + // Single Data Transfer + LDR_STR, + // Halfword and Signed Data Transfer + LDR_STR_HS_REG, + // Halfword and Signed Data Transfer + LDR_STR_HS_IMM, + // Data Processing + DP, + // Block Data Transfer + LDM_STM, + // Single Data Swap + SWP, + // Transfer PSR contents to a register + MRS, + // Transfer register contents to PSR + MSR_REG, + // Tanssfer immediate/register to PSR flags only + MSR_FLAGS, +} + +#[derive(Debug, Primitive)] +pub enum ArmOpCode { + AND = 0b0000, + EOR = 0b0001, + SUB = 0b0010, + RSB = 0b0011, + ADD = 0b0100, + ADC = 0b0101, + SBC = 0b0110, + RSC = 0b0111, + TST = 0b1000, + TEQ = 0b1001, + CMP = 0b1010, + CMN = 0b1011, + ORR = 0b1100, + MOV = 0b1101, + BIC = 0b1110, + MVN = 0b1111, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct ArmInstruction { + pub cond: ArmCond, + pub fmt: ArmInstructionFormat, + pub raw: u32, + pub pc: u32, +} + +impl TryFrom<(u32, u32)> for ArmInstruction { + type Error = ArmError; + + fn try_from(value: (u32, u32)) -> Result { + use ArmInstructionFormat::*; + let (raw, addr) = value; + + let cond_code = raw.bit_range(28..32) as u8; + let cond = match ArmCond::from_u8(cond_code) { + Some(cond) => Ok(cond), + None => Err(ArmError::UndefinedConditionCode(cond_code as u32)), + }?; + + let fmt = if (0x0fff_fff0 & raw) == 0x012f_ff10 { + Ok(BX) + } else if (0x0e00_0000 & raw) == 0x0a00_0000 { + Ok(B_BL) + } else if (0x0fc0_00f0 & raw) == 0x0000_0090 { + Ok(MUL_MLA) + } else if (0x0f80_00f0 & raw) == 0x0080_0090 { + Ok(MULL_MLAL) + } else if (0x0c00_0000 & raw) == 0x0400_0000 { + Ok(LDR_STR) + } else if (0x0e40_0F90 & raw) == 0x0000_0090 { + Ok(LDR_STR_HS_REG) + } else if (0x0e40_0090 & raw) == 0x0040_0090 { + 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 { + Err(ArmError::UnknownInstructionFormat(raw)) + }?; + + Ok(ArmInstruction { + cond: cond, + fmt: fmt, + raw: raw, + pc: addr, + }) + } +} + +#[derive(Debug, PartialEq, Primitive)] +pub enum ArmShiftType { + LSL = 0, + LSR = 1, + ASR = 2, + ROR = 3, +} + +#[derive(Debug, PartialEq)] +pub enum ArmShift { + ImmediateShift(u32, ArmShiftType), + RegisterShift(usize, ArmShiftType), +} + +impl TryFrom for ArmShift { + type Error = ArmError; + + fn try_from(v: u32) -> Result { + let typ = match ArmShiftType::from_u8(v.bit_range(5..7) as u8) { + Some(s) => Ok(s), + _ => Err(ArmError::InvalidShiftType(v.bit_range(5..7))), + }?; + if v.bit(4) { + let rs = v.bit_range(8..12) as usize; + Ok(ArmShift::RegisterShift(rs, typ)) + } else { + let amount = v.bit_range(7..12) as u32; + Ok(ArmShift::ImmediateShift(amount, typ)) + } + } +} + +#[derive(Debug, PartialEq)] +pub enum ArmInstructionShiftValue { + ImmediateValue(u32), + RotatedImmediate(u32, u32), + ShiftedRegister(usize, ArmShift), +} + +impl ArmInstructionShiftValue { + /// Decode operand2 as an immediate value + pub fn decode_rotated_immediate(&self) -> Option { + if let ArmInstructionShiftValue::RotatedImmediate(immediate, rotate) = self { + return Some(immediate.rotate_right(*rotate) as i32); + } + None + } +} + +impl ArmInstruction { + pub fn rn(&self) -> usize { + match self.fmt { + ArmInstructionFormat::MUL_MLA => self.raw.bit_range(12..16) as usize, + ArmInstructionFormat::MULL_MLAL => self.raw.bit_range(8..12) as usize, + ArmInstructionFormat::BX => self.raw.bit_range(0..4) as usize, + _ => self.raw.bit_range(16..20) as usize, + } + } + + pub fn rd(&self) -> usize { + match self.fmt { + ArmInstructionFormat::MUL_MLA => self.raw.bit_range(16..20) as usize, + _ => self.raw.bit_range(12..16) as usize, + } + } + + pub fn rm(&self) -> usize { + self.raw.bit_range(0..4) as usize + } + + pub fn opcode(&self) -> Option { + ArmOpCode::from_u32(self.raw.bit_range(21..25)) + } + + pub fn branch_offset(&self) -> i32 { + ((((self.raw << 8) as i32) >> 8) << 2) + 8 + } + + pub fn is_load(&self) -> bool { + self.raw.bit(20) + } + + pub fn is_set_cond(&self) -> bool { + self.raw.bit(20) + } + + pub fn is_write_back(&self) -> bool { + self.raw.bit(21) + } + + pub fn transfer_size(&self) -> usize { + if self.raw.bit(22) { + 1 + } else { + 4 + } + } + + pub fn is_loading_psr_and_forcing_user_mode(&self) -> bool { + self.raw.bit(22) + } + + pub fn is_spsr(&self) -> bool { + self.raw.bit(22) + } + + pub fn is_ofs_added(&self) -> bool { + self.raw.bit(23) + } + + pub fn is_pre_indexing(&self) -> bool { + self.raw.bit(24) + } + + pub fn is_linked_branch(&self) -> bool { + self.raw.bit(24) + } + + pub fn offset(&self) -> ArmInstructionShiftValue { + 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) + } else { + ArmInstructionShiftValue::ImmediateValue(ofs) + } + } + + pub fn operand2(&self) -> ArmInstructionShiftValue { + 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) + } else { + let reg = op2 & 0xf; + let shift = ArmShift::try_from(op2).unwrap(); // TODO error handling + ArmInstructionShiftValue::ShiftedRegister(reg as usize, shift) + } + } + + pub fn register_list(&self) -> Vec { + let list_bits = self.raw & 0xffff; + let mut list = Vec::with_capacity(16); + for i in 0..16 { + if (list_bits & (1 << i)) != 0 { + list.push(i) + } + } + list + } +} diff --git a/arm7tdmi/src/arm/display.rs b/arm7tdmi/src/arm/display.rs new file mode 100644 index 0000000..c451099 --- /dev/null +++ b/arm7tdmi/src/arm/display.rs @@ -0,0 +1,239 @@ +use super::super::reg_string; +use super::arm_isa::{ + ArmCond, ArmInstruction, ArmInstructionFormat, ArmInstructionShiftValue, ArmOpCode, ArmShift, + ArmShiftType, +}; +use std::fmt; + +impl fmt::Display for ArmCond { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ArmCond::*; + match self { + Equal => write!(f, "eq"), + NotEqual => write!(f, "ne"), + UnsignedHigherOrSame => write!(f, "cs"), + UnsignedLower => write!(f, "cc"), + Negative => write!(f, "mi"), + PositiveOrZero => write!(f, "pl"), + Overflow => write!(f, "vs"), + NoOverflow => write!(f, "vc"), + UnsignedHigher => write!(f, "hi"), + UnsignedLowerOrSame => write!(f, "ls"), + GreaterOrEqual => write!(f, "ge"), + LessThan => write!(f, "lt"), + GreaterThan => write!(f, "gt"), + LessThanOrEqual => write!(f, "le"), + Always => write!(f, ""), // the dissasembly should ignore this + } + } +} + +impl fmt::Display for ArmOpCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ArmOpCode::*; + match self { + AND => write!(f, "and"), + EOR => write!(f, "eor"), + SUB => write!(f, "sub"), + RSB => write!(f, "rsb"), + ADD => write!(f, "add"), + ADC => write!(f, "adc"), + SBC => write!(f, "sbc"), + RSC => write!(f, "rsc"), + TST => write!(f, "tst"), + TEQ => write!(f, "teq"), + CMP => write!(f, "cmp"), + CMN => write!(f, "cmn"), + ORR => write!(f, "orr"), + MOV => write!(f, "mov"), + BIC => write!(f, "bic"), + MVN => write!(f, "mvn"), + } + } +} + +impl fmt::Display for ArmShiftType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ArmShiftType::*; + match self { + LSL => write!(f, "lsl"), + LSR => write!(f, "lsr"), + ASR => write!(f, "asr"), + ROR => write!(f, "ror"), + } + } +} + +fn is_shift(shift: &ArmShift) -> bool { + if let ArmShift::ImmediateShift(val, typ) = shift { + return !(*val == 0 && *typ == ArmShiftType::LSL); + } + true +} + +impl ArmInstruction { + fn make_shifted_reg_string(&self, reg: usize, shift: ArmShift) -> String { + let reg = reg_string(reg).to_string(); + if !is_shift(&shift) { + return reg; + } + + match shift { + ArmShift::ImmediateShift(imm, typ) => format!("{}, {} #{}", reg, typ, imm), + ArmShift::RegisterShift(rs, typ) => format!("{}, {} {}", reg, typ, reg_string(rs)), + } + } + + fn fmt_bx(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "bx\t{Rn}", Rn = reg_string(self.rn())) + } + + fn fmt_branch(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "b{link}{cond}\t{ofs:#x}", + link = if self.is_linked_branch() { "l" } else { "" }, + cond = self.cond, + ofs = self.pc.wrapping_add(self.branch_offset() as u32) as u32 + ) + } + + 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 = opcode, + cond = self.cond, + S = if self.is_set_cond() { "s" } else { "" }, + Rd = rd + ), + // {cond}{S} Rd,Rn, + _ => write!( + f, + "{opcode}{cond}\t{Rd}, {Rn}", + opcode = opcode, + cond = self.cond, + Rd = rd, + Rn = reg_string(self.rn()) + ), + }?; + + let operand2 = self.operand2(); + match operand2 { + ArmInstructionShiftValue::RotatedImmediate(_, _) => { + write!(f, ", #{:#x}", operand2.decode_rotated_immediate().unwrap()) + } + ArmInstructionShiftValue::ShiftedRegister(reg, shift) => { + write!(f, ", {}", self.make_shifted_reg_string(reg, shift)) + } + _ => write!(f, "RegisterNotImpl"), + } + } + + /// {cond}{B}{T} Rd,
+ 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" }, + B = if self.transfer_size() == 1 { "b" } else { "" }, + cond = self.cond, + T = if !self.is_pre_indexing() && self.is_write_back() { + "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) + } + } + + /// {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' }, + cond = self.cond, + Rn = reg_string(self.rn()), + auto_inc = if self.is_write_back() { "!" } else { "" } + )?; + + let mut register_list = self.register_list().into_iter(); + if let Some(reg) = register_list.next() { + write!(f, "{}", reg_string(reg))?; + } + for reg in register_list { + write!(f, ", {}", reg_string(reg))?; + } + write!(f, "}}") + } + + /// 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" } + ) + } + + /// 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" }, + Rm = reg_string(self.rm()), + + ) + } +} + +impl fmt::Display for ArmInstruction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use ArmInstructionFormat::*; + match self.fmt { + BX => self.fmt_bx(f), + B_BL => self.fmt_branch(f), + DP => self.fmt_data_processing(f), + LDR_STR => self.fmt_ldr_str(f), + LDM_STM => self.fmt_ldm_stm(f), + MRS => self.fmt_mrs(f), + MSR_REG => self.fmt_msr_reg(f), + _ => write!(f, "({:?})", self), + } + } +} diff --git a/arm7tdmi/src/arm/mod.rs b/arm7tdmi/src/arm/mod.rs new file mode 100644 index 0000000..de577ea --- /dev/null +++ b/arm7tdmi/src/arm/mod.rs @@ -0,0 +1,2 @@ +pub mod arm_isa; +pub mod display; diff --git a/arm7tdmi/src/lib.rs b/arm7tdmi/src/lib.rs index 31e1bb2..da47a17 100644 --- a/arm7tdmi/src/lib.rs +++ b/arm7tdmi/src/lib.rs @@ -1,3 +1,19 @@ +#[macro_use] +extern crate enum_primitive_derive; +extern crate num_traits; + +extern crate bit; + +pub mod arm; + +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", + "lr", "pc", + ]; + reg_names[reg] +} + #[cfg(test)] mod tests { #[test] diff --git a/src/disassembler.rs b/src/disassembler.rs new file mode 100644 index 0000000..1444a75 --- /dev/null +++ b/src/disassembler.rs @@ -0,0 +1,64 @@ +use std::env; +use std::io; +use std::io::Cursor; +use std::io::prelude::*; +use std::fs::File; +use std::convert::TryFrom; + +extern crate byteorder; +use byteorder::{LittleEndian, ReadBytesExt}; + +extern crate arm7tdmi; + +use arm7tdmi::arm::arm_isa::ArmInstruction; + +#[derive(Debug)] +pub enum DisassemblerError { + IO(io::Error), +} + +impl From for DisassemblerError { + fn from(err: io::Error) -> DisassemblerError { + DisassemblerError::IO(err) + } +} + +fn read_file(filename: &str) -> Result, DisassemblerError> { + let mut buf = Vec::new(); + let mut file = File::open(filename)?; + file.read_to_end(&mut buf)?; + Ok(buf) +} + +fn main() { + let filename = match env::args().nth(1) { + Some(filename) => filename, + None => panic!("usage: {} ", env::args().nth(0).unwrap()) + }; + + // let num_instructions = match env::args().nth(2) { + // Some(n) => n, + // None => panic!("usage: {} ", env::args().nth(0).unwrap()) + // }.parse::(); + + let buf = match read_file(&filename) { + Ok(buf) => buf, + Err(e) => panic!(e) + }; + + let mut rdr = Cursor::new(buf); + loop { + let value: u32 = match rdr.read_u32::() { + Ok(v) => v, + Err(err) => { + panic!("got an error {:?}", err); + } + }; + let addr = (rdr.position() - 4) as u32; + print!("{:08x}: <{:08x}>\t", addr, value); + match ArmInstruction::try_from((value, addr)) { + Ok(insn) => println!("{}", insn), + Err(_) => println!("") + } + } +}