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

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