Mega commit - model CPU pipelining.
I except many bugs to arise.. Former-commit-id: bcc6ea57af803f783b0dd548b50956b3ccda2b1a
This commit is contained in:
parent
4c75970512
commit
967ccca8dd
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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)",
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
match insn.fmt {
|
return Ok(CpuPipelineAction::IncPC);
|
||||||
ArmInstructionFormat::BX => self.exec_bx(sysbus, insn),
|
}
|
||||||
ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn),
|
match insn.fmt {
|
||||||
ArmInstructionFormat::DP => self.exec_data_processing(sysbus, insn),
|
ArmInstructionFormat::BX => self.exec_bx(sysbus, insn),
|
||||||
ArmInstructionFormat::SWI => self.exec_swi(sysbus, insn),
|
ArmInstructionFormat::B_BL => self.exec_b_bl(sysbus, insn),
|
||||||
_ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(
|
ArmInstructionFormat::DP => self.exec_data_processing(sysbus, insn),
|
||||||
insn,
|
ArmInstructionFormat::SWI => self.exec_swi(sysbus, insn),
|
||||||
))),
|
ArmInstructionFormat::LDR_STR => self.exec_ldr_str(sysbus, insn),
|
||||||
}
|
_ => Err(CpuError::UnimplementedCpuInstruction(CpuInstruction::Arm(
|
||||||
} else {
|
insn,
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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 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 {
|
fn is_only_fetched(&self) -> bool {
|
||||||
match self {
|
self.fetched.is_some() && self.decoded.is_none()
|
||||||
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 is_ready_to_execute(&self) -> bool {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
self.fetched.is_some() && self.decoded.is_some()
|
||||||
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] {
|
||||||
|
®_dirty_style
|
||||||
|
} else {
|
||||||
|
®_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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::cpu::{Core, CpuMode, CpuState};
|
use super::{cpu::Core, CpuMode, CpuState};
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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::*;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -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);
|
||||||
|
|
Reference in a new issue