rusty_emu/src/gui/components.rs
Jan-Peter von Hunnius 08f53d68fe 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.
2026-05-14 21:00:22 +02:00

1224 lines
47 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! # GUI Components Module
//!
//! This module implements all the individual GUI components for the Intel MCS-4 emulator.
//! Each component is responsible for rendering its specific section of the interface
//! and handling user interactions within that section.
//!
//! ## Component Architecture
//!
//! The GUI is organized into several specialized components:
//!
//! - **`GuiComponents`**: Main container managing all components
//! - **`ControlPanel`**: System control buttons and actions
//! - **`MemoryViewer`**: RAM content display and inspection
//! - **`RegisterViewer`**: CPU register state visualization
//! - **`RomLoader`**: File dialog integration for ROM loading
//! - **`StatusBar`**: System status and component health display
//!
//! ## Design Principles
//!
//! ### Separation of Concerns
//! Each component handles only its specific functionality:
//! - State management is centralized in `GuiState`
//! - Components receive immutable references where possible
//! - User interactions trigger state changes through controlled interfaces
//!
//! ### Performance Optimization
//! - Components minimize allocations and cloning
//! - State updates are batched and efficient
//! - UI layout uses egui's immediate mode for optimal performance
//!
//! ### User Experience
//! - Consistent visual design across all components
//! - Clear visual feedback for user actions
//! - Intuitive layout and component organization
//!
//! ## Component Communication
//!
//! Components communicate through the shared `GuiState`:
//! - Read-only access to state for display components
//! - Controlled mutation through specific methods
//! - Thread-safe state sharing with the emulator system
//!
//! ## Error Handling
//!
//! Components handle errors gracefully:
//! - Invalid states are displayed to users
//! - Failed operations show clear error messages
//! - Recovery options are provided where applicable
use super::state::GuiState;
use crate::system_config::{ConfigurableSystem, SystemController};
use crate::virtual_clock::SimulationMode;
use eframe::egui;
use parking_lot::Mutex;
use std::sync::Arc;
/// Container for all GUI components
///
/// This structure manages the lifecycle and coordination of all GUI components.
/// It acts as a facade, providing a clean interface for rendering different
/// sections of the user interface while maintaining proper separation of concerns.
///
/// ## Component Management
///
/// The container is responsible for:
/// - Component initialization and configuration
/// - Render orchestration and layout management
/// - State distribution to appropriate components
/// - Component lifecycle and cleanup
///
/// ## Performance Considerations
///
/// - Components are created once and reused across frames
/// - State references are passed efficiently without cloning
/// - Layout is managed centrally for consistent spacing
pub struct GuiComponents {
/// System control and management interface
control_panel: ControlPanel,
/// Timing/speed/mode/step controls
timing_controls: TimingControls,
/// RAM content inspection and visualization
memory_viewer: MemoryViewer,
/// ROM content inspection and visualization
rom_viewer: RomViewer,
/// CPU register state display
register_viewer: RegisterViewer,
/// ROM file loading and management
#[allow(dead_code)] // RomLoader is currently a placeholder implementation
rom_loader: RomLoader,
/// System status and health monitoring
status_bar: StatusBar,
}
impl GuiComponents {
/// Create a new GUI components container with all components initialized
///
/// This constructor sets up all GUI components with their default configurations.
/// Components are ready to use immediately after creation.
///
/// # Returns
/// A fully initialized `GuiComponents` instance
///
/// # Example
/// ```rust,no_run
/// use rusty_emu::gui::components::GuiComponents;
///
/// let mut components = GuiComponents::new();
/// // Components are ready to render
/// ```
pub fn new() -> Self {
Self {
control_panel: ControlPanel::new(),
timing_controls: TimingControls::new(),
memory_viewer: MemoryViewer::new(),
rom_viewer: RomViewer::new(),
register_viewer: RegisterViewer::new(),
rom_loader: RomLoader::new(),
status_bar: StatusBar::new(),
}
}
/// Render the control panel component
///
/// The control panel provides system management functionality including
/// start/stop/reset controls and ROM loading capabilities.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for control operations
/// * `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 timing controls component
///
/// Displays simulation speed, mode, step controls, and virtual time.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for display and interaction
/// * `system` - Optional reference to the emulator system for clock control
pub fn render_timing_controls(
&mut self,
ui: &mut egui::Ui,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
self.timing_controls.render(ui, state, system);
}
/// Render the memory viewer component
///
/// Displays RAM contents in a tabular format with bank selection
/// and hex/decimal viewing options.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for display and interaction
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
self.memory_viewer.render(ui, state);
}
/// Render the ROM viewer component
///
/// Displays ROM contents in a tabular format with address selection
/// and hex/decimal viewing options.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for display and interaction
pub fn render_rom_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
self.rom_viewer.render(ui, state);
}
/// Render the register viewer component
///
/// Shows CPU register state including accumulator, program counter,
/// index registers, and system flags.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for display and interaction
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
self.register_viewer.render(ui, state);
}
/// Render the status bar component
///
/// Displays system health, component status, cycle counts,
/// and any error conditions.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state for display
pub fn render_status_bar(&self, ui: &mut egui::Ui, state: &GuiState) {
self.status_bar.render(ui, state);
}
}
/// Control panel component for system control buttons and operations
///
/// The control panel provides the primary interface for managing the emulator system.
/// It offers intuitive controls for starting, stopping, and resetting the emulation,
/// along with system configuration and file management capabilities.
///
/// ## Features
///
/// - **System Lifecycle Management**: Start, stop, and reset emulator execution
/// - **Visual State Feedback**: Dynamic button states reflecting current system status
/// - **Error Handling**: Clear error display and recovery mechanisms
/// - **Application Control**: Window management and application termination
///
/// ## Button States
///
/// The control panel adapts its interface based on system state:
/// - **Stopped**: Shows "Start System" button, disabled stop/reset buttons
/// - **Running**: Shows "System Running..." text, enabled stop/reset buttons
/// - **Error**: Displays error messages with option to clear them
pub struct ControlPanel {
/// Dynamic text for the start/stop button based on system state
start_button_text: String,
/// Whether the stop button should be enabled
stop_button_enabled: bool,
/// Whether the reset button should be enabled
reset_button_enabled: bool,
}
impl ControlPanel {
/// Create a new control panel with default state
///
/// Initializes the control panel in the "stopped" state with appropriate
/// button configurations for a system that's ready to start.
///
/// # Returns
/// A new `ControlPanel` instance ready for system control
pub fn new() -> Self {
Self {
start_button_text: "Start System".to_string(),
stop_button_enabled: false,
reset_button_enabled: false,
}
}
/// Render the control panel interface
///
/// Creates a horizontal layout with all control buttons and handles
/// user interactions. The interface adapts dynamically based on system state.
///
/// # Arguments
/// * `ui` - egui UI context for rendering and interaction
/// * `state` - Mutable reference to GUI state for system control
///
/// # Layout
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ System Control ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ │
/// │ [Load ROM] [Start System] [Stop] [Reset] [Close] │
/// └─────────────────────────────────────────────────┘
/// ```
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");
// ROM loading button
if ui.button("Load ROM").clicked() {
// ROM loading will be handled by RomLoader component
state.set_error("ROM loader not yet implemented".to_string());
}
// Start/Stop system button (context-sensitive)
if ui.button(&self.start_button_text).clicked() {
if !state.system_running {
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, system);
}
// Reset system button
if ui.button("Reset System").clicked() {
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);
}
});
ui.separator();
}
/// Start the emulator system
///
/// Transitions the system from stopped to running state and updates
/// all related UI elements to reflect the new state.
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
/// * `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
///
/// Transitions the system from running to stopped state and updates
/// UI elements accordingly.
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
/// * `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
///
/// Performs a complete system reset, stopping execution and clearing
/// all state including cycle counts and error conditions.
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
/// * `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();
self.stop_button_enabled = false;
self.reset_button_enabled = false;
state.clear_error();
}
}
/// Timing controls component for simulation speed, mode, and step management.
///
/// This component provides controls for the virtual clock system:
/// - **Speed slider**: Logarithmic scale from 0.001× to 100×
/// - **Mode selector**: RealTime / MaxSpeed / SingleStep radio buttons
/// - **Step button**: Advance one tick in SingleStep mode
/// - **Virtual time display**: Current time, tick count, actual vs target speed
///
/// ## Logarithmic Slider Mapping
///
/// The slider position `t` (0.0 to 1.0) maps to speed via:
/// `speed = 10^(t * 5 - 3)` giving range [0.001, 100.0]
pub struct TimingControls {
/// Current slider position (0.0 to 1.0), mapped logarithmically to speed
speed_slider_pos: f32,
}
impl TimingControls {
/// Create a new timing controls component
pub fn new() -> Self {
// Default: 1.0x speed → log10(1.0) = 0, so t = (0 + 3) / 5 = 0.6
Self {
speed_slider_pos: 0.6,
}
}
/// Convert slider position (0..1) to speed multiplier using logarithmic scale.
///
/// Range: 0.001× (position 0.0) to 100.0× (position 1.0)
/// Formula: speed = 10^(pos * 5 - 3)
fn pos_to_speed(pos: f32) -> f64 {
let pos = pos.clamp(0.0, 1.0) as f64;
10f64.powf(pos * 5.0 - 3.0)
}
/// Convert speed multiplier to slider position (0..1) using logarithmic scale.
///
/// Formula: pos = (log10(speed) + 3) / 5
fn speed_to_pos(speed: f64) -> f32 {
if speed <= 0.0 {
return 0.0;
}
let pos = (speed.log10() + 3.0) / 5.0;
pos.clamp(0.0, 1.0) as f32
}
/// Render the timing controls interface.
///
/// # Layout
/// ```text
/// ┌──────────────────────────────────────────────────────────────────┐
/// │ Timing Controls │
/// │ Speed: [========●===========] 1.00× (actual: 0.98×) │
/// │ Mode: (●) RealTime ( ) MaxSpeed ( ) SingleStep [Step] │
/// │ Virtual Time: 1.23 ms | Ticks: 3,690 | Paused: No │
/// └──────────────────────────────────────────────────────────────────┘
/// ```
pub fn render(
&mut self,
ui: &mut egui::Ui,
state: &mut GuiState,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
) {
// Sync slider position from actual clock speed so external changes
// (e.g., console "speed 0.1" command) are reflected in the GUI.
self.speed_slider_pos = Self::speed_to_pos(state.timing_state.target_speed);
ui.vertical(|ui| {
ui.heading("Timing Controls");
// Speed slider row
ui.horizontal(|ui| {
ui.label("Speed:");
let slider = egui::Slider::new(&mut self.speed_slider_pos, 0.0..=1.0)
.show_value(false)
.step_by(0.001);
let response = ui.add(slider);
if response.changed() {
let new_speed = Self::pos_to_speed(self.speed_slider_pos);
if let Some(sys) = system {
let sys_guard = sys.lock();
let clock = sys_guard.get_virtual_clock();
clock.set_speed_multiplier(new_speed);
// If in MaxSpeed mode, switch back to RealTime when slider moves
if clock.mode() == SimulationMode::MaxSpeed {
clock.set_mode(SimulationMode::RealTime);
}
}
}
// Show current target speed
let speed_text = state.timing_state.format_speed();
ui.label(format!("Target: {}", speed_text));
// Show actual speed
let actual_text = state.timing_state.format_actual_speed();
ui.label(format!("Actual: {}", actual_text));
});
// Mode selector and step button row
ui.horizontal(|ui| {
ui.label("Mode:");
let current_mode = state.timing_state.mode.as_str();
let is_realtime = current_mode == "RealTime";
let is_maxspeed = current_mode == "MaxSpeed";
let is_singlestep = current_mode == "SingleStep";
// RealTime radio
if ui.radio(is_realtime, "RealTime").clicked() {
self.apply_mode(system, SimulationMode::RealTime);
}
// MaxSpeed radio
if ui.radio(is_maxspeed, "MaxSpeed").clicked() {
self.apply_mode(system, SimulationMode::MaxSpeed);
}
// SingleStep radio
if ui.radio(is_singlestep, "SingleStep").clicked() {
self.apply_mode(system, SimulationMode::SingleStep);
}
ui.separator();
// Step button (enabled only in SingleStep mode)
let step_pending = state.timing_state.step_pending;
let step_button = ui.add_enabled(
is_singlestep && !step_pending,
egui::Button::new("Step"),
);
if step_button.clicked() {
if let Some(sys) = system {
let sys_guard = sys.lock();
let clock = sys_guard.get_virtual_clock();
clock.trigger_step();
}
}
if is_singlestep && step_pending {
ui.colored_label(egui::Color32::YELLOW, "⏳ Step pending...");
}
});
// Virtual time display row
ui.horizontal(|ui| {
ui.label("Virtual Time:");
ui.strong(state.timing_state.format_virtual_time());
ui.separator();
ui.label("Ticks:");
ui.strong(format!("{}", state.timing_state.tick_count));
ui.separator();
// Paused indicator
if state.timing_state.paused {
ui.colored_label(egui::Color32::YELLOW, "⏸ Paused");
} else if state.system_running {
ui.colored_label(egui::Color32::GREEN, "▶ Running");
} else {
ui.colored_label(egui::Color32::RED, "⏹ Stopped");
}
});
});
ui.separator();
}
/// Apply a simulation mode change to the virtual clock.
fn apply_mode(
&mut self,
system: Option<&Arc<Mutex<ConfigurableSystem>>>,
mode: SimulationMode,
) {
if let Some(sys) = system {
let sys_guard = sys.lock();
let clock = sys_guard.get_virtual_clock();
clock.set_mode(mode);
}
}
}
/// Memory viewer component for displaying RAM contents and state
///
/// The memory viewer provides comprehensive RAM inspection capabilities,
/// allowing users to examine memory contents across different banks with
/// flexible display options and intuitive navigation.
///
/// ## Features
///
/// - **Multi-Bank Display**: View RAM contents across 4 banks simultaneously
/// - **Flexible Formatting**: Toggle between hexadecimal and decimal display
/// - **Interactive Bank Selection**: Choose which memory bank to inspect
/// - **Scrollable Interface**: Navigate through memory contents efficiently
/// - **Real-time Updates**: Live memory content updates during emulation
///
/// ## Display Format
///
/// The memory viewer shows data in a structured grid:
/// - **Address Column**: Memory addresses in hexadecimal format
/// - **Bank Columns**: B0-B3 showing contents of each memory bank
/// - **Value Display**: Configurable hex or decimal representation
///
/// ## Memory Organization
///
/// The Intel 4002 RAM has the following structure:
/// - **4 Banks**: Independent memory banks (B0-B3)
/// - **4 Bytes per Bank**: Addresses 0x00-0x03 in each bank
/// - **4-bit Values**: Each memory location stores a 4-bit nibble
pub struct MemoryViewer {
/// Number of bytes to display per row (currently fixed at 16)
#[allow(dead_code)]
// bytes_per_row is currently fixed at 16 but kept for future flexibility
bytes_per_row: usize,
}
impl MemoryViewer {
/// Create a new memory viewer with default configuration
///
/// Initializes the viewer with standard layout optimized for the Intel 4002 RAM structure.
///
/// # Returns
/// A new `MemoryViewer` instance ready for RAM content display
pub fn new() -> Self {
Self { bytes_per_row: 16 }
}
/// Render the memory viewer interface
///
/// Creates a comprehensive memory inspection interface with bank selection,
/// display options, and a scrollable memory contents grid.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state containing memory data
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ Memory Viewer [─] [□] │
/// │ Bank: [0] □ Hex View │
/// ├─────────────────────────────────────────────────┤
/// │ Address B0 B1 B2 B3 │
/// │ [00] [12] [34] [56] [78] │
/// │ [01] [9A] [BC] [DE] [F0] │
/// │ [02] [11] [22] [33] [44] │
/// │ [03] [55] [66] [77] [88] │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
ui.vertical(|ui| {
// Section header
ui.heading("Memory Viewer");
// Control bar with bank selection and display options
ui.horizontal(|ui| {
ui.label("Bank:");
// Bank selector (0-3 for Intel 4002)
ui.add(
egui::DragValue::new(&mut state.memory_state.selected_bank).clamp_range(0..=3),
);
ui.checkbox(&mut state.memory_state.show_hex, "Hex View");
});
ui.separator();
// Memory contents in scrollable area
egui::ScrollArea::vertical().show(ui, |ui| {
egui::Grid::new("memory_grid").striped(true).show(ui, |ui| {
// Header row with bank labels
ui.label("Address");
for i in 0..4 {
ui.label(format!("B{}", i));
}
ui.end_row();
// Memory contents rows
for addr in 0..4 {
// Address column
ui.label(format!("{:02X}", addr));
// Bank data columns
for bank in 0..4 {
let value = state.memory_state.ram_contents[bank][addr];
if state.memory_state.show_hex {
ui.label(format!("{:02X}", value));
} else {
ui.label(format!("{}", value));
}
}
ui.end_row();
}
});
});
});
ui.separator();
}
}
/// Register viewer component for displaying CPU register state
///
/// The register viewer provides comprehensive CPU state visualization,
/// showing all key registers, flags, and pointers that define the current
/// execution state of the Intel 4004 microprocessor.
///
/// ## Features
///
/// - **Complete Register Set**: Display all 16 index registers (R0-R15)
/// - **Core CPU State**: Accumulator, program counter, stack pointer
/// - **Flag Visualization**: Carry flag status with clear indicators
/// - **Interactive Selection**: Choose which index register to highlight
/// - **Dual Format Display**: Hexadecimal and decimal representations
/// - **Real-time Updates**: Live register state during emulation
///
/// ## Intel 4004 Register Architecture
///
/// The Intel 4004 has the following register structure:
/// - **Accumulator**: Main arithmetic register (4 bits)
/// - **Index Registers**: 16 general-purpose registers (R0-R15)
/// - **Program Counter**: 12-bit address pointer
/// - **Stack Pointer**: 4-bit stack address register
/// - **Carry Flag**: Arithmetic carry/borrow indicator
///
/// ## Display Organization
///
/// Registers are organized in a clear hierarchy:
/// - **Primary Registers**: Most frequently used (accumulator, PC, stack)
/// - **Flags**: System status indicators
/// - **Index Registers**: General-purpose register file
pub struct RegisterViewer {
// No internal state - all UI state is stored in GuiState
}
impl RegisterViewer {
/// Create a new register viewer with default configuration
///
/// Initializes the viewer with standard display formatting optimized
/// for the Intel 4004 architecture.
///
/// # Returns
/// A new `RegisterViewer` instance ready for CPU state display
pub fn new() -> Self {
Self {}
}
/// Render the register viewer interface
///
/// Creates a comprehensive register inspection interface with organized
/// display of all CPU state elements and interactive index register selection.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state containing register data
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ CPU Registers [─] [□] │
/// │ Index Register: [0] │
/// ├─────────────────────────────────────────────────┤
/// │ Register │ Value (Hex) │ Value (Dec) │
/// │ Accumulator │ 0F │ 15 │
/// │ Carry Flag │ 1 │ Set │
/// │ Program Counter│ 123 │ 291 │
/// │ Index R0 │ 2A │ 42 │
/// │ Stack Pointer │ 08 │ 8 │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
ui.vertical(|ui| {
// Section header
ui.heading("CPU Registers");
// Index register selector
ui.horizontal(|ui| {
ui.label("Index Register:");
ui.add(
egui::DragValue::new(&mut state.register_state.selected_register)
.clamp_range(0..=15),
);
});
ui.separator();
// Register display grid
egui::Grid::new("register_grid")
.striped(true)
.show(ui, |ui| {
// Header row
ui.label("Register");
ui.label("Value (Hex)");
ui.label("Value (Dec)");
ui.end_row();
// Core CPU registers
ui.label("Accumulator");
ui.label(format!("{:X}", state.register_state.accumulator));
ui.label(format!("{}", state.register_state.accumulator));
ui.end_row();
// System flags
ui.label("Carry Flag");
ui.label(if state.register_state.carry_flag {
"1"
} else {
"0"
});
ui.label(if state.register_state.carry_flag {
"Set"
} else {
"Clear"
});
ui.end_row();
// Program execution state
ui.label("Program Counter");
ui.label(format!("{:03X}", state.register_state.program_counter));
ui.label(format!("{}", state.register_state.program_counter));
ui.end_row();
// Selected index register (highlighted)
let selected = state.register_state.selected_register;
ui.label(format!("Index R{}", selected));
ui.label(format!(
"{:X}",
state.register_state.index_registers[selected]
));
ui.label(format!(
"{}",
state.register_state.index_registers[selected]
));
ui.end_row();
// Stack management
ui.label("Stack Pointer");
ui.label(format!("{:X}", state.register_state.stack_pointer));
ui.label(format!("{}", state.register_state.stack_pointer));
ui.end_row();
});
});
ui.separator();
}
}
/// ROM viewer component for displaying ROM contents
///
/// The ROM viewer provides comprehensive ROM inspection capabilities,
/// allowing users to examine program memory contents with flexible
/// display options and intuitive navigation.
///
/// ## Features
///
/// - **Program Memory Display**: View ROM contents from Intel 4001 chips
/// - **Flexible Formatting**: Toggle between hexadecimal and decimal display
/// - **Address Navigation**: Jump to specific memory addresses
/// - **Scrollable Interface**: Navigate through memory contents efficiently
/// - **Real-time Updates**: Live ROM content display
///
/// ## Display Format
///
/// The ROM viewer shows data in a structured grid:
/// - **Address Column**: Memory addresses in hexadecimal format
/// - **Data Columns**: 16 bytes per row for easy viewing
/// - **Value Display**: Configurable hex or decimal representation
///
/// ## Memory Organization
///
/// The Intel 4001 ROM has the following structure:
/// - **256 Bytes**: Total ROM capacity per chip
/// - **8-bit Values**: Each memory location stores a full byte
pub struct RomViewer {
/// Number of bytes to display per row
bytes_per_row: usize,
}
impl RomViewer {
/// Create a new ROM viewer with default configuration
///
/// Initializes the viewer with standard layout optimized for the Intel 4001 ROM structure.
///
/// # Returns
/// A new `RomViewer` instance ready for ROM content display
pub fn new() -> Self {
Self { bytes_per_row: 16 }
}
/// Render the ROM viewer interface
///
/// Creates a comprehensive ROM inspection interface with address navigation,
/// display options, and a scrollable memory contents grid.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state containing ROM data
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ ROM Viewer (Chip: ROM_4001_1) [─] [□] │
/// │ □ Hex View │
/// ├─────────────────────────────────────────────────┤
/// │ Address 00 01 02 03 04 05 06 07 08 09 0A 0B... │
/// │ 0x000 D4 C3 B2 A1 00 11 22 33 44 55 66 77... │
/// │ 0x010 88 99 AA BB CC DD EE FF 00 11 22 33... │
/// │ ... │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
ui.vertical(|ui| {
// Section header with chip name
let header = if state.rom_state.chip_name.is_empty() {
"ROM Viewer".to_string()
} else {
format!("ROM Viewer ({})", state.rom_state.chip_name)
};
ui.heading(header);
// Control bar with display options
ui.horizontal(|ui| {
ui.checkbox(&mut state.rom_state.show_hex, "Hex View");
});
ui.separator();
// ROM contents in scrollable area
egui::ScrollArea::vertical()
.max_height(200.0)
.show(ui, |ui| {
egui::Grid::new("rom_grid").striped(true).show(ui, |ui| {
// Header row with column labels
ui.label("Addr");
for i in 0..self.bytes_per_row {
ui.label(format!("{:02X}", i));
}
ui.end_row();
// ROM contents rows
let total_rows = (state.rom_state.rom_contents.len() + self.bytes_per_row
- 1)
/ self.bytes_per_row;
for row in 0..total_rows {
let addr = row * self.bytes_per_row;
// Address column
ui.label(format!("{:03X}", addr));
// Data columns
for col in 0..self.bytes_per_row {
let idx = addr + col;
if idx < state.rom_state.rom_contents.len() {
let value = state.rom_state.rom_contents[idx];
if state.rom_state.show_hex {
ui.label(format!("{:02X}", value));
} else {
ui.label(format!("{:3}", value));
}
} else {
ui.label("--");
}
}
ui.end_row();
}
});
});
});
ui.separator();
}
}
/// ROM loader component for file dialog integration and program management
///
/// The ROM loader handles program file selection, loading, and management.
/// It provides the interface for users to load Intel 4001 ROM files and
/// binary programs into the emulator system.
///
/// ## Features
///
/// - **File Dialog Integration**: Native file browser for program selection
/// - **Program Validation**: Basic file format and size validation
/// - **Load Feedback**: Visual confirmation of successful loads
/// - **Error Handling**: Clear error messages for failed operations
/// - **Future Extensions**: Support for multiple ROM chips and formats
///
/// ## Supported Formats
///
/// - **Binary Files**: Raw binary program data (.bin)
/// - **Intel 4001 Format**: MCS-4 ROM file format (planned)
/// - **Configuration Files**: System configuration integration (planned)
///
/// ## Integration Notes
///
/// Currently shows placeholder for file dialog implementation.
/// Production version should integrate with native file dialogs using:
/// - **rfd** crate for cross-platform file dialogs
/// - **async** operations for non-blocking file I/O
/// - **Progress feedback** for large file operations
pub struct RomLoader {
/// Whether to show the file selection dialog
show_file_dialog: bool,
/// Currently selected file path (if any)
selected_file: Option<String>,
}
impl RomLoader {
/// Create a new ROM loader with default state
///
/// Initializes the loader with no file selected and dialog closed.
/// Ready to handle user file selection requests.
///
/// # Returns
/// A new `RomLoader` instance ready for file operations
pub fn new() -> Self {
Self {
show_file_dialog: false,
selected_file: None,
}
}
/// Render the ROM loader interface
///
/// Creates the file management interface with load button and
/// file selection dialog integration.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `_state` - GUI state (currently unused but reserved for future integration)
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ ROM Management [─] [□]│
/// │ [Load ROM File...] │
/// │ Selected: /path/to/program.bin │
/// │ │
/// │ [File Dialog Placeholder] │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&mut self, ui: &mut egui::Ui, _state: &mut GuiState) {
ui.vertical(|ui| {
ui.heading("ROM Management");
// Load button to trigger file selection
if ui.button("Load ROM File...").clicked() {
self.show_file_dialog = true;
}
// Display currently selected file
if let Some(ref file) = self.selected_file {
ui.label(format!("Selected: {}", file));
}
// File dialog implementation (placeholder)
if self.show_file_dialog {
// Placeholder for file dialog
ui.label("File dialog would open here");
if ui.button("Cancel").clicked() {
self.show_file_dialog = false;
}
}
});
ui.separator();
}
}
/// Status bar component for system status display and health monitoring
///
/// The status bar provides comprehensive system health information,
/// component status indicators, and real-time performance metrics.
/// It serves as the primary location for system state overview.
///
/// ## Features
///
/// - **System Status**: Running/stopped state with visual indicators
/// - **Performance Metrics**: Cycle count and execution speed
/// - **Component Health**: Individual component status monitoring
/// - **Error Display**: Real-time error message display
/// - **System Information**: CPU speed, component count, and configuration
///
/// ## Visual Design
///
/// - **Color Coding**: Green for healthy, red for errors/stopped
/// - **Layout Organization**: Status on left, errors on right
/// - **Real-time Updates**: Live status changes during operation
/// - **Compact Display**: Information-dense but readable layout
///
/// ## Component Status Indicators
///
/// Monitors the health of all major system components:
/// - **CPU**: Intel 4004 execution status
/// - **RAM**: Intel 4002 memory operations
/// - **ROM**: Intel 4001 program storage
/// - **CLK**: System clock generation
pub struct StatusBar;
impl StatusBar {
/// Create a new status bar component
///
/// Initializes a stateless status bar ready to display
/// current system health and performance information.
///
/// # Returns
/// A new `StatusBar` instance
pub fn new() -> Self {
Self
}
/// Render the status bar interface
///
/// Creates a comprehensive status display with system information,
/// component health indicators, and error reporting.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state for status information
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────────────────┐
/// │ Status: ● Running │ Cycles: 12345 │ CPU: 0.7 MHz │
/// │ Components: 4 │ CPU RAM ROM CLK │ Error: Connection │
/// └─────────────────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
ui.separator();
ui.horizontal(|ui| {
// Left side: Status information and metrics
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
// System running status
ui.label("Status:");
if state.system_running {
ui.colored_label(egui::Color32::GREEN, "● Running");
} else {
ui.colored_label(egui::Color32::RED, "● Stopped");
}
ui.separator();
// Performance metrics
ui.label(format!("Cycles: {}", state.cycle_count));
ui.separator();
// System information
if let Some(ref info) = state.system_info {
ui.label(format!("CPU: {:.1} MHz", info.cpu_speed / 1_000_000.0));
ui.label(format!("Components: {}", info.component_count));
}
ui.separator();
// Component status indicators
ui.label("Components:");
ui.colored_label(
if state.component_states.cpu_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"CPU",
);
ui.colored_label(
if state.component_states.ram_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"RAM",
);
ui.colored_label(
if state.component_states.rom_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"ROM",
);
ui.colored_label(
if state.component_states.clock_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"CLK",
);
});
// Right side: Error messages
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if let Some(ref error) = state.last_error {
ui.colored_label(egui::Color32::RED, format!("Error: {}", error));
}
});
});
}
}