feat: migrate to parking_lot mutex and add system control architecture
This commit is contained in:
parent
14060e15e5
commit
54144d10e9
29 changed files with 2387 additions and 1130 deletions
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
89
src/main.rs
89
src/main.rs
|
|
@ -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!(
|
||||
|
|
|
|||
20
src/pin.rs
20
src/pin.rs
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue