//! # GUI State Management //! //! This module manages the state of the GUI application, including system status, //! component states, and user interface state. It provides thread-safe access //! to emulator state for real-time GUI updates. use crate::system_config::ConfigurableSystem; use crate::real_time_governor::RealTimeGovernor; use crate::virtual_clock::SimulationMode; use parking_lot::Mutex; use std::sync::Arc; use std::time::Duration; /// GUI state structure containing all UI-relevant data #[derive(Debug, Clone)] pub struct GuiState { /// Whether a system is currently loaded pub system_loaded: bool, /// Whether the system is currently running pub system_running: bool, /// Current cycle count pub cycle_count: u64, /// System information pub system_info: Option, /// Component states pub component_states: ComponentStates, /// Memory state pub memory_state: MemoryState, /// ROM state pub rom_state: RomState, /// CPU register state pub register_state: RegisterState, /// Last error message pub last_error: Option, /// Timing/speed control state pub timing_state: SimulationTimingState, /// Speed snapshot for measuring actual simulation speed. /// Uses RealTimeGovernor::SpeedSnapshot which wraps (Instant, u64) properly. speed_snap_prev: Option, } /// Timing and speed control state for the simulation. /// /// Named `SimulationTimingState` to avoid confusion with the hardware /// [`TimingState`](crate::components::common::intel_400x::TimingState) enum /// that represents component-level timing states (e.g., WaitLatency, DriveData). #[derive(Debug, Clone)] pub struct SimulationTimingState { /// Current virtual time in nanoseconds pub virtual_time_ns: u64, /// Total ticks elapsed pub tick_count: u64, /// Current target speed multiplier (from VirtualClock) pub target_speed: f64, /// Actual measured speed ratio (from RealTimeGovernor snapshots) /// 1.0 = real-time, 0.5 = half speed, 2.0 = double speed pub actual_speed: f64, /// Current simulation mode as string: "RealTime", "MaxSpeed", "SingleStep" pub mode: String, /// Whether a step is pending (SingleStep mode) pub step_pending: bool, /// Whether the simulation is paused pub paused: bool, } impl Default for SimulationTimingState { fn default() -> Self { Self { virtual_time_ns: 0, tick_count: 0, target_speed: 1.0, actual_speed: 0.0, mode: "RealTime".to_string(), step_pending: false, paused: false, } } } /// Format a virtual time value in nanoseconds as a human-readable string. /// /// Automatically selects the most appropriate unit (ns, µs, ms, s). /// This is the single source of truth for virtual time formatting, /// used by both the GUI and console interfaces. pub fn format_virtual_time_ns(ns: u64) -> String { if ns == 0 { return "0 ns".to_string(); } if ns < 1_000 { format!("{} ns", ns) } else if ns < 1_000_000 { format!("{:.2} µs", ns as f64 / 1_000.0) } else if ns < 1_000_000_000 { format!("{:.2} ms", ns as f64 / 1_000_000.0) } else { format!("{:.3} s", ns as f64 / 1_000_000_000.0) } } impl SimulationTimingState { /// Format virtual time as a human-readable string. /// /// Automatically selects the most appropriate unit (ns, µs, ms, s). /// Delegates to [`format_virtual_time_ns`] for the actual formatting. pub fn format_virtual_time(&self) -> String { format_virtual_time_ns(self.virtual_time_ns) } /// Format the speed multiplier for display. pub fn format_speed(&self) -> String { if self.target_speed >= 100.0 { format!("{:.0}×", self.target_speed) } else if self.target_speed >= 1.0 { format!("{:.1}×", self.target_speed) } else { format!("{:.3}×", self.target_speed) } } /// Format the actual measured speed for display. pub fn format_actual_speed(&self) -> String { let speed = self.actual_speed; if speed <= 0.0 { "—".to_string() } else if speed >= 100.0 { format!("{:.0}×", speed) } else if speed >= 1.0 { format!("{:.1}×", speed) } else { format!("{:.3}×", speed) } } } /// System information for display #[derive(Debug, Clone)] pub struct SystemInfo { pub name: String, pub description: String, pub component_count: usize, pub cpu_speed: f64, pub rom_size: usize, pub ram_size: usize, } /// Component states for monitoring #[derive(Debug, Clone)] pub struct ComponentStates { pub cpu_running: bool, pub ram_running: bool, pub rom_running: bool, pub clock_running: bool, } /// Memory state for display #[derive(Debug, Clone)] pub struct MemoryState { pub ram_contents: Vec<[u8; 4]>, // 4 banks of RAM data pub selected_bank: usize, pub selected_address: usize, /// Display mode: true for hexadecimal, false for decimal pub show_hex: bool, } /// ROM state for display #[derive(Debug, Clone)] pub struct RomState { pub rom_contents: Vec, pub selected_address: usize, pub chip_name: String, /// Display mode: true for hexadecimal, false for decimal pub show_hex: bool, } /// CPU register state for display #[derive(Debug, Clone)] pub struct RegisterState { pub accumulator: u8, pub carry_flag: bool, pub program_counter: u16, pub index_registers: [u8; 16], // 16 index registers (R0-R15) for Intel 4004 pub stack_pointer: u8, /// Currently selected index register for detailed view (0-15) pub selected_register: usize, } impl GuiState { /// Create a new GUI state instance pub fn new() -> Self { Self { system_loaded: false, system_running: false, cycle_count: 0, system_info: None, component_states: ComponentStates { cpu_running: false, ram_running: false, rom_running: false, clock_running: false, }, memory_state: MemoryState { ram_contents: vec![[0; 4]; 4], // Initialize 4 banks with 4 bytes each selected_bank: 0, selected_address: 0, show_hex: true, }, rom_state: RomState { rom_contents: vec![0; 256], // Initialize with 256 bytes selected_address: 0, chip_name: String::new(), show_hex: true, }, register_state: RegisterState { accumulator: 0, carry_flag: false, program_counter: 0, index_registers: [0; 16], stack_pointer: 0, selected_register: 0, }, last_error: None, timing_state: SimulationTimingState::default(), speed_snap_prev: None, } } /// Update state from the current system pub fn update_from_system(&mut self, system: &Arc>) { 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 component states self.update_component_states(&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 ROM state from real data self.update_rom_state(&system_guard); // Update timing state from virtual clock self.update_timing_state(&system_guard); // Update cycle count (simulate for now) if self.system_running { self.cycle_count += 1; } } /// Update component running states /// 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 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.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.lock().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 fn update_register_state(&mut self, system: &ConfigurableSystem) { if let Some(cpu_data) = system.get_cpu_register_data() { self.register_state.accumulator = cpu_data.accumulator; self.register_state.carry_flag = cpu_data.carry; self.register_state.program_counter = cpu_data.program_counter; self.register_state.stack_pointer = cpu_data.stack_pointer; self.register_state .index_registers .copy_from_slice(&cpu_data.index_registers); } } /// Update RAM state from real component data fn update_ram_state(&mut self, system: &ConfigurableSystem) { let ram_data = system.get_ram_data(4); // Get up to 4 rows per bank if !ram_data.is_empty() { // Take the first RAM chip's data let (_, banks) = &ram_data[0]; // Convert bank data to our display format self.memory_state.ram_contents.clear(); for bank in banks { let mut row = [0u8; 4]; for (i, nibble) in bank.iter().take(4).enumerate() { row[i] = *nibble; } self.memory_state.ram_contents.push(row); } } } /// Update ROM state from real component data fn update_rom_state(&mut self, system: &ConfigurableSystem) { let rom_data = system.get_rom_data(256); // Get up to 256 bytes if !rom_data.is_empty() { let (chip_name, data) = &rom_data[0]; self.rom_state.chip_name = chip_name.clone(); self.rom_state.rom_contents = data.clone(); } } /// Update timing state from the virtual clock fn update_timing_state(&mut self, system: &ConfigurableSystem) { let clock = system.get_virtual_clock(); // Read current values from the virtual clock self.timing_state.virtual_time_ns = clock.now_ns(); self.timing_state.tick_count = clock.tick_count(); self.timing_state.target_speed = clock.speed_multiplier(); self.timing_state.step_pending = clock.is_step_pending(); self.timing_state.paused = clock.is_paused(); let mode = clock.mode(); self.timing_state.mode = match mode { SimulationMode::RealTime => "RealTime".to_string(), SimulationMode::MaxSpeed => "MaxSpeed".to_string(), SimulationMode::SingleStep => "SingleStep".to_string(), }; // Compute actual speed using RealTimeGovernor's snapshot infrastructure. // Shift current → previous, take new measurement, then compute delta. // This avoids the precision loss from accumulating nanos in a single u128 // and delegates the calculation to the well-tested compute_speed() method. // // We require at least 100ms of wall-clock time between snapshots to produce // a stable measurement. At 60 FPS, consecutive frames are ~16ms apart, which // is too short for accurate speed calculation — especially at low simulation // speeds where virtual time barely advances between frames. const MIN_SPEED_SAMPLE_INTERVAL: Duration = Duration::from_millis(100); let new_snap = RealTimeGovernor::take_speed_snapshot(clock); let should_compute = self.speed_snap_prev.as_ref().map_or(false, |prev| { new_snap.wall.duration_since(prev.wall) >= MIN_SPEED_SAMPLE_INTERVAL }); if should_compute { if let Some(prev) = &self.speed_snap_prev { let speed = RealTimeGovernor::compute_speed(prev, &new_snap); if speed > 0.0 { self.timing_state.actual_speed = speed; } } // Only shift snapshots when we actually computed a new speed value, // so the next computation uses a baseline at least 100ms old. self.speed_snap_prev = Some(new_snap); } else if self.speed_snap_prev.is_none() { // First frame — initialize the baseline snapshot self.speed_snap_prev = Some(new_snap); } } /// Set an error message pub fn set_error(&mut self, error: String) { self.last_error = Some(error); } /// Clear the error message pub fn clear_error(&mut self) { self.last_error = None; } /// Get the current error message pub fn get_error(&self) -> Option<&str> { self.last_error.as_deref() } } impl From for SystemInfo { fn from(info: crate::system_config::SystemInfo) -> Self { Self { name: info.name, description: info.description, component_count: info.component_count, cpu_speed: info.cpu_speed, rom_size: info.rom_size, ram_size: info.ram_size, } } }