feat: migrate to parking_lot mutex and add system control architecture

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

View file

@ -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();