feat: implement virtual clock timing architecture (Phases 5-8)

Introduces a VirtualClock-based timing system replacing wall-clock
Instant-based timing for hardware-accurate simulation.

New modules:
- virtual_clock.rs: Shared virtual time source with condvar sync
- real_time_governor.rs: Thread that advances virtual time
- component_timing.rs: Per-component timing constraints

Key changes:
- Component trait: added update_at_time(), timing(), set_virtual_clock_optional()
- All Intel 400x components migrated to virtual-time-aware operation
- ConfigurableSystem: start() returns Result, spawns governor thread
- GUI: timing controls (speed slider, mode selector, step button)
- Console: speed/mode/step/time commands
- Backward compatible: legacy fallback for components without VirtualClock

Reviewed-by: code review found 2 warnings (clock resume() dead code,
reset() safety docs) — both fixed.
This commit is contained in:
Jan-Peter von Hunnius 2026-05-14 21:00:22 +02:00
parent add7ace674
commit 08f53d68fe
28 changed files with 7186 additions and 270 deletions

View file

@ -6,7 +6,6 @@ use rusty_emu::pin::{Pin, PinValue};
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
// Mock component for testing trait implementations
#[derive(Debug)]
@ -247,7 +246,7 @@ mod tests {
let mut high_nibble: Option<u8> = None;
let mut low_nibble: Option<u8> = None;
let mut full_address_ready = false;
let mut address_latch_time: Option<Instant> = None;
let mut address_latch_time: Option<Duration> = None;
let access_time = Duration::from_nanos(100);
// Mock address handler for testing the algorithm
@ -275,6 +274,7 @@ mod tests {
&mut full_address_ready,
&mut address_latch_time,
access_time,
Duration::from_nanos(100), // virtual_time
);
assert_eq!(high_nibble, Some(0x12));
@ -282,7 +282,7 @@ mod tests {
assert_eq!(full_address_ready, false);
assert_eq!(address_latch_time, None);
// Second cycle: low nibble
// Second cycle: low nibble — address_latch_time is now set internally
handler.handle_address_latching(
0x34,
&mut high_nibble,
@ -290,12 +290,14 @@ mod tests {
&mut full_address_ready,
&mut address_latch_time,
access_time,
Duration::from_nanos(200), // virtual_time when address is fully latched
);
assert_eq!(high_nibble, None); // Should be cleared
assert_eq!(low_nibble, None); // Should be cleared
assert_eq!(full_address_ready, true);
assert!(address_latch_time.is_some());
// address_latch_time is now set by handle_address_latching() internally
assert_eq!(address_latch_time, Some(Duration::from_nanos(200)));
// Verify the assembled address
let assembled = handler.assemble_full_address(Some(0x02), Some(0x04));
@ -311,26 +313,37 @@ mod tests {
Intel400xAddressHandling::handle_latency_wait(
&MockComponent::new(),
&None,
access_time
access_time,
Duration::from_nanos(200),
),
false
);
// Test with recent latch time - need to ensure enough time has elapsed
// Create the latch time, then sleep to guarantee elapsed >= access_time
let recent_time = Some(Instant::now());
thread::sleep(Duration::from_micros(1));
// Test with latch time and sufficient virtual time elapsed
let latch_time = Some(Duration::from_nanos(50));
let virtual_time = Duration::from_nanos(200);
assert_eq!(
Intel400xAddressHandling::handle_latency_wait(
&MockComponent::new(),
&recent_time,
access_time
&latch_time,
access_time,
virtual_time,
),
true
);
// Note: Testing with elapsed time would require sleeping or mocking time
// This demonstrates the logic structure is correct
// Test with latch time but insufficient virtual time elapsed
let latch_time = Some(Duration::from_nanos(150));
let virtual_time = Duration::from_nanos(200);
assert_eq!(
Intel400xAddressHandling::handle_latency_wait(
&MockComponent::new(),
&latch_time,
access_time,
virtual_time,
),
false
);
}
#[test]