This repository has been archived on 2024-12-14. You can view files and clone it, but cannot push or open issues or pull requests.
rustboyadvance-ng/src/core/arm7tdmi/cpu.rs
Michel Heily f0aa671674 optimize/cpu: Pass Arm/Thumb Instruction and other large structs as references.
Also, get rid of unnecessary derive Copy.

Since the ArmInstruction (and Thumb) derived from Copy, the compiler
always saves them to the stack when they are passed by value to other
functions, thus making some unwanted performance overhead.

I removed the derive Copy, and also pass them as references. This
project has a lot of "derive Copy" everywhere, and I should take note if
this is happenning elsewhere aswell.


Former-commit-id: 2f94c6050fa26c5b777244bd26706d4e6e2f0dc9
2020-03-28 16:14:59 +03:00

442 lines
13 KiB
Rust

#[cfg(feature = "debugger")]
use super::reg_string;
#[cfg(feature = "debugger")]
use ansi_term::{Colour, Style};
use serde::{Deserialize, Serialize};
#[cfg(feature = "debugger")]
use std::fmt;
pub use super::exception::Exception;
use super::CpuAction;
#[cfg(feature = "debugger")]
use super::DecodedInstruction;
use super::{
arm::*, psr::RegPSR, thumb::ThumbInstruction, Addr, CpuMode, CpuState, InstructionDecoder,
};
use crate::core::bus::Bus;
use crate::core::sysbus::{MemoryAccessType::*, MemoryAccessWidth::*, SysBus};
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Core {
pub pc: u32,
pub gpr: [u32; 15],
// r13 and r14 are banked for all modes. System&User mode share them
pub(super) gpr_banked_r13: [u32; 6],
pub(super) gpr_banked_r14: [u32; 6],
// r8-r12 are banked for fiq mode
pub(super) gpr_banked_old_r8_12: [u32; 5],
pub(super) gpr_banked_fiq_r8_12: [u32; 5],
pub cpsr: RegPSR,
pub(super) spsr: RegPSR,
pub(super) spsr_bank: [RegPSR; 6],
pub(super) bs_carry_out: bool,
pipeline: [u32; 2],
#[cfg(feature = "debugger")]
pub last_executed: Option<DecodedInstruction>,
pub cycles: usize,
// store the gpr before executing an instruction to show diff in the Display impl
gpr_previous: [u32; 15],
memreq: Addr,
pub breakpoints: Vec<u32>,
pub verbose: bool,
pub trace_opcodes: bool,
pub trace_exceptions: bool,
}
impl Core {
pub fn new() -> Core {
let cpsr = RegPSR::new(0x0000_00D3);
Core {
memreq: 0xffff_0000, // set memreq to an invalid addr so the first load cycle will be non-sequential
cpsr: cpsr,
..Default::default()
}
}
pub fn set_verbose(&mut self, v: bool) {
self.verbose = v;
}
pub fn get_reg(&self, r: usize) -> u32 {
match r {
0..=14 => self.gpr[r],
15 => self.pc,
_ => panic!("invalid register {}", r),
}
}
pub fn get_reg_user(&mut self, r: usize) -> u32 {
match r {
0..=7 => self.gpr[r],
8..=12 => {
if self.cpsr.mode() == CpuMode::Fiq {
self.gpr[r]
} else {
self.gpr_banked_old_r8_12[r - 8]
}
}
13 => self.gpr_banked_r13[0],
14 => self.gpr_banked_r14[0],
_ => panic!("invalid register"),
}
}
pub fn set_reg(&mut self, r: usize, val: u32) {
match r {
0..=14 => self.gpr[r] = val,
15 => {
self.pc = {
match self.cpsr.state() {
CpuState::THUMB => val & !1,
CpuState::ARM => val & !3,
}
}
}
_ => panic!("invalid register"),
}
}
pub fn set_reg_user(&mut self, r: usize, val: u32) {
match r {
0..=7 => self.gpr[r] = val,
8..=12 => {
if self.cpsr.mode() == CpuMode::Fiq {
self.gpr[r] = val;
} else {
self.gpr_banked_old_r8_12[r - 8] = val;
}
}
13 => {
self.gpr_banked_r13[0] = val;
}
14 => {
self.gpr_banked_r14[0] = val;
}
_ => panic!("invalid register"),
}
}
pub(super) fn write_32(&mut self, addr: Addr, value: u32, bus: &mut SysBus) {
bus.write_32(addr & !0x3, value);
}
pub(super) fn write_16(&mut self, addr: Addr, value: u16, bus: &mut SysBus) {
bus.write_16(addr & !0x1, value);
}
pub(super) fn write_8(&mut self, addr: Addr, value: u8, bus: &mut SysBus) {
bus.write_8(addr, value);
}
/// Helper function for "ldr" instruction that handles misaligned addresses
pub(super) fn ldr_word(&mut self, addr: Addr, bus: &SysBus) -> u32 {
if addr & 0x3 != 0 {
let rotation = (addr & 0x3) << 3;
let value = bus.read_32(addr & !0x3);
self.ror(value, rotation, self.cpsr.C(), false, false)
} else {
bus.read_32(addr)
}
}
/// Helper function for "ldrh" instruction that handles misaligned addresses
pub(super) fn ldr_half(&mut self, addr: Addr, bus: &SysBus) -> u32 {
if addr & 0x1 != 0 {
let rotation = (addr & 0x1) << 3;
let value = bus.read_16(addr & !0x1);
self.ror(value as u32, rotation, self.cpsr.C(), false, false)
} else {
bus.read_16(addr) as u32
}
}
/// Helper function for "ldrsh" instruction that handles misaligned addresses
pub(super) fn ldr_sign_half(&mut self, addr: Addr, bus: &SysBus) -> u32 {
if addr & 0x1 != 0 {
bus.read_8(addr) as i8 as i32 as u32
} else {
bus.read_16(addr) as i16 as i32 as u32
}
}
pub fn get_registers(&self) -> [u32; 15] {
self.gpr.clone()
}
pub(super) fn change_mode(&mut self, old_mode: CpuMode, new_mode: CpuMode) {
let new_index = new_mode.bank_index();
let old_index = old_mode.bank_index();
if new_index == old_index {
return;
}
self.spsr_bank[old_index] = self.spsr;
self.gpr_banked_r13[old_index] = self.gpr[13];
self.gpr_banked_r14[old_index] = self.gpr[14];
self.spsr = self.spsr_bank[new_index];
self.gpr[13] = self.gpr_banked_r13[new_index];
self.gpr[14] = self.gpr_banked_r14[new_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 old_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];
}
}
self.cpsr.set_mode(new_mode);
}
/// Resets the cpu
pub fn reset(&mut self, sb: &mut SysBus) {
self.exception(sb, Exception::Reset, 0);
}
pub fn word_size(&self) -> usize {
match self.cpsr.state() {
CpuState::ARM => 4,
CpuState::THUMB => 2,
}
}
pub fn cycles(&self) -> usize {
self.cycles
}
pub(super) fn add_cycle(&mut self) {
// println!("<cycle I-Cyclel> total: {}", self.cycles);
self.cycles += 1;
}
pub(super) fn get_required_multipiler_array_cycles(&self, rs: u32) -> usize {
if rs & 0xff == rs {
1
} else if rs & 0xffff == rs {
2
} else if rs & 0xffffff == rs {
3
} else {
4
}
}
#[allow(non_snake_case)]
#[inline(always)]
pub(super) fn S_cycle32(&mut self, sb: &SysBus, addr: u32) {
self.cycles += 1;
self.cycles += sb.get_cycles(addr, Seq + MemoryAccess32);
}
#[allow(non_snake_case)]
#[inline(always)]
pub(super) fn S_cycle16(&mut self, sb: &SysBus, addr: u32) {
self.cycles += 1;
self.cycles += sb.get_cycles(addr, Seq + MemoryAccess16);
}
#[allow(non_snake_case)]
#[inline(always)]
pub(super) fn S_cycle8(&mut self, sb: &SysBus, addr: u32) {
self.cycles += 1;
self.cycles += sb.get_cycles(addr, Seq + MemoryAccess8);
}
#[allow(non_snake_case)]
#[inline(always)]
pub(super) fn N_cycle32(&mut self, sb: &SysBus, addr: u32) {
self.cycles += 1;
self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess32);
}
#[allow(non_snake_case)]
#[inline(always)]
pub(super) fn N_cycle16(&mut self, sb: &SysBus, addr: u32) {
self.cycles += 1;
self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess16);
}
#[allow(non_snake_case)]
#[inline(always)]
pub(super) fn N_cycle8(&mut self, sb: &SysBus, addr: u32) {
self.cycles += 1;
self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess8);
}
#[inline]
pub(super) fn check_arm_cond(&self, cond: ArmCond) -> bool {
use ArmCond::*;
match cond {
EQ => self.cpsr.Z(),
NE => !self.cpsr.Z(),
HS => self.cpsr.C(),
LO => !self.cpsr.C(),
MI => self.cpsr.N(),
PL => !self.cpsr.N(),
VS => self.cpsr.V(),
VC => !self.cpsr.V(),
HI => self.cpsr.C() && !self.cpsr.Z(),
LS => !self.cpsr.C() || self.cpsr.Z(),
GE => self.cpsr.N() == self.cpsr.V(),
LT => self.cpsr.N() != self.cpsr.V(),
GT => !self.cpsr.Z() && (self.cpsr.N() == self.cpsr.V()),
LE => self.cpsr.Z() || (self.cpsr.N() != self.cpsr.V()),
AL => true,
}
}
fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) {
let decoded_arm = ArmInstruction::decode(insn, self.pc.wrapping_sub(8));
#[cfg(feature = "debugger")]
{
self.gpr_previous = self.get_registers();
self.last_executed = Some(DecodedInstruction::Arm(decoded_arm.clone()));
}
let result = self.exec_arm(sb, &decoded_arm);
match result {
CpuAction::AdvancePC => self.advance_arm(),
CpuAction::FlushPipeline => {}
}
}
fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) {
let decoded_thumb = ThumbInstruction::decode(insn, self.pc.wrapping_sub(4));
#[cfg(feature = "debugger")]
{
self.gpr_previous = self.get_registers();
self.last_executed = Some(DecodedInstruction::Thumb(decoded_thumb.clone()));
}
let result = self.exec_thumb(sb, &decoded_thumb);
match result {
CpuAction::AdvancePC => self.advance_thumb(),
CpuAction::FlushPipeline => {}
}
}
#[inline(always)]
pub fn reload_pipeline16(&mut self, sb: &mut SysBus) {
self.pipeline[0] = sb.read_16(self.pc) as u32;
self.N_cycle16(sb, self.pc);
self.advance_thumb();
self.pipeline[1] = sb.read_16(self.pc) as u32;
self.S_cycle16(sb, self.pc);
self.advance_thumb();
}
#[inline(always)]
pub fn reload_pipeline32(&mut self, sb: &mut SysBus) {
self.pipeline[0] = sb.read_32(self.pc);
self.N_cycle16(sb, self.pc);
self.advance_arm();
self.pipeline[1] = sb.read_32(self.pc);
self.S_cycle16(sb, self.pc);
self.advance_arm();
}
#[inline]
pub(super) fn advance_thumb(&mut self) {
self.pc = self.pc.wrapping_add(2)
}
#[inline]
pub(super) fn advance_arm(&mut self) {
self.pc = self.pc.wrapping_add(4)
}
/// Perform a pipeline step
/// If an instruction was executed in this step, return it.
pub fn step(&mut self, bus: &mut SysBus) {
let pc = self.pc;
match self.cpsr.state() {
CpuState::ARM => {
let fetched_now = bus.read_32(pc);
let insn = self.pipeline[0];
self.pipeline[0] = self.pipeline[1];
self.pipeline[1] = fetched_now;
self.step_arm_exec(insn, bus)
}
CpuState::THUMB => {
let fetched_now = bus.read_16(pc);
let insn = self.pipeline[0];
self.pipeline[0] = self.pipeline[1];
self.pipeline[1] = fetched_now as u32;
self.step_thumb_exec(insn as u16, bus)
}
}
}
/// Get's the address of the next instruction that is going to be executed
pub fn get_next_pc(&self) -> Addr {
let insn_size = self.word_size() as u32;
self.pc - 2 * insn_size
}
pub fn get_cpu_state(&self) -> CpuState {
self.cpsr.state()
}
pub fn skip_bios(&mut self) {
self.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS
self.gpr_banked_r13[1] = 0x0300_7f00; // FIQ
self.gpr_banked_r13[2] = 0x0300_7fa0; // IRQ
self.gpr_banked_r13[3] = 0x0300_7fe0; // SVC
self.gpr_banked_r13[4] = 0x0300_7f00; // ABT
self.gpr_banked_r13[5] = 0x0300_7f00; // UND
self.gpr[13] = 0x0300_7f00;
self.pc = 0x0800_0000;
self.cpsr.set(0x5f);
}
}
#[cfg(feature = "debugger")]
impl fmt::Display for Core {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "ARM7TDMI Core Status:")?;
writeln!(f, "\tCycles: {}", self.cycles)?;
writeln!(f, "\tCPSR: {}", self.cpsr)?;
writeln!(f, "\tGeneral Purpose Registers:")?;
let reg_normal_style = Style::new().bold();
let reg_dirty_style = Colour::Black.bold().on(Colour::Yellow);
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] {
&reg_dirty_style
} else {
&reg_normal_style
};
let entry = format!("\t{:-3} = 0x{:08x}", reg_name, gpr[i]);
write!(
f,
"{}{}",
style.paint(entry),
if (i + 1) % 4 == 0 { "\n" } else { "" }
)?;
}
let pc = format!("\tPC = 0x{:08x}", self.get_next_pc());
writeln!(f, "{}", reg_normal_style.paint(pc))
}
}