Skip to content

Commit

Permalink
Extract CPU interface to trait, remove SystemInfo struct
Browse files Browse the repository at this point in the history
Co-authored-by: Ava Silver <[email protected]>
  • Loading branch information
breqdev and ava-silver committed Dec 30, 2023
1 parent 92c6854 commit c62509c
Show file tree
Hide file tree
Showing 22 changed files with 147 additions and 127 deletions.
10 changes: 10 additions & 0 deletions src/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
pub mod mos6502;

pub trait Cpu {
fn reset(&mut self);

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

/// Execute a single instruction. Return the number of cycles elapsed.
fn tick(&mut self) -> u8;
}
26 changes: 14 additions & 12 deletions src/cpu/mos6502/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod execute;
mod fetch;
mod registers;
use crate::memory::{ActiveInterrupt, Memory, SystemInfo};
use crate::memory::{ActiveInterrupt, Memory};
use execute::Execute;
use fetch::Fetch;
use registers::{flags, Registers};

const CLOCKS_PER_POLL: u32 = 100;
use super::Cpu;

const CLOCKS_PER_POLL: u64 = 100;

#[derive(Copy, Clone, PartialEq)]
pub enum Mos6502Variant {
Expand All @@ -21,7 +23,7 @@ pub struct Mos6502 {
pub registers: Registers,
pub memory: Box<dyn Memory>,
cycle_count: u64,
cycles_since_poll: u32,
cycles_since_poll: u64,
variant: Mos6502Variant,
}

Expand Down Expand Up @@ -144,33 +146,33 @@ impl Mos6502 {
variant,
}
}
}

pub fn reset(&mut self) {
impl Cpu for Mos6502 {
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,
}
fn get_cycle_count(&self) -> u64 {
self.cycle_count
}

/// Execute a single instruction.
pub fn tick(&mut self) -> u8 {
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;
self.cycles_since_poll += cycles as u64;

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

match self.memory.poll(self.cycles_since_poll, &info) {
match self.memory.poll(self.cycles_since_poll, total_cycle_count) {
ActiveInterrupt::None => (),
ActiveInterrupt::NMI => self.interrupt(false, false),
ActiveInterrupt::IRQ => self.interrupt(true, false),
Expand Down
6 changes: 3 additions & 3 deletions src/memory/banked.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{cell::Cell, rc::Rc};

use super::{ActiveInterrupt, Memory, SystemInfo};
use super::{ActiveInterrupt, Memory};

/// Represents the memory banking features found in the Commodore 64 and other
/// devices. Multiple memory implementations are all mapped to the same
Expand Down Expand Up @@ -48,11 +48,11 @@ impl Memory for BankedMemory {
}
}

fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt {
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
let mut highest = ActiveInterrupt::None;

for mapped in &mut self.banks {
let interrupt = mapped.poll(cycles, info);
let interrupt = mapped.poll(cycles_since_poll, total_cycle_count);

match interrupt {
ActiveInterrupt::None => (),
Expand Down
4 changes: 2 additions & 2 deletions src/memory/block.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::memory::{ActiveInterrupt, Memory, SystemInfo};
use crate::memory::{ActiveInterrupt, Memory};
use crate::roms::RomFile;

/// Represents a simple block of contiguous memory, with no additional hardware.
Expand Down Expand Up @@ -85,7 +85,7 @@ impl Memory for BlockMemory {
}
}

fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> ActiveInterrupt {
fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt {
ActiveInterrupt::None
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/memory/branch.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::memory::{ActiveInterrupt, Memory, SystemInfo};
use crate::memory::{ActiveInterrupt, Memory};

/// Maps several Memory objects into a single contiguous address space.
/// Each mapped object is assigned a starting address, and reads and writes
Expand Down Expand Up @@ -66,11 +66,11 @@ impl Memory for BranchMemory {
}
}

fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt {
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
let mut highest = ActiveInterrupt::None;

for (_, mapped) in &mut self.mapping {
let interrupt = mapped.poll(cycles, info);
let interrupt = mapped.poll(cycles_since_poll, total_cycle_count);

match interrupt {
ActiveInterrupt::None => (),
Expand Down
6 changes: 3 additions & 3 deletions src/memory/logging.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{ActiveInterrupt, Memory, SystemInfo};
use super::{ActiveInterrupt, Memory};

pub struct LoggingMemory {
backing: Box<dyn Memory>,
Expand Down Expand Up @@ -43,8 +43,8 @@ impl Memory for LoggingMemory {
println!("[Memory Reset]: {}", self.message);
}

fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt {
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
// println!("[Memory Poll]: {}", self.message);
self.backing.poll(cycles, info)
self.backing.poll(cycles_since_poll, total_cycle_count)
}
}
9 changes: 1 addition & 8 deletions src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@ pub enum ActiveInterrupt {
IRQ,
}

/// Information about the system that Memory implementations can use to
/// determine if an interrupt should be triggered.
#[derive(Debug, Default)]
pub struct SystemInfo {
pub cycle_count: u64,
}

/// Represents a contiguous block of memory which can be read, written,
/// reset, and polled to see if an interrupt has been triggered.
pub trait Memory {
Expand All @@ -52,5 +45,5 @@ pub trait Memory {
/// Poll this memory to see if an interrupt has been triggered.
/// Implementations may trigger an NMI or IRQ for any
/// implementation-dependent reason.
fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt;
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt;
}
6 changes: 3 additions & 3 deletions src/memory/mos6510.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{ActiveInterrupt, Memory, Port, SystemInfo};
use super::{ActiveInterrupt, Memory, Port};

/// Represents the port built into a MOS 6510 processor, mapped to memory addresses 0x0000 (for the DDR) and 0x0001 (for the port itself).
pub struct Mos6510Port {
Expand Down Expand Up @@ -50,8 +50,8 @@ impl Memory for Mos6510Port {
self.port.reset();
}

fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt {
match self.port.poll(cycles, info) {
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
match self.port.poll(cycles_since_poll, total_cycle_count) {
true => ActiveInterrupt::IRQ,
false => ActiveInterrupt::None,
}
Expand Down
50 changes: 23 additions & 27 deletions src/memory/mos652x/cia.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::memory::{
mos652x::{InterruptRegister, PortRegisters, ShiftRegister, Timer},
ActiveInterrupt, Memory, Port, SystemInfo,
ActiveInterrupt, Memory, Port,
};

struct TimeRegisters {
Expand Down Expand Up @@ -208,20 +208,22 @@ impl Memory for Cia {
self.interrupts.reset();
}

fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt {
if self.timer_a.poll(cycles, info)
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
if self.timer_a.poll(cycles_since_poll, total_cycle_count)
&& (self.interrupts.interrupt_enable & interrupt_bits::TIMER_A) != 0
{
return ActiveInterrupt::IRQ;
}

if self.timer_b.poll(cycles, info)
if self.timer_b.poll(cycles_since_poll, total_cycle_count)
&& (self.interrupts.interrupt_enable & interrupt_bits::TIMER_B) != 0
{
return ActiveInterrupt::IRQ;
}

if self.a.poll(cycles, info) || self.b.poll(cycles, info) {
if self.a.poll(cycles_since_poll, total_cycle_count)
|| self.b.poll(cycles_since_poll, total_cycle_count)
{
return ActiveInterrupt::IRQ;
}

Expand Down Expand Up @@ -276,14 +278,14 @@ mod tests {
cia.write(0x0E, 0b0000_1001);

for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}

assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));

// polling again shouldn't do anything
for _ in 0..0x20 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
}

Expand All @@ -302,10 +304,10 @@ mod tests {
cia.write(0x0F, 0b0000_1001);

for _ in 0..0x1233 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}

assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}

#[test]
Expand All @@ -322,19 +324,13 @@ mod tests {
// start the timer, and enable continuous operation
cia.write(0x0E, 0b0000_0001);

assert_eq!(
ActiveInterrupt::None,
cia.poll(0x0F, &SystemInfo::default())
);
assert_eq!(ActiveInterrupt::None, cia.poll(0x0F, 0));

assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));

assert_eq!(
ActiveInterrupt::None,
cia.poll(0x0F, &SystemInfo::default())
);
assert_eq!(ActiveInterrupt::None, cia.poll(0x0F, 0));

assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}

#[test]
Expand All @@ -358,10 +354,10 @@ mod tests {

// timer 1 should interrupt first
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}

assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}

#[test]
Expand All @@ -384,7 +380,7 @@ mod tests {

// timer 2 shouldn't trigger an interrupt
for _ in 0..0x08 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}

// ...but the flag register should be set
Expand All @@ -395,9 +391,9 @@ mod tests {

// timer 1 should then trigger an interrupt
for _ in 0..0x07 {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));

// ...and set the corresponding flag, plus the master bit
assert_eq!(
Expand All @@ -410,8 +406,8 @@ mod tests {

// if we let timer 1 run again, it should set the flag again
for _ in 0..0x0F {
assert_eq!(ActiveInterrupt::None, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::None, cia.poll(1, 0));
}
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, &SystemInfo::default()));
assert_eq!(ActiveInterrupt::IRQ, cia.poll(1, 0));
}
}
10 changes: 5 additions & 5 deletions src/memory/mos652x/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use cia::Cia;
pub use pia::Pia;
pub use via::Via;

use crate::memory::{Port, SystemInfo};
use crate::memory::Port;

/// A port and its associated registers on the MOS 6522 VIA or MOS 6526 CIA.
pub struct PortRegisters {
Expand Down Expand Up @@ -45,8 +45,8 @@ impl PortRegisters {
}

/// Poll the underlying port for interrupts.
pub fn poll(&mut self, cycles: u32, info: &SystemInfo) -> bool {
self.port.poll(cycles, info)
pub fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool {
self.port.poll(cycles_since_poll, total_cycle_count)
}

/// Reset the port to its initial state.
Expand Down Expand Up @@ -127,7 +127,7 @@ impl Timer {
}

/// Poll the timer (decrement the counter, fire the interrupt if necessary).
pub fn poll(&mut self, cycles: u32, _info: &SystemInfo) -> bool {
pub fn poll(&mut self, cycles_since_poll: u64, _total_cycle_count: u64) -> bool {
if self.counter <= 0 {
if self.continuous {
self.counter += self.latch as i32;
Expand All @@ -138,7 +138,7 @@ impl Timer {
}

if self.running {
self.counter -= cycles as i32;
self.counter -= cycles_since_poll as i32;

if self.counter <= 0 {
// The counter underflowed
Expand Down
12 changes: 6 additions & 6 deletions src/memory/mos652x/pia.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::memory::{ActiveInterrupt, Memory, Port, SystemInfo};
use crate::memory::{ActiveInterrupt, Memory, Port};

// MOS 6520

Expand Down Expand Up @@ -55,8 +55,8 @@ impl PiaPortRegisters {
}

/// Poll the underlying port for interrupts.
pub fn poll(&mut self, cycles: u32, info: &SystemInfo) -> bool {
self.port.poll(cycles, info)
pub fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool {
self.port.poll(cycles_since_poll, total_cycle_count)
}

/// Reset the DDR, control register, and underlying port.
Expand Down Expand Up @@ -122,9 +122,9 @@ impl Memory for Pia {
self.b.reset();
}

fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt {
let a = self.a.poll(cycles, info);
let b = self.b.poll(cycles, info);
fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt {
let a = self.a.poll(cycles_since_poll, total_cycle_count);
let b = self.b.poll(cycles_since_poll, total_cycle_count);

if a || b {
ActiveInterrupt::IRQ
Expand Down
Loading

0 comments on commit c62509c

Please sign in to comment.