Start arm disassembler

This commit is contained in:
Michel Heily 2019-06-23 02:57:14 +03:00
parent f18ec05c17
commit 377f350e12
8 changed files with 721 additions and 0 deletions

93
Cargo.lock generated
View file

@ -1,6 +1,99 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # 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]] [[package]]
name = "rustboyadvance" name = "rustboyadvance"
version = "0.1.0" 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"

View file

@ -5,3 +5,9 @@ authors = ["Michel Heily <michelheily@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
byteorder = "*"
arm7tdmi = {path = "arm7tdmi"}
[[bin]]
name = "disassembler"
path = "src/disassembler.rs"

View file

@ -5,3 +5,6 @@ authors = ["Michel Heily <michelheily@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
enum-primitive-derive = "^0.1"
num-traits = "^0.1"
bit = "^0.1"

298
arm7tdmi/src/arm/arm_isa.rs Normal file
View file

@ -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<Self, Self::Error> {
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<u32> for ArmShift {
type Error = ArmError;
fn try_from(v: u32) -> Result<Self, Self::Error> {
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<i32> {
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> {
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<usize> {
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
}
}

239
arm7tdmi/src/arm/display.rs Normal file
View file

@ -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 {
// <opcode>{cond}{S} Rd,<Op2>
MOV | MVN => write!(
f,
"{opcode}{cond}{S}\t{Rd}",
opcode = opcode,
cond = self.cond,
S = if self.is_set_cond() { "s" } else { "" },
Rd = rd
),
// <opcode>{cond}{S} Rd,Rn,<Op2>
_ => 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"),
}
}
/// <LDR|STR>{cond}{B}{T} Rd,<Address>
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)
}
}
/// <LDM|STM>{cond}<FD|ED|FA|EA|IA|IB|DA|DB> Rn{!},<Rlist>{^}
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,<psr>
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} <psr>,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),
}
}
}

2
arm7tdmi/src/arm/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod arm_isa;
pub mod display;

View file

@ -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)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]

64
src/disassembler.rs Normal file
View file

@ -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<io::Error> for DisassemblerError {
fn from(err: io::Error) -> DisassemblerError {
DisassemblerError::IO(err)
}
}
fn read_file(filename: &str) -> Result<Vec<u8>, 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: {} <file> <n>", env::args().nth(0).unwrap())
};
// let num_instructions = match env::args().nth(2) {
// Some(n) => n,
// None => panic!("usage: {} <file> <n>", env::args().nth(0).unwrap())
// }.parse::<usize>();
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::<LittleEndian>() {
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!("<UNDEFINED>")
}
}
}