Mega commit - model CPU pipelining.

I except many bugs to arise..


Former-commit-id: bcc6ea57af803f783b0dd548b50956b3ccda2b1a
This commit is contained in:
Michel Heily 2019-06-29 01:52:10 +03:00
parent 4c75970512
commit 967ccca8dd
14 changed files with 351 additions and 224 deletions

1
Cargo.lock generated
View file

@ -413,6 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "rustboyadvance-ng" name = "rustboyadvance-ng"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "bit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -13,4 +13,8 @@ clap = {version = "2.33", features = ["color", "yaml"]}
rustyline = "5.0.0" rustyline = "5.0.0"
nom = "5.0.0" nom = "5.0.0"
colored = "1.8" colored = "1.8"
ansi_term = "0.11.0"
hexdump = "0.1.0" hexdump = "0.1.0"
[profile.dev]
opt-level = 0

View file

@ -108,7 +108,7 @@ impl ArmInstruction {
"b{link}{cond}\t{ofs:#x}", "b{link}{cond}\t{ofs:#x}",
link = if self.link_flag() { "l" } else { "" }, link = if self.link_flag() { "l" } else { "" },
cond = self.cond, cond = self.cond,
ofs = self.pc.wrapping_add(self.branch_offset() as u32) as u32 ofs = 8 + self.pc.wrapping_add(self.branch_offset() as u32) as u32
) )
} }

View file

@ -1,54 +1,32 @@
use crate::bit::BitIndex; use crate::bit::BitIndex;
use crate::arm7tdmi; use super::super::{
use arm7tdmi::cpu::{ cpu::{Core, CpuExecResult, CpuPipelineAction},
Core, CpuError, CpuExecResult, CpuInstruction, CpuPipelineAction, CpuResult, CpuState, exception::Exception,
Exception, sysbus::SysBus,
CpuError, CpuInstruction, CpuResult, CpuState, REG_PC,
}; };
use super::super::sysbus::SysBus;
use super::{ use super::{
ArmCond, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmRegisterShift, ArmShiftType, ArmCond, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmRegisterShift, ArmShiftType,
ArmShiftedValue, ArmShiftedValue,
}; };
impl Core { impl Core {
fn check_arm_cond(&self, cond: ArmCond) -> bool {
use ArmCond::*;
match cond {
Equal => self.cpsr.Z(),
NotEqual => !self.cpsr.Z(),
UnsignedHigherOrSame => self.cpsr.C(),
UnsignedLower => !self.cpsr.C(),
Negative => self.cpsr.N(),
PositiveOrZero => !self.cpsr.N(),
Overflow => self.cpsr.V(),
NoOverflow => !self.cpsr.V(),
UnsignedHigher => self.cpsr.C() && !self.cpsr.Z(),
UnsignedLowerOrSame => !self.cpsr.C() && self.cpsr.Z(),
GreaterOrEqual => self.cpsr.N() == self.cpsr.V(),
LessThan => self.cpsr.N() != self.cpsr.V(),
GreaterThan => !self.cpsr.Z() && (self.cpsr.N() == self.cpsr.V()),
LessThanOrEqual => self.cpsr.Z() || (self.cpsr.N() != self.cpsr.V()),
Always => true,
}
}
pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult { pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
let action = if self.check_arm_cond(insn.cond) { if !self.check_arm_cond(insn.cond) {
return Ok(CpuPipelineAction::IncPC);
}
match insn.fmt { match insn.fmt {
ArmInstructionFormat::BX => self.exec_bx(sysbus, insn), ArmInstructionFormat::BX => self.exec_bx(sysbus, insn),
ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn), ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn),
ArmInstructionFormat::DP => self.exec_data_processing(sysbus, insn), ArmInstructionFormat::DP => self.exec_data_processing(sysbus, insn),
ArmInstructionFormat::SWI => self.exec_swi(sysbus, insn), ArmInstructionFormat::SWI => self.exec_swi(sysbus, insn),
ArmInstructionFormat::LDR_STR => self.exec_ldr_str(sysbus, insn),
_ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm( _ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(
insn, insn,
))), ))),
} }
} else {
Ok(CpuPipelineAction::AdvanceProgramCounter)
}?;
Ok((CpuInstruction::Arm(insn), action))
} }
fn exec_b_bl( fn exec_b_bl(
@ -57,13 +35,12 @@ impl Core {
insn: ArmInstruction, insn: ArmInstruction,
) -> CpuResult<CpuPipelineAction> { ) -> CpuResult<CpuPipelineAction> {
if self.verbose && insn.cond != ArmCond::Always { if self.verbose && insn.cond != ArmCond::Always {
println!("branch taken!")
} }
if insn.link_flag() { if insn.link_flag() {
self.set_reg(14, self.pc & !0b1); self.set_reg(14, self.pc & !0b1);
} }
self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32; self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32;
Ok(CpuPipelineAction::Branch) Ok(CpuPipelineAction::Flush)
} }
fn exec_bx( fn exec_bx(
@ -78,7 +55,8 @@ impl Core {
self.cpsr.set_state(CpuState::ARM); self.cpsr.set_state(CpuState::ARM);
} }
Ok(CpuPipelineAction::Branch) self.pc = rn & !1;
Ok(CpuPipelineAction::Flush)
} }
fn exec_swi( fn exec_swi(
@ -87,7 +65,7 @@ impl Core {
_insn: ArmInstruction, _insn: ArmInstruction,
) -> CpuResult<CpuPipelineAction> { ) -> CpuResult<CpuPipelineAction> {
self.exception(Exception::SoftwareInterrupt); self.exception(Exception::SoftwareInterrupt);
Ok(CpuPipelineAction::Branch) Ok(CpuPipelineAction::Flush)
} }
fn barrel_shift(val: i32, amount: u32, shift: ArmShiftType) -> i32 { fn barrel_shift(val: i32, amount: u32, shift: ArmShiftType) -> i32 {
@ -106,7 +84,7 @@ impl Core {
Ok(Core::barrel_shift(val, amount, shift)) Ok(Core::barrel_shift(val, amount, shift))
} }
ArmRegisterShift::ShiftRegister(reg, shift) => { ArmRegisterShift::ShiftRegister(reg, shift) => {
if reg != arm7tdmi::REG_PC { if reg != REG_PC {
Ok(Core::barrel_shift(val, self.get_reg(reg) & 0xff, shift)) Ok(Core::barrel_shift(val, self.get_reg(reg) & 0xff, shift))
} else { } else {
Err(CpuError::IllegalInstruction) Err(CpuError::IllegalInstruction)
@ -189,6 +167,16 @@ impl Core {
self.set_reg(insn.rd(), result as u32) self.set_reg(insn.rd(), result as u32)
} }
Ok(CpuPipelineAction::AdvanceProgramCounter) Ok(CpuPipelineAction::IncPC)
}
fn exec_ldr_str(
&mut self,
_sysbus: &mut SysBus,
insn: ArmInstruction,
) -> CpuResult<CpuPipelineAction> {
let rn = self.get_reg(insn.rn());
let rd = self.get_reg(insn.rd());
Ok(CpuPipelineAction::IncPC)
} }
} }

View file

@ -295,7 +295,7 @@ impl ArmInstruction {
} }
pub fn branch_offset(&self) -> i32 { pub fn branch_offset(&self) -> i32 {
(((self.raw.bit_range(0..24) << 8) as i32) >> 6).wrapping_add(8) (((self.raw.bit_range(0..24) << 8) as i32) >> 6)
} }
pub fn load_flag(&self) -> bool { pub fn load_flag(&self) -> bool {
@ -451,20 +451,20 @@ mod tests {
assert_eq!(decoded.fmt, ArmInstructionFormat::B_BL); assert_eq!(decoded.fmt, ArmInstructionFormat::B_BL);
assert_eq!(decoded.link_flag(), false); assert_eq!(decoded.link_flag(), false);
assert_eq!( assert_eq!(
(decoded.pc as i32).wrapping_add(decoded.branch_offset()), (decoded.pc as i32).wrapping_add(decoded.branch_offset()) + 8,
0x30 0x30
); );
assert_eq!(format!("{}", decoded), "b\t0x30"); assert_eq!(format!("{}", decoded), "b\t0x30");
} }
#[test] #[test]
fn test_decode_branch_backwards() { fn test_decode_branch_link_backwards() {
// 0x20: bl 0x10 // 0x20: bl 0x10
let decoded = ArmInstruction::try_from((0xeb_ff_ff_fa, 0x20)).unwrap(); let decoded = ArmInstruction::try_from((0xeb_ff_ff_fa, 0x20)).unwrap();
assert_eq!(decoded.fmt, ArmInstructionFormat::B_BL); assert_eq!(decoded.fmt, ArmInstructionFormat::B_BL);
assert_eq!(decoded.link_flag(), true); assert_eq!(decoded.link_flag(), true);
assert_eq!( assert_eq!(
(decoded.pc as i32).wrapping_add(decoded.branch_offset()), (decoded.pc as i32).wrapping_add(decoded.branch_offset()) + 8,
0x10 0x10
); );
assert_eq!(format!("{}", decoded), "bl\t0x10"); assert_eq!(format!("{}", decoded), "bl\t0x10");

View file

@ -1,135 +1,40 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use crate::num_traits::FromPrimitive; use ansi_term::{Colour, Style};
use colored::*;
use super::*;
use super::arm::*;
pub use super::exception::Exception; pub use super::exception::Exception;
use super::psr::RegPSR; use super::psr::RegPSR;
use super::reg_string; use super::reg_string;
use super::sysbus::SysBus; use super::sysbus::SysBus;
#[derive(Debug, PartialEq)] type Addr = u32;
pub enum CpuInstruction {
Arm(ArmInstruction), #[derive(Debug, Default)]
Thumb, pub struct PipelineContext {
fetched: Option<(Addr, u32)>,
decoded: Option<ArmInstruction>,
} }
#[derive(Debug, PartialEq, Primitive, Copy, Clone)] impl PipelineContext {
#[repr(u8)] fn is_flushed(&self) -> bool {
pub enum CpuState { self.fetched.is_none() && self.decoded.is_none()
ARM = 0,
THUMB = 1,
} }
impl fmt::Display for CpuState { fn is_only_fetched(&self) -> bool {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.fetched.is_some() && self.decoded.is_none()
use CpuState::*;
match self {
ARM => write!(f, "ARM"),
THUMB => write!(f, "THUMB"),
}
}
} }
#[derive(Debug, Primitive, Copy, Clone, PartialEq)] fn is_ready_to_execute(&self) -> bool {
pub enum CpuMode { self.fetched.is_some() && self.decoded.is_some()
User = 0b10000,
Fiq = 0b10001,
Irq = 0b10010,
Supervisor = 0b10011,
Abort = 0b10111,
Undefined = 0b11011,
System = 0b11111,
} }
impl CpuMode {
pub fn spsr_index(&self) -> Option<usize> {
match self {
CpuMode::Fiq => Some(0),
CpuMode::Irq => Some(1),
CpuMode::Supervisor => Some(2),
CpuMode::Abort => Some(3),
CpuMode::Undefined => Some(4),
_ => None,
}
}
pub fn bank_index(&self) -> usize {
match self {
CpuMode::User | CpuMode::System => 0,
CpuMode::Fiq => 1,
CpuMode::Irq => 2,
CpuMode::Supervisor => 3,
CpuMode::Abort => 4,
CpuMode::Undefined => 5,
}
}
}
impl fmt::Display for CpuMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CpuMode::*;
match self {
User => write!(f, "USR"),
Fiq => write!(f, "FIQ"),
Irq => write!(f, "IRQ"),
Supervisor => write!(f, "SVC"),
Abort => write!(f, "ABT"),
Undefined => write!(f, "UND"),
System => write!(f, "SYS"),
}
}
}
#[derive(Debug, PartialEq)]
pub enum CpuError {
ArmDecodeError(ArmDecodeError),
IllegalInstruction,
UnimplementedCpuInstruction(CpuInstruction),
}
impl From<ArmDecodeError> for CpuError {
fn from(e: ArmDecodeError) -> CpuError {
CpuError::ArmDecodeError(e)
}
}
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 => write!(
f,
"illegal instruction"
),
e => write!(f, "error: {:#x?}", e),
}
}
}
pub type CpuResult<T> = Result<T, CpuError>;
pub struct CpuModeContext {
// r8-r14
banked_gpr: [u32; 7],
spsr: u32,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Core { pub struct Core {
pub pc: u32, pub pc: u32,
// r0-r7
pub gpr: [u32; 15], pub gpr: [u32; 15],
// r13 and r14 are banked for all modes. System&User mode share them // r13 and r14 are banked for all modes. System&User mode share them
pub gpr_banked_r13: [u32; 6], pub gpr_banked_r13: [u32; 6],
@ -141,16 +46,22 @@ pub struct Core {
pub cpsr: RegPSR, pub cpsr: RegPSR,
pub spsr: [RegPSR; 5], pub spsr: [RegPSR; 5],
pub pipeline: PipelineContext,
cycles: usize,
// store the gpr before executing an instruction to show diff in the Display impl
gpr_previous: [u32; 15],
pub verbose: bool, pub verbose: bool,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum CpuPipelineAction { pub enum CpuPipelineAction {
AdvanceProgramCounter, IncPC,
Branch, Flush,
} }
pub type CpuExecResult = CpuResult<(CpuInstruction, CpuPipelineAction)>; pub type CpuExecResult = CpuResult<CpuPipelineAction>;
impl Core { impl Core {
pub fn new() -> Core { pub fn new() -> Core {
@ -179,6 +90,10 @@ impl Core {
} }
} }
pub fn get_registers(&self) -> [u32; 15] {
self.gpr.clone()
}
fn map_banked_registers(&mut self, curr_mode: CpuMode, new_mode: CpuMode) { fn map_banked_registers(&mut self, curr_mode: CpuMode, new_mode: CpuMode) {
let next_index = new_mode.bank_index(); let next_index = new_mode.bank_index();
let curr_index = curr_mode.bank_index(); let curr_index = curr_mode.bank_index();
@ -217,7 +132,7 @@ impl Core {
self.exception(Exception::Reset); self.exception(Exception::Reset);
} }
fn word_size(&self) -> usize { pub fn word_size(&self) -> usize {
match self.cpsr.state() { match self.cpsr.state() {
CpuState::ARM => 4, CpuState::ARM => 4,
CpuState::THUMB => 2, CpuState::THUMB => 2,
@ -228,44 +143,133 @@ 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) -> CpuExecResult { pub fn check_arm_cond(&self, cond: ArmCond) -> bool {
// fetch use ArmCond::*;
let insn = sysbus.read_32(self.pc); match cond {
// decode Equal => self.cpsr.Z(),
let insn = ArmInstruction::try_from((insn, self.pc))?; NotEqual => !self.cpsr.Z(),
// exec UnsignedHigherOrSame => self.cpsr.C(),
self.exec_arm(sysbus, insn) UnsignedLower => !self.cpsr.C(),
Negative => self.cpsr.N(),
PositiveOrZero => !self.cpsr.N(),
Overflow => self.cpsr.V(),
NoOverflow => !self.cpsr.V(),
UnsignedHigher => self.cpsr.C() && !self.cpsr.Z(),
UnsignedLowerOrSame => !self.cpsr.C() && self.cpsr.Z(),
GreaterOrEqual => self.cpsr.N() == self.cpsr.V(),
LessThan => self.cpsr.N() != self.cpsr.V(),
GreaterThan => !self.cpsr.Z() && (self.cpsr.N() == self.cpsr.V()),
LessThanOrEqual => self.cpsr.Z() || (self.cpsr.N() != self.cpsr.V()),
Always => true,
}
} }
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> { fn step_arm(
let (executed_insn, pipeline_action) = match self.cpsr.state() { &mut self,
sysbus: &mut SysBus,
) -> CpuResult<(Option<ArmInstruction>, CpuPipelineAction)> {
// fetch
let new_fetched = sysbus.read_32(self.pc);
// decode
let new_decoded = match self.pipeline.fetched {
Some((addr, i)) => Some(ArmInstruction::try_from((i, addr)).unwrap()),
None => None,
};
// exec
let result = match self.pipeline.decoded {
Some(d) => {
self.gpr_previous = self.get_registers();
let action = self.exec_arm(sysbus, d)?;
Ok((Some(d), action))
}
None => Ok((None, CpuPipelineAction::IncPC)),
};
self.pipeline.fetched = Some((self.pc, new_fetched));
if let Some(d) = new_decoded {
self.pipeline.decoded = Some(d);
}
result
}
/// 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>> {
if self.cycles > 0 {
self.cycles -= 1;
return Ok(None);
}
let (executed_instruction, pipeline_action) = match self.cpsr.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 { match pipeline_action {
if let CpuInstruction::Arm(insn) = executed_insn { CpuPipelineAction::IncPC => self.advance_pc(),
println!("{:8x}:\t{:08x} \t{}", insn.pc, insn.raw, insn) CpuPipelineAction::Flush => {
self.pipeline.fetched = None;
self.pipeline.decoded = None;
} }
} }
if CpuPipelineAction::AdvanceProgramCounter == pipeline_action { Ok(executed_instruction)
self.advance_pc();
} }
Ok(()) /// 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
} 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!()
}
}
/// 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> {
loop {
if let Some(i) = self.step(sysbus)? {
return Ok(i);
}
}
} }
} }
impl fmt::Display for Core { impl fmt::Display for Core {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "ARM7TDMI Core")?; writeln!(f, "ARM7TDMI Core Status:")?;
writeln!(f, "REGISTERS:")?; writeln!(f, "\tCPSR: {}", self.cpsr)?;
for i in 0..16 { writeln!(f, "\tGeneral Purpose Registers:")?;
let mut reg = reg_string(i).to_string(); let reg_normal_style = Style::new().bold();
reg.make_ascii_uppercase(); let reg_dirty_style = Colour::Green.bold().on(Colour::Yellow);
writeln!(f, "\t{}\t= 0x{:08x}", reg.bright_yellow(), self.get_reg(i))?; let gpr = self.get_registers();
for i in 0..15 {
let mut reg_name = reg_string(i).to_string();
reg_name.make_ascii_uppercase();
let style = if gpr[i] != self.gpr_previous[i] {
&reg_dirty_style
} else {
&reg_normal_style
};
let entry = format!("\t{}\t= 0x{:08x}", reg_name, gpr[i]);
write!(
f,
"{}{}",
style.paint(entry),
if (i + 1) % 4 == 0 { "\n" } else { "" }
)?;
} }
write!(f, "CPSR: {}", self.cpsr) let pc = format!("\tPC\t= 0x{:08x}", self.pc);
writeln!(f, "{}", reg_normal_style.paint(pc))
} }
} }

View file

@ -1,4 +1,4 @@
use super::cpu::{Core, CpuMode, CpuState}; use super::{cpu::Core, CpuMode, CpuState};
use colored::*; use colored::*;

View file

@ -1,4 +1,10 @@
use std::fmt;
use crate::num_traits::FromPrimitive;
pub mod arm; pub mod arm;
use arm::*;
pub mod cpu; pub mod cpu;
mod exception; mod exception;
mod psr; mod psr;
@ -17,6 +23,119 @@ pub fn reg_string(reg: usize) -> &'static str {
reg_names[reg] reg_names[reg]
} }
#[derive(Debug, PartialEq)]
pub enum CpuInstruction {
Arm(ArmInstruction),
Thumb,
}
#[derive(Debug, PartialEq, Primitive, Copy, Clone)]
#[repr(u8)]
pub enum CpuState {
ARM = 0,
THUMB = 1,
}
impl fmt::Display for CpuState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CpuState::*;
match self {
ARM => write!(f, "ARM"),
THUMB => write!(f, "THUMB"),
}
}
}
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
pub enum CpuMode {
User = 0b10000,
Fiq = 0b10001,
Irq = 0b10010,
Supervisor = 0b10011,
Abort = 0b10111,
Undefined = 0b11011,
System = 0b11111,
}
impl CpuMode {
pub fn spsr_index(&self) -> Option<usize> {
match self {
CpuMode::Fiq => Some(0),
CpuMode::Irq => Some(1),
CpuMode::Supervisor => Some(2),
CpuMode::Abort => Some(3),
CpuMode::Undefined => Some(4),
_ => None,
}
}
pub fn bank_index(&self) -> usize {
match self {
CpuMode::User | CpuMode::System => 0,
CpuMode::Fiq => 1,
CpuMode::Irq => 2,
CpuMode::Supervisor => 3,
CpuMode::Abort => 4,
CpuMode::Undefined => 5,
}
}
}
impl fmt::Display for CpuMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use CpuMode::*;
match self {
User => write!(f, "USR"),
Fiq => write!(f, "FIQ"),
Irq => write!(f, "IRQ"),
Supervisor => write!(f, "SVC"),
Abort => write!(f, "ABT"),
Undefined => write!(f, "UND"),
System => write!(f, "SYS"),
}
}
}
#[derive(Debug, PartialEq)]
pub enum CpuError {
ArmDecodeError(ArmDecodeError),
IllegalInstruction,
UnimplementedCpuInstruction(CpuInstruction),
}
impl From<ArmDecodeError> for CpuError {
fn from(e: ArmDecodeError) -> CpuError {
CpuError::ArmDecodeError(e)
}
}
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 => write!(f, "illegal instruction"),
e => write!(f, "error: {:#x?}", e),
}
}
}
pub type CpuResult<T> = Result<T, CpuError>;
pub struct CpuModeContext {
// r8-r14
banked_gpr: [u32; 7],
spsr: u32,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]

View file

@ -4,7 +4,7 @@ use std::fmt;
use crate::bit::BitIndex; use crate::bit::BitIndex;
use crate::num_traits::FromPrimitive; use crate::num_traits::FromPrimitive;
use super::cpu::{CpuMode, CpuState}; use super::{CpuMode, CpuState};
use colored::*; use colored::*;

View file

@ -1,13 +1,16 @@
use crate::arm7tdmi::{reg_string, REG_PC};
use crate::debugger::Debugger; use crate::debugger::Debugger;
use crate::disass::Disassembler; use crate::disass::Disassembler;
use ansi_term::Colour;
use colored::*; use colored::*;
use hexdump; use hexdump;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Command { pub enum Command {
Info, Info,
SingleStep, SingleStep(bool),
Continue, Continue,
HexDump(u32, usize), HexDump(u32, usize),
Disass(u32, usize), Disass(u32, usize),
@ -24,16 +27,24 @@ impl Command {
use Command::*; use Command::*;
match *self { match *self {
Info => println!("{}", debugger.cpu), Info => println!("{}", debugger.cpu),
SingleStep => { SingleStep(_cycle) => {
if let Some(bp) = debugger.check_breakpoint() { if let Some(bp) = debugger.check_breakpoint() {
println!("hit breakpoint #0x{:08x}!", bp); println!("hit breakpoint #0x{:08x}!", bp);
debugger.delete_breakpoint(bp); debugger.delete_breakpoint(bp);
} else { } else {
match debugger.cpu.step(&mut debugger.sysbus) { match debugger.cpu.step_debugger(&mut debugger.sysbus) {
Ok(_) => (), Ok(insn) => {
println!("{}\n", debugger.cpu);
println!(
"Executed at @0x{:08x}:\n\t{}",
insn.pc,
Colour::Yellow.italic().paint(format!("{} ", insn))
);
println!("Next instruction at @0x{:08x}", debugger.cpu.get_next_pc())
}
Err(e) => { Err(e) => {
println!("{}: {}", "cpu encountered an error".red(), e); println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:#x?}", debugger.cpu) println!("cpu: {:x?}", debugger.cpu)
} }
} }
} }
@ -44,11 +55,11 @@ impl Command {
debugger.delete_breakpoint(bp); debugger.delete_breakpoint(bp);
break; break;
} }
match debugger.cpu.step(&mut debugger.sysbus) { match debugger.cpu.step_debugger(&mut debugger.sysbus) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
println!("{}: {}", "cpu encountered an error".red(), e); println!("{}: {}", "cpu encountered an error".red(), e);
println!("cpu: {:#x?}", debugger.cpu); println!("cpu: {:x?}", debugger.cpu);
break; break;
} }
}; };
@ -72,7 +83,7 @@ impl Command {
} }
}; };
let disass = Disassembler::new(addr, bytes); let disass = Disassembler::new(addr, bytes);
for line in disass { for (_, line) in disass {
println!("{}", line) println!("{}", line)
} }
} }

View file

@ -3,7 +3,7 @@ use rustyline::Editor;
use colored::*; use colored::*;
use super::arm7tdmi::cpu; use super::arm7tdmi;
use super::sysbus::SysBus; use super::sysbus::SysBus;
mod parser; mod parser;
@ -15,14 +15,14 @@ use command::Command;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DebuggerError { pub enum DebuggerError {
ParsingError(String), ParsingError(String),
CpuError(cpu::CpuError), CpuError(arm7tdmi::CpuError),
InvalidCommand(String), InvalidCommand(String),
InvalidArgument(String), InvalidArgument(String),
InvalidCommandFormat(String), InvalidCommandFormat(String),
} }
impl From<cpu::CpuError> for DebuggerError { impl From<arm7tdmi::CpuError> for DebuggerError {
fn from(e: cpu::CpuError) -> DebuggerError { fn from(e: arm7tdmi::CpuError) -> DebuggerError {
DebuggerError::CpuError(e) DebuggerError::CpuError(e)
} }
} }
@ -31,7 +31,7 @@ type DebuggerResult<T> = Result<T, DebuggerError>;
#[derive(Debug)] #[derive(Debug)]
pub struct Debugger { pub struct Debugger {
pub cpu: cpu::Core, pub cpu: arm7tdmi::cpu::Core,
pub sysbus: SysBus, pub sysbus: SysBus,
running: bool, running: bool,
breakpoints: Vec<u32>, breakpoints: Vec<u32>,
@ -39,7 +39,7 @@ pub struct Debugger {
} }
impl Debugger { impl Debugger {
pub fn new(cpu: cpu::Core, sysbus: SysBus) -> Debugger { pub fn new(cpu: arm7tdmi::cpu::Core, sysbus: SysBus) -> Debugger {
Debugger { Debugger {
cpu: cpu, cpu: cpu,
sysbus: sysbus, sysbus: sysbus,
@ -50,10 +50,10 @@ impl Debugger {
} }
pub fn check_breakpoint(&self) -> Option<u32> { pub fn check_breakpoint(&self) -> Option<u32> {
let pc = self.cpu.get_reg(15); let next_pc = self.cpu.get_next_pc();
for bp in &self.breakpoints { for bp in &self.breakpoints {
if *bp == pc { if *bp == next_pc {
return Some(pc); return Some(next_pc);
} }
} }
@ -123,7 +123,8 @@ impl Debugger {
match command.as_ref() { match command.as_ref() {
"i" | "info" => Ok(Command::Info), "i" | "info" => Ok(Command::Info),
"s" | "step" => Ok(Command::SingleStep), "s" | "step" => Ok(Command::SingleStep(false)),
"sc" | "stepcycle" => Ok(Command::SingleStep(true)),
"c" | "continue" => Ok(Command::Continue), "c" | "continue" => Ok(Command::Continue),
"x" | "hexdump" => { "x" | "hexdump" => {
let (addr, n) = match args.len() { let (addr, n) = match args.len() {
@ -255,7 +256,7 @@ impl Debugger {
let mut rl = Editor::<()>::new(); let mut rl = Editor::<()>::new();
rl.load_history(".rustboyadvance_history"); rl.load_history(".rustboyadvance_history");
while self.running { while self.running {
let readline = rl.readline(&format!("({}) >> ", "rustboyadvance-dbg".bold().cyan())); let readline = rl.readline(&format!("({}) ", "rustboyadvance-dbg".bold().cyan()));
match readline { match readline {
Ok(line) => { Ok(line) => {
if line.is_empty() { if line.is_empty() {

View file

@ -4,7 +4,7 @@ use nom;
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::{tag, take_while_m_n}; use nom::bytes::complete::{tag, take_while_m_n};
use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1}; use nom::character::complete::{alphanumeric1, char, digit1, multispace0, multispace1};
use nom::combinator::{map, map_res, cut}; use nom::combinator::{cut, map, map_res};
use nom::error::{context, convert_error, ParseError, VerboseError}; use nom::error::{context, convert_error, ParseError, VerboseError};
use nom::multi::separated_list; use nom::multi::separated_list;
use nom::sequence::{preceded, separated_pair, terminated, tuple}; use nom::sequence::{preceded, separated_pair, terminated, tuple};

View file

@ -28,7 +28,7 @@ impl<'a> Seek for Disassembler<'a> {
} }
impl<'a> Iterator for Disassembler<'a> { impl<'a> Iterator for Disassembler<'a> {
type Item = String; type Item = (u32, String);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let mut line = String::new(); let mut line = String::new();
@ -50,6 +50,6 @@ impl<'a> Iterator for Disassembler<'a> {
Err(_) => line.push_str("<UNDEFINED>"), Err(_) => line.push_str("<UNDEFINED>"),
}; };
Some(line) Some((addr, line))
} }
} }

View file

@ -19,13 +19,12 @@ extern crate rustyline;
extern crate nom; extern crate nom;
extern crate colored; // not needed in Rust 2018 extern crate colored; // not needed in Rust 2018
extern crate ansi_term;
pub mod sysbus; pub mod sysbus;
use sysbus::SysBus; use sysbus::SysBus;
mod arm7tdmi; mod arm7tdmi;
use arm7tdmi::arm;
use arm7tdmi::cpu;
mod debugger; mod debugger;
use debugger::{Debugger, DebuggerError}; use debugger::{Debugger, DebuggerError};
@ -36,8 +35,8 @@ use disass::Disassembler;
#[derive(Debug)] #[derive(Debug)]
pub enum GBAError { pub enum GBAError {
IO(io::Error), IO(io::Error),
ArmDecodeError(arm::ArmDecodeError), ArmDecodeError(arm7tdmi::arm::ArmDecodeError),
CpuError(cpu::CpuError), CpuError(arm7tdmi::CpuError),
DebuggerError(DebuggerError) DebuggerError(DebuggerError)
} }
@ -49,14 +48,14 @@ impl From<io::Error> for GBAError {
} }
} }
impl From<arm::ArmDecodeError> for GBAError { impl From<arm7tdmi::arm::ArmDecodeError> for GBAError {
fn from(err: arm::ArmDecodeError) -> GBAError { fn from(err: arm7tdmi::arm::ArmDecodeError) -> GBAError {
GBAError::ArmDecodeError(err) GBAError::ArmDecodeError(err)
} }
} }
impl From<cpu::CpuError> for GBAError { impl From<arm7tdmi::CpuError> for GBAError {
fn from(err: cpu::CpuError) -> GBAError { fn from(err: arm7tdmi::CpuError) -> GBAError {
GBAError::CpuError(err) GBAError::CpuError(err)
} }
} }
@ -80,7 +79,7 @@ fn run_disass(matches: &ArgMatches) -> GBAResult<()> {
let bin = read_bin_file(&input)?; let bin = read_bin_file(&input)?;
let disassembler = Disassembler::new(0, &bin); let disassembler = Disassembler::new(0, &bin);
for line in disassembler { for (_, line) in disassembler {
println!("{}", line) println!("{}", line)
} }
Ok(()) Ok(())
@ -92,7 +91,7 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
let bios_bin = read_bin_file(gba_bios_path)?; let bios_bin = read_bin_file(gba_bios_path)?;
let sysbus = SysBus::new(bios_bin); let sysbus = SysBus::new(bios_bin);
let mut core = cpu::Core::new(); let mut core = arm7tdmi::cpu::Core::new();
core.reset(); core.reset();
core.set_verbose(true); core.set_verbose(true);
let mut debugger = Debugger::new(core, sysbus); let mut debugger = Debugger::new(core, sysbus);