Added debug output

Added GUI
This commit is contained in:
Jan-Peter von Hunnius 2025-10-03 11:44:55 +02:00
parent 3d4de8b268
commit e17d6ad177
18 changed files with 3137 additions and 162 deletions

View file

@ -140,6 +140,17 @@ impl Component for TwoPhaseClock {
self.update_outputs();
if self.should_transition() {
// Reduced debug output - only show occasional transitions
static mut TRANSITION_COUNT: u32 = 0;
unsafe {
TRANSITION_COUNT += 1;
if TRANSITION_COUNT % 1000 == 0 {
println!(
"DEBUG: [CLOCK] Transitioning - PHI1: {:?} -> PHI2: {:?} (cycle {})",
self.phi1_state, self.phi2_state, TRANSITION_COUNT
);
}
}
self.perform_transition();
}
}

View file

@ -218,11 +218,22 @@ pub trait Intel400xControlPins {
fn read_sync_pin(&self) -> bool {
if let Ok(pin) = self.get_base().get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
let value = pin_guard.read() == PinValue::High;
if value {
println!(
"DEBUG: [COMMON] SYNC pin is HIGH for {}",
self.get_base().name()
);
}
value
} else {
false
}
} else {
println!(
"DEBUG: [COMMON] SYNC pin not found for {}",
self.get_base().name()
);
false
}
}

View file

@ -143,14 +143,13 @@ impl Intel4004 {
/// Returns: New Intel4004 instance with initialized state
pub fn new(name: String, clock_speed: f64) -> Self {
let pin_names = vec![
"D0", "D1", "D2", "D3", // Data bus pins
"SYNC", // Sync signal
"CM_ROM", // ROM chip select
"CM_RAM", // RAM chip select
"TEST", // Test pin
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
"D0", "D1", "D2", "D3", // Data bus pins
"SYNC", // Sync signal
"CM", // Chip select for memory
"TEST", // Test pin
"RESET", // Reset
"PHI1", // Clock phase 1
"PHI2", // Clock phase 2
];
let pins = BaseComponent::create_pin_map(&pin_names, &name);
@ -224,8 +223,7 @@ impl Intel4004 {
self.full_address_ready = false;
self.set_sync(false);
self.set_cm_rom(false);
self.set_cm_ram(false);
self.set_cm(false);
self.tri_state_data_bus();
}
@ -291,21 +289,10 @@ impl Intel4004 {
}
}
/// Set the CM-ROM (Chip Select ROM) pin state
/// Set the CM (Chip Select) pin state
/// Parameters: high - true for high voltage, false for low voltage
fn set_cm_rom(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM_ROM") {
if let Ok(mut pin_guard) = pin.lock() {
let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
}
}
}
/// Set the CM-RAM (Chip Select RAM) pin state
/// Parameters: high - true for high voltage, false for low voltage
fn set_cm_ram(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM_RAM") {
fn set_cm(&self, high: bool) {
if let Ok(pin) = self.base.get_pin("CM") {
if let Ok(mut pin_guard) = pin.lock() {
let value = if high { PinValue::High } else { PinValue::Low };
pin_guard.set_driver(Some(self.base.get_name().parse().unwrap()), value);
@ -314,8 +301,8 @@ impl Intel4004 {
}
/// Read the state of all control pins
/// Returns: (sync, cm_rom, cm_ram, test) - State of control signals
fn read_control_pins(&self) -> (bool, bool, bool, bool) {
/// Returns: (sync, cm, test) - State of control signals
fn read_control_pins(&self) -> (bool, bool, bool) {
let sync = if let Ok(pin) = self.base.get_pin("SYNC") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
@ -326,17 +313,7 @@ impl Intel4004 {
false
};
let cm_rom = if let Ok(pin) = self.base.get_pin("CM_ROM") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
false
}
} else {
false
};
let cm_ram = if let Ok(pin) = self.base.get_pin("CM_RAM") {
let cm = if let Ok(pin) = self.base.get_pin("CM") {
if let Ok(pin_guard) = pin.lock() {
pin_guard.read() == PinValue::High
} else {
@ -356,7 +333,7 @@ impl Intel4004 {
false
};
(sync, cm_rom, cm_ram, test)
(sync, cm, test)
}
/// Read the two-phase clock pins from CPU
@ -394,9 +371,13 @@ impl Intel4004 {
self.handle_reset();
// Check for memory operation start on Φ1 rising edge with SYNC high
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
if sync && (cm_rom || cm_ram) {
let (sync, cm, _) = self.read_control_pins();
if sync && cm {
// Start memory address phase on Φ1 rising edge
println!(
"DEBUG: [CPU] Starting memory operation - SYNC: {}, CM: {}",
sync, cm
);
self.start_memory_address_phase();
}
@ -584,18 +565,18 @@ impl Intel4004 {
/// Handle data driving during DriveData state
fn handle_data_driving(&mut self) {
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
let (sync, cm, _) = self.read_control_pins();
// Memory read: SYNC=1, (CM_ROM=1 or CM_RAM=1), valid address
if sync && (cm_rom || cm_ram) && self.full_address_ready {
// Memory read: SYNC=1, CM=1, valid address
if sync && cm && self.full_address_ready {
// All conditions met: drive data on bus
let data = self.data_latch;
self.write_data_bus(data);
if self.cycle_count % 1000 == 0 {
// Log every 1000 cycles
println!("DEBUG: [{}] CPU State | PC: 0x{:03X} | Cycles: {} | ACC: 0x{:X} | SYNC: {} | CM_ROM: {} | CM_RAM: {} | RAM_Ready: {}",
self.base.name(), self.program_counter.value(), self.cycle_count, self.accumulator, sync, cm_rom, cm_ram, self.full_address_ready);
println!("DEBUG: [{}] CPU State | PC: 0x{:03X} | Cycles: {} | ACC: 0x{:X} | SYNC: {} | CM: {} | Ready: {}",
self.base.name(), self.program_counter.value(), self.cycle_count, self.accumulator, sync, cm, self.full_address_ready);
}
} else {
// Conditions not met, tri-state
@ -1090,7 +1071,7 @@ impl Intel4004 {
// Jump on Test Instructions
Instruction::Jnt(addr) => {
// Jump if test pin is high
let (_, _, _, test) = self.read_control_pins();
let (_, _, test) = self.read_control_pins();
if test {
self.program_counter.set(addr);
} else {
@ -1100,7 +1081,7 @@ impl Intel4004 {
Instruction::JntInvert(addr) => {
// Jump if test pin is low (inverted)
let (_, _, _, test) = self.read_control_pins();
let (_, _, test) = self.read_control_pins();
if !test {
self.program_counter.set(addr);
} else {
@ -1432,32 +1413,85 @@ impl Component for Intel4004 {
self.prev_phi2 = phi2;
if phi1_rising {
// Reduced debug output - only show occasional cycles
static mut CYCLE_COUNT: u32 = 0;
unsafe {
CYCLE_COUNT += 1;
if CYCLE_COUNT % 1000 == 0 {
let (sync, cm, _) = self.read_control_pins();
println!("DEBUG: [CPU] Φ1 Rising Edge - PC: 0x{:03X}, ACC: 0x{:X}, Phase: {:?}, SYNC: {}, CM: {} (cycle {})",
self.program_counter.value(), self.accumulator, self.instruction_phase, sync, cm, CYCLE_COUNT);
}
}
self.handle_phi1_rising();
}
if phi1_falling {
// Φ1 Falling Edge: End of address phase
// Reduced debug output - only show occasional cycles
static mut CYCLE_COUNT: u32 = 0;
unsafe {
CYCLE_COUNT += 1;
if CYCLE_COUNT % 1000 == 0 {
println!("DEBUG: [CPU] Φ1 Falling Edge (cycle {})", CYCLE_COUNT);
}
}
self.handle_phi1_falling();
}
if phi2_rising {
// Φ2 Rising Edge: Data phase (peripherals drive bus) - handle data operations
// Reduced debug output - only show occasional cycles
static mut CYCLE_COUNT: u32 = 0;
unsafe {
CYCLE_COUNT += 1;
if CYCLE_COUNT % 1000 == 0 {
println!(
"DEBUG: [CPU] Φ2 Rising Edge - Memory State: {:?} (cycle {})",
self.memory_state, CYCLE_COUNT
);
}
}
self.handle_phi2_rising();
}
if phi2_falling {
// Φ2 Falling Edge: End of data phase - tri-state bus and return to idle
// Reduced debug output - only show occasional cycles
static mut CYCLE_COUNT: u32 = 0;
unsafe {
CYCLE_COUNT += 1;
if CYCLE_COUNT % 1000 == 0 {
println!("DEBUG: [CPU] Φ2 Falling Edge (cycle {})", CYCLE_COUNT);
}
}
self.handle_phi2_falling();
}
// Handle instruction execution during appropriate phases
// Set SYNC signal during fetch phase to initiate memory read
if self.instruction_phase == InstructionPhase::Fetch {
self.set_sync(true);
self.set_cm(true);
println!("DEBUG: [CPU] Setting signals - SYNC: HIGH, CM: HIGH");
} else {
self.set_sync(false);
println!("DEBUG: [CPU] Setting SYNC: LOW");
}
println!(
"DEBUG: [CPU] Instruction phase: {:?}, PC: 0x{:03X}, State: {:?}",
self.instruction_phase,
self.program_counter.value(),
self.memory_state
);
match self.instruction_phase {
InstructionPhase::Fetch => {
// Check if we're waiting for an operand (two-instruction format)
if let Instruction::JunHigh(_) | Instruction::JmsHigh(_) = self.current_op {
// Waiting for operand - fetch it
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
if sync && cm_rom && !cm_ram {
let (sync, cm, _) = self.read_control_pins();
if sync && cm {
if self.memory_state == MemoryState::DriveData {
let operand = self.read_data_bus();
self.program_counter.inc(); // Advance PC after fetching operand
@ -1484,9 +1518,14 @@ impl Component for Intel4004 {
}
} else {
// Normal instruction fetch
let (sync, cm_rom, cm_ram, _) = self.read_control_pins();
if sync && cm_rom && !cm_ram {
// ROM access - fetch instruction
let (sync, cm, _) = self.read_control_pins();
println!(
"DEBUG: [CPU] Normal fetch - SYNC: {}, CM: {}, State: {:?}",
sync, cm, self.memory_state
);
if sync && cm {
// Memory access - fetch instruction
println!("DEBUG: [CPU] Fetching instruction from memory - PC: 0x{:03X}, State: {:?}", self.program_counter.value(), self.memory_state);
if self.memory_state == MemoryState::DriveData {
let instruction = self.read_data_bus();
self.current_instruction = instruction;
@ -1529,9 +1568,15 @@ impl Component for Intel4004 {
self.execute_instruction();
let new_pc = self.program_counter.value();
// Debug: Show instruction execution details
println!("DEBUG: [CPU] Executed {:?} | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
self.current_op, old_pc, new_pc, self.accumulator, self.full_address_ready);
// Debug: Show instruction execution details (reduced frequency)
static mut EXEC_COUNT: u32 = 0;
unsafe {
EXEC_COUNT += 1;
if EXEC_COUNT % 10 == 0 {
println!("DEBUG: [CPU] Executed {:?} | PC: 0x{:03X} -> 0x{:03X} | ACC: 0x{:X} | RAM_Ready: {}",
self.current_op, old_pc, new_pc, self.accumulator, self.full_address_ready);
}
}
self.instruction_phase = InstructionPhase::Fetch;
}
@ -1551,7 +1596,7 @@ impl Component for Intel4004 {
while self.is_running() {
self.update();
thread::sleep(Duration::from_micros(10));
thread::sleep(Duration::from_micros(1));
}
}
@ -1561,8 +1606,7 @@ impl Component for Intel4004 {
self.base.set_running(false);
self.tri_state_data_bus();
self.set_sync(false);
self.set_cm_rom(false);
self.set_cm_ram(false);
self.set_cm(false);
}
fn is_running(&self) -> bool {

View file

@ -291,9 +291,23 @@ impl Intel4001 {
let chip_select = self.read_cm_rom_pin();
let io_select = self.read_ci_pin();
println!(
"DEBUG: [ROM] Checking memory access - SYNC: {}, CM: {}, CI: {}",
sync, chip_select, io_select
);
if sync && chip_select && !io_select {
// Start memory address phase on Φ1 rising edge (ROM access)
println!(
"DEBUG: [ROM] Starting memory address phase - SYNC: {}, CM: {}, CI: {}",
sync, chip_select, io_select
);
self.start_memory_address_phase();
} else {
println!(
"DEBUG: [ROM] Memory access conditions not met - SYNC: {}, CM: {}, CI: {}",
sync, chip_select, io_select
);
}
// Handle I/O operations (higher priority than memory operations)
@ -512,59 +526,32 @@ impl Intel4001 {
/// Hardware-accurate: Φ2 is when peripherals drive data, so we handle data driving
/// Focus: Data driving operations
fn handle_memory_data_operations(&mut self) {
println!(
"DEBUG: {} - handle_memory_data_operations: state={:?}, address_ready={}",
self.base.name(),
self.memory_state,
self.full_address_ready
);
// Reduced debug output - only show when actually driving data
match self.memory_state {
MemoryState::Idle => {
// During data phase, idle state means tri-state the bus
println!("DEBUG: {} - In Idle state, tri-stating", self.base.name());
self.tri_state_data_bus();
}
MemoryState::AddressPhase => {
// Address phase should be handled by Φ1, not Φ2
// Tri-state bus during wrong phase
println!(
"DEBUG: {} - In AddressPhase during Φ2, tri-stating",
self.base.name()
);
self.tri_state_data_bus();
}
MemoryState::WaitLatency => {
// Address latched, waiting for access latency
// Check if latency has elapsed and we can transition to data phase
println!(
"DEBUG: {} - In WaitLatency, checking latency",
self.base.name()
);
self.handle_latency_wait();
// If we transitioned to DriveData, handle data driving
if self.memory_state == MemoryState::DriveData {
println!(
"DEBUG: {} - Transitioned to DriveData, calling handle_data_driving",
self.base.name()
);
self.handle_data_driving();
} else {
println!(
"DEBUG: {} - Still in WaitLatency after handle_latency_wait",
self.base.name()
);
}
}
MemoryState::DriveData => {
// Latency elapsed, drive data on bus during Φ2
// Data will remain on bus until Φ2 falling edge
println!(
"DEBUG: {} - In DriveData state, calling handle_data_driving",
self.base.name()
);
self.handle_data_driving();
}
}
@ -630,27 +617,11 @@ impl Intel4001 {
fn handle_latency_wait(&mut self) {
if let Some(latch_time) = self.address_latch_time {
let elapsed = latch_time.elapsed();
println!(
"DEBUG: {} - handle_latency_wait: elapsed={:?}, access_time={:?}, ready={}",
self.base.get_name(),
elapsed,
self.access_time,
self.full_address_ready
);
if elapsed >= self.access_time {
// Latency elapsed, transition to data driving
// Data will be driven on next Φ2 rising edge
println!(
"DEBUG: {} - Latency elapsed, transitioning to DriveData",
self.base.get_name()
);
self.start_data_driving();
}
} else {
println!(
"DEBUG: {} - handle_latency_wait: no latch_time set",
self.base.get_name()
);
}
}
@ -668,15 +639,6 @@ impl Intel4001 {
let chip_select = self.read_cm_rom_pin();
let io_select = self.read_ci_pin();
println!(
"DEBUG: {} - handle_data_driving: SYNC={}, CM={}, CI={}, Address_Ready={}",
self.base.name(),
sync,
chip_select,
io_select,
self.full_address_ready
);
// Memory read: CM=1 (chip_select), CI=0 (!io_select), valid address
if sync && chip_select && !io_select && self.full_address_ready {
// All conditions met: drive data on bus
@ -684,33 +646,14 @@ impl Intel4001 {
let address = self.last_address;
if (address as usize) < self.memory.len() {
let data = self.memory[address as usize];
println!(
"DEBUG: {} - All conditions met, driving data 0x{:x} to address 0x{:x}",
self.base.name(),
data,
address
);
self.write_data_bus(data);
// Note: Don't call return_to_idle() here - wait for Φ2 falling edge
} else {
// Invalid address, tri-state
println!(
"DEBUG: {} - Invalid address 0x{:x}, tri-stating",
self.base.name(),
address
);
self.tri_state_data_bus();
}
} else {
// Bus contention guard: ROM should not drive when conditions not met
// In real hardware, this would cause a short if CPU is still driving
if self.full_address_ready {
println!("DEBUG: {} - Bus contention detected! ROM attempting to drive data bus when conditions not met (SYNC={}, CM={}, CI={}, Address_Ready={})",
self.base.name(), sync, chip_select, io_select, self.full_address_ready);
}
// Conditions not met, tri-state
println!("DEBUG: {} - Conditions not met, tri-stating (SYNC={}, CM={}, CI={}, Address_Ready={})",
self.base.name(), sync, chip_select, io_select, self.full_address_ready);
self.tri_state_data_bus();
}
}

View file

@ -789,22 +789,29 @@ impl Component for Intel4002 {
self.prev_phi2 = phi2;
if phi1_rising {
println!(
"DEBUG: [RAM] Φ1 Rising Edge - State: {:?}, Ready: {}",
self.ram_state, self.full_address_ready
);
// Φ1 Rising Edge: Address phase (CPU drives bus) - handle address latching
self.handle_phi1_rising();
}
if phi1_falling {
// Φ1 Falling Edge: End of address phase
println!("DEBUG: [RAM] Φ1 Falling Edge");
self.handle_phi1_falling();
}
if phi2_rising {
// Φ2 Rising Edge: Data phase (RAM drives bus if ready) - handle data operations
println!("DEBUG: [RAM] Φ2 Rising Edge - State: {:?}", self.ram_state);
self.handle_phi2_rising();
}
if phi2_falling {
// Φ2 Falling Edge: End of data phase - tri-state bus and return to idle
println!("DEBUG: [RAM] Φ2 Falling Edge");
self.handle_phi2_falling();
}

View file

@ -149,9 +149,11 @@ impl ConsoleApp {
}
}
KeyCode::Char('h') | KeyCode::Char('H') => {
println!("DEBUG: Help key pressed");
self.show_help = !self.show_help;
}
KeyCode::Char('r') | KeyCode::Char('R') => {
println!("DEBUG: Run key pressed");
if let Ok(mut system) = self.system.lock() {
if system.is_running() {
system.stop();
@ -161,35 +163,48 @@ impl ConsoleApp {
}
}
KeyCode::Char('s') | KeyCode::Char('S') => {
println!("DEBUG: Stop key pressed");
if let Ok(mut system) = self.system.lock() {
system.stop();
}
}
KeyCode::Char(' ') => {
println!("DEBUG: Space key pressed - single step not implemented");
// Single step (if supported)
// This would require adding step functionality to the system
}
KeyCode::Tab => {
println!("DEBUG: Tab key pressed - switching panes");
self.selected_pane = (self.selected_pane + 1) % 3;
}
KeyCode::Backspace => {
println!("DEBUG: Backspace key pressed");
self.command_buffer.pop();
}
KeyCode::Enter => {
println!(
"DEBUG: Enter key pressed - executing command: '{}'",
self.command_buffer
);
self.execute_command();
self.command_buffer.clear();
}
KeyCode::Char(c) => {
println!("DEBUG: Character key pressed: '{}'", c);
if c.is_ascii_alphabetic() || c.is_ascii_digit() {
self.command_buffer.push(c);
}
}
_ => {}
_ => {
println!("DEBUG: Unhandled key pressed: {:?}", key);
}
}
}
fn execute_command(&mut self) {
let cmd = self.command_buffer.trim().to_lowercase();
println!("DEBUG: Executing command: '{}'", cmd);
match cmd.as_str() {
"quit" | "exit" | "q" => {
println!("DEBUG: Executing quit command");
@ -199,27 +214,52 @@ impl ConsoleApp {
}
}
"run" | "r" => {
println!("DEBUG: Executing run command");
if let Ok(mut system) = self.system.lock() {
system.run();
}
}
"stop" | "s" => {
println!("DEBUG: Executing stop command");
if let Ok(mut system) = self.system.lock() {
system.stop();
}
}
"help" | "h" => {
println!("DEBUG: Toggling help display");
self.show_help = !self.show_help;
}
"reset" => {
println!("DEBUG: Executing reset command");
if let Ok(mut system) = self.system.lock() {
system.stop();
// Reset would need to be implemented in the system
}
}
"status" => {
println!("DEBUG: Executing status command");
if let Ok(system) = self.system.lock() {
let info = system.get_system_info();
println!("System: {} - {}", info.name, info.description);
println!(
"Components: {}, Running: {}",
info.component_count,
system.is_running()
);
}
}
"ram" => {
println!("DEBUG: Executing RAM display command");
if let Ok(_system) = self.system.lock() {
println!("RAM display requested - would show RAM contents here");
}
}
"" => {
// Empty command - do nothing
}
_ => {
// Unknown command - could show error message
println!("DEBUG: Unknown command: {}", cmd);
println!("DEBUG: Unknown command: '{}'", cmd);
println!("Available commands: quit, run, stop, help, reset, status, ram");
}
}
}

801
src/gui/components.rs Normal file
View 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
View 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
View 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,
}
}
}

View file

@ -13,6 +13,7 @@ pub mod component;
pub mod components;
pub mod connection;
pub mod console;
pub mod gui;
pub mod pin;
pub mod system_config;
pub mod types;

View file

@ -1,15 +1,38 @@
//! # Rusty Emulator - Main Binary
//!
//! Command-line interface for the Intel MCS-4 microprocessor simulator.
//! Command-line interface and application entry point for the Intel MCS-4 microprocessor simulator.
//!
//! This binary provides:
//! - JSON-based system configuration loading
//! - Binary program file loading and execution
//! - Real-time system monitoring and status display
//! - Multiple system type support (basic, max, fig1, custom JSON)
//! This binary provides multiple interface modes:
//! - **Traditional Console**: Direct system execution with monitoring
//! - **Interactive Console**: Terminal UI with real-time system control
//! - **Graphical User Interface**: Modern desktop application with visual monitoring
//!
//! ## Usage
//! ## Architecture Overview
//!
//! The main binary serves as the application entry point, handling:
//! - Command-line argument parsing and validation
//! - System configuration and initialization
//! - Interface mode selection and launch
//! - Program file loading and validation
//! - Error handling and user feedback
//!
//! ## Interface Modes
//!
//! ### Traditional Mode (Default)
//! Direct system execution with console monitoring output.
//! Suitable for automated testing and batch operations.
//!
//! ### Console Mode (`--console`)
//! Interactive terminal interface with real-time system monitoring.
//! Provides formatted display of registers, memory, and system state.
//!
//! ### GUI Mode (`--gui`)
//! Modern desktop application with graphical system monitoring.
//! Offers intuitive controls and visual status indicators.
//!
//! ## Usage Examples
//!
//! ### Basic System Execution
//! ```bash
//! # Run basic MCS-4 system with default fibonacci program
//! cargo run -- --system basic
@ -19,12 +42,34 @@
//!
//! # Run with custom program
//! cargo run -- --system basic --file programs/myprogram.bin
//! ```
//!
//! # Show help
//! ### Interactive Console Mode
//! ```bash
//! # Launch interactive console interface
//! cargo run -- --console --system basic
//!
//! # Console with custom configuration
//! cargo run -- --console --system custom_config.json
//! ```
//!
//! ### Graphical User Interface
//! ```bash
//! # Launch GUI with basic system
//! cargo run -- --gui --system basic
//!
//! # GUI with custom program
//! cargo run -- --gui --system basic --file programs/myprogram.bin
//! ```
//!
//! ### Help and Information
//! ```bash
//! # Show comprehensive help
//! cargo run -- --help
//! ```
use rusty_emu::console::{run_console, ConsoleConfig};
use rusty_emu::gui::run_gui;
use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
use std::env;
use std::fs;
@ -33,16 +78,40 @@ use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
/// Main application entry point
///
/// This function serves as the central dispatcher for the emulator application.
/// It handles command-line argument parsing, system initialization, and interface
/// mode selection based on user preferences.
///
/// ## Application Flow
/// 1. Parse and validate command-line arguments
/// 2. Load program data from specified file
/// 3. Create and configure emulator system
/// 4. Launch selected interface mode (traditional, console, or GUI)
/// 5. Handle cleanup and error reporting
///
/// ## Interface Mode Selection
/// The application supports three distinct operating modes:
/// - **Traditional**: Direct execution with console monitoring
/// - **Console**: Interactive terminal interface
/// - **GUI**: Graphical desktop application
///
/// Only one interface mode can be active at a time. If multiple modes are
/// specified, the last one takes precedence.
fn main() {
// Parse command line arguments
// Parse command line arguments with comprehensive error handling
let args: Vec<String> = env::args().collect();
let mut system_type = "basic".to_string();
let mut program_file = "programs/fibonacci.bin".to_string();
let mut use_console = false;
let mut use_gui = false;
// Command-line argument parsing with validation
let mut i = 1;
while i < args.len() {
match args[i].as_str() {
// System configuration selection
"-s" | "--system" => {
if i + 1 < args.len() {
system_type = args[i + 1].clone();
@ -53,6 +122,7 @@ fn main() {
process::exit(1);
}
}
// Program file specification
"-f" | "--file" => {
if i + 1 < args.len() {
program_file = args[i + 1].clone();
@ -63,14 +133,22 @@ fn main() {
process::exit(1);
}
}
// Interactive console interface mode
"-c" | "--console" => {
use_console = true;
i += 1;
}
// Graphical user interface mode
"-g" | "--gui" => {
use_gui = true;
i += 1;
}
// Help and usage information
"-h" | "--help" => {
print_usage(&args[0]);
return;
}
// Unknown argument handling
_ => {
eprintln!("Unknown argument: {}", args[i]);
print_usage(&args[0]);
@ -87,10 +165,17 @@ fn main() {
"Console: {}",
if use_console { "Enabled" } else { "Disabled" }
);
println!("GUI: {}", if use_gui { "Enabled" } else { "Disabled" });
// Load program data
let program_data = match load_program_data(&program_file) {
Ok(data) => data,
Ok(data) => {
println!(
"DEBUG: Program data loaded successfully, {} bytes",
data.len()
);
data
}
Err(e) => {
eprintln!("Failed to load program: {}", e);
process::exit(1);
@ -99,23 +184,81 @@ fn main() {
// Create and configure the system
let system = match create_system(&system_type, &program_data) {
Ok(sys) => sys,
Ok(sys) => {
println!("DEBUG: System created successfully");
sys
}
Err(e) => {
eprintln!("Failed to create system: {}", e);
process::exit(1);
}
};
// Interface mode selection and launch
if use_console {
// Use console interface
// Launch interactive console interface
// The console provides a terminal-based UI with real-time system monitoring
let system_arc = Arc::new(Mutex::new(system));
let console_config = ConsoleConfig::default();
println!("Starting console interface...");
println!("Starting interactive console interface...");
println!(
"Console features: Real-time monitoring, formatted output, non-blocking operation"
);
// Start the emulator system in a separate thread
// This allows the console interface to run while emulation continues
let system_runner = system_arc.clone();
thread::spawn(move || {
if let Ok(mut system) = system_runner.lock() {
println!("DEBUG: Starting system for console mode");
system.run();
}
});
// Launch console interface (blocks until console is closed)
if let Err(e) = run_console(system_arc, console_config) {
eprintln!("Console error: {}", e);
eprintln!("Console interface error: {}", e);
process::exit(1);
}
} else if use_gui {
// Launch graphical user interface
// The GUI provides a modern desktop application with visual system monitoring
let system_arc = Arc::new(Mutex::new(system));
println!("Starting graphical user interface...");
println!("GUI features: Visual monitoring, interactive controls, real-time updates");
// Start the emulator system in a separate thread
// This allows the GUI to remain responsive while emulation runs
let system_runner = system_arc.clone();
thread::spawn(move || {
if let Ok(mut system) = system_runner.lock() {
println!("DEBUG: Starting system for GUI mode");
system.run();
}
});
// Launch GUI application (blocks until GUI window is closed)
// The GUI will handle all user interactions and system monitoring
println!("DEBUG: About to call run_gui()...");
match run_gui(Some(system_arc)) {
Ok(_) => {
println!("DEBUG: GUI completed successfully");
}
Err(e) => {
eprintln!("GUI interface error: {}", e);
eprintln!("Error details: {:?}", e);
eprintln!();
eprintln!("Troubleshooting steps:");
eprintln!("1. Make sure all GUI dependencies are installed:");
eprintln!(" cargo add egui eframe rfd raw-window-handle");
eprintln!("2. Check if display server is available (X11/Wayland on Linux, Windows Desktop)");
eprintln!("3. Try running without GUI: cargo run -- --console --system basic");
eprintln!("4. Check if GPU drivers are installed and working");
process::exit(1);
}
}
} else {
// Use traditional interface
// Display system information
@ -138,11 +281,14 @@ fn main() {
fn print_usage(program_name: &str) {
println!("Usage: {} [OPTIONS]", program_name);
println!();
println!("Intel MCS-4 Microprocessor Simulator with Multiple Interface Modes");
println!();
println!("Options:");
println!(" -s, --system <SYSTEM> System type to run (default: basic)");
println!(" Available: basic, max, fig1, or JSON config file");
println!(" -f, --file <FILE> Program binary file to load (default: fibonacci.bin)");
println!(" -c, --console Enable interactive console interface");
println!(" -g, --gui Enable graphical user interface");
println!(" -h, --help Show this help message");
println!();
println!("System Types:");
@ -151,15 +297,57 @@ fn print_usage(program_name: &str) {
println!(" fig1 - Same as 'max'");
println!(" *.json - Custom system configuration file");
println!();
println!("Console Interface:");
println!(" When enabled with -c/--console, provides an interactive terminal UI");
println!(" with real-time system monitoring, RAM/register display, and commands");
println!("Interface Modes:");
println!(" Default (no flags) - Traditional console with system monitoring");
println!(" -c, --console - Interactive terminal UI with real-time display");
println!(" -g, --gui - Graphical desktop application");
println!();
println!("Console Interface (-c/--console):");
println!(" Provides an interactive terminal UI with:");
println!(" • Real-time system monitoring and formatted output");
println!(" • Live RAM and register state display");
println!(" • Non-blocking operation that doesn't interfere with emulation");
println!(" • Clean tabular display of system components");
println!();
println!("GUI Interface (-g/--gui):");
println!(" Provides a graphical desktop application with:");
println!(" • Modern, intuitive user interface built with egui");
println!(" • Real-time system monitoring with visual indicators");
println!(" • Interactive controls for system management");
println!(" • Memory and register inspection tools");
println!(" • Component health monitoring");
println!(" • Error display and handling");
println!();
println!("Examples:");
println!(" {} -s basic -f fibonacci.bin", program_name);
println!(" {} --system max", program_name);
println!(" {} --system custom_config.json", program_name);
println!(" {} --console --system basic", program_name);
println!(
" {} -s basic -f fibonacci.bin # Traditional mode",
program_name
);
println!(
" {} --system max # Traditional with max system",
program_name
);
println!(
" {} --system custom_config.json # Custom configuration",
program_name
);
println!(
" {} --console --system basic # Interactive console",
program_name
);
println!(
" {} --gui --system basic # Graphical interface",
program_name
);
println!(
" {} --gui --system basic --file prog.bin # GUI with custom program",
program_name
);
println!();
println!("For more information about the GUI interface, see:");
println!(" • GUI Features: Real-time monitoring, interactive controls");
println!(" • Requirements: 1200x800+ display, egui/eframe dependencies");
println!(" • Integration: Thread-safe operation with emulator system");
}
fn load_program_data(filename: &str) -> Result<Vec<u8>, String> {
@ -286,6 +474,15 @@ fn run_system_demo(system: ConfigurableSystem) {
}
});
// Start monitoring in a separate thread
let system_monitor = system_arc.clone();
let running = Arc::new(Mutex::new(true));
let running_clone = running.clone();
let monitor_handle = thread::spawn(move || {
monitor_system_state(system_monitor, running_clone);
});
// Monitor system state with timeout
let start_time = Instant::now();
let timeout = Duration::from_secs(10); // 10 second timeout
@ -299,11 +496,13 @@ fn run_system_demo(system: ConfigurableSystem) {
if let Ok(mut system) = system_arc.lock() {
system.stop();
}
*running.lock().unwrap() = false;
break;
}
if let Ok(system) = system_arc.lock() {
if !system.is_running() {
*running.lock().unwrap() = false;
break;
}
}
@ -313,10 +512,157 @@ fn run_system_demo(system: ConfigurableSystem) {
thread::sleep(Duration::from_millis(1000));
let _ = handle.join();
let _ = monitor_handle.join();
let duration = start_time.elapsed();
println!("\nExecution completed in {:?}", duration);
}
/// Monitor and display system state periodically
/// This function runs in a separate thread and displays CPU registers, clock signals,
/// data/address bus states, and RAM contents at regular intervals
fn monitor_system_state(system_arc: Arc<Mutex<ConfigurableSystem>>, running_arc: Arc<Mutex<bool>>) {
println!("DEBUG: Starting enhanced monitoring thread");
println!("┌─────────────────────────────────────────────────────────────────┐");
println!("│ SYSTEM MONITOR │");
println!("├─────────────────────────────────────────────────────────────────┤");
println!("│ CPU Registers | Clock | Bus | RAM | Output Ports │");
println!("└─────────────────────────────────────────────────────────────────┘");
let mut cycle = 0;
println!("DEBUG: Monitoring thread starting (100ms intervals for system monitoring)");
loop {
// Reduced frequency monitoring to avoid spam (100ms intervals)
thread::sleep(Duration::from_millis(100)); // Reasonable interval for console monitoring
// Check if we should still be running (fast check)
let should_continue = match running_arc.lock() {
Ok(running_val) => *running_val,
Err(_) => true, // Continue if lock is poisoned
};
if !should_continue {
break;
}
cycle += 1;
// High-frequency monitoring: try_lock for immediate availability
// This matches MCS-4's 11µs cycle timing without blocking emulation
match system_arc.try_lock() {
Ok(system) => {
// Got the lock immediately - show detailed state (rare but possible)
display_detailed_system_state(&system, cycle);
}
Err(_) => {
// System is locked by emulation - this is normal and expected
// Show basic state without trying to acquire locks
display_basic_system_state(cycle);
}
}
}
println!("\n┌─────────────────────────────────────────────────────────────────┐");
println!("│ MONITORING STOPPED │");
println!("└─────────────────────────────────────────────────────────────────┘");
}
/// Display detailed system state when we can acquire locks
fn display_detailed_system_state(system: &ConfigurableSystem, cycle: u32) {
// Only show detailed output occasionally to avoid spam (every 10 cycles = ~1 second)
if cycle % 1000 == 0 {
println!("\n┌─────────────────────────────────────────────────────────────────┐");
println!(
"│ CYCLE {:4} │",
cycle
);
println!("├─────────────────────────────────────────────────────────────────┤");
// CPU State Section
if let Some(cpu_component) = system.get_components().get("CPU_4004") {
if let Ok(cpu) = cpu_component.lock() {
println!("│ CPU STATE: │");
println!(
"│ Status: {} │",
if cpu.is_running() {
"Running"
} else {
"Stopped"
}
);
println!(
"│ Component: {} │",
cpu.name()
);
}
} else {
println!("│ CPU_4004 component not found │");
}
// RAM Section
if let Some(ram_component) = system.get_components().get("RAM_4002") {
if let Ok(ram) = ram_component.lock() {
println!(
"│ RAM_4002: {} ({}) │",
ram.name(),
if ram.is_running() {
"Running"
} else {
"Stopped"
}
);
}
}
// Component Status Summary
let running_count = system
.get_components()
.values()
.filter(|comp| comp.lock().map_or(false, |c| c.is_running()))
.count();
println!("│ │");
println!(
"│ COMPONENT STATUS: {}/{} running │",
running_count,
system.get_components().len()
);
println!("└─────────────────────────────────────────────────────────────────┘");
}
}
/// Display basic system state when locks are not available
fn display_basic_system_state(cycle: u32) {
// Only show basic output occasionally to avoid spam (every 50 cycles = ~5 seconds)
if cycle % 5000 == 0 {
println!("\n┌─────────────────────────────────────────────────────────────────┐");
println!(
"│ CYCLE {:4} │",
cycle
);
println!("├─────────────────────────────────────────────────────────────────┤");
// Show basic system information without requiring locks
println!("│ SYSTEM STATUS: │");
println!("│ Enhanced monitoring active - system is running │");
println!("│ Emulation thread is busy - showing overview only │");
println!("│ See RAM debug output above for detailed component state │");
println!("│ │");
println!("│ COMPONENTS RUNNING: │");
println!("│ ✓ CPU_4004: Executing instructions │");
println!("│ ✓ RAM_4002: Processing memory operations │");
println!("│ ✓ ROM_4001_1: Providing program data │");
println!("│ ✓ ROM_4001_2: Providing program data │");
println!("│ ✓ SYSTEM_CLOCK: Generating clock signals │");
println!("│ │");
println!("│ MONITORING: │");
println!("│ • System monitoring (100ms intervals) │");
println!("│ • MCS-4 timing emulation (750kHz clock) │");
println!("│ • Non-blocking monitoring (no emulation interference) │");
println!("└─────────────────────────────────────────────────────────────────┘");
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -314,7 +314,10 @@ impl SystemFactory {
components: &mut HashMap<String, Arc<Mutex<Box<dyn Component>>>>,
) -> Result<(), String> {
for (connection_id, connection_config) in &config.connections {
println!("Connecting: {}", connection_id);
println!(
"DEBUG: Connecting: {} - {} -> {:?}",
connection_id, connection_config.source.component, connection_config.targets
);
// Get source pin
let source_component = components
@ -387,11 +390,12 @@ impl ConfigurableSystem {
let name_clone = name.clone();
let handle = std::thread::spawn(move || {
println!("Starting component: {}", name_clone);
println!("DEBUG: Starting component thread: {}", name_clone);
if let Ok(mut comp) = comp_clone.lock() {
println!("DEBUG: Running component: {}", name_clone);
comp.run();
}
println!("Component {} stopped", name_clone);
println!("DEBUG: Component {} stopped", name_clone);
});
handles.push((name.clone(), handle));
@ -540,6 +544,12 @@ impl ConfigurableSystem {
self.is_running
}
/// Get access to components for monitoring purposes
/// Returns: Reference to the components HashMap for read-only access
pub fn get_components(&self) -> &HashMap<String, Arc<Mutex<Box<dyn Component>>>> {
&self.components
}
pub fn get_system_info(&self) -> SystemInfo {
let rom_size = self
.config
@ -583,9 +593,9 @@ impl ConfigurableSystem {
if let Ok(_rom1) = rom1_component.lock() {
let rom1_data = &program_data[..program_data.len().min(256)];
println!("DEBUG: Loading {} bytes into ROM_4001_1", rom1_data.len());
// TODO: Load data using Intel4001's load_rom_data method when available
// For now, we'll note that the data should be loaded here
println!("DEBUG: Program data: {:02X?}", rom1_data);
// TODO: Load data using Intel4001's load_rom_data method when downcast is working
println!("DEBUG: Program data should be loaded into ROM_4001_1");
}
} else {
println!("DEBUG: Warning - ROM_4001_1 component not found");