cpu: Model exceptions
This commit is contained in:
parent
fc6410b510
commit
1a0725f1a3
|
@ -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,
|
||||||
|
|
|
@ -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
52
src/arm7tdmi/exception.rs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Reference in a new issue