diff --git a/src/console.rs b/src/console.rs deleted file mode 100644 index 951ae52..0000000 --- a/src/console.rs +++ /dev/null @@ -1,515 +0,0 @@ -//! # Console Interface Module -//! -//! Interactive terminal-based user interface for the Intel MCS-4 emulator. -//! Provides real-time system monitoring, debugging capabilities, and user controls. -//! -//! ## Features -//! - Real-time RAM and register display -//! - Interactive command interface -//! - System state monitoring -//! - Configurable display options -//! - Graceful interrupt handling - -use crossterm::{ - event::{self, Event, KeyCode}, - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, -}; -use ratatui::{ - backend::CrosstermBackend, - layout::{Constraint, Direction, Layout, Rect}, - style::{Color, Modifier, Style}, - text::{Line, Span}, - widgets::{Block, Borders, Paragraph, Wrap}, - Frame, Terminal, -}; -use serde::{Deserialize, Serialize}; -use std::io; -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::{Duration, Instant}; - -use crate::system_config::ConfigurableSystem; - -/// Console configuration structure -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConsoleConfig { - pub enabled: bool, - pub refresh_rate_ms: u64, - pub show_ram: bool, - pub show_registers: bool, - pub show_system_info: bool, - pub ram_banks_per_row: usize, - pub max_ram_rows: usize, -} - -impl Default for ConsoleConfig { - fn default() -> Self { - Self { - enabled: true, - refresh_rate_ms: 100, - show_ram: true, - show_registers: true, - show_system_info: true, - ram_banks_per_row: 4, - max_ram_rows: 5, - } - } -} - -/// Console UI application state -pub struct ConsoleApp { - system: Arc>, - config: ConsoleConfig, - running: bool, - command_buffer: String, - show_help: bool, - selected_pane: usize, -} - -impl ConsoleApp { - pub fn new(system: Arc>, config: ConsoleConfig) -> Self { - Self { - system, - config, - running: false, - command_buffer: String::new(), - show_help: false, - selected_pane: 0, - } - } - - pub fn run(&mut self) -> Result<(), Box> { - // Setup terminal - enable_raw_mode().map_err(|e| format!("Failed to enable raw mode: {}", e))?; - let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen) - .map_err(|e| format!("Failed to enter alternate screen: {}", e))?; - - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - - self.running = true; - let mut last_draw = Instant::now(); - - // Main event loop - while self.running { - let now = Instant::now(); - - // Handle input - if let Ok(true) = event::poll(Duration::from_millis(10)) { - if let Ok(Event::Key(key)) = event::read() { - self.handle_key_event(key.code); - } - } - - // Update display at regular intervals - if now.duration_since(last_draw) >= Duration::from_millis(self.config.refresh_rate_ms) { - if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { - eprintln!("DEBUG: Failed to draw UI: {}", e); - break; - } - last_draw = now; - } - - // Check if system should still be running - if let Ok(system) = self.system.lock() { - if !system.is_running() && !self.show_help { - // System stopped, show final state briefly - if let Err(e) = terminal.draw(|f| self.draw_ui(f)) { - eprintln!("DEBUG: Failed to draw final UI: {}", e); - } - thread::sleep(Duration::from_millis(500)); - break; - } - } - - // Small delay to prevent busy waiting - thread::sleep(Duration::from_millis(1)); - } - - // Restore terminal - disable_raw_mode().map_err(|e| format!("Failed to disable raw mode: {}", e))?; - execute!(terminal.backend_mut(), LeaveAlternateScreen) - .map_err(|e| format!("Failed to leave alternate screen: {}", e))?; - terminal - .show_cursor() - .map_err(|e| format!("Failed to show cursor: {}", e))?; - - Ok(()) - } - - fn handle_key_event(&mut self, key: KeyCode) { - match key { - KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => { - println!("DEBUG: Quit key pressed, stopping console"); - self.running = false; - if let Ok(mut system) = self.system.lock() { - system.stop(); - } - } - 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(); - } else { - system.run(); - } - } - } - 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"); - self.running = false; - if let Ok(mut system) = self.system.lock() { - system.stop(); - } - } - "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 - } - _ => { - println!("DEBUG: Unknown command: '{}'", cmd); - println!("Available commands: quit, run, stop, help, reset, status, ram"); - } - } - } - - fn draw_ui(&self, f: &mut Frame) { - let size = f.size(); - - if self.show_help { - self.draw_help_screen(f); - return; - } - - // Create main layout with proper constraints - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(4), // Title bar - Constraint::Min(8), // Main content - Constraint::Length(3), // Command bar - ]) - .split(size); - - // Title bar - let title_text = vec![ - Line::from(vec![Span::styled( - "Intel MCS-4 Emulator Console", - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD), - )]), - Line::from(vec![ - Span::raw("Commands: "), - Span::styled("q/Q", Style::default().fg(Color::Yellow)), - Span::raw("=quit, "), - Span::styled("r/R", Style::default().fg(Color::Yellow)), - Span::raw("=run, "), - Span::styled("s/S", Style::default().fg(Color::Yellow)), - Span::raw("=stop, "), - Span::styled("h/H", Style::default().fg(Color::Yellow)), - Span::raw("=help"), - ]), - ]; - - let title = Paragraph::new(title_text) - .block(Block::default().borders(Borders::ALL).title("Status")) - .wrap(Wrap { trim: true }); - f.render_widget(title, chunks[0]); - - // Main content area - let content_chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Percentage(50), // Left pane - Constraint::Percentage(50), // Right pane - ]) - .split(chunks[1]); - - // Left pane - System info and registers - self.draw_system_info(f, content_chunks[0]); - - // Right pane - RAM contents - self.draw_ram_contents(f, content_chunks[1]); - - // Command bar - let command_text = if self.command_buffer.is_empty() { - "Enter command (type 'h' for help)..." - } else { - &self.command_buffer - }; - - let command_bar = Paragraph::new(command_text) - .style(Style::default().fg(Color::White)) - .block(Block::default().borders(Borders::ALL).title("Command")); - f.render_widget(command_bar, chunks[2]); - } - - fn draw_help_screen(&self, f: &mut Frame) { - let size = f.size(); - let help_text = vec![ - Line::from(vec![Span::styled( - "Intel MCS-4 Emulator Console Help", - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD), - )]), - Line::from(""), - Line::from(vec![Span::styled( - "Commands:", - Style::default().add_modifier(Modifier::BOLD), - )]), - Line::from(vec![ - Span::styled(" q, quit, exit", Style::default().fg(Color::Yellow)), - Span::raw(" - Exit emulator"), - ]), - Line::from(vec![ - Span::styled(" r, run", Style::default().fg(Color::Yellow)), - Span::raw(" - Start/stop system execution"), - ]), - Line::from(vec![ - Span::styled(" s, stop", Style::default().fg(Color::Yellow)), - Span::raw(" - Stop system execution"), - ]), - Line::from(vec![ - Span::styled(" h, help", Style::default().fg(Color::Yellow)), - Span::raw(" - Show/hide this help"), - ]), - Line::from(vec![ - Span::styled(" reset", Style::default().fg(Color::Yellow)), - Span::raw(" - Reset system"), - ]), - Line::from(""), - Line::from(vec![Span::styled( - "Navigation:", - Style::default().add_modifier(Modifier::BOLD), - )]), - Line::from(vec![ - Span::styled(" Tab", Style::default().fg(Color::Yellow)), - Span::raw(" - Switch between panes"), - ]), - Line::from(vec![ - Span::styled(" Enter", Style::default().fg(Color::Yellow)), - Span::raw(" - Execute command"), - ]), - Line::from(vec![ - Span::styled(" Backspace", Style::default().fg(Color::Yellow)), - Span::raw(" - Delete character"), - ]), - Line::from(""), - Line::from(vec![Span::raw("Press any key to return to main view...")]), - ]; - - let help = Paragraph::new(help_text) - .style(Style::default().fg(Color::White)) - .wrap(Wrap { trim: true }) - .block(Block::default().borders(Borders::ALL).title("Help")); - f.render_widget(help, size); - } - - fn draw_system_info(&self, f: &mut Frame, area: Rect) { - let info_chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(8), // System info - Constraint::Min(5), // Registers - ]) - .split(area); - - // System information - let system_info = match self.system.lock() { - Ok(system) => match system.get_system_info() { - info => vec![ - Line::from(vec![Span::raw(format!("System: {}", info.name))]), - Line::from(vec![Span::raw(format!( - "Description: {}", - info.description - ))]), - Line::from(vec![Span::raw(format!( - "Components: {}", - info.component_count - ))]), - Line::from(vec![Span::raw(format!("CPU Speed: {} Hz", info.cpu_speed))]), - Line::from(vec![Span::raw(format!( - "ROM Size: {} bytes", - info.rom_size - ))]), - Line::from(vec![Span::raw(format!( - "RAM Size: {} nibbles", - info.ram_size - ))]), - Line::from(vec![Span::raw(format!("Running: {}", system.is_running()))]), - ], - }, - Err(_) => { - vec![Line::from(vec![Span::raw( - "System information unavailable", - )])] - } - }; - - let system_widget = Paragraph::new(system_info) - .block( - Block::default() - .borders(Borders::ALL) - .title("System Information"), - ) - .wrap(Wrap { trim: true }); - f.render_widget(system_widget, info_chunks[0]); - - // CPU register display (enhanced with execution status) - let register_info = vec![ - Line::from(vec![Span::raw("CPU Registers:")]), - Line::from(vec![Span::raw("Status: Running (check console output)")]), - Line::from(vec![Span::raw("PC: 0x000 (see DEBUG output)")]), - Line::from(vec![Span::raw("ACC: 0x0 (see DEBUG output)")]), - Line::from(vec![Span::raw( - "Instructions: Executing (see DEBUG output)", - )]), - ]; - - let register_widget = Paragraph::new(register_info) - .block( - Block::default() - .borders(Borders::ALL) - .title("CPU Registers"), - ) - .wrap(Wrap { trim: true }); - f.render_widget(register_widget, info_chunks[1]); - } - - fn draw_ram_contents(&self, f: &mut Frame, area: Rect) { - let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])]; - - // Try to get actual RAM data from the system - match self.system.lock() { - Ok(_system) => { - // This is a simplified version - in a real implementation, - // we would need to access the actual RAM components - // For now, show a more informative placeholder - ram_info.push(Line::from(vec![Span::raw("Reading RAM contents...")])); - - // Show some sample memory ranges - for bank in 0..4 { - let mut bank_data = format!("Bank {}: [", bank); - for i in 0..20 { - if i > 0 && i % 4 == 0 { - bank_data.push(' '); - } - bank_data.push_str("00"); - } - bank_data.push(']'); - ram_info.push(Line::from(vec![Span::raw(bank_data)])); - } - } - Err(_) => { - ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")])); - } - } - - let ram_widget = Paragraph::new(ram_info) - .block(Block::default().borders(Borders::ALL).title("RAM Contents")) - .wrap(Wrap { trim: true }); - f.render_widget(ram_widget, area); - } -} - -/// Public interface for launching the console -pub fn run_console( - system: Arc>, - config: ConsoleConfig, -) -> Result<(), Box> { - let mut app = ConsoleApp::new(system, config); - app.run() -}