feat: migrate to parking_lot mutex and add system control architecture

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

3
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
@ -2503,6 +2503,7 @@ dependencies = [
"eframe", "eframe",
"egui", "egui",
"gtk", "gtk",
"parking_lot",
"ratatui", "ratatui",
"raw-window-handle 0.5.2", "raw-window-handle 0.5.2",
"rfd", "rfd",

View file

@ -17,6 +17,7 @@ egui = "0.24"
eframe = { version = "0.24", default-features = false, features = ["default_fonts", "glow", "persistence"] } eframe = { version = "0.24", default-features = false, features = ["default_fonts", "glow", "persistence"] }
rfd = "0.14" rfd = "0.14"
raw-window-handle = "0.5" raw-window-handle = "0.5"
parking_lot = "0.12"
# Force compatible versions for Rust 1.81.0 # Force compatible versions for Rust 1.81.0
[dependencies.tokio] [dependencies.tokio]

View file

@ -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<String, serde_json::Value>,
pub components: HashMap<String, ComponentConfig>,
pub connections: HashMap<String, ConnectionConfig>,
pub layout: Option<LayoutConfig>,
// NEW: UI Layout configuration
pub ui_layout: Option<UiLayoutConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiLayoutConfig {
pub console: Option<ConsoleLayoutConfig>,
pub gui: Option<GuiLayoutConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GuiLayoutConfig {
pub components: HashMap<String, ComponentLayoutConfig>,
pub grid_size: Option<[usize; 2]>,
pub refresh_rate_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentLayoutConfig {
pub position: [usize; 2],
pub size: [usize; 2],
pub visible: Option<bool>,
pub display_format: Option<String>,
}
```
### 3. Observer Registration in ConfigurableSystem
```rust
pub struct ConfigurableSystem {
config: SystemConfig,
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
is_running: Arc<Mutex<bool>>,
component_handles: Arc<Mutex<Option<ComponentHandles>>>,
// NEW: Observer registry
observers: Arc<Mutex<Vec<Box<dyn SystemObserver>>>>,
}
```
### 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

View file

@ -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<String, Arc<Mutex<Box<dyn Component>>>>,
}
```
## 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 <freq>` - 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

View file

@ -1,8 +1,9 @@
use parking_lot::Mutex;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Mutex, Arc,
}; };
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -42,10 +43,21 @@ pub trait Component: Send + Sync {
/// Returns: true if component is running, false otherwise /// Returns: true if component is running, false otherwise
fn is_running(&self) -> bool; 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 /// Get a reference to this component as Any for downcasting
/// This allows runtime type inspection to access component-specific methods /// This allows runtime type inspection to access component-specific methods
/// Returns: A reference to self as &dyn Any /// Returns: A reference to self as &dyn Any
fn as_any(&self) -> &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 /// Extended trait for components that can be run in their own threads
/// Provides automatic thread spawning functionality for components /// Provides automatic thread spawning functionality for components
@ -164,9 +176,17 @@ impl Component for BaseComponent {
self.running.load(Ordering::SeqCst) self.running.load(Ordering::SeqCst)
} }
fn set_running(&mut self, running: bool) {
self.running.store(running, Ordering::SeqCst);
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl RunnableComponent for BaseComponent {} impl RunnableComponent for BaseComponent {}

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -16,6 +17,8 @@ pub struct GenericClock {
high_time: Duration, high_time: Duration,
low_time: Duration, low_time: Duration,
enabled: bool, enabled: bool,
paused: bool, // Pause state for system control
pause_start: Option<Instant>, // When pause started, to track pause duration
} }
impl GenericClock { impl GenericClock {
@ -32,12 +35,27 @@ impl GenericClock {
high_time: Duration::from_secs_f64(0.5 / frequency), // Will be recalculated in set_duty_cycle 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 low_time: Duration::from_secs_f64(0.5 / frequency), // Will be recalculated in set_duty_cycle
enabled: true, enabled: true,
paused: false,
pause_start: None,
}; };
clock.set_duty_cycle(0.5); // Initialize timing clock.set_duty_cycle(0.5); // Initialize timing
clock 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) { 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.duty_cycle = duty_cycle.clamp(0.1, 0.9); // Keep within reasonable bounds
self.update_timing(); self.update_timing();
@ -56,6 +74,30 @@ impl GenericClock {
self.set_clock_output(PinValue::Low); 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 { fn frequency_to_duration(frequency: f64) -> Duration {
if frequency <= 0.0 { if frequency <= 0.0 {
Duration::from_secs(0) Duration::from_secs(0)
@ -72,18 +114,16 @@ impl GenericClock {
fn set_clock_output(&self, value: PinValue) { fn set_clock_output(&self, value: PinValue) {
if let Ok(clock_pin) = self.base.get_pin("CLK") { if let Ok(clock_pin) = self.base.get_pin("CLK") {
if let Ok(mut pin_guard) = clock_pin.lock() { let mut pin_guard = clock_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), value); pin_guard.set_driver(Some(self.base.get_name().to_string()), value);
} }
} }
}
fn read_enable_pin(&self) -> bool { fn read_enable_pin(&self) -> bool {
if let Ok(enable_pin) = self.base.get_pin("ENABLE") { if let Ok(enable_pin) = self.base.get_pin("ENABLE") {
if let Ok(pin_guard) = enable_pin.lock() { let pin_guard = enable_pin.lock();
return pin_guard.read() == PinValue::High; return pin_guard.read() == PinValue::High;
} }
}
true // Enabled by default if pin doesn't exist or can't be read 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) { fn update(&mut self) {
// Check pause state first - don't update if paused
if self.paused {
return;
}
// Read external enable control // Read external enable control
let external_enable = self.read_enable_pin(); let external_enable = self.read_enable_pin();
let should_be_enabled = self.enabled && external_enable; let should_be_enabled = self.enabled && external_enable;
@ -173,9 +218,17 @@ impl Component for GenericClock {
self.base.is_running() self.base.is_running()
} }
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
// Advanced clock features // Advanced clock features

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -14,6 +15,9 @@ pub struct TwoPhaseClock {
last_transition: Instant, last_transition: Instant,
phase_time: Duration, phase_time: Duration,
enabled: bool, enabled: bool,
paused: bool, // Pause state for system control
pause_start: Option<Instant>, // When pause started, to track pause duration
frequency: f64, // Store frequency for dynamic adjustment
} }
impl TwoPhaseClock { impl TwoPhaseClock {
@ -34,9 +38,25 @@ impl TwoPhaseClock {
last_transition: Instant::now(), last_transition: Instant::now(),
phase_time, phase_time,
enabled: true, 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) { pub fn enable(&mut self) {
self.enabled = true; self.enabled = true;
self.phi1_state = PinValue::High; self.phi1_state = PinValue::High;
@ -55,18 +75,41 @@ impl TwoPhaseClock {
self.update_outputs(); 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) { fn update_outputs(&self) {
// Set CLK output (for compatibility) // Set CLK output (for compatibility)
if let Ok(clk_pin) = self.base.get_pin("CLK") { if let Ok(clk_pin) = self.base.get_pin("CLK") {
if let Ok(mut pin_guard) = clk_pin.lock() { let mut pin_guard = clk_pin.lock();
// CLK follows PHI1 for compatibility // CLK follows PHI1 for compatibility
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state); pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
} }
}
// Set PHI1 output // Set PHI1 output
if let Ok(phi1_pin) = self.base.get_pin("PHI1") { if let Ok(phi1_pin) = self.base.get_pin("PHI1") {
if let Ok(mut pin_guard) = phi1_pin.lock() { let mut pin_guard = phi1_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state); pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi1_state);
// Check if pin has connections and trigger propagation // Check if pin has connections and trigger propagation
@ -76,11 +119,10 @@ impl TwoPhaseClock {
pin_guard.propagate(); pin_guard.propagate();
} }
} }
}
// Set PHI2 output // Set PHI2 output
if let Ok(phi2_pin) = self.base.get_pin("PHI2") { if let Ok(phi2_pin) = self.base.get_pin("PHI2") {
if let Ok(mut pin_guard) = phi2_pin.lock() { let mut pin_guard = phi2_pin.lock();
pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi2_state); pin_guard.set_driver(Some(self.base.get_name().to_string()), self.phi2_state);
// Check if pin has connections and trigger propagation // Check if pin has connections and trigger propagation
@ -90,7 +132,6 @@ impl TwoPhaseClock {
} }
} }
} }
}
fn should_transition(&self) -> bool { fn should_transition(&self) -> bool {
self.last_transition.elapsed() >= self.phase_time self.last_transition.elapsed() >= self.phase_time
@ -134,6 +175,11 @@ impl Component for TwoPhaseClock {
} }
fn update(&mut self) { fn update(&mut self) {
// Check pause state first - don't update if paused
if self.paused {
return;
}
if !self.enabled { if !self.enabled {
return; return;
} }
@ -174,7 +220,15 @@ impl Component for TwoPhaseClock {
self.base.is_running() self.base.is_running()
} }
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }

View file

@ -1,4 +1,5 @@
use std::sync::{Arc, Mutex}; use parking_lot::Mutex;
use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::component::{BaseComponent, Component}; use crate::component::{BaseComponent, Component};
@ -81,14 +82,11 @@ pub trait Intel400xClockHandling {
/// Get a clock pin value safely /// Get a clock pin value safely
fn get_clock_pin(&self, name: &str) -> PinValue { fn get_clock_pin(&self, name: &str) -> PinValue {
if let Ok(pin) = self.get_base().get_pin(name) { if let Ok(pin) = self.get_base().get_pin(name) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read_immediate() pin_guard.read_immediate()
} else { } else {
PinValue::Low PinValue::Low
} }
} else {
PinValue::Low
}
} }
/// Check for Φ1 rising edge /// Check for Φ1 rising edge
@ -134,13 +132,12 @@ pub trait Intel400xDataBus {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
if pin_guard.read_immediate() == PinValue::High { if pin_guard.read_immediate() == PinValue::High {
data |= 1 << i; data |= 1 << i;
} }
} }
} }
}
data & 0x0F data & 0x0F
} }
@ -152,7 +149,7 @@ pub trait Intel400xDataBus {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let bit_value = (nibble >> i) & 1; let bit_value = (nibble >> i) & 1;
let pin_value = if bit_value == 1 { let pin_value = if bit_value == 1 {
PinValue::High PinValue::High
@ -166,13 +163,12 @@ pub trait Intel400xDataBus {
} }
} }
} }
}
/// Set data bus to high-impedance state to avoid bus contention /// Set data bus to high-impedance state to avoid bus contention
fn tri_state_data_bus(&self) { fn tri_state_data_bus(&self) {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) { if let Ok(pin) = self.get_base().get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
pin_guard.set_driver( pin_guard.set_driver(
Some(format!("{}_DATA", self.get_base().get_name())), Some(format!("{}_DATA", self.get_base().get_name())),
PinValue::HighZ, PinValue::HighZ,
@ -180,7 +176,6 @@ pub trait Intel400xDataBus {
} }
} }
} }
}
} }
/// Common address handling functionality /// Common address handling functionality
@ -256,55 +251,43 @@ pub trait Intel400xControlPins {
/// Read SYNC pin state /// Read SYNC pin state
fn read_sync_pin(&self) -> bool { fn read_sync_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("SYNC") { if let Ok(pin) = self.get_base().get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read_immediate() == PinValue::High pin_guard.read_immediate() == PinValue::High
} else { } else {
false false
} }
} else {
false
}
} }
/// Read CM-ROM pin state /// Read CM-ROM pin state
fn read_cm_rom_pin(&self) -> bool { fn read_cm_rom_pin(&self) -> bool {
// Note: CM pin name varies by component // Note: CM pin name varies by component
if let Ok(pin) = self.get_base().get_pin("CM") { if let Ok(pin) = self.get_base().get_pin("CM") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else { } else {
false false
} }
} else {
false
}
} }
/// Read the CI (I/O select) pin state /// Read the CI (I/O select) pin state
fn read_ci_pin(&self) -> bool { fn read_ci_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("CI") { if let Ok(pin) = self.get_base().get_pin("CI") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else { } else {
false false
} }
} else {
false
}
} }
/// Read RESET pin state /// Read RESET pin state
/// Hardware: RESET is active LOW (pin LOW = reset active) /// Hardware: RESET is active LOW (pin LOW = reset active)
fn read_reset_pin(&self) -> bool { fn read_reset_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("RESET") { if let Ok(pin) = self.get_base().get_pin("RESET") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active
} else { } else {
false false
} }
} else {
false
}
} }
} }
@ -317,13 +300,10 @@ pub trait Intel400xResetHandling {
/// Note: RESET is active LOW (pin LOW = reset active) /// Note: RESET is active LOW (pin LOW = reset active)
fn handle_reset(&mut self, reset_pin_name: &str) -> bool { fn handle_reset(&mut self, reset_pin_name: &str) -> bool {
let reset = if let Ok(pin) = self.get_base().get_pin(reset_pin_name) { let reset = if let Ok(pin) = self.get_base().get_pin(reset_pin_name) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active pin_guard.read() == PinValue::Low // Active LOW: LOW means reset is active
} else { } else {
false false
}
} else {
false
}; };
if reset { if reset {
@ -1330,37 +1310,27 @@ pub mod utils {
/// Check if a pin value represents a logical high /// Check if a pin value represents a logical high
pub fn is_pin_high(pin: &Arc<Mutex<Pin>>) -> bool { pub fn is_pin_high(pin: &Arc<Mutex<Pin>>) -> bool {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else {
false
}
} }
/// Check if a pin value represents high impedance /// Check if a pin value represents high impedance
pub fn is_pin_high_z(pin: &Arc<Mutex<Pin>>) -> bool { pub fn is_pin_high_z(pin: &Arc<Mutex<Pin>>) -> bool {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::HighZ pin_guard.read() == PinValue::HighZ
} else {
false
}
} }
/// Safely read a pin value with fallback /// Safely read a pin value with fallback
pub fn read_pin_safe(pin: &Arc<Mutex<Pin>>, default: PinValue) -> PinValue { pub fn read_pin_safe(pin: &Arc<Mutex<Pin>>, _default: PinValue) -> PinValue {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() pin_guard.read()
} else {
default
}
} }
/// Safely set a pin driver with error handling /// Safely set a pin driver with error handling
pub fn set_pin_driver_safe(pin: &Arc<Mutex<Pin>>, driver_name: String, value: PinValue) { pub fn set_pin_driver_safe(pin: &Arc<Mutex<Pin>>, driver_name: String, value: PinValue) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(driver_name), value); pin_guard.set_driver(Some(driver_name), value);
} }
}
} }
// Re-export commonly used items for convenience // Re-export commonly used items for convenience

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -262,13 +263,12 @@ impl Intel4004 {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
if pin_guard.read() == PinValue::High { if pin_guard.read() == PinValue::High {
data |= 1 << i; data |= 1 << i;
} }
} }
} }
}
data & 0x0F data & 0x0F
} }
@ -280,7 +280,7 @@ impl Intel4004 {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let bit_value = (nibble >> i) & 1; let bit_value = (nibble >> i) & 1;
let pin_value = if bit_value == 1 { let pin_value = if bit_value == 1 {
PinValue::High PinValue::High
@ -291,17 +291,14 @@ impl Intel4004 {
} }
} }
} }
}
/// Set data bus to high-impedance state to avoid bus contention /// Set data bus to high-impedance state to avoid bus contention
/// CRITICAL: Must be called whenever CPU is not actively driving valid data /// CRITICAL: Must be called whenever CPU is not actively driving valid data
fn tri_state_data_bus(&self) { fn tri_state_data_bus(&self) {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
pin_guard pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
.set_driver(Some(self.base.get_name().parse().unwrap()), PinValue::HighZ);
}
} }
} }
} }
@ -310,55 +307,44 @@ impl Intel4004 {
/// Parameters: high - true for high voltage, false for low voltage /// Parameters: high - true for high voltage, false for low voltage
fn set_sync(&self, high: bool) { fn set_sync(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("SYNC") { if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let value = if high { PinValue::High } else { PinValue::Low }; let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value); pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
} }
} }
}
/// Set the CM (Chip Select) pin state /// Set the CM (Chip Select) pin state
/// Parameters: high - true for high voltage, false for low voltage /// Parameters: high - true for high voltage, false for low voltage
fn set_cm(&self, high: bool) { fn set_cm(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM") { if let Ok(pin) = self.base.get_pin("CM") {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let value = if high { PinValue::High } else { PinValue::Low }; let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value); pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
} }
} }
}
/// Read the state of all control pins /// Read the state of all control pins
/// Returns: (sync, cm, test) - State of control signals /// Returns: (sync, cm, test) - State of control signals
fn read_control_pins(&self) -> (bool, bool, bool) { fn read_control_pins(&self) -> (bool, bool, bool) {
let sync = if let Ok(pin) = self.base.get_pin("SYNC") { let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else { } else {
false false
}
} else {
false
}; };
let cm = if let Ok(pin) = self.base.get_pin("CM") { let cm = if let Ok(pin) = self.base.get_pin("CM") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else { } else {
false false
}
} else {
false
}; };
let test = if let Ok(pin) = self.base.get_pin("TEST") { let test = if let Ok(pin) = self.base.get_pin("TEST") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High pin_guard.read() == PinValue::High
} else { } else {
false false
}
} else {
false
}; };
(sync, cm, test) (sync, cm, test)
@ -369,23 +355,17 @@ impl Intel4004 {
/// Hardware: Intel 4004 provides two-phase clock for synchronization /// Hardware: Intel 4004 provides two-phase clock for synchronization
fn read_clock_pins(&self) -> (PinValue, PinValue) { fn read_clock_pins(&self) -> (PinValue, PinValue) {
let phi1 = if let Ok(pin) = self.base.get_pin("PHI1") { let phi1 = if let Ok(pin) = self.base.get_pin("PHI1") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() pin_guard.read()
} else { } else {
PinValue::Low PinValue::Low
}
} else {
PinValue::Low
}; };
let phi2 = if let Ok(pin) = self.base.get_pin("PHI2") { let phi2 = if let Ok(pin) = self.base.get_pin("PHI2") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() pin_guard.read()
} else { } else {
PinValue::Low PinValue::Low
}
} else {
PinValue::Low
}; };
(phi1, phi2) (phi1, phi2)
@ -442,13 +422,10 @@ impl Intel4004 {
/// Note: RESET is active low (triggered when pin is Low) /// Note: RESET is active low (triggered when pin is Low)
fn handle_reset(&mut self) { fn handle_reset(&mut self) {
let reset = if let Ok(pin) = self.base.get_pin("RESET") { let reset = if let Ok(pin) = self.base.get_pin("RESET") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low pin_guard.read() == PinValue::Low
} else { } else {
false false
}
} else {
false
}; };
if reset { if reset {
@ -1579,9 +1556,17 @@ impl Component for Intel4004 {
self.base.is_running() self.base.is_running()
} }
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl RunnableComponent for Intel4004 { impl RunnableComponent for Intel4004 {

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -73,7 +74,7 @@ impl MOS6502 {
fn set_address_bus(&self, address: u16) { fn set_address_bus(&self, address: u16) {
for i in 0..16 { for i in 0..16 {
if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("A{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let bit_value = (address >> i) & 1; let bit_value = (address >> i) & 1;
let pin_value = if bit_value == 1 { let pin_value = if bit_value == 1 {
PinValue::High PinValue::High
@ -84,12 +85,11 @@ impl MOS6502 {
} }
} }
} }
}
fn set_data_bus(&self, data: u8) { fn set_data_bus(&self, data: u8) {
for i in 0..8 { for i in 0..8 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let bit_value = (data >> i) & 1; let bit_value = (data >> i) & 1;
let pin_value = if bit_value == 1 { let pin_value = if bit_value == 1 {
PinValue::High PinValue::High
@ -100,72 +100,57 @@ impl MOS6502 {
} }
} }
} }
}
fn set_rw_pin(&self, read: bool) { fn set_rw_pin(&self, read: bool) {
if let Ok(pin) = self.base.get_pin("RW") { if let Ok(pin) = self.base.get_pin("RW") {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
let pin_value = if read { PinValue::High } else { PinValue::Low }; let pin_value = if read { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value); pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), pin_value);
} }
} }
}
fn _read_data_bus(&self) -> u8 { fn _read_data_bus(&self) -> u8 {
let mut data = 0; let mut data = 0;
for i in 0..8 { for i in 0..8 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
if pin_guard.read() == PinValue::High { if pin_guard.read() == PinValue::High {
data |= 1 << i; data |= 1 << i;
} }
} }
} }
}
data data
} }
pub(crate) fn read_control_pins(&self) -> (bool, bool, bool, bool) { pub(crate) fn read_control_pins(&self) -> (bool, bool, bool, bool) {
let irq = if let Ok(pin) = self.base.get_pin("IRQ") { let irq = if let Ok(pin) = self.base.get_pin("IRQ") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // IRQ is active low pin_guard.read() == PinValue::Low // IRQ is active low
} else { } else {
false false
}
} else {
false
}; };
let nmi = if let Ok(pin) = self.base.get_pin("NMI") { let nmi = if let Ok(pin) = self.base.get_pin("NMI") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // NMI is active low pin_guard.read() == PinValue::Low // NMI is active low
} else { } else {
false false
}
} else {
false
}; };
let reset = if let Ok(pin) = self.base.get_pin("RES") { let reset = if let Ok(pin) = self.base.get_pin("RES") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::Low // RESET is active low pin_guard.read() == PinValue::Low // RESET is active low
} else { } else {
false false
}
} else {
false
}; };
let rdy = if let Ok(pin) = self.base.get_pin("RDY") { let rdy = if let Ok(pin) = self.base.get_pin("RDY") {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
pin_guard.read() == PinValue::High // RDY is active high pin_guard.read() == PinValue::High // RDY is active high
} else { } else {
true // Default to ready if pin doesn't exist true // Default to ready if pin doesn't exist
}
} else {
true
}; };
(irq, nmi, reset, rdy) (irq, nmi, reset, rdy)
@ -233,9 +218,17 @@ impl Component for MOS6502 {
self.is_running self.is_running
} }
fn set_running(&mut self, running: bool) {
self.is_running = running;
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
// 6502-specific methods // 6502-specific methods

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
use parking_lot::Mutex;
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -337,12 +338,11 @@ impl Intel4003 {
fn tri_state_data_bus(&self) { fn tri_state_data_bus(&self) {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.base.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ); pin_guard.set_driver(Some(self.base.name()), PinValue::HighZ);
} }
} }
} }
}
/// Handle Φ1 rising edge - Address and control phase /// Handle Φ1 rising edge - Address and control phase
/// Hardware: Φ1 high = CPU drives bus with address/control information /// Hardware: Φ1 high = CPU drives bus with address/control information
@ -621,9 +621,17 @@ impl Component for Intel4003 {
self.base.is_running() self.base.is_running()
} }
fn set_running(&mut self, running: bool) {
self.base.set_running(running);
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl RunnableComponent for Intel4003 {} impl RunnableComponent for Intel4003 {}

View file

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

View file

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

View file

@ -15,6 +15,7 @@ use crossterm::{
execute, execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
}; };
use parking_lot::Mutex;
use ratatui::{ use ratatui::{
backend::CrosstermBackend, backend::CrosstermBackend,
layout::{Constraint, Direction, Layout, Rect}, layout::{Constraint, Direction, Layout, Rect},
@ -25,11 +26,11 @@ use ratatui::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io; use std::io;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use crate::system_config::ConfigurableSystem; use crate::system_config::{ConfigurableSystem, SystemController, SystemState};
/// Display format for memory and register values /// Display format for memory and register values
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
@ -89,7 +90,7 @@ impl Default for ConsoleConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
enabled: true, enabled: true,
refresh_rate_ms: 100, refresh_rate_ms: 10, // 100 Hz update rate (10ms interval)
show_ram: true, show_ram: true,
show_registers: true, show_registers: true,
show_system_info: true, show_system_info: true,
@ -111,6 +112,10 @@ pub struct ConsoleApp {
command_buffer: String, command_buffer: String,
show_help: bool, show_help: bool,
selected_pane: usize, selected_pane: usize,
/// Status message to display to the user (e.g., errors, feedback)
status_message: Option<String>,
/// When the status message was set, for auto-clearing
status_message_time: Option<Instant>,
} }
impl ConsoleApp { impl ConsoleApp {
@ -122,18 +127,76 @@ impl ConsoleApp {
command_buffer: String::new(), command_buffer: String::new(),
show_help: false, show_help: false,
selected_pane: 0, 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<dyn std::error::Error>> { pub fn run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
// Setup terminal // Set up panic hook to ensure terminal cleanup on panic
enable_raw_mode().map_err(|e| format!("Failed to enable raw mode: {}", e))?; // 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<dyn std::error::Error>> {
// 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(); let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen) execute!(stdout, EnterAlternateScreen).map_err(|e| {
.map_err(|e| format!("Failed to enter alternate screen: {}", 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 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; self.running = true;
let mut last_draw = Instant::now(); let mut last_draw = Instant::now();
@ -142,40 +205,63 @@ impl ConsoleApp {
while self.running { while self.running {
let now = Instant::now(); let now = Instant::now();
// Handle input with longer poll interval to reduce CPU usage // Handle input with poll interval to reduce CPU usage
if let Ok(true) = event::poll(Duration::from_millis(50)) { match event::poll(Duration::from_millis(50)) {
if let Ok(Event::Key(key)) = event::read() { Ok(true) => {
match event::read() {
Ok(Event::Key(key)) => {
self.handle_key_event(key.code, key.modifiers); 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 // Update display at regular intervals
if now.duration_since(last_draw) >= Duration::from_millis(self.config.refresh_rate_ms) { 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)) { match terminal.draw(|f| self.draw_ui(f)) {
eprintln!("DEBUG: Failed to draw UI: {}", e); Ok(_) => {
break;
}
last_draw = now; last_draw = now;
} }
Err(e) => {
// Check if system should still be running using try_lock to avoid blocking eprintln!("Terminal draw error: {}", e);
match self.system.try_lock() { // Try to recover by clearing and redrawing
Ok(system) => { if let Err(clear_err) = terminal.clear() {
if !system.is_running() && !self.show_help { eprintln!("Failed to clear terminal: {}", clear_err);
// 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);
}
thread::sleep(Duration::from_millis(500));
break; 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; break;
} }
} }
@ -184,13 +270,30 @@ impl ConsoleApp {
thread::sleep(Duration::from_millis(10)); thread::sleep(Duration::from_millis(10));
} }
// Restore terminal // Restore terminal - always attempt cleanup even if errors occurred
disable_raw_mode().map_err(|e| format!("Failed to disable raw mode: {}", e))?; let cleanup_result = self.cleanup_terminal(&mut terminal);
execute!(terminal.backend_mut(), LeaveAlternateScreen)
.map_err(|e| format!("Failed to leave alternate screen: {}", e))?; cleanup_result
terminal }
.show_cursor()
.map_err(|e| format!("Failed to show cursor: {}", e))?; fn cleanup_terminal(
&self,
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
) -> Result<(), Box<dyn std::error::Error>> {
// 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(()) Ok(())
} }
@ -200,138 +303,215 @@ impl ConsoleApp {
if key == KeyCode::Char('q') || key == KeyCode::Char('Q') { if key == KeyCode::Char('q') || key == KeyCode::Char('Q') {
if modifiers.contains(KeyModifiers::CONTROL) { if modifiers.contains(KeyModifiers::CONTROL) {
self.running = false; self.running = false;
if let Ok(mut system) = self.system.lock() { let mut system = self.system.lock();
system.stop(); system.stop();
}
return; return;
} }
} }
match key { match key {
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => { KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => {
println!("DEBUG: Quit key pressed, stopping console");
self.running = false; self.running = false;
if let Ok(mut system) = self.system.lock() { let mut system = self.system.lock();
system.stop(); system.stop();
} }
}
KeyCode::Char('h') | KeyCode::Char('H') => { KeyCode::Char('h') | KeyCode::Char('H') => {
println!("DEBUG: Help key pressed");
self.show_help = !self.show_help; self.show_help = !self.show_help;
} }
KeyCode::Char('r') | KeyCode::Char('R') => { KeyCode::Char('r') | KeyCode::Char('R') => {
println!("DEBUG: Run key pressed"); let mut system = self.system.lock();
if let Ok(mut system) = self.system.lock() {
if system.is_running() { if system.is_running() {
system.stop(); let _ = system.stop();
} else { } else {
system.run(); let _ = system.start();
}
} }
} }
KeyCode::Char('s') | KeyCode::Char('S') => { KeyCode::Char('s') | KeyCode::Char('S') => {
println!("DEBUG: Stop key pressed"); let mut system = self.system.lock();
if let Ok(mut system) = self.system.lock() {
system.stop(); system.stop();
} }
}
KeyCode::Char(' ') => { KeyCode::Char(' ') => {
println!("DEBUG: Space key pressed - single step not implemented"); // Space = pause/resume toggle
// Single step (if supported) let mut system = self.system.lock();
// This would require adding step functionality to the system 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 => { KeyCode::Tab => {
println!("DEBUG: Tab key pressed - switching panes");
self.selected_pane = (self.selected_pane + 1) % 3; self.selected_pane = (self.selected_pane + 1) % 3;
} }
KeyCode::Backspace => { KeyCode::Backspace => {
println!("DEBUG: Backspace key pressed");
self.command_buffer.pop(); self.command_buffer.pop();
} }
KeyCode::Enter => { KeyCode::Enter => {
println!(
"DEBUG: Enter key pressed - executing command: '{}'",
self.command_buffer
);
self.execute_command(); self.execute_command();
self.command_buffer.clear(); self.command_buffer.clear();
} }
KeyCode::Char(c) => { KeyCode::Char(c) => {
println!("DEBUG: Character key pressed: '{}'", c); if c.is_ascii_alphabetic() || c.is_ascii_digit() || c == '.' || c == ' ' {
if c.is_ascii_alphabetic() || c.is_ascii_digit() {
self.command_buffer.push(c); self.command_buffer.push(c);
} }
} }
_ => { _ => {}
println!("DEBUG: Unhandled key pressed: {:?}", key);
}
} }
} }
fn execute_command(&mut self) { fn execute_command(&mut self) {
let cmd = self.command_buffer.trim().to_lowercase(); 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" => { "quit" | "exit" | "q" => {
println!("DEBUG: Executing quit command");
self.running = false; self.running = false;
if let Ok(mut system) = self.system.lock() { let mut system = self.system.lock();
system.stop(); system.stop();
} }
} "run" | "r" | "start" => {
"run" | "r" => { let mut system = self.system.lock();
println!("DEBUG: Executing run command"); let _ = system.start();
if let Ok(mut system) = self.system.lock() {
system.run();
}
} }
"stop" | "s" => { "stop" | "s" => {
println!("DEBUG: Executing stop command"); let mut system = self.system.lock();
if let Ok(mut system) = self.system.lock() { let _ = system.stop();
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" => { "help" | "h" => {
println!("DEBUG: Toggling help display");
self.show_help = !self.show_help; self.show_help = !self.show_help;
} }
"reset" => { "reset" => {
println!("DEBUG: Executing reset command"); let mut system = self.system.lock();
if let Ok(mut system) = self.system.lock() { let _ = system.stop();
system.stop();
// Reset would need to be implemented in the system // Reset would need to be implemented in the system
} }
}
"status" => { "status" => {
println!("DEBUG: Executing status command"); let system = self.system.lock();
if let Ok(system) = self.system.lock() {
let info = system.get_system_info(); 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!("System: {} - {}", info.name, info.description);
println!( println!("Components: {}, State: {:?}", info.component_count, state);
"Components: {}, Running: {}", println!("Clock Frequency: {:.2} Hz", freq);
info.component_count, }
system.is_running() "freq" | "frequency" => {
); if parts.len() > 1 {
if let Ok(freq) = parts[1].parse::<f64>() {
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" => { "ram" => {
println!("DEBUG: Executing RAM display command"); // RAM display is always shown in the UI
if let Ok(_system) = self.system.lock() {
println!("RAM display requested - would show RAM contents here");
}
} }
"" => { "" => {
// Empty command - do nothing // Empty command - do nothing
} }
_ => { _ => {
println!("DEBUG: Unknown command: '{}'", cmd); println!("Unknown command: '{}'", cmd);
println!("Available commands: quit, run, stop, help, reset, status, ram"); 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(); let size = f.size();
if self.show_help { if self.show_help {
@ -358,14 +538,16 @@ impl ConsoleApp {
.add_modifier(Modifier::BOLD), .add_modifier(Modifier::BOLD),
)]), )]),
Line::from(vec![ Line::from(vec![
Span::raw("Commands: "), Span::raw("Keys: "),
Span::styled("q/Q", Style::default().fg(Color::Yellow)), Span::styled("Space", Style::default().fg(Color::Yellow)),
Span::raw("=quit, "), Span::raw("=pause/resume, "),
Span::styled("r/R", Style::default().fg(Color::Yellow)), Span::styled("+/-", Style::default().fg(Color::Yellow)),
Span::raw("=freq, "),
Span::styled("r", Style::default().fg(Color::Yellow)),
Span::raw("=run, "), Span::raw("=run, "),
Span::styled("s/S", Style::default().fg(Color::Yellow)), Span::styled("s", Style::default().fg(Color::Yellow)),
Span::raw("=stop, "), Span::raw("=stop, "),
Span::styled("h/H", Style::default().fg(Color::Yellow)), Span::styled("h", Style::default().fg(Color::Yellow)),
Span::raw("=help"), Span::raw("=help"),
]), ]),
]; ];
@ -390,15 +572,23 @@ impl ConsoleApp {
// Right pane - RAM contents // Right pane - RAM contents
self.draw_ram_contents(f, content_chunks[1]); self.draw_ram_contents(f, content_chunks[1]);
// Command bar // Command bar - show status message or command buffer
let command_text = if self.command_buffer.is_empty() { 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)..." "Enter command (type 'h' for help)..."
} else { } else {
&self.command_buffer &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) let command_bar = Paragraph::new(command_text)
.style(Style::default().fg(Color::White)) .style(command_style)
.block(Block::default().borders(Borders::ALL).title("Command")); .block(Block::default().borders(Borders::ALL).title("Command"));
f.render_widget(command_bar, chunks[2]); f.render_widget(command_bar, chunks[2]);
} }
@ -414,7 +604,7 @@ impl ConsoleApp {
)]), )]),
Line::from(""), Line::from(""),
Line::from(vec![Span::styled( Line::from(vec![Span::styled(
"Commands:", "System Control Commands:",
Style::default().add_modifier(Modifier::BOLD), Style::default().add_modifier(Modifier::BOLD),
)]), )]),
Line::from(vec![ Line::from(vec![
@ -422,22 +612,64 @@ impl ConsoleApp {
Span::raw(" - Exit emulator"), Span::raw(" - Exit emulator"),
]), ]),
Line::from(vec![ Line::from(vec![
Span::styled(" r, run", Style::default().fg(Color::Yellow)), Span::styled(" r, run, start", Style::default().fg(Color::Yellow)),
Span::raw(" - Start/stop system execution"), Span::raw(" - Start system execution"),
]), ]),
Line::from(vec![ Line::from(vec![
Span::styled(" s, stop", Style::default().fg(Color::Yellow)), Span::styled(" s, stop", Style::default().fg(Color::Yellow)),
Span::raw(" - Stop system execution"), Span::raw(" - Stop system execution"),
]), ]),
Line::from(vec![ Line::from(vec![
Span::styled(" h, help", Style::default().fg(Color::Yellow)), Span::styled(" p, pause", Style::default().fg(Color::Yellow)),
Span::raw(" - Show/hide this help"), 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![ Line::from(vec![
Span::styled(" reset", Style::default().fg(Color::Yellow)), Span::styled(" reset", Style::default().fg(Color::Yellow)),
Span::raw(" - Reset system"), Span::raw(" - Reset system"),
]), ]),
Line::from(""), 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( Line::from(vec![Span::styled(
"Navigation:", "Navigation:",
Style::default().add_modifier(Modifier::BOLD), Style::default().add_modifier(Modifier::BOLD),
@ -454,6 +686,10 @@ impl ConsoleApp {
Span::styled(" Backspace", Style::default().fg(Color::Yellow)), Span::styled(" Backspace", Style::default().fg(Color::Yellow)),
Span::raw(" - Delete character"), 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(""),
Line::from(vec![Span::raw("Press any key to return to main view...")]), Line::from(vec![Span::raw("Press any key to return to main view...")]),
]; ];
@ -469,16 +705,27 @@ impl ConsoleApp {
let info_chunks = Layout::default() let info_chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
Constraint::Length(8), // System info Constraint::Length(10), // System info (increased for state display)
Constraint::Min(5), // Registers Constraint::Min(5), // Registers
]) ])
.split(area); .split(area);
// System information - use try_lock to avoid blocking // System information - use blocking lock, GUI can wait
let system_info = match self.system.try_lock() { let system = self.system.lock();
Ok(system) => {
let info = system.get_system_info(); let info = system.get_system_info();
vec![ 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!("System: {}", info.name))]),
Line::from(vec![Span::raw(format!( Line::from(vec![Span::raw(format!(
"Description: {}", "Description: {}",
@ -488,6 +735,22 @@ impl ConsoleApp {
"Components: {}", "Components: {}",
info.component_count 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!("CPU Speed: {} Hz", info.cpu_speed))]),
Line::from(vec![Span::raw(format!( Line::from(vec![Span::raw(format!(
"ROM Size: {} bytes", "ROM Size: {} bytes",
@ -497,20 +760,8 @@ impl ConsoleApp {
"RAM Size: {} nibbles", "RAM Size: {} nibbles",
info.ram_size info.ram_size
))]), ))]),
Line::from(vec![Span::raw(format!("Running: {}", system.is_running()))]), ];
] drop(system); // Release lock before rendering
}
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",
)])]
}
};
let system_widget = Paragraph::new(system_info) let system_widget = Paragraph::new(system_info)
.block( .block(
@ -542,10 +793,11 @@ impl ConsoleApp {
fn get_cpu_register_info(&self) -> Vec<Line<'static>> { fn get_cpu_register_info(&self) -> Vec<Line<'static>> {
let mut register_info = vec![Line::from(vec![Span::raw("CPU Registers:")])]; let mut register_info = vec![Line::from(vec![Span::raw("CPU Registers:")])];
match self.system.try_lock() { // Use blocking lock - GUI can wait for simulation
Ok(system) => { let system = self.system.lock();
// Try to find Intel 4004 CPU and get register data // Try to find Intel 4004 CPU and get register data
let cpu_data = system.get_cpu_register_data(); let cpu_data = system.get_cpu_register_data();
drop(system); // Release lock after getting data
if let Some(cpu) = cpu_data { if let Some(cpu) = cpu_data {
let fmt = &self.config.display_format; let fmt = &self.config.display_format;
@ -582,28 +834,35 @@ impl ConsoleApp {
} else { } else {
register_info.push(Line::from(vec![Span::raw("No Intel 4004 CPU found")])); register_info.push(Line::from(vec![Span::raw("No Intel 4004 CPU found")]));
} }
}
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 register_info
} }
fn draw_ram_contents(&self, f: &mut Frame, area: Rect) { 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:")])]; 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 { if self.config.show_ram {
match self.system.try_lock() { let system = self.system.lock();
Ok(system) => {
let ram_data = system.get_ram_data(self.config.max_ram_rows); let ram_data = system.get_ram_data(self.config.max_ram_rows);
drop(system); // Release lock after getting data
if ram_data.is_empty() { if ram_data.is_empty() {
ram_info.push(Line::from(vec![Span::raw("No RAM chips found")])); ram_info.push(Line::from(vec![Span::raw("No RAM chips found")]));
@ -631,16 +890,6 @@ impl ConsoleApp {
} }
} }
} }
}
Err(std::sync::TryLockError::WouldBlock) => {
ram_info.push(Line::from(vec![Span::raw(
"RAM data temporarily unavailable (system busy)...",
)]));
}
Err(_) => {
ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")]));
}
}
} else { } else {
ram_info.push(Line::from(vec![Span::raw("RAM display disabled")])); ram_info.push(Line::from(vec![Span::raw("RAM display disabled")]));
} }
@ -650,6 +899,59 @@ impl ConsoleApp {
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
f.render_widget(ram_widget, area); 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 /// Public interface for launching the console

View file

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

View file

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

View file

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

View file

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

View file

@ -53,9 +53,11 @@ use crate::component::Component;
use crate::components::cpu::intel_4004::Intel4004; use crate::components::cpu::intel_4004::Intel4004;
use crate::components::memory::intel_4001::Intel4001; use crate::components::memory::intel_4001::Intel4001;
use crate::components::memory::intel_4002::Intel4002; use crate::components::memory::intel_4002::Intel4002;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::time::Duration;
/// CPU register data for display purposes /// CPU register data for display purposes
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
@ -73,6 +75,74 @@ pub type RamData = Vec<(String, Vec<Vec<u8>>)>;
/// ROM data for display purposes (chip name -> bytes) /// ROM data for display purposes (chip name -> bytes)
pub type RomData = Vec<(String, Vec<u8>)>; pub type RomData = Vec<(String, Vec<u8>)>;
/// Maximum allowed clock frequency (10 MHz)
pub const MAX_CLOCK_FREQUENCY: f64 = 10_000_000.0;
/// Minimum allowed clock frequency (1 Hz)
pub const MIN_CLOCK_FREQUENCY: f64 = 1.0;
/// System execution state
/// Tracks the overall state of the system for control and monitoring
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SystemState {
/// System is stopped - no components are running
Stopped,
/// System is running normally
Running,
/// System is paused - components are frozen but can be resumed
Paused,
/// System encountered an error
Error,
}
impl std::fmt::Display for SystemState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SystemState::Stopped => write!(f, "Stopped"),
SystemState::Running => write!(f, "Running"),
SystemState::Paused => write!(f, "Paused"),
SystemState::Error => write!(f, "Error"),
}
}
}
/// System controller trait for comprehensive system management
/// Provides start/stop/pause/resume functionality and clock control
pub trait SystemController {
/// Start the system from a stopped or paused state
fn start(&mut self) -> Result<(), String>;
/// Stop the system completely
fn stop(&mut self) -> Result<(), String>;
/// Pause the system - freeze all components
fn pause(&mut self) -> Result<(), String>;
/// Resume the system from a paused state
fn resume(&mut self) -> Result<(), String>;
/// Check if the system is currently running
fn is_running(&self) -> bool;
/// Check if the system is paused
fn is_paused(&self) -> bool;
/// Get the current system state
fn get_system_state(&self) -> SystemState;
/// Set the clock frequency for all clock components
fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), String>;
/// Get the current clock frequency
fn get_clock_frequency(&self) -> f64;
/// Enable or disable the clock
fn set_clock_enabled(&mut self, enabled: bool) -> Result<(), String>;
/// Check if the clock is enabled
fn is_clock_enabled(&self) -> bool;
}
/// JSON-based system configuration structures /// JSON-based system configuration structures
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemConfig { pub struct SystemConfig {
@ -348,7 +418,7 @@ impl SystemFactory {
})?; })?;
let source_pin = { let source_pin = {
let component = source_component.lock().unwrap(); let component = source_component.lock();
component component
.get_pin(&connection_config.source.pin) .get_pin(&connection_config.source.pin)
.map_err(|e| format!("Failed to get source pin: {}", e))? .map_err(|e| format!("Failed to get source pin: {}", e))?
@ -361,14 +431,14 @@ impl SystemFactory {
})?; })?;
let target_pin = { let target_pin = {
let component = target_component.lock().unwrap(); let component = target_component.lock();
component component
.get_pin(&target_ref.pin) .get_pin(&target_ref.pin)
.map_err(|e| format!("Failed to get target pin: {}", e))? .map_err(|e| format!("Failed to get target pin: {}", e))?
}; };
// Connect the pins - source connects to target for proper signal flow // 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()); source_pin_guard.connect_to(target_pin.clone());
} }
} }
@ -385,6 +455,9 @@ pub struct ConfigurableSystem {
config: SystemConfig, config: SystemConfig,
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>, components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
is_running: Arc<Mutex<bool>>, is_running: Arc<Mutex<bool>>,
is_paused: Arc<Mutex<bool>>,
clock_frequency: Arc<Mutex<f64>>,
clock_enabled: Arc<Mutex<bool>>,
component_handles: Arc<Mutex<Option<ComponentHandles>>>, component_handles: Arc<Mutex<Option<ComponentHandles>>>,
} }
@ -393,10 +466,20 @@ impl ConfigurableSystem {
config: SystemConfig, config: SystemConfig,
components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>, components: HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
) -> Self { ) -> 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 { ConfigurableSystem {
config, config,
components, components,
is_running: Arc::new(Mutex::new(false)), 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)), 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. /// allowing the caller to retain access to the system via the mutex.
/// The system will continue running in the background until `stop()` is called. /// The system will continue running in the background until `stop()` is called.
pub fn start(&mut self) { 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 { if *is_running {
println!("System is already running"); println!("System is already running");
return; return;
@ -420,31 +503,80 @@ impl ConfigurableSystem {
let mut handles = vec![]; let mut handles = vec![];
let is_running_clone = Arc::clone(&self.is_running); 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 { for (name, component) in &self.components {
let comp_clone = Arc::clone(component); let comp_clone = Arc::clone(component);
let name_clone = name.clone();
let running_clone = Arc::clone(&is_running_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 || { let handle = std::thread::spawn(move || {
println!("DEBUG: Starting component thread: {}", name_clone); // Run loop that doesn't hold the lock continuously
if let Ok(mut comp) = comp_clone.lock() { // This allows other threads (like cleanup) to access the component
println!("DEBUG: Running component: {}", name_clone); loop {
comp.run(); // Check system-level running flag first
} let should_continue = *running_clone.lock();
println!("DEBUG: Component {} stopped", name_clone);
// Signal that system should stop when any component stops if !should_continue {
if let Ok(mut running) = running_clone.lock() { break;
*running = false;
} }
// 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)); handles.push((name.clone(), handle));
} }
// Store handles for later cleanup // 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); *stored_handles = Some(handles);
} }
@ -469,22 +601,32 @@ impl ConfigurableSystem {
/// Clean up component threads and stop all components. /// Clean up component threads and stop all components.
fn cleanup(&mut self) { fn cleanup(&mut self) {
// Signal stop // Signal stop first - this tells all component threads to stop
if let Ok(mut is_running) = self.is_running.lock() { {
let mut is_running = self.is_running.lock();
*is_running = false; *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..."); println!("\nStopping system components...");
for (name, component) in &self.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(); comp.stop();
println!("Stopped component: {}", name); 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 // Wait for threads to finish
if let Ok(mut handles_guard) = self.component_handles.lock() { // 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() { if let Some(handles) = handles_guard.take() {
for (name, handle) in handles { for (name, handle) in handles {
match handle.join() { match handle.join() {
@ -511,9 +653,7 @@ impl ConfigurableSystem {
std::thread::sleep(std::time::Duration::from_millis(500)); std::thread::sleep(std::time::Duration::from_millis(500));
// Check if system should stop // Check if system should stop
let should_continue = self.is_running.lock() let should_continue = *self.is_running.lock();
.map(|guard| *guard)
.unwrap_or(false);
if !should_continue { if !should_continue {
println!("\nSystem stop requested - terminating monitoring"); println!("\nSystem stop requested - terminating monitoring");
@ -545,7 +685,7 @@ impl ConfigurableSystem {
// Display RAM contents prominently // Display RAM contents prominently
if let Some(ram_component) = self.components.get("RAM_4002") { if let Some(ram_component) = self.components.get("RAM_4002") {
if let Ok(ram) = ram_component.lock() { let ram = ram_component.lock();
println!("│ RAM Contents (Intel 4002 - 320 bits / 80 x 4-bit words): │"); println!("│ RAM Contents (Intel 4002 - 320 bits / 80 x 4-bit words): │");
// Display RAM in a grid format for better readability // Display RAM in a grid format for better readability
@ -553,16 +693,13 @@ impl ConfigurableSystem {
print!("│ Bank {:2}: ", bank); print!("│ Bank {:2}: ", bank);
for addr in 0..4 { for addr in 0..4 {
let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr)) { let value = if let Ok(pin) = ram.get_pin(&format!("D{}", addr)) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
// Read actual pin value if possible // Read actual pin value if possible
match pin_guard.read() { match pin_guard.read() {
crate::pin::PinValue::High => "1", crate::pin::PinValue::High => "1",
crate::pin::PinValue::Low => "0", crate::pin::PinValue::Low => "0",
crate::pin::PinValue::HighZ => "Z", crate::pin::PinValue::HighZ => "Z",
} }
} else {
"L"
}
} else { } else {
"?" "?"
}; };
@ -570,14 +707,13 @@ impl ConfigurableSystem {
} }
println!(" │"); println!(" │");
} }
}
} else { } else {
println!("│ RAM_4002 component not found │"); println!("│ RAM_4002 component not found │");
} }
// Display CPU state // Display CPU state
if let Some(cpu_component) = self.components.get("CPU_4004") { if let Some(cpu_component) = self.components.get("CPU_4004") {
if let Ok(_cpu) = cpu_component.lock() { let _cpu = cpu_component.lock();
println!("│ CPU State: │"); println!("│ CPU State: │");
println!( println!(
"│ Accumulator: 0x{:X} Carry: {} PC: 0x{:03X} │", "│ Accumulator: 0x{:X} Carry: {} PC: 0x{:03X} │",
@ -586,20 +722,18 @@ impl ConfigurableSystem {
0 0
); // Placeholder - would need CPU-specific method ); // Placeholder - would need CPU-specific method
} }
}
// Display clock state // Display clock state
if let Some(clock_component) = self.components.get("SYSTEM_CLOCK") { if let Some(clock_component) = self.components.get("SYSTEM_CLOCK") {
if let Ok(_clock) = clock_component.lock() { let _clock = clock_component.lock();
println!("│ Clock: Running │"); println!("│ Clock: Running │");
} }
}
// Component status summary // Component status summary
let running_count = self let running_count = self
.components .components
.values() .values()
.filter(|comp| comp.lock().map_or(false, |c| c.is_running())) .filter(|comp| comp.lock().is_running())
.count(); .count();
println!( println!(
"│ Components: {}/{} running │", "│ Components: {}/{} running │",
@ -611,7 +745,8 @@ impl ConfigurableSystem {
} }
pub fn stop(&mut self) { 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; *is_running = false;
} }
// Also cleanup threads // Also cleanup threads
@ -619,9 +754,7 @@ impl ConfigurableSystem {
} }
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {
self.is_running.lock() *self.is_running.lock()
.map(|guard| *guard)
.unwrap_or(false)
} }
/// Get access to components for monitoring purposes /// Get access to components for monitoring purposes
@ -664,48 +797,54 @@ impl ConfigurableSystem {
/// Returns: Ok(()) on success, Err(String) on failure /// Returns: Ok(()) on success, Err(String) on failure
pub fn load_program_data(&mut self, program_data: &[u8]) -> Result<(), String> { pub fn load_program_data(&mut self, program_data: &[u8]) -> Result<(), String> {
println!( println!(
"DEBUG: Loading {} bytes of program data into ROM components", "Loading {} bytes of program data into ROM components",
program_data.len() program_data.len()
); );
// Load program data into ROM_4001_1 (first 256 bytes) // Load program data into ROM_4001_1 (first 256 bytes)
if let Some(rom1_component) = self.components.get_mut("ROM_4001_1") { if let Some(rom1_component) = self.components.get("ROM_4001_1") {
if let Ok(_rom1) = rom1_component.lock() { let mut rom1 = rom1_component.lock();
if let Some(rom) = rom1.as_any_mut().downcast_mut::<Intel4001>() {
let rom1_data = &program_data[..program_data.len().min(256)]; let rom1_data = &program_data[..program_data.len().min(256)];
println!("DEBUG: Loading {} bytes into ROM_4001_1", rom1_data.len()); rom.load_rom_data(rom1_data.to_vec(), 0)
println!("DEBUG: Program data: {:02X?}", rom1_data); .map_err(|e| format!("Failed to load ROM_4001_1: {}", e))?;
// TODO: Load data using Intel4001's load_rom_data method when downcast is working println!("Loaded {} bytes into ROM_4001_1", rom1_data.len());
println!("DEBUG: Program data should be loaded into ROM_4001_1"); } else {
return Err("ROM_4001_1 is not an Intel4001 component".to_string());
} }
} else { } 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 // Load remaining data into ROM_4001_2 if program is larger than 256 bytes
if program_data.len() > 256 { if program_data.len() > 256 {
if let Some(rom2_component) = self.components.get_mut("ROM_4001_2") { if let Some(rom2_component) = self.components.get("ROM_4001_2") {
if let Ok(_rom2) = rom2_component.lock() { let mut rom2 = rom2_component.lock();
if let Some(rom) = rom2.as_any_mut().downcast_mut::<Intel4001>() {
let rom2_data = &program_data[256..program_data.len().min(512)]; let rom2_data = &program_data[256..program_data.len().min(512)];
println!("DEBUG: Loading {} bytes into ROM_4001_2", rom2_data.len()); rom.load_rom_data(rom2_data.to_vec(), 0)
// TODO: Load data using Intel4001's load_rom_data method when available .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 { } 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(()) Ok(())
} }
/// Get CPU register data from Intel 4004 CPU /// Get CPU register data from Intel 4004 CPU
/// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise /// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise
/// Uses try_lock() to avoid blocking when component is busy /// Uses blocking lock - GUI can wait for simulation to release lock
pub fn get_cpu_register_data(&self) -> Option<CpuRegisterData> { pub fn get_cpu_register_data(&self) -> Option<CpuRegisterData> {
// Look for Intel 4004 CPU components // Look for Intel 4004 CPU components
for (_name, component) in &self.components { for (_name, component) in &self.components {
match component.try_lock() { // Use blocking lock - GUI can wait for simulation
Ok(comp) => { let comp = component.lock();
// Try to downcast to Intel4004 // Try to downcast to Intel4004
if let Some(cpu) = comp.as_any().downcast_ref::<Intel4004>() { if let Some(cpu) = comp.as_any().downcast_ref::<Intel4004>() {
let mut index_registers = [0u8; 16]; let mut index_registers = [0u8; 16];
@ -722,29 +861,19 @@ impl ConfigurableSystem {
}); });
} }
} }
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;
}
}
}
None None
} }
/// Get RAM data from Intel 4002 RAM chips /// Get RAM data from Intel 4002 RAM chips
/// Parameters: max_rows - Maximum number of rows to return per bank /// 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 /// 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 { pub fn get_ram_data(&self, max_rows: usize) -> RamData {
let mut ram_data = Vec::new(); let mut ram_data = Vec::new();
for (name, component) in &self.components { for (_name, component) in &self.components {
match component.try_lock() { // Use blocking lock - GUI can wait for simulation
Ok(comp) => { let comp = component.lock();
// Try to downcast to Intel4002 // Try to downcast to Intel4002
if let Some(ram) = comp.as_any().downcast_ref::<Intel4002>() { if let Some(ram) = comp.as_any().downcast_ref::<Intel4002>() {
let mut banks = Vec::new(); let mut banks = Vec::new();
@ -766,17 +895,7 @@ impl ConfigurableSystem {
banks.push(bank_data); banks.push(bank_data);
} }
ram_data.push((name.clone(), banks)); ram_data.push((_name.clone(), banks));
}
}
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;
}
} }
} }
@ -786,13 +905,13 @@ impl ConfigurableSystem {
/// Get ROM data from Intel 4001 ROM chips /// Get ROM data from Intel 4001 ROM chips
/// Parameters: max_rows - Maximum number of bytes to return per chip /// Parameters: max_rows - Maximum number of bytes to return per chip
/// Returns: Vector of (chip_name, bytes) /// 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 { pub fn get_rom_data(&self, max_rows: usize) -> RomData {
let mut rom_data = Vec::new(); let mut rom_data = Vec::new();
for (name, component) in &self.components { for (name, component) in &self.components {
match component.try_lock() { // Use blocking lock - GUI can wait for simulation
Ok(comp) => { let comp = component.lock();
// Try to downcast to Intel4001 // Try to downcast to Intel4001
if let Some(rom) = comp.as_any().downcast_ref::<Intel4001>() { if let Some(rom) = comp.as_any().downcast_ref::<Intel4001>() {
let mut data = Vec::new(); let mut data = Vec::new();
@ -806,18 +925,252 @@ impl ConfigurableSystem {
rom_data.push((name.clone(), data)); rom_data.push((name.clone(), data));
} }
} }
Err(std::sync::TryLockError::WouldBlock) => {
// Component is busy, skip to next component rom_data
continue;
} }
Err(std::sync::TryLockError::Poisoned(_)) => { }
// Handle poisoned mutex - skip to next component
continue; // Implement SystemController trait for ConfigurableSystem
impl SystemController for ConfigurableSystem {
fn start(&mut self) -> Result<(), String> {
let is_paused = *self.is_paused.lock();
if is_paused {
// Resume from pause
return self.resume();
} }
let is_running = *self.is_running.lock();
if is_running {
return Err("System is already running".to_string());
}
// Call the inherent method explicitly to avoid infinite recursion
ConfigurableSystem::start(self);
Ok(())
}
fn stop(&mut self) -> Result<(), String> {
let is_running = *self.is_running.lock();
if !is_running {
return Err("System is not running".to_string());
}
// Reset pause state when stopping
{
let mut paused = self.is_paused.lock();
*paused = false;
}
// Call the inherent method explicitly to avoid infinite recursion
ConfigurableSystem::stop(self);
Ok(())
}
fn pause(&mut self) -> Result<(), String> {
let is_running = *self.is_running.lock();
if !is_running {
return Err("System is not running".to_string());
}
let is_paused = *self.is_paused.lock();
if is_paused {
return Err("System is already paused".to_string());
}
// Set pause state
{
let mut paused = self.is_paused.lock();
*paused = true;
}
// Propagate pause to ALL components (clocks, CPU, memory)
// The system-level is_paused flag handles the thread-level pause,
// but we also propagate to components for component-specific pause behavior
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
clock.pause();
println!("Clock {} paused", name);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
clock.pause();
println!("Clock {} paused", name);
}
// Note: CPU and memory components don't have explicit pause methods,
// but they are effectively paused by the system-level is_paused flag
// which prevents their update() from being called in the thread loop
}
println!("System paused - all components frozen");
Ok(())
}
fn resume(&mut self) -> Result<(), String> {
let is_paused = *self.is_paused.lock();
if !is_paused {
return Err("System is not paused".to_string());
}
// Clear pause state
{
let mut paused = self.is_paused.lock();
*paused = false;
}
// Propagate resume to ALL components (clocks, CPU, memory)
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
clock.resume();
println!("Clock {} resumed", name);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
clock.resume();
println!("Clock {} resumed", name);
}
// Note: CPU and memory components resume automatically via the
// system-level is_paused flag being cleared in the thread loop
}
println!("System resumed - all components running");
Ok(())
}
fn is_running(&self) -> bool {
*self.is_running.lock()
}
fn is_paused(&self) -> bool {
*self.is_paused.lock()
}
fn get_system_state(&self) -> SystemState {
let is_running = *self.is_running.lock();
let is_paused = *self.is_paused.lock();
if !is_running {
SystemState::Stopped
} else if is_paused {
SystemState::Paused
} else {
SystemState::Running
} }
} }
rom_data fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), String> {
if frequency < MIN_CLOCK_FREQUENCY {
return Err(format!(
"Frequency must be at least {} Hz",
MIN_CLOCK_FREQUENCY
));
}
if frequency > MAX_CLOCK_FREQUENCY {
return Err(format!(
"Frequency must not exceed {} Hz",
MAX_CLOCK_FREQUENCY
));
}
// Update our stored frequency
{
let mut freq = self.clock_frequency.lock();
*freq = frequency;
}
// Update all clock components
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
clock.set_frequency(frequency);
println!("Updated clock frequency for {} to {} Hz", name, frequency);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
clock.set_frequency(frequency);
println!("Updated clock frequency for {} to {} Hz", name, frequency);
}
}
Ok(())
}
fn get_clock_frequency(&self) -> f64 {
*self.clock_frequency.lock()
}
fn set_clock_enabled(&mut self, enabled: bool) -> Result<(), String> {
// Update our stored state
{
let mut clk_enabled = self.clock_enabled.lock();
*clk_enabled = enabled;
}
// Update all clock components
for (name, component) in &self.components {
let mut comp = component.lock();
// Try to downcast to GenericClock
if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::generic_clock::GenericClock>()
{
if enabled {
clock.enable();
} else {
clock.disable();
}
println!(
"Clock {} {}",
name,
if enabled { "enabled" } else { "disabled" }
);
}
// Try to downcast to TwoPhaseClock
else if let Some(clock) =
comp.as_any_mut()
.downcast_mut::<crate::components::clock::two_phase_clock::TwoPhaseClock>()
{
if enabled {
clock.enable();
} else {
clock.disable();
}
println!(
"Clock {} {}",
name,
if enabled { "enabled" } else { "disabled" }
);
}
}
Ok(())
}
fn is_clock_enabled(&self) -> bool {
*self.clock_enabled.lock()
} }
} }

View file

@ -6,9 +6,10 @@
#![cfg(test)] #![cfg(test)]
use parking_lot::Mutex;
use rusty_emu::gui::GuiApp; use rusty_emu::gui::GuiApp;
use rusty_emu::system_config::{ConfigurableSystem, SystemFactory}; use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::thread; use std::thread;
use std::time::Duration; 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. /// Note: This function acquires the lock internally. Do NOT call it while holding the lock.
pub fn simulate_gui_updates(system: &Arc<Mutex<ConfigurableSystem>>) { pub fn simulate_gui_updates(system: &Arc<Mutex<ConfigurableSystem>>) {
// Simulate the kind of updates that would happen in the GUI thread // 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 // Simulate some system activity
let _ = system_guard.is_running(); // Access system state let _ = system_guard.is_running(); // Access system state
} }
@ -48,14 +50,19 @@ mod gui_integration_test_utils {
// Spawn a thread that accesses the system // Spawn a thread that accesses the system
let handle = thread::spawn(move || { let handle = thread::spawn(move || {
// Try to acquire the lock - return true if successful (regardless of is_running value) // Try to acquire the lock - return true if successful (parking_lot::lock always succeeds)
system_clone.lock().is_ok() let _guard = system_clone.lock();
true
}); });
// Main thread also accesses the system // Main thread also accesses the system - but release lock before joining to avoid deadlock
let main_thread_result = system.lock().is_ok(); {
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); let spawned_thread_result = handle.join().unwrap_or(false);
// Both threads should be able to acquire the lock without deadlock // Both threads should be able to acquire the lock without deadlock
@ -150,7 +157,8 @@ mod gui_system_integration_tests {
thread::spawn(move || { thread::spawn(move || {
// Simulate GUI update pattern // Simulate GUI update pattern
for _ in 0..10 { for _ in 0..10 {
if let Ok(_system_guard) = system_clone.lock() { let _system_guard = system_clone.lock();
{
// Simulate some processing time // Simulate some processing time
thread::sleep(Duration::from_micros(10)); thread::sleep(Duration::from_micros(10));
} }
@ -196,7 +204,7 @@ mod gui_system_integration_tests {
// Test the polling mechanism that GUI would use // Test the polling mechanism that GUI would use
for i in 0..10 { for i in 0..10 {
{ {
let system_guard = system.lock().unwrap(); let system_guard = system.lock();
let is_running = system_guard.is_running(); let is_running = system_guard.is_running();
// Simulate state updates that would happen in GUI // 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 // 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 // Test that system provides memory information
let _ = system_guard.get_system_info(); // Access system info let _ = system_guard.get_system_info(); // Access system info
} // system_guard is dropped here } // system_guard is dropped here
@ -241,7 +249,7 @@ mod gui_system_integration_tests {
// Test register state structure for GUI integration // 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 // In a real implementation, this would access actual CPU registers
// For now, we test the GUI state structure // For now, we test the GUI state structure
} }
@ -262,7 +270,7 @@ mod gui_system_integration_tests {
// Test component state monitoring for GUI // Test component state monitoring for GUI
{ {
let system_guard = system.lock().unwrap(); let system_guard = system.lock();
let components = system_guard.get_components(); let components = system_guard.get_components();
// Test that components are accessible (GUI would monitor these) // 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 // Hold the lock for a short time to simulate contention
let system_clone = Arc::clone(&system); let system_clone = Arc::clone(&system);
let lock_handle = thread::spawn(move || { 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)); thread::sleep(Duration::from_millis(10));
} }
}); });
// Try to access from main thread (should not deadlock) // 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(); lock_handle.join().unwrap();
// Should be able to access after the lock is released // 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] #[test]
@ -481,7 +490,7 @@ mod gui_real_time_tests {
for i in 0..20 { for i in 0..20 {
{ {
let _system_guard = system.lock().unwrap(); let _system_guard = system.lock();
// Simulate GUI state polling // Simulate GUI state polling
update_count += 1; update_count += 1;
@ -535,7 +544,7 @@ mod gui_real_time_tests {
// For now, we test the data structures are compatible // 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 // Test memory data structure compatibility
} }
@ -559,7 +568,7 @@ mod gui_real_time_tests {
let system = create_test_system(); 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(); let mut gui_state = rusty_emu::gui::state::GuiState::new();

View file

@ -6,9 +6,10 @@
#![cfg(test)] #![cfg(test)]
use eframe::egui; use eframe::egui;
use parking_lot::Mutex;
use rusty_emu::gui::components::*; use rusty_emu::gui::components::*;
use rusty_emu::gui::state::*; use rusty_emu::gui::state::*;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
/// Test utilities for GUI testing /// Test utilities for GUI testing
mod gui_test_utils { mod gui_test_utils {
@ -304,7 +305,8 @@ mod gui_integration_tests {
// For now, we verify the components exist and have valid state // For now, we verify the components exist and have valid state
// Test control panel rendering (would panic if component is invalid) // 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 // Test memory viewer rendering
components.render_memory_viewer(&mut create_test_ui(&ctx), &mut state); components.render_memory_viewer(&mut create_test_ui(&ctx), &mut state);

View file

@ -38,8 +38,8 @@ fn test_intel_4001_state_machine_progression() {
// Initialize clock pins to Low // Initialize clock pins to Low
{ {
let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_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 // Initialize control pins
// Note: RESET is active LOW, so set it to High to ensure component is not in reset // 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 sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock();
let mut reset_guard = reset_pin.lock().unwrap(); let mut reset_guard = reset_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low); sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
cm_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); 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 // Initialize data pins to Low
{ {
let mut d0_guard = d0_pin.lock().unwrap(); let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock().unwrap(); let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock().unwrap(); let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock().unwrap(); let mut d3_guard = d3_pin.lock();
d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d1_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); 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) // Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{ {
let mut sync_guard = sync_pin.lock().unwrap(); let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_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 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) // Set up address high nibble (0x1 for address 0x12)
{ {
let mut d0_guard = d0_pin.lock().unwrap(); let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock().unwrap(); let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock().unwrap(); let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock().unwrap(); let mut d3_guard = d3_pin.lock();
// 0x1 = 0001 (D0=1, D1=0, D2=0, D3=0) // 0x1 = 0001 (D0=1, D1=0, D2=0, D3=0)
d0_guard.set_driver(Some("TEST".to_string()), PinValue::High); d0_guard.set_driver(Some("TEST".to_string()), PinValue::High);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); 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 // 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); 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 // 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); 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) // Set up address low nibble (0x2 for address 0x12)
{ {
let mut d0_guard = d0_pin.lock().unwrap(); let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock().unwrap(); let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock().unwrap(); let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock().unwrap(); let mut d3_guard = d3_pin.lock();
// 0x2 = 0010 (D0=0, D1=1, D2=0, D3=0) // 0x2 = 0010 (D0=0, D1=1, D2=0, D3=0)
d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low); d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::High); 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 // Trigger Φ1 rising edge again - ROM should stay in AddressPhase
// (address nibbles are latched on Φ1 falling edge, not rising) // (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); 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 // 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); 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 // Remove test drivers from data pins to allow ROM to drive bus
{ {
let mut d0_guard = d0_pin.lock().unwrap(); let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock().unwrap(); let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock().unwrap(); let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock().unwrap(); let mut d3_guard = d3_pin.lock();
d0_guard.remove_driver("TEST"); d0_guard.remove_driver("TEST");
d1_guard.remove_driver("TEST"); d1_guard.remove_driver("TEST");
d2_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 { if rom.get_timing_state() == TimingState::WaitLatency {
// Trigger Φ2 rising edge // 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); 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 // Verify data is being driven on the bus
// Data 0xAB has low nibble 0xB = 1011 (D0=1, D1=1, D2=0, D3=1) // 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 // 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!(d0_pin.lock().read(), PinValue::High);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High); assert_eq!(d1_pin.lock().read(), PinValue::High);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low); assert_eq!(d2_pin.lock().read(), PinValue::Low);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::High); assert_eq!(d3_pin.lock().read(), PinValue::High);
// Reset Φ2 to Low // 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); 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()); println!("Final state: {:?}", rom.get_timing_state());
// Verify data bus is tri-stated // Verify data bus is tri-stated
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().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 /// 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 // Initialize clock pins to Low
{ {
let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_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 // 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); 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 // 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); 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 // 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); 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 // 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); 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) // Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{ {
let mut sync_guard = sync_pin.lock().unwrap(); let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_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 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 // 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); 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) // 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); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }

View file

@ -32,8 +32,8 @@ fn create_test_ram() -> Intel4002 {
let phi1_pin = ram.get_pin("PHI1").unwrap(); let phi1_pin = ram.get_pin("PHI1").unwrap();
let phi2_pin = ram.get_pin("PHI2").unwrap(); let phi2_pin = ram.get_pin("PHI2").unwrap();
{ {
let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_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 { for i in 0..4 {
if let Ok(pin) = ram.get_pin(&format!("D{}", i)) { 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 bit_value = (nibble >> i) & 1;
let pin_value = if bit_value == 1 { let pin_value = if bit_value == 1 {
PinValue::High PinValue::High
@ -74,7 +74,7 @@ fn set_data_bus(ram: &Intel4002, value: u8) {
fn clear_data_bus_drivers(ram: &Intel4002) { fn clear_data_bus_drivers(ram: &Intel4002) {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = ram.get_pin(&format!("D{}", i)) { 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"); 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 // Set clock pins
{ {
let mut phi1_guard = phi1_pin.lock().unwrap(); let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock().unwrap(); let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver( phi1_guard.set_driver(
Some("TEST".to_string()), Some("TEST".to_string()),
@ -217,16 +217,16 @@ fn test_ram_read_state_progression() {
// Read data bus value // Read data bus value
let mut data_value = 0; let mut data_value = 0;
if d0_pin.lock().unwrap().read() == PinValue::High { if d0_pin.lock().read() == PinValue::High {
data_value |= 1 << 0; data_value |= 1 << 0;
} }
if d1_pin.lock().unwrap().read() == PinValue::High { if d1_pin.lock().read() == PinValue::High {
data_value |= 1 << 1; data_value |= 1 << 1;
} }
if d2_pin.lock().unwrap().read() == PinValue::High { if d2_pin.lock().read() == PinValue::High {
data_value |= 1 << 2; data_value |= 1 << 2;
} }
if d3_pin.lock().unwrap().read() == PinValue::High { if d3_pin.lock().read() == PinValue::High {
data_value |= 1 << 3; data_value |= 1 << 3;
} }
@ -470,7 +470,7 @@ fn test_reset_functionality() {
// Assert RESET (active low) // Assert RESET (active low)
let reset_pin = ram.get_pin("RESET").unwrap(); 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); reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
wait_for_pin_settlement(); wait_for_pin_settlement();

View file

@ -13,7 +13,7 @@ use rusty_emu::pin::PinValue;
/// Test helper to set pin value with proper driver management /// Test helper to set pin value with proper driver management
fn set_pin_value(component: &Intel4003, pin_name: &str, value: PinValue, driver: &str) { 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(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); 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 /// Test helper to remove pin driver
fn remove_pin_driver(component: &Intel4003, pin_name: &str) { fn remove_pin_driver(component: &Intel4003, pin_name: &str) {
if let Ok(pin) = component.get_pin(pin_name) { 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); pin_guard.set_driver(None, PinValue::HighZ);
} }
} }

View file

@ -3,7 +3,8 @@
//! This test verifies that the Intel 4004 CPU state machine progresses correctly //! This test verifies that the Intel 4004 CPU state machine progresses correctly
//! through all phases: Idle → AddressPhase → WaitLatency → DriveData → Idle //! 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::thread;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@ -46,16 +47,8 @@ impl CpuTestHelper {
let phi2 = Arc::new(Mutex::new(Pin::new("PHI2".to_string()))); let phi2 = Arc::new(Mutex::new(Pin::new("PHI2".to_string())));
// Connect CPU to test clock // Connect CPU to test clock
cpu.get_pin("PHI1") cpu.get_pin("PHI1").unwrap().lock().connect_to(phi1.clone());
.unwrap() cpu.get_pin("PHI2").unwrap().lock().connect_to(phi2.clone());
.lock()
.unwrap()
.connect_to(phi1.clone());
cpu.get_pin("PHI2")
.unwrap()
.lock()
.unwrap()
.connect_to(phi2.clone());
CpuTestHelper { CpuTestHelper {
cpu, cpu,
@ -90,11 +83,9 @@ impl CpuTestHelper {
// Phase 1: Φ1 high, Φ2 low // Phase 1: Φ1 high, Φ2 low
self.phi1 self.phi1
.lock() .lock()
.unwrap()
.set_driver(Some("clock".to_string()), PinValue::High); .set_driver(Some("clock".to_string()), PinValue::High);
self.phi2 self.phi2
.lock() .lock()
.unwrap()
.set_driver(Some("clock".to_string()), PinValue::Low); .set_driver(Some("clock".to_string()), PinValue::Low);
thread::sleep(Duration::from_millis(1)); thread::sleep(Duration::from_millis(1));
@ -104,11 +95,9 @@ impl CpuTestHelper {
// Phase 2: Φ1 low, Φ2 high // Phase 2: Φ1 low, Φ2 high
self.phi1 self.phi1
.lock() .lock()
.unwrap()
.set_driver(Some("clock".to_string()), PinValue::Low); .set_driver(Some("clock".to_string()), PinValue::Low);
self.phi2 self.phi2
.lock() .lock()
.unwrap()
.set_driver(Some("clock".to_string()), PinValue::High); .set_driver(Some("clock".to_string()), PinValue::High);
thread::sleep(Duration::from_millis(1)); thread::sleep(Duration::from_millis(1));
@ -117,11 +106,9 @@ impl CpuTestHelper {
// Phase 3: Both low // Phase 3: Both low
self.phi1 self.phi1
.lock() .lock()
.unwrap()
.set_driver(Some("clock".to_string()), PinValue::Low); .set_driver(Some("clock".to_string()), PinValue::Low);
self.phi2 self.phi2
.lock() .lock()
.unwrap()
.set_driver(Some("clock".to_string()), PinValue::Low); .set_driver(Some("clock".to_string()), PinValue::Low);
thread::sleep(Duration::from_millis(1)); thread::sleep(Duration::from_millis(1));
@ -137,25 +124,15 @@ impl CpuTestHelper {
} }
fn set_control_pins(&self, sync: bool, cm: bool, reset: bool) { fn set_control_pins(&self, sync: bool, cm: bool, reset: bool) {
self.cpu self.cpu.get_pin("SYNC").unwrap().lock().set_driver(
.get_pin("SYNC")
.unwrap()
.lock()
.unwrap()
.set_driver(
Some("test".to_string()), Some("test".to_string()),
if sync { PinValue::High } else { PinValue::Low }, if sync { PinValue::High } else { PinValue::Low },
); );
self.cpu.get_pin("CM").unwrap().lock().unwrap().set_driver( self.cpu.get_pin("CM").unwrap().lock().set_driver(
Some("test".to_string()), Some("test".to_string()),
if cm { PinValue::High } else { PinValue::Low }, if cm { PinValue::High } else { PinValue::Low },
); );
self.cpu self.cpu.get_pin("RESET").unwrap().lock().set_driver(
.get_pin("RESET")
.unwrap()
.lock()
.unwrap()
.set_driver(
Some("test".to_string()), Some("test".to_string()),
if reset { PinValue::High } else { PinValue::Low }, if reset { PinValue::High } else { PinValue::Low },
); );
@ -168,7 +145,6 @@ impl CpuTestHelper {
.get_pin(&format!("D{}", i)) .get_pin(&format!("D{}", i))
.unwrap() .unwrap()
.lock() .lock()
.unwrap()
.set_driver( .set_driver(
Some("test".to_string()), Some("test".to_string()),
if bit_value == 1 { if bit_value == 1 {
@ -184,25 +160,23 @@ impl CpuTestHelper {
let mut data = 0; let mut data = 0;
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.cpu.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.cpu.get_pin(&format!("D{}", i)) {
if let Ok(pin_guard) = pin.lock() { let pin_guard = pin.lock();
if pin_guard.read() == PinValue::High { if pin_guard.read() == PinValue::High {
data |= 1 << i; data |= 1 << i;
} }
} }
} }
}
data & 0x0F data & 0x0F
} }
fn tri_state_data_bus(&self) { fn tri_state_data_bus(&self) {
for i in 0..4 { for i in 0..4 {
if let Ok(pin) = self.cpu.get_pin(&format!("D{}", i)) { if let Ok(pin) = self.cpu.get_pin(&format!("D{}", i)) {
if let Ok(mut pin_guard) = pin.lock() { let mut pin_guard = pin.lock();
pin_guard.set_driver(Some("test".to_string()), PinValue::HighZ); pin_guard.set_driver(Some("test".to_string()), PinValue::HighZ);
} }
} }
} }
}
} }
/// Wrapper function to run tests with timeout /// Wrapper function to run tests with timeout
@ -294,7 +268,7 @@ fn test_intel_4004_state_machine_progression() {
for i in 0..4 { for i in 0..4 {
let pin_name = format!("D{}", i); let pin_name = format!("D{}", i);
if let Ok(pin) = helper.cpu.get_pin(&pin_name) { 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); pin_guard.set_driver(Some("test".to_string()), PinValue::High);
} }
} }
@ -352,7 +326,6 @@ fn test_intel_4004_reset_functionality() {
.get_pin("RESET") .get_pin("RESET")
.unwrap() .unwrap()
.lock() .lock()
.unwrap()
.set_driver(Some("test".to_string()), PinValue::High); .set_driver(Some("test".to_string()), PinValue::High);
helper.cpu.update(); helper.cpu.update();
@ -362,7 +335,6 @@ fn test_intel_4004_reset_functionality() {
.get_pin("RESET") .get_pin("RESET")
.unwrap() .unwrap()
.lock() .lock()
.unwrap()
.set_driver(Some("test".to_string()), PinValue::Low); .set_driver(Some("test".to_string()), PinValue::Low);
// Process the reset signal for multiple cycles to ensure it's applied // 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") .get_pin("RESET")
.unwrap() .unwrap()
.lock() .lock()
.unwrap()
.set_driver(Some("test".to_string()), PinValue::High); .set_driver(Some("test".to_string()), PinValue::High);
helper.cpu.update(); helper.cpu.update();
thread::sleep(Duration::from_millis(1)); thread::sleep(Duration::from_millis(1));

View file

@ -1,10 +1,11 @@
use parking_lot::Mutex;
use rusty_emu::component::{BaseComponent, Component}; use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::components::common::intel_400x::*; use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode}; use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode};
use rusty_emu::pin::{Pin, PinValue}; use rusty_emu::pin::{Pin, PinValue};
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex}; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
// Mock component for testing trait implementations // Mock component for testing trait implementations
@ -47,9 +48,17 @@ impl Component for MockComponent {
false false
} }
fn set_running(&mut self, _running: bool) {
// No-op for mock
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
impl Intel400xAddressHandling for MockComponent { impl Intel400xAddressHandling for MockComponent {
@ -354,57 +363,29 @@ mod tests {
let d3_pin = rom.get_pin("D3").unwrap(); let d3_pin = rom.get_pin("D3").unwrap();
// Set settlement time to 0 for immediate testing // Set settlement time to 0 for immediate testing
sync_pin sync_pin.lock().set_settlement_time(Duration::from_nanos(0));
.lock() cm_pin.lock().set_settlement_time(Duration::from_nanos(0));
.unwrap() ci_pin.lock().set_settlement_time(Duration::from_nanos(0));
.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));
reset_pin reset_pin
.lock() .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)); .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 // Initially, all data pins should be HighZ
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
// Test reset (RESET is active LOW) // Test reset (RESET is active LOW)
println!("=== Testing Reset ==="); 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 reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset
} }
rom.update(); rom.update();
@ -413,7 +394,7 @@ mod tests {
// Release reset (set HIGH for normal operation) // 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 reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset
} }
rom.update(); rom.update();
@ -466,11 +447,11 @@ mod tests {
// Pin state snapshots // Pin state snapshots
println!( println!(
"Pin states: SYNC: {:?}, CM: {:?}, CI: {:?}, PHI1: {:?}, PHI2: {:?}", "Pin states: SYNC: {:?}, CM: {:?}, CI: {:?}, PHI1: {:?}, PHI2: {:?}",
sync_pin.lock().unwrap().read(), sync_pin.lock().read(),
cm_pin.lock().unwrap().read(), cm_pin.lock().read(),
ci_pin.lock().unwrap().read(), ci_pin.lock().read(),
phi1_pin.lock().unwrap().read(), phi1_pin.lock().read(),
phi2_pin.lock().unwrap().read() phi2_pin.lock().read()
); );
println!("Test completed successfully!"); println!("Test completed successfully!");
@ -493,22 +474,22 @@ mod tests {
// Reset to known state first (RESET is active LOW) // Reset to known state first (RESET is active LOW)
{ {
let reset_pin = rom.get_pin("RESET").unwrap(); 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) reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Assert reset (active LOW)
} }
rom.update(); rom.update();
{ {
let reset_pin = rom.get_pin("RESET").unwrap(); 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) reset_guard.set_driver(Some("TEST".to_string()), PinValue::High); // Release reset (HIGH = normal operation)
} }
rom.update(); rom.update();
// Set SYNC=1, CM=1, CI=0 for memory read // Set SYNC=1, CM=1, CI=0 for memory read
{ {
let mut sync_guard = sync_pin.lock().unwrap(); let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_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); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -517,14 +498,14 @@ mod tests {
// Ensure PHI2 is low initially // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
rom.update(); rom.update();
// Ensure PHI1 is low initially // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
rom.update(); rom.update();
@ -534,7 +515,7 @@ mod tests {
// PHI1 low to high: Address phase for high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
rom.update(); rom.update();
@ -546,14 +527,14 @@ mod tests {
// PHI1 high to low: Complete address phase for high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
rom.update(); rom.update();
// PHI1 low to high again: Address phase for low nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
rom.update(); rom.update();
@ -564,7 +545,7 @@ mod tests {
// PHI1 high to low: Complete address phase // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
rom.update(); rom.update();
@ -589,14 +570,14 @@ mod tests {
// Release data bus from test control to allow ROM to drive it // Release data bus from test control to allow ROM to drive it
for i in 0..4 { for i in 0..4 {
let pin = rom.get_pin(&format!("D{}", i)).unwrap(); 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); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ);
} }
rom.update(); rom.update();
// PHI2 low to high: Data phase // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
rom.update(); rom.update();
@ -612,17 +593,17 @@ mod tests {
// PHI2 high to low: Cleanup // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
rom.update(); rom.update();
assert_eq!(rom.get_timing_state(), TimingState::Idle); assert_eq!(rom.get_timing_state(), TimingState::Idle);
// Tri-state pins // Tri-state pins
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ); assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
} }
#[test] #[test]
@ -644,46 +625,18 @@ mod tests {
let d3_pin = chip.get_pin("D3").unwrap(); let d3_pin = chip.get_pin("D3").unwrap();
// Set settlement time to 0 for immediate testing // Set settlement time to 0 for immediate testing
sync_pin sync_pin.lock().set_settlement_time(Duration::from_nanos(0));
.lock() cm_pin.lock().set_settlement_time(Duration::from_nanos(0));
.unwrap() ci_pin.lock().set_settlement_time(Duration::from_nanos(0));
.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));
reset_pin reset_pin
.lock() .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)); .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 // 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 // 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 // Set settlement times to 0
test_sync test_sync
.lock() .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)); .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 test_phi1
.lock() .lock()
.unwrap()
.set_settlement_time(Duration::from_nanos(0)); .set_settlement_time(Duration::from_nanos(0));
test_phi2 test_phi2
.lock() .lock()
.unwrap()
.set_settlement_time(Duration::from_nanos(0)); .set_settlement_time(Duration::from_nanos(0));
// Initialize PHI2 to Low to ensure proper edge detection // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
test_chip.update(); test_chip.update();
@ -890,9 +834,9 @@ mod tests {
// Manually step through the state machine to verify timing // Manually step through the state machine to verify timing
// Set control pins for memory read // Set control pins for memory read
{ {
let mut sync_guard = test_sync.lock().unwrap(); let mut sync_guard = test_sync.lock();
let mut cm_guard = test_cm.lock().unwrap(); let mut cm_guard = test_cm.lock();
let mut ci_guard = test_ci.lock().unwrap(); let mut ci_guard = test_ci.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_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); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -902,14 +846,14 @@ mod tests {
// Ensure PHI1 is low initially // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
test_chip.update(); test_chip.update();
// Start address phase - first PHI1 cycle for high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
test_chip.update(); test_chip.update();
@ -921,7 +865,7 @@ mod tests {
// PHI1 falling edge - latch high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
test_chip.update(); test_chip.update();
@ -929,7 +873,7 @@ mod tests {
// Second PHI1 cycle for low nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
test_chip.update(); test_chip.update();
@ -940,7 +884,7 @@ mod tests {
// PHI1 falling edge - latch low nibble and complete address // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
test_chip.update(); test_chip.update();
@ -951,7 +895,7 @@ mod tests {
// PHI2 high: Data phase // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
test_chip.update(); test_chip.update();
@ -963,7 +907,7 @@ mod tests {
// Complete cycle // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
test_chip.update(); test_chip.update();
@ -984,37 +928,28 @@ mod tests {
// Set settlement times to 0 // Set settlement times to 0
fast_sync fast_sync
.lock() .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)); .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 fast_phi1
.lock() .lock()
.unwrap()
.set_settlement_time(Duration::from_nanos(0)); .set_settlement_time(Duration::from_nanos(0));
fast_phi2 fast_phi2
.lock() .lock()
.unwrap()
.set_settlement_time(Duration::from_nanos(0)); .set_settlement_time(Duration::from_nanos(0));
// Initialize PHI2 to Low to ensure proper edge detection // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
fast_chip.update(); fast_chip.update();
// Set up for memory read // Set up for memory read
{ {
let mut sync_guard = fast_sync.lock().unwrap(); let mut sync_guard = fast_sync.lock();
let mut cm_guard = fast_cm.lock().unwrap(); let mut cm_guard = fast_cm.lock();
let mut ci_guard = fast_ci.lock().unwrap(); let mut ci_guard = fast_ci.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_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); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -1023,14 +958,14 @@ mod tests {
// Ensure PHI1 is low initially // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
fast_chip.update(); fast_chip.update();
// First PHI1 cycle - latch high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
fast_chip.update(); fast_chip.update();
@ -1041,14 +976,14 @@ mod tests {
// PHI1 falling edge - latch high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
fast_chip.update(); fast_chip.update();
// Second PHI1 cycle - latch low nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
fast_chip.update(); fast_chip.update();
@ -1059,7 +994,7 @@ mod tests {
// PHI1 falling edge - latch low nibble and complete address // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
fast_chip.update(); fast_chip.update();
@ -1075,14 +1010,14 @@ mod tests {
// Release data bus from test control to allow ROM to drive it // Release data bus from test control to allow ROM to drive it
for i in 0..4 { for i in 0..4 {
let pin = fast_chip.get_pin(&format!("D{}", i)).unwrap(); 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); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ);
} }
fast_chip.update(); fast_chip.update();
// Try to read data - PHI2 rising edge triggers data driving if latency elapsed // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
fast_chip.update(); fast_chip.update();
@ -1124,9 +1059,9 @@ mod tests {
// Set control pins for memory read // Set control pins for memory read
{ {
let mut sync_guard = sync_pin.lock().unwrap(); let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock().unwrap(); let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock().unwrap(); let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High); sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_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); ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -1135,21 +1070,21 @@ mod tests {
// Ensure PHI2 is low initially // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
chip.update(); chip.update();
// Ensure PHI1 is low initially // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
chip.update(); chip.update();
// PHI1 low to high: Address phase for high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
chip.update(); chip.update();
@ -1160,14 +1095,14 @@ mod tests {
// PHI1 high to low: Complete address phase for high nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
chip.update(); chip.update();
// PHI1 low to high again: Address phase for low nibble // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
chip.update(); chip.update();
@ -1178,7 +1113,7 @@ mod tests {
// PHI1 high to low: Complete address phase // 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); phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
chip.update(); chip.update();
@ -1208,14 +1143,14 @@ mod tests {
// Release data bus from test control to allow ROM to drive it // Release data bus from test control to allow ROM to drive it
for i in 0..4 { for i in 0..4 {
let pin = chip.get_pin(&format!("D{}", i)).unwrap(); 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); pin_guard.set_driver(Some("TEST".to_string()), PinValue::HighZ);
} }
chip.update(); chip.update();
// PHI2 low to high: Data phase // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
} }
chip.update(); chip.update();
@ -1230,7 +1165,7 @@ mod tests {
// PHI2 high to low: Cleanup // 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); phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
} }
chip.update(); chip.update();