Starting point for MCS-4 emulation and MOS 6502 inheritance

This commit is contained in:
Jan-Peter von Hunnius 2025-09-21 18:54:27 +02:00
parent 4ddb4a060f
commit ac8ebbafe9
13 changed files with 823 additions and 4 deletions

View file

@ -0,0 +1,70 @@
use std::collections::HashMap;
use crate::{PinValue, BaseComponent, Component, Pin};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
#[derive(Clone)]
pub struct GenericClock {
base: BaseComponent,
frequency: u64,
half_period: Duration,
last_state: bool,
}
impl GenericClock {
pub fn new(name: String, frequency_hz: u64) -> Self {
let mut base = BaseComponent::new(name);
let out_pin = base.add_pin("out".to_string(), PinValue::Low, true);
let half_period_ms = 500 / frequency_hz; // Convert to milliseconds
Self {
base,
frequency: frequency_hz,
half_period: Duration::from_millis(half_period_ms),
last_state: false,
}
}
pub fn out(&self) -> Option<Arc<Pin>> {
self.base.get_pin("out")
}
}
impl Component for GenericClock {
fn name(&self) -> &str {
self.base.name()
}
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
self.base.pins()
}
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
self.base.get_pin(name)
}
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
self.base.connect_pin(pin_name, other_pin)
}
fn update(&mut self) -> Result<(), String> {
let out_pin = self.base.pins.get("out").unwrap();
// Toggle clock output
self.last_state = !self.last_state;
let new_value = if self.last_state { PinValue::High } else { PinValue::Low };
out_pin.write(new_value, true);
thread::sleep(self.half_period);
Ok(())
}
fn run(&mut self) {
self.base.run();
}
fn stop(&mut self) {
self.base.stop();
}
}

View file

@ -0,0 +1 @@
pub(crate) mod generic_clock;

View file

@ -0,0 +1,115 @@
use crate::{PinValue, Component, Pin};
use std::sync::Arc;
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<crate::Pin>> {
self.base_6502.pins()
}
fn get_pin(&self, name: &str) -> Option<Arc<crate::Pin>> {
self.base_6502.get_pin(name)
}
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<crate::Pin>) -> Result<(), String> {
self.base_6502.connect_pin(pin_name, other_pin)
}
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

@ -0,0 +1,138 @@
use crate::{PinValue, Component};
use crate::components::cpu::cmos_65c02::CMOS65C02;
use crate::components::cpu::mos_6502::{Registers, MOS6502};
pub trait CPU: Component {
fn reset(&mut self);
fn execute_instruction(&mut self);
fn get_registers(&self) -> &Registers;
fn get_registers_mut(&mut self) -> &mut Registers;
fn read_memory(&self, address: u16) -> u8;
fn write_memory(&mut self, address: u16, value: u8);
}
pub trait MOS6502Family: CPU {
// Common 6502 family methods
fn lda(&mut self, value: u8);
fn sta(&mut self, address: u16);
fn tax(&mut self);
// More common instructions...
}
pub trait CMOS65C02Extensions: MOS6502Family {
// 65C02-specific methods
fn bra(&mut self, address: u16);
fn phx(&mut self);
fn ply(&mut self);
fn stp(&mut self);
fn wai(&mut self);
}
impl CPU for MOS6502 {
fn reset(&mut self) {
todo!()
}
fn execute_instruction(&mut self) {
todo!()
}
fn get_registers(&self) -> &Registers {
todo!()
}
fn get_registers_mut(&mut self) -> &mut Registers {
todo!()
}
fn read_memory(&self, address: u16) -> u8 {
todo!()
}
fn write_memory(&mut self, address: u16, value: u8) {
todo!()
}
}
// Implement for both MOS6502 and CMOS65C02
impl MOS6502Family for MOS6502 {
fn lda(&mut self, value: u8) {
self.lda(value)
}
fn sta(&mut self, address: u16) {
todo!()
}
fn tax(&mut self) {
todo!()
}
// Implement other methods...
}
impl CPU for CMOS65C02 {
fn reset(&mut self) {
todo!()
}
fn execute_instruction(&mut self) {
todo!()
}
fn get_registers(&self) -> &Registers {
todo!()
}
fn get_registers_mut(&mut self) -> &mut Registers {
todo!()
}
fn read_memory(&self, address: u16) -> u8 {
todo!()
}
fn write_memory(&mut self, address: u16, value: u8) {
todo!()
}
}
impl MOS6502Family for CMOS65C02 {
fn lda(&mut self, value: u8) {
self.base_6502.lda(value)
}
fn sta(&mut self, address: u16) {
todo!()
}
fn tax(&mut self) {
todo!()
}
// Implement other methods...
}
impl CMOS65C02Extensions for CMOS65C02 {
fn bra(&mut self, address: u16) {
self.bra(address)
}
fn phx(&mut self) {
todo!()
}
fn ply(&mut self) {
todo!()
}
fn stp(&mut self) {
todo!()
}
fn wai(&mut self) {
todo!()
}
// Implement other 65C02-specific methods...
}

View file

@ -0,0 +1,114 @@
use std::collections::HashMap;
use crate::{PinValue, BaseComponent, Component, Pin};
use std::sync::Arc;
// Simple 4-bit and 12-bit types for the 4004
#[derive(Debug, Clone, Copy)]
struct u4(u8);
#[derive(Debug, Clone, Copy)]
struct u12(u16);
impl u4 {
fn wrapping_add(self, other: u4) -> u4 {
u4((self.0 + other.0) & 0xF)
}
}
impl u12 {
fn wrapping_add(self, other: u12) -> u12 {
u12((self.0 + other.0) & 0xFFF)
}
}
#[derive(Clone)]
pub struct Intel4004 {
base: BaseComponent,
program_counter: u12,
accumulator: u4,
last_clock_state: PinValue,
}
impl Intel4004 {
pub fn new(name: String) -> Self {
let mut base = BaseComponent::new(name);
// Add essential pins
base.add_pin("clk".to_string(), PinValue::Low, false); // Input pin
base.add_pin("reset".to_string(), PinValue::Low, false); // Input pin
base.add_pin("data".to_string(), PinValue::HighZ, false); // Bidirectional
Self {
base,
program_counter: u12(0),
accumulator: u4(0),
last_clock_state: PinValue::Low,
}
}
pub fn clk(&self) -> Option<Arc<Pin>> {
self.base.get_pin("clk")
}
pub fn reset(&self) -> Option<Arc<Pin>> {
self.base.get_pin("reset")
}
pub fn data(&self) -> Option<Arc<Pin>> {
self.base.get_pin("data")
}
}
impl Component for Intel4004 {
fn name(&self) -> &str {
self.base.name()
}
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
self.base.pins()
}
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
self.base.get_pin(name)
}
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
self.base.connect_pin(pin_name, other_pin)
}
fn update(&mut self) -> Result<(), String> {
// Read clock pin (check if any connected component is driving it low)
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 {
// Simple fetch-execute cycle
self.program_counter = self.program_counter.wrapping_add(u12(1));
// Read reset pin
let reset_pin = self.base.pins.get("reset").unwrap();
let reset_value = reset_pin.read();
if reset_value == PinValue::High {
self.program_counter = u12(0);
self.accumulator = u4(0);
println!("CPU {}: RESET", self.name());
} else {
// For now, just print state
println!("CPU {}: PC={:03X}, ACC={:X}",
self.name(), self.program_counter.0, self.accumulator.0);
}
}
self.last_clock_state = clk_value;
Ok(())
}
fn run(&mut self) {
self.base.run();
}
fn stop(&mut self) {
self.base.stop();
}
}

View file

@ -0,0 +1,4 @@
mod cpu_traits;
pub(crate) mod intel_4004;
mod mos_6502;
mod cmos_65c02;

View file

@ -0,0 +1,162 @@
use std::collections::HashMap;
use crate::{PinValue, BaseComponent, Component, Pin};
use std::sync::Arc;
// 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;
pub struct MOS6502 {
base: BaseComponent,
pub(crate) registers: Registers,
cycle_count: u64,
last_clock_state: PinValue,
// Memory interface would be added here
}
impl MOS6502 {
pub fn new(name: String) -> Self {
let mut base = BaseComponent::new(name);
// 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...
Self {
base,
registers: Registers::default(),
cycle_count: 0,
last_clock_state: PinValue::Low,
}
}
// 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;
}
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;
}
}
pub fn get_flag(&self, flag: u8) -> bool {
self.registers.sr & flag != 0
}
// Common instruction implementations
pub fn lda(&mut self, value: u8) {
self.set_register_a(value);
}
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 {
fn name(&self) -> &str {
todo!()
}
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
todo!()
}
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
todo!()
}
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
todo!()
}
// 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
}
self.last_clock_state = clk_value;
Ok(())
}
fn run(&mut self) {
todo!()
}
fn stop(&mut self) {
todo!()
}
// Other trait methods...
}

2
src/components/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub(crate) mod cpu;
pub(crate) mod clock;