2019-06-25 03:16:14 +01:00
|
|
|
use std::convert::TryFrom;
|
2019-06-25 03:35:52 +01:00
|
|
|
use std::fmt;
|
2019-06-25 03:16:14 +01:00
|
|
|
|
2019-06-27 13:13:38 +01:00
|
|
|
use crate::num_traits::FromPrimitive;
|
2019-06-26 22:45:53 +01:00
|
|
|
use colored::*;
|
2019-06-25 00:10:09 +01:00
|
|
|
|
|
|
|
use super::arm::*;
|
2019-06-28 09:46:36 +01:00
|
|
|
pub use super::exception::Exception;
|
2019-06-27 13:13:38 +01:00
|
|
|
use super::psr::RegPSR;
|
|
|
|
use super::reg_string;
|
2019-06-25 00:10:09 +01:00
|
|
|
use super::sysbus::SysBus;
|
|
|
|
|
2019-06-25 03:35:52 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum CpuInstruction {
|
|
|
|
Arm(ArmInstruction),
|
|
|
|
Thumb,
|
|
|
|
}
|
|
|
|
|
2019-06-27 13:13:38 +01:00
|
|
|
#[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"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-25 00:10:09 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum CpuError {
|
2019-06-25 03:16:14 +01:00
|
|
|
ArmDecodeError(ArmDecodeError),
|
2019-06-28 09:46:36 +01:00
|
|
|
IllegalInstruction,
|
2019-06-25 03:35:52 +01:00
|
|
|
UnimplementedCpuInstruction(CpuInstruction),
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
|
2019-06-25 03:16:14 +01:00
|
|
|
impl From<ArmDecodeError> for CpuError {
|
|
|
|
fn from(e: ArmDecodeError) -> CpuError {
|
|
|
|
CpuError::ArmDecodeError(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-25 03:35:52 +01:00
|
|
|
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
|
|
|
|
),
|
2019-06-28 09:46:36 +01:00
|
|
|
CpuError::IllegalInstruction => write!(
|
2019-06-25 03:35:52 +01:00
|
|
|
f,
|
2019-06-28 09:46:36 +01:00
|
|
|
"illegal instruction"
|
2019-06-25 03:35:52 +01:00
|
|
|
),
|
2019-06-26 22:45:53 +01:00
|
|
|
e => write!(f, "error: {:#x?}", e),
|
2019-06-25 03:35:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-25 00:10:09 +01:00
|
|
|
pub type CpuResult<T> = Result<T, CpuError>;
|
|
|
|
|
|
|
|
pub struct CpuModeContext {
|
|
|
|
// r8-r14
|
|
|
|
banked_gpr: [u32; 7],
|
|
|
|
spsr: u32,
|
|
|
|
}
|
|
|
|
|
2019-06-27 13:13:38 +01:00
|
|
|
#[derive(Debug, Default)]
|
2019-06-25 00:10:09 +01:00
|
|
|
pub struct Core {
|
2019-06-25 03:35:52 +01:00
|
|
|
pub pc: u32,
|
2019-06-25 00:10:09 +01:00
|
|
|
// r0-r7
|
2019-06-27 13:13:38 +01:00
|
|
|
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],
|
|
|
|
|
2019-06-26 22:45:53 +01:00
|
|
|
pub cpsr: RegPSR,
|
2019-06-27 13:13:38 +01:00
|
|
|
pub spsr: [RegPSR; 5],
|
|
|
|
|
2019-06-26 22:45:53 +01:00
|
|
|
pub verbose: bool,
|
2019-06-25 03:35:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum CpuPipelineAction {
|
2019-06-26 22:48:43 +01:00
|
|
|
AdvanceProgramCounter,
|
2019-06-25 03:35:52 +01:00
|
|
|
Branch,
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
|
2019-06-25 03:35:52 +01:00
|
|
|
pub type CpuExecResult = CpuResult<(CpuInstruction, CpuPipelineAction)>;
|
|
|
|
|
2019-06-25 00:10:09 +01:00
|
|
|
impl Core {
|
|
|
|
pub fn new() -> Core {
|
|
|
|
Core {
|
2019-06-27 13:13:38 +01:00
|
|
|
..Default::default()
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-25 03:35:52 +01:00
|
|
|
pub fn set_verbose(&mut self, v: bool) {
|
|
|
|
self.verbose = v;
|
|
|
|
}
|
|
|
|
|
2019-06-25 00:10:09 +01:00
|
|
|
pub fn get_reg(&self, reg_num: usize) -> u32 {
|
|
|
|
match reg_num {
|
2019-06-26 22:45:53 +01:00
|
|
|
0...14 => self.gpr[reg_num],
|
2019-06-25 00:10:09 +01:00
|
|
|
15 => self.pc,
|
2019-06-27 13:13:38 +01:00
|
|
|
_ => panic!("invalid register"),
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_reg(&mut self, reg_num: usize, val: u32) {
|
|
|
|
match reg_num {
|
2019-06-26 22:45:53 +01:00
|
|
|
0...14 => self.gpr[reg_num] = val,
|
2019-06-25 00:10:09 +01:00
|
|
|
15 => self.pc = val,
|
2019-06-27 13:13:38 +01:00
|
|
|
_ => panic!("invalid register"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
2019-06-27 13:13:38 +01:00
|
|
|
self.map_banked_registers(curr_mode, new_mode);
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Resets the cpu
|
|
|
|
pub fn reset(&mut self) {
|
2019-06-27 13:13:38 +01:00
|
|
|
self.exception(Exception::Reset);
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn word_size(&self) -> usize {
|
2019-06-26 22:45:53 +01:00
|
|
|
match self.cpsr.state() {
|
2019-06-25 00:10:09 +01:00
|
|
|
CpuState::ARM => 4,
|
|
|
|
CpuState::THUMB => 2,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn advance_pc(&mut self) {
|
|
|
|
self.pc = self.pc.wrapping_add(self.word_size() as u32)
|
|
|
|
}
|
|
|
|
|
2019-06-25 03:35:52 +01:00
|
|
|
fn step_arm(&mut self, sysbus: &mut SysBus) -> CpuExecResult {
|
2019-06-25 03:16:14 +01:00
|
|
|
// fetch
|
|
|
|
let insn = sysbus.read_32(self.pc);
|
|
|
|
// decode
|
|
|
|
let insn = ArmInstruction::try_from((insn, self.pc))?;
|
2019-06-25 03:35:52 +01:00
|
|
|
// exec
|
|
|
|
self.exec_arm(sysbus, insn)
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn step(&mut self, sysbus: &mut SysBus) -> CpuResult<()> {
|
2019-06-26 22:45:53 +01:00
|
|
|
let (executed_insn, pipeline_action) = match self.cpsr.state() {
|
2019-06-25 03:35:52 +01:00
|
|
|
CpuState::ARM => self.step_arm(sysbus),
|
2019-06-25 00:10:09 +01:00
|
|
|
CpuState::THUMB => unimplemented!("thumb not implemented :("),
|
2019-06-25 03:35:52 +01:00
|
|
|
}?;
|
|
|
|
|
|
|
|
if self.verbose {
|
|
|
|
if let CpuInstruction::Arm(insn) = executed_insn {
|
|
|
|
println!("{:8x}:\t{:08x} \t{}", insn.pc, insn.raw, insn)
|
|
|
|
}
|
|
|
|
}
|
2019-06-25 00:10:09 +01:00
|
|
|
|
2019-06-26 22:48:43 +01:00
|
|
|
if CpuPipelineAction::AdvanceProgramCounter == pipeline_action {
|
2019-06-25 03:35:52 +01:00
|
|
|
self.advance_pc();
|
|
|
|
}
|
2019-06-25 00:10:09 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2019-06-26 22:45:53 +01:00
|
|
|
|
|
|
|
impl fmt::Display for Core {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
writeln!(f, "ARM7TDMI Core")?;
|
|
|
|
writeln!(f, "REGISTERS:")?;
|
|
|
|
for i in 0..16 {
|
|
|
|
let mut reg = reg_string(i).to_string();
|
|
|
|
reg.make_ascii_uppercase();
|
|
|
|
writeln!(f, "\t{}\t= 0x{:08x}", reg.bright_yellow(), self.get_reg(i))?;
|
|
|
|
}
|
|
|
|
write!(f, "CPSR: {}", self.cpsr)
|
|
|
|
}
|
|
|
|
}
|