From 4d1a87e2af04d5475baf938009a2a9d32e304d33 Mon Sep 17 00:00:00 2001 From: Jan-Peter von Hunnius Date: Wed, 24 Sep 2025 07:40:42 +0200 Subject: [PATCH] Fixes for 6502 CPUs. They are not complete yet --- src/components/cpu/cmos_65c02.rs | 106 --------- src/components/cpu/mod.rs | 11 +- src/components/cpu/mos_6502.rs | 361 ++++++++++++++++++++++--------- src/components/cpu/wdc_65c02.rs | 171 +++++++++++++++ 4 files changed, 437 insertions(+), 212 deletions(-) delete mode 100644 src/components/cpu/cmos_65c02.rs create mode 100644 src/components/cpu/wdc_65c02.rs diff --git a/src/components/cpu/cmos_65c02.rs b/src/components/cpu/cmos_65c02.rs deleted file mode 100644 index 3ee6d2f..0000000 --- a/src/components/cpu/cmos_65c02.rs +++ /dev/null @@ -1,106 +0,0 @@ -use std::sync::Arc; -use crate::component::Component; -use crate::pin::Pin; -use crate::components::cpu::mos_6502::MOS6502; - -pub struct CMOS65C02 { - pub(crate) base_6502: MOS6502, - // 65C02-specific state - stopped: bool, - waiting: bool, -} -impl CMOS65C02 { - pub fn new(name: String) -> Self { - let base_6502 = MOS6502::new(name); - - Self { - base_6502, - stopped: false, - waiting: false, - } - } - - // Inherit methods from MOS6502 - pub fn get_register_a(&self) -> u8 { - self.base_6502.get_register_a() - } - - pub fn set_register_a(&mut self, value: u8) { - self.base_6502.set_register_a(value) - } - - // 65C02-specific instructions - pub fn bra(&mut self, address: u16) { - // Branch always - new in 65C02 - self.base_6502.registers.pc = address; - } - - pub fn phx(&mut self) { - // Push X - new in 65C02 - // Stack push implementation would go here - let x = self.base_6502.registers.x; - println!("PHX: Pushing X register ({:02X}) to stack", x); - } - - pub fn ply(&mut self) { - // Pull Y - new in 65C02 - // Stack pull implementation would go here - println!("PLY: Pulling value from stack to Y register"); - } - - pub fn stp(&mut self) { - // Stop instruction - new in 65C02 - self.stopped = true; - println!("STP: Processor stopped"); - } - - pub fn wai(&mut self) { - // Wait for interrupt - new in 65C02 - self.waiting = true; - println!("WAI: Processor waiting for interrupt"); - } - - // Enhanced addressing modes or modified instructions - pub fn jmp_indirect(&mut self, address: u16) { - // 65C02 fixes the JMP indirect bug - // Implementation would go here - println!("JMP (indirect) to {:04X}", address); - self.base_6502.registers.pc = address; - } - - // Additional 65C02-specific methods - pub fn is_stopped(&self) -> bool { - self.stopped - } - - pub fn is_waiting(&self) -> bool { - self.waiting - } -} -// Implement Component by delegating to the base 6502 -impl Component for CMOS65C02 { - fn name(&self) -> &str { - self.base_6502.name() - } - fn pins(&self) -> &std::collections::HashMap> { - self.base_6502.pins() - } - fn get_pin(&self, name: &str) -> Option> { - self.base_6502.get_pin(name) - } - fn update(&mut self) -> Result<(), String> { - if self.stopped { - // Don't execute instructions if stopped - return Ok(()); - } - - // Delegate to base implementation - self.base_6502.update() - } - fn run(&mut self) { - self.base_6502.run() - } - fn stop(&mut self) { - self.base_6502.stop() - } -} \ No newline at end of file diff --git a/src/components/cpu/mod.rs b/src/components/cpu/mod.rs index 5847e5d..87bfb03 100644 --- a/src/components/cpu/mod.rs +++ b/src/components/cpu/mod.rs @@ -1,4 +1,9 @@ -pub mod cpu_traits; +// CPU components module +pub mod mos_6502; +pub mod wdc_65c02; pub mod intel_4004; -mod mos_6502; -mod cmos_65c02; + +// Re-export the CPU types +pub use mos_6502::MOS6502; +pub use wdc_65c02::WDC65C02; +pub use intel_4004::Intel4004; \ No newline at end of file diff --git a/src/components/cpu/mos_6502.rs b/src/components/cpu/mos_6502.rs index 2bfa1c8..57aba44 100644 --- a/src/components/cpu/mos_6502.rs +++ b/src/components/cpu/mos_6502.rs @@ -1,141 +1,296 @@ use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::Duration; + use crate::component::{BaseComponent, Component}; use crate::pin::{Pin, PinValue}; -// 6502 addressing modes -pub enum AddressingMode { - Immediate, - ZeroPage, - ZeroPageX, - ZeroPageY, - Absolute, - AbsoluteX, - AbsoluteY, - Indirect, - IndirectX, - IndirectY, - Implied, - Accumulator, - Relative, -} -// 6502 registers -#[derive(Clone, Default)] -pub struct Registers { - pub a: u8, // Accumulator - pub x: u8, // X index - pub y: u8, // Y index - pub sp: u8, // Stack pointer - pub pc: u16, // Program counter - pub sr: u8, // Status register -} -// Bit positions in status register -pub const CARRY_FLAG: u8 = 1 << 0; -pub const ZERO_FLAG: u8 = 1 << 1; -pub const INTERRUPT_DISABLE: u8 = 1 << 2; -pub const DECIMAL_MODE: u8 = 1 << 3; -pub const BREAK_COMMAND: u8 = 1 << 4; -pub const UNUSED_FLAG: u8 = 1 << 5; -pub const OVERFLOW_FLAG: u8 = 1 << 6; -pub const NEGATIVE_FLAG: u8 = 1 << 7; +/// MOS Technology 6502 CPU - 8-bit microprocessor pub struct MOS6502 { base: BaseComponent, - pub(crate) registers: Registers, + // Registers + accumulator: u8, + x_register: u8, + y_register: u8, + stack_pointer: u8, + program_counter: u16, + status_register: u8, + + // Internal state cycle_count: u64, - last_clock_state: PinValue, - // Memory interface would be added here + is_reset: bool, + is_running: bool, } + impl MOS6502 { pub fn new(name: String) -> Self { - let mut base = BaseComponent::new(name); + let pin_names = vec![ + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", // 16 address lines + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", // 8 data lines + "RW", // Read/Write + "IRQ", // Interrupt Request + "NMI", // Non-Maskable Interrupt + "RES", // Reset + "CLK", // Clock + "SYNC", // Sync + "RDY", // Ready + ]; - // Add 6502 pins - base.add_pin("clk".to_string(), PinValue::Low, false); - base.add_pin("reset".to_string(), PinValue::Low, false); - base.add_pin("irq".to_string(), PinValue::High, false); - base.add_pin("nmi".to_string(), PinValue::High, false); - base.add_pin("rw".to_string(), PinValue::High, true); // Read/Write - base.add_pin("sync".to_string(), PinValue::Low, true); // Sync - // Add address and data pins... + let pins = BaseComponent::create_pin_map(&pin_names, &name); - Self { - base, - registers: Registers::default(), + MOS6502 { + base: BaseComponent::new(name, pins), + accumulator: 0, + x_register: 0, + y_register: 0, + stack_pointer: 0xFD, + program_counter: 0xFFFC, // Reset vector location + status_register: 0x20, // Always set bit 5 cycle_count: 0, - last_clock_state: PinValue::Low, + is_reset: false, + is_running: false, } } - // Common 6502 methods that will be inherited + pub fn reset(&mut self) { - self.registers.pc = 0xFFFC; - self.registers.sp = 0xFD; - self.registers.sr = 0x34; // I flag set, others cleared - self.cycle_count = 0; + self.accumulator = 0; + self.x_register = 0; + self.y_register = 0; + self.stack_pointer = 0xFD; + self.program_counter = 0xFFFC; + self.status_register = 0x20; + self.is_reset = true; + + // Set initial pin states + self.set_address_bus(0xFFFC); + self.set_data_bus(0xFF); + self.set_rw_pin(true); // Start in read mode } - pub fn get_register_a(&self) -> u8 { - self.registers.a - } - pub fn set_register_a(&mut self, value: u8) { - self.registers.a = value; - self.update_zero_negative_flags(value); - } - // Common flag handling - pub fn update_zero_negative_flags(&mut self, value: u8) { - self.set_flag(ZERO_FLAG, value == 0); - self.set_flag(NEGATIVE_FLAG, value & 0x80 != 0); - } - pub fn set_flag(&mut self, flag: u8, condition: bool) { - if condition { - self.registers.sr |= flag; - } else { - self.registers.sr &= !flag; + + fn set_address_bus(&self, address: u16) { + for i in 0..16 { + if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + let bit_value = (address >> i) & 1; + let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value); + } + } } } - pub fn get_flag(&self, flag: u8) -> bool { - self.registers.sr & flag != 0 + fn set_data_bus(&self, data: u8) { + for i in 0..8 { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(mut pin_guard) = pin.lock() { + let bit_value = (data >> i) & 1; + let pin_value = if bit_value == 1 { PinValue::High } else { PinValue::Low }; + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value); + } + } + } } - // Common instruction implementations - pub fn lda(&mut self, value: u8) { - self.set_register_a(value); + fn set_rw_pin(&self, read: bool) { + if let Ok(pin) = self.base.get_pin("RW") { + if let Ok(mut pin_guard) = pin.lock() { + let pin_value = if read { PinValue::High } else { PinValue::Low }; + pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value); + } + } } - pub fn tax(&mut self) { - self.registers.x = self.registers.a; - self.update_zero_negative_flags(self.registers.x); + fn read_data_bus(&self) -> u8 { + let mut data = 0; + + for i in 0..8 { + if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { + if let Ok(pin_guard) = pin.lock() { + if pin_guard.read() == PinValue::High { + data |= 1 << i; + } + } + } + } + + data + } + + pub(crate) fn read_control_pins(&self) -> (bool, bool, bool, bool) { + let irq = if let Ok(pin) = self.base.get_pin("IRQ") { + if let Ok(pin_guard) = pin.lock() { + pin_guard.read() == PinValue::Low // IRQ is active low + } else { + false + } + } else { + false + }; + + let nmi = if let Ok(pin) = self.base.get_pin("NMI") { + if let Ok(pin_guard) = pin.lock() { + pin_guard.read() == PinValue::Low // NMI is active low + } else { + false + } + } else { + false + }; + + let reset = if let Ok(pin) = self.base.get_pin("RES") { + if let Ok(pin_guard) = pin.lock() { + pin_guard.read() == PinValue::Low // RESET is active low + } else { + false + } + } else { + false + }; + + let rdy = if let Ok(pin) = self.base.get_pin("RDY") { + if let Ok(pin_guard) = pin.lock() { + pin_guard.read() == PinValue::High // RDY is active high + } else { + true // Default to ready if pin doesn't exist + } + } else { + true + }; + + (irq, nmi, reset, rdy) + } + + fn execute_instruction(&mut self) { + // Simplified instruction execution - just increment PC for compilation + self.program_counter = self.program_counter.wrapping_add(1); + self.cycle_count += 1; + + // Minimal implementation to satisfy compilation + self.set_address_bus(self.program_counter); + self.set_rw_pin(true); // Always reading for now } - // More common instructions... } impl Component for MOS6502 { - fn name(&self) -> &str { - todo!() + fn name(&self) -> String { + self.base.name() } - fn pins(&self) -> &HashMap> { - todo!() + + fn pins(&self) -> &HashMap>> { + self.base.pins() } - fn get_pin(&self, _name: &str) -> Option> { - todo!() + + fn get_pin(&self, name: &str) -> Result>, String> { + self.base.get_pin(name) } - // Implement Component trait methods... - fn update(&mut self) -> Result<(), String> { - // Clock handling and instruction execution - let clk_pin = self.base.pins.get("clk").unwrap(); - let clk_value = clk_pin.read(); - // Detect rising edge - if clk_value == PinValue::High && self.last_clock_state == PinValue::Low { - self.cycle_count += 1; - // Instruction execution would happen here + + fn update(&mut self) { + if !self.is_running { + return; } - self.last_clock_state = clk_value; - Ok(()) + + let (irq, nmi, reset, rdy) = self.read_control_pins(); + + if reset && !self.is_reset { + self.reset(); + return; + } + + self.is_reset = reset; + + if rdy { + self.execute_instruction(); + } + // If RDY is low, the CPU waits } + fn run(&mut self) { - todo!() + self.is_running = true; + self.reset(); + + while self.is_running { + self.update(); + thread::sleep(Duration::from_micros(1)); + } } + fn stop(&mut self) { - todo!() + self.is_running = false; + } + + fn is_running(&self) -> bool { + self.is_running + } +} + +// 6502-specific methods +impl MOS6502 { + pub fn get_program_counter(&self) -> u16 { + self.program_counter + } + + pub fn set_program_counter(&mut self, address: u16) { + self.program_counter = address; + } + + pub fn get_accumulator(&self) -> u8 { + self.accumulator + } + + pub fn get_x_register(&self) -> u8 { + self.x_register + } + + pub fn get_y_register(&self) -> u8 { + self.y_register + } + + pub fn get_stack_pointer(&self) -> u8 { + self.stack_pointer + } + + pub fn get_status_register(&self) -> u8 { + self.status_register + } + + pub fn get_cycle_count(&self) -> u64 { + self.cycle_count + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_6502_creation() { + let cpu = MOS6502::new("CPU_6502".to_string()); + assert_eq!(cpu.name(), "CPU_6502"); + assert!(!cpu.is_running()); + } + + #[test] + fn test_6502_registers() { + let mut cpu = MOS6502::new("CPU_6502".to_string()); + + cpu.set_program_counter(0x1234); + assert_eq!(cpu.get_program_counter(), 0x1234); + + assert_eq!(cpu.get_accumulator(), 0); + assert_eq!(cpu.get_x_register(), 0); + assert_eq!(cpu.get_y_register(), 0); + assert_eq!(cpu.get_stack_pointer(), 0xFD); + assert_eq!(cpu.get_status_register(), 0x20); + } + + #[test] + fn test_6502_reset() { + let mut cpu = MOS6502::new("CPU_6502".to_string()); + + cpu.set_program_counter(0x1000); + cpu.reset(); + + assert_eq!(cpu.get_program_counter(), 0xFFFC); } - // Other trait methods... } \ No newline at end of file diff --git a/src/components/cpu/wdc_65c02.rs b/src/components/cpu/wdc_65c02.rs new file mode 100644 index 0000000..a152573 --- /dev/null +++ b/src/components/cpu/wdc_65c02.rs @@ -0,0 +1,171 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +use crate::component::Component; +use crate::components::cpu::MOS6502; +use crate::pin::Pin; + +/// WDC 65C02 - CMOS version of 6502 with additional instructions +pub struct WDC65C02 { + base: MOS6502, + // 65C02-specific state + stop_mode: bool, + wait_mode: bool, +} + +impl WDC65C02 { + pub fn new(name: String) -> Self { + WDC65C02 { + base: MOS6502::new(name), + stop_mode: false, + wait_mode: false, + } + } + + // 65C02 specific methods + pub fn enter_stop_mode(&mut self) { + self.stop_mode = true; + self.wait_mode = false; + } + + pub fn enter_wait_mode(&mut self) { + self.wait_mode = true; + self.stop_mode = false; + } + + pub fn exit_low_power_modes(&mut self) { + self.stop_mode = false; + self.wait_mode = false; + } + + pub fn is_in_stop_mode(&self) -> bool { + self.stop_mode + } + + pub fn is_in_wait_mode(&self) -> bool { + self.wait_mode + } + + // 65C02 additional instructions would be implemented here + fn execute_65c02_instruction(&mut self) { + if self.stop_mode { + // CPU is stopped - no operation + return; + } + + if self.wait_mode { + // CPU is waiting for interrupt - no operation + return; + } + + // For compilation, just call the base 6502 implementation + // In a full implementation, this would handle 65C02-specific instructions + } +} + +impl Component for WDC65C02 { + fn name(&self) -> String { + self.base.name() + } + + fn pins(&self) -> &HashMap>> { + self.base.pins() + } + + fn get_pin(&self, name: &str) -> Result>, String> { + self.base.get_pin(name) + } + + fn update(&mut self) { + if self.stop_mode || self.wait_mode { + // In low-power modes, only check for interrupts + let (irq, nmi, reset, _) = self.base.read_control_pins(); + + if nmi || reset || (irq && !self.base.get_status_register() & 0x04 == 0) { + // Exit low-power modes on interrupt or reset + self.exit_low_power_modes(); + } else { + return; // Stay in low-power mode + } + } + + // Delegate to base 6502 implementation + self.base.update(); + + // 65C02-specific processing would go here + self.execute_65c02_instruction(); + } + + fn run(&mut self) { + self.base.run(); + } + + fn stop(&mut self) { + self.base.stop(); + } + + fn is_running(&self) -> bool { + self.base.is_running() + } +} + +// 65C02-specific enhancements +impl WDC65C02 { + pub fn get_base_cpu(&self) -> &MOS6502 { + &self.base + } + + pub fn get_base_cpu_mut(&mut self) -> &mut MOS6502 { + &mut self.base + } + + // Additional 65C02 functionality can be added here + // - New addressing modes + // - Additional instructions (STZ, BRA, etc.) + // - Improved interrupt handling + // - Low-power mode control +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_65c02_creation() { + let cpu = WDC65C02::new("CPU_65C02".to_string()); + assert_eq!(cpu.name(), "CPU_65C02"); + assert!(!cpu.is_running()); + } + + #[test] + fn test_65c02_low_power_modes() { + let mut cpu = WDC65C02::new("CPU_65C02".to_string()); + + assert!(!cpu.is_in_stop_mode()); + assert!(!cpu.is_in_wait_mode()); + + cpu.enter_stop_mode(); + assert!(cpu.is_in_stop_mode()); + assert!(!cpu.is_in_wait_mode()); + + cpu.enter_wait_mode(); + assert!(!cpu.is_in_stop_mode()); + assert!(cpu.is_in_wait_mode()); + + cpu.exit_low_power_modes(); + assert!(!cpu.is_in_stop_mode()); + assert!(!cpu.is_in_wait_mode()); + } + + #[test] + fn test_65c02_inheritance() { + let mut cpu = WDC65C02::new("CPU_65C02".to_string()); + + // Verify that 65C02 inherits 6502 functionality + cpu.get_base_cpu_mut().set_program_counter(0x1000); + assert_eq!(cpu.get_base_cpu().get_program_counter(), 0x1000); + + assert_eq!(cpu.get_base_cpu().get_accumulator(), 0); + assert_eq!(cpu.get_base_cpu().get_x_register(), 0); + } +} \ No newline at end of file