diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index 616ac7d9..639411f0 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -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, - 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; } diff --git a/src/cpu/execute.rs b/src/cpu/mos6502/execute.rs similarity index 99% rename from src/cpu/execute.rs rename to src/cpu/mos6502/execute.rs index a19402e2..db57ce01 100644 --- a/src/cpu/execute.rs +++ b/src/cpu/mos6502/execute.rs @@ -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; diff --git a/src/cpu/fetch.rs b/src/cpu/mos6502/fetch.rs similarity index 98% rename from src/cpu/fetch.rs rename to src/cpu/mos6502/fetch.rs index e1615ed8..7c0c9a7f 100644 --- a/src/cpu/fetch.rs +++ b/src/cpu/mos6502/fetch.rs @@ -1,4 +1,4 @@ -use crate::cpu::{MemoryIO, Mos6502}; +use crate::cpu::mos6502::{MemoryIO, Mos6502}; use super::Mos6502Variant; diff --git a/src/cpu/mos6502/mod.rs b/src/cpu/mos6502/mod.rs new file mode 100644 index 00000000..347aa38b --- /dev/null +++ b/src/cpu/mos6502/mod.rs @@ -0,0 +1,195 @@ +mod execute; +mod fetch; +mod registers; +use crate::memory::{ActiveInterrupt, Memory}; +use execute::Execute; +use fetch::Fetch; +use registers::{flags, Registers}; + +use super::Cpu; + +const CLOCKS_PER_POLL: u64 = 100; + +#[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, + cycle_count: u64, + cycles_since_poll: u64, + 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, + } + } +} + +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. + fn get_cycle_count(&self) -> u64 { + self.cycle_count + } + + /// Execute a single instruction. + 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 u64; + + if self.cycles_since_poll >= CLOCKS_PER_POLL { + let total_cycle_count = self.get_cycle_count(); + + 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), + } + + self.cycles_since_poll = 0; + } + + cycles + } + Err(_) => { + panic!( + "Failed to execute instruction {:02x} at {:04x}", + opcode, + self.registers.pc.address() + ); + } + } + } +} diff --git a/src/cpu/registers.rs b/src/cpu/mos6502/registers.rs similarity index 100% rename from src/cpu/registers.rs rename to src/cpu/mos6502/registers.rs diff --git a/src/main.rs b/src/main.rs index 77c4e014..a3f7814a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,10 +4,10 @@ use libnoentiendo::{ platform::{SyncPlatform, TextPlatform, WinitPlatform}, roms::DiskLoadable, systems::{ - basic::BasicSystemBuilder, c64::C64SystemBuilder, c64::C64SystemConfig, c64::C64SystemRoms, - easy::Easy6502SystemBuilder, klaus::KlausSystemBuilder, pet::PetSystemBuilder, - pet::PetSystemConfig, pet::PetSystemRoms, vic::Vic20SystemBuilder, vic::Vic20SystemConfig, - vic::Vic20SystemRoms, SystemBuilder, + basic::BasicSystem, c64::C64System, c64::C64SystemConfig, c64::C64SystemRoms, + easy::Easy6502System, klaus::KlausSystem, pet::PetSystem, pet::PetSystemConfig, + pet::PetSystemRoms, vic::Vic20System, vic::Vic20SystemConfig, vic::Vic20SystemRoms, + BuildableSystem, }, }; @@ -55,7 +55,7 @@ struct Args { #[cfg(not(target_arch = "wasm32"))] fn main() { - use libnoentiendo::{cpu::Mos6502Variant, systems::klaus::KlausSystemConfig}; + use libnoentiendo::{cpu::mos6502::Mos6502Variant, systems::klaus::KlausSystemConfig}; let args = Args::parse(); @@ -75,9 +75,9 @@ fn main() { }; let system = match args.system { - SystemArg::Basic => BasicSystemBuilder::build(romfile.unwrap(), (), platform.provider()), - SystemArg::Easy => Easy6502SystemBuilder::build(romfile.unwrap(), (), platform.provider()), - SystemArg::Klaus => KlausSystemBuilder::build( + SystemArg::Basic => BasicSystem::build(romfile.unwrap(), (), platform.provider()), + SystemArg::Easy => Easy6502System::build(romfile.unwrap(), (), platform.provider()), + SystemArg::Klaus => KlausSystem::build( romfile.unwrap(), KlausSystemConfig { pc_report: None, @@ -85,12 +85,12 @@ fn main() { }, platform.provider(), ), - SystemArg::Pet => PetSystemBuilder::build( + SystemArg::Pet => PetSystem::build( PetSystemRoms::from_disk(), PetSystemConfig { mapping }, platform.provider(), ), - SystemArg::Vic => Vic20SystemBuilder::build( + SystemArg::Vic => Vic20System::build( Vic20SystemRoms::from_disk(match romfile { Some(_) => Some(args.rom_path.as_str()), None => None, @@ -98,7 +98,7 @@ fn main() { Vic20SystemConfig { mapping }, platform.provider(), ), - SystemArg::C64 => C64SystemBuilder::build( + SystemArg::C64 => C64System::build( C64SystemRoms::from_disk(), C64SystemConfig { mapping }, platform.provider(), diff --git a/src/memory/banked.rs b/src/memory/banked.rs index d3a108e4..e965ec3e 100644 --- a/src/memory/banked.rs +++ b/src/memory/banked.rs @@ -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 @@ -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 => (), diff --git a/src/memory/block.rs b/src/memory/block.rs index 95e90353..4de9b01d 100644 --- a/src/memory/block.rs +++ b/src/memory/block.rs @@ -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. @@ -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 } } diff --git a/src/memory/branch.rs b/src/memory/branch.rs index 64f120f0..71222919 100644 --- a/src/memory/branch.rs +++ b/src/memory/branch.rs @@ -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 @@ -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 => (), diff --git a/src/memory/logging.rs b/src/memory/logging.rs index e7b841eb..b60f4622 100644 --- a/src/memory/logging.rs +++ b/src/memory/logging.rs @@ -1,4 +1,4 @@ -use super::{ActiveInterrupt, Memory, SystemInfo}; +use super::{ActiveInterrupt, Memory}; pub struct LoggingMemory { backing: Box, @@ -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) } } diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 9b8e44c3..ce76cd8c 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -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 { @@ -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; } diff --git a/src/memory/mos6510.rs b/src/memory/mos6510.rs index 16ae7d96..ec2b17bb 100644 --- a/src/memory/mos6510.rs +++ b/src/memory/mos6510.rs @@ -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 { @@ -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, } diff --git a/src/memory/mos652x/cia.rs b/src/memory/mos652x/cia.rs index 03de9bef..973c4899 100644 --- a/src/memory/mos652x/cia.rs +++ b/src/memory/mos652x/cia.rs @@ -1,6 +1,6 @@ use crate::memory::{ mos652x::{InterruptRegister, PortRegisters, ShiftRegister, Timer}, - ActiveInterrupt, Memory, Port, SystemInfo, + ActiveInterrupt, Memory, Port, }; struct TimeRegisters { @@ -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; } @@ -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)); } } @@ -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] @@ -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] @@ -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] @@ -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 @@ -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!( @@ -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)); } } diff --git a/src/memory/mos652x/mod.rs b/src/memory/mos652x/mod.rs index c8048af6..792a63e1 100644 --- a/src/memory/mos652x/mod.rs +++ b/src/memory/mos652x/mod.rs @@ -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 { @@ -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. @@ -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; @@ -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 diff --git a/src/memory/mos652x/pia.rs b/src/memory/mos652x/pia.rs index 9b10cb8b..a112b5ff 100644 --- a/src/memory/mos652x/pia.rs +++ b/src/memory/mos652x/pia.rs @@ -1,4 +1,4 @@ -use crate::memory::{ActiveInterrupt, Memory, Port, SystemInfo}; +use crate::memory::{ActiveInterrupt, Memory, Port}; // MOS 6520 @@ -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. @@ -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 diff --git a/src/memory/mos652x/via.rs b/src/memory/mos652x/via.rs index fc864c81..3e5ab2e5 100644 --- a/src/memory/mos652x/via.rs +++ b/src/memory/mos652x/via.rs @@ -1,6 +1,6 @@ use crate::memory::{ mos652x::{InterruptRegister, PortRegisters, ShiftRegister, Timer, TimerOutput}, - ActiveInterrupt, Memory, Port, SystemInfo, + ActiveInterrupt, Memory, Port, }; #[allow(dead_code)] @@ -183,16 +183,22 @@ impl Memory for Via { self.b.reset(); } - fn poll(&mut self, cycles: u32, info: &SystemInfo) -> ActiveInterrupt { - if self.t1.poll(cycles, info) && self.interrupts.is_enabled(interrupt_bits::T1_ENABLE) { + fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt { + if self.t1.poll(cycles_since_poll, total_cycle_count) + && self.interrupts.is_enabled(interrupt_bits::T1_ENABLE) + { return ActiveInterrupt::IRQ; } - if self.t2.poll(cycles, info) && self.interrupts.is_enabled(interrupt_bits::T2_ENABLE) { + if self.t2.poll(cycles_since_poll, total_cycle_count) + && self.interrupts.is_enabled(interrupt_bits::T2_ENABLE) + { 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; } @@ -244,14 +250,14 @@ mod tests { via.write(0x05, 0x00); for _ in 0..0x0F { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); // polling again shouldn't do anything for _ in 0..0x20 { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } } @@ -266,16 +272,16 @@ mod tests { via.write(0x08, 0x34); // polling now shouldn't do anything - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); // timer begins when the high byte is written via.write(0x09, 0x12); for _ in 0..0x1233 { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); } #[test] @@ -293,16 +299,16 @@ mod tests { via.write(0x05, 0x00); for _ in 0..0x0F { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); for _ in 0..0x0F { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); } #[test] @@ -353,10 +359,10 @@ mod tests { // timer 1 should interrupt first for _ in 0..0x0F { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); } #[test] @@ -379,7 +385,7 @@ mod tests { // timer 2 shouldn't trigger an interrupt for _ in 0..0x08 { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } // ...but the flag register should be set @@ -387,9 +393,9 @@ mod tests { // timer 1 should then trigger an interrupt for _ in 0..0x07 { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); // ...and set the corresponding flag, plus the master bit assert_eq!( @@ -414,8 +420,8 @@ mod tests { // if we let timer 1 run again, it should set the flag again for _ in 0..0x0F { - assert_eq!(ActiveInterrupt::None, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::None, via.poll(1, 0)); } - assert_eq!(ActiveInterrupt::IRQ, via.poll(1, &SystemInfo::default())); + assert_eq!(ActiveInterrupt::IRQ, via.poll(1, 0)); } } diff --git a/src/memory/null.rs b/src/memory/null.rs index f5066739..9e6fe05b 100644 --- a/src/memory/null.rs +++ b/src/memory/null.rs @@ -1,4 +1,4 @@ -use crate::memory::{ActiveInterrupt, Memory, SystemInfo}; +use crate::memory::{ActiveInterrupt, Memory}; /// Memory that does nothing when read or written to. #[derive(Default)] @@ -36,7 +36,7 @@ impl Memory for NullMemory { fn reset(&mut self) {} - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> ActiveInterrupt { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt { ActiveInterrupt::None } } diff --git a/src/memory/ports.rs b/src/memory/ports.rs index 5fc99dc7..1df290f8 100644 --- a/src/memory/ports.rs +++ b/src/memory/ports.rs @@ -1,5 +1,3 @@ -use crate::memory::SystemInfo; - /// A Port that can be read from, written to, reset, or polled for interrupts. /// Used in the MOS 6520 PIA and the 6522 VIA. pub trait Port { @@ -12,7 +10,7 @@ pub trait Port { /// Poll the port for interrupts. A port may trigger an interrupt for any /// implementation-defined reason. - fn poll(&mut self, cycles: u32, info: &SystemInfo) -> bool; + fn poll(&mut self, cycles_since_poll: u64, total_cycle_count: u64) -> bool; /// Reset the port to its initial state, analogous to a system reboot. fn reset(&mut self); @@ -52,7 +50,7 @@ impl Port for NullPort { } } - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 454c649b..4ed73c9c 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -23,18 +23,23 @@ pub use self::winit::{WinitPlatform, WinitPlatformProvider}; /// It handles starting and ticking the system, and provides a PlatformProvider /// to the system for screen/keyboard/etc. access. pub trait Platform { + /// Return a reference to a provider for systems to interact with this platform. fn provider(&self) -> Arc; } /// A platform which can be run synchronously. pub trait SyncPlatform: Platform { + /// Run the given system within this platform. fn run(&mut self, system: Box); } /// A platform which can be run asynchronously. #[async_trait(?Send)] pub trait AsyncPlatform: Platform { + /// Set up this platform for use. async fn setup(&mut self); + + /// Execute one "step" of this platform. A step is implementation-defined. async fn tick(&mut self, system: &mut Box); } diff --git a/src/platform/winit/mod.rs b/src/platform/winit/mod.rs index 4ee4f0da..021c0533 100644 --- a/src/platform/winit/mod.rs +++ b/src/platform/winit/mod.rs @@ -183,7 +183,12 @@ impl SyncPlatform for WinitPlatform { key_state.lock().unwrap().release(key); } }, - WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::CloseRequested => { + if let Err(msg) = system.cleanup() { + println!("Error during cleanup: {}", msg); + } + *control_flow = ControlFlow::Exit; + } _ => (), }, _ => (), diff --git a/src/systems/basic.rs b/src/systems/basic.rs index f4e90fc3..fab01177 100644 --- a/src/systems/basic.rs +++ b/src/systems/basic.rs @@ -1,11 +1,14 @@ use instant::Duration; -use crate::cpu::{Mos6502, Mos6502Variant}; -use crate::memory::{ActiveInterrupt, Memory, SystemInfo}; +use crate::cpu::{ + mos6502::{Mos6502, Mos6502Variant}, + Cpu, +}; +use crate::memory::{ActiveInterrupt, Memory}; use crate::memory::{BlockMemory, BranchMemory}; use crate::platform::{PlatformProvider, WindowConfig}; use crate::roms::RomFile; -use crate::systems::{System, SystemBuilder}; +use crate::systems::{BuildableSystem, System}; use std::io::Write; use std::sync::Arc; @@ -60,15 +63,12 @@ impl Memory for MappedStdIO { fn reset(&mut self) {} - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> ActiveInterrupt { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt { ActiveInterrupt::None } } -/// A factory for creating a BasicSystem. -pub struct BasicSystemBuilder; - -impl SystemBuilder for BasicSystemBuilder { +impl BuildableSystem for BasicSystem { fn build(rom: RomFile, _config: (), platform: Arc) -> Box { let ram = BlockMemory::ram(0x4000); let io = MappedStdIO::new(platform); @@ -91,6 +91,10 @@ pub struct BasicSystem { } impl System for BasicSystem { + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> { + Box::new(&mut self.cpu) + } + fn tick(&mut self) -> Duration { Duration::from_secs_f64(1.0 / 20_000.0) * self.cpu.tick().into() } diff --git a/src/systems/c64/mod.rs b/src/systems/c64/mod.rs index 5670a09e..52d57526 100644 --- a/src/systems/c64/mod.rs +++ b/src/systems/c64/mod.rs @@ -5,14 +5,14 @@ use std::{ }; use crate::{ - cpu::{Mos6502, Mos6502Variant}, + cpu::mos6502::{Mos6502, Mos6502Variant}, + cpu::Cpu, keyboard::{ commodore::{C64KeyboardAdapter, C64SymbolAdapter, C64VirtualAdapter}, KeyAdapter, KeyMappingStrategy, SymbolAdapter, }, memory::{ mos652x::Cia, BankedMemory, BlockMemory, BranchMemory, Mos6510Port, NullMemory, NullPort, Port, - SystemInfo, }, platform::{PlatformProvider, WindowConfig}, systems::System, @@ -30,7 +30,7 @@ use self::{ vic_ii::{VicIIChip, VicIIChipIO}, }; -use super::SystemBuilder; +use super::BuildableSystem; /// Port A on the first CIA chip on the C64 deals with setting the keyboard row being scanned. struct C64Cia1PortA { @@ -59,7 +59,7 @@ impl Port for C64Cia1PortA { self.keyboard_row.set(value); } - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -119,7 +119,7 @@ impl Port for C64Cia1PortB { panic!("Tried to write to keyboard row"); } - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -188,7 +188,7 @@ impl Port for C64BankSwitching { self.selectors[5].set(if !self.hiram { 1 } else { 0 }); } - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -204,10 +204,7 @@ pub struct C64SystemConfig { pub mapping: KeyMappingStrategy, } -/// A factory for creating a Commodore 64 system. -pub struct C64SystemBuilder; - -impl SystemBuilder for C64SystemBuilder { +impl BuildableSystem for C64System { fn build( roms: C64SystemRoms, config: C64SystemConfig, @@ -316,6 +313,10 @@ pub struct C64System { } impl System for C64System { + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> { + Box::new(&mut self.cpu) + } + fn tick(&mut self) -> Duration { Duration::from_secs_f64(1.0 / 1_000_000.0) * self.cpu.tick() as u32 } diff --git a/src/systems/c64/vic_ii.rs b/src/systems/c64/vic_ii.rs index 653cfc5b..d9d807bf 100644 --- a/src/systems/c64/vic_ii.rs +++ b/src/systems/c64/vic_ii.rs @@ -1,4 +1,4 @@ -use crate::memory::{ActiveInterrupt, Memory, SystemInfo}; +use crate::memory::{ActiveInterrupt, Memory}; use crate::platform::{Color, WindowConfig}; use std::cell::RefCell; use std::rc::Rc; @@ -450,8 +450,8 @@ impl Memory for VicIIChipIO { self.chip.borrow_mut().reset(); } - fn poll(&mut self, _cycles: u32, info: &SystemInfo) -> ActiveInterrupt { - self.chip.borrow_mut().raster_counter = ((info.cycle_count / 83) % 312) as u16; + fn poll(&mut self, _cycles_since_poll: u64, total_cycle_count: u64) -> ActiveInterrupt { + self.chip.borrow_mut().raster_counter = ((total_cycle_count / 83) % 312) as u16; ActiveInterrupt::None } diff --git a/src/systems/easy.rs b/src/systems/easy.rs index a4ca9183..4895b9a1 100644 --- a/src/systems/easy.rs +++ b/src/systems/easy.rs @@ -1,11 +1,14 @@ use instant::Duration; -use crate::cpu::{MemoryIO, Mos6502, Mos6502Variant}; +use crate::cpu::{ + mos6502::{MemoryIO, Mos6502, Mos6502Variant}, + Cpu, +}; use crate::keyboard::KeyPosition; -use crate::memory::{ActiveInterrupt, BlockMemory, BranchMemory, Memory, SystemInfo}; +use crate::memory::{ActiveInterrupt, BlockMemory, BranchMemory, Memory}; use crate::platform::{Color, PlatformProvider, WindowConfig}; use crate::roms::RomFile; -use crate::systems::{System, SystemBuilder}; +use crate::systems::{BuildableSystem, System}; use std::sync::Arc; const WIDTH: u32 = 32; @@ -52,15 +55,13 @@ impl Memory for EasyIO { fn reset(&mut self) {} - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> ActiveInterrupt { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt { ActiveInterrupt::None } } /// A factory for the Easy6502 system. -pub struct Easy6502SystemBuilder; - -impl SystemBuilder for Easy6502SystemBuilder { +impl BuildableSystem for Easy6502System { fn build(rom: RomFile, _config: (), platform: Arc) -> Box { platform.request_window(WindowConfig::new(WIDTH, WIDTH, SCALE as f64)); @@ -92,6 +93,10 @@ pub struct Easy6502System { } impl System for Easy6502System { + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> { + Box::new(&mut self.cpu) + } + fn tick(&mut self) -> Duration { Duration::from_secs_f64(1.0 / 20_000.0) * self.cpu.tick().into() } diff --git a/src/systems/klaus.rs b/src/systems/klaus.rs index a43f1d93..cb81f1c3 100644 --- a/src/systems/klaus.rs +++ b/src/systems/klaus.rs @@ -1,6 +1,9 @@ use instant::Duration; -use crate::cpu::{Mos6502, Mos6502Variant}; +use crate::cpu::{ + mos6502::{Mos6502, Mos6502Variant}, + Cpu, +}; use crate::memory::BlockMemory; use crate::platform::{PlatformProvider, WindowConfig}; use crate::roms::RomFile; @@ -9,7 +12,7 @@ use std::cell::Cell; use std::rc::Rc; use std::sync::Arc; -use super::SystemBuilder; +use super::BuildableSystem; pub struct KlausSystemConfig { pub pc_report: Option>>, @@ -17,9 +20,7 @@ pub struct KlausSystemConfig { } /// A factory for creating a system that runs Klaus Dormann's 6502 CPU test suite. -pub struct KlausSystemBuilder; - -impl SystemBuilder for KlausSystemBuilder { +impl BuildableSystem for KlausSystem { fn build( rom: RomFile, config: KlausSystemConfig, @@ -44,6 +45,10 @@ pub struct KlausSystem { } impl System for KlausSystem { + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> { + Box::new(&mut self.cpu) + } + fn tick(&mut self) -> Duration { self.cpu.tick(); if let Some(pc) = &self.pc { @@ -74,7 +79,7 @@ mod tests { let platform = TextPlatform::new(); let pc = Rc::new(Cell::new(0)); - let mut system = KlausSystemBuilder::build( + let mut system = KlausSystem::build( roms, KlausSystemConfig { pc_report: Some(pc.clone()), @@ -96,7 +101,7 @@ mod tests { let platform = TextPlatform::new(); let pc = Rc::new(Cell::new(0)); - let mut system = KlausSystemBuilder::build( + let mut system = KlausSystem::build( roms, KlausSystemConfig { pc_report: Some(pc.clone()), diff --git a/src/systems/mod.rs b/src/systems/mod.rs index ad2c1537..3f7e9861 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,4 +1,7 @@ -use crate::platform::{PlatformProvider, WindowConfig}; +use crate::{ + cpu::Cpu, + platform::{PlatformProvider, WindowConfig}, +}; use instant::Duration; use std::sync::Arc; @@ -9,8 +12,8 @@ pub mod klaus; pub mod pet; pub mod vic; -pub trait SystemBuilder { - /// Create a new system from the given roms, configuration, and with I/O provided by the given +pub trait BuildableSystem { + /// Instantiate this system from the given roms, configuration, and with I/O provided by the given /// platform provider. fn build( roms: RomRegistry, @@ -21,6 +24,9 @@ pub trait SystemBuilder { /// A representation of an emulated system. pub trait System { + /// Return a mutable reference to the CPU used in this system. + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu>; + /// Advance the system by one tick. fn tick(&mut self) -> Duration; @@ -29,4 +35,9 @@ pub trait System { /// Render the current state of the system to the given framebuffer. fn render(&mut self, framebuffer: &mut [u8], window: WindowConfig); + + /// Clean up any resources used by this system. + fn cleanup(&mut self) -> Result<(), &str> { + Ok(()) + } } diff --git a/src/systems/pet/mod.rs b/src/systems/pet/mod.rs index 222f0433..9ee8f5b9 100644 --- a/src/systems/pet/mod.rs +++ b/src/systems/pet/mod.rs @@ -1,9 +1,12 @@ -use crate::cpu::{MemoryIO, Mos6502, Mos6502Variant}; +use crate::cpu::{ + mos6502::{MemoryIO, Mos6502, Mos6502Variant}, + Cpu, +}; use crate::keyboard::{KeyAdapter, KeyMappingStrategy, SymbolAdapter}; use crate::memory::mos652x::{Pia, Via}; -use crate::memory::{BlockMemory, BranchMemory, NullMemory, NullPort, Port, SystemInfo}; +use crate::memory::{BlockMemory, BranchMemory, NullMemory, NullPort, Port}; use crate::platform::{Color, PlatformProvider, WindowConfig}; -use crate::systems::{System, SystemBuilder}; +use crate::systems::{BuildableSystem, System}; use instant::Instant; use std::cell::Cell; use std::rc::Rc; @@ -60,16 +63,16 @@ impl Port for PetPia1PortA { self.keyboard_row.set(value & 0b1111); } - fn poll(&mut self, _cycles: u32, info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, total_cycle_count: u64) -> bool { // let min_elapsed = ((info.cycles_per_second as f64 / 60.0) * (2.0 / 3.0)) as u64; let min_elapsed = 0; // TODO: fix match self.last_draw_instant { Some(last_draw) => { if (last_draw.elapsed() > Duration::from_millis(17)) - && (info.cycle_count > self.last_draw_cycle + min_elapsed) + && (total_cycle_count > self.last_draw_cycle + min_elapsed) { - self.last_draw_cycle = info.cycle_count; + self.last_draw_cycle = total_cycle_count; self.last_draw_instant = Some(Instant::now()); true // false @@ -136,7 +139,7 @@ impl Port for PetPia1PortB { fn write(&mut self, _value: u8) {} - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -148,10 +151,7 @@ pub struct PetSystemConfig { pub mapping: KeyMappingStrategy, } -/// A factory for the Commodore PET. -pub struct PetSystemBuilder; - -impl SystemBuilder for PetSystemBuilder { +impl BuildableSystem for PetSystem { fn build( roms: PetSystemRoms, config: PetSystemConfig, @@ -210,6 +210,10 @@ pub struct PetSystem { } impl System for PetSystem { + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> { + Box::new(&mut self.cpu) + } + fn tick(&mut self) -> Duration { Duration::from_secs_f64(1.0 / 1_000_000.0) * self.cpu.tick() as u32 } diff --git a/src/systems/vic/chip.rs b/src/systems/vic/chip.rs index dd3d3861..d5bd598b 100644 --- a/src/systems/vic/chip.rs +++ b/src/systems/vic/chip.rs @@ -1,4 +1,4 @@ -use crate::memory::{ActiveInterrupt, Memory, SystemInfo}; +use crate::memory::{ActiveInterrupt, Memory}; use crate::platform::{Color, PlatformProvider, WindowConfig}; use std::cell::RefCell; use std::rc::Rc; @@ -493,7 +493,7 @@ impl Memory for VicChipIO { self.chip.borrow_mut().reset(); } - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> ActiveInterrupt { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> ActiveInterrupt { ActiveInterrupt::None } } diff --git a/src/systems/vic/mod.rs b/src/systems/vic/mod.rs index 8293a536..bbd78401 100644 --- a/src/systems/vic/mod.rs +++ b/src/systems/vic/mod.rs @@ -1,11 +1,14 @@ -use crate::cpu::{Mos6502, Mos6502Variant}; +use crate::cpu::{ + mos6502::{Mos6502, Mos6502Variant}, + Cpu, +}; use crate::keyboard::commodore::C64VirtualAdapter; use crate::keyboard::{ commodore::{C64KeyboardAdapter, C64SymbolAdapter}, KeyAdapter, KeyMappingStrategy, SymbolAdapter, }; use crate::memory::mos652x::Via; -use crate::memory::{BlockMemory, BranchMemory, NullMemory, NullPort, Port, SystemInfo}; +use crate::memory::{BlockMemory, BranchMemory, NullMemory, NullPort, Port}; use crate::platform::{PlatformProvider, WindowConfig}; use crate::roms::RomFile; use crate::systems::System; @@ -31,7 +34,7 @@ use wasm_bindgen::JsCast; #[cfg(target_arch = "wasm32")] use js_sys::Uint8Array; -use super::SystemBuilder; +use super::BuildableSystem; /// The set of ROM files required to run a VIC-20 system. pub struct Vic20SystemRoms { @@ -137,7 +140,7 @@ impl Port for VicVia1PortA { fn write(&mut self, _value: u8) {} - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -175,7 +178,7 @@ impl Port for VicVia2PortB { self.keyboard_col.set(value); } - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -234,7 +237,7 @@ impl Port for VicVia2PortA { fn write(&mut self, _value: u8) {} - fn poll(&mut self, _cycles: u32, _info: &SystemInfo) -> bool { + fn poll(&mut self, _cycles_since_poll: u64, _total_cycle_count: u64) -> bool { false } @@ -246,10 +249,7 @@ pub struct Vic20SystemConfig { pub mapping: KeyMappingStrategy, } -/// A factory for creating a VIC-20 system. -pub struct Vic20SystemBuilder; - -impl SystemBuilder for Vic20SystemBuilder { +impl BuildableSystem for Vic20System { fn build( roms: Vic20SystemRoms, config: Vic20SystemConfig, @@ -311,6 +311,10 @@ pub struct Vic20System { } impl System for Vic20System { + fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu> { + Box::new(&mut self.cpu) + } + fn tick(&mut self) -> instant::Duration { Duration::from_secs_f64(1.0 / 1_000_000.0) * self.cpu.tick() as u32 } diff --git a/src/wasm.rs b/src/wasm.rs index 9f4dfffc..cf6ef38b 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -18,10 +18,10 @@ use crate::{ keyboard::KeyMappingStrategy, platform::{AsyncPlatform, CanvasPlatform, Platform}, systems::{ - c64::{C64SystemBuilder, C64SystemConfig, C64SystemRoms}, - pet::{PetSystemBuilder, PetSystemConfig, PetSystemRoms}, - vic::{Vic20SystemBuilder, Vic20SystemConfig, Vic20SystemRoms}, - System, SystemBuilder, + c64::{C64System, C64SystemConfig, C64SystemRoms}, + pet::{PetSystem, PetSystemConfig, PetSystemRoms}, + vic::{Vic20System, Vic20SystemConfig, Vic20SystemRoms}, + BuildableSystem, System, }, }; @@ -79,21 +79,21 @@ impl NoentiendoBuilder { let system = self.system.as_ref().expect("System not set"); let mut system = match system.as_str() { - "pet" => PetSystemBuilder::build( + "pet" => PetSystem::build( pet_roms, PetSystemConfig { mapping: KeyMappingStrategy::Symbolic, }, platform.provider(), ), - "vic" => Vic20SystemBuilder::build( + "vic" => Vic20System::build( vic_roms, Vic20SystemConfig { mapping: KeyMappingStrategy::Symbolic, }, platform.provider(), ), - "c64" => C64SystemBuilder::build( + "c64" => C64System::build( c64_roms, C64SystemConfig { mapping: KeyMappingStrategy::Symbolic, @@ -141,7 +141,7 @@ impl Noentiendo { let interval_id = { let system = system.clone(); - let handler: Box ()> = Box::new(move || { + let handler: Box = Box::new(move || { if platform_ready.get() { let platform = platform.clone(); let system = system.clone();