Mega Commit #2 - Add some thumb decoding and disassembly

Former-commit-id: e3a89ac681a8d6f6f0bee85b32f64d181e11242f
This commit is contained in:
Michel Heily 2019-07-02 16:57:35 +03:00
parent cbddeffd91
commit 6f81c236a6
17 changed files with 792 additions and 209 deletions

View file

@ -1,8 +1,8 @@
use std::fmt;
use super::{
ArmCond, ArmHalfwordTransferType, ArmInstruction, ArmFormat, ArmOpCode,
ArmRegisterShift, ArmShiftType, ArmShiftedValue,
ArmCond, ArmFormat, ArmHalfwordTransferType, ArmInstruction, ArmOpCode, ArmRegisterShift,
ArmShiftType, ArmShiftedValue,
};
use crate::arm7tdmi::{reg_string, Addr, REG_PC};

View file

@ -3,14 +3,13 @@ use crate::bit::BitIndex;
use crate::arm7tdmi::bus::{Bus, MemoryAccessType::*, MemoryAccessWidth::*};
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
use crate::arm7tdmi::exception::Exception;
use crate::arm7tdmi::{Addr, CpuError, CpuInstruction, CpuResult, CpuState, REG_PC};
use crate::arm7tdmi::psr::RegPSR;
use crate::arm7tdmi::{Addr, CpuError, CpuResult, CpuState, DecodedInstruction, REG_PC};
use crate::sysbus::SysBus;
use super::{
ArmCond, ArmInstruction, ArmFormat, ArmOpCode, ArmRegisterShift, ArmShiftType,
ArmShiftedValue,
ArmFormat, ArmInstruction, ArmOpCode, ArmRegisterShift, ArmShiftType, ArmShiftedValue,
};
impl Core {
@ -31,7 +30,11 @@ impl Core {
ArmFormat::SWI => self.exec_swi(sysbus, insn),
ArmFormat::LDR_STR => self.exec_ldr_str(sysbus, insn),
ArmFormat::MSR_REG => self.exec_msr_reg(sysbus, insn),
_ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn))),
_ => Err(CpuError::UnimplementedCpuInstruction(
insn.pc,
insn.raw,
DecodedInstruction::Arm(insn),
)),
}
}

View file

@ -1,11 +1,14 @@
pub mod display;
pub mod exec;
use crate::arm7tdmi::Addr;
use crate::arm7tdmi::{Addr, InstructionDecoder, InstructionDecoderError};
use crate::bit::BitIndex;
use crate::num_traits::FromPrimitive;
use crate::byteorder::{LittleEndian, ReadBytesExt};
use crate::num::FromPrimitive;
use std::convert::TryFrom;
use std::io;
#[derive(Debug, PartialEq)]
pub enum ArmDecodeErrorKind {
@ -14,6 +17,7 @@ pub enum ArmDecodeErrorKind {
UndefinedConditionCode(u32),
InvalidShiftType(u32),
InvalidHSBits(u32),
IoError(io::ErrorKind),
}
use ArmDecodeErrorKind::*;
@ -130,13 +134,11 @@ pub struct ArmInstruction {
pub pc: Addr,
}
impl TryFrom<(u32, Addr)> for ArmInstruction {
type Error = ArmDecodeError;
impl InstructionDecoder for ArmInstruction {
type IntType = u32;
fn try_from(value: (u32, Addr)) -> Result<Self, Self::Error> {
fn decode(raw: u32, addr: Addr) -> Result<Self, InstructionDecoderError> {
use ArmFormat::*;
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),
@ -188,13 +190,17 @@ impl TryFrom<(u32, Addr)> for ArmInstruction {
pc: addr,
})
}
fn decode_from_bytes(bytes: &[u8], addr: Addr) -> Result<Self, InstructionDecoderError> {
let mut rdr = std::io::Cursor::new(bytes);
let raw = rdr
.read_u32::<LittleEndian>()
.map_err(|e| InstructionDecoderError::IoError(e.kind()))?;
Self::decode(raw, addr)
}
impl TryFrom<u32> for ArmInstruction {
type Error = ArmDecodeError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
ArmInstruction::try_from((value, 0))
fn get_raw(&self) -> u32 {
self.raw
}
}
@ -440,7 +446,7 @@ mod tests {
#[test]
fn test_decode_swi() {
// swi #0x1337
let decoded = ArmInstruction::try_from(0xef001337).unwrap();
let decoded = ArmInstruction::decode(0xef001337, 0).unwrap();
assert_eq!(decoded.fmt, ArmFormat::SWI);
assert_eq!(decoded.swi_comment(), 0x1337);
assert_eq!(format!("{}", decoded), "swi\t#0x1337");
@ -449,7 +455,7 @@ mod tests {
#[test]
fn test_decode_branch_forwards() {
// 0x20: b 0x30
let decoded = ArmInstruction::try_from((0xea_00_00_02, 0x20)).unwrap();
let decoded = ArmInstruction::decode(0xea_00_00_02, 0x20).unwrap();
assert_eq!(decoded.fmt, ArmFormat::B_BL);
assert_eq!(decoded.link_flag(), false);
assert_eq!(
@ -462,7 +468,7 @@ mod tests {
#[test]
fn test_decode_branch_link_backwards() {
// 0x20: bl 0x10
let decoded = ArmInstruction::try_from((0xeb_ff_ff_fa, 0x20)).unwrap();
let decoded = ArmInstruction::decode(0xeb_ff_ff_fa, 0x20).unwrap();
assert_eq!(decoded.fmt, ArmFormat::B_BL);
assert_eq!(decoded.link_flag(), true);
assert_eq!(
@ -473,9 +479,9 @@ mod tests {
}
#[test]
fn test_decode_ldr_preindex() {
fn test_decode_ldr_pre_index() {
// ldreq r2, [r5, -r6, lsl #5]
let decoded = ArmInstruction::try_from(0x07_15_22_86).unwrap();
let decoded = ArmInstruction::decode(0x07_15_22_86, 0).unwrap();
assert_eq!(decoded.fmt, ArmFormat::LDR_STR);
assert_eq!(decoded.cond, ArmCond::Equal);
assert_eq!(decoded.load_flag(), true);
@ -496,9 +502,9 @@ mod tests {
}
#[test]
fn test_decode_str_postindex() {
fn test_decode_str_post_index() {
// strteq r2, [r4], -r7, lsl #8
let decoded = ArmInstruction::try_from(0x06_24_24_47).unwrap();
let decoded = ArmInstruction::decode(0x06_24_24_47, 0).unwrap();
assert_eq!(decoded.fmt, ArmFormat::LDR_STR);
assert_eq!(decoded.cond, ArmCond::Equal);
assert_eq!(decoded.load_flag(), false);

View file

@ -27,35 +27,33 @@ pub struct MemoryAccess(pub MemoryAccessType, pub MemoryAccessWidth);
pub trait Bus {
fn read_32(&self, addr: Addr) -> u32 {
self.get_bytes(addr, 4).read_u32::<LittleEndian>().unwrap()
self.get_bytes(addr).read_u32::<LittleEndian>().unwrap()
}
fn read_16(&self, addr: Addr) -> u16 {
self.get_bytes(addr, 2).read_u16::<LittleEndian>().unwrap()
self.get_bytes(addr).read_u16::<LittleEndian>().unwrap()
}
fn read_8(&self, addr: Addr) -> u8 {
self.get_bytes(addr, 1)[0]
self.get_bytes(addr)[0]
}
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error> {
self.get_bytes_mut(addr, 4).write_u32::<LittleEndian>(value)
self.get_bytes_mut(addr).write_u32::<LittleEndian>(value)
}
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error> {
self.get_bytes_mut(addr, 2).write_u16::<LittleEndian>(value)
self.get_bytes_mut(addr).write_u16::<LittleEndian>(value)
}
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error> {
self.get_bytes_mut(addr, 1).write_u8(value)
self.get_bytes_mut(addr).write_u8(value)
}
/// Return a slice of bytes
/// Will panic if requested range is out of bounds
fn get_bytes(&self, addr: Addr, len: usize) -> &[u8];
fn get_bytes(&self, addr: Addr) -> &[u8];
/// Return a mutable slice of bytes
/// Will panic if requested range is out of bounds
fn get_bytes_mut(&mut self, addr: Addr, len: usize) -> &mut [u8];
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8];
/// returns the number of cycles needed for this memory access
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize;

View file

@ -3,6 +3,7 @@ use std::fmt;
use std::ops::Add;
use ansi_term::{Colour, Style};
use num_traits::Num;
use crate::sysbus::SysBus;
@ -11,16 +12,44 @@ use super::{
arm::*,
bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*},
psr::RegPSR,
reg_string, Addr, CpuMode, CpuResult, CpuState,
reg_string,
thumb::ThumbInstruction,
Addr, CpuMode, CpuResult, CpuState, DecodedInstruction, InstructionDecoder,
};
#[derive(Debug, Default)]
pub struct PipelineContext {
fetched: Option<(Addr, u32)>,
decoded: Option<ArmInstruction>,
#[derive(Debug)]
pub struct PipelineContext<D, N>
where
D: InstructionDecoder,
N: Num,
{
fetched: Option<(Addr, N)>,
decoded: Option<D>,
}
impl<D, N> Default for PipelineContext<D, N>
where
D: InstructionDecoder,
N: Num,
{
fn default() -> PipelineContext<D, N> {
PipelineContext {
fetched: None,
decoded: None,
}
}
}
impl<D, N> PipelineContext<D, N>
where
D: InstructionDecoder,
N: Num,
{
fn flush(&mut self) {
self.fetched = None;
self.decoded = None;
}
impl PipelineContext {
fn is_flushed(&self) -> bool {
self.fetched.is_none() && self.decoded.is_none()
}
@ -48,7 +77,8 @@ pub struct Core {
pub cpsr: RegPSR,
pub spsr: [RegPSR; 5],
pub pipeline: PipelineContext,
pub pipeline_arm: PipelineContext<ArmInstruction, u32>,
pub pipeline_thumb: PipelineContext<ThumbInstruction, u16>,
cycles: usize,
// store the gpr before executing an instruction to show diff in the Display impl
@ -178,32 +208,69 @@ impl Core {
}
}
fn step_arm(
fn step_thumb(
&mut self,
sysbus: &mut SysBus,
) -> CpuResult<(Option<ArmInstruction>, CpuPipelineAction)> {
) -> CpuResult<(Option<DecodedInstruction>, CpuPipelineAction)> {
// fetch
let new_fetched = sysbus.read_32(self.pc);
let new_fetched = sysbus.read_16(self.pc);
// decode
let new_decoded = match self.pipeline.fetched {
Some((addr, i)) => Some(ArmInstruction::try_from((i, addr)).unwrap()),
let new_decoded = match self.pipeline_thumb.fetched {
Some((addr, i)) => {
let insn = ThumbInstruction::decode(i, addr)?;
Some(insn)
}
None => None,
};
// exec
let result = match self.pipeline.decoded {
let result = match self.pipeline_thumb.decoded {
Some(d) => {
self.gpr_previous = self.get_registers();
let action = self.exec_arm(sysbus, d)?;
Ok((Some(d), action))
let action = self.exec_thumb(sysbus, d)?;
Ok((Some(DecodedInstruction::Thumb(d)), action))
}
None => Ok((None, CpuPipelineAction::IncPC)),
};
self.pipeline.fetched = Some((self.pc, new_fetched));
self.pipeline_thumb.fetched = Some((self.pc, new_fetched));
if let Some(d) = new_decoded {
self.pipeline.decoded = Some(d);
self.pipeline_thumb.decoded = Some(d);
}
result
}
fn step_arm(
&mut self,
sysbus: &mut SysBus,
) -> CpuResult<(Option<DecodedInstruction>, CpuPipelineAction)> {
// fetch
let new_fetched = sysbus.read_32(self.pc);
// decode
let new_decoded = match self.pipeline_arm.fetched {
Some((addr, i)) => {
let insn = ArmInstruction::decode(i, addr)?;
Some(insn)
}
None => None,
};
// exec
let result = match self.pipeline_arm.decoded {
Some(d) => {
self.gpr_previous = self.get_registers();
let action = self.exec_arm(sysbus, d)?;
Ok((Some(DecodedInstruction::Arm(d)), action))
}
None => Ok((None, CpuPipelineAction::IncPC)),
};
self.pipeline_arm.fetched = Some((self.pc, new_fetched));
if let Some(d) = new_decoded {
self.pipeline_arm.decoded = Some(d);
}
result
@ -211,17 +278,17 @@ impl Core {
/// Perform a pipeline step
/// If an instruction was executed in this step, return it.
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<Option<ArmInstruction>> {
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<Option<DecodedInstruction>> {
let (executed_instruction, pipeline_action) = match self.cpsr.state() {
CpuState::ARM => self.step_arm(sysbus),
CpuState::THUMB => unimplemented!("thumb not implemented :("),
CpuState::THUMB => self.step_thumb(sysbus),
}?;
match pipeline_action {
CpuPipelineAction::IncPC => self.advance_pc(),
CpuPipelineAction::Flush => {
self.pipeline.fetched = None;
self.pipeline.decoded = None;
self.pipeline_arm.flush();
self.pipeline_thumb.flush();
}
}
@ -230,21 +297,36 @@ impl Core {
/// Get's the address of the next instruction that is going to be executed
pub fn get_next_pc(&self) -> Addr {
if self.pipeline.is_flushed() {
match self.cpsr.state() {
CpuState::ARM => {
if self.pipeline_arm.is_flushed() {
self.pc as Addr
} else if self.pipeline.is_only_fetched() {
self.pipeline.fetched.unwrap().0
} else if self.pipeline.is_ready_to_execute() {
self.pipeline.decoded.unwrap().pc
} else if self.pipeline_arm.is_only_fetched() {
self.pipeline_arm.fetched.unwrap().0
} else if self.pipeline_arm.is_ready_to_execute() {
self.pipeline_arm.decoded.unwrap().pc
} else {
unreachable!()
}
}
CpuState::THUMB => {
if self.pipeline_thumb.is_flushed() {
self.pc as Addr
} else if self.pipeline_thumb.is_only_fetched() {
self.pipeline_thumb.fetched.unwrap().0
} else if self.pipeline_thumb.is_ready_to_execute() {
self.pipeline_thumb.decoded.unwrap().pc
} else {
unreachable!()
}
}
}
}
/// A step that returns only once an instruction was executed.
/// Returns the address of PC before executing an instruction,
/// and the address of the next instruction to be executed;
pub fn step_debugger(&mut self, sysbus: &mut SysBus) -> CpuResult<ArmInstruction> {
pub fn step_debugger(&mut self, sysbus: &mut SysBus) -> CpuResult<DecodedInstruction> {
loop {
if let Some(i) = self.step(sysbus)? {
return Ok(i);

View file

@ -3,7 +3,10 @@ use std::fmt;
use num::Num;
pub mod arm;
pub mod thumb;
use arm::{ArmDecodeError, ArmInstruction};
use thumb::{ThumbDecodeError, ThumbInstruction};
pub mod cpu;
pub use cpu::*;
@ -18,6 +21,57 @@ pub const REG_SP: usize = 13;
pub type Addr = u32;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum DecodedInstruction {
Arm(ArmInstruction),
Thumb(ThumbInstruction),
}
impl DecodedInstruction {
pub fn get_pc(&self) -> Addr {
match self {
DecodedInstruction::Arm(a) => a.pc,
DecodedInstruction::Thumb(t) => t.pc,
}
}
}
impl fmt::Display for DecodedInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DecodedInstruction::Arm(a) => write!(f, "{}", a),
DecodedInstruction::Thumb(t) => write!(f, "{:#?}", t),
}
}
}
#[derive(Debug, PartialEq)]
pub enum InstructionDecoderError {
ArmDecodeError(ArmDecodeError),
ThumbDecodeError(ThumbDecodeError),
IoError(std::io::ErrorKind),
}
impl From<ArmDecodeError> for InstructionDecoderError {
fn from(e: ArmDecodeError) -> InstructionDecoderError {
InstructionDecoderError::ArmDecodeError(e)
}
}
impl From<ThumbDecodeError> for InstructionDecoderError {
fn from(e: ThumbDecodeError) -> InstructionDecoderError {
InstructionDecoderError::ThumbDecodeError(e)
}
}
pub trait InstructionDecoder: Sized + fmt::Display {
type IntType: Num;
fn decode(n: Self::IntType, addr: Addr) -> Result<Self, InstructionDecoderError>;
/// Helper functions for the Disassembler
fn decode_from_bytes(bytes: &[u8], addr: Addr) -> Result<Self, InstructionDecoderError>;
fn get_raw(&self) -> Self::IntType;
}
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",
@ -26,12 +80,6 @@ pub fn reg_string(reg: usize) -> &'static str {
reg_names[reg]
}
#[derive(Debug, PartialEq)]
pub enum CpuInstruction {
Arm(ArmInstruction),
Thumb,
}
#[derive(Debug, PartialEq, Primitive, Copy, Clone)]
#[repr(u8)]
pub enum CpuState {
@ -101,29 +149,46 @@ impl fmt::Display for CpuMode {
#[derive(Debug, PartialEq)]
pub enum CpuError {
ArmDecodeError(ArmDecodeError),
DecodeError(InstructionDecoderError),
IllegalInstruction,
UnimplementedCpuInstruction(CpuInstruction),
UnimplementedCpuInstruction(Addr, u32, DecodedInstruction),
}
impl From<InstructionDecoderError> for CpuError {
fn from(e: InstructionDecoderError) -> CpuError {
CpuError::DecodeError(e)
}
}
impl From<ArmDecodeError> for CpuError {
fn from(e: ArmDecodeError) -> CpuError {
CpuError::ArmDecodeError(e)
CpuError::DecodeError(InstructionDecoderError::ArmDecodeError(e))
}
}
impl From<ThumbDecodeError> for CpuError {
fn from(e: ThumbDecodeError) -> CpuError {
CpuError::DecodeError(InstructionDecoderError::ThumbDecodeError(e))
}
}
impl fmt::Display for CpuError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CpuError::ArmDecodeError(e) => write!(
CpuError::DecodeError(InstructionDecoderError::ArmDecodeError(e)) => write!(
f,
"arm decoding error at address @0x{:08x} (instruction 0x{:08x}): {:?}",
e.addr, e.insn, e.kind
),
CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn)) => write!(
CpuError::DecodeError(InstructionDecoderError::ThumbDecodeError(e)) => write!(
f,
"unimplemented instruction: 0x{:08x}:\t0x{:08x}\t{}",
insn.pc, insn.raw, insn
"thumb decoding error at address @0x{:08x} (instruction 0x{:08x}): {:?}",
e.addr, e.insn, e.kind
),
CpuError::UnimplementedCpuInstruction(addr, raw, d) => write!(
f,
"unimplemented instruction: 0x{:08x}:\t0x{:08x}\t{:?}",
addr, raw, d
),
CpuError::IllegalInstruction => write!(f, "illegal instruction"),
e => write!(f, "error: {:#x?}", e),

View file

@ -0,0 +1,142 @@
use std::fmt;
use crate::bit::BitIndex;
use super::*;
use crate::arm7tdmi::reg_string;
impl ThumbInstruction {
fn fmt_move_shifted_reg(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{op}\t{Rd}, {Rs}, #{Offset5}",
op = self.format1_op(),
Rd = reg_string(self.rd()),
Rs = reg_string(self.rs()),
Offset5 = self.offset5()
)
}
fn fmt_data_process_imm(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{op}\t{Rd}, #{Offset8}",
op = self.format3_op(),
Rd = reg_string(self.rd()),
Offset8 = self.offset8()
)
}
fn fmt_high_reg_op_or_bx(&self, f: &mut fmt::Formatter) -> fmt::Result {
let op = self.format5_op();
let dst_reg = if self.flag(ThumbInstruction::FLAG_H1) {
self.rd() + 8
} else {
self.rd()
};
let src_reg = if self.flag(ThumbInstruction::FLAG_H2) {
self.rs() + 8
} else {
self.rs()
};
write!(f, "{}\t", op)?;
match op {
OpFormat5::BX => write!(f, "{}", reg_string(src_reg)),
_ => write!(
f,
"{dst}, {src}",
dst = reg_string(dst_reg),
src = reg_string(src_reg)
),
}
}
fn fmt_ldr_pc(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ldr\t{Rd}, [pc, #{Imm:#x}] ; = #{effective:#x}",
Rd = reg_string(self.rd()),
Imm = self.word8(),
effective = self.pc + 2 + (self.word8() as Addr)
)
}
fn fmt_ldr_str_reg_offset(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{op}{b}\t{Rd}, [{Rb}, {Ro}]",
op = if self.is_load() { "ldr" } else { "str" },
b = if self.is_transfering_bytes() { "b" } else { "" },
Rd = reg_string(self.rd()),
Rb = reg_string(self.rb()),
Ro = reg_string(self.ro()),
)
}
fn fmt_add_sub(&self, f: &mut fmt::Formatter) -> fmt::Result {
let operand = if self.is_immediate_operand() {
format!("#{:x}", self.raw.bit_range(6..9))
} else {
String::from(reg_string(self.rn()))
};
write!(
f,
"{op}\t{Rd}, [{Rs}, {operand}]",
op = if self.is_subtract() { "sub" } else { "add" },
Rd = reg_string(self.rd()),
Rs = reg_string(self.rs()),
operand = operand
)
}
fn fmt_branch_with_cond(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"b{cond}\t{addr:#x}",
cond = self.cond(),
addr = {
let offset = self.offset8() as i8 as i32;
(self.pc as i32).wrapping_add(offset) as Addr
}
)
}
}
impl fmt::Display for ThumbInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.fmt {
ThumbFormat::MoveShiftedReg => self.fmt_move_shifted_reg(f),
ThumbFormat::AddSub => self.fmt_add_sub(f),
ThumbFormat::DataProcessImm => self.fmt_data_process_imm(f),
ThumbFormat::HiRegOpOrBranchExchange => self.fmt_high_reg_op_or_bx(f),
ThumbFormat::LdrPc => self.fmt_ldr_pc(f),
ThumbFormat::LdrStrRegOffset => self.fmt_ldr_str_reg_offset(f),
ThumbFormat::BranchConditional => self.fmt_branch_with_cond(f),
_ => write!(f, "({:?})", self),
}
}
}
impl fmt::Display for OpFormat3 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OpFormat3::MOV => write!(f, "mov"),
OpFormat3::CMP => write!(f, "cmp"),
OpFormat3::ADD => write!(f, "add"),
OpFormat3::SUB => write!(f, "sub"),
}
}
}
impl fmt::Display for OpFormat5 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OpFormat5::ADD => write!(f, "add"),
OpFormat5::CMP => write!(f, "cmp"),
OpFormat5::MOV => write!(f, "mov"),
OpFormat5::BX => write!(f, "bx"),
}
}
}

View file

@ -0,0 +1,9 @@
use super::super::super::sysbus::SysBus;
use super::super::cpu::{Core, CpuExecResult};
use super::ThumbInstruction;
impl Core {
pub fn exec_thumb(&mut self, sysbus: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
unimplemented!("thumb not implemented {:#}", insn)
}
}

241
src/arm7tdmi/thumb/mod.rs Normal file
View file

@ -0,0 +1,241 @@
use std::io;
use crate::bit::BitIndex;
use crate::byteorder::{LittleEndian, ReadBytesExt};
use crate::num::FromPrimitive;
use super::arm::{ArmCond, ArmShiftType};
use super::{Addr, InstructionDecoder, InstructionDecoderError};
pub mod display;
pub mod exec;
#[derive(Debug, PartialEq)]
pub enum ThumbDecodeErrorKind {
UnknownInstructionFormat,
IoError(io::ErrorKind),
}
use ThumbDecodeErrorKind::*;
#[derive(Debug, PartialEq)]
pub struct ThumbDecodeError {
pub kind: ThumbDecodeErrorKind,
pub insn: u16,
pub addr: Addr,
}
impl ThumbDecodeError {
fn new(kind: ThumbDecodeErrorKind, insn: u16, addr: Addr) -> ThumbDecodeError {
ThumbDecodeError {
kind: kind,
insn: insn,
addr: addr,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ThumbFormat {
/// Format 1
MoveShiftedReg,
/// Format 2
AddSub,
/// Format 3
DataProcessImm,
/// Format 4
AluOps,
/// Format 5
HiRegOpOrBranchExchange,
/// Format 6
LdrPc,
/// Format 7
LdrStrRegOffset,
/// Format 8
LdrStrSHB,
/// Format 9
LdrStrImmOffset,
/// Format 10
LdrStrHalfWord,
/// Format 11
LdrStrSp,
/// Format 12
LdrAddress,
/// Format 13
AddSp,
/// Format 14
PushPop,
/// Format 15
LdmStm,
/// Format 16
BranchConditional,
/// Format 17
Swi,
/// Format 18
Branch,
/// Format 19
BranchLongWithLink,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ThumbInstruction {
pub fmt: ThumbFormat,
pub raw: u16,
pub pc: Addr,
}
impl InstructionDecoder for ThumbInstruction {
type IntType = u16;
fn decode(raw: u16, addr: Addr) -> Result<Self, InstructionDecoderError> {
use self::ThumbFormat::*;
let fmt = if raw & 0xf800 == 0x1800 {
Ok(AddSub)
} else if raw & 0xe000 == 0x0000 {
Ok(MoveShiftedReg)
} else if raw & 0xe000 == 0x2000 {
Ok(DataProcessImm)
} else if raw & 0xfc00 == 0x4000 {
Ok(AluOps)
} else if raw & 0xfc00 == 0x4400 {
Ok(HiRegOpOrBranchExchange)
} else if raw & 0xf800 == 0x4800 {
Ok(LdrPc)
} else if raw & 0xf200 == 0x5000 {
Ok(LdrStrRegOffset)
} else if raw & 0xf200 == 0x5200 {
Ok(LdrStrSHB)
} else if raw & 0xe000 == 0x6000 {
Ok(LdrStrImmOffset)
} else if raw & 0xf000 == 0x8000 {
Ok(LdrStrHalfWord)
} else if raw & 0xf000 == 0x9000 {
Ok(LdrStrSp)
} else if raw & 0xf000 == 0xa000 {
Ok(LdrAddress)
} else if raw & 0xff00 == 0xb000 {
Ok(AddSp)
} else if raw & 0xf600 == 0xb400 {
Ok(PushPop)
} else if raw & 0xf000 == 0xc000 {
Ok(LdmStm)
} else if raw & 0xf000 == 0xd000 {
Ok(BranchConditional)
} else if raw & 0xff00 == 0xdf00 {
Ok(Swi)
} else if raw & 0xf800 == 0xe000 {
Ok(Branch)
} else if raw & 0xf000 == 0xf000 {
Ok(BranchLongWithLink)
} else {
Err(ThumbDecodeError::new(UnknownInstructionFormat, raw, addr))
}?;
Ok(ThumbInstruction {
fmt: fmt,
raw: raw,
pc: addr,
})
}
fn decode_from_bytes(bytes: &[u8], addr: Addr) -> Result<Self, InstructionDecoderError> {
let mut rdr = std::io::Cursor::new(bytes);
let raw = rdr
.read_u16::<LittleEndian>()
.map_err(|e| InstructionDecoderError::IoError(e.kind()))?;
Self::decode(raw, addr)
}
fn get_raw(&self) -> u16 {
self.raw
}
}
#[derive(Debug, Primitive)]
pub enum OpFormat3 {
MOV = 0,
CMP = 1,
ADD = 2,
SUB = 3,
}
#[derive(Debug, Primitive)]
pub enum OpFormat5 {
ADD = 0,
CMP = 1,
MOV = 2,
BX = 3,
}
impl ThumbInstruction {
const FLAG_H1: usize = 7;
const FLAG_H2: usize = 6;
pub fn rd(&self) -> usize {
(self.raw & 0b111) as usize
}
pub fn rs(&self) -> usize {
self.raw.bit_range(3..6) as usize
}
pub fn rb(&self) -> usize {
self.raw.bit_range(3..6) as usize
}
pub fn ro(&self) -> usize {
self.raw.bit_range(6..9) as usize
}
pub fn rn(&self) -> usize {
self.raw.bit_range(6..9) as usize
}
pub fn format1_op(&self) -> ArmShiftType {
ArmShiftType::from_u8(self.raw.bit_range(11..13) as u8).unwrap()
}
pub fn format3_op(&self) -> OpFormat3 {
OpFormat3::from_u8(self.raw.bit_range(11..13) as u8).unwrap()
}
pub fn format5_op(&self) -> OpFormat5 {
OpFormat5::from_u8(self.raw.bit_range(8..10) as u8).unwrap()
}
pub fn offset5(&self) -> i8 {
self.raw.bit_range(6..11) as i8
}
pub fn offset8(&self) -> i8 {
self.raw.bit_range(0..8) as i8
}
pub fn word8(&self) -> u16 {
self.raw.bit_range(0..8) << 2
}
pub fn is_transfering_bytes(&self) -> bool {
self.raw.bit(10)
}
pub fn is_load(&self) -> bool {
self.raw.bit(11)
}
pub fn is_subtract(&self) -> bool {
self.raw.bit(9)
}
pub fn is_immediate_operand(&self) -> bool {
self.raw.bit(10)
}
pub fn cond(&self) -> ArmCond {
ArmCond::from_u8(self.raw.bit_range(8..12) as u8).unwrap()
}
pub fn flag(&self, bit: usize) -> bool {
self.raw.bit(bit)
}
}

View file

@ -16,11 +16,3 @@ subcommands:
takes_value: true
help: Sets the game-rom file to use
required: true
- disass:
about: disassemble an arm binary file
args:
- INPUT:
help: Sets the input file to use
required: true
index: 1

View file

@ -9,7 +9,6 @@ extern crate rustboyadvance_ng;
use rustboyadvance_ng::arm7tdmi;
use rustboyadvance_ng::debugger::{Debugger, DebuggerError};
use rustboyadvance_ng::disass::Disassembler;
use rustboyadvance_ng::sysbus::SysBus;
use rustboyadvance_ng::util::read_bin_file;
@ -47,17 +46,6 @@ impl From<DebuggerError> for GBAError {
}
}
fn run_disass(matches: &ArgMatches) -> GBAResult<()> {
let input = matches.value_of("INPUT").unwrap();
let bin = read_bin_file(&input)?;
let disassembler = Disassembler::new(0, &bin);
for (_, line) in disassembler {
println!("{}", line)
}
Ok(())
}
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
let bios_bin = read_bin_file(matches.value_of("bios").unwrap_or_default())?;
let rom_bin = read_bin_file(matches.value_of("game_rom").unwrap())?;
@ -81,7 +69,6 @@ fn main() {
let result = match matches.subcommand() {
("debug", Some(m)) => run_debug(m),
("disass", Some(m)) => run_disass(m),
_ => Ok(()),
};

View file

@ -1,5 +1,5 @@
use crate::arm7tdmi::bus::Bus;
use crate::arm7tdmi::{reg_string, Addr, REG_PC};
use crate::arm7tdmi::{Addr, CpuState};
use crate::disass::Disassembler;
use super::{parser::Value, Debugger, DebuggerError, DebuggerResult};
@ -9,15 +9,21 @@ use ansi_term::Colour;
use colored::*;
use hexdump;
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum DisassMode {
ModeArm,
ModeThumb,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Command {
Info,
SingleStep(bool),
Continue,
HexDump(u32, usize),
Disass(u32, usize),
AddBreakpoint(u32),
DelBreakpoint(u32),
HexDump(Addr, usize),
Disass(DisassMode, Addr, usize),
AddBreakpoint(Addr),
DelBreakpoint(Addr),
ClearBreakpoints,
ListBreakpoints,
Reset,
@ -38,8 +44,8 @@ impl Command {
Ok(insn) => {
println!("{}\n", debugger.cpu);
println!(
"Executed at @0x{:08x}:\n\t{}",
insn.pc,
"Executed at @0x{:08x}:\t{}",
insn.get_pc(),
Colour::Yellow.italic().paint(format!("{} ", insn))
);
println!("Next instruction at @0x{:08x}", debugger.cpu.get_next_pc())
@ -60,8 +66,8 @@ impl Command {
match debugger.cpu.step_debugger(&mut debugger.sysbus) {
Ok(insn) => {
println!(
"@0x{:08x}:\n\t{}",
insn.pc,
"@0x{:08x}:\t{}",
insn.get_pc(),
Colour::Yellow.italic().paint(format!("{} ", insn))
);
}
@ -73,16 +79,29 @@ impl Command {
};
},
HexDump(addr, nbytes) => {
let bytes = debugger.sysbus.get_bytes(addr, nbytes);
hexdump::hexdump(bytes);
let bytes = debugger.sysbus.get_bytes(addr);
hexdump::hexdump(&bytes[0..nbytes]);
}
Disass(addr, n) => {
let bytes = debugger.sysbus.get_bytes(addr, 4 * n);
let disass = Disassembler::new(addr, bytes);
for (_, line) in disass {
Disass(mode, addr, n) => {
use crate::arm7tdmi::arm::ArmInstruction;
use crate::arm7tdmi::thumb::ThumbInstruction;
let bytes = debugger.sysbus.get_bytes(addr);
match mode {
DisassMode::ModeArm => {
let disass = Disassembler::<ArmInstruction>::new(addr, bytes);
for (_, line) in disass.take(n) {
println!("{}", line)
}
}
DisassMode::ModeThumb => {
let disass = Disassembler::<ThumbInstruction>::new(addr, bytes);
for (_, line) in disass.take(n) {
println!("{}", line)
}
}
};
}
Quit => {
print!("Quitting!");
debugger.stop();
@ -114,9 +133,37 @@ impl Command {
}
impl Debugger {
fn get_disassembler_args(&self, args: Vec<Value>) -> DebuggerResult<(Addr, usize)> {
match args.len() {
2 => {
let addr = self.val_address(&args[0])?;
let n = self.val_number(&args[1])?;
Ok((addr, n as usize))
}
1 => {
let addr = self.val_address(&args[0])?;
Ok((addr, 10))
}
0 => {
if let Some(Command::Disass(_mode, addr, n)) = &self.previous_command {
Ok((*addr + (4 * (*n as u32)), 10))
} else {
Ok((self.cpu.get_next_pc(), 10))
}
}
_ => {
return Err(DebuggerError::InvalidCommandFormat(
"disass [addr] [n]".to_string(),
))
}
}
}
pub fn eval_command(&self, command: Value, args: Vec<Value>) -> DebuggerResult<Command> {
let command = match command {
Value::Name(command) => command,
Value::Identifier(command) => command,
_ => {
return Err(DebuggerError::InvalidCommand("expected a name".to_string()));
}
@ -156,33 +203,23 @@ impl Debugger {
Ok(Command::HexDump(addr, n))
}
"d" | "disass" => {
let (addr, n) = match args.len() {
2 => {
let addr = self.val_address(&args[0])?;
let n = self.val_number(&args[1])?;
let (addr, n) = self.get_disassembler_args(args)?;
(addr, n as usize)
}
1 => {
let addr = self.val_address(&args[0])?;
(addr, 10)
}
0 => {
if let Some(Command::Disass(addr, n)) = self.previous_command {
(addr + (4 * n as u32), 10)
} else {
(self.cpu.get_next_pc(), 10)
}
}
_ => {
return Err(DebuggerError::InvalidCommandFormat(
"disass [addr] [n]".to_string(),
))
}
let m = match self.cpu.cpsr.state() {
CpuState::ARM => DisassMode::ModeArm,
CpuState::THUMB => DisassMode::ModeThumb,
};
Ok(Command::Disass(m, addr, n))
}
"da" | "disass-arm" => {
let (addr, n) = self.get_disassembler_args(args)?;
Ok(Command::Disass(addr, n))
Ok(Command::Disass(DisassMode::ModeArm, addr, n))
}
"dt" | "disass-thumb" => {
let (addr, n) = self.get_disassembler_args(args)?;
Ok(Command::Disass(DisassMode::ModeThumb, addr, n))
}
"b" | "break" => {
if args.len() != 1 {

View file

@ -82,7 +82,7 @@ impl Debugger {
fn val_reg(&self, arg: &Value) -> DebuggerResult<usize> {
match arg {
Value::Name(reg) => self.decode_reg(&reg),
Value::Identifier(reg) => self.decode_reg(&reg),
v => Err(DebuggerError::InvalidArgument(format!(
"expected a number, got {:?}",
v
@ -103,7 +103,7 @@ impl Debugger {
fn val_address(&self, arg: &Value) -> DebuggerResult<Addr> {
match arg {
Value::Num(n) => Ok(*n),
Value::Name(reg) => {
Value::Identifier(reg) => {
let reg = self.decode_reg(&reg)?;
Ok(self.cpu.get_reg(reg))
}
@ -167,7 +167,7 @@ impl Debugger {
println!("Welcome to rustboyadvance-NG debugger 😎!\n");
self.running = true;
let mut rl = Editor::<()>::new();
rl.load_history(".rustboyadvance_history");
rl.load_history(".rustboyadvance_history").unwrap();
while self.running {
let readline = rl.readline(&format!("({}) ᐅ ", "rustboyadvance-dbg".bold().cyan()));
match readline {

View file

@ -4,8 +4,8 @@ use crate::arm7tdmi::Addr;
use nom;
use nom::branch::alt;
use nom::bytes::complete::{tag, take_while_m_n};
use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1};
use nom::bytes::complete::{tag, take_while1, take_while_m_n};
use nom::character::complete::{char, digit1, multispace0, multispace1};
use nom::combinator::{cut, map, map_res, opt};
use nom::error::{context, convert_error, ParseError, VerboseError};
use nom::multi::separated_list;
@ -25,7 +25,7 @@ pub enum DerefType {
pub enum Value {
Num(u32),
Boolean(bool),
Name(String),
Identifier(String),
Deref(Box<Value>, DerefType),
}
@ -63,8 +63,11 @@ fn parse_boolean<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Val
)(i)
}
fn parse_name<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
map(alphanumeric1, |s: &str| Value::Name(String::from(s)))(i)
fn parse_identifier<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
map(
take_while1(|c: char| c.is_alphanumeric() || c == '_' || c == '-'),
|s: &str| Value::Identifier(String::from(s)),
)(i)
}
fn parse_deref_type<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, DerefType, E> {
@ -90,7 +93,7 @@ fn parse_deref<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value
Some(t) => t,
None => DerefType::Word,
}),
alt((parse_num, parse_name)),
alt((parse_num, parse_identifier)),
)),
|(t, v)| Value::Deref(Box::new(v), t),
)),
@ -101,7 +104,7 @@ fn parse_deref<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value
fn parse_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, Value, E> {
context(
"argument",
alt((parse_boolean, parse_deref, parse_num, parse_name)),
alt((parse_boolean, parse_deref, parse_num, parse_identifier)),
)(i)
}
@ -110,7 +113,7 @@ fn parse_command<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&str, Expr,
"command",
map(
tuple((
terminated(parse_name, multispace0),
terminated(parse_identifier, multispace0),
separated_list(multispace1, parse_value),
)),
|(cmd, args)| Expr::Command(cmd, args),
@ -169,14 +172,17 @@ mod tests {
fn test_parse_command_expr() {
assert_eq!(
parse_expr("command"),
Ok(Expr::Command(Value::Name("command".to_string()), vec![]))
Ok(Expr::Command(
Value::Identifier("command".to_string()),
vec![]
))
);
assert_eq!(
parse_expr("command arg0 0x1337 true "),
Ok(Expr::Command(
Value::Name("command".to_string()),
Value::Identifier("command".to_string()),
vec![
Value::Name("arg0".to_string()),
Value::Identifier("arg0".to_string()),
Value::Num(0x1337),
Value::Boolean(true)
]
@ -189,22 +195,22 @@ mod tests {
assert_eq!(
parse_expr(" pc = 0x1337 "),
Ok(Expr::Assignment(
Value::Name("pc".to_string()),
Value::Identifier("pc".to_string()),
Value::Num(0x1337)
))
);
assert_eq!(
parse_expr("aaa = false "),
Ok(Expr::Assignment(
Value::Name("aaa".to_string()),
Value::Identifier("aaa".to_string()),
Value::Boolean(false)
))
);
assert_eq!(
parse_expr(" pc = lr "),
Ok(Expr::Assignment(
Value::Name("pc".to_string()),
Value::Name("lr".to_string())
Value::Identifier("pc".to_string()),
Value::Identifier("lr".to_string())
))
);
}
@ -222,7 +228,10 @@ mod tests {
parse_deref::<VerboseError<&str>>("*r10"),
Ok((
"",
Value::Deref(Box::new(Value::Name("r10".to_string())), DerefType::Word)
Value::Deref(
Box::new(Value::Identifier("r10".to_string())),
DerefType::Word
)
))
);
}

View file

@ -1,55 +1,69 @@
use std::convert::TryFrom;
use std::marker::PhantomData;
use super::arm7tdmi::{Addr, InstructionDecoder, InstructionDecoderError};
use std::io;
use std::io::ErrorKind;
use std::io::{Cursor, Seek, SeekFrom};
use byteorder::{LittleEndian, ReadBytesExt};
use super::arm7tdmi::arm;
pub struct Disassembler<'a> {
base: u32,
rdr: Cursor<&'a [u8]>,
pub struct Disassembler<'a, D>
where
D: InstructionDecoder,
{
base: Addr,
pos: usize,
bytes: &'a [u8],
pub word_size: usize,
instruction_decoder: PhantomData<D>,
}
impl<'a> Disassembler<'a> {
pub fn new(base: u32, bin: &'a [u8]) -> Disassembler {
impl<'a, D> Disassembler<'a, D>
where
D: InstructionDecoder,
{
pub fn new(base: Addr, bytes: &'a [u8]) -> Disassembler<D> {
Disassembler {
base: base,
rdr: Cursor::new(bin),
base: base as Addr,
pos: 0,
bytes: bytes,
word_size: std::mem::size_of::<D::IntType>(),
instruction_decoder: PhantomData,
}
}
}
impl<'a> Seek for Disassembler<'a> {
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
self.rdr.seek(pos)
}
}
impl<'a> Iterator for Disassembler<'a> {
type Item = (u32, String);
impl<'a, D> Iterator for Disassembler<'a, D>
where
D: InstructionDecoder,
<D as InstructionDecoder>::IntType: std::fmt::LowerHex,
{
type Item = (Addr, String);
fn next(&mut self) -> Option<Self::Item> {
let mut line = String::new();
let value: u32 = match self.rdr.read_u32::<LittleEndian>() {
Ok(value) => value,
Err(e) => match e.kind() {
ErrorKind::UnexpectedEof => {
let addr = self.base + self.pos as Addr;
let decoded: Option<D> =
match D::decode_from_bytes(&self.bytes[(self.pos as usize)..], addr) {
Ok(decoded) => {
self.pos += self.word_size;
Some(decoded)
}
Err(InstructionDecoderError::IoError(ErrorKind::UnexpectedEof)) => {
return None;
}
_ => panic!("unexpected error"),
},
_ => {
self.pos += self.word_size;
None
}
};
let addr = self.base + (self.rdr.position() - 4) as u32;
line.push_str(&format!("{:8x}:\t{:08x} \t", addr, value));
match arm::ArmInstruction::try_from((value, addr)) {
Ok(insn) => line.push_str(&format!("{}", insn)),
Err(_) => line.push_str("<UNDEFINED>"),
match decoded {
Some(insn) => {
line.push_str(&format!("{:8x}:\t{:08x} \t{}", addr, insn.get_raw(), insn))
}
_ => line.push_str(&format!("{:8x}:\t \t<UNDEFINED>", addr)),
};
Some((addr, line))
Some((self.pos as Addr, line))
}
}

View file

@ -11,11 +11,11 @@ extern crate rustyline;
extern crate nom;
extern crate colored; // not needed in Rust 2018
extern crate ansi_term;
extern crate colored; // not needed in Rust 2018
pub mod sysbus;
pub mod arm7tdmi;
pub mod debugger;
pub mod disass;
pub mod sysbus;
pub mod util;

View file

@ -36,14 +36,12 @@ impl Default for WaitState {
}
impl Bus for BoxedMemory {
fn get_bytes(&self, addr: Addr, size: usize) -> &[u8] {
let addr = addr as usize;
&self.0[addr..addr + size]
fn get_bytes(&self, addr: Addr) -> &[u8] {
&self.0[addr as usize..]
}
fn get_bytes_mut(&mut self, addr: Addr, size: usize) -> &mut [u8] {
let addr = addr as usize;
&mut self.0[addr..addr + size]
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8] {
&mut self.0[addr as usize..]
}
fn get_cycles(&self, _addr: Addr, access: MemoryAccess) -> usize {
@ -149,12 +147,12 @@ impl Bus for SysBus {
self.map_mut(addr).write_8(addr & 0xff_ffff, value)
}
fn get_bytes(&self, addr: Addr, size: usize) -> &[u8] {
self.map(addr).get_bytes(addr & 0xff_ffff, size)
fn get_bytes(&self, addr: Addr) -> &[u8] {
self.map(addr).get_bytes(addr & 0xff_ffff)
}
fn get_bytes_mut(&mut self, addr: Addr, size: usize) -> &mut [u8] {
self.map_mut(addr).get_bytes_mut(addr & 0xff_ffff, size)
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8] {
self.map_mut(addr).get_bytes_mut(addr & 0xff_ffff)
}
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {