Added debug output
Added GUI
This commit is contained in:
parent
3d4de8b268
commit
e17d6ad177
18 changed files with 3137 additions and 162 deletions
801
src/gui/components.rs
Normal file
801
src/gui/components.rs
Normal file
|
|
@ -0,0 +1,801 @@
|
|||
//! # 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 eframe::egui;
|
||||
|
||||
/// 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,
|
||||
/// RAM content inspection and visualization
|
||||
memory_viewer: MemoryViewer,
|
||||
/// CPU register state display
|
||||
register_viewer: RegisterViewer,
|
||||
/// ROM file loading and management
|
||||
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(),
|
||||
memory_viewer: MemoryViewer::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
|
||||
pub fn render_control_panel(&mut self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
self.control_panel.render(ui, state);
|
||||
}
|
||||
|
||||
/// 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` - Immutable reference to GUI state for display
|
||||
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
|
||||
self.memory_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` - Immutable reference to GUI state for display
|
||||
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop system button (only enabled when running)
|
||||
if ui.button("Stop System").clicked() && state.system_running {
|
||||
self.stop_system(state);
|
||||
}
|
||||
|
||||
// Reset system button
|
||||
if ui.button("Reset System").clicked() {
|
||||
self.reset_system(state);
|
||||
}
|
||||
|
||||
// Application close button
|
||||
if ui.button("Close").clicked() {
|
||||
// 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
|
||||
fn start_system(&mut self, state: &mut GuiState) {
|
||||
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
|
||||
fn stop_system(&mut self, state: &mut GuiState) {
|
||||
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
|
||||
fn reset_system(&mut self, state: &mut GuiState) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Display mode: true for hexadecimal, false for decimal
|
||||
show_hex: bool,
|
||||
/// Number of bytes to display per row (currently fixed at 16)
|
||||
bytes_per_row: usize,
|
||||
}
|
||||
|
||||
impl MemoryViewer {
|
||||
/// Create a new memory viewer with default configuration
|
||||
///
|
||||
/// Initializes the viewer with hexadecimal display mode and standard
|
||||
/// layout optimized for the Intel 4002 RAM structure.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `MemoryViewer` instance ready for RAM content display
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
show_hex: true,
|
||||
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` - Immutable 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: &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.clone())
|
||||
.clamp_range(0..=3),
|
||||
);
|
||||
ui.checkbox(&mut self.show_hex.clone(), "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 self.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 {
|
||||
/// Currently selected index register for detailed view (0-15)
|
||||
selected_register: usize,
|
||||
}
|
||||
|
||||
impl RegisterViewer {
|
||||
/// Create a new register viewer with default configuration
|
||||
///
|
||||
/// Initializes the viewer with index register 0 selected and standard
|
||||
/// display formatting optimized for the Intel 4004 architecture.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `RegisterViewer` instance ready for CPU state display
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
selected_register: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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` - Immutable 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: &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 self.selected_register.clone()).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!("{:02X}", 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)
|
||||
ui.label(format!("Index R{}", self.selected_register));
|
||||
ui.label(format!(
|
||||
"{:02X}",
|
||||
state.register_state.index_registers[self.selected_register]
|
||||
));
|
||||
ui.label(format!(
|
||||
"{}",
|
||||
state.register_state.index_registers[self.selected_register]
|
||||
));
|
||||
ui.end_row();
|
||||
|
||||
// Stack management
|
||||
ui.label("Stack Pointer");
|
||||
ui.label(format!("{:02X}", state.register_state.stack_pointer));
|
||||
ui.label(format!("{}", state.register_state.stack_pointer));
|
||||
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.clone() {
|
||||
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));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
240
src/gui/mod.rs
Normal file
240
src/gui/mod.rs
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
//! # GUI Module
|
||||
//!
|
||||
//! This module provides a complete graphical user interface for the Intel MCS-4 emulator.
|
||||
//! It includes state management, component rendering, and system integration.
|
||||
|
||||
pub mod components;
|
||||
pub mod state;
|
||||
|
||||
use crate::system_config::ConfigurableSystem;
|
||||
use eframe::egui;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Main GUI application structure
|
||||
///
|
||||
/// This structure manages the entire GUI application lifecycle, including:
|
||||
/// - System integration and control
|
||||
/// - State management and updates
|
||||
/// - Component rendering and interaction
|
||||
/// - Error handling and user feedback
|
||||
pub struct GuiApp {
|
||||
/// The emulator system being monitored and controlled
|
||||
/// Wrapped in Arc<Mutex<>> for thread-safe access
|
||||
system: Option<Arc<Mutex<ConfigurableSystem>>>,
|
||||
/// GUI-specific state management
|
||||
gui_state: state::GuiState,
|
||||
/// Container for all GUI components
|
||||
components: components::GuiComponents,
|
||||
}
|
||||
|
||||
impl GuiApp {
|
||||
/// Create a new GUI application instance
|
||||
///
|
||||
/// This constructor initializes all GUI components and state management systems.
|
||||
/// The application starts without a system loaded - use `set_system()` to connect
|
||||
/// an emulator instance.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `_cc` - eframe creation context (currently unused but required by the framework)
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `GuiApp` instance ready for system integration
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,no_run
|
||||
/// use rusty_emu::gui::GuiApp;
|
||||
///
|
||||
/// let app = GuiApp::new(&eframe::CreationContext {
|
||||
/// egui_ctx: Default::default(),
|
||||
/// integration_info: Default::default(),
|
||||
/// storage: None,
|
||||
/// });
|
||||
/// ```
|
||||
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||
let gui_state = state::GuiState::new();
|
||||
let components = components::GuiComponents::new();
|
||||
|
||||
Self {
|
||||
system: None,
|
||||
gui_state,
|
||||
components,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the emulator system for the GUI to control and monitor
|
||||
///
|
||||
/// This method establishes the connection between the GUI and an emulator system.
|
||||
/// Once connected, the GUI will begin monitoring system state and providing
|
||||
/// interactive controls.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `system` - Thread-safe reference to the emulator system
|
||||
///
|
||||
/// # Thread Safety
|
||||
/// This method is not thread-safe and should only be called during GUI initialization
|
||||
/// or when no emulation is running.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,no_run
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use rusty_emu::gui::GuiApp;
|
||||
/// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
|
||||
///
|
||||
/// let mut app = GuiApp::new(&creation_context);
|
||||
/// let factory = SystemFactory::new();
|
||||
/// let system = Arc::new(Mutex::new(factory.create_from_json("config.json")?));
|
||||
/// app.set_system(system);
|
||||
/// ```
|
||||
pub fn set_system(&mut self, system: Arc<Mutex<ConfigurableSystem>>) {
|
||||
self.system = Some(system);
|
||||
self.gui_state.system_loaded = true;
|
||||
}
|
||||
|
||||
/// Get current system reference if available
|
||||
///
|
||||
/// Returns a cloned Arc reference to the system for thread-safe access.
|
||||
/// This method is used internally by the GUI to update state and render
|
||||
/// system information.
|
||||
///
|
||||
/// # Returns
|
||||
/// `Some(Arc<Mutex<ConfigurableSystem>>)` if a system is loaded, `None` otherwise
|
||||
fn get_system(&self) -> Option<Arc<Mutex<ConfigurableSystem>>> {
|
||||
self.system.as_ref().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for GuiApp {
|
||||
/// Main update loop for the GUI application
|
||||
///
|
||||
/// This method is called by eframe for each frame and handles:
|
||||
/// - Requesting continuous repaints for real-time updates
|
||||
/// - Updating GUI state from the emulator system
|
||||
/// - Rendering the complete user interface
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ctx` - egui context for rendering and interaction
|
||||
/// * `_frame` - eframe frame (currently unused)
|
||||
///
|
||||
/// # Performance
|
||||
/// - Requests repaint at ~60 FPS for smooth real-time interaction
|
||||
/// - State updates are performed without blocking the GUI thread
|
||||
/// - System lock is held briefly to copy current state
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
// Request repaint for smooth real-time updates (60 FPS)
|
||||
ctx.request_repaint();
|
||||
|
||||
// Update system state if available - non-blocking operation
|
||||
if let Some(system) = self.get_system() {
|
||||
self.gui_state.update_from_system(&system);
|
||||
}
|
||||
|
||||
// Render the complete GUI interface
|
||||
self.render_gui(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiApp {
|
||||
/// Render the complete GUI interface
|
||||
///
|
||||
/// This method orchestrates the rendering of all GUI components in their
|
||||
/// proper layout. It creates a clean, organized interface with logical
|
||||
/// grouping of related functionality.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ctx` - egui context for rendering operations
|
||||
///
|
||||
/// # Layout Structure
|
||||
/// - Header with application title
|
||||
/// - Control panel for system management
|
||||
/// - Memory viewer for RAM inspection
|
||||
/// - Register viewer for CPU state
|
||||
/// - Status bar for system health and errors
|
||||
fn render_gui(&mut self, ctx: &egui::Context) {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
// Application header
|
||||
ui.heading("Intel MCS-4 Emulator");
|
||||
|
||||
ui.separator();
|
||||
|
||||
// Main GUI sections - organized for optimal workflow
|
||||
self.components
|
||||
.render_control_panel(ui, &mut self.gui_state);
|
||||
self.components.render_memory_viewer(ui, &self.gui_state);
|
||||
self.components.render_register_viewer(ui, &self.gui_state);
|
||||
self.components.render_status_bar(ui, &self.gui_state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the GUI application as a standalone desktop application
|
||||
///
|
||||
/// This function launches the complete GUI application with the specified system.
|
||||
/// It handles all eframe initialization, window creation, and event loop management.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `system` - Optional system to load into the GUI (can be None for manual loading)
|
||||
///
|
||||
/// # Returns
|
||||
/// `eframe::Result<()>` - Success or failure of GUI initialization and execution
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if:
|
||||
/// - eframe initialization fails
|
||||
/// - Window creation fails
|
||||
/// - GUI event loop encounters critical errors
|
||||
/// - Display server is not available (headless environment)
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,no_run
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use rusty_emu::gui::run_gui;
|
||||
/// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
|
||||
///
|
||||
/// // Create system (optional)
|
||||
/// let factory = SystemFactory::new();
|
||||
/// let system = Some(Arc::new(Mutex::new(
|
||||
/// factory.create_from_json("configs/mcs4_basic.json")?
|
||||
/// )));
|
||||
///
|
||||
/// // Launch GUI - this will block until the GUI window is closed
|
||||
/// if let Err(e) = run_gui(system) {
|
||||
/// eprintln!("GUI failed to start: {}", e);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Window Configuration
|
||||
/// - **Default Size**: 1200x800 pixels
|
||||
/// - **Title**: "Intel MCS-4 Emulator"
|
||||
/// - **Resizable**: Yes (default eframe behavior)
|
||||
/// - **Frame Rate**: ~60 FPS for smooth interaction
|
||||
///
|
||||
/// # Platform Notes
|
||||
/// - **Windows**: Requires Windows 10 or later
|
||||
/// - **Linux**: Requires X11 or Wayland display server
|
||||
/// - **macOS**: Requires macOS 10.15 or later
|
||||
pub fn run_gui(system: Option<Arc<Mutex<ConfigurableSystem>>>) -> eframe::Result<()> {
|
||||
// Configure native window options
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([1200.0, 800.0])
|
||||
.with_title("Intel MCS-4 Emulator"),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Launch the native GUI application
|
||||
// eframe will handle CreationContext creation internally
|
||||
eframe::run_native(
|
||||
"Intel MCS-4 Emulator",
|
||||
options,
|
||||
Box::new(|cc| {
|
||||
let mut app = GuiApp::new(cc);
|
||||
|
||||
// Connect system if provided
|
||||
if let Some(sys) = system {
|
||||
app.set_system(sys);
|
||||
}
|
||||
|
||||
Box::new(app)
|
||||
}),
|
||||
)
|
||||
}
|
||||
169
src/gui/state.rs
Normal file
169
src/gui/state.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
//! # 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 std::sync::{Arc, Mutex};
|
||||
|
||||
/// 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<SystemInfo>,
|
||||
/// Component states
|
||||
pub component_states: ComponentStates,
|
||||
/// Memory state
|
||||
pub memory_state: MemoryState,
|
||||
/// CPU register state
|
||||
pub register_state: RegisterState,
|
||||
/// Last error message
|
||||
pub last_error: Option<String>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// 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)
|
||||
pub stack_pointer: u8,
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
register_state: RegisterState {
|
||||
accumulator: 0,
|
||||
carry_flag: false,
|
||||
program_counter: 0,
|
||||
index_registers: [0; 16],
|
||||
stack_pointer: 0,
|
||||
},
|
||||
last_error: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update state from the current system
|
||||
pub fn update_from_system(&mut self, system: &Arc<Mutex<ConfigurableSystem>>) {
|
||||
if let Ok(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 cycle count (simulate for now)
|
||||
if self.system_running {
|
||||
self.cycle_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update component running states
|
||||
fn update_component_states(&mut self, system: &ConfigurableSystem) {
|
||||
let components = system.get_components();
|
||||
|
||||
self.component_states.cpu_running = components
|
||||
.get("CPU_4004")
|
||||
.map_or(false, |comp| comp.lock().map_or(false, |c| c.is_running()));
|
||||
|
||||
self.component_states.ram_running = components
|
||||
.get("RAM_4002")
|
||||
.map_or(false, |comp| comp.lock().map_or(false, |c| c.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().map_or(false, |c| c.is_running()));
|
||||
|
||||
self.component_states.clock_running = components
|
||||
.get("SYSTEM_CLOCK")
|
||||
.map_or(false, |comp| comp.lock().map_or(false, |c| c.is_running()));
|
||||
}
|
||||
|
||||
/// 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<crate::system_config::SystemInfo> 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue