Mega Commit #2 - Add some thumb decoding and disassembly
Former-commit-id: e3a89ac681a8d6f6f0bee85b32f64d181e11242f
This commit is contained in:
parent
cbddeffd91
commit
6f81c236a6
|
@ -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};
|
||||
|
||||
|
|
|
@ -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),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for ArmInstruction {
|
||||
type Error = ArmDecodeError;
|
||||
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)
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 PipelineContext {
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
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 {
|
||||
unreachable!()
|
||||
match self.cpsr.state() {
|
||||
CpuState::ARM => {
|
||||
if self.pipeline_arm.is_flushed() {
|
||||
self.pc as Addr
|
||||
} 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);
|
||||
|
|
|
@ -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),
|
||||
|
|
142
src/arm7tdmi/thumb/display.rs
Normal file
142
src/arm7tdmi/thumb/display.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
}
|
9
src/arm7tdmi/thumb/exec.rs
Normal file
9
src/arm7tdmi/thumb/exec.rs
Normal 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
241
src/arm7tdmi/thumb/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -15,12 +15,4 @@ subcommands:
|
|||
long: game-rom
|
||||
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
|
||||
|
||||
required: true
|
|
@ -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(()),
|
||||
};
|
||||
|
||||
|
|
|
@ -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,15 +79,28 @@ 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 {
|
||||
println!("{}", line)
|
||||
}
|
||||
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!");
|
||||
|
@ -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 {
|
||||
|
|
|
@ -82,7 +82,7 @@ impl Debugger {
|
|||
|
||||
fn val_reg(&self, arg: &Value) -> DebuggerResult<usize> {
|
||||
match arg {
|
||||
Value::Name(reg) => self.decode_reg(®),
|
||||
Value::Identifier(reg) => self.decode_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(®)?;
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
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)),
|
||||
};
|
||||
|
||||
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>"),
|
||||
};
|
||||
|
||||
Some((addr, line))
|
||||
Some((self.pos as Addr, line))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 util;
|
||||
pub mod sysbus;
|
||||
pub mod util;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Reference in a new issue