feat: migrate to parking_lot mutex and add system control architecture

This commit is contained in:
Jan-Peter von Hunnius 2026-02-28 18:27:27 +01:00
parent 14060e15e5
commit 54144d10e9
29 changed files with 2387 additions and 1130 deletions

View file

@ -1,8 +1,9 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
Arc,
};
use std::thread;
use std::time::Duration;
@ -42,10 +43,21 @@ pub trait Component: Send + Sync {
/// Returns: true if component is running, false otherwise
fn is_running(&self) -> bool;
/// Set the component's running state
/// This is used by the system controller to manage component lifecycle
/// without requiring the blocking run() method
/// Parameters: running - New running state
fn set_running(&mut self, running: bool);
/// Get a reference to this component as Any for downcasting
/// This allows runtime type inspection to access component-specific methods
/// Returns: A reference to self as &dyn Any
fn as_any(&self) -> &dyn Any;
/// Get a mutable reference to this component as Any for downcasting
/// This allows runtime type inspection to access component-specific methods mutably
/// Returns: A mutable reference to self as &mut dyn Any
fn as_any_mut(&mut self) -> &mut dyn Any;
}
/// Extended trait for components that can be run in their own threads
/// Provides automatic thread spawning functionality for components
@ -164,9 +176,17 @@ impl Component for BaseComponent {
self.running.load(Ordering::SeqCst)
}
fn set_running(&mut self, running: bool) {
self.running.store(running, Ordering::SeqCst);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl RunnableComponent for BaseComponent {}

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -16,6 +17,8 @@ pub struct GenericClock {
high_time: Duration,
low_time: Duration,
enabled: bool,
paused: bool, // Pause state for system control
pause_start: Option<Instant>, // When pause started, to track pause duration
}
impl GenericClock {
@ -32,12 +35,27 @@ impl GenericClock {
high_time: Duration::from_secs_f64(0.5 / frequency), // Will be recalculated in set_duty_cycle
low_time: Duration::from_secs_f64(0.5 / frequency), // Will be recalculated in set_duty_cycle
enabled: true,
paused: false,
pause_start: None,
};
clock.set_duty_cycle(0.5); // Initialize timing
clock
}
/// Set the clock frequency dynamically
pub fn set_frequency(&mut self, frequency: f64) {
if frequency > 0.0 {
self.frequency = frequency;
self.update_timing();
}
}
/// Get the current clock frequency
pub fn get_frequency(&self) -> f64 {
self.frequency
}
pub fn set_duty_cycle(&mut self, duty_cycle: f64) {
self.duty_cycle = duty_cycle.clamp(0.1, 0.9); // Keep within reasonable bounds
self.update_timing();
@ -56,6 +74,30 @@ impl GenericClock {
self.set_clock_output(PinValue::Low);
}
/// Pause the clock - freeze output state
pub fn pause(&mut self) {
self.paused = true;
self.pause_start = Some(Instant::now());
}
/// Resume the clock from paused state
/// Maintains phase consistency by adjusting last_transition to account for pause duration
pub fn resume(&mut self) {
if let Some(pause_start) = self.pause_start {
// Calculate how long we were paused
let pause_duration = pause_start.elapsed();
// Adjust last_transition forward by the pause duration to maintain phase
self.last_transition += pause_duration;
}
self.paused = false;
self.pause_start = None;
}
/// Check if the clock is paused
pub fn is_paused(&self) -> bool {
self.paused
}
fn frequency_to_duration(frequency: f64) -> Duration {
if frequency <= 0.0 {
Duration::from_secs(0)
@ -72,17 +114,15 @@ impl GenericClock {
fn set_clock_output(&self, value: PinValue) {
if let Ok(clock_pin) = self.base.get_pin("CLK") {
if let Ok(mut pin_guard) = clock_pin.lock() {
pin_guard.set_driver(Some(self.base.get_name().to_string()), value);
}
let mut pin_guard = clock_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), value);
}
}
fn read_enable_pin(&self) -> bool {
if let Ok(enable_pin) = self.base.get_pin("ENABLE") {
if let Ok(pin_guard) = enable_pin.lock() {
return pin_guard.read() == PinValue::High;
}
let pin_guard = enable_pin.lock();
return pin_guard.read() == PinValue::High;
}
true // Enabled by default if pin doesn't exist or can't be read
}
@ -124,6 +164,11 @@ impl Component for GenericClock {
}
fn update(&mut self) {
// Check pause state first - don't update if paused
if self.paused {
return;
}
// Read external enable control
let external_enable = self.read_enable_pin();
let should_be_enabled = self.enabled && external_enable;
@ -173,9 +218,17 @@ impl Component for GenericClock {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
// Advanced clock features

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -14,6 +15,9 @@ pub struct TwoPhaseClock {
last_transition: Instant,
phase_time: Duration,
enabled: bool,
paused: bool, // Pause state for system control
pause_start: Option<Instant>, // When pause started, to track pause duration
frequency: f64, // Store frequency for dynamic adjustment
}
impl TwoPhaseClock {
@ -34,9 +38,25 @@ impl TwoPhaseClock {
last_transition: Instant::now(),
phase_time,
enabled: true,
paused: false,
pause_start: None,
frequency,
}
}
/// Set the clock frequency dynamically
pub fn set_frequency(&mut self, frequency: f64) {
if frequency > 0.0 {
self.frequency = frequency;
self.phase_time = Duration::from_secs_f64(1.0 / frequency / 2.0);
}
}
/// Get the current clock frequency
pub fn get_frequency(&self) -> f64 {
self.frequency
}
pub fn enable(&mut self) {
self.enabled = true;
self.phi1_state = PinValue::High;
@ -55,39 +75,60 @@ impl TwoPhaseClock {
self.update_outputs();
}
/// Pause the clock - freeze output state
pub fn pause(&mut self) {
self.paused = true;
self.pause_start = Some(Instant::now());
}
/// Resume the clock from paused state
/// Maintains phase consistency by adjusting last_transition to account for pause duration
pub fn resume(&mut self) {
if let Some(pause_start) = self.pause_start {
// Calculate how long we were paused
let pause_duration = pause_start.elapsed();
// Adjust last_transition forward by the pause duration to maintain phase
self.last_transition += pause_duration;
}
self.paused = false;
self.pause_start = None;
}
/// Check if the clock is paused
pub fn is_paused(&self) -> bool {
self.paused
}
fn update_outputs(&self) {
// Set CLK output (for compatibility)
if let Ok(clk_pin) = self.base.get_pin("CLK") {
if let Ok(mut pin_guard) = clk_pin.lock() {
// CLK follows PHI1 for compatibility
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
}
let mut pin_guard = clk_pin.lock();
// CLK follows PHI1 for compatibility
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
}
// Set PHI1 output
if let Ok(phi1_pin) = self.base.get_pin("PHI1") {
if let Ok(mut pin_guard) = phi1_pin.lock() {
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
let mut pin_guard = phi1_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
// Check if pin has connections and trigger propagation
let connection_count = pin_guard.get_connected_pins().len();
if connection_count > 0 {
pin_guard.propagate();
pin_guard.propagate();
}
// Check if pin has connections and trigger propagation
let connection_count = pin_guard.get_connected_pins().len();
if connection_count > 0 {
pin_guard.propagate();
pin_guard.propagate();
}
}
// Set PHI2 output
if let Ok(phi2_pin) = self.base.get_pin("PHI2") {
if let Ok(mut pin_guard) = phi2_pin.lock() {
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi2_state);
let mut pin_guard = phi2_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi2_state);
// Check if pin has connections and trigger propagation
let connection_count = pin_guard.get_connected_pins().len();
if connection_count > 0 {
pin_guard.propagate();
}
// Check if pin has connections and trigger propagation
let connection_count = pin_guard.get_connected_pins().len();
if connection_count > 0 {
pin_guard.propagate();
}
}
}
@ -134,6 +175,11 @@ impl Component for TwoPhaseClock {
}
fn update(&mut self) {
// Check pause state first - don't update if paused
if self.paused {
return;
}
if !self.enabled {
return;
}
@ -174,7 +220,15 @@ impl Component for TwoPhaseClock {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}

View file

@ -1,4 +1,5 @@
use std::sync::{Arc, Mutex};
use parking_lot::Mutex;
use std::sync::Arc;
use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component};
@ -81,11 +82,8 @@ pub trait Intel400xClockHandling {
/// Get a clock pin value safely
fn get_clock_pin(&self, name: &str) -> PinValue {
if let Ok(pin) = self.get_base().get_pin(name) {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read_immediate()
} else {
PinValue::Low
}
let pin_guard = pin.lock();
pin_guard.read_immediate()
} else {
PinValue::Low
}
@ -134,10 +132,9 @@ pub trait Intel400xDataBus {
for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(pin_guard) = pin.lock() {
if pin_guard.read_immediate() == PinValue::High {
data |= 1 << i;
}
let pin_guard = pin.lock();
if pin_guard.read_immediate() == PinValue::High {
data |= 1 << i;
}
}
}
@ -152,18 +149,17 @@ pub trait Intel400xDataBus {
for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
let bit_value = (nibble >> i) & 1;
let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())),
pin_value,
);
}
let mut pin_guard = pin.lock();
let bit_value = (nibble >> i) & 1;
let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())),
pin_value,
);
}
}
}
@ -172,12 +168,11 @@ pub trait Intel400xDataBus {
fn tri_state_data_bus(&self) {
for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())),
PinValue::HighZ,
);
}
let mut pin_guard = pin.lock();
pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())),
PinValue::HighZ,
);
}
}
}
@ -256,11 +251,8 @@ pub trait Intel400xControlPins {
/// Read SYNC pin state
fn read_sync_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read_immediate() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read_immediate() == PinValue::High
} else {
false
}
@ -270,11 +262,8 @@ pub trait Intel400xControlPins {
fn read_cm_rom_pin(&self) -> bool {
// Note: CM pin name varies by component
if let Ok(pin) = self.get_base().get_pin("CM") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
} else {
false
}
@ -283,11 +272,8 @@ pub trait Intel400xControlPins {
/// Read the CI (I/O select) pin state
fn read_ci_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("CI") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
} else {
false
}
@ -297,11 +283,8 @@ pub trait Intel400xControlPins {
/// Hardware: RESET is active LOW (pin LOW = reset active)
fn read_reset_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("RESET") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active
} else {
false
}
@ -317,11 +300,8 @@ pub trait Intel400xResetHandling {
/// Note: RESET is active LOW (pin LOW = reset active)
fn handle_reset(&mut self, reset_pin_name: &str) -> bool {
let reset = if let Ok(pin) = self.get_base().get_pin(reset_pin_name) {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active
} else {
false
};
@ -1330,36 +1310,26 @@ pub mod utils {
/// Check if a pin value represents a logical high
pub fn is_pin_high(pin: &Arc<Mutex<Pin>>) -> bool {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
}
/// Check if a pin value represents high impedance
pub fn is_pin_high_z(pin: &Arc<Mutex<Pin>>) -> bool {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::HighZ
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::HighZ
}
/// Safely read a pin value with fallback
pub fn read_pin_safe(pin: &Arc<Mutex<Pin>>, default: PinValue) -> PinValue {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read()
} else {
default
}
pub fn read_pin_safe(pin: &Arc<Mutex<Pin>>, _default: PinValue) -> PinValue {
let pin_guard = pin.lock();
pin_guard.read()
}
/// Safely set a pin driver with error handling
pub fn set_pin_driver_safe(pin: &Arc<Mutex<Pin>>, driver_name: String, value: PinValue) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(driver_name), value);
}
let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(driver_name), value);
}
}

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -262,10 +263,9 @@ impl Intel4004 {
for i in 0..4 {
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;
}
let pin_guard = pin.lock();
if pin_guard.read() == PinValue::High {
data |= 1 << i;
}
}
}
@ -280,15 +280,14 @@ impl Intel4004 {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
let bit_value = (nibble >> 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);
}
let mut pin_guard = pin.lock();
let bit_value = (nibble >> 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);
}
}
}
@ -298,10 +297,8 @@ impl Intel4004 {
fn tri_state_data_bus(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
}
let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
}
}
}
@ -310,10 +307,9 @@ impl Intel4004 {
/// Parameters: high - true for high voltage, false for low voltage
fn set_sync(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(mut pin_guard) = pin.lock() {
let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
}
let mut pin_guard = pin.lock();
let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
}
}
@ -321,10 +317,9 @@ impl Intel4004 {
/// Parameters: high - true for high voltage, false for low voltage
fn set_cm(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM") {
if let Ok(mut pin_guard) = pin.lock() {
let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
}
let mut pin_guard = pin.lock();
let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
}
}
@ -332,31 +327,22 @@ impl Intel4004 {
/// Returns: (sync, cm, test) - State of control signals
fn read_control_pins(&self) -> (bool, bool, bool) {
let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
} else {
false
};
let cm = if let Ok(pin) = self.base.get_pin("CM") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
} else {
false
};
let test = if let Ok(pin) = self.base.get_pin("TEST") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
} else {
false
};
@ -369,21 +355,15 @@ impl Intel4004 {
/// Hardware: Intel 4004 provides two-phase clock for synchronization
fn read_clock_pins(&self) -> (PinValue, PinValue) {
let phi1 = if let Ok(pin) = self.base.get_pin("PHI1") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read()
} else {
PinValue::Low
}
let pin_guard = pin.lock();
pin_guard.read()
} else {
PinValue::Low
};
let phi2 = if let Ok(pin) = self.base.get_pin("PHI2") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read()
} else {
PinValue::Low
}
let pin_guard = pin.lock();
pin_guard.read()
} else {
PinValue::Low
};
@ -442,11 +422,8 @@ impl Intel4004 {
/// Note: RESET is active low (triggered when pin is Low)
fn handle_reset(&mut self) {
let reset = if let Ok(pin) = self.base.get_pin("RESET") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::Low
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low
} else {
false
};
@ -1579,9 +1556,17 @@ impl Component for Intel4004 {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl RunnableComponent for Intel4004 {

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
@ -73,15 +74,14 @@ impl MOS6502 {
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);
}
let 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);
}
}
}
@ -89,25 +89,23 @@ impl MOS6502 {
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);
}
let 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);
}
let 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);
}
}
@ -116,10 +114,9 @@ impl MOS6502 {
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;
}
let pin_guard = pin.lock();
if pin_guard.read() == PinValue::High {
data |= 1 << i;
}
}
}
@ -129,43 +126,31 @@ impl MOS6502 {
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
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // IRQ is active low
} 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
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // NMI is active low
} 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
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // RESET is active low
} 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
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High // RDY is active high
} else {
true
true // Default to ready if pin doesn't exist
};
(irq, nmi, reset, rdy)
@ -233,9 +218,17 @@ impl Component for MOS6502 {
self.is_running
}
fn set_running(&mut self, running: bool) {
self.is_running = running;
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
// 6502-specific methods

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use crate::component::Component;
use crate::components::cpu::MOS6502;
@ -109,9 +110,17 @@ impl Component for WDC65C02 {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
// 65C02-specific enhancements

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -382,10 +383,9 @@ impl Intel4001 {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("IO{}", i)) {
if let Ok(pin_guard) = pin.lock() {
if pin_guard.read() == PinValue::High {
data |= 1 << i;
}
let pin_guard = pin.lock();
if pin_guard.read() == PinValue::High {
data |= 1 << i;
}
}
}
@ -499,9 +499,8 @@ impl Intel4001 {
fn tri_state_io_pins(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("IO{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
}
}
@ -575,11 +574,8 @@ impl Intel4001 {
/// Hardware: CI pin distinguishes I/O vs ROM access when chip selected (CM=1)
fn read_ci_pin(&self) -> bool {
if let Ok(pin) = self.base.get_pin("CI") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
let pin_guard = pin.lock();
pin_guard.read() == PinValue::High
} else {
false
}
@ -620,22 +616,21 @@ impl Intel4001 {
fn update_io_pins(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("IO{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
match self.io_direction[i] {
IoDirection::Output => {
// Drive pin with output port value
let bit_value = (self.io_ports[i]) & 1;
let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
pin_guard.set_driver(Some(self.base.name()), pin_value);
}
IoDirection::Input => {
// Input mode - tri-state pin
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
let mut pin_guard = pin.lock();
match self.io_direction[i] {
IoDirection::Output => {
// Drive pin with output port value
let bit_value = (self.io_ports[i]) & 1;
let pin_value = if bit_value == 1 {
PinValue::High
} else {
PinValue::Low
};
pin_guard.set_driver(Some(self.base.name()), pin_value);
}
IoDirection::Input => {
// Input mode - tri-state pin
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
}
}
@ -926,9 +921,17 @@ impl Component for Intel4001 {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl RunnableComponent for Intel4001 {}
@ -1159,15 +1162,15 @@ mod tests {
// Initialize PHI1 to Low first
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
// Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access
@ -1175,7 +1178,7 @@ mod tests {
// Set Φ1 high (creating rising edge)
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -1224,9 +1227,9 @@ mod tests {
// Set up memory read operation: SYNC=1, chip_select=1, io_select=0 (memory access)
{
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access, not I/O
@ -1259,9 +1262,9 @@ mod tests {
// Set memory operation active
{
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access, not I/O
@ -1272,7 +1275,7 @@ mod tests {
// Now assert RESET
{
let mut reset_guard = reset_pin.lock().unwrap();
let mut reset_guard = reset_pin.lock();
reset_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -1290,10 +1293,10 @@ mod tests {
let d3_pin = rom.get_pin("D3").unwrap();
// After reset, all pins should be HighZ (tri-stated)
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
}
#[test]
@ -1310,19 +1313,19 @@ mod tests {
let d3_pin = rom.get_pin("D3").unwrap();
// Initially, no operation - should be tri-stated
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
// Set up memory operation but don't complete address phase
let sync_pin = rom.get_pin("SYNC").unwrap();
let cm_pin = rom.get_pin("CM").unwrap();
let ci_pin = rom.get_pin("CI").unwrap();
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access, not I/O
@ -1331,20 +1334,20 @@ mod tests {
rom.update();
// Should still be tri-stated during address phase
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
// Complete address phase but don't wait for latency
// Should still be tri-stated
rom.update();
// Should still be tri-stated (waiting for latency)
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
}
#[test]

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -407,11 +408,9 @@ impl Intel4002 {
fn tri_state_data_bus(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
// Use the same driver name as write_data_bus() to properly tri-state
pin_guard
.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
}
let mut pin_guard = pin.lock();
// Use the same driver name as write_data_bus() to properly tri-state
pin_guard.set_driver(Some(format!("{}_DATA", self.base.name())), PinValue::HighZ);
}
}
}
@ -420,9 +419,8 @@ impl Intel4002 {
fn tri_state_status_pins(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("S{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
}
}
@ -749,9 +747,17 @@ impl Component for Intel4002 {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl RunnableComponent for Intel4002 {}

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -337,9 +338,8 @@ impl Intel4003 {
fn tri_state_data_bus(&self) {
for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() {
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
}
}
}
@ -621,9 +621,17 @@ impl Component for Intel4003 {
self.base.is_running()
}
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl RunnableComponent for Intel4003 {}

View file

@ -1,6 +1,7 @@
use crate::pin::Pin;
use parking_lot::Mutex;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
/// Manages electrical connections between pins
pub struct ConnectionManager {
@ -31,32 +32,24 @@ impl ConnectionManager {
pin2: Arc<Mutex<Pin>>,
) -> Result<(), String> {
let pin1_name = {
let p1 = pin1
.lock()
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
let p1 = pin1.lock();
p1.name().to_string()
};
let pin2_name = {
let p2 = pin2
.lock()
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
let p2 = pin2.lock();
p2.name().to_string()
};
// Connect pin1 to pin2
{
let mut p1 = pin1
.lock()
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
let mut p1 = pin1.lock();
p1.connect_to(pin2.clone());
}
// Connect pin2 to pin1 (bidirectional)
{
let mut p2 = pin2
.lock()
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
let mut p2 = pin2.lock();
p2.connect_to(pin1.clone());
}
@ -96,32 +89,24 @@ impl ConnectionManager {
pin2: &Arc<Mutex<Pin>>,
) -> Result<(), String> {
let pin1_name = {
let p1 = pin1
.lock()
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
let p1 = pin1.lock();
p1.name().to_string()
};
let pin2_name = {
let p2 = pin2
.lock()
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
let p2 = pin2.lock();
p2.name().to_string()
};
// Disconnect pin1 from pin2
{
let mut p1 = pin1
.lock()
.map_err(|e| format!("Failed to lock pin1: {}", e))?;
let mut p1 = pin1.lock();
p1.disconnect_from_pin(pin2);
}
// Disconnect pin2 from pin1
{
let mut p2 = pin2
.lock()
.map_err(|e| format!("Failed to lock pin2: {}", e))?;
let mut p2 = pin2.lock();
p2.disconnect_from_pin(pin1);
}
@ -153,9 +138,7 @@ impl ConnectionManager {
/// Disconnect all pins from a given pin
pub fn disconnect_all(&mut self, pin: &Arc<Mutex<Pin>>) -> Result<(), String> {
let pin_name = {
let p = pin
.lock()
.map_err(|e| format!("Failed to lock pin: {}", e))?;
let p = pin.lock();
p.name().to_string()
};
@ -182,9 +165,7 @@ impl ConnectionManager {
// Clear the pin's internal connections
{
let mut p = pin
.lock()
.map_err(|e| format!("Failed to lock pin: {}", e))?;
let mut p = pin.lock();
p.clear_connections();
}
@ -252,8 +233,8 @@ mod tests {
assert!(connect_pins(pin1.clone(), pin2.clone()).is_ok());
// Verify connection
let p1 = pin1.lock().unwrap();
let p2 = pin2.lock().unwrap();
let p1 = pin1.lock();
let p2 = pin2.lock();
assert_eq!(p1.get_connection_count(), 1);
assert_eq!(p2.get_connection_count(), 1);
@ -271,8 +252,8 @@ mod tests {
manager.disconnect_pins(&pin1, &pin2).unwrap();
let p1 = pin1.lock().unwrap();
let p2 = pin2.lock().unwrap();
let p1 = pin1.lock();
let p2 = pin2.lock();
assert_eq!(p1.get_connection_count(), 0);
assert_eq!(p2.get_connection_count(), 0);
@ -290,7 +271,7 @@ mod tests {
// Verify all pins are connected to each other
for i in 0..pins.len() {
let pin = pins[i].lock().unwrap();
let pin = pins[i].lock();
assert_eq!(pin.get_connection_count(), pins.len() - 1);
}
}
@ -307,7 +288,7 @@ mod tests {
manager.disconnect_all(&pin1).unwrap();
let p1 = pin1.lock().unwrap();
let p1 = pin1.lock();
assert_eq!(p1.get_connection_count(), 0);
}
}

View file

@ -48,7 +48,10 @@
//! - Recovery options are provided where applicable
use super::state::GuiState;
use crate::system_config::{ConfigurableSystem, SystemController};
use eframe::egui;
use parking_lot::Mutex;
use std::sync::Arc;
/// Container for all GUI components
///
@ -120,8 +123,14 @@ impl GuiComponents {
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for control operations
pub fn render_control_panel(&mut self, ui: &mut egui::Ui, state: &mut GuiState) {
self.control_panel.render(ui, state);
/// * `system` - Optional reference to the emulator system for actual control
pub fn render_control_panel(
&mut self,
ui: &mut egui::Ui,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
self.control_panel.render(ui, state, system);
}
/// Render the memory viewer component
@ -233,7 +242,12 @@ impl ControlPanel {
/// │ [Load ROM] [Start System] [Stop] [Reset] [Close] │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&mut self, ui: &mut egui::Ui, state: &mut GuiState) {
pub fn render(
&mut self,
ui: &mut egui::Ui,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
ui.horizontal(|ui| {
// Section header
ui.heading("System Control");
@ -247,22 +261,28 @@ impl ControlPanel {
// Start/Stop system button (context-sensitive)
if ui.button(&self.start_button_text).clicked() {
if !state.system_running {
self.start_system(state);
self.start_system(state, system);
}
}
// Stop system button (only enabled when running)
if ui.button("Stop System").clicked() && state.system_running {
self.stop_system(state);
self.stop_system(state, system);
}
// Reset system button
if ui.button("Reset System").clicked() {
self.reset_system(state);
self.reset_system(state, system);
}
// Application close button
if ui.button("Close").clicked() {
// Stop the system before closing if available
if let Some(sys) = system {
let mut sys_guard = sys.lock();
// Use the SystemController trait method explicitly
let _ = SystemController::stop(&mut *sys_guard);
}
// Close application - handled by eframe
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
}
@ -278,12 +298,37 @@ impl ControlPanel {
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
fn start_system(&mut self, state: &mut GuiState) {
state.system_running = true;
self.start_button_text = "System Running...".to_string();
self.stop_button_enabled = true;
self.reset_button_enabled = true;
state.clear_error();
/// * `system` - Optional reference to the emulator system
fn start_system(
&mut self,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
// Actually start the system if available
if let Some(sys) = system {
let mut sys_guard = sys.lock();
// Use the SystemController trait method explicitly
let result = SystemController::start(&mut *sys_guard);
match result {
Ok(()) => {
state.system_running = true;
self.start_button_text = "System Running...".to_string();
self.stop_button_enabled = true;
self.reset_button_enabled = true;
state.clear_error();
}
Err(e) => {
state.set_error(format!("Failed to start system: {}", e));
}
}
} else {
// No system available - just update UI state
state.system_running = true;
self.start_button_text = "System Running...".to_string();
self.stop_button_enabled = true;
self.reset_button_enabled = true;
state.clear_error();
}
}
/// Stop the emulator system
@ -293,11 +338,35 @@ impl ControlPanel {
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
fn stop_system(&mut self, state: &mut GuiState) {
state.system_running = false;
self.start_button_text = "Start System".to_string();
self.stop_button_enabled = false;
state.clear_error();
/// * `system` - Optional reference to the emulator system
fn stop_system(
&mut self,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
// Actually stop the system if available
if let Some(sys) = system {
let mut sys_guard = sys.lock();
// Use the SystemController trait method explicitly
let result = SystemController::stop(&mut *sys_guard);
match result {
Ok(()) => {
state.system_running = false;
self.start_button_text = "Start System".to_string();
self.stop_button_enabled = false;
state.clear_error();
}
Err(e) => {
state.set_error(format!("Failed to stop system: {}", e));
}
}
} else {
// No system available - just update UI state
state.system_running = false;
self.start_button_text = "Start System".to_string();
self.stop_button_enabled = false;
state.clear_error();
}
}
/// Reset the emulator system
@ -307,7 +376,19 @@ impl ControlPanel {
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
fn reset_system(&mut self, state: &mut GuiState) {
/// * `system` - Optional reference to the emulator system
fn reset_system(
&mut self,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
// Stop the system first if available
if let Some(sys) = system {
let mut sys_guard = sys.lock();
// Use the SystemController trait method explicitly
let _ = SystemController::stop(&mut *sys_guard);
}
// Reset UI state
state.system_running = false;
state.cycle_count = 0;
self.start_button_text = "Start System".to_string();
@ -785,7 +866,7 @@ impl RomLoader {
}
// Display currently selected file
if let Some(ref file) = self.selected_file.clone() {
if let Some(ref file) = self.selected_file {
ui.label(format!("Selected: {}", file));
}

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,8 @@ pub mod state;
use crate::system_config::ConfigurableSystem;
use eframe::egui;
use std::sync::{Arc, Mutex};
use parking_lot::Mutex;
use std::sync::Arc;
/// Main GUI application structure
///
@ -93,7 +94,8 @@ impl GuiApp {
///
/// # Example
/// ```rust,no_run
/// use std::sync::{Arc, Mutex};
/// use parking_lot::Mutex;
/// use std::sync::Arc;
/// use rusty_emu::gui::GuiApp;
/// use rusty_emu::system_config::SystemFactory;
///
@ -148,9 +150,8 @@ impl eframe::App for GuiApp {
if ctrl_q_pressed {
// Stop the system if available
if let Some(system) = self.get_system() {
if let Ok(mut sys) = system.lock() {
sys.stop();
}
let mut sys = system.lock();
sys.stop();
}
// Close the GUI window
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
@ -192,9 +193,19 @@ impl GuiApp {
// Main GUI sections - organized for optimal workflow
self.components
.render_control_panel(ui, &mut self.gui_state);
self.components
.render_memory_viewer(ui, &mut self.gui_state);
.render_control_panel(ui, &mut self.gui_state, self.system.as_ref());
// Memory section: RAM and ROM side by side
ui.horizontal(|ui| {
ui.vertical(|ui| {
self.components
.render_memory_viewer(ui, &mut self.gui_state);
});
ui.vertical(|ui| {
self.components.render_rom_viewer(ui, &mut self.gui_state);
});
});
self.components
.render_register_viewer(ui, &mut self.gui_state);
self.components.render_status_bar(ui, &self.gui_state);
@ -222,7 +233,8 @@ impl GuiApp {
///
/// # Example
/// ```rust,no_run
/// use std::sync::{Arc, Mutex};
/// use parking_lot::Mutex;
/// use std::sync::Arc;
/// use rusty_emu::gui::run_gui;
/// use rusty_emu::system_config::SystemFactory;
///

View file

@ -5,7 +5,8 @@
//! to emulator state for real-time GUI updates.
use crate::system_config::ConfigurableSystem;
use std::sync::{Arc, Mutex};
use parking_lot::Mutex;
use std::sync::Arc;
/// GUI state structure containing all UI-relevant data
#[derive(Debug, Clone)]
@ -122,61 +123,55 @@ impl GuiState {
/// Update state from the current system
pub fn update_from_system(&mut self, system: &Arc<Mutex<ConfigurableSystem>>) {
if let Ok(system_guard) = system.lock() {
// Update basic system state
self.system_running = system_guard.is_running();
let system_guard = system.lock();
// Update basic system state
self.system_running = system_guard.is_running();
// Update system info if not already set
if self.system_info.is_none() {
self.system_info = Some(system_guard.get_system_info().into());
}
// Update system info if not already set
if self.system_info.is_none() {
self.system_info = Some(system_guard.get_system_info().into());
}
// Update component states
self.update_component_states(&system_guard);
// Update component states
self.update_component_states(&system_guard);
// Update CPU register state from real data
self.update_register_state(&system_guard);
// Update CPU register state from real data
self.update_register_state(&system_guard);
// Update RAM state from real data
self.update_ram_state(&system_guard);
// Update RAM state from real data
self.update_ram_state(&system_guard);
// Update ROM state from real data
self.update_rom_state(&system_guard);
// Update ROM state from real data
self.update_rom_state(&system_guard);
// Update cycle count (simulate for now)
if self.system_running {
self.cycle_count += 1;
}
// Update cycle count (simulate for now)
if self.system_running {
self.cycle_count += 1;
}
}
/// Update component running states
/// Uses try_lock() to avoid potential deadlocks when multiple threads
/// access components in different orders
/// Uses blocking locks for consistent GUI data - GUI can wait for simulation
fn update_component_states(&mut self, system: &ConfigurableSystem) {
let components = system.get_components();
// Use try_lock() to avoid blocking - if we can't get the lock immediately,
// just assume the component is not running (safe default for GUI display)
self.component_states.cpu_running = components.get("CPU_4004").map_or(false, |comp| {
comp.try_lock().map_or(false, |c| c.is_running())
});
// Use blocking locks for consistent data - GUI can wait for simulation
self.component_states.cpu_running = components
.get("CPU_4004")
.map_or(false, |comp| comp.lock().is_running());
self.component_states.ram_running = components.get("RAM_4002").map_or(false, |comp| {
comp.try_lock().map_or(false, |c| c.is_running())
});
self.component_states.ram_running = components
.get("RAM_4002")
.map_or(false, |comp| comp.lock().is_running());
self.component_states.rom_running = components
.get("ROM_4001_1")
.or_else(|| components.get("ROM_4001_2"))
.map_or(false, |comp| {
comp.try_lock().map_or(false, |c| c.is_running())
});
.map_or(false, |comp| comp.lock().is_running());
self.component_states.clock_running =
components.get("SYSTEM_CLOCK").map_or(false, |comp| {
comp.try_lock().map_or(false, |c| c.is_running())
});
self.component_states.clock_running = components
.get("SYSTEM_CLOCK")
.map_or(false, |comp| comp.lock().is_running());
}
/// Update CPU register state from real component data

View file

@ -68,13 +68,14 @@
//! cargo run -- --help
//! ```
use parking_lot::Mutex;
use rusty_emu::gui::console::{run_console, ConsoleConfig};
use rusty_emu::gui::run_gui;
use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
use std::env;
use std::fs;
use std::process;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
@ -208,7 +209,8 @@ fn main() {
// Start the emulator system using start() which doesn't block
// This allows the console interface to access the system while emulation runs
if let Ok(mut system) = system_arc.lock() {
{
let mut system = system_arc.lock();
println!("DEBUG: Starting system for console mode (non-blocking)");
system.start();
}
@ -228,7 +230,8 @@ fn main() {
// Start the emulator system using start() which doesn't block
// This allows the GUI to remain responsive while emulation runs
if let Ok(mut system) = system_arc.lock() {
{
let mut system = system_arc.lock();
println!("DEBUG: Starting system for GUI mode (non-blocking)");
system.start();
}
@ -459,13 +462,12 @@ fn run_system_demo(system: ConfigurableSystem) {
println!("CPU Speed: {} Hz", info.cpu_speed);
// Run system in a separate thread
let system_arc = std::sync::Arc::new(std::sync::Mutex::new(system));
let system_arc = Arc::new(Mutex::new(system));
let system_clone = system_arc.clone();
let handle = thread::spawn(move || {
if let Ok(mut system) = system_clone.lock() {
system.run();
}
let mut system = system_clone.lock();
system.run();
});
// Start monitoring in a separate thread
@ -487,16 +489,18 @@ fn run_system_demo(system: ConfigurableSystem) {
// Check for timeout
if start_time.elapsed() >= timeout {
println!("\nSimulation timed out after 10 seconds - stopping system");
if let Ok(mut system) = system_arc.lock() {
{
let mut system = system_arc.lock();
system.stop();
}
*running.lock().unwrap() = false;
*running.lock() = false;
break;
}
if let Ok(system) = system_arc.lock() {
{
let system = system_arc.lock();
if !system.is_running() {
*running.lock().unwrap() = false;
*running.lock() = false;
break;
}
}
@ -529,10 +533,8 @@ fn monitor_system_state(system_arc: Arc<Mutex<ConfigurableSystem>>, running_arc:
thread::sleep(Duration::from_millis(100)); // Reasonable interval for console monitoring
// Check if we should still be running (fast check)
let should_continue = match running_arc.lock() {
Ok(running_val) => *running_val,
Err(_) => true, // Continue if lock is poisoned
};
// parking_lot::Mutex::lock() returns MutexGuard directly (no Result)
let should_continue = *running_arc.lock();
if !should_continue {
break;
@ -542,12 +544,13 @@ fn monitor_system_state(system_arc: Arc<Mutex<ConfigurableSystem>>, running_arc:
// High-frequency monitoring: try_lock for immediate availability
// This matches MCS-4's 11µs cycle timing without blocking emulation
// parking_lot::Mutex::try_lock() returns Option<MutexGuard>
match system_arc.try_lock() {
Ok(system) => {
Some(system) => {
// Got the lock immediately - show detailed state (rare but possible)
display_detailed_system_state(&system, cycle);
}
Err(_) => {
None => {
// System is locked by emulation - this is normal and expected
// Show basic state without trying to acquire locks
display_basic_system_state(cycle);
@ -573,45 +576,43 @@ fn display_detailed_system_state(system: &ConfigurableSystem, cycle: u32) {
// CPU State Section
if let Some(cpu_component) = system.get_components().get("CPU_4004") {
if let Ok(cpu) = cpu_component.lock() {
println!("│ CPU STATE: │");
println!(
"│ Status: {} │",
if cpu.is_running() {
"Running"
} else {
"Stopped"
}
);
println!(
"│ Component: {} │",
cpu.name()
);
}
let cpu = cpu_component.lock();
println!("│ CPU STATE: │");
println!(
"│ Status: {} │",
if cpu.is_running() {
"Running"
} else {
"Stopped"
}
);
println!(
"│ Component: {} │",
cpu.name()
);
} else {
println!("│ CPU_4004 component not found │");
}
// RAM Section
if let Some(ram_component) = system.get_components().get("RAM_4002") {
if let Ok(ram) = ram_component.lock() {
println!(
"│ RAM_4002: {} ({}) │",
ram.name(),
if ram.is_running() {
"Running"
} else {
"Stopped"
}
);
}
let ram = ram_component.lock();
println!(
"│ RAM_4002: {} ({}) │",
ram.name(),
if ram.is_running() {
"Running"
} else {
"Stopped"
}
);
}
// Component Status Summary
let running_count = system
.get_components()
.values()
.filter(|comp| comp.lock().map_or(false, |c| c.is_running()))
.filter(|comp| comp.lock().is_running())
.count();
println!("│ │");
println!(

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq)]
@ -188,11 +189,10 @@ impl Pin {
};
for connected_pin in &self.connected_pins {
if let Ok(mut pin) = connected_pin.lock() {
// Copy all our drivers with their strengths
for (driver_name, (value, strength)) in &self.drivers {
pin.set_driver_with_strength(Some(driver_name.clone()), *value, *strength);
}
let mut pin = connected_pin.lock();
// Copy all our drivers with their strengths
for (driver_name, (value, strength)) in &self.drivers {
pin.set_driver_with_strength(Some(driver_name.clone()), *value, *strength);
}
}
@ -303,8 +303,6 @@ impl std::fmt::Display for Pin {
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use std::sync::Mutex;
#[test]
fn test_pin_creation() {
@ -359,13 +357,13 @@ mod tests {
// Connect pins
{
let mut p1 = pin1.lock().unwrap();
let mut p1 = pin1.lock();
p1.connect_to(pin2.clone());
}
// Drive pin1
{
let mut p1 = pin1.lock().unwrap();
let mut p1 = pin1.lock();
p1.set_driver(Some("test".to_string()), PinValue::High);
}
@ -373,7 +371,7 @@ mod tests {
std::thread::sleep(Duration::from_millis(1));
// Check pin2
let p2 = pin2.lock().unwrap();
let p2 = pin2.lock();
assert_eq!(p2.read(), PinValue::High);
}

View file

@ -53,9 +53,11 @@ use crate::component::Component;
use crate::components::cpu::intel_4004::Intel4004;
use crate::components::memory::intel_4001::Intel4001;
use crate::components::memory::intel_4002::Intel4002;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::time::Duration;
/// CPU register data for display purposes
#[derive(Debug, Clone, Default)]
@ -73,6 +75,74 @@ pub type RamData = Vec<(String, Vec<Vec<u8>>)>;
/// ROM data for display purposes (chip name -> bytes)
pub type RomData = Vec<(String, Vec<u8>)>;
/// Maximum allowed clock frequency (10 MHz)
pub const MAX_CLOCK_FREQUENCY: f64 = 10_000_000.0;
/// Minimum allowed clock frequency (1 Hz)
pub const MIN_CLOCK_FREQUENCY: f64 = 1.0;
/// System execution state
/// Tracks the overall state of the system for control and monitoring
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SystemState {
/// System is stopped - no components are running
Stopped,
/// System is running normally
Running,
/// System is paused - components are frozen but can be resumed
Paused,
/// System encountered an error
Error,
}
impl std::fmt::Display for SystemState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SystemState::Stopped => write!(f, "Stopped"),
SystemState::Running => write!(f, "Running"),
SystemState::Paused => write!(f, "Paused"),
SystemState::Error => write!(f, "Error"),
}
}
}
/// System controller trait for comprehensive system management
/// Provides start/stop/pause/resume functionality and clock control
pub trait SystemController {
/// Start the system from a stopped or paused state
fn start(&mut self) -> Result<(), String>;
/// Stop the system completely
fn stop(&mut self) -> Result<(), String>;
/// Pause the system - freeze all components
fn pause(&mut self) -> Result<(), String>;
/// Resume the system from a paused state
fn resume(&mut self) -> Result<(), String>;
/// Check if the system is currently running
fn is_running(&self) -> bool;
/// Check if the system is paused
fn is_paused(&self) -> bool;
/// Get the current system state
fn get_system_state(&self) -> SystemState;
/// Set the clock frequency for all clock components
fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), String>;
/// Get the current clock frequency
fn get_clock_frequency(&self) -> f64;
/// Enable or disable the clock
fn set_clock_enabled(&mut self, enabled: bool) -> Result<(), String>;
/// Check if the clock is enabled
fn is_clock_enabled(&self) -> bool;
}
/// JSON-based system configuration structures
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemConfig {
@ -348,7 +418,7 @@ impl SystemFactory {
})?;
let source_pin = {
let component = source_component.lock().unwrap();
let component = source_component.lock();
component
.get_pin(&connection_config.source.pin)
.map_err(|e| format!("Failed to get source pin: {}", e))?
@ -361,14 +431,14 @@ impl SystemFactory {
})?;
let target_pin = {
let component = target_component.lock().unwrap();
let component = target_component.lock();
component
.get_pin(&target_ref.pin)
.map_err(|e| format!("Failed to get target pin: {}", e))?
};
// Connect the pins - source connects to target for proper signal flow
let mut source_pin_guard = source_pin.lock().unwrap();
let mut source_pin_guard = source_pin.lock();
source_pin_guard.connect_to(target_pin.clone());
}
}
@ -385,6 +455,9 @@ pub struct ConfigurableSystem {
config: SystemConfig,
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
is_running: Arc<Mutex<bool>>,
is_paused: Arc<Mutex<bool>>,
clock_frequency: Arc<Mutex<f64>>,
clock_enabled: Arc<Mutex<bool>>,
component_handles: Arc<Mutex<Option<ComponentHandles>>>,
}
@ -393,10 +466,20 @@ impl ConfigurableSystem {
config: SystemConfig,
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
) -> Self {
// Extract initial clock frequency from metadata
let initial_frequency = config
.metadata
.get("cpu_speed")
.and_then(|v| v.as_f64())
.unwrap_or(750000.0);
ConfigurableSystem {
config,
components,
is_running: Arc::new(Mutex::new(false)),
is_paused: Arc::new(Mutex::new(false)),
clock_frequency: Arc::new(Mutex::new(initial_frequency)),
clock_enabled: Arc::new(Mutex::new(true)),
component_handles: Arc::new(Mutex::new(None)),
}
}
@ -407,7 +490,7 @@ impl ConfigurableSystem {
/// allowing the caller to retain access to the system via the mutex.
/// The system will continue running in the background until `stop()` is called.
pub fn start(&mut self) {
let mut is_running = self.is_running.lock().unwrap();
let mut is_running = self.is_running.lock();
if *is_running {
println!("System is already running");
return;
@ -420,31 +503,80 @@ impl ConfigurableSystem {
let mut handles = vec![];
let is_running_clone = Arc::clone(&self.is_running);
let is_paused_clone = Arc::clone(&self.is_paused);
let clock_frequency_clone = Arc::clone(&self.clock_frequency);
for (name, component) in &self.components {
let comp_clone = Arc::clone(component);
let name_clone = name.clone();
let running_clone = Arc::clone(&is_running_clone);
let paused_clone = Arc::clone(&is_paused_clone);
let frequency_clone = Arc::clone(&clock_frequency_clone);
let handle = std::thread::spawn(move || {
println!("DEBUG: Starting component thread: {}", name_clone);
if let Ok(mut comp) = comp_clone.lock() {
println!("DEBUG: Running component: {}", name_clone);
comp.run();
}
println!("DEBUG: Component {} stopped", name_clone);
// Signal that system should stop when any component stops
if let Ok(mut running) = running_clone.lock() {
*running = false;
// Run loop that doesn't hold the lock continuously
// This allows other threads (like cleanup) to access the component
loop {
// Check system-level running flag first
let should_continue = *running_clone.lock();
if !should_continue {
break;
}
// Check for pause state - skip update but keep thread alive
let is_paused = *paused_clone.lock();
if is_paused {
// While paused, sleep longer to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(10));
continue;
}
// Use immediate try_lock() for component threads
// This ensures simulation threads never wait for GUI
// GUI uses blocking locks and updates at 100 Hz
if let Some(mut comp) = comp_clone.try_lock() {
// Ensure component is in running state before calling update()
// This is needed because many components check is_running() at the start
// of update() and return early if false
if !comp.is_running() {
comp.set_running(true);
}
// Call update() for one simulation cycle
comp.update();
}
// If lock is held by GUI, skip this cycle and try again immediately
// This prioritizes simulation performance over GUI updates
// Adaptive sleep based on clock frequency
// For high-frequency clocks (>100kHz), use yield instead of sleep
// For lower frequencies, calculate appropriate sleep duration
let frequency = *frequency_clone.lock();
if frequency > 100_000.0 {
// High frequency: use yield for better responsiveness
std::thread::yield_now();
} else {
// Lower frequency: calculate sleep based on period
// Sleep for 1/10 of the clock period to balance responsiveness and CPU usage
let sleep_micros =
(1_000_000.0 / frequency / 10.0).min(1000.0).max(1.0) as u64;
std::thread::sleep(std::time::Duration::from_micros(sleep_micros));
}
}
// Stop the component after loop exits
comp_clone.lock().stop();
});
handles.push((name.clone(), handle));
}
// Store handles for later cleanup
if let Ok(mut stored_handles) = self.component_handles.lock() {
{
let mut stored_handles = self.component_handles.lock();
*stored_handles = Some(handles);
}
@ -459,7 +591,7 @@ impl ConfigurableSystem {
/// for non-blocking operation.
pub fn run(&mut self) {
self.start();
// Monitor system state during execution
self.monitor_system_state();
@ -469,22 +601,32 @@ impl ConfigurableSystem {
/// Clean up component threads and stop all components.
fn cleanup(&mut self) {
// Signal stop
if let Ok(mut is_running) = self.is_running.lock() {
// Signal stop first - this tells all component threads to stop
{
let mut is_running = self.is_running.lock();
*is_running = false;
}
// Stop all components
// Stop all components using try_lock_for to avoid deadlock
// If a component is busy (its thread holds the lock), wait briefly
// - the thread will exit when it checks the is_running flag
println!("\nStopping system components...");
for (name, component) in &self.components {
if let Ok(mut comp) = component.lock() {
if let Some(mut comp) = component.try_lock_for(Duration::from_millis(100)) {
comp.stop();
println!("Stopped component: {}", name);
} else {
// Component is busy running - it will stop when is_running becomes false
println!("Component {} is busy, will stop via is_running flag", name);
}
}
// Wait for threads
if let Ok(mut handles_guard) = self.component_handles.lock() {
// Wait for threads to finish
// Note: join() blocks indefinitely - threads should respond to is_running flag
if let Some(mut handles_guard) = self
.component_handles
.try_lock_for(Duration::from_millis(100))
{
if let Some(handles) = handles_guard.take() {
for (name, handle) in handles {
match handle.join() {
@ -511,10 +653,8 @@ impl ConfigurableSystem {
std::thread::sleep(std::time::Duration::from_millis(500));
// Check if system should stop
let should_continue = self.is_running.lock()
.map(|guard| *guard)
.unwrap_or(false);
let should_continue = *self.is_running.lock();
if !should_continue {
println!("\nSystem stop requested - terminating monitoring");
break;
@ -545,31 +685,27 @@ impl ConfigurableSystem {
// Display RAM contents prominently
if let Some(ram_component) = self.components.get("RAM_4002") {
if let Ok(ram) = ram_component.lock() {
println!("│ RAM Contents (Intel 4002 - 320 bits / 80 x 4-bit words): │");
let ram = ram_component.lock();
println!("│ RAM Contents (Intel 4002 - 320 bits / 80 x 4-bit words): │");
// Display RAM in a grid format for better readability
for bank in 0..4 {
print!("│ Bank {:2}: ", bank);
for addr in 0..4 {
let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr)) {
if let Ok(pin_guard) = pin.lock() {
// Read actual pin value if possible
match pin_guard.read() {
crate::pin::PinValue::High => "1",
crate::pin::PinValue::Low => "0",
crate::pin::PinValue::HighZ => "Z",
}
} else {
"L"
}
} else {
"?"
};
print!("{:1}", value);
}
println!(" │");
// Display RAM in a grid format for better readability
for bank in 0..4 {
print!("│ Bank {:2}: ", bank);
for addr in 0..4 {
let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr)) {
let pin_guard = pin.lock();
// Read actual pin value if possible
match pin_guard.read() {
crate::pin::PinValue::High => "1",
crate::pin::PinValue::Low => "0",
crate::pin::PinValue::HighZ => "Z",
}
} else {
"?"
};
print!("{:1}", value);
}
println!(" │");
}
} else {
println!("│ RAM_4002 component not found │");
@ -577,29 +713,27 @@ impl ConfigurableSystem {
// Display CPU state
if let Some(cpu_component) = self.components.get("CPU_4004") {
if let Ok(_cpu) = cpu_component.lock() {
println!("│ CPU State: │");
println!(
"│ Accumulator: 0x{:X} Carry: {} PC: 0x{:03X} │",
0, // Placeholder - would need CPU-specific method
false, // Placeholder - would need CPU-specific method
0
); // Placeholder - would need CPU-specific method
}
let _cpu = cpu_component.lock();
println!("│ CPU State: │");
println!(
"│ Accumulator: 0x{:X} Carry: {} PC: 0x{:03X} │",
0, // Placeholder - would need CPU-specific method
false, // Placeholder - would need CPU-specific method
0
); // Placeholder - would need CPU-specific method
}
// Display clock state
if let Some(clock_component) = self.components.get("SYSTEM_CLOCK") {
if let Ok(_clock) = clock_component.lock() {
println!("│ Clock: Running │");
}
let _clock = clock_component.lock();
println!("│ Clock: Running │");
}
// Component status summary
let running_count = self
.components
.values()
.filter(|comp| comp.lock().map_or(false, |c| c.is_running()))
.filter(|comp| comp.lock().is_running())
.count();
println!(
"│ Components: {}/{} running │",
@ -611,7 +745,8 @@ impl ConfigurableSystem {
}
pub fn stop(&mut self) {
if let Ok(mut is_running) = self.is_running.lock() {
{
let mut is_running = self.is_running.lock();
*is_running = false;
}
// Also cleanup threads
@ -619,9 +754,7 @@ impl ConfigurableSystem {
}
pub fn is_running(&self) -> bool {
self.is_running.lock()
.map(|guard| *guard)
.unwrap_or(false)
*self.is_running.lock()
}
/// Get access to components for monitoring purposes
@ -664,72 +797,68 @@ impl ConfigurableSystem {
/// Returns: Ok(()) on success, Err(String) on failure
pub fn load_program_data(&mut self, program_data: &[u8]) -> Result<(), String> {
println!(
"DEBUG: Loading {} bytes of program data into ROM components",
"Loading {} bytes of program data into ROM components",
program_data.len()
);
// Load program data into ROM_4001_1 (first 256 bytes)
if let Some(rom1_component) = self.components.get_mut("ROM_4001_1") {
if let Ok(_rom1) = rom1_component.lock() {
if let Some(rom1_component) = self.components.get("ROM_4001_1") {
let mut rom1 = rom1_component.lock();
if let Some(rom) = rom1.as_any_mut().downcast_mut::<Intel4001>() {
let rom1_data = &program_data[..program_data.len().min(256)];
println!("DEBUG: Loading {} bytes into ROM_4001_1", rom1_data.len());
println!("DEBUG: Program data: {:02X?}", rom1_data);
// TODO: Load data using Intel4001's load_rom_data method when downcast is working
println!("DEBUG: Program data should be loaded into ROM_4001_1");
rom.load_rom_data(rom1_data.to_vec(), 0)
.map_err(|e| format!("Failed to load ROM_4001_1: {}", e))?;
println!("Loaded {} bytes into ROM_4001_1", rom1_data.len());
} else {
return Err("ROM_4001_1 is not an Intel4001 component".to_string());
}
} else {
println!("DEBUG: Warning - ROM_4001_1 component not found");
println!("Warning - ROM_4001_1 component not found");
}
// Load remaining data into ROM_4001_2 if program is larger than 256 bytes
if program_data.len() > 256 {
if let Some(rom2_component) = self.components.get_mut("ROM_4001_2") {
if let Ok(_rom2) = rom2_component.lock() {
if let Some(rom2_component) = self.components.get("ROM_4001_2") {
let mut rom2 = rom2_component.lock();
if let Some(rom) = rom2.as_any_mut().downcast_mut::<Intel4001>() {
let rom2_data = &program_data[256..program_data.len().min(512)];
println!("DEBUG: Loading {} bytes into ROM_4001_2", rom2_data.len());
// TODO: Load data using Intel4001's load_rom_data method when available
rom.load_rom_data(rom2_data.to_vec(), 0)
.map_err(|e| format!("Failed to load ROM_4001_2: {}", e))?;
println!("Loaded {} bytes into ROM_4001_2", rom2_data.len());
} else {
return Err("ROM_4001_2 is not an Intel4001 component".to_string());
}
} else {
println!("DEBUG: Warning - ROM_4001_2 component not found");
println!("Warning - ROM_4001_2 component not found");
}
}
println!("DEBUG: Program loading completed");
println!("Program loading completed");
Ok(())
}
/// Get CPU register data from Intel 4004 CPU
/// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise
/// Uses try_lock() to avoid blocking when component is busy
/// Uses blocking lock - GUI can wait for simulation to release lock
pub fn get_cpu_register_data(&self) -> Option<CpuRegisterData> {
// Look for Intel 4004 CPU components
for (_name, component) in &self.components {
match component.try_lock() {
Ok(comp) => {
// Try to downcast to Intel4004
if let Some(cpu) = comp.as_any().downcast_ref::<Intel4004>() {
let mut index_registers = [0u8; 16];
for i in 0..16 {
index_registers[i] = cpu.get_register(i as u8).unwrap_or(0);
}
// Use blocking lock - GUI can wait for simulation
let comp = component.lock();
// Try to downcast to Intel4004
if let Some(cpu) = comp.as_any().downcast_ref::<Intel4004>() {
let mut index_registers = [0u8; 16];
for i in 0..16 {
index_registers[i] = cpu.get_register(i as u8).unwrap_or(0);
}
return Some(CpuRegisterData {
program_counter: cpu.get_program_counter(),
accumulator: cpu.get_accumulator(),
carry: cpu.get_carry(),
stack_pointer: cpu.get_stack_pointer(),
index_registers,
});
}
}
Err(std::sync::TryLockError::WouldBlock) => {
// Component is busy, continue to next component
continue;
}
Err(std::sync::TryLockError::Poisoned(_)) => {
// Handle poisoned mutex - continue to next component
continue;
}
return Some(CpuRegisterData {
program_counter: cpu.get_program_counter(),
accumulator: cpu.get_accumulator(),
carry: cpu.get_carry(),
stack_pointer: cpu.get_stack_pointer(),
index_registers,
});
}
}
None
@ -738,45 +867,35 @@ impl ConfigurableSystem {
/// Get RAM data from Intel 4002 RAM chips
/// Parameters: max_rows - Maximum number of rows to return per bank
/// Returns: Vector of (chip_name, banks) where each bank is a vector of nibbles
/// Uses try_lock() to avoid blocking when component is busy
/// Uses blocking lock - GUI can wait for simulation to release lock
pub fn get_ram_data(&self, max_rows: usize) -> RamData {
let mut ram_data = Vec::new();
for (name, component) in &self.components {
match component.try_lock() {
Ok(comp) => {
// Try to downcast to Intel4002
if let Some(ram) = comp.as_any().downcast_ref::<Intel4002>() {
let mut banks = Vec::new();
for (_name, component) in &self.components {
// Use blocking lock - GUI can wait for simulation
let comp = component.lock();
// Try to downcast to Intel4002
if let Some(ram) = comp.as_any().downcast_ref::<Intel4002>() {
let mut banks = Vec::new();
// Intel 4002 has 4 banks of 16 4-bit words each (64 nibbles total per chip)
// But actually it has 4 main memory registers, each with 16 4-bit characters
// and 4 status characters = 4 x 20 = 80 nibbles
for bank in 0..4 {
let mut bank_data = Vec::new();
// Read up to max_rows * 4 nibbles per bank
// The read_ram_direct takes a single address parameter
for addr in 0..(max_rows * 4).min(20) {
// Calculate the combined address for this bank and offset
let full_addr = (bank * 20 + addr) as u8;
// Use read_ram_direct to get the nibble at this address
let value = ram.read_ram_direct(full_addr).unwrap_or(0);
bank_data.push(value);
}
banks.push(bank_data);
}
ram_data.push((name.clone(), banks));
// Intel 4002 has 4 banks of 16 4-bit words each (64 nibbles total per chip)
// But actually it has 4 main memory registers, each with 16 4-bit characters
// and 4 status characters = 4 x 20 = 80 nibbles
for bank in 0..4 {
let mut bank_data = Vec::new();
// Read up to max_rows * 4 nibbles per bank
// The read_ram_direct takes a single address parameter
for addr in 0..(max_rows * 4).min(20) {
// Calculate the combined address for this bank and offset
let full_addr = (bank * 20 + addr) as u8;
// Use read_ram_direct to get the nibble at this address
let value = ram.read_ram_direct(full_addr).unwrap_or(0);
bank_data.push(value);
}
banks.push(bank_data);
}
Err(std::sync::TryLockError::WouldBlock) => {
// Component is busy, skip to next component
continue;
}
Err(std::sync::TryLockError::Poisoned(_)) => {
// Handle poisoned mutex - skip to next component
continue;
}
ram_data.push((_name.clone(), banks));
}
}
@ -786,34 +905,24 @@ impl ConfigurableSystem {
/// Get ROM data from Intel 4001 ROM chips
/// Parameters: max_rows - Maximum number of bytes to return per chip
/// Returns: Vector of (chip_name, bytes)
/// Uses try_lock() to avoid blocking when component is busy
/// Uses blocking lock - GUI can wait for simulation to release lock
pub fn get_rom_data(&self, max_rows: usize) -> RomData {
let mut rom_data = Vec::new();
for (name, component) in &self.components {
match component.try_lock() {
Ok(comp) => {
// Try to downcast to Intel4001
if let Some(rom) = comp.as_any().downcast_ref::<Intel4001>() {
let mut data = Vec::new();
// Use blocking lock - GUI can wait for simulation
let comp = component.lock();
// Try to downcast to Intel4001
if let Some(rom) = comp.as_any().downcast_ref::<Intel4001>() {
let mut data = Vec::new();
// Intel 4001 has 256 bytes of ROM
for addr in 0..max_rows.min(256) {
let value = rom.read_rom(addr as u8).unwrap_or(0);
data.push(value);
}
// Intel 4001 has 256 bytes of ROM
for addr in 0..max_rows.min(256) {
let value = rom.read_rom(addr as u8).unwrap_or(0);
data.push(value);
}
rom_data.push((name.clone(), data));
}
}
Err(std::sync::TryLockError::WouldBlock) => {
// Component is busy, skip to next component
continue;
}
Err(std::sync::TryLockError::Poisoned(_)) => {
// Handle poisoned mutex - skip to next component
continue;
}
rom_data.push((name.clone(), data));
}
}
@ -821,6 +930,250 @@ impl ConfigurableSystem {
}
}
// Implement SystemController trait for ConfigurableSystem
impl SystemController for ConfigurableSystem {
fn start(&mut self) -> Result<(), String> {
let is_paused = *self.is_paused.lock();
if is_paused {
// Resume from pause
return self.resume();
}
let is_running = *self.is_running.lock();
if is_running {
return Err("System is already running".to_string());
}
// Call the inherent method explicitly to avoid infinite recursion
ConfigurableSystem::start(self);
Ok(())
}
fn stop(&mut self) -> Result<(), String> {
let is_running = *self.is_running.lock();
if !is_running {
return Err("System is not running".to_string());
}
// Reset pause state when stopping
{
let mut paused = self.is_paused.lock();
*paused = false;
}
// Call the inherent method explicitly to avoid infinite recursion
ConfigurableSystem::stop(self);
Ok(())
}
fn pause(&mut self) -> Result<(), String> {
let is_running = *self.is_running.lock();
if !is_running {
return Err("System is not running".to_string());
}
let is_paused = *self.is_paused.lock();
if is_paused {
return Err("System is already paused".to_string());
}
// Set pause state
{
let mut paused = self.is_paused.lock();
*paused = true;
}
// Propagate pause to ALL components (clocks, CPU, memory)
// The system-level is_paused flag handles the thread-level pause,
// but we also propagate to components for component-specific pause behavior
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
clock.pause();
println!("Clock {} paused", name);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
clock.pause();
println!("Clock {} paused", name);
}
// Note: CPU and memory components don't have explicit pause methods,
// but they are effectively paused by the system-level is_paused flag
// which prevents their update() from being called in the thread loop
}
println!("System paused - all components frozen");
Ok(())
}
fn resume(&mut self) -> Result<(), String> {
let is_paused = *self.is_paused.lock();
if !is_paused {
return Err("System is not paused".to_string());
}
// Clear pause state
{
let mut paused = self.is_paused.lock();
*paused = false;
}
// Propagate resume to ALL components (clocks, CPU, memory)
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
clock.resume();
println!("Clock {} resumed", name);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
clock.resume();
println!("Clock {} resumed", name);
}
// Note: CPU and memory components resume automatically via the
// system-level is_paused flag being cleared in the thread loop
}
println!("System resumed - all components running");
Ok(())
}
fn is_running(&self) -> bool {
*self.is_running.lock()
}
fn is_paused(&self) -> bool {
*self.is_paused.lock()
}
fn get_system_state(&self) -> SystemState {
let is_running = *self.is_running.lock();
let is_paused = *self.is_paused.lock();
if !is_running {
SystemState::Stopped
} else if is_paused {
SystemState::Paused
} else {
SystemState::Running
}
}
fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), String> {
if frequency < MIN_CLOCK_FREQUENCY {
return Err(format!(
"Frequency must be at least {} Hz",
MIN_CLOCK_FREQUENCY
));
}
if frequency > MAX_CLOCK_FREQUENCY {
return Err(format!(
"Frequency must not exceed {} Hz",
MAX_CLOCK_FREQUENCY
));
}
// Update our stored frequency
{
let mut freq = self.clock_frequency.lock();
*freq = frequency;
}
// Update all clock components
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
clock.set_frequency(frequency);
println!("Updated clock frequency for {} to {} Hz", name, frequency);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
clock.set_frequency(frequency);
println!("Updated clock frequency for {} to {} Hz", name, frequency);
}
}
Ok(())
}
fn get_clock_frequency(&self) -> f64 {
*self.clock_frequency.lock()
}
fn set_clock_enabled(&mut self, enabled: bool) -> Result<(), String> {
// Update our stored state
{
let mut clk_enabled = self.clock_enabled.lock();
*clk_enabled = enabled;
}
// Update all clock components
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
if enabled {
clock.enable();
} else {
clock.disable();
}
println!(
"Clock {} {}",
name,
if enabled { "enabled" } else { "disabled" }
);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
if enabled {
clock.enable();
} else {
clock.disable();
}
println!(
"Clock {} {}",
name,
if enabled { "enabled" } else { "disabled" }
);
}
}
Ok(())
}
fn is_clock_enabled(&self) -> bool {
*self.clock_enabled.lock()
}
}
#[derive(Debug, Clone)]
pub struct SystemInfo {
pub name: String,