Fixes for 6502 CPUs.

They are not complete yet
This commit is contained in:
Jan-Peter von Hunnius 2025-09-24 07:40:42 +02:00
parent 2a78581a2d
commit 4d1a87e2af
4 changed files with 437 additions and 212 deletions

View file

@ -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<String, Arc<Pin>> {
self.base_6502.pins()
}
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
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()
}
}

View file

@ -1,4 +1,9 @@
pub mod cpu_traits; // CPU components module
pub mod mos_6502;
pub mod wdc_65c02;
pub mod intel_4004; 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;

View file

@ -1,141 +1,296 @@
use std::collections::HashMap; 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::component::{BaseComponent, Component};
use crate::pin::{Pin, PinValue}; use crate::pin::{Pin, PinValue};
// 6502 addressing modes /// MOS Technology 6502 CPU - 8-bit microprocessor
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;
pub struct MOS6502 { pub struct MOS6502 {
base: BaseComponent, 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, cycle_count: u64,
last_clock_state: PinValue, is_reset: bool,
// Memory interface would be added here is_running: bool,
} }
impl MOS6502 { impl MOS6502 {
pub fn new(name: String) -> Self { 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 let pins = BaseComponent::create_pin_map(&pin_names, &name);
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...
Self { MOS6502 {
base, base: BaseComponent::new(name, pins),
registers: Registers::default(), 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, 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) { pub fn reset(&mut self) {
self.registers.pc = 0xFFFC; self.accumulator = 0;
self.registers.sp = 0xFD; self.x_register = 0;
self.registers.sr = 0x34; // I flag set, others cleared self.y_register = 0;
self.cycle_count = 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 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 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; 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);
}
}
}
}
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);
}
}
}
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 { } else {
self.registers.sr &= !flag; 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)
} }
pub fn get_flag(&self, flag: u8) -> bool { fn execute_instruction(&mut self) {
self.registers.sr & flag != 0 // Simplified instruction execution - just increment PC for compilation
} self.program_counter = self.program_counter.wrapping_add(1);
self.cycle_count += 1;
// Common instruction implementations // Minimal implementation to satisfy compilation
pub fn lda(&mut self, value: u8) { self.set_address_bus(self.program_counter);
self.set_register_a(value); self.set_rw_pin(true); // Always reading for now
} }
pub fn tax(&mut self) {
self.registers.x = self.registers.a;
self.update_zero_negative_flags(self.registers.x);
}
// More common instructions...
} }
impl Component for MOS6502 { impl Component for MOS6502 {
fn name(&self) -> &str { fn name(&self) -> String {
todo!() self.base.name()
} }
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
todo!() fn pins(&self) -> &HashMap<String, Arc<Mutex<Pin>>> {
self.base.pins()
} }
fn get_pin(&self, _name: &str) -> Option<Arc<Pin>> {
todo!() fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, String> {
self.base.get_pin(name)
} }
// Implement Component trait methods...
fn update(&mut self) -> Result<(), String> { fn update(&mut self) {
// Clock handling and instruction execution if !self.is_running {
let clk_pin = self.base.pins.get("clk").unwrap(); return;
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
} }
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) { 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) { 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...
} }

View file

@ -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<String, Arc<Mutex<Pin>>> {
self.base.pins()
}
fn get_pin(&self, name: &str) -> Result<Arc<Mutex<Pin>>, 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);
}
}