diff --git a/Cargo.toml b/Cargo.toml index 89c7f47..ff937b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,3 @@ edition = "2021" [dependencies] serialport = "4.6.0" -connect = "0.2.4" diff --git a/src/component.rs b/src/component.rs new file mode 100644 index 0000000..fbee86b --- /dev/null +++ b/src/component.rs @@ -0,0 +1,72 @@ +use std::collections::HashMap; +use std::sync::{Arc, Mutex, RwLock}; +use std::thread; +use std::time::Duration; +use crate::pin::{Pin, PinValue}; +// Component trait +pub trait Component: Send { + fn name(&self) -> &str; + fn pins(&self) -> &HashMap>; + fn get_pin(&self, name: &str) -> Option>; + fn update(&mut self) -> Result<(), String>; + fn run(&mut self); + fn stop(&mut self); +} +#[derive(Clone)] +pub struct BaseComponent { + pub name: String, + pub pins: HashMap>, + pub running: Arc>, +} +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 { + 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> { + &self.pins + } + fn get_pin(&self, name: &str) -> Option> { + self.pins.get(name).cloned() + } + 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; + } +} \ No newline at end of file diff --git a/src/components/clock/generic_clock.rs b/src/components/clock/generic_clock.rs index 79e9a78..d3ad1bc 100644 --- a/src/components/clock/generic_clock.rs +++ b/src/components/clock/generic_clock.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; -use crate::{PinValue, BaseComponent, Component, Pin}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; +use crate::component::{BaseComponent, Component}; +use crate::pin::{Pin, PinValue}; + #[derive(Clone)] pub struct GenericClock { base: BaseComponent, @@ -13,7 +15,7 @@ pub struct GenericClock { 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 _out_pin = base.add_pin("out".to_string(), PinValue::Low, true); let half_period_ms = 500 / frequency_hz; // Convert to milliseconds @@ -24,29 +26,20 @@ impl GenericClock { last_state: false, } } - pub fn out(&self) -> Option> { self.base.get_pin("out") } } - impl Component for GenericClock { fn name(&self) -> &str { self.base.name() } - fn pins(&self) -> &HashMap> { self.base.pins() } - fn get_pin(&self, name: &str) -> Option> { self.base.get_pin(name) } - - fn connect_pin(&mut self, pin_name: &str, other_pin: Arc) -> 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(); @@ -59,11 +52,9 @@ impl Component for GenericClock { Ok(()) } - fn run(&mut self) { self.base.run(); } - fn stop(&mut self) { self.base.stop(); } diff --git a/src/components/cpu/cmos_65c02.rs b/src/components/cpu/cmos_65c02.rs index 848b1e9..3ee6d2f 100644 --- a/src/components/cpu/cmos_65c02.rs +++ b/src/components/cpu/cmos_65c02.rs @@ -1,5 +1,6 @@ -use crate::{PinValue, Component, Pin}; use std::sync::Arc; +use crate::component::Component; +use crate::pin::Pin; use crate::components::cpu::mos_6502::MOS6502; pub struct CMOS65C02 { @@ -76,25 +77,17 @@ impl CMOS65C02 { 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> { + fn pins(&self) -> &std::collections::HashMap> { self.base_6502.pins() } - - fn get_pin(&self, name: &str) -> Option> { + fn get_pin(&self, name: &str) -> Option> { self.base_6502.get_pin(name) } - - fn connect_pin(&mut self, pin_name: &str, other_pin: Arc) -> 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 @@ -104,11 +97,9 @@ impl Component for CMOS65C02 { // Delegate to base implementation self.base_6502.update() } - fn run(&mut self) { self.base_6502.run() } - fn stop(&mut self) { self.base_6502.stop() } diff --git a/src/components/cpu/cpu_traits.rs b/src/components/cpu/cpu_traits.rs index 87da1b3..7af67d8 100644 --- a/src/components/cpu/cpu_traits.rs +++ b/src/components/cpu/cpu_traits.rs @@ -1,4 +1,4 @@ -use crate::{PinValue, Component}; +use crate::component::Component; use crate::components::cpu::cmos_65c02::CMOS65C02; use crate::components::cpu::mos_6502::{Registers, MOS6502}; diff --git a/src/components/cpu/intel_4004.rs b/src/components/cpu/intel_4004.rs index 5813ff4..92d801b 100644 --- a/src/components/cpu/intel_4004.rs +++ b/src/components/cpu/intel_4004.rs @@ -1,25 +1,23 @@ use std::collections::HashMap; -use crate::{PinValue, BaseComponent, Component, Pin}; use std::sync::Arc; +use crate::component::{BaseComponent, Component}; +use crate::pin::{Pin, PinValue}; // 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, @@ -27,7 +25,6 @@ pub struct Intel4004 { accumulator: u4, last_clock_state: PinValue, } - impl Intel4004 { pub fn new(name: String) -> Self { let mut base = BaseComponent::new(name); @@ -44,15 +41,12 @@ impl Intel4004 { last_clock_state: PinValue::Low, } } - pub fn clk(&self) -> Option> { self.base.get_pin("clk") } - pub fn reset(&self) -> Option> { self.base.get_pin("reset") } - pub fn data(&self) -> Option> { self.base.get_pin("data") } @@ -62,19 +56,12 @@ impl Component for Intel4004 { fn name(&self) -> &str { self.base.name() } - fn pins(&self) -> &HashMap> { self.base.pins() } - fn get_pin(&self, name: &str) -> Option> { self.base.get_pin(name) } - - fn connect_pin(&mut self, pin_name: &str, other_pin: Arc) -> 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(); @@ -103,11 +90,9 @@ impl Component for Intel4004 { self.last_clock_state = clk_value; Ok(()) } - fn run(&mut self) { self.base.run(); } - fn stop(&mut self) { self.base.stop(); } diff --git a/src/components/cpu/mod.rs b/src/components/cpu/mod.rs index f060142..5847e5d 100644 --- a/src/components/cpu/mod.rs +++ b/src/components/cpu/mod.rs @@ -1,4 +1,4 @@ -mod cpu_traits; -pub(crate) mod intel_4004; +pub mod cpu_traits; +pub mod intel_4004; mod mos_6502; mod cmos_65c02; diff --git a/src/components/cpu/mos_6502.rs b/src/components/cpu/mos_6502.rs index 322ac9a..5cfd08d 100644 --- a/src/components/cpu/mos_6502.rs +++ b/src/components/cpu/mos_6502.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use crate::{PinValue, BaseComponent, Component, Pin}; use std::sync::Arc; +use crate::component::{BaseComponent, Component}; +use crate::pin::{Pin, PinValue}; // 6502 addressing modes pub enum AddressingMode { @@ -18,7 +19,6 @@ pub enum AddressingMode { Accumulator, Relative, } - // 6502 registers #[derive(Clone, Default)] pub struct Registers { @@ -29,7 +29,6 @@ pub struct Registers { 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; @@ -39,7 +38,6 @@ 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, @@ -47,7 +45,6 @@ pub struct MOS6502 { last_clock_state: PinValue, // Memory interface would be added here } - impl MOS6502 { pub fn new(name: String) -> Self { let mut base = BaseComponent::new(name); @@ -68,7 +65,6 @@ impl MOS6502 { last_clock_state: PinValue::Low, } } - // Common 6502 methods that will be inherited pub fn reset(&mut self) { self.registers.pc = 0xFFFC; @@ -76,22 +72,18 @@ impl MOS6502 { 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; @@ -113,7 +105,6 @@ impl MOS6502 { self.registers.x = self.registers.a; self.update_zero_negative_flags(self.registers.x); } - // More common instructions... } @@ -121,42 +112,30 @@ impl Component for MOS6502 { fn name(&self) -> &str { todo!() } - fn pins(&self) -> &HashMap> { todo!() } - fn get_pin(&self, name: &str) -> Option> { todo!() } - - fn connect_pin(&mut self, pin_name: &str, other_pin: Arc) -> 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... } \ No newline at end of file diff --git a/src/components/mod.rs b/src/components/mod.rs index 2f5fcc3..183ed32 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,2 +1,2 @@ -pub(crate) mod cpu; -pub(crate) mod clock; \ No newline at end of file +pub mod cpu; +pub mod clock; diff --git a/src/connection.rs b/src/connection.rs new file mode 100644 index 0000000..38c5d05 --- /dev/null +++ b/src/connection.rs @@ -0,0 +1,12 @@ +use std::sync::Arc; +use crate::pin::Pin; + +pub fn connect_pins(source: &Arc, destination: &Arc) -> 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(()) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b4f5404..affc109 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,188 +1,10 @@ -mod systems; -mod components; +pub mod component; +pub mod pin; +pub mod connection; +pub mod components; +pub mod systems; -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>, // Use RwLock for multiple readers - pub output_enable: Arc>, // Whether this pin is driving the line - pub connections: Arc>>>, -} - -// 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>; - fn get_pin(&self, name: &str) -> Option>; - fn connect_pin(&mut self, pin_name: &str, other_pin: Arc) -> Result<(), String>; - fn update(&mut self) -> Result<(), String>; - fn run(&mut self); - fn stop(&mut self); -} -pub fn connect_pins(source: &Arc, destination: &Arc) -> 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>, - pub running: Arc>, -} - -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 { - 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> { - &self.pins - } - - fn get_pin(&self, name: &str) -> Option> { - self.pins.get(name).cloned() - } - - fn connect_pin(&mut self, pin_name: &str, other_pin: Arc) -> 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)] -mod tests { - use super::*; - - #[test] - fn it_works() { - 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()); - } - } -} \ No newline at end of file +// Re-export commonly used items for easier importing +pub use component::{Component, BaseComponent}; +pub use pin::{Pin, PinValue}; +pub use connection::connect_pins; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index ee0d0dd..44653df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -mod systems; +use rusty_emu::systems; fn main() { - + systems::intel_mcs_4::emulation(); } \ No newline at end of file diff --git a/src/pin.rs b/src/pin.rs new file mode 100644 index 0000000..6e26068 --- /dev/null +++ b/src/pin.rs @@ -0,0 +1,53 @@ +use std::sync::{Arc, Mutex, RwLock}; + +// 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>, // Use RwLock for multiple readers + pub output_enable: Arc>, // Whether this pin is driving the line + pub connections: Arc>>>, +} +// 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; + } +} diff --git a/src/systems/intel_mcs_4.rs b/src/systems/intel_mcs_4.rs index f8ee781..a904b63 100644 --- a/src/systems/intel_mcs_4.rs +++ b/src/systems/intel_mcs_4.rs @@ -1,10 +1,11 @@ use std::thread; use std::time::Duration; -use crate::Component; +use crate::component::Component; use crate::components::clock::generic_clock::GenericClock; use crate::components::cpu::intel_4004::Intel4004; +use crate::connection::connect_pins; -fn emulation() { +pub fn emulation() { println!("Starting MCS-4 emulation with pin state resolution..."); // Create components @@ -13,7 +14,7 @@ fn emulation() { // 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); + connect_pins(&clock_out, &cpu_clk).expect("Connection could not be made!"); println!("Connected clock to CPU"); } diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 3b90b22..245f27e 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1 +1 @@ -mod intel_mcs_4; \ No newline at end of file +pub mod intel_mcs_4; \ No newline at end of file