Start arm disassembler
This commit is contained in:
parent
f18ec05c17
commit
377f350e12
93
Cargo.lock
generated
93
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
@ -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
298
arm7tdmi/src/arm/arm_isa.rs
Normal 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
239
arm7tdmi/src/arm/display.rs
Normal 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
2
arm7tdmi/src/arm/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod arm_isa;
|
||||||
|
pub mod display;
|
|
@ -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
64
src/disassembler.rs
Normal 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>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue