Starting point for MCS-4 emulation and MOS 6502 inheritance
This commit is contained in:
parent
4ddb4a060f
commit
ac8ebbafe9
13 changed files with 823 additions and 4 deletions
70
src/components/clock/generic_clock.rs
Normal file
70
src/components/clock/generic_clock.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
1
src/components/clock/mod.rs
Normal file
1
src/components/clock/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub(crate) mod generic_clock;
|
||||
115
src/components/cpu/cmos_65c02.rs
Normal file
115
src/components/cpu/cmos_65c02.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
138
src/components/cpu/cpu_traits.rs
Normal file
138
src/components/cpu/cpu_traits.rs
Normal 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...
|
||||
}
|
||||
114
src/components/cpu/intel_4004.rs
Normal file
114
src/components/cpu/intel_4004.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
4
src/components/cpu/mod.rs
Normal file
4
src/components/cpu/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
mod cpu_traits;
|
||||
pub(crate) mod intel_4004;
|
||||
mod mos_6502;
|
||||
mod cmos_65c02;
|
||||
162
src/components/cpu/mos_6502.rs
Normal file
162
src/components/cpu/mos_6502.rs
Normal 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
2
src/components/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub(crate) mod cpu;
|
||||
pub(crate) mod clock;
|
||||
182
src/lib.rs
182
src/lib.rs
|
|
@ -1,5 +1,165 @@
|
|||
pub fn add(left: u64, right: u64) -> u64 {
|
||||
left + right
|
||||
mod systems;
|
||||
mod components;
|
||||
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::thread;
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
// Pin value representation
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PinValue {
|
||||
High,
|
||||
Low,
|
||||
HighZ, // High impedance (disconnected)
|
||||
}
|
||||
// Pin structure with output enable
|
||||
#[derive(Clone)]
|
||||
pub struct Pin {
|
||||
pub name: String,
|
||||
pub value: Arc<RwLock<PinValue>>, // Use RwLock for multiple readers
|
||||
pub output_enable: Arc<Mutex<bool>>, // Whether this pin is driving the line
|
||||
pub connections: Arc<Mutex<Vec<Arc<Pin>>>>,
|
||||
}
|
||||
|
||||
// Component trait
|
||||
impl Pin {
|
||||
// Get the effective value considering all connected pins
|
||||
pub fn read(&self) -> PinValue {
|
||||
let connections = self.connections.lock().unwrap();
|
||||
|
||||
// Check if any connected pin is actively driving low
|
||||
for connected_pin in connections.iter() {
|
||||
let output_enable = connected_pin.output_enable.lock().unwrap();
|
||||
let value = connected_pin.value.read().unwrap();
|
||||
|
||||
if *output_enable && *value == PinValue::Low {
|
||||
return PinValue::Low;
|
||||
}
|
||||
}
|
||||
|
||||
// If no one is driving low, check if anyone is driving high
|
||||
for connected_pin in connections.iter() {
|
||||
let output_enable = connected_pin.output_enable.lock().unwrap();
|
||||
let value = connected_pin.value.read().unwrap();
|
||||
|
||||
if *output_enable && *value == PinValue::High {
|
||||
return PinValue::High;
|
||||
}
|
||||
}
|
||||
|
||||
// If no one is driving, return HighZ
|
||||
PinValue::HighZ
|
||||
}
|
||||
|
||||
// Set this pin's value and output enable
|
||||
pub fn write(&self, value: PinValue, output_enable: bool) {
|
||||
*self.value.write().unwrap() = value;
|
||||
*self.output_enable.lock().unwrap() = output_enable;
|
||||
}
|
||||
}
|
||||
|
||||
// Component trait
|
||||
pub trait Component: Send {
|
||||
fn name(&self) -> &str;
|
||||
fn pins(&self) -> &HashMap<String, Arc<Pin>>;
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>>;
|
||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String>;
|
||||
fn update(&mut self) -> Result<(), String>;
|
||||
fn run(&mut self);
|
||||
fn stop(&mut self);
|
||||
}
|
||||
pub fn connect_pins(source: &Arc<Pin>, destination: &Arc<Pin>) -> Result<(), String> {
|
||||
let mut source_connections = source.connections.lock().unwrap();
|
||||
source_connections.push(destination.clone());
|
||||
|
||||
let mut dest_connections = destination.connections.lock().unwrap();
|
||||
dest_connections.push(source.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Helper macro for easy connections
|
||||
#[macro_export]
|
||||
macro_rules! connect_pin {
|
||||
($source:expr, $dest:expr) => {
|
||||
crate::connect_pins($source, $dest).unwrap()
|
||||
};
|
||||
}// Base component implementation
|
||||
#[derive(Clone)]
|
||||
pub struct BaseComponent {
|
||||
pub name: String,
|
||||
pub pins: HashMap<String, Arc<Pin>>,
|
||||
pub running: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl BaseComponent {
|
||||
pub fn new(name: String) -> Self {
|
||||
Self {
|
||||
name,
|
||||
pins: HashMap::new(),
|
||||
running: Arc::new(Mutex::new(false)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_pin(&mut self, name: String, initial_value: PinValue, initial_output_enable: bool) -> Arc<Pin> {
|
||||
let pin = Arc::new(Pin {
|
||||
name: name.clone(),
|
||||
value: Arc::new(RwLock::new(initial_value)),
|
||||
output_enable: Arc::new(Mutex::new(initial_output_enable)),
|
||||
connections: Arc::new(Mutex::new(Vec::new())),
|
||||
});
|
||||
|
||||
self.pins.insert(name, pin.clone());
|
||||
pin
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for BaseComponent {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn pins(&self) -> &HashMap<String, Arc<Pin>> {
|
||||
&self.pins
|
||||
}
|
||||
|
||||
fn get_pin(&self, name: &str) -> Option<Arc<Pin>> {
|
||||
self.pins.get(name).cloned()
|
||||
}
|
||||
|
||||
fn connect_pin(&mut self, pin_name: &str, other_pin: Arc<Pin>) -> Result<(), String> {
|
||||
if let Some(pin) = self.pins.get_mut(pin_name) {
|
||||
let mut connections = pin.connections.lock().unwrap();
|
||||
connections.push(other_pin);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Pin {} not found", pin_name))
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self) -> Result<(), String> {
|
||||
// Base implementation does nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
*self.running.lock().unwrap() = true;
|
||||
let running = self.running.clone();
|
||||
let mut self_clone = self.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
while *running.lock().unwrap() {
|
||||
if let Err(e) = self_clone.update() {
|
||||
eprintln!("Error in {}: {}", self_clone.name, e);
|
||||
}
|
||||
thread::sleep(Duration::from_micros(10));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
*self.running.lock().unwrap() = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -8,7 +168,21 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
let result = 4;
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn list_ports() {
|
||||
let ports = serialport::available_ports().expect("No ports found!");
|
||||
for port in ports {
|
||||
let mut port = serialport::new(port.port_name, 115_200)
|
||||
.timeout(std::time::Duration::from_millis(10))
|
||||
.open().expect("Failed to open port");
|
||||
|
||||
let output = "This is a test. This is only a test.".as_bytes();
|
||||
let result = port.write(output);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/main.rs
Normal file
4
src/main.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
mod systems;
|
||||
fn main() {
|
||||
|
||||
}
|
||||
32
src/systems/intel_mcs_4.rs
Normal file
32
src/systems/intel_mcs_4.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use crate::Component;
|
||||
use crate::components::clock::generic_clock::GenericClock;
|
||||
use crate::components::cpu::intel_4004::Intel4004;
|
||||
|
||||
fn emulation() {
|
||||
println!("Starting MCS-4 emulation with pin state resolution...");
|
||||
|
||||
// Create components
|
||||
let mut clock = GenericClock::new("sys_clock".to_string(), 2); // 2Hz clock
|
||||
let mut cpu = Intel4004::new("cpu4004".to_string());
|
||||
|
||||
// Connect clock to CPU using the connection macro
|
||||
if let (Some(clock_out), Some(cpu_clk)) = (clock.out(), cpu.clk()) {
|
||||
connect_pin!(&clock_out, &cpu_clk);
|
||||
println!("Connected clock to CPU");
|
||||
}
|
||||
|
||||
// Start components
|
||||
clock.run();
|
||||
cpu.run();
|
||||
|
||||
// Let it run for a while
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
|
||||
// Stop components
|
||||
clock.stop();
|
||||
cpu.stop();
|
||||
|
||||
println!("Emulation completed");
|
||||
}
|
||||
1
src/systems/mod.rs
Normal file
1
src/systems/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
mod intel_mcs_4;
|
||||
Loading…
Add table
Add a link
Reference in a new issue