Add continue command
This commit is contained in:
parent
9921f1c974
commit
22a915ec85
19
src/arm7tdmi/arm/exec.rs
Normal file
19
src/arm7tdmi/arm/exec.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use super::super::cpu::{Core, CpuPipelineAction, CpuError, CpuInstruction, CpuExecResult};
|
||||||
|
use super::super::sysbus::SysBus;
|
||||||
|
use super::{ArmInstruction, ArmInstructionFormat};
|
||||||
|
|
||||||
|
impl Core {
|
||||||
|
pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
match insn.fmt {
|
||||||
|
ArmInstructionFormat::BX => {
|
||||||
|
self.pc = self.get_reg(insn.rn());
|
||||||
|
Ok((CpuInstruction::Arm(insn), CpuPipelineAction::Branch))
|
||||||
|
},
|
||||||
|
ArmInstructionFormat::B_BL => {
|
||||||
|
self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32;
|
||||||
|
Ok((CpuInstruction::Arm(insn), CpuPipelineAction::Branch))
|
||||||
|
}
|
||||||
|
fmt => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(insn))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod display;
|
pub mod display;
|
||||||
|
pub mod exec;
|
||||||
|
|
||||||
use crate::bit::BitIndex;
|
use crate::bit::BitIndex;
|
||||||
use crate::num_traits::FromPrimitive;
|
use crate::num_traits::FromPrimitive;
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use crate::num_traits::FromPrimitive;
|
use crate::num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
use super::arm::exec;
|
||||||
use super::arm::*;
|
use super::arm::*;
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CpuInstruction {
|
||||||
|
Arm(ArmInstruction),
|
||||||
|
Thumb,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum CpuError {
|
pub enum CpuError {
|
||||||
ArmDecodeError(ArmDecodeError),
|
ArmDecodeError(ArmDecodeError),
|
||||||
IllegalInstruction,
|
IllegalInstruction(CpuInstruction),
|
||||||
|
UnimplementedCpuInstruction(CpuInstruction),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ArmDecodeError> for CpuError {
|
impl From<ArmDecodeError> for CpuError {
|
||||||
|
@ -17,6 +26,29 @@ impl From<ArmDecodeError> for CpuError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CpuError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
CpuError::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!(
|
||||||
|
f,
|
||||||
|
"unimplemented instruction: 0x{:08x}:\t0x{:08x}\t{}",
|
||||||
|
insn.pc, insn.raw, insn
|
||||||
|
),
|
||||||
|
CpuError::IllegalInstruction(CpuInstruction::Arm(insn)) => write!(
|
||||||
|
f,
|
||||||
|
"illegal instruction at address @0x{:08x} (0x{:08x})",
|
||||||
|
insn.pc, insn.raw
|
||||||
|
),
|
||||||
|
e => write!(f, "error: {:#x?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type CpuResult<T> = Result<T, CpuError>;
|
pub type CpuResult<T> = Result<T, CpuError>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -45,15 +77,24 @@ pub struct CpuModeContext {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Core {
|
pub struct Core {
|
||||||
pc: u32,
|
pub pc: u32,
|
||||||
// r0-r7
|
// r0-r7
|
||||||
gpr: [u32; 8],
|
gpr: [u32; 8],
|
||||||
cpsr: u32,
|
cpsr: u32,
|
||||||
|
|
||||||
mode: CpuMode,
|
mode: CpuMode,
|
||||||
state: CpuState,
|
state: CpuState,
|
||||||
|
verbose: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum CpuPipelineAction {
|
||||||
|
AdvancePc,
|
||||||
|
Branch,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CpuExecResult = CpuResult<(CpuInstruction, CpuPipelineAction)>;
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new() -> Core {
|
pub fn new() -> Core {
|
||||||
Core {
|
Core {
|
||||||
|
@ -62,9 +103,14 @@ impl Core {
|
||||||
cpsr: 0,
|
cpsr: 0,
|
||||||
mode: CpuMode::System,
|
mode: CpuMode::System,
|
||||||
state: CpuState::ARM,
|
state: CpuState::ARM,
|
||||||
|
verbose: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_verbose(&mut self, v: bool) {
|
||||||
|
self.verbose = v;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_reg(&self, reg_num: usize) -> u32 {
|
pub fn get_reg(&self, reg_num: usize) -> u32 {
|
||||||
match reg_num {
|
match reg_num {
|
||||||
0...7 => self.gpr[reg_num],
|
0...7 => self.gpr[reg_num],
|
||||||
|
@ -100,22 +146,30 @@ impl Core {
|
||||||
self.pc = self.pc.wrapping_add(self.word_size() as u32)
|
self.pc = self.pc.wrapping_add(self.word_size() as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_arm(&mut self, sysbus: &mut SysBus) -> CpuResult<()> {
|
fn step_arm(&mut self, sysbus: &mut SysBus) -> CpuExecResult {
|
||||||
// fetch
|
// fetch
|
||||||
let insn = sysbus.read_32(self.pc);
|
let insn = sysbus.read_32(self.pc);
|
||||||
// decode
|
// decode
|
||||||
let insn = ArmInstruction::try_from((insn, self.pc))?;
|
let insn = ArmInstruction::try_from((insn, self.pc))?;
|
||||||
|
// exec
|
||||||
Ok(())
|
self.exec_arm(sysbus, insn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> {
|
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> {
|
||||||
match self.state {
|
let (executed_insn, pipeline_action) = match self.state {
|
||||||
CpuState::ARM => self.step_arm(sysbus)?,
|
CpuState::ARM => self.step_arm(sysbus),
|
||||||
CpuState::THUMB => unimplemented!("thumb not implemented :("),
|
CpuState::THUMB => unimplemented!("thumb not implemented :("),
|
||||||
};
|
}?;
|
||||||
|
|
||||||
|
if self.verbose {
|
||||||
|
if let CpuInstruction::Arm(insn) = executed_insn {
|
||||||
|
println!("{:8x}:\t{:08x} \t{}", insn.pc, insn.raw, insn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if CpuPipelineAction::AdvancePc == pipeline_action {
|
||||||
self.advance_pc();
|
self.advance_pc();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use super::sysbus::SysBus;
|
||||||
pub struct Debugger {
|
pub struct Debugger {
|
||||||
cpu: cpu::Core,
|
cpu: cpu::Core,
|
||||||
sysbus: SysBus,
|
sysbus: SysBus,
|
||||||
|
running: bool,
|
||||||
breakpoints: Vec<u32>,
|
breakpoints: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,31 +119,60 @@ impl Debugger {
|
||||||
cpu: cpu,
|
cpu: cpu,
|
||||||
sysbus: sysbus,
|
sysbus: sysbus,
|
||||||
breakpoints: Vec::new(),
|
breakpoints: Vec::new(),
|
||||||
|
running: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repl(&mut self) -> DebuggerResult<()> {
|
fn is_breakpoint_reached(&self) -> bool {
|
||||||
let mut rl = Editor::<()>::new();
|
let pc = self.cpu.pc;
|
||||||
loop {
|
for b in &self.breakpoints {
|
||||||
let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".cyan()));
|
if *b == pc {
|
||||||
match readline {
|
return true;
|
||||||
Ok(line) => {
|
}
|
||||||
let command = parse_debugger_command(&line);
|
}
|
||||||
match command {
|
|
||||||
Ok(Nop) => (),
|
false
|
||||||
Ok(Info) => {
|
}
|
||||||
println!("cpu info: {:#?}", self.cpu)
|
|
||||||
|
fn command(&mut self, cmd: DebuggerCommand) {
|
||||||
|
match cmd {
|
||||||
|
Nop => (),
|
||||||
|
Info => {
|
||||||
|
println!("cpu info: {:#x?}", self.cpu)
|
||||||
|
}
|
||||||
|
SingleStep => {
|
||||||
|
;
|
||||||
|
match self.cpu.step(&mut self.sysbus) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
|
println!("cpu: {:#x?}", self.cpu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if self.is_breakpoint_reached() {
|
||||||
|
println!("breakpoint 0x{:08x} reached!", self.cpu.pc)
|
||||||
}
|
}
|
||||||
Ok(SingleStep) => {
|
|
||||||
println!("single step:");
|
|
||||||
self.cpu.step(&mut self.sysbus)?;
|
|
||||||
()
|
|
||||||
},
|
},
|
||||||
Ok(Quit) => {
|
Continue => {
|
||||||
print!("Quitting!");
|
loop {
|
||||||
|
match self.cpu.step(&mut self.sysbus) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
|
println!("cpu: {:#x?}", self.cpu);
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if self.is_breakpoint_reached() {
|
||||||
|
println!("breakpoint 0x{:08x} reached!", self.cpu.pc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Quit => {
|
||||||
|
print!("Quitting!");
|
||||||
|
self.running = false;
|
||||||
},
|
},
|
||||||
Ok(AddBreakpoint(addr)) => {
|
AddBreakpoint(addr) => {
|
||||||
if !self.breakpoints.contains(&addr) {
|
if !self.breakpoints.contains(&addr) {
|
||||||
let new_index = self.breakpoints.len();
|
let new_index = self.breakpoints.len();
|
||||||
self.breakpoints.push(addr);
|
self.breakpoints.push(addr);
|
||||||
|
@ -151,16 +181,31 @@ impl Debugger {
|
||||||
println!("breakpoint already exists!")
|
println!("breakpoint already exists!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ListBreakpoints) => {
|
ListBreakpoints => {
|
||||||
println!("breakpoint list:");
|
println!("breakpoint list:");
|
||||||
for (i, b) in self.breakpoints.iter().enumerate() {
|
for (i, b) in self.breakpoints.iter().enumerate() {
|
||||||
println!("[{}] 0x{:08x}", i, b)
|
println!("[{}] 0x{:08x}", i, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Reset) => {
|
Reset => {
|
||||||
println!("resetting cpu...");
|
println!("resetting cpu...");
|
||||||
self.cpu.reset();
|
self.cpu.reset();
|
||||||
println!("cpu is restarted!")
|
println!("cpu is restarted!")
|
||||||
|
},
|
||||||
|
_ => panic!("command {:?} not implemented", cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn repl(&mut self) -> DebuggerResult<()> {
|
||||||
|
self.running = true;
|
||||||
|
let mut rl = Editor::<()>::new();
|
||||||
|
while self.running {
|
||||||
|
let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".cyan()));
|
||||||
|
match readline {
|
||||||
|
Ok(line) => {
|
||||||
|
let command = parse_debugger_command(&line);
|
||||||
|
match command {
|
||||||
|
Ok(cmd) => {
|
||||||
|
self.command(cmd)
|
||||||
}
|
}
|
||||||
Err(DebuggerError::InvalidCommand(command)) => {
|
Err(DebuggerError::InvalidCommand(command)) => {
|
||||||
println!("invalid command: {}", command)
|
println!("invalid command: {}", command)
|
||||||
|
@ -171,7 +216,6 @@ impl Debugger {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
Ok(command) => println!("got command: {:?}", command),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
|
|
|
@ -108,6 +108,7 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
|
|
||||||
let mut sysbus = SysBus::new(bios_bin);
|
let mut sysbus = SysBus::new(bios_bin);
|
||||||
let mut core = cpu::Core::new();
|
let mut core = cpu::Core::new();
|
||||||
|
core.set_verbose(true);
|
||||||
let mut debugger = Debugger::new(core, sysbus);
|
let mut debugger = Debugger::new(core, sysbus);
|
||||||
|
|
||||||
println!("starting debugger...");
|
println!("starting debugger...");
|
||||||
|
|
Reference in a new issue