Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Cpu and System traits #40

Merged
merged 7 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 7 additions & 189 deletions src/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,193 +1,11 @@
mod execute;
mod fetch;
mod registers;
use crate::memory::{ActiveInterrupt, Memory, SystemInfo};
use execute::Execute;
use fetch::Fetch;
use registers::{flags, Registers};
pub mod mos6502;

const CLOCKS_PER_POLL: u32 = 100;
pub trait Cpu {
fn reset(&mut self);

#[derive(Copy, Clone, PartialEq)]
pub enum Mos6502Variant {
/// 6502
NMOS,
/// 65C02
CMOS,
}

/// The MOS 6502 CPU and its associated memory.
pub struct Mos6502 {
pub registers: Registers,
pub memory: Box<dyn Memory>,
cycle_count: u64,
cycles_since_poll: u32,
variant: Mos6502Variant,
}

/// Read and write from the system's memory.
pub trait MemoryIO {
/// Read a byte from the given address in memory.
fn read(&mut self, address: u16) -> u8;

/// Write a byte to the given address in memory.
fn write(&mut self, address: u16, value: u8);

/// Read a word (little-endian) from the given address in memory.
fn read_word(&mut self, address: u16) -> u16;

/// Write a word (little-endian) to the given address in memory.
fn write_word(&mut self, address: u16, value: u16);
}

impl MemoryIO for Mos6502 {
fn read(&mut self, address: u16) -> u8 {
self.memory.read(address)
}

fn read_word(&mut self, address: u16) -> u16 {
let lo = self.memory.read(address);
let hi = self.memory.read(address + 1);
(hi as u16) << 8 | lo as u16
}

fn write(&mut self, address: u16, value: u8) {
self.memory.write(address, value);
}

fn write_word(&mut self, address: u16, value: u16) {
self.memory.write(address, value as u8);
self.memory.write(address + 1, (value >> 8) as u8);
}
}

/// Push and pop values from the stack.
pub trait Stack {
/// Push a byte onto the stack.
fn push(&mut self, value: u8);

/// Pop a byte from the stack.
fn pop(&mut self) -> u8;

/// Push a word (little-endian) onto the stack.
fn push_word(&mut self, value: u16);

/// Pop a word (little-endian) from the stack.
fn pop_word(&mut self) -> u16;
}

impl Stack for Mos6502 {
fn push(&mut self, value: u8) {
self.write(self.registers.sp.address(), value);
self.registers.sp.push();
}

fn pop(&mut self) -> u8 {
self.registers.sp.pop();
self.read(self.registers.sp.address())
}

fn push_word(&mut self, value: u16) {
self.push((value >> 8) as u8);
self.push((value & 0xFF) as u8);
}

fn pop_word(&mut self) -> u16 {
let lo = self.pop();
let hi = self.pop();
(hi as u16) << 8 | lo as u16
}
}

/// Handle interrupts by setting the applicable flags, pushing the program counter
/// onto the stack, and loading the interrupt vector into the program counter.
pub trait InterruptHandler {
fn interrupt(&mut self, maskable: bool, set_brk: bool);
}

impl InterruptHandler for Mos6502 {
fn interrupt(&mut self, maskable: bool, break_instr: bool) {
if maskable && !break_instr && self.registers.sr.read(flags::INTERRUPT) {
return;
}

self.push_word(self.registers.pc.address());

if break_instr {
self.push(self.registers.sr.get() | flags::BREAK);
} else {
self.push(self.registers.sr.get() & !flags::BREAK);
}

if let Mos6502Variant::CMOS = self.variant {
self.registers.sr.clear(flags::DECIMAL);
}

self.registers.sr.set(flags::INTERRUPT);

let dest = match maskable {
false => self.read_word(0xFFFA),
true => self.read_word(0xFFFE),
};

self.registers.pc.load(dest);
}
}

impl Mos6502 {
pub fn new(memory: impl Memory + 'static, variant: Mos6502Variant) -> Mos6502 {
Mos6502 {
registers: Registers::new(),
memory: Box::new(memory),
cycle_count: 0,
cycles_since_poll: 0,
variant,
}
}

pub fn reset(&mut self) {
self.memory.reset();
self.registers.reset();
let pc_address = self.read_word(0xFFFC);
self.registers.pc.load(pc_address);
}

/// Return a SystemInfo struct containing the current system status.
pub fn get_info(&self) -> SystemInfo {
SystemInfo {
cycle_count: self.cycle_count,
}
}

/// Execute a single instruction.
pub fn tick(&mut self) -> u8 {
let opcode = self.fetch();
match self.execute(opcode) {
Ok(cycles) => {
self.cycle_count += cycles as u64;
self.cycles_since_poll += cycles as u32;

if self.cycles_since_poll >= CLOCKS_PER_POLL {
let info = self.get_info();

match self.memory.poll(self.cycles_since_poll, &info) {
ActiveInterrupt::None => (),
ActiveInterrupt::NMI => self.interrupt(false, false),
ActiveInterrupt::IRQ => self.interrupt(true, false),
}

self.cycles_since_poll = 0;
}
/// Return the number of cycles elapsed since the system last reset.
fn get_cycle_count(&self) -> u64;

cycles
}
Err(_) => {
panic!(
"Failed to execute instruction {:02x} at {:04x}",
opcode,
self.registers.pc.address()
);
}
}
}
/// Execute a single instruction. Return the number of cycles elapsed.
fn tick(&mut self) -> u8;
}
8 changes: 5 additions & 3 deletions src/cpu/execute.rs → src/cpu/mos6502/execute.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::cpu::fetch::Fetch;
use crate::cpu::registers::{flags, Alu};
use crate::cpu::{InterruptHandler, MemoryIO, Mos6502, Stack};
use crate::cpu::mos6502::{
fetch::Fetch,
registers::{flags, Alu},
InterruptHandler, MemoryIO, Mos6502, Stack,
};

use super::Mos6502Variant;

Expand Down
2 changes: 1 addition & 1 deletion src/cpu/fetch.rs → src/cpu/mos6502/fetch.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::cpu::{MemoryIO, Mos6502};
use crate::cpu::mos6502::{MemoryIO, Mos6502};

use super::Mos6502Variant;

Expand Down
Loading
Loading