cpu: Model exceptions

This commit is contained in:
Michel Heily 2019-06-27 15:13:38 +03:00
parent fc6410b510
commit 1a0725f1a3
6 changed files with 185 additions and 72 deletions

View file

@ -1,7 +1,6 @@
use super::super::cpu::{ use super::super::cpu::{
Core, CpuError, CpuExecResult, CpuInstruction, CpuPipelineAction, CpuResult, Core, CpuError, CpuExecResult, CpuInstruction, CpuPipelineAction, CpuResult, CpuState
}; };
use super::super::psr::CpuState;
use super::super::sysbus::SysBus; use super::super::sysbus::SysBus;
use super::{ use super::{
ArmCond, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmRegisterShift, ArmShiftType, ArmCond, ArmInstruction, ArmInstructionFormat, ArmOpCode, ArmRegisterShift, ArmShiftType,

View file

@ -1,11 +1,13 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
use crate::num_traits::FromPrimitive;
use colored::*; use colored::*;
use super::reg_string;
use super::arm::*; use super::arm::*;
use super::psr::{CpuMode, CpuState, RegPSR}; use super::exception::Exception;
use super::psr::RegPSR;
use super::reg_string;
use super::sysbus::SysBus; use super::sysbus::SysBus;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -14,6 +16,73 @@ pub enum CpuInstruction {
Thumb, 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)] #[derive(Debug, PartialEq)]
pub enum CpuError { pub enum CpuError {
ArmDecodeError(ArmDecodeError), ArmDecodeError(ArmDecodeError),
@ -58,12 +127,21 @@ pub struct CpuModeContext {
spsr: u32, spsr: u32,
} }
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Core { pub struct Core {
pub pc: u32, pub pc: u32,
// r0-r7 // r0-r7
gpr: [u32; 15], pub gpr: [u32; 15],
// r13 and r14 are banked for all modes. System&User mode share them
pub gpr_banked_r13: [u32; 6],
pub gpr_banked_r14: [u32; 6],
// r8-r12 are banked for fiq mode
pub gpr_banked_old_r8_12: [u32; 5],
pub gpr_banked_fiq_r8_12: [u32; 5],
pub cpsr: RegPSR, pub cpsr: RegPSR,
pub spsr: [RegPSR; 5],
pub verbose: bool, pub verbose: bool,
} }
@ -78,10 +156,7 @@ pub type CpuExecResult = CpuResult<(CpuInstruction, CpuPipelineAction)>;
impl Core { impl Core {
pub fn new() -> Core { pub fn new() -> Core {
Core { Core {
pc: 0, ..Default::default()
gpr: [0; 15],
cpsr: RegPSR::new(),
verbose: false,
} }
} }
@ -93,8 +168,7 @@ impl Core {
match reg_num { match reg_num {
0...14 => self.gpr[reg_num], 0...14 => self.gpr[reg_num],
15 => self.pc, 15 => self.pc,
_ => panic!("invalid register") _ => panic!("invalid register"),
// _ => 0x12345678 // unimplemented!("TODO banked registers"),
} }
} }
@ -102,15 +176,46 @@ impl Core {
match reg_num { match reg_num {
0...14 => self.gpr[reg_num] = val, 0...14 => self.gpr[reg_num] = val,
15 => self.pc = val, 15 => self.pc = val,
_ => panic!("invalid register") _ => panic!("invalid register"),
// _ => unimplemented!("TODO banked registers"),
} }
} }
fn map_banked_registers(&mut self, curr_mode: CpuMode, new_mode: CpuMode) {
let next_index = new_mode.bank_index();
let curr_index = curr_mode.bank_index();
self.gpr_banked_r13[curr_index] = self.gpr[13];
self.gpr[13] = self.gpr_banked_r13[next_index];
self.gpr_banked_r14[curr_index] = self.gpr[14];
self.gpr_banked_r14[next_index] = self.pc; // Store the return address in LR_mode
self.gpr[14] = self.gpr_banked_r14[next_index];
if new_mode == CpuMode::Fiq {
for r in 0..5 {
self.gpr_banked_old_r8_12[r] = self.gpr[r + 8];
self.gpr[r + 8] = self.gpr_banked_fiq_r8_12[r];
}
} else if curr_mode == CpuMode::Fiq {
for r in 0..5 {
self.gpr_banked_fiq_r8_12[r] = self.gpr[r + 8];
self.gpr[r + 8] = self.gpr_banked_old_r8_12[r];
}
}
}
pub fn change_mode(&mut self, new_mode: CpuMode) {
let curr_mode = self.cpsr.mode();
// Copy CPSR to SPSR_mode
if let Some(index) = new_mode.spsr_index() {
self.spsr[index] = self.cpsr;
}
self.map_banked_registers(curr_mode, new_mode);
}
/// Resets the cpu /// Resets the cpu
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.pc = 0; self.exception(Exception::Reset);
self.cpsr.set(0);
} }
fn word_size(&self) -> usize { fn word_size(&self) -> usize {

52
src/arm7tdmi/exception.rs Normal file
View file

@ -0,0 +1,52 @@
use super::cpu::{Core, CpuMode, CpuState};
use colored::*;
#[derive(Debug, Clone, Copy, PartialEq)]
#[allow(dead_code)]
/// Models a CPU exception, and maps to the relavnt entry in the exception vector
pub enum Exception {
Reset = 0x00,
UndefinedInstruction = 0x04,
SoftwareInterrupt = 0x08,
PrefatchAbort = 0x0c,
DataAbort = 0x10,
Reserved = 0x14,
Irq = 0x18,
Fiq = 0x1c,
}
impl From<Exception> for CpuMode {
/// Return cpu mode upon entry
fn from(e: Exception) -> CpuMode {
use Exception::*;
match e {
Reset | SoftwareInterrupt | Reserved => CpuMode::Supervisor,
PrefatchAbort | DataAbort => CpuMode::Abort,
UndefinedInstruction => CpuMode::Undefined,
Irq => CpuMode::Irq,
Fiq => CpuMode::Fiq,
}
}
}
impl Core {
pub fn exception(&mut self, e: Exception) {
let vector = e as u32;
let new_mode = CpuMode::from(e);
if self.verbose {
println!("{}: {:?}, new_mode: {:?}", "Exception".cyan(), e, new_mode);
}
self.change_mode(new_mode);
// Set appropriate CPSR bits
self.cpsr.set_state(CpuState::ARM);
self.cpsr.set_mode(new_mode);
self.cpsr.set_irq_disabled(true);
let disabled_fiq = e == Exception::Reset || e == Exception::Fiq;
self.cpsr.set_fiq_disabled(disabled_fiq);
// Set PC to vector address
self.pc = vector;
}
}

View file

@ -1,6 +1,8 @@
mod psr;
pub mod arm; pub mod arm;
pub mod cpu; pub mod cpu;
mod psr;
mod exception;
pub use super::sysbus; pub use super::sysbus;
pub const REG_PC: usize = 15; pub const REG_PC: usize = 15;

View file

@ -4,27 +4,10 @@ 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 colored::*; use colored::*;
use super::arm::ArmCond;
#[derive(Debug, PartialEq, Primitive)]
#[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"),
}
}
}
impl From<CpuState> for bool { impl From<CpuState> for bool {
fn from(state: CpuState) -> bool { fn from(state: CpuState) -> bool {
match state { match state {
@ -44,33 +27,6 @@ impl From<bool> for CpuState {
} }
} }
#[derive(Debug, Primitive)]
#[repr(u8)]
pub enum CpuMode {
User = 0b10000,
Fiq = 0b10001,
Irq = 0b10010,
Supervisor = 0b10011,
Abort = 0b10111,
Undefined = 0b11011,
System = 0b11111,
}
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, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct RegPSR { pub struct RegPSR {
raw: u32, raw: u32,
@ -81,19 +37,17 @@ fn clear_reserved(n: u32) -> u32 {
n & !RESERVED_BIT_MASK n & !RESERVED_BIT_MASK
} }
impl RegPSR { impl Default for RegPSR {
pub fn new() -> RegPSR { fn default() -> RegPSR {
let mut psr = RegPSR { raw: 0 }; let mut psr = RegPSR {
raw: clear_reserved(0),
psr.set_irq_disabled(true); };
psr.set_fiq_disabled(true);
psr.set_mode(CpuMode::Supervisor); psr.set_mode(CpuMode::Supervisor);
psr.set_state(CpuState::ARM);
println!("RAW: 0x{:08x}", psr.raw);
psr psr
} }
}
impl RegPSR {
pub fn get(&self) -> u32 { pub fn get(&self) -> u32 {
self.raw self.raw
} }

View file

@ -93,6 +93,7 @@ fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
let sysbus = SysBus::new(bios_bin); let sysbus = SysBus::new(bios_bin);
let mut core = cpu::Core::new(); let mut core = cpu::Core::new();
core.reset();
core.set_verbose(true); core.set_verbose(true);
let mut debugger = Debugger::new(core, sysbus); let mut debugger = Debugger::new(core, sysbus);