Created
June 22, 2020 12:33
-
-
Save exjam/aefbc69a877ff78878495dc1ee5875d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] | |
extern crate bitfield; | |
#[macro_use] extern crate bitpat; | |
use std::io::prelude::*; | |
use std::fs::File; | |
use std::convert::TryInto; | |
use std::u32; | |
#[macro_use] | |
mod macros; | |
struct MemoryRegion { | |
name: &'static str, | |
start: usize, | |
end: usize, | |
size: usize, | |
data: Vec<u8>, | |
} | |
impl MemoryRegion { | |
pub fn new(name: &'static str, address: usize, size: usize, data: Option<Vec<u8>>) -> MemoryRegion { | |
let mut data = data.unwrap_or(Vec::with_capacity(size)); | |
data.resize(size, 0); | |
MemoryRegion { | |
name: name, | |
start: address, | |
end: address + size, | |
size: size, | |
data: data | |
} | |
} | |
} | |
#[derive(Default)] | |
struct Memory { | |
regions: Vec<MemoryRegion>, | |
} | |
enum MemoryError | |
{ | |
InvalidAddress | |
} | |
impl Memory { | |
pub fn new() -> Memory { | |
Default::default() | |
} | |
pub fn add_region(&mut self, name: &'static str, address: usize, size: usize, data: Option<Vec<u8>>) { | |
self.regions.push(MemoryRegion::new(name, address, size, data)); | |
} | |
pub fn find_region(&self, address: usize) -> Result<&MemoryRegion, MemoryError> { | |
for region in &self.regions { | |
if address >= region.start && address < region.end { | |
return Ok(region) | |
} | |
} | |
return Err(MemoryError::InvalidAddress) | |
} | |
pub fn read_u32(&self, address: usize) -> Result<u32, MemoryError> { | |
let region = self.find_region(address)?; | |
let offset = address - region.start; | |
Ok(u32::from_be_bytes(region.data[offset .. offset+4].try_into().unwrap())) | |
} | |
} | |
mod armv5 | |
{ | |
use num_enum::TryFromPrimitive; | |
use std::convert::TryFrom; | |
#[derive(Debug,TryFromPrimitive)] | |
#[repr(u32)] | |
pub enum Condition { | |
Equal = 0b0000, | |
NotEqual = 0b0001, | |
UnsignedGreaterThanEqual = 0b0010, | |
UnsignedLessThan = 0b0011, | |
Negative = 0b0100, | |
Positive = 0b0101, | |
Overflow = 0b0110, | |
NoOverflow = 0b0111, | |
UnsignedGreaterThan = 0b1000, | |
UnsignedLessThanEqual = 0b1001, | |
SignedGreaterThanEqual = 0b1010, | |
SignedLessThan = 0b1011, | |
SignedGreaterThan = 0b1100, | |
SignedLessThanEqual = 0b1101, | |
Always = 0b1110, | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
pub struct Link(pub bool); | |
impl From<bool> for Link { | |
fn from(flag: bool) -> Self { | |
Link(flag) | |
} | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
pub struct SetFlags(pub bool); | |
impl From<bool> for SetFlags { | |
fn from(flag: bool) -> Self { | |
SetFlags(flag) | |
} | |
} | |
#[derive(Debug)] | |
pub enum ShifterOperand { | |
Immediate { | |
rotate_imm: u8, | |
immed_8: u8, | |
}, | |
ImmediateShift { | |
shift_imm: u8, | |
shift: u8, | |
rm: u8, | |
}, | |
RegisterShift { | |
rs: u8, | |
shift: u8, | |
rm: u8, | |
} | |
} | |
#[derive(Debug)] | |
pub enum Instruction { | |
ADC { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
ADD { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
AND { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
B { cond: Condition, link: bool, signed_immed_24: i32 }, | |
BIC { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
CMN { cond: Condition, rn: u8, shifter_operand: ShifterOperand }, | |
CMP { cond: Condition, rn: u8, shifter_operand: ShifterOperand }, | |
EOR { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
MOV { cond: Condition, s: bool, rd: u8, shifter_operand: ShifterOperand }, | |
MCR { cond: Condition, opcode_1: u8, crn: u8, rd: u8, cp_num: u8, opcode_2: u8, crm: u8 }, | |
MCR2 { opcode_1: u8, crn: u8, rd: u8, cp_num: u8, opcode_2: u8, crm: u8 }, | |
MVN { cond: Condition, s: bool, rd: u8, shifter_operand: ShifterOperand }, | |
ORR { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
RSB { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
RSC { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
SBC { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
SUB { cond: Condition, s: bool, rn: u8, rd: u8, shifter_operand: ShifterOperand }, | |
TEQ { cond: Condition, rn: u8, shifter_operand: ShifterOperand }, | |
TST { cond: Condition, rn: u8, shifter_operand: ShifterOperand }, | |
} | |
pub enum DecoderError | |
{ | |
InvalidInstruction, | |
} | |
impl From<num_enum::TryFromPrimitiveError<Condition>> for DecoderError { | |
fn from(_: num_enum::TryFromPrimitiveError<Condition>) -> Self { | |
DecoderError::InvalidInstruction | |
} | |
} | |
bitfield!{ | |
pub struct CPSR(u32); | |
impl Debug; | |
pub get_thumb_mode, set_thumb_mode: 5; | |
} | |
pub struct State { | |
pub cpsr: CPSR | |
} | |
pub fn decode(state: &State, word: u32) -> Result<Instruction, DecoderError> { | |
if state.cpsr.get_thumb_mode() { | |
decode_thumb(word) | |
} else { | |
decode_armv5(word) | |
} | |
} | |
pub fn decode_armv5(word: u32) -> Result<Instruction, DecoderError> { | |
if bits!(word, 31, 28) == 0b1111 { | |
decode_armv5_unconditional(word) | |
} else { | |
match bits!(word, 27, 25) { | |
0b000 => { | |
if !bit!(word, 4) { | |
if bits!(word, 24, 23) == 0b10 && !bit!(word, 20) { | |
decode_armv5_miscellaneous(word) | |
} else { | |
decode_armv5_data_processing(word) | |
} | |
} else if !bit!(word, 7) { | |
if bits!(word, 24, 23) == 0b10 && !bit!(word, 20) { | |
decode_armv5_miscellaneous(word) | |
} else { | |
decode_armv5_data_processing(word) | |
} | |
} else { | |
decode_armv5_load_store_multiply_extension(word) | |
} | |
}, | |
0b001 => { | |
if bits!(word, 24, 23) == 0b10 && !bit!(word, 20) { | |
if bit!(word, 21) { | |
Err(DecoderError::InvalidInstruction) | |
} else { | |
decode_armv5_move_immediate_status_register(word) | |
} | |
} else { | |
decode_armv5_data_processing(word) | |
} | |
}, | |
0b010 => decode_armv5_load_store_immediate(word), | |
0b011 if !bit!(word, 4) => decode_armv5_load_store_register(word), | |
0b011 if bit!(word, 4) => decode_armv5_media(word), | |
0b011 if bits!(word, 24, 21) == 0b11111 && bits!(word, 7, 4) == 0b1111 => Err(DecoderError::InvalidInstruction), | |
0b100 => decode_armv5_load_store_multiple(word), | |
0b101 => decode_armv5_branch(word), | |
0b110 => decode_armv5_coprocessor_load_store(word), | |
0b111 if !bit!(word, 24) && !bit!(word, 4) => decode_armv5_coprocessor_data_processing(word), | |
0b111 if !bit!(word, 24) && bit!(word, 4) => decode_armv5_coprocessor_register_transfers(word), | |
0b111 if bit!(word, 24) => decode_armv5_software_interrupt(word), | |
_ => Err(DecoderError::InvalidInstruction), | |
} | |
} | |
} | |
pub fn decode_armv5_branch(word: u32) -> Result<Instruction, DecoderError> { | |
Ok(Instruction::B { | |
cond: Condition::try_from(bits!(word, 31, 28)) ?, | |
link: bit!(word, 24), | |
signed_immed_24: ((bits!(word, 23, 0) as i32) << 8) >> 6, | |
}) | |
} | |
pub fn decode_armv5_coprocessor_data_processing(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_coprocessor_load_store(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_coprocessor_register_transfers(word: u32) -> Result<Instruction, DecoderError> { | |
if !bit!(word, 20) { | |
Ok(Instruction::MCR { | |
cond: Condition::try_from(bits!(word, 31, 28)) ?, | |
opcode_1: bits!(word, 23, 21) as u8, | |
crn: bits!(word, 19, 16) as u8, | |
rd: bits!(word, 15, 12) as u8, | |
cp_num: bits!(word, 11, 8) as u8, | |
opcode_2: bits!(word, 7, 5) as u8, | |
crm: bits!(word, 3, 0) as u8, | |
}) | |
} else { | |
// LDC | |
Err(DecoderError::InvalidInstruction) | |
} | |
} | |
pub fn decode_armv5_data_processing(word: u32) -> Result<Instruction, DecoderError> { | |
let cond = Condition::try_from(bits!(word, 31, 28)) ?; | |
let opcode = bits!(word, 24, 21); | |
let s = bit!(word, 20); | |
let rn = bits!(word, 19, 16) as u8; | |
let rd = bits!(word, 15, 12) as u8; | |
let shifter_operand = if bit!(word, 25) { | |
ShifterOperand::Immediate { | |
rotate_imm: bits!(word, 11, 8) as u8, | |
immed_8: bits!(word, 7, 0) as u8, | |
} | |
} else if bit!(word, 4) { | |
ShifterOperand::RegisterShift { | |
rs: bits!(word, 11, 8) as u8, | |
shift: bits!(word, 6, 5) as u8, | |
rm: bits!(word, 3, 0) as u8, | |
} | |
} else { | |
ShifterOperand::ImmediateShift { | |
shift_imm: bits!(word, 11, 7) as u8, | |
shift: bits!(word, 6, 5) as u8, | |
rm: bits!(word, 3, 0) as u8, | |
} | |
}; | |
match opcode { | |
0b0000 => Ok(Instruction::AND { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0001 => Ok(Instruction::EOR { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0010 => Ok(Instruction::SUB { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0011 => Ok(Instruction::RSB { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0100 => Ok(Instruction::ADD { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0101 => Ok(Instruction::ADC { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0110 => Ok(Instruction::SBC { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b0111 => Ok(Instruction::RSC { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b1000 => Ok(Instruction::TST { cond: cond, rn: rn, shifter_operand: shifter_operand }), | |
0b1001 => Ok(Instruction::TEQ { cond: cond, rn: rn, shifter_operand: shifter_operand }), | |
0b1010 => Ok(Instruction::CMP { cond: cond, rn: rn, shifter_operand: shifter_operand }), | |
0b1011 => Ok(Instruction::CMN { cond: cond, rn: rn, shifter_operand: shifter_operand }), | |
0b1100 => Ok(Instruction::ORR { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b1101 => Ok(Instruction::MOV { cond: cond, s: s, rd: rd, shifter_operand: shifter_operand }), | |
0b1110 => Ok(Instruction::BIC { cond: cond, s: s, rd: rd, rn: rn, shifter_operand: shifter_operand }), | |
0b1111 => Ok(Instruction::MVN { cond: cond, s: s, rd: rd, shifter_operand: shifter_operand }), | |
_ => Err(DecoderError::InvalidInstruction) | |
} | |
} | |
pub fn decode_armv5_load_store_immediate(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_load_store_register(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_load_store_multiple(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_load_store_multiply_extension(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_media(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_miscellaneous(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_move_immediate_status_register(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_software_interrupt(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_armv5_unconditional(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
pub fn decode_thumb(word: u32) -> Result<Instruction, DecoderError> { | |
Err(DecoderError::InvalidInstruction) | |
} | |
} | |
fn main() -> std::io::Result<()> { | |
let mut f = File::open("boot0.bin")?; | |
let mut boot0 = Vec::new(); | |
f.read_to_end(&mut boot0)?; | |
let mut f = File::open("boot1.bin")?; | |
let mut boot1 = Vec::new(); | |
f.read_to_end(&mut boot1)?; | |
let mut memory = Memory::new(); | |
memory.add_region("SRAM0", 0x0D410000, 0x10000, None); | |
memory.add_region("SRAM1", 0x0D400000, 0x10000, None); | |
memory.add_region("BOOT0_ROM", 0xFFFF0000, 0x4000, Some(boot0)); | |
memory.add_region("BOOT1_ROM", 0xFFF00000, 0xF800, Some(boot1)); | |
let insn = memory.read_u32(0xFFFF00A4).unwrap_or(0); | |
println!("First instruction: {:08X}", insn); | |
let mut state = armv5::State { | |
cpsr: armv5::CPSR(0) | |
}; | |
let decoded = armv5::decode(&state, insn); | |
println!("{:#?}", decoded.ok().unwrap()); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment