rusty_emu/src/main.rs

676 lines
28 KiB
Rust

//! # Rusty Emulator - Main Binary
//!
//! Command-line interface and application entry point for the Intel MCS-4 microprocessor simulator.
//!
//! 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
//!
//! ## 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
//!
//! # Run Fig.1 MCS-4 Max system
//! cargo run -- --system max
//!
//! # Run with custom program
//! cargo run -- --system basic --file programs/myprogram.bin
//! ```
//!
//! ### 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::gui::console::{run_console, ConsoleConfig};
use rusty_emu::gui::run_gui;
use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
use std::env;
use std::fs;
use std::process;
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 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();
i += 2;
} else {
eprintln!("Error: --system requires a value");
print_usage(&args[0]);
process::exit(1);
}
}
// Program file specification
"-f" | "--file" => {
if i + 1 < args.len() {
program_file = args[i + 1].clone();
i += 2;
} else {
eprintln!("Error: --file requires a value");
print_usage(&args[0]);
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]);
process::exit(1);
}
}
}
println!("Intel MCS-4 Emulator");
println!("===================");
println!("System: {}", system_type);
println!("Program: {}", program_file);
println!(
"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) => {
println!(
"DEBUG: Program data loaded successfully, {} bytes",
data.len()
);
data
}
Err(e) => {
eprintln!("Failed to load program: {}", e);
process::exit(1);
}
};
// Create and configure the system
let system = match create_system(&system_type, &program_data) {
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 {
// 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 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 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
let info = system.get_system_info();
println!("\nSystem Information:");
println!(" CPU Speed: {} Hz", info.cpu_speed);
println!(" ROM Size: {} bytes", info.rom_size);
println!(" RAM Size: {} nibbles", info.ram_size);
println!(" Components: {}", info.component_count);
println!("\nStarting execution...");
println!("Press Ctrl+C to stop execution");
println!();
// Run the system
run_system_demo(system);
}
}
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:");
println!(" basic - Basic MCS-4 system (CPU, clock, 2 ROMs, 1 RAM)");
println!(" max - Fig.1 MCS-4 Max system (16 ROMs, 16 RAMs, shift registers)");
println!(" fig1 - Same as 'max'");
println!(" *.json - Custom system configuration file");
println!();
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 # 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> {
println!("DEBUG: Attempting to load program from: {}", filename);
match fs::read(filename) {
Ok(data) => {
println!(
"DEBUG: Successfully loaded {} bytes from {}",
data.len(),
filename
);
Ok(data)
}
Err(e) => {
// If file doesn't exist, try to use default program
if filename == "programs/fibonacci.bin" {
println!(
"DEBUG: File {} not found ({}), using default fibonacci program",
filename, e
);
let default_program = get_default_fibonacci_program();
println!(
"DEBUG: Default program size: {} bytes",
default_program.len()
);
Ok(default_program)
} else {
println!("DEBUG: Failed to read file {}: {}", filename, e);
Err(format!("Failed to read file {}: {}", filename, e))
}
}
}
}
fn get_default_fibonacci_program() -> Vec<u8> {
// Fibonacci program for Intel 4004
// This implements a simple Fibonacci sequence generator
vec![
0x20, 0x00, // LDM 0 (Load accumulator with 0) - First Fibonacci number
0x10, 0x00, // LD 0 (Load accumulator from register 0)
0x50, // WRM (Write accumulator to RAM at current pointer)
0x21, 0x01, // LDM 1 (Load accumulator with 1) - Second Fibonacci number
0x10, 0x01, // LD 1 (Load accumulator from register 1)
0x50, // WRM (Write accumulator to RAM)
// Fibonacci calculation loop
0x00, 0x02, // LD 2 (Load register 2 into accumulator) - Loop counter
0x76, // IAC (Increment accumulator)
0x00, 0x02, // LD 2 (Store back to register 2)
// Calculate next Fibonacci number: F(n) = F(n-1) + F(n-2)
0x00, 0x00, // LD 0 (Load F(n-2) into accumulator)
0x10, 0x01, // ADD 1 (Add F(n-1) to accumulator)
0x50, // WRM (Store result to RAM)
// Update registers for next iteration
0x00, 0x01, // LD 1 (Load F(n-1) into accumulator)
0x00, 0x00, // LD 0 (Store F(n-1) to register 0)
0x50, // WRM (Write to RAM - this will be read back as F(n-2))
// Check if we've calculated enough numbers (8 iterations)
0x00, 0x02, // LD 2 (Load loop counter)
0x30, 0x08, // JCN 8 (Jump if accumulator == 8) - Exit condition
0x40, 0x0C, // JCN 12 (Jump back to loop start)
// Exit - halt program
0x00, 0x00, // NOP (placeholder for halt)
]
}
fn create_system(system_type: &str, program_data: &[u8]) -> Result<ConfigurableSystem, String> {
let factory = SystemFactory::new();
match system_type {
"mcs4" | "basic" => {
// Use the basic MCS-4 configuration
let mut system = factory
.create_from_json("configs/mcs4_basic.json")
.map_err(|e| format!("Failed to create basic MCS-4 system: {}", e))?;
// Load program data into ROM components
system.load_program_data(program_data)?;
Ok(system)
}
"mcs4_max" | "max" | "fig1" => {
// Use the Fig.1 MCS-4 Max configuration
let mut system = factory
.create_from_json("configs/mcs4_max.json")
.map_err(|e| format!("Failed to create MCS-4 Max system: {}", e))?;
// Load program data into ROM components
system.load_program_data(program_data)?;
Ok(system)
}
_ => {
// Try to use provided config file directly
if system_type.ends_with(".json") {
let mut system = factory.create_from_json(system_type).map_err(|e| {
format!("Failed to create system from '{}': {}", system_type, e)
})?;
// Load program data into ROM components
system.load_program_data(program_data)?;
Ok(system)
} else {
Err(format!("Unknown system type: {}. Use 'basic', 'max', or provide a JSON config file path.", system_type))
}
}
}
}
fn run_system_demo(system: ConfigurableSystem) {
// Display system information
let info = system.get_system_info();
println!("System: {} - {}", info.name, info.description);
println!("Components: {}", info.component_count);
println!("CPU Speed: {} Hz", info.cpu_speed);
// Run system in a separate thread
let system_arc = std::sync::Arc::new(std::sync::Mutex::new(system));
let system_clone = system_arc.clone();
let handle = thread::spawn(move || {
if let Ok(mut system) = system_clone.lock() {
system.run();
}
});
// 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
loop {
thread::sleep(Duration::from_millis(500));
// Check for timeout
if start_time.elapsed() >= timeout {
println!("\nSimulation timed out after 10 seconds - stopping system");
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;
}
}
}
// Wait a bit for components to stop gracefully
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::*;
#[test]
fn test_fibonacci_program() {
let program = get_default_fibonacci_program();
assert!(!program.is_empty());
println!("Fibonacci program size: {} bytes", program.len());
}
}