#![allow(
clippy::cognitive_complexity, // instruction decode methods are large
clippy::many_single_char_names, // ...it's a CPU, what do you expect?
clippy::cast_lossless, // Register types _won't_ be changed in the future
)]
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "advanced_disasm")]
use capstone::prelude::*;
pub mod reg;
mod alignment;
mod arm;
mod example_mem;
mod exception;
mod mode;
mod thumb;
mod util;
pub use crate::exception::Exception;
pub use example_mem::ExampleMem;
pub use mode::Mode;
use crate::alignment::AlignmentWrapper;
use crate::reg::*;
pub trait Memory {
fn r8(&mut self, addr: u32) -> u8;
fn r16(&mut self, addr: u32) -> u16;
fn r32(&mut self, addr: u32) -> u32;
fn w8(&mut self, addr: u32, val: u8);
fn w16(&mut self, addr: u32, val: u16);
fn w32(&mut self, addr: u32, val: u32);
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Cpu {
reg: RegFile,
#[cfg(feature = "advanced_disasm")]
#[cfg_attr(feature = "serde", serde(skip))]
cs: Option<Capstone>,
}
impl std::fmt::Debug for Cpu {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.debug_struct("Cpu").field("reg", &self.reg).finish()
}
}
impl Default for Cpu {
fn default() -> Self {
Self::new()
}
}
impl Cpu {
pub fn new() -> Cpu {
let mut cpu = Cpu {
reg: RegFile::new_empty(),
#[cfg(feature = "advanced_disasm")]
cs: Some(
Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Arm)
.detail(true)
.build()
.unwrap(),
),
};
cpu.reg_set(Mode::User, reg::PC, 0x00);
cpu.reg_set(Mode::User, reg::CPSR, 0xd3);
cpu
}
pub fn step(&mut self, mem: &mut impl Memory) -> bool {
let mut mem = AlignmentWrapper::new(mem);
let decode_ok = if !self.thumb_mode() {
self.execute_arm(&mut mem)
} else {
self.execute_thumb(&mut mem)
};
if !decode_ok {
self.exception(Exception::Undefined)
}
decode_ok
}
pub fn exception(&mut self, exc: Exception) {
match exc {
Exception::Interrupt => {
if !self.irq_enable() {
return;
}
}
Exception::FastInterrupt => {
if !self.fiq_enable() {
return;
}
}
_ => (),
}
let new_mode = exc.mode_on_entry();
let new_bank = new_mode.reg_bank();
let cpsr = self.reg.get(0, reg::CPSR);
let pc = self.reg.get(0, reg::PC);
let new_lr = match exc {
Exception::Interrupt => pc + 4,
Exception::FastInterrupt => pc + 4,
_ => pc,
};
self.reg.set(new_bank, reg::LR, new_lr);
self.reg.set(new_bank, reg::SPSR, cpsr);
self.reg.set(0, reg::PC, exc.address());
#[allow(clippy::identity_op)]
let new_cpsr =
(new_mode.bits() as u32) |
(0 << 5) |
((exc.fiq_disable() as u32) << 6) |
(1 << 7) |
(cpsr & (0xf << 28)) ;
self.reg.set(0, reg::CPSR, new_cpsr);
}
pub fn thumb_mode(&self) -> bool {
(self.reg[reg::CPSR] & (1u32 << cpsr::T)) != 0
}
pub fn reg_set(&mut self, mode: Mode, reg: Reg, val: u32) {
self.reg.set(mode.reg_bank(), reg, val)
}
pub fn reg_get(&self, mode: Mode, reg: Reg) -> u32 {
self.reg.get(mode.reg_bank(), reg)
}
pub fn mode(&self) -> Mode {
self.reg.mode()
}
pub fn irq_enable(&self) -> bool {
self.reg.get(0, reg::CPSR) & (1 << 7) == 0
}
pub fn fiq_enable(&self) -> bool {
self.reg.get(0, reg::CPSR) & (1 << 6) == 0
}
}