diff --git a/Cargo.lock b/Cargo.lock index e8205a1..f46d288 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -2503,6 +2503,7 @@ dependencies = [ "eframe", "egui", "gtk", + "parking_lot", "ratatui", "raw-window-handle 0.5.2", "rfd", diff --git a/Cargo.toml b/Cargo.toml index 6138d2d..774edfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ egui = "0.24" eframe = { version = "0.24", default-features = false, features = ["default_fonts", "glow", "persistence"] } rfd = "0.14" raw-window-handle = "0.5" +parking_lot = "0.12" # Force compatible versions for Rust 1.81.0 [dependencies.tokio] diff --git a/plans/observer_pattern_and_ui_layout.md b/plans/observer_pattern_and_ui_layout.md new file mode 100644 index 0000000..06cb31f --- /dev/null +++ b/plans/observer_pattern_and_ui_layout.md @@ -0,0 +1,296 @@ +# Observer Pattern and UI Layout Configuration Implementation Plan + +## Overview + +This plan implements two key improvements to the rusty_emu project: + +1. **Observer Pattern**: Replace inefficient polling with event-driven state updates +2. **UI Layout Configuration**: Enable configurable GUI layouts from JSON config files + +## Current Issues + +### Performance Issues +- Console polls system state at 100ms intervals (inefficient) +- GUI uses `try_lock()` but still has potential contention +- No way to configure GUI layout from JSON config + +### Architecture Limitations +- Tight coupling between components and UI +- No standardized way to notify UI of state changes +- Hardcoded UI layouts + +## Solution Architecture + +### Observer Pattern Implementation + +```mermaid +graph TD + A[SystemObserver Trait] --> B[CPU Observer] + A --> C[RAM Observer] + A --> D[ROM Observer] + + B --> E[ConfigurableSystem] + C --> E + D --> E + + E --> F[Console UI] + E --> G[GUI Interface] + + F --> H[Event-driven Updates] + G --> H +``` + +### UI Layout Configuration + +```mermaid +graph LR + A[SystemConfig JSON] --> B[UI Layout Section] + B --> C[Component Positions] + B --> D[Component Sizes] + B --> E[Display Options] + + C --> F[GUI Renderer] + D --> F + E --> F +``` + +## Implementation Details + +### 1. SystemObserver Trait (Fine-grained with Filtering) + +```rust +#[derive(Debug, Clone)] +pub enum ObserverFilter { + All, + CpuOnly, + MemoryOnly, + StateChangesOnly, + RegisterChangesOnly, +} + +pub trait SystemObserver: Send + Sync { + fn get_filter(&self) -> ObserverFilter { return ObserverFilter::All; } + + fn on_state_change(&self, component: &str, old_state: ComponentState, new_state: ComponentState); + fn on_ram_change(&self, chip: &str, bank: usize, address: u8, old_value: u8, new_value: u8); + fn on_rom_change(&self, chip: &str, address: u16, old_value: u8, new_value: u8); + fn on_cpu_register_change(&self, cpu: &str, register: &str, old_value: u8, new_value: u8); + fn on_cpu_pc_change(&self, cpu: &str, old_pc: u16, new_pc: u16); + fn on_cpu_instruction_executed(&self, cpu: &str, instruction: u8, pc: u16); +} +``` + +### 2. Enhanced SystemConfig + +```rust +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SystemConfig { + pub name: String, + pub description: String, + pub version: String, + pub metadata: HashMap, + pub components: HashMap, + pub connections: HashMap, + pub layout: Option, + // NEW: UI Layout configuration + pub ui_layout: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UiLayoutConfig { + pub console: Option, + pub gui: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GuiLayoutConfig { + pub components: HashMap, + pub grid_size: Option<[usize; 2]>, + pub refresh_rate_ms: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ComponentLayoutConfig { + pub position: [usize; 2], + pub size: [usize; 2], + pub visible: Option, + pub display_format: Option, +} +``` + +### 3. Observer Registration in ConfigurableSystem + +```rust +pub struct ConfigurableSystem { + config: SystemConfig, + components: HashMap>>>, + is_running: Arc>, + component_handles: Arc>>, + // NEW: Observer registry + observers: Arc>>>, +} +``` + +### 4. Component Integration + +Each component will notify observers of state changes: + +- **Intel4004 CPU**: Register changes, PC changes, instruction execution +- **Intel4001 ROM**: Memory reads, address changes +- **Intel4002 RAM**: Memory reads/writes, I/O changes + +### 5. UI Updates + +#### Console Updates (Performance Target: 50% CPU reduction, sub-10ms responsiveness) +- Replace 100ms polling with event-driven updates using observer pattern +- Observer receives notifications and updates display immediately +- Implement filtering to reduce unnecessary UI updates +- Target: Reduce CPU usage by 50% compared to current polling +- Target: Update responsiveness to under 10ms for state changes + +#### GUI Updates +- Use layout configuration from JSON for component positioning/sizing +- Position components according to config (basic implementation) +- Show/hide components based on config visibility settings +- Use observer pattern for real-time updates +- Support configurable refresh rates from UI layout config + +## Configuration Examples + +### Enhanced JSON Config + +```json +{ + "name": "IntelMcs4", + "description": "Basic MCS-4 System with CPU, Clock, 2 ROMs, and 1 RAM", + "version": "1.0", + "metadata": { + "author": "RustyEmu", + "created": "2024-01-01", + "cpu_speed": 750000, + "architecture": "mcs4", + "rom_count": 2, + "ram_count": 1 + }, + "components": { + "CPU_4004": { + "single": { + "component_type": "intel_4004", + "name": "CPU_4004", + "properties": { + "clock_speed": 750000 + } + } + } + // ... other components + }, + "connections": { + // ... existing connections + }, + "layout": { + "grid_size": [10, 10], + "positions": { + "cpu": [1, 1], + "clock": [0, 0], + "rom1": [2, 0], + "rom2": [3, 0], + "ram": [2, 2] + } + }, + "ui_layout": { + "console": { + "refresh_rate_ms": 50, + "show_ram": true, + "show_registers": true, + "show_system_info": true + }, + "gui": { + "grid_size": [20, 20], + "components": { + "CPU_4004": { + "position": [1, 1], + "size": [20, 10], + "visible": true, + "display_format": "hex" + }, + "RAM_4002": { + "position": [3, 2], + "size": [25, 15], + "visible": true, + "display_format": "hex" + }, + "ROM_4001_1": { + "position": [2, 0], + "size": [20, 8], + "visible": true, + "display_format": "hex" + } + }, + "refresh_rate_ms": 60 + } + } +} +``` + +## Benefits + +### Performance Improvements +- **Reduced CPU usage**: Event-driven vs polling +- **Better responsiveness**: Immediate updates vs 100ms delays +- **Less contention**: Observer pattern reduces lock contention + +### User Experience +- **Configurable layouts**: Users can customize UI arrangement +- **Persistent settings**: Layouts saved in config files +- **Flexible display options**: Show/hide components, change formats + +### Architecture Benefits +- **Loose coupling**: Components don't depend on UI +- **Extensibility**: Easy to add new observers +- **Maintainability**: Clear separation of concerns + +## Implementation Steps + +1. **Add SystemObserver trait** to `system_config.rs` +2. **Extend SystemConfig** with UI layout configuration +3. **Add observer registration** to `ConfigurableSystem` +4. **Implement observer notifications** in Intel4004 CPU +5. **Implement observer notifications** in Intel4001 ROM +6. **Implement observer notifications** in Intel4002 RAM +7. **Update console.rs** to use observer pattern +8. **Update GUI** to use observer pattern and layout configuration +9. **Add UI layout configuration** to existing JSON configs +10. **Test** the observer pattern and UI layout system +11. **Update documentation** with new features + +## Testing Strategy + +### Unit Tests +- Test observer registration and notification +- Test UI layout configuration parsing +- Test component state change notifications + +### Integration Tests +- Test console with observer pattern +- Test GUI with layout configuration +- Test end-to-end state change propagation + +### Performance Tests +- Measure CPU usage reduction +- Test responsiveness improvements +- Validate lock contention reduction + +## Backward Compatibility + +- Existing configs without `ui_layout` section will use defaults (console: 100ms, current GUI layout) +- Observer pattern is additive - doesn't break existing code, components work without observers +- ConfigurableSystem will handle missing `ui_layout` gracefully with sensible defaults +- Gradual migration path: existing configs continue to work, new configs get enhanced features +- No breaking changes to existing JSON configs - only additive new section + +## Future Enhancements + +- **Multiple observer types**: Different observers for different concerns +- **Layout templates**: Predefined layouts for different use cases +- **Dynamic layout editing**: Runtime layout modification +- **Observer filtering**: Selective notification based on component types \ No newline at end of file diff --git a/plans/system_control_architecture.md b/plans/system_control_architecture.md new file mode 100644 index 0000000..dcadc6f --- /dev/null +++ b/plans/system_control_architecture.md @@ -0,0 +1,218 @@ +# System Control Architecture Design + +## Overview + +This document outlines the architecture for implementing enhanced system start/stop/pause controls for the Intel MCS-4 emulator. The design provides centralized system management with fine-grained control over execution state and clock parameters. + +## System Architecture + +```mermaid +graph TB + subgraph "System Controller Layer" + SC[SystemController Trait] + EC[ExecutionController] + SS[SystemState Enum] + end + + subgraph "System Layer" + CS[ConfigurableSystem] + SF[SystemFactory] + end + + subgraph "Component Layer" + CPU[Intel4004] + CLK1[GenericClock] + CLK2[TwoPhaseClock] + ROM[Intel4001] + RAM[Intel4002] + end + + subgraph "Interface Layer" + GUI[GUI Control Panel] + CON[Console Commands] + KB[Keyboard Shortcuts] + end + + SC --> EC + EC --> CS + CS --> CPU + CS --> CLK1 + CS --> CLK2 + CS --> ROM + CS --> RAM + + GUI --> SC + CON --> SC + KB --> SC +``` + +## Core Components + +### 1. SystemController Trait + +Defines the interface for system-wide control operations: + +```rust +pub trait SystemController { + fn start(&mut self) -> Result<(), String>; + fn stop(&mut self) -> Result<(), String>; + fn pause(&mut self) -> Result<(), String>; + fn resume(&mut self) -> Result<(), String>; + fn is_running(&self) -> bool; + fn is_paused(&self) -> bool; + fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), String>; + fn set_clock_enabled(&mut self, enabled: bool) -> Result<(), String>; + fn get_system_state(&self) -> SystemState; + fn get_clock_frequency(&self) -> f64; + fn get_clock_enabled(&self) -> bool; +} +``` + +### 2. SystemState Enum + +Tracks the overall system execution state: + +```rust +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum SystemState { + Stopped, + Running, + Paused, + Error(String), +} +``` + +### 3. ExecutionController + +Manages system lifecycle and coordinates component states: + +```rust +pub struct ExecutionController { + system_state: SystemState, + clock_frequency: f64, + clock_enabled: bool, + components: HashMap>>>, +} +``` + +## Component Enhancements + +### Clock Components + +**GenericClock & TwoPhaseClock Enhancements:** +- Add `paused` boolean field +- Implement `pause()` and `resume()` methods +- Add dynamic frequency adjustment with `set_frequency(f64)` +- Add `enable()` and `disable()` methods for power control +- Update `update()` methods to respect pause state + +### CPU Component + +**Intel4004 Enhancements:** +- Add `paused` boolean field +- Implement `pause()` to halt instruction execution +- Implement `resume()` to continue instruction execution +- Update `update()` to check pause state before executing instructions +- Maintain clock edge detection during pause for seamless resume + +### Memory Components + +**Intel4001 & Intel4002 Enhancements:** +- Add `paused` boolean field +- Implement `pause()` and `resume()` methods +- Update `update()` to respect pause state +- Ensure memory operations complete current cycle before pausing + +## Interface Updates + +### Console Commands + +New console commands: +- `pause` / `p` - Pause system execution +- `resume` - Resume system execution +- `clock ` - Set clock frequency (e.g., `clock 500000`) +- `clock on` / `clock off` - Enable/disable clock +- `speed +` / `speed -` - Increase/decrease clock frequency by 50% + +### GUI Controls + +New GUI control panel: +- System state indicator (Stopped/Running/Paused) +- Start/Stop/Pause/Resume buttons +- Clock frequency slider (100Hz - 10MHz range) +- Clock enable/disable toggle +- Real-time frequency display +- Keyboard shortcuts (Space=pause/resume, +/- for frequency) + +## Configuration Schema + +Extended JSON configuration with clock control: + +```json +{ + "clock_control": { + "initial_frequency": 750000, + "min_frequency": 1000, + "max_frequency": 10000000, + "frequency_step": 50000, + "auto_start": true + } +} +``` + +## Implementation Strategy + +### Phase 1: Core Infrastructure +1. Implement SystemController trait +2. Create SystemState enum +3. Build ExecutionController +4. Update ConfigurableSystem + +### Phase 2: Component Updates +1. Update clock components with pause/resume +2. Update CPU with execution control +3. Update memory components +4. Add dynamic frequency adjustment + +### Phase 3: Interface Integration +1. Update console with new commands +2. Enhance GUI with control panel +3. Add keyboard shortcuts +4. Update configuration handling + +### Phase 4: Testing & Documentation +1. Create comprehensive test suite +2. Update all documentation +3. Verify backwards compatibility + +## Key Design Decisions + +### Pause Behavior +- Complete system freeze including clock signals +- Components maintain state for seamless resume +- Clock edges are tracked but not propagated during pause + +### Frequency Adjustment +- Dynamic adjustment during runtime +- Smooth transitions without glitches +- Validation against component limits + +### State Management +- Centralized state tracking +- Thread-safe state updates +- Consistent state across all components + +## Benefits + +1. **Enhanced Control**: Fine-grained system management +2. **Dynamic Adjustment**: Runtime frequency changes +3. **Better Debugging**: Pause/resume for analysis +4. **User Experience**: Intuitive controls in both interfaces +5. **Extensibility**: Clean architecture for future enhancements + +## Compatibility + +- Maintains existing Component trait +- Backwards compatible with current configurations +- No breaking changes to existing APIs +- Optional features with sensible defaults \ No newline at end of file diff --git a/src/component.rs b/src/component.rs index da43117..f725485 100644 --- a/src/component.rs +++ b/src/component.rs @@ -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 {} diff --git a/src/components/clock/generic_clock.rs b/src/components/clock/generic_clock.rs index 38683f1..e8a3252 100644 --- a/src/components/clock/generic_clock.rs +++ b/src/components/clock/generic_clock.rs @@ -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, // 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 diff --git a/src/components/clock/two_phase_clock.rs b/src/components/clock/two_phase_clock.rs index ae34d87..dc99650 100644 --- a/src/components/clock/two_phase_clock.rs +++ b/src/components/clock/two_phase_clock.rs @@ -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, // 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 + } } diff --git a/src/components/common/intel_400x.rs b/src/components/common/intel_400x.rs index 8d7c6ca..4cfae88 100644 --- a/src/components/common/intel_400x.rs +++ b/src/components/common/intel_400x.rs @@ -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>) -> 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>) -> 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>, default: PinValue) -> PinValue { - if let Ok(pin_guard) = pin.lock() { - pin_guard.read() - } else { - default - } + pub fn read_pin_safe(pin: &Arc>, _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>, 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); } } diff --git a/src/components/cpu/intel_4004.rs b/src/components/cpu/intel_4004.rs index ad2ebcb..ed86e35 100644 --- a/src/components/cpu/intel_4004.rs +++ b/src/components/cpu/intel_4004.rs @@ -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 { diff --git a/src/components/cpu/mos_6502.rs b/src/components/cpu/mos_6502.rs index 74d8f54..c2fe096 100644 --- a/src/components/cpu/mos_6502.rs +++ b/src/components/cpu/mos_6502.rs @@ -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 diff --git a/src/components/cpu/wdc_65c02.rs b/src/components/cpu/wdc_65c02.rs index 0761457..78b065d 100644 --- a/src/components/cpu/wdc_65c02.rs +++ b/src/components/cpu/wdc_65c02.rs @@ -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 diff --git a/src/components/memory/intel_4001.rs b/src/components/memory/intel_4001.rs index d435040..e53ed13 100644 --- a/src/components/memory/intel_4001.rs +++ b/src/components/memory/intel_4001.rs @@ -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] diff --git a/src/components/memory/intel_4002.rs b/src/components/memory/intel_4002.rs index 3805e60..67b7325 100644 --- a/src/components/memory/intel_4002.rs +++ b/src/components/memory/intel_4002.rs @@ -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 {} diff --git a/src/components/memory/intel_4003.rs b/src/components/memory/intel_4003.rs index 455bc12..0c31f4e 100644 --- a/src/components/memory/intel_4003.rs +++ b/src/components/memory/intel_4003.rs @@ -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 {} diff --git a/src/connection.rs b/src/connection.rs index ee50d60..40ba7f4 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -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>, ) -> 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>, ) -> 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>) -> 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); } } diff --git a/src/gui/components.rs b/src/gui/components.rs index ed656d2..fa5481f 100644 --- a/src/gui/components.rs +++ b/src/gui/components.rs @@ -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>>, + ) { + 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>>, + ) { 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>>, + ) { + // 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>>, + ) { + // 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>>, + ) { + // 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)); } diff --git a/src/gui/console.rs b/src/gui/console.rs index 32dcb47..2b24325 100644 --- a/src/gui/console.rs +++ b/src/gui/console.rs @@ -15,6 +15,7 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; +use parking_lot::Mutex; use ratatui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout, Rect}, @@ -25,11 +26,11 @@ use ratatui::{ }; use serde::{Deserialize, Serialize}; use std::io; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; -use crate::system_config::ConfigurableSystem; +use crate::system_config::{ConfigurableSystem, SystemController, SystemState}; /// Display format for memory and register values #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] @@ -89,7 +90,7 @@ impl Default for ConsoleConfig { fn default() -> Self { Self { enabled: true, - refresh_rate_ms: 100, + refresh_rate_ms: 10, // 100 Hz update rate (10ms interval) show_ram: true, show_registers: true, show_system_info: true, @@ -111,6 +112,10 @@ pub struct ConsoleApp { command_buffer: String, show_help: bool, selected_pane: usize, + /// Status message to display to the user (e.g., errors, feedback) + status_message: Option, + /// When the status message was set, for auto-clearing + status_message_time: Option, } impl ConsoleApp { @@ -122,18 +127,76 @@ impl ConsoleApp { command_buffer: String::new(), show_help: false, selected_pane: 0, + status_message: None, + status_message_time: None, + } + } + + /// Set a status message to display to the user + fn set_status(&mut self, message: String) { + self.status_message = Some(message); + self.status_message_time = Some(Instant::now()); + } + + /// Clear the status message if it has been displayed long enough (3 seconds) + fn maybe_clear_status(&mut self) { + if let Some(time) = self.status_message_time { + if time.elapsed().as_secs() >= 3 { + self.status_message = None; + self.status_message_time = None; + } } } pub fn run(&mut self) -> Result<(), Box> { - // Setup terminal - enable_raw_mode().map_err(|e| format!("Failed to enable raw mode: {}", e))?; + // Set up panic hook to ensure terminal cleanup on panic + // Note: We use a custom cleanup hook that runs before the default panic handler + std::panic::set_hook(Box::new(|panic_info| { + // Try to restore terminal state before printing panic + let _ = disable_raw_mode(); + let _ = execute!(io::stdout(), LeaveAlternateScreen); + // Print the panic info after cleanup + eprintln!("\npanic occurred: {}", panic_info); + })); + + let result = self.run_inner(); + + // Restore default panic hook after normal completion + let _ = std::panic::take_hook(); + + result + } + + fn run_inner(&mut self) -> Result<(), Box> { + // Setup terminal with explicit error handling + enable_raw_mode().map_err(|e| { + eprintln!("ERROR: Failed to enable raw mode: {}", e); + eprintln!("This may occur if the terminal doesn't support raw mode."); + format!("Failed to enable raw mode: {}", e) + })?; + let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen) - .map_err(|e| format!("Failed to enter alternate screen: {}", e))?; + execute!(stdout, EnterAlternateScreen).map_err(|e| { + let _ = disable_raw_mode(); + eprintln!("ERROR: Failed to enter alternate screen: {}", e); + eprintln!("The console requires an alternate screen buffer."); + format!("Failed to enter alternate screen: {}", e) + })?; let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; + let mut terminal = Terminal::new(backend).map_err(|e| { + let _ = disable_raw_mode(); + let _ = execute!(io::stdout(), LeaveAlternateScreen); + eprintln!("ERROR: Failed to create terminal backend: {}", e); + format!("Failed to create terminal backend: {}", e) + })?; + + // Clear the terminal initially to ensure a fresh start + terminal.clear().map_err(|e| { + let _ = disable_raw_mode(); + let _ = execute!(io::stdout(), LeaveAlternateScreen); + format!("Failed to clear terminal: {}", e) + })?; self.running = true; let mut last_draw = Instant::now(); @@ -142,40 +205,63 @@ impl ConsoleApp { while self.running { let now = Instant::now(); - // Handle input with longer poll interval to reduce CPU usage - if let Ok(true) = event::poll(Duration::from_millis(50)) { - if let Ok(Event::Key(key)) = event::read() { - self.handle_key_event(key.code, key.modifiers); + // Handle input with poll interval to reduce CPU usage + match event::poll(Duration::from_millis(50)) { + Ok(true) => { + match event::read() { + Ok(Event::Key(key)) => { + self.handle_key_event(key.code, key.modifiers); + } + Ok(Event::Resize(_, _)) => { + // Terminal was resized, force a redraw + if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { + eprintln!("Terminal draw error after resize: {}", e); + break; + } + } + Ok(_) => { + // Other events (mouse, etc.) - ignore + } + Err(e) => { + eprintln!("Event read error: {}", e); + } + } + } + Ok(false) => { + // No event available, continue + } + Err(e) => { + eprintln!("Event poll error: {}", e); } } // Update display at regular intervals if now.duration_since(last_draw) >= Duration::from_millis(self.config.refresh_rate_ms) { - if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { - eprintln!("DEBUG: Failed to draw UI: {}", e); - break; - } - last_draw = now; - } - - // Check if system should still be running using try_lock to avoid blocking - match self.system.try_lock() { - Ok(system) => { - if !system.is_running() && !self.show_help { - // System stopped, show final state briefly - if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { - eprintln!("DEBUG: Failed to draw final UI: {}", e); + match terminal.draw(|f| self.draw_ui(f)) { + Ok(_) => { + last_draw = now; + } + Err(e) => { + eprintln!("Terminal draw error: {}", e); + // Try to recover by clearing and redrawing + if let Err(clear_err) = terminal.clear() { + eprintln!("Failed to clear terminal: {}", clear_err); + break; } - thread::sleep(Duration::from_millis(500)); - break; } } - Err(std::sync::TryLockError::WouldBlock) => { - // System is busy, continue running - this is expected - } - Err(std::sync::TryLockError::Poisoned(_)) => { - // System mutex is poisoned, exit gracefully - eprintln!("DEBUG: System mutex poisoned, exiting"); + } + + // Check if system should still be running using blocking lock + // GUI can wait for simulation - this ensures we always get the data + { + let system = self.system.lock(); + let state = system.get_system_state(); + if state == SystemState::Stopped && !self.show_help { + // System stopped, show final state briefly + drop(system); // Release lock before draw + let _ = terminal.draw(|f| self.draw_ui(f)); + thread::sleep(Duration::from_millis(500)); break; } } @@ -184,13 +270,30 @@ impl ConsoleApp { thread::sleep(Duration::from_millis(10)); } - // Restore terminal - disable_raw_mode().map_err(|e| format!("Failed to disable raw mode: {}", e))?; - execute!(terminal.backend_mut(), LeaveAlternateScreen) - .map_err(|e| format!("Failed to leave alternate screen: {}", e))?; - terminal - .show_cursor() - .map_err(|e| format!("Failed to show cursor: {}", e))?; + // Restore terminal - always attempt cleanup even if errors occurred + let cleanup_result = self.cleanup_terminal(&mut terminal); + + cleanup_result + } + + fn cleanup_terminal( + &self, + terminal: &mut Terminal>, + ) -> Result<(), Box> { + // Disable raw mode first + if let Err(e) = disable_raw_mode() { + eprintln!("Warning: Failed to disable raw mode: {}", e); + } + + // Leave alternate screen + if let Err(e) = execute!(terminal.backend_mut(), LeaveAlternateScreen) { + eprintln!("Warning: Failed to leave alternate screen: {}", e); + } + + // Show cursor + if let Err(e) = terminal.show_cursor() { + eprintln!("Warning: Failed to show cursor: {}", e); + } Ok(()) } @@ -200,138 +303,215 @@ impl ConsoleApp { if key == KeyCode::Char('q') || key == KeyCode::Char('Q') { if modifiers.contains(KeyModifiers::CONTROL) { self.running = false; - if let Ok(mut system) = self.system.lock() { - system.stop(); - } + let mut system = self.system.lock(); + system.stop(); return; } } match key { KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => { - println!("DEBUG: Quit key pressed, stopping console"); self.running = false; - if let Ok(mut system) = self.system.lock() { - system.stop(); - } + let mut system = self.system.lock(); + system.stop(); } KeyCode::Char('h') | KeyCode::Char('H') => { - println!("DEBUG: Help key pressed"); self.show_help = !self.show_help; } KeyCode::Char('r') | KeyCode::Char('R') => { - println!("DEBUG: Run key pressed"); - if let Ok(mut system) = self.system.lock() { - if system.is_running() { - system.stop(); - } else { - system.run(); - } + let mut system = self.system.lock(); + if system.is_running() { + let _ = system.stop(); + } else { + let _ = system.start(); } } KeyCode::Char('s') | KeyCode::Char('S') => { - println!("DEBUG: Stop key pressed"); - if let Ok(mut system) = self.system.lock() { - system.stop(); - } + let mut system = self.system.lock(); + system.stop(); } KeyCode::Char(' ') => { - println!("DEBUG: Space key pressed - single step not implemented"); - // Single step (if supported) - // This would require adding step functionality to the system + // Space = pause/resume toggle + let mut system = self.system.lock(); + let state = system.get_system_state(); + match state { + SystemState::Running => { + let _ = system.pause(); + } + SystemState::Paused => { + let _ = system.resume(); + } + SystemState::Stopped => { + let _ = system.start(); + } + SystemState::Error => { + // Try to recover by stopping and starting + system.stop(); + let _ = system.start(); + } + } + } + KeyCode::Char('+') | KeyCode::Char('=') => { + // Increase clock frequency by 20% + let status_msg = { + let mut system = self.system.lock(); + let current = system.get_clock_frequency(); + let new_freq = current * 1.2; + match system.set_clock_frequency(new_freq) { + Ok(_) => Some(format!("Frequency: {:.0} Hz", new_freq)), + Err(e) => Some(format!("Error: {}", e)), + } + }; + if let Some(msg) = status_msg { + self.set_status(msg); + } + } + KeyCode::Char('-') | KeyCode::Char('_') => { + // Decrease clock frequency by 20% + let status_msg = { + let mut system = self.system.lock(); + let current = system.get_clock_frequency(); + let new_freq = current / 1.2; + match system.set_clock_frequency(new_freq) { + Ok(_) => Some(format!("Frequency: {:.0} Hz", new_freq)), + Err(e) => Some(format!("Error: {}", e)), + } + }; + if let Some(msg) = status_msg { + self.set_status(msg); + } + } + KeyCode::Char('p') | KeyCode::Char('P') => { + // Pause command + let mut system = self.system.lock(); + let _ = system.pause(); + } + KeyCode::Char('c') | KeyCode::Char('C') => { + // Resume (continue) command + let mut system = self.system.lock(); + let _ = system.resume(); } KeyCode::Tab => { - println!("DEBUG: Tab key pressed - switching panes"); self.selected_pane = (self.selected_pane + 1) % 3; } KeyCode::Backspace => { - println!("DEBUG: Backspace key pressed"); self.command_buffer.pop(); } KeyCode::Enter => { - println!( - "DEBUG: Enter key pressed - executing command: '{}'", - self.command_buffer - ); self.execute_command(); self.command_buffer.clear(); } KeyCode::Char(c) => { - println!("DEBUG: Character key pressed: '{}'", c); - if c.is_ascii_alphabetic() || c.is_ascii_digit() { + if c.is_ascii_alphabetic() || c.is_ascii_digit() || c == '.' || c == ' ' { self.command_buffer.push(c); } } - _ => { - println!("DEBUG: Unhandled key pressed: {:?}", key); - } + _ => {} } } fn execute_command(&mut self) { let cmd = self.command_buffer.trim().to_lowercase(); - println!("DEBUG: Executing command: '{}'", cmd); + let parts: Vec<&str> = cmd.split_whitespace().collect(); - match cmd.as_str() { + if parts.is_empty() { + return; + } + + match parts[0] { "quit" | "exit" | "q" => { - println!("DEBUG: Executing quit command"); self.running = false; - if let Ok(mut system) = self.system.lock() { - system.stop(); - } + let mut system = self.system.lock(); + system.stop(); } - "run" | "r" => { - println!("DEBUG: Executing run command"); - if let Ok(mut system) = self.system.lock() { - system.run(); - } + "run" | "r" | "start" => { + let mut system = self.system.lock(); + let _ = system.start(); } "stop" | "s" => { - println!("DEBUG: Executing stop command"); - if let Ok(mut system) = self.system.lock() { - system.stop(); - } + let mut system = self.system.lock(); + let _ = system.stop(); + } + "pause" | "p" => { + let mut system = self.system.lock(); + let _ = system.pause(); + } + "resume" | "c" | "continue" => { + let mut system = self.system.lock(); + let _ = system.resume(); } "help" | "h" => { - println!("DEBUG: Toggling help display"); self.show_help = !self.show_help; } "reset" => { - println!("DEBUG: Executing reset command"); - if let Ok(mut system) = self.system.lock() { - system.stop(); - // Reset would need to be implemented in the system - } + let mut system = self.system.lock(); + let _ = system.stop(); + // Reset would need to be implemented in the system } "status" => { - println!("DEBUG: Executing status command"); - if let Ok(system) = self.system.lock() { - let info = system.get_system_info(); - println!("System: {} - {}", info.name, info.description); - println!( - "Components: {}, Running: {}", - info.component_count, - system.is_running() - ); + let system = self.system.lock(); + let info = system.get_system_info(); + let state = system.get_system_state(); + let freq = system.get_clock_frequency(); + println!("System: {} - {}", info.name, info.description); + println!("Components: {}, State: {:?}", info.component_count, state); + println!("Clock Frequency: {:.2} Hz", freq); + } + "freq" | "frequency" => { + if parts.len() > 1 { + if let Ok(freq) = parts[1].parse::() { + let mut system = self.system.lock(); + match system.set_clock_frequency(freq) { + Ok(_) => println!("Clock frequency set to {:.2} Hz", freq), + Err(e) => println!("Error setting frequency: {}", e), + } + } else { + println!("Invalid frequency value: {}", parts[1]); + } + } else { + let system = self.system.lock(); + let freq = system.get_clock_frequency(); + println!("Current clock frequency: {:.2} Hz", freq); + } + } + "clock" => { + if parts.len() > 1 { + match parts[1] { + "on" | "enable" => { + let mut system = self.system.lock(); + let _ = system.set_clock_enabled(true); + } + "off" | "disable" => { + let mut system = self.system.lock(); + let _ = system.set_clock_enabled(false); + } + _ => { + println!("Usage: clock [on|off]"); + } + } + } else { + let system = self.system.lock(); + let enabled = system.is_clock_enabled(); + println!("Clock enabled: {}", enabled); } } "ram" => { - println!("DEBUG: Executing RAM display command"); - if let Ok(_system) = self.system.lock() { - println!("RAM display requested - would show RAM contents here"); - } + // RAM display is always shown in the UI } "" => { // Empty command - do nothing } _ => { - println!("DEBUG: Unknown command: '{}'", cmd); - println!("Available commands: quit, run, stop, help, reset, status, ram"); + println!("Unknown command: '{}'", cmd); + println!("Available commands: quit, run, stop, pause, resume, help, reset, status, freq, clock"); } } } - fn draw_ui(&self, f: &mut Frame) { + fn draw_ui(&mut self, f: &mut Frame) { + // Clear old status messages first + self.maybe_clear_status(); + let size = f.size(); if self.show_help { @@ -358,14 +538,16 @@ impl ConsoleApp { .add_modifier(Modifier::BOLD), )]), Line::from(vec![ - Span::raw("Commands: "), - Span::styled("q/Q", Style::default().fg(Color::Yellow)), - Span::raw("=quit, "), - Span::styled("r/R", Style::default().fg(Color::Yellow)), + Span::raw("Keys: "), + Span::styled("Space", Style::default().fg(Color::Yellow)), + Span::raw("=pause/resume, "), + Span::styled("+/-", Style::default().fg(Color::Yellow)), + Span::raw("=freq, "), + Span::styled("r", Style::default().fg(Color::Yellow)), Span::raw("=run, "), - Span::styled("s/S", Style::default().fg(Color::Yellow)), + Span::styled("s", Style::default().fg(Color::Yellow)), Span::raw("=stop, "), - Span::styled("h/H", Style::default().fg(Color::Yellow)), + Span::styled("h", Style::default().fg(Color::Yellow)), Span::raw("=help"), ]), ]; @@ -390,15 +572,23 @@ impl ConsoleApp { // Right pane - RAM contents self.draw_ram_contents(f, content_chunks[1]); - // Command bar - let command_text = if self.command_buffer.is_empty() { + // Command bar - show status message or command buffer + let command_text = if let Some(ref msg) = self.status_message { + msg.as_str() + } else if self.command_buffer.is_empty() { "Enter command (type 'h' for help)..." } else { &self.command_buffer }; + let command_style = if self.status_message.is_some() { + Style::default().fg(Color::Yellow) + } else { + Style::default().fg(Color::White) + }; + let command_bar = Paragraph::new(command_text) - .style(Style::default().fg(Color::White)) + .style(command_style) .block(Block::default().borders(Borders::ALL).title("Command")); f.render_widget(command_bar, chunks[2]); } @@ -414,7 +604,7 @@ impl ConsoleApp { )]), Line::from(""), Line::from(vec![Span::styled( - "Commands:", + "System Control Commands:", Style::default().add_modifier(Modifier::BOLD), )]), Line::from(vec![ @@ -422,22 +612,64 @@ impl ConsoleApp { Span::raw(" - Exit emulator"), ]), Line::from(vec![ - Span::styled(" r, run", Style::default().fg(Color::Yellow)), - Span::raw(" - Start/stop system execution"), + Span::styled(" r, run, start", Style::default().fg(Color::Yellow)), + Span::raw(" - Start system execution"), ]), Line::from(vec![ Span::styled(" s, stop", Style::default().fg(Color::Yellow)), Span::raw(" - Stop system execution"), ]), Line::from(vec![ - Span::styled(" h, help", Style::default().fg(Color::Yellow)), - Span::raw(" - Show/hide this help"), + Span::styled(" p, pause", Style::default().fg(Color::Yellow)), + Span::raw(" - Pause system execution"), + ]), + Line::from(vec![ + Span::styled(" c, resume, continue", Style::default().fg(Color::Yellow)), + Span::raw(" - Resume from pause"), ]), Line::from(vec![ Span::styled(" reset", Style::default().fg(Color::Yellow)), Span::raw(" - Reset system"), ]), Line::from(""), + Line::from(vec![Span::styled( + "Clock Control Commands:", + Style::default().add_modifier(Modifier::BOLD), + )]), + Line::from(vec![ + Span::styled(" freq [Hz]", Style::default().fg(Color::Yellow)), + Span::raw(" - Get/set clock frequency"), + ]), + Line::from(vec![ + Span::styled(" clock [on|off]", Style::default().fg(Color::Yellow)), + Span::raw(" - Enable/disable clock"), + ]), + Line::from(""), + Line::from(vec![Span::styled( + "Keyboard Shortcuts:", + Style::default().add_modifier(Modifier::BOLD), + )]), + Line::from(vec![ + Span::styled(" Space", Style::default().fg(Color::Yellow)), + Span::raw(" - Toggle pause/resume"), + ]), + Line::from(vec![ + Span::styled(" +/=", Style::default().fg(Color::Yellow)), + Span::raw(" - Increase clock frequency by 20%"), + ]), + Line::from(vec![ + Span::styled(" -", Style::default().fg(Color::Yellow)), + Span::raw(" - Decrease clock frequency by 20%"), + ]), + Line::from(vec![ + Span::styled(" p", Style::default().fg(Color::Yellow)), + Span::raw(" - Pause system"), + ]), + Line::from(vec![ + Span::styled(" c", Style::default().fg(Color::Yellow)), + Span::raw(" - Resume (continue) system"), + ]), + Line::from(""), Line::from(vec![Span::styled( "Navigation:", Style::default().add_modifier(Modifier::BOLD), @@ -454,6 +686,10 @@ impl ConsoleApp { Span::styled(" Backspace", Style::default().fg(Color::Yellow)), Span::raw(" - Delete character"), ]), + Line::from(vec![ + Span::styled(" h, help", Style::default().fg(Color::Yellow)), + Span::raw(" - Show/hide this help"), + ]), Line::from(""), Line::from(vec![Span::raw("Press any key to return to main view...")]), ]; @@ -469,49 +705,64 @@ impl ConsoleApp { let info_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ - Constraint::Length(8), // System info - Constraint::Min(5), // Registers + Constraint::Length(10), // System info (increased for state display) + Constraint::Min(5), // Registers ]) .split(area); - // System information - use try_lock to avoid blocking - let system_info = match self.system.try_lock() { - Ok(system) => { - let info = system.get_system_info(); - vec![ - Line::from(vec![Span::raw(format!("System: {}", info.name))]), - Line::from(vec![Span::raw(format!( - "Description: {}", - info.description - ))]), - Line::from(vec![Span::raw(format!( - "Components: {}", - info.component_count - ))]), - Line::from(vec![Span::raw(format!("CPU Speed: {} Hz", info.cpu_speed))]), - Line::from(vec![Span::raw(format!( - "ROM Size: {} bytes", - info.rom_size - ))]), - Line::from(vec![Span::raw(format!( - "RAM Size: {} nibbles", - info.ram_size - ))]), - Line::from(vec![Span::raw(format!("Running: {}", system.is_running()))]), - ] - } - Err(std::sync::TryLockError::WouldBlock) => { - vec![Line::from(vec![Span::raw( - "System busy (emulation running)...", - )])] - } - Err(_) => { - vec![Line::from(vec![Span::raw( - "System information unavailable", - )])] - } + // System information - use blocking lock, GUI can wait + let system = self.system.lock(); + let info = system.get_system_info(); + let state = system.get_system_state(); + let freq = system.get_clock_frequency(); + let clock_enabled = system.is_clock_enabled(); + + // Color code the state + let state_style = match state { + SystemState::Running => Style::default().fg(Color::Green), + SystemState::Paused => Style::default().fg(Color::Yellow), + SystemState::Stopped => Style::default().fg(Color::Red), + SystemState::Error => Style::default().fg(Color::Red).add_modifier(Modifier::BOLD), }; + let system_info = vec![ + Line::from(vec![Span::raw(format!("System: {}", info.name))]), + Line::from(vec![Span::raw(format!( + "Description: {}", + info.description + ))]), + Line::from(vec![Span::raw(format!( + "Components: {}", + info.component_count + ))]), + Line::from(vec![ + Span::raw("State: "), + Span::styled(format!("{:?}", state), state_style), + ]), + Line::from(vec![ + Span::raw("Clock: "), + Span::styled( + if clock_enabled { "ON" } else { "OFF" }, + if clock_enabled { + Style::default().fg(Color::Green) + } else { + Style::default().fg(Color::Red) + }, + ), + Span::raw(format!(" @ {:.2} Hz", freq)), + ]), + Line::from(vec![Span::raw(format!("CPU Speed: {} Hz", info.cpu_speed))]), + Line::from(vec![Span::raw(format!( + "ROM Size: {} bytes", + info.rom_size + ))]), + Line::from(vec![Span::raw(format!( + "RAM Size: {} nibbles", + info.ram_size + ))]), + ]; + drop(system); // Release lock before rendering + let system_widget = Paragraph::new(system_info) .block( Block::default() @@ -542,103 +793,101 @@ impl ConsoleApp { fn get_cpu_register_info(&self) -> Vec> { let mut register_info = vec![Line::from(vec![Span::raw("CPU Registers:")])]; - match self.system.try_lock() { - Ok(system) => { - // Try to find Intel 4004 CPU and get register data - let cpu_data = system.get_cpu_register_data(); + // Use blocking lock - GUI can wait for simulation + let system = self.system.lock(); + // Try to find Intel 4004 CPU and get register data + let cpu_data = system.get_cpu_register_data(); + drop(system); // Release lock after getting data - if let Some(cpu) = cpu_data { - let fmt = &self.config.display_format; + if let Some(cpu) = cpu_data { + let fmt = &self.config.display_format; - register_info.push(Line::from(vec![ - Span::styled("PC: ", Style::default().fg(Color::Yellow)), - Span::raw(fmt.format_address(cpu.program_counter)), - ])); + register_info.push(Line::from(vec![ + Span::styled("PC: ", Style::default().fg(Color::Yellow)), + Span::raw(fmt.format_address(cpu.program_counter)), + ])); - register_info.push(Line::from(vec![ - Span::styled("ACC: ", Style::default().fg(Color::Yellow)), - Span::raw(fmt.format_nibble(cpu.accumulator)), - ])); + register_info.push(Line::from(vec![ + Span::styled("ACC: ", Style::default().fg(Color::Yellow)), + Span::raw(fmt.format_nibble(cpu.accumulator)), + ])); - register_info.push(Line::from(vec![ - Span::styled("CY: ", Style::default().fg(Color::Yellow)), - Span::raw(if cpu.carry { "1" } else { "0" }), - ])); + register_info.push(Line::from(vec![ + Span::styled("CY: ", Style::default().fg(Color::Yellow)), + Span::raw(if cpu.carry { "1" } else { "0" }), + ])); - register_info.push(Line::from(vec![ - Span::styled("SP: ", Style::default().fg(Color::Yellow)), - Span::raw(format!("{}", cpu.stack_pointer)), - ])); + register_info.push(Line::from(vec![ + Span::styled("SP: ", Style::default().fg(Color::Yellow)), + Span::raw(format!("{}", cpu.stack_pointer)), + ])); - // Index registers - let mut reg_line = String::from("Index: "); - for (i, val) in cpu.index_registers.iter().enumerate() { - if i > 0 { - reg_line.push_str(", "); - } - reg_line.push_str(&format!("R{}={}", i, fmt.format_nibble(*val))); - } - register_info.push(Line::from(vec![Span::raw(reg_line)])); - } else { - register_info.push(Line::from(vec![Span::raw("No Intel 4004 CPU found")])); + // Index registers + let mut reg_line = String::from("Index: "); + for (i, val) in cpu.index_registers.iter().enumerate() { + if i > 0 { + reg_line.push_str(", "); } + reg_line.push_str(&format!("R{}={}", i, fmt.format_nibble(*val))); } - Err(std::sync::TryLockError::WouldBlock) => { - register_info.push(Line::from(vec![Span::raw( - "CPU data temporarily unavailable (system busy)...", - )])); - } - Err(_) => { - register_info.push(Line::from(vec![Span::raw("Unable to access CPU data")])); - } + register_info.push(Line::from(vec![Span::raw(reg_line)])); + } else { + register_info.push(Line::from(vec![Span::raw("No Intel 4004 CPU found")])); } register_info } fn draw_ram_contents(&self, f: &mut Frame, area: Rect) { + // Split area into RAM and ROM sections + let memory_chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage(50), // RAM + Constraint::Percentage(50), // ROM + ]) + .split(area); + + // Draw RAM contents + self.draw_ram_panel(f, memory_chunks[0]); + + // Draw ROM contents + self.draw_rom_panel(f, memory_chunks[1]); + } + + fn draw_ram_panel(&self, f: &mut Frame, area: Rect) { let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])]; - // Try to get actual RAM data from the system using try_lock + // Get actual RAM data from the system using blocking lock if self.config.show_ram { - match self.system.try_lock() { - Ok(system) => { - let ram_data = system.get_ram_data(self.config.max_ram_rows); + let system = self.system.lock(); + let ram_data = system.get_ram_data(self.config.max_ram_rows); + drop(system); // Release lock after getting data - if ram_data.is_empty() { - ram_info.push(Line::from(vec![Span::raw("No RAM chips found")])); - } else { - let fmt = &self.config.display_format; + if ram_data.is_empty() { + ram_info.push(Line::from(vec![Span::raw("No RAM chips found")])); + } else { + let fmt = &self.config.display_format; - for (chip_name, banks) in ram_data { - // Clone chip_name to avoid lifetime issues - let chip_name = chip_name.clone(); - ram_info.push(Line::from(vec![Span::styled( - chip_name, - Style::default().fg(Color::Cyan), - )])); - - for (bank_idx, data) in banks.iter().enumerate() { - let mut bank_line = format!(" Bank {}: [", bank_idx); - for (i, nibble) in data.iter().enumerate() { - if i > 0 && i % 4 == 0 { - bank_line.push(' '); - } - bank_line.push_str(&fmt.format_nibble(*nibble)); - } - bank_line.push(']'); - ram_info.push(Line::from(vec![Span::raw(bank_line)])); - } - } - } - } - Err(std::sync::TryLockError::WouldBlock) => { - ram_info.push(Line::from(vec![Span::raw( - "RAM data temporarily unavailable (system busy)...", + for (chip_name, banks) in ram_data { + // Clone chip_name to avoid lifetime issues + let chip_name = chip_name.clone(); + ram_info.push(Line::from(vec![Span::styled( + chip_name, + Style::default().fg(Color::Cyan), )])); - } - Err(_) => { - ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")])); + + for (bank_idx, data) in banks.iter().enumerate() { + let mut bank_line = format!(" Bank {}: [", bank_idx); + for (i, nibble) in data.iter().enumerate() { + if i > 0 && i % 4 == 0 { + bank_line.push(' '); + } + bank_line.push_str(&fmt.format_nibble(*nibble)); + } + bank_line.push(']'); + ram_info.push(Line::from(vec![Span::raw(bank_line)])); + } } } } else { @@ -650,6 +899,59 @@ impl ConsoleApp { .wrap(Wrap { trim: true }); f.render_widget(ram_widget, area); } + + fn draw_rom_panel(&self, f: &mut Frame, area: Rect) { + let mut rom_info = vec![Line::from(vec![Span::raw("ROM Contents:")])]; + + // Get actual ROM data from the system using blocking lock + if self.config.show_rom { + let system = self.system.lock(); + let rom_data = system.get_rom_data(self.config.max_rom_rows); + drop(system); // Release lock after getting data + + if rom_data.is_empty() { + rom_info.push(Line::from(vec![Span::raw("No ROM chips found")])); + } else { + let fmt = &self.config.display_format; + + for (chip_name, data) in rom_data { + // Clone chip_name to avoid lifetime issues + let chip_name = chip_name.clone(); + rom_info.push(Line::from(vec![Span::styled( + chip_name, + Style::default().fg(Color::Cyan), + )])); + + // Display ROM data in rows of 8 bytes + let mut addr = 0; + for chunk in data.chunks(8) { + let mut row = format!(" {:03X}: ", addr); + for (i, byte) in chunk.iter().enumerate() { + if i > 0 { + row.push(' '); + } + row.push_str(&fmt.format_byte(*byte)); + } + rom_info.push(Line::from(vec![Span::raw(row)])); + addr += 8; + } + + // Show total size + rom_info.push(Line::from(vec![Span::styled( + format!(" ({} bytes total)", data.len()), + Style::default().fg(Color::DarkGray), + )])); + } + } + } else { + rom_info.push(Line::from(vec![Span::raw("ROM display disabled")])); + } + + let rom_widget = Paragraph::new(rom_info) + .block(Block::default().borders(Borders::ALL).title("ROM Contents")) + .wrap(Wrap { trim: true }); + f.render_widget(rom_widget, area); + } } /// Public interface for launching the console diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 86d940e..47320b3 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -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; /// diff --git a/src/gui/state.rs b/src/gui/state.rs index 95e8bdc..00723ad 100644 --- a/src/gui/state.rs +++ b/src/gui/state.rs @@ -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>) { - 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 diff --git a/src/main.rs b/src/main.rs index b281fbc..0f9c0f3 100644 --- a/src/main.rs +++ b/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>, 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>, 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 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!( diff --git a/src/pin.rs b/src/pin.rs index 5da233a..4f52048 100644 --- a/src/pin.rs +++ b/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); } diff --git a/src/system_config.rs b/src/system_config.rs index 47c150c..a958aa4 100644 --- a/src/system_config.rs +++ b/src/system_config.rs @@ -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>)>; /// ROM data for display purposes (chip name -> bytes) pub type RomData = Vec<(String, Vec)>; +/// 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>>>, is_running: Arc>, + is_paused: Arc>, + clock_frequency: Arc>, + clock_enabled: Arc>, component_handles: Arc>>, } @@ -393,10 +466,20 @@ impl ConfigurableSystem { config: SystemConfig, components: HashMap>>>, ) -> 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::() { 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::() { 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 { // 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::() { - 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::() { + 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::() { - 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::() { + 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::() { - 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::() { + 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::() + { + clock.pause(); + println!("Clock {} paused", name); + } + // Try to downcast to TwoPhaseClock + else if let Some(clock) = + comp.as_any_mut() + .downcast_mut::() + { + 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::() + { + clock.resume(); + println!("Clock {} resumed", name); + } + // Try to downcast to TwoPhaseClock + else if let Some(clock) = + comp.as_any_mut() + .downcast_mut::() + { + 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::() + { + 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::() + { + 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::() + { + 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::() + { + 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, diff --git a/tests/gui_integration_tests.rs b/tests/gui_integration_tests.rs index 0af389b..66d7793 100644 --- a/tests/gui_integration_tests.rs +++ b/tests/gui_integration_tests.rs @@ -6,9 +6,10 @@ #![cfg(test)] +use parking_lot::Mutex; use rusty_emu::gui::GuiApp; use rusty_emu::system_config::{ConfigurableSystem, SystemFactory}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::thread; use std::time::Duration; @@ -36,7 +37,8 @@ mod gui_integration_test_utils { /// Note: This function acquires the lock internally. Do NOT call it while holding the lock. pub fn simulate_gui_updates(system: &Arc>) { // Simulate the kind of updates that would happen in the GUI thread - if let Ok(system_guard) = system.lock() { + let system_guard = system.lock(); + { // Simulate some system activity let _ = system_guard.is_running(); // Access system state } @@ -48,14 +50,19 @@ mod gui_integration_test_utils { // Spawn a thread that accesses the system let handle = thread::spawn(move || { - // Try to acquire the lock - return true if successful (regardless of is_running value) - system_clone.lock().is_ok() + // Try to acquire the lock - return true if successful (parking_lot::lock always succeeds) + let _guard = system_clone.lock(); + true }); - // Main thread also accesses the system - let main_thread_result = system.lock().is_ok(); + // Main thread also accesses the system - but release lock before joining to avoid deadlock + { + let _guard = system.lock(); + // Access the system while holding the lock + } + let main_thread_result = true; - // Wait for spawned thread + // Wait for spawned thread (lock is released, so no deadlock) let spawned_thread_result = handle.join().unwrap_or(false); // Both threads should be able to acquire the lock without deadlock @@ -150,7 +157,8 @@ mod gui_system_integration_tests { thread::spawn(move || { // Simulate GUI update pattern for _ in 0..10 { - if let Ok(_system_guard) = system_clone.lock() { + let _system_guard = system_clone.lock(); + { // Simulate some processing time thread::sleep(Duration::from_micros(10)); } @@ -196,7 +204,7 @@ mod gui_system_integration_tests { // Test the polling mechanism that GUI would use for i in 0..10 { { - let system_guard = system.lock().unwrap(); + let system_guard = system.lock(); let is_running = system_guard.is_running(); // Simulate state updates that would happen in GUI @@ -221,7 +229,7 @@ mod gui_system_integration_tests { // In a real implementation, this would test actual memory access { - let system_guard = system.lock().unwrap(); + let system_guard = system.lock(); // Test that system provides memory information let _ = system_guard.get_system_info(); // Access system info } // system_guard is dropped here @@ -241,7 +249,7 @@ mod gui_system_integration_tests { // Test register state structure for GUI integration { - let _system_guard = system.lock().unwrap(); + let _system_guard = system.lock(); // In a real implementation, this would access actual CPU registers // For now, we test the GUI state structure } @@ -262,7 +270,7 @@ mod gui_system_integration_tests { // Test component state monitoring for GUI { - let system_guard = system.lock().unwrap(); + let system_guard = system.lock(); let components = system_guard.get_components(); // Test that components are accessible (GUI would monitor these) @@ -434,18 +442,19 @@ mod gui_error_handling_tests { // Hold the lock for a short time to simulate contention let system_clone = Arc::clone(&system); let lock_handle = thread::spawn(move || { - if let Ok(_system_guard) = system_clone.lock() { + let _system_guard = system_clone.lock(); + { thread::sleep(Duration::from_millis(10)); } }); // Try to access from main thread (should not deadlock) - let access_result = system.try_lock().is_ok(); + let access_result = system.try_lock().is_some(); lock_handle.join().unwrap(); // Should be able to access after the lock is released - assert!(access_result || system.try_lock().is_ok()); + assert!(access_result || system.try_lock().is_some()); } #[test] @@ -481,7 +490,7 @@ mod gui_real_time_tests { for i in 0..20 { { - let _system_guard = system.lock().unwrap(); + let _system_guard = system.lock(); // Simulate GUI state polling update_count += 1; @@ -535,7 +544,7 @@ mod gui_real_time_tests { // For now, we test the data structures are compatible { - let _system_guard = system.lock().unwrap(); + let _system_guard = system.lock(); // Test memory data structure compatibility } @@ -559,7 +568,7 @@ mod gui_real_time_tests { let system = create_test_system(); { - let _system_guard = system.lock().unwrap(); + let _system_guard = system.lock(); } let mut gui_state = rusty_emu::gui::state::GuiState::new(); diff --git a/tests/gui_tests.rs b/tests/gui_tests.rs index 2360faa..917d7ca 100644 --- a/tests/gui_tests.rs +++ b/tests/gui_tests.rs @@ -6,9 +6,10 @@ #![cfg(test)] use eframe::egui; +use parking_lot::Mutex; use rusty_emu::gui::components::*; use rusty_emu::gui::state::*; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; /// Test utilities for GUI testing mod gui_test_utils { @@ -304,7 +305,8 @@ mod gui_integration_tests { // For now, we verify the components exist and have valid state // Test control panel rendering (would panic if component is invalid) - components.render_control_panel(&mut create_test_ui(&ctx), &mut state); + // Note: render_control_panel now takes an optional system parameter + components.render_control_panel(&mut create_test_ui(&ctx), &mut state, None); // Test memory viewer rendering components.render_memory_viewer(&mut create_test_ui(&ctx), &mut state); diff --git a/tests/intel_4001_state_machine_test.rs b/tests/intel_4001_state_machine_test.rs index ee9ef73..160d252 100644 --- a/tests/intel_4001_state_machine_test.rs +++ b/tests/intel_4001_state_machine_test.rs @@ -38,8 +38,8 @@ fn test_intel_4001_state_machine_progression() { // Initialize clock pins to Low { - let mut phi1_guard = phi1_pin.lock().unwrap(); - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi1_guard = phi1_pin.lock(); + let mut phi2_guard = phi2_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } @@ -47,10 +47,10 @@ fn test_intel_4001_state_machine_progression() { // Initialize control pins // Note: RESET is active LOW, so set it to High to ensure component is not in reset { - 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 reset_guard = reset_pin.lock().unwrap(); + let mut sync_guard = sync_pin.lock(); + let mut cm_guard = cm_pin.lock(); + let mut ci_guard = ci_pin.lock(); + let mut reset_guard = reset_pin.lock(); sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low); cm_guard.set_driver(Some("TEST".to_string()), PinValue::Low); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); @@ -59,10 +59,10 @@ fn test_intel_4001_state_machine_progression() { // Initialize data pins to Low { - let mut d0_guard = d0_pin.lock().unwrap(); - let mut d1_guard = d1_pin.lock().unwrap(); - let mut d2_guard = d2_pin.lock().unwrap(); - let mut d3_guard = d3_pin.lock().unwrap(); + let mut d0_guard = d0_pin.lock(); + let mut d1_guard = d1_pin.lock(); + let mut d2_guard = d2_pin.lock(); + let mut d3_guard = d3_pin.lock(); d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); @@ -78,9 +78,9 @@ fn test_intel_4001_state_machine_progression() { // 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 @@ -88,10 +88,10 @@ fn test_intel_4001_state_machine_progression() { // Set up address high nibble (0x1 for address 0x12) { - let mut d0_guard = d0_pin.lock().unwrap(); - let mut d1_guard = d1_pin.lock().unwrap(); - let mut d2_guard = d2_pin.lock().unwrap(); - let mut d3_guard = d3_pin.lock().unwrap(); + let mut d0_guard = d0_pin.lock(); + let mut d1_guard = d1_pin.lock(); + let mut d2_guard = d2_pin.lock(); + let mut d3_guard = d3_pin.lock(); // 0x1 = 0001 (D0=1, D1=0, D2=0, D3=0) d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); @@ -104,7 +104,7 @@ fn test_intel_4001_state_machine_progression() { // Trigger Φ1 rising edge to start memory operation and latch high nibble { - 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); } @@ -120,7 +120,7 @@ fn test_intel_4001_state_machine_progression() { // Reset Φ1 to Low { - 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); } @@ -129,10 +129,10 @@ fn test_intel_4001_state_machine_progression() { // Set up address low nibble (0x2 for address 0x12) { - let mut d0_guard = d0_pin.lock().unwrap(); - let mut d1_guard = d1_pin.lock().unwrap(); - let mut d2_guard = d2_pin.lock().unwrap(); - let mut d3_guard = d3_pin.lock().unwrap(); + let mut d0_guard = d0_pin.lock(); + let mut d1_guard = d1_pin.lock(); + let mut d2_guard = d2_pin.lock(); + let mut d3_guard = d3_pin.lock(); // 0x2 = 0010 (D0=0, D1=1, D2=0, D3=0) d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d1_guard.set_driver(Some("TEST".to_string()), PinValue::High); @@ -146,7 +146,7 @@ fn test_intel_4001_state_machine_progression() { // Trigger Φ1 rising edge again - ROM should stay in AddressPhase // (address nibbles are latched on Φ1 falling edge, not rising) { - 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); } @@ -166,7 +166,7 @@ fn test_intel_4001_state_machine_progression() { // Reset Φ1 to Low to trigger falling edge - this is when low nibble is latched { - 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); } @@ -189,10 +189,10 @@ fn test_intel_4001_state_machine_progression() { // Remove test drivers from data pins to allow ROM to drive bus { - let mut d0_guard = d0_pin.lock().unwrap(); - let mut d1_guard = d1_pin.lock().unwrap(); - let mut d2_guard = d2_pin.lock().unwrap(); - let mut d3_guard = d3_pin.lock().unwrap(); + let mut d0_guard = d0_pin.lock(); + let mut d1_guard = d1_pin.lock(); + let mut d2_guard = d2_pin.lock(); + let mut d3_guard = d3_pin.lock(); d0_guard.remove_driver("TEST"); d1_guard.remove_driver("TEST"); d2_guard.remove_driver("TEST"); @@ -203,7 +203,7 @@ fn test_intel_4001_state_machine_progression() { if rom.get_timing_state() == TimingState::WaitLatency { // Trigger Φ2 rising edge { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } @@ -221,14 +221,14 @@ fn test_intel_4001_state_machine_progression() { // Verify data is being driven on the bus // Data 0xAB has low nibble 0xB = 1011 (D0=1, D1=1, D2=0, D3=1) // Note: Due to bit ordering, D0 should be High, D1 should be High, D2 should be Low, D3 should be High - assert_eq!(d0_pin.lock().unwrap().read(), PinValue::High); - assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); - assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low); - assert_eq!(d3_pin.lock().unwrap().read(), PinValue::High); + assert_eq!(d0_pin.lock().read(), PinValue::High); + assert_eq!(d1_pin.lock().read(), PinValue::High); + assert_eq!(d2_pin.lock().read(), PinValue::Low); + assert_eq!(d3_pin.lock().read(), PinValue::High); // Reset Φ2 to Low { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } @@ -240,10 +240,10 @@ fn test_intel_4001_state_machine_progression() { println!("Final state: {:?}", rom.get_timing_state()); // Verify data bus is 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 to verify that unified state machine doesn't progress without proper control signals @@ -261,8 +261,8 @@ fn test_intel_4001_state_machine_no_progression_without_signals() { // Initialize clock pins to Low { - let mut phi1_guard = phi1_pin.lock().unwrap(); - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi1_guard = phi1_pin.lock(); + let mut phi2_guard = phi2_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } @@ -275,7 +275,7 @@ fn test_intel_4001_state_machine_no_progression_without_signals() { // Trigger clock edges without setting up control signals { - 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); } @@ -287,7 +287,7 @@ fn test_intel_4001_state_machine_no_progression_without_signals() { // Reset Φ1 to Low { - 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); } @@ -296,7 +296,7 @@ fn test_intel_4001_state_machine_no_progression_without_signals() { // Trigger Φ2 rising edge { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } @@ -325,7 +325,7 @@ fn test_intel_4001_state_machine_reset() { // Initialize clock pins to Low { - 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); } @@ -337,9 +337,9 @@ fn test_intel_4001_state_machine_reset() { // 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 @@ -347,7 +347,7 @@ fn test_intel_4001_state_machine_reset() { // Trigger Φ1 rising edge to start memory operation { - 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); } @@ -359,7 +359,7 @@ fn test_intel_4001_state_machine_reset() { // Assert RESET (active LOW - set to Low to trigger 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::Low); } diff --git a/tests/intel_4002_state_machine_test.rs b/tests/intel_4002_state_machine_test.rs index b6c7687..341655d 100644 --- a/tests/intel_4002_state_machine_test.rs +++ b/tests/intel_4002_state_machine_test.rs @@ -32,8 +32,8 @@ fn create_test_ram() -> Intel4002 { let phi1_pin = ram.get_pin("PHI1").unwrap(); let phi2_pin = ram.get_pin("PHI2").unwrap(); { - let mut phi1_guard = phi1_pin.lock().unwrap(); - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi1_guard = phi1_pin.lock(); + let mut phi2_guard = phi2_pin.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } @@ -55,7 +55,7 @@ fn set_data_bus(ram: &Intel4002, value: u8) { for i in 0..4 { if let Ok(pin) = ram.get_pin(&format!("D{}", i)) { - if let Ok(mut pin_guard) = pin.lock() { + if let Some(mut pin_guard) = pin.try_lock() { let bit_value = (nibble >> i) & 1; let pin_value = if bit_value == 1 { PinValue::High @@ -74,7 +74,7 @@ fn set_data_bus(ram: &Intel4002, value: u8) { fn clear_data_bus_drivers(ram: &Intel4002) { for i in 0..4 { if let Ok(pin) = ram.get_pin(&format!("D{}", i)) { - if let Ok(mut pin_guard) = pin.lock() { + if let Some(mut pin_guard) = pin.try_lock() { pin_guard.remove_driver("TEST"); } } @@ -90,8 +90,8 @@ fn generate_clock_edge(ram: &mut Intel4002, phi1_high: bool, phi2_high: bool) { // Set clock pins { - let mut phi1_guard = phi1_pin.lock().unwrap(); - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi1_guard = phi1_pin.lock(); + let mut phi2_guard = phi2_pin.lock(); phi1_guard.set_driver( Some("TEST".to_string()), @@ -217,16 +217,16 @@ fn test_ram_read_state_progression() { // Read data bus value let mut data_value = 0; - if d0_pin.lock().unwrap().read() == PinValue::High { + if d0_pin.lock().read() == PinValue::High { data_value |= 1 << 0; } - if d1_pin.lock().unwrap().read() == PinValue::High { + if d1_pin.lock().read() == PinValue::High { data_value |= 1 << 1; } - if d2_pin.lock().unwrap().read() == PinValue::High { + if d2_pin.lock().read() == PinValue::High { data_value |= 1 << 2; } - if d3_pin.lock().unwrap().read() == PinValue::High { + if d3_pin.lock().read() == PinValue::High { data_value |= 1 << 3; } @@ -470,7 +470,7 @@ fn test_reset_functionality() { // Assert RESET (active low) let reset_pin = ram.get_pin("RESET").unwrap(); { - let mut reset_guard = reset_pin.lock().unwrap(); + let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } wait_for_pin_settlement(); diff --git a/tests/intel_4003_state_machine_test.rs b/tests/intel_4003_state_machine_test.rs index e9767dc..53f2ac2 100644 --- a/tests/intel_4003_state_machine_test.rs +++ b/tests/intel_4003_state_machine_test.rs @@ -13,7 +13,7 @@ use rusty_emu::pin::PinValue; /// Test helper to set pin value with proper driver management fn set_pin_value(component: &Intel4003, pin_name: &str, value: PinValue, driver: &str) { if let Ok(pin) = component.get_pin(pin_name) { - if let Ok(mut pin_guard) = pin.lock() { + if let Some(mut pin_guard) = pin.try_lock() { pin_guard.set_driver(Some(driver.to_string()), value); } } @@ -22,7 +22,7 @@ fn set_pin_value(component: &Intel4003, pin_name: &str, value: PinValue, driver: /// Test helper to remove pin driver fn remove_pin_driver(component: &Intel4003, pin_name: &str) { if let Ok(pin) = component.get_pin(pin_name) { - if let Ok(mut pin_guard) = pin.lock() { + if let Some(mut pin_guard) = pin.try_lock() { pin_guard.set_driver(None, PinValue::HighZ); } } diff --git a/tests/intel_4004_state_machine_test.rs b/tests/intel_4004_state_machine_test.rs index 368a144..e444b32 100644 --- a/tests/intel_4004_state_machine_test.rs +++ b/tests/intel_4004_state_machine_test.rs @@ -3,7 +3,8 @@ //! This test verifies that the Intel 4004 CPU state machine progresses correctly //! through all phases: Idle → AddressPhase → WaitLatency → DriveData → Idle -use std::sync::{Arc, Mutex}; +use parking_lot::Mutex; +use std::sync::Arc; use std::thread; use std::time::{Duration, Instant}; @@ -46,16 +47,8 @@ impl CpuTestHelper { let phi2 = Arc::new(Mutex::new(Pin::new("PHI2".to_string()))); // Connect CPU to test clock - cpu.get_pin("PHI1") - .unwrap() - .lock() - .unwrap() - .connect_to(phi1.clone()); - cpu.get_pin("PHI2") - .unwrap() - .lock() - .unwrap() - .connect_to(phi2.clone()); + cpu.get_pin("PHI1").unwrap().lock().connect_to(phi1.clone()); + cpu.get_pin("PHI2").unwrap().lock().connect_to(phi2.clone()); CpuTestHelper { cpu, @@ -90,11 +83,9 @@ impl CpuTestHelper { // Phase 1: Φ1 high, Φ2 low self.phi1 .lock() - .unwrap() .set_driver(Some("clock".to_string()), PinValue::High); self.phi2 .lock() - .unwrap() .set_driver(Some("clock".to_string()), PinValue::Low); thread::sleep(Duration::from_millis(1)); @@ -104,11 +95,9 @@ impl CpuTestHelper { // Phase 2: Φ1 low, Φ2 high self.phi1 .lock() - .unwrap() .set_driver(Some("clock".to_string()), PinValue::Low); self.phi2 .lock() - .unwrap() .set_driver(Some("clock".to_string()), PinValue::High); thread::sleep(Duration::from_millis(1)); @@ -117,11 +106,9 @@ impl CpuTestHelper { // Phase 3: Both low self.phi1 .lock() - .unwrap() .set_driver(Some("clock".to_string()), PinValue::Low); self.phi2 .lock() - .unwrap() .set_driver(Some("clock".to_string()), PinValue::Low); thread::sleep(Duration::from_millis(1)); @@ -137,28 +124,18 @@ impl CpuTestHelper { } fn set_control_pins(&self, sync: bool, cm: bool, reset: bool) { - self.cpu - .get_pin("SYNC") - .unwrap() - .lock() - .unwrap() - .set_driver( - Some("test".to_string()), - if sync { PinValue::High } else { PinValue::Low }, - ); - self.cpu.get_pin("CM").unwrap().lock().unwrap().set_driver( + self.cpu.get_pin("SYNC").unwrap().lock().set_driver( + Some("test".to_string()), + if sync { PinValue::High } else { PinValue::Low }, + ); + self.cpu.get_pin("CM").unwrap().lock().set_driver( Some("test".to_string()), if cm { PinValue::High } else { PinValue::Low }, ); - self.cpu - .get_pin("RESET") - .unwrap() - .lock() - .unwrap() - .set_driver( - Some("test".to_string()), - if reset { PinValue::High } else { PinValue::Low }, - ); + self.cpu.get_pin("RESET").unwrap().lock().set_driver( + Some("test".to_string()), + if reset { PinValue::High } else { PinValue::Low }, + ); } fn drive_data_bus(&self, data: u8) { @@ -168,7 +145,6 @@ impl CpuTestHelper { .get_pin(&format!("D{}", i)) .unwrap() .lock() - .unwrap() .set_driver( Some("test".to_string()), if bit_value == 1 { @@ -184,10 +160,9 @@ impl CpuTestHelper { let mut data = 0; for i in 0..4 { if let Ok(pin) = self.cpu.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; } } } @@ -197,9 +172,8 @@ impl CpuTestHelper { fn tri_state_data_bus(&self) { for i in 0..4 { if let Ok(pin) = self.cpu.get_pin(&format!("D{}", i)) { - if let Ok(mut pin_guard) = pin.lock() { - pin_guard.set_driver(Some("test".to_string()), PinValue::HighZ); - } + let mut pin_guard = pin.lock(); + pin_guard.set_driver(Some("test".to_string()), PinValue::HighZ); } } } @@ -294,7 +268,7 @@ fn test_intel_4004_state_machine_progression() { for i in 0..4 { let pin_name = format!("D{}", i); if let Ok(pin) = helper.cpu.get_pin(&pin_name) { - let mut pin_guard = pin.lock().unwrap(); + let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("test".to_string()), PinValue::High); } } @@ -352,7 +326,6 @@ fn test_intel_4004_reset_functionality() { .get_pin("RESET") .unwrap() .lock() - .unwrap() .set_driver(Some("test".to_string()), PinValue::High); helper.cpu.update(); @@ -362,7 +335,6 @@ fn test_intel_4004_reset_functionality() { .get_pin("RESET") .unwrap() .lock() - .unwrap() .set_driver(Some("test".to_string()), PinValue::Low); // Process the reset signal for multiple cycles to ensure it's applied @@ -378,7 +350,6 @@ fn test_intel_4004_reset_functionality() { .get_pin("RESET") .unwrap() .lock() - .unwrap() .set_driver(Some("test".to_string()), PinValue::High); helper.cpu.update(); thread::sleep(Duration::from_millis(1)); diff --git a/tests/intel_400x_tests.rs b/tests/intel_400x_tests.rs index 27263a2..6ed0bd5 100644 --- a/tests/intel_400x_tests.rs +++ b/tests/intel_400x_tests.rs @@ -1,10 +1,11 @@ +use parking_lot::Mutex; use rusty_emu::component::{BaseComponent, Component}; use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode}; use rusty_emu::pin::{Pin, PinValue}; use std::any::Any; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::time::Instant; // Mock component for testing trait implementations @@ -47,9 +48,17 @@ impl Component for MockComponent { false } + fn set_running(&mut self, _running: bool) { + // No-op for mock + } + fn as_any(&self) -> &dyn Any { self } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } } impl Intel400xAddressHandling for MockComponent { @@ -354,57 +363,29 @@ mod tests { let d3_pin = rom.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing - sync_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - cm_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - ci_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); + sync_pin.lock().set_settlement_time(Duration::from_nanos(0)); + cm_pin.lock().set_settlement_time(Duration::from_nanos(0)); + ci_pin.lock().set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - phi1_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - phi2_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d0_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d1_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d2_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d3_pin - .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); + phi1_pin.lock().set_settlement_time(Duration::from_nanos(0)); + phi2_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d0_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d1_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d2_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d3_pin.lock().set_settlement_time(Duration::from_nanos(0)); // Initially, all data pins should be HighZ - 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 reset (RESET is active LOW) println!("=== Testing 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::Low); // Assert reset } rom.update(); @@ -413,7 +394,7 @@ mod tests { // Release reset (set HIGH for normal operation) { - 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); // Release reset } rom.update(); @@ -466,11 +447,11 @@ mod tests { // Pin state snapshots println!( "Pin states: SYNC: {:?}, CM: {:?}, CI: {:?}, PHI1: {:?}, PHI2: {:?}", - sync_pin.lock().unwrap().read(), - cm_pin.lock().unwrap().read(), - ci_pin.lock().unwrap().read(), - phi1_pin.lock().unwrap().read(), - phi2_pin.lock().unwrap().read() + sync_pin.lock().read(), + cm_pin.lock().read(), + ci_pin.lock().read(), + phi1_pin.lock().read(), + phi2_pin.lock().read() ); println!("Test completed successfully!"); @@ -493,22 +474,22 @@ mod tests { // Reset to known state first (RESET is active LOW) { let reset_pin = rom.get_pin("RESET").unwrap(); - let mut reset_guard = reset_pin.lock().unwrap(); + let mut reset_guard = reset_pin.lock(); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW) } rom.update(); { let reset_pin = rom.get_pin("RESET").unwrap(); - 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); // Release reset (HIGH = normal operation) } rom.update(); // Set SYNC=1, CM=1, CI=0 for memory read { - 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); @@ -517,14 +498,14 @@ mod tests { // Ensure PHI2 is low initially { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); // Ensure PHI1 is low initially { - 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); } rom.update(); @@ -534,7 +515,7 @@ mod tests { // PHI1 low to high: Address phase for high nibble { - 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); } rom.update(); @@ -546,14 +527,14 @@ mod tests { // PHI1 high to low: Complete address phase for high nibble { - 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); } rom.update(); // PHI1 low to high again: Address phase for low nibble { - 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); } rom.update(); @@ -564,7 +545,7 @@ mod tests { // PHI1 high to low: Complete address phase { - 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); } rom.update(); @@ -589,14 +570,14 @@ mod tests { // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = rom.get_pin(&format!("D{}", i)).unwrap(); - let mut pin_guard = pin.lock().unwrap(); + let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } rom.update(); // PHI2 low to high: Data phase { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } rom.update(); @@ -612,17 +593,17 @@ mod tests { // PHI2 high to low: Cleanup { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } rom.update(); assert_eq!(rom.get_timing_state(), TimingState::Idle); // Tri-state pins - 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] @@ -644,46 +625,18 @@ mod tests { let d3_pin = chip.get_pin("D3").unwrap(); // Set settlement time to 0 for immediate testing - sync_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - cm_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - ci_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); + sync_pin.lock().set_settlement_time(Duration::from_nanos(0)); + cm_pin.lock().set_settlement_time(Duration::from_nanos(0)); + ci_pin.lock().set_settlement_time(Duration::from_nanos(0)); reset_pin .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - phi1_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - phi2_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d0_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d1_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d2_pin - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - d3_pin - .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); + phi1_pin.lock().set_settlement_time(Duration::from_nanos(0)); + phi2_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d0_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d1_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d2_pin.lock().set_settlement_time(Duration::from_nanos(0)); + d3_pin.lock().set_settlement_time(Duration::from_nanos(0)); // Test memory read at minimum timing // Note: Intel 4004 has a 4-bit data bus, so we can only read 4 bits at a time @@ -861,28 +814,19 @@ mod tests { // Set settlement times to 0 test_sync .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - test_cm - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - test_ci - .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); + test_cm.lock().set_settlement_time(Duration::from_nanos(0)); + test_ci.lock().set_settlement_time(Duration::from_nanos(0)); test_phi1 .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); test_phi2 .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Initialize PHI2 to Low to ensure proper edge detection { - let mut phi2_guard = test_phi2.lock().unwrap(); + let mut phi2_guard = test_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); @@ -890,9 +834,9 @@ mod tests { // Manually step through the state machine to verify timing // Set control pins for memory read { - let mut sync_guard = test_sync.lock().unwrap(); - let mut cm_guard = test_cm.lock().unwrap(); - let mut ci_guard = test_ci.lock().unwrap(); + let mut sync_guard = test_sync.lock(); + let mut cm_guard = test_cm.lock(); + let mut ci_guard = test_ci.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); @@ -902,14 +846,14 @@ mod tests { // Ensure PHI1 is low initially { - let mut phi1_guard = test_phi1.lock().unwrap(); + let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); // Start address phase - first PHI1 cycle for high nibble { - let mut phi1_guard = test_phi1.lock().unwrap(); + let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); @@ -921,7 +865,7 @@ mod tests { // PHI1 falling edge - latch high nibble { - let mut phi1_guard = test_phi1.lock().unwrap(); + let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); @@ -929,7 +873,7 @@ mod tests { // Second PHI1 cycle for low nibble { - let mut phi1_guard = test_phi1.lock().unwrap(); + let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); @@ -940,7 +884,7 @@ mod tests { // PHI1 falling edge - latch low nibble and complete address { - let mut phi1_guard = test_phi1.lock().unwrap(); + let mut phi1_guard = test_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); @@ -951,7 +895,7 @@ mod tests { // PHI2 high: Data phase { - let mut phi2_guard = test_phi2.lock().unwrap(); + let mut phi2_guard = test_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } test_chip.update(); @@ -963,7 +907,7 @@ mod tests { // Complete cycle { - let mut phi2_guard = test_phi2.lock().unwrap(); + let mut phi2_guard = test_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } test_chip.update(); @@ -984,37 +928,28 @@ mod tests { // Set settlement times to 0 fast_sync .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - fast_cm - .lock() - .unwrap() - .set_settlement_time(Duration::from_nanos(0)); - fast_ci - .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); + fast_cm.lock().set_settlement_time(Duration::from_nanos(0)); + fast_ci.lock().set_settlement_time(Duration::from_nanos(0)); fast_phi1 .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); fast_phi2 .lock() - .unwrap() .set_settlement_time(Duration::from_nanos(0)); // Initialize PHI2 to Low to ensure proper edge detection { - let mut phi2_guard = fast_phi2.lock().unwrap(); + let mut phi2_guard = fast_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Set up for memory read { - let mut sync_guard = fast_sync.lock().unwrap(); - let mut cm_guard = fast_cm.lock().unwrap(); - let mut ci_guard = fast_ci.lock().unwrap(); + let mut sync_guard = fast_sync.lock(); + let mut cm_guard = fast_cm.lock(); + let mut ci_guard = fast_ci.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); @@ -1023,14 +958,14 @@ mod tests { // Ensure PHI1 is low initially { - let mut phi1_guard = fast_phi1.lock().unwrap(); + let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // First PHI1 cycle - latch high nibble { - let mut phi1_guard = fast_phi1.lock().unwrap(); + let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); @@ -1041,14 +976,14 @@ mod tests { // PHI1 falling edge - latch high nibble { - let mut phi1_guard = fast_phi1.lock().unwrap(); + let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); // Second PHI1 cycle - latch low nibble { - let mut phi1_guard = fast_phi1.lock().unwrap(); + let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); @@ -1059,7 +994,7 @@ mod tests { // PHI1 falling edge - latch low nibble and complete address { - let mut phi1_guard = fast_phi1.lock().unwrap(); + let mut phi1_guard = fast_phi1.lock(); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } fast_chip.update(); @@ -1075,14 +1010,14 @@ mod tests { // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = fast_chip.get_pin(&format!("D{}", i)).unwrap(); - let mut pin_guard = pin.lock().unwrap(); + let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } fast_chip.update(); // Try to read data - PHI2 rising edge triggers data driving if latency elapsed { - let mut phi2_guard = fast_phi2.lock().unwrap(); + let mut phi2_guard = fast_phi2.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } fast_chip.update(); @@ -1124,9 +1059,9 @@ mod tests { // Set control pins for memory read { - 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); @@ -1135,21 +1070,21 @@ mod tests { // Ensure PHI2 is low initially { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update(); // Ensure PHI1 is low initially { - 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); } chip.update(); // PHI1 low to high: Address phase for high nibble { - 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); } chip.update(); @@ -1160,14 +1095,14 @@ mod tests { // PHI1 high to low: Complete address phase for high nibble { - 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); } chip.update(); // PHI1 low to high again: Address phase for low nibble { - 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); } chip.update(); @@ -1178,7 +1113,7 @@ mod tests { // PHI1 high to low: Complete address phase { - 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); } chip.update(); @@ -1208,14 +1143,14 @@ mod tests { // Release data bus from test control to allow ROM to drive it for i in 0..4 { let pin = chip.get_pin(&format!("D{}", i)).unwrap(); - let mut pin_guard = pin.lock().unwrap(); + let mut pin_guard = pin.lock(); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ); } chip.update(); // PHI2 low to high: Data phase { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High); } chip.update(); @@ -1230,7 +1165,7 @@ mod tests { // PHI2 high to low: Cleanup { - let mut phi2_guard = phi2_pin.lock().unwrap(); + let mut phi2_guard = phi2_pin.lock(); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low); } chip.update();