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.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "ab_glyph"
@ -2503,6 +2503,7 @@ dependencies = [
"eframe",
"egui",
"gtk",
"parking_lot",
"ratatui",
"raw-window-handle 0.5.2",
"rfd",

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -38,8 +38,8 @@ fn test_intel_4001_state_machine_progression() {
// Initialize clock pins to Low
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi2_guard = phi2_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -47,10 +47,10 @@ fn test_intel_4001_state_machine_progression() {
// Initialize control pins
// Note: RESET is active LOW, so set it to High to ensure component is not in reset
{
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut reset_guard = reset_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
let mut reset_guard = reset_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -59,10 +59,10 @@ fn test_intel_4001_state_machine_progression() {
// Initialize data pins to Low
{
let mut d0_guard = d0_pin.lock().unwrap();
let mut d1_guard = d1_pin.lock().unwrap();
let mut d2_guard = d2_pin.lock().unwrap();
let mut d3_guard = d3_pin.lock().unwrap();
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -78,9 +78,9 @@ fn test_intel_4001_state_machine_progression() {
// Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access
@ -88,10 +88,10 @@ fn test_intel_4001_state_machine_progression() {
// Set up address high nibble (0x1 for address 0x12)
{
let mut d0_guard = d0_pin.lock().unwrap();
let mut d1_guard = d1_pin.lock().unwrap();
let mut d2_guard = d2_pin.lock().unwrap();
let mut d3_guard = d3_pin.lock().unwrap();
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
// 0x1 = 0001 (D0=1, D1=0, D2=0, D3=0)
d0_guard.set_driver(Some("TEST".to_string()), PinValue::High);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
@ -104,7 +104,7 @@ fn test_intel_4001_state_machine_progression() {
// Trigger Φ1 rising edge to start memory operation and latch high nibble
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -120,7 +120,7 @@ fn test_intel_4001_state_machine_progression() {
// Reset Φ1 to Low
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -129,10 +129,10 @@ fn test_intel_4001_state_machine_progression() {
// Set up address low nibble (0x2 for address 0x12)
{
let mut d0_guard = d0_pin.lock().unwrap();
let mut d1_guard = d1_pin.lock().unwrap();
let mut d2_guard = d2_pin.lock().unwrap();
let mut d3_guard = d3_pin.lock().unwrap();
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
// 0x2 = 0010 (D0=0, D1=1, D2=0, D3=0)
d0_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
d1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
@ -146,7 +146,7 @@ fn test_intel_4001_state_machine_progression() {
// Trigger Φ1 rising edge again - ROM should stay in AddressPhase
// (address nibbles are latched on Φ1 falling edge, not rising)
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -166,7 +166,7 @@ fn test_intel_4001_state_machine_progression() {
// Reset Φ1 to Low to trigger falling edge - this is when low nibble is latched
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -189,10 +189,10 @@ fn test_intel_4001_state_machine_progression() {
// Remove test drivers from data pins to allow ROM to drive bus
{
let mut d0_guard = d0_pin.lock().unwrap();
let mut d1_guard = d1_pin.lock().unwrap();
let mut d2_guard = d2_pin.lock().unwrap();
let mut d3_guard = d3_pin.lock().unwrap();
let mut d0_guard = d0_pin.lock();
let mut d1_guard = d1_pin.lock();
let mut d2_guard = d2_pin.lock();
let mut d3_guard = d3_pin.lock();
d0_guard.remove_driver("TEST");
d1_guard.remove_driver("TEST");
d2_guard.remove_driver("TEST");
@ -203,7 +203,7 @@ fn test_intel_4001_state_machine_progression() {
if rom.get_timing_state() == TimingState::WaitLatency {
// Trigger Φ2 rising edge
{
let mut phi2_guard = phi2_pin.lock().unwrap();
let mut phi2_guard = phi2_pin.lock();
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -221,14 +221,14 @@ fn test_intel_4001_state_machine_progression() {
// Verify data is being driven on the bus
// Data 0xAB has low nibble 0xB = 1011 (D0=1, D1=1, D2=0, D3=1)
// Note: Due to bit ordering, D0 should be High, D1 should be High, D2 should be Low, D3 should be High
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::High);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::High);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::Low);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::High);
assert_eq!(d0_pin.lock().read(), PinValue::High);
assert_eq!(d1_pin.lock().read(), PinValue::High);
assert_eq!(d2_pin.lock().read(), PinValue::Low);
assert_eq!(d3_pin.lock().read(), PinValue::High);
// Reset Φ2 to Low
{
let mut phi2_guard = phi2_pin.lock().unwrap();
let mut phi2_guard = phi2_pin.lock();
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -240,10 +240,10 @@ fn test_intel_4001_state_machine_progression() {
println!("Final state: {:?}", rom.get_timing_state());
// Verify data bus is tri-stated
assert_eq!(d0_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().unwrap().read(), PinValue::HighZ);
assert_eq!(d0_pin.lock().read(), PinValue::HighZ);
assert_eq!(d1_pin.lock().read(), PinValue::HighZ);
assert_eq!(d2_pin.lock().read(), PinValue::HighZ);
assert_eq!(d3_pin.lock().read(), PinValue::HighZ);
}
/// Test to verify that unified state machine doesn't progress without proper control signals
@ -261,8 +261,8 @@ fn test_intel_4001_state_machine_no_progression_without_signals() {
// Initialize clock pins to Low
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi2_guard = phi2_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
let mut phi2_guard = phi2_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -275,7 +275,7 @@ fn test_intel_4001_state_machine_no_progression_without_signals() {
// Trigger clock edges without setting up control signals
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -287,7 +287,7 @@ fn test_intel_4001_state_machine_no_progression_without_signals() {
// Reset Φ1 to Low
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -296,7 +296,7 @@ fn test_intel_4001_state_machine_no_progression_without_signals() {
// Trigger Φ2 rising edge
{
let mut phi2_guard = phi2_pin.lock().unwrap();
let mut phi2_guard = phi2_pin.lock();
phi2_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -325,7 +325,7 @@ fn test_intel_4001_state_machine_reset() {
// Initialize clock pins to Low
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}
@ -337,9 +337,9 @@ fn test_intel_4001_state_machine_reset() {
// Set up memory read operation: SYNC=1, CM=1, CI=0 (memory access)
{
let mut sync_guard = sync_pin.lock().unwrap();
let mut cm_guard = cm_pin.lock().unwrap();
let mut ci_guard = ci_pin.lock().unwrap();
let mut sync_guard = sync_pin.lock();
let mut cm_guard = cm_pin.lock();
let mut ci_guard = ci_pin.lock();
sync_guard.set_driver(Some("TEST".to_string()), PinValue::High);
cm_guard.set_driver(Some("TEST".to_string()), PinValue::High);
ci_guard.set_driver(Some("TEST".to_string()), PinValue::Low); // Memory access
@ -347,7 +347,7 @@ fn test_intel_4001_state_machine_reset() {
// Trigger Φ1 rising edge to start memory operation
{
let mut phi1_guard = phi1_pin.lock().unwrap();
let mut phi1_guard = phi1_pin.lock();
phi1_guard.set_driver(Some("TEST".to_string()), PinValue::High);
}
@ -359,7 +359,7 @@ fn test_intel_4001_state_machine_reset() {
// Assert RESET (active LOW - set to Low to trigger reset)
{
let mut reset_guard = reset_pin.lock().unwrap();
let mut reset_guard = reset_pin.lock();
reset_guard.set_driver(Some("TEST".to_string()), PinValue::Low);
}

View file

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

View file

@ -13,7 +13,7 @@ use rusty_emu::pin::PinValue;
/// Test helper to set pin value with proper driver management
fn set_pin_value(component: &Intel4003, pin_name: &str, value: PinValue, driver: &str) {
if let Ok(pin) = component.get_pin(pin_name) {
if let Ok(mut pin_guard) = pin.lock() {
if let Some(mut pin_guard) = pin.try_lock() {
pin_guard.set_driver(Some(driver.to_string()), value);
}
}
@ -22,7 +22,7 @@ fn set_pin_value(component: &Intel4003, pin_name: &str, value: PinValue, driver:
/// Test helper to remove pin driver
fn remove_pin_driver(component: &Intel4003, pin_name: &str) {
if let Ok(pin) = component.get_pin(pin_name) {
if let Ok(mut pin_guard) = pin.lock() {
if let Some(mut pin_guard) = pin.try_lock() {
pin_guard.set_driver(None, PinValue::HighZ);
}
}

View file

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

View file

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