diff --git a/configs/console_config.xml b/configs/console_config.xml
index 142a62d..52b5ef5 100644
--- a/configs/console_config.xml
+++ b/configs/console_config.xml
@@ -6,8 +6,12 @@
true
true
true
+ true
+ true
+ hex
4
5
+ 5
@@ -16,6 +20,7 @@
white
white
white
+ white
white
diff --git a/plans/codebase_analysis.md b/plans/codebase_analysis.md
new file mode 100644
index 0000000..10ad0ea
--- /dev/null
+++ b/plans/codebase_analysis.md
@@ -0,0 +1,589 @@
+# MCS-4 Emulator Codebase Analysis
+
+## Executive Summary
+
+This document provides a comprehensive analysis of the MCS-4 emulator codebase structure, focusing on GUI, console output, configuration systems, and entry points. The analysis identifies specific locations for implementing ROM/RAM/CPU register display and Ctrl-Q keyboard handling.
+
+---
+
+## 1. GUI Structure Analysis
+
+### 1.1 Framework and Architecture
+
+**Location:** [`src/gui/mod.rs`](src/gui/mod.rs:1-259)
+
+The GUI is built using **eframe/egui** (Rust immediate mode GUI framework):
+
+```rust
+use eframe::egui;
+```
+
+**Key Structure:**
+- [`GuiApp`](src/gui/mod.rs:21-29) - Main GUI application struct
+ - `system: Option>>` - Thread-safe system reference
+ - `gui_state: state::GuiState` - GUI state management
+ - `components: components::GuiComponents` - Component container
+
+**Entry Point:** [`run_gui()`](src/gui/mod.rs:234-259) function launches the GUI
+
+### 1.2 State Management
+
+**Location:** [`src/gui/state.rs`](src/gui/state.rs:1-176)
+
+**Key Structures:**
+
+| Structure | Purpose | Location |
+|-----------|---------|----------|
+| [`GuiState`](src/gui/state.rs:12-29) | Main UI state container | Lines 12-29 |
+| [`SystemInfo`](src/gui/state.rs:32-40) | System metadata display | Lines 32-40 |
+| [`ComponentStates`](src/gui/state.rs:43-49) | Component running states | Lines 43-49 |
+| [`MemoryState`](src/gui/state.rs:52-57) | RAM contents display | Lines 52-57 |
+| [`RegisterState`](src/gui/state.rs:60-67) | CPU register display | Lines 60-67 |
+
+**Current State Fields:**
+```rust
+pub struct MemoryState {
+ pub ram_contents: Vec<[u8; 4]>, // 4 banks of RAM data
+ pub selected_bank: usize,
+ pub selected_address: usize,
+}
+
+pub struct RegisterState {
+ pub accumulator: u8,
+ pub carry_flag: bool,
+ pub program_counter: u16,
+ pub index_registers: [u8; 16], // R0-R15
+ pub stack_pointer: u8,
+}
+```
+
+**State Update Method:** [`update_from_system()`](src/gui/state.rs:100-118)
+
+### 1.3 GUI Components
+
+**Location:** [`src/gui/components.rs`](src/gui/components.rs:1-804)
+
+| Component | Purpose | Location |
+|-----------|---------|----------|
+| [`GuiComponents`](src/gui/components.rs:72-84) | Component container | Lines 72-84 |
+| [`ControlPanel`](src/gui/components.rs:180-303) | Start/Stop/Reset buttons | Lines 180-303 |
+| [`MemoryViewer`](src/gui/components.rs:305-428) | RAM display | Lines 305-428 |
+| [`RegisterViewer`](src/gui/components.rs:430-574) | CPU registers display | Lines 430-574 |
+| [`RomLoader`](src/gui/components.rs:576-671) | ROM file loading | Lines 576-671 |
+| [`StatusBar`](src/gui/components.rs:673-804) | System status | Lines 673-804 |
+
+**Where to Add ROM Display:**
+- Create new `RomViewer` component similar to `MemoryViewer`
+- Add to [`GuiComponents`](src/gui/components.rs:72-84) struct
+- Render in [`render_gui()`](src/gui/mod.rs:171-185)
+
+### 1.4 Current Display Gaps
+
+**Missing ROM Display:**
+- No ROM content viewer exists
+- Need to add ROM data retrieval from Intel4001 components
+
+**RAM Display Status:**
+- [`MemoryState.ram_contents`](src/gui/state.rs:54) exists but uses placeholder data
+- [`update_from_system()`](src/gui/state.rs:100-118) does NOT populate actual RAM data
+- Need to add RAM data extraction from Intel4002 components
+
+**CPU Register Status:**
+- [`RegisterState`](src/gui/state.rs:60-67) exists with all needed fields
+- [`update_from_system()`](src/gui/state.rs:100-118) does NOT populate actual CPU data
+- Need to add CPU data extraction from Intel4004 component
+
+---
+
+## 2. Console Output Analysis
+
+### 2.1 Console Interface
+
+**Location:** [`src/gui/console.rs`](src/gui/console.rs:1-515)
+
+**Framework:** ratatui + crossterm (Terminal UI)
+
+**Key Structure:**
+```rust
+pub struct ConsoleApp {
+ system: Arc>,
+ config: ConsoleConfig,
+ running: bool,
+ command_buffer: String,
+ show_help: bool,
+ selected_pane: usize,
+}
+```
+
+### 2.2 Console Configuration
+
+**Location:** [`src/gui/console.rs:35-58`](src/gui/console.rs:35-58)
+
+```rust
+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,
+}
+```
+
+**XML Config File:** [`configs/console_config.xml`](configs/console_config.xml:1-45)
+
+### 2.3 Current Console Display
+
+**RAM Display:** [`draw_ram_contents()`](src/gui/console.rs:472-505)
+- Currently shows placeholder data (`00` values)
+- Does NOT read actual RAM from Intel4002
+
+**CPU Register Display:** [`draw_system_info()`](src/gui/console.rs:401-470)
+- Shows placeholder text: "see DEBUG output"
+- Does NOT read actual registers from Intel4004
+
+**ROM Display:**
+- NOT implemented in console
+- Would need new `draw_rom_contents()` method
+
+### 2.4 Input Handling
+
+**Location:** [`handle_key_event()`](src/gui/console.rs:142-202)
+
+Current key bindings:
+- `q/Q/Esc` - Quit
+- `r/R` - Run/Stop toggle
+- `s/S` - Stop
+- `h/H` - Help
+- `Tab` - Switch panes
+- `Enter` - Execute command
+
+**Ctrl-Q NOT currently handled** - Need to add `KeyCode::Char('q')` with Ctrl modifier check
+
+---
+
+## 3. Configuration System Analysis
+
+### 3.1 System Configuration
+
+**Location:** [`src/system_config.rs`](src/system_config.rs:1-629)
+
+**Key Structures:**
+
+| Structure | Purpose | Location |
+|-----------|---------|----------|
+| [`SystemConfig`](src/system_config.rs:58-67) | JSON system definition | Lines 58-67 |
+| [`ComponentConfig`](src/system_config.rs:70-75) | Component configuration enum | Lines 70-75 |
+| [`ConfigurableSystem`](src/system_config.rs:362-363) | Runtime system instance | Lines 362-428 |
+
+**Configuration Files:**
+- [`configs/mcs4_basic.json`](configs/mcs4_basic.json:1-167) - Basic MCS-4 system
+- [`configs/mcs4_max.json`](configs/mcs4_max.json) - Maximum configuration
+- [`configs/mcs4_io_demo.json`](configs/mcs4_io_demo.json) - I/O demonstration
+
+### 3.2 Existing Configuration Options
+
+**JSON Configuration Structure:**
+```json
+{
+ "name": "IntelMcs4",
+ "description": "Basic MCS-4 System",
+ "version": "1.0",
+ "metadata": {
+ "cpu_speed": 750000,
+ "rom_count": 2,
+ "ram_count": 1
+ },
+ "components": { ... },
+ "connections": { ... }
+}
+```
+
+### 3.3 Where Display Configuration Fits
+
+**Option 1: Extend ConsoleConfig** in [`src/gui/console.rs:35-58`](src/gui/console.rs:35-58)
+```rust
+pub struct ConsoleConfig {
+ // Existing fields...
+ pub show_rom: bool, // NEW: ROM display toggle
+ pub rom_address_range: String, // NEW: ROM address range to display
+ pub display_format: String, // NEW: hex/decimal/ascii
+}
+```
+
+**Option 2: Extend XML Config** [`configs/console_config.xml`](configs/console_config.xml:1-45)
+```xml
+
+
+ true
+ 16
+ hex
+
+```
+
+**Option 3: Create New DisplayConfig** structure for unified GUI/Console settings
+
+---
+
+## 4. Main Entry Point Analysis
+
+### 4.1 Application Flow
+
+**Location:** [`src/main.rs`](src/main.rs:1-676)
+
+```mermaid
+flowchart TD
+ A[main] --> B[Parse CLI Args]
+ B --> C{Mode?}
+ C -->|--console| D[Launch Console]
+ C -->|--gui| E[Launch GUI]
+ C -->|default| F[Traditional Mode]
+ D --> G[run_console]
+ E --> H[run_gui]
+ F --> I[run_system_demo]
+```
+
+### 4.2 Mode Selection
+
+**Location:** [`main()`](src/main.rs:102-279)
+
+```rust
+// Interface mode selection and launch
+if use_console {
+ // Launch interactive console interface
+ let console_config = ConsoleConfig::default();
+ run_console(system_arc, console_config)
+} else if use_gui {
+ // Launch graphical user interface
+ run_gui(Some(system_arc))
+} else {
+ // Traditional mode
+ run_system_demo(system)
+}
+```
+
+### 4.3 System Start/Stop
+
+**ConfigurableSystem Methods:**
+- [`run()`](src/system_config.rs:380-428) - Start system
+- [`stop()`](src/system_config.rs:538-540) - Stop system
+- [`is_running()`](src/system_config.rs:542-544) - Check running state
+
+### 4.4 Keyboard Input Handling
+
+**Console:** [`handle_key_event()`](src/gui/console.rs:142-202)
+
+**GUI:** Uses egui event system - no explicit keyboard handler
+
+**Traditional Mode:** No keyboard handling (Ctrl+C only)
+
+**Where Ctrl-Q Fits:**
+
+| Mode | Location | Implementation |
+|------|----------|----------------|
+| Console | [`handle_key_event()`](src/gui/console.rs:142) | Add Ctrl-Q detection |
+| GUI | [`GuiApp`](src/gui/mod.rs:21) update loop | Add keyboard shortcut |
+| Traditional | [`run_system_demo()`](src/main.rs:460) | Add terminal input thread |
+
+---
+
+## 5. Component Data Access Analysis
+
+### 5.1 Intel 4004 CPU
+
+**Location:** [`src/components/cpu/intel_4004.rs`](src/components/cpu/intel_4004.rs:1-1602)
+
+**Register Access Methods:**
+| Method | Returns | Location |
+|--------|---------|----------|
+| [`get_accumulator()`](src/components/cpu/intel_4004.rs:1142-1144) | `u8` | Line 1142 |
+| [`get_carry()`](src/components/cpu/intel_4004.rs:1154-1156) | `bool` | Line 1154 |
+| [`get_program_counter()`](src/components/cpu/intel_4004.rs:1130-1132) | `u16` | Line 1130 |
+| [`get_register(index)`](src/components/cpu/intel_4004.rs:1191-1197) | `Option` | Line 1191 |
+| [`get_stack_pointer()`](src/components/cpu/intel_4004.rs:1160-1162) | `u8` | Line 1160 |
+| [`get_cycle_count()`](src/components/cpu/intel_4004.rs:1166-1168) | `u64` | Line 1166 |
+
+### 5.2 Intel 4002 RAM
+
+**Location:** [`src/components/memory/intel_4002.rs`](src/components/memory/intel_4002.rs:1-1140)
+
+**Memory Access Methods:**
+| Method | Returns | Location |
+|--------|---------|----------|
+| [`read_ram_direct(addr)`](src/components/memory/intel_4002.rs:818-824) | `Option` | Line 818 |
+| [`get_io_port(port)`](src/components/memory/intel_4002.rs:842-848) | `Option` | Line 842 |
+| [`get_ram_size()`](src/components/memory/intel_4002.rs:810-813) | `usize` | Line 810 |
+
+### 5.3 Intel 4001 ROM
+
+**Location:** [`src/components/memory/intel_4001.rs`](src/components/memory/intel_4001.rs)
+
+**Expected Methods (need verification):**
+- ROM data read/access
+- I/O port access
+
+### 5.4 Component Access Pattern
+
+**From ConfigurableSystem:**
+```rust
+// Access components HashMap
+let components = system.get_components();
+
+// Get CPU
+if let Some(cpu_component) = components.get("CPU_4004") {
+ if let Ok(cpu) = cpu_component.lock() {
+ // Access CPU methods - but need downcast to Intel4004
+ }
+}
+```
+
+**Challenge:** Components are stored as `Box` - need downcasting to access type-specific methods.
+
+---
+
+## 6. Fibonacci Example Analysis
+
+### 6.1 Program Files
+
+**Location:** [`programs/`](programs/)
+
+| File | Size | Purpose |
+|------|------|---------|
+| [`fibonacci.bin`](programs/fibonacci.bin) | 33 bytes | Basic Fibonacci (RAM only) |
+| [`fibonacci_output.bin`](programs/fibonacci_output.bin) | 55 bytes | Fibonacci with output ports |
+
+### 6.2 Expected Behavior
+
+**From [`programs/README.md`](programs/README.md:7-13):**
+- Calculates first 8 Fibonacci numbers
+- Results stored in RAM locations 0-7
+- `fibonacci_output.bin` also outputs to ports for visibility
+
+### 6.3 Where Results Appear
+
+**RAM Locations:** 0x00-0x07 (first 8 Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13)
+
+**Output Ports:** Port values visible during execution with `fibonacci_output.bin`
+
+---
+
+## 7. Implementation Recommendations
+
+### 7.1 ROM/RAM/CPU Display - Console
+
+**File:** [`src/gui/console.rs`](src/gui/console.rs)
+
+**Changes Required:**
+
+1. **Add ROM display method** (new ~50 lines after line 505):
+```rust
+fn draw_rom_contents(&self, f: &mut Frame, area: Rect) {
+ // Access ROM components and display contents
+}
+```
+
+2. **Update RAM display** [`draw_ram_contents()`](src/gui/console.rs:472-505):
+ - Add actual RAM data retrieval from Intel4002
+ - Use `read_ram_direct()` method
+
+3. **Update CPU display** [`draw_system_info()`](src/gui/console.rs:401-470):
+ - Add actual register retrieval from Intel4004
+ - Use getter methods: `get_accumulator()`, `get_program_counter()`, etc.
+
+4. **Add component downcasting helper** (new method):
+```rust
+fn get_cpu_state(&self) -> Option { ... }
+fn get_ram_state(&self) -> Option { ... }
+fn get_rom_state(&self) -> Option { ... }
+```
+
+### 7.2 ROM/RAM/CPU Display - GUI
+
+**File:** [`src/gui/state.rs`](src/gui/state.rs)
+
+**Changes Required:**
+
+1. **Extend `MemoryState`** (line 52-57):
+```rust
+pub struct MemoryState {
+ pub ram_contents: Vec<[u8; 4]>,
+ pub rom_contents: Vec, // NEW: ROM data
+ pub rom_address_range: (u16, u16), // NEW: Address range
+ // ...existing fields
+}
+```
+
+2. **Update `update_from_system()`** (line 100-118):
+ - Add actual CPU register retrieval
+ - Add actual RAM data retrieval
+ - Add ROM data retrieval
+
+**File:** [`src/gui/components.rs`](src/gui/components.rs)
+
+3. **Add `RomViewer` component** (new ~100 lines after line 428):
+ - Similar to `MemoryViewer`
+ - Display ROM contents in hex grid
+
+### 7.3 Display Configuration
+
+**File:** [`src/gui/console.rs`](src/gui/console.rs:35-58)
+
+**Extend `ConsoleConfig`:**
+```rust
+pub struct ConsoleConfig {
+ // Existing...
+ pub show_rom: bool, // NEW
+ pub display_refresh_rate: u64, // NEW
+ pub address_format: String, // NEW: hex/decimal
+}
+```
+
+**File:** [`configs/console_config.xml`](configs/console_config.xml)
+
+**Add display options:**
+```xml
+
+ true
+ true
+
+```
+
+### 7.4 Ctrl-Q Keyboard Handling
+
+**Console - File:** [`src/gui/console.rs`](src/gui/console.rs:142)
+
+**Add in `handle_key_event()`:**
+```rust
+KeyCode::Char('q') if key.modifiers.contains(KeyModifiers::CONTROL) => {
+ self.running = false;
+ if let Ok(mut system) = self.system.lock() {
+ system.stop();
+ }
+}
+```
+
+**GUI - File:** [`src/gui/mod.rs`](src/gui/mod.rs:141)
+
+**Add in `update()` method:**
+```rust
+// Check for Ctrl-Q shortcut
+if ui.input(|i| i.key_pressed(egui::Key::Q) && i.modifiers.ctrl) {
+ ctx.send_viewport_cmd(egui::ViewportCommand::Close);
+}
+```
+
+**Traditional Mode - File:** [`src/main.rs`](src/main.rs:460)
+
+**Add terminal input thread in `run_system_demo()`**:
+```rust
+thread::spawn(move || {
+ let stdin = std::io::stdin();
+ for line in stdin.lock().lines() {
+ if line.unwrap() == "\x11" { // Ctrl-Q
+ *running.lock().unwrap() = false;
+ break;
+ }
+ }
+});
+```
+
+---
+
+## 8. Summary of Required Changes
+
+| Task | File | Location | Type |
+|------|------|----------|------|
+| Console RAM display | [`src/gui/console.rs`](src/gui/console.rs:472) | `draw_ram_contents()` | Modify |
+| Console CPU display | [`src/gui/console.rs`](src/gui/console.rs:401) | `draw_system_info()` | Modify |
+| Console ROM display | [`src/gui/console.rs`](src/gui/console.rs:505) | New method | Add |
+| GUI state update | [`src/gui/state.rs`](src/gui/state.rs:100) | `update_from_system()` | Modify |
+| GUI ROM viewer | [`src/gui/components.rs`](src/gui/components.rs:428) | New component | Add |
+| Console config | [`src/gui/console.rs`](src/gui/console.rs:35) | `ConsoleConfig` | Extend |
+| XML config | [`configs/console_config.xml`](configs/console_config.xml) | Add display options | Extend |
+| Console Ctrl-Q | [`src/gui/console.rs`](src/gui/console.rs:142) | `handle_key_event()` | Modify |
+| GUI Ctrl-Q | [`src/gui/mod.rs`](src/gui/mod.rs:141) | `update()` | Modify |
+| Traditional Ctrl-Q | [`src/main.rs`](src/main.rs:460) | `run_system_demo()` | Modify |
+
+---
+
+## 9. Architecture Diagram
+
+```mermaid
+graph TB
+ subgraph Entry
+ main[main.rs]
+ end
+
+ subgraph Modes
+ console[Console Mode]
+ gui[GUI Mode]
+ traditional[Traditional Mode]
+ end
+
+ subgraph Console Components
+ console_app[ConsoleApp]
+ console_config[ConsoleConfig]
+ draw_ram[draw_ram_contents]
+ draw_cpu[draw_system_info]
+ handle_keys[handle_key_event]
+ end
+
+ subgraph GUI Components
+ gui_app[GuiApp]
+ gui_state[GuiState]
+ gui_components[GuiComponents]
+ mem_viewer[MemoryViewer]
+ reg_viewer[RegisterViewer]
+ end
+
+ subgraph System
+ config_sys[ConfigurableSystem]
+ components[Component HashMap]
+ end
+
+ subgraph Hardware Components
+ cpu[Intel4004 CPU]
+ ram[Intel4002 RAM]
+ rom[Intel4001 ROM]
+ end
+
+ main --> console
+ main --> gui
+ main --> traditional
+
+ console --> console_app
+ console_app --> draw_ram
+ console_app --> draw_cpu
+ console_app --> handle_keys
+
+ gui --> gui_app
+ gui_app --> gui_state
+ gui_app --> gui_components
+ gui_components --> mem_viewer
+ gui_components --> reg_viewer
+
+ console_app --> config_sys
+ gui_state --> config_sys
+ config_sys --> components
+ components --> cpu
+ components --> ram
+ components --> rom
+```
+
+---
+
+## 10. Key Code Locations Reference
+
+| Component | File | Key Lines |
+|-----------|------|-----------|
+| GUI Entry | [`src/gui/mod.rs`](src/gui/mod.rs:234) | 234-259 |
+| GUI State | [`src/gui/state.rs`](src/gui/state.rs:12) | 12-176 |
+| GUI Components | [`src/gui/components.rs`](src/gui/components.rs:72) | 72-804 |
+| Console Entry | [`src/gui/console.rs`](src/gui/console.rs:509) | 509-515 |
+| Console Config | [`src/gui/console.rs`](src/gui/console.rs:35) | 35-58 |
+| Console Keys | [`src/gui/console.rs`](src/gui/console.rs:142) | 142-202 |
+| System Config | [`src/system_config.rs`](src/system_config.rs:58) | 58-629 |
+| Main Entry | [`src/main.rs`](src/main.rs:102) | 102-279 |
+| CPU Getters | [`src/components/cpu/intel_4004.rs`](src/components/cpu/intel_4004.rs:1130) | 1130-1197 |
+| RAM Getters | [`src/components/memory/intel_4002.rs`](src/components/memory/intel_4002.rs:810) | 810-866 |
diff --git a/src/component.rs b/src/component.rs
index f721e42..da43117 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicBool, Ordering},
@@ -40,6 +41,11 @@ pub trait Component: Send + Sync {
/// Check if the component is currently running
/// Returns: true if component is running, false otherwise
fn is_running(&self) -> bool;
+
+ /// Get a reference to this component as Any for downcasting
+ /// This allows runtime type inspection to access component-specific methods
+ /// Returns: A reference to self as &dyn Any
+ fn as_any(&self) -> &dyn Any;
}
/// Extended trait for components that can be run in their own threads
/// Provides automatic thread spawning functionality for components
@@ -157,6 +163,10 @@ impl Component for BaseComponent {
fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
impl RunnableComponent for BaseComponent {}
diff --git a/src/components/clock/generic_clock.rs b/src/components/clock/generic_clock.rs
index 4c6e7e0..38683f1 100644
--- a/src/components/clock/generic_clock.rs
+++ b/src/components/clock/generic_clock.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -171,6 +172,10 @@ impl Component for GenericClock {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
// Advanced clock features
diff --git a/src/components/clock/two_phase_clock.rs b/src/components/clock/two_phase_clock.rs
index 8974317..7d29a57 100644
--- a/src/components/clock/two_phase_clock.rs
+++ b/src/components/clock/two_phase_clock.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -182,4 +183,8 @@ impl Component for TwoPhaseClock {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
diff --git a/src/components/common/intel_400x.rs b/src/components/common/intel_400x.rs
index 4c31731..1f97b70 100644
--- a/src/components/common/intel_400x.rs
+++ b/src/components/common/intel_400x.rs
@@ -592,7 +592,12 @@ pub enum Intel400xEvent {
///
/// # Example
///
-/// ```rust
+/// ```rust,ignore
+/// use std::time::Duration;
+/// use rusty_emu::components::common::intel_400x::{
+/// Intel400xComponentBehavior, Intel400xComponentType, Intel400xUnifiedState
+/// };
+///
/// struct MyCustomComponent {
/// // Component fields...
/// }
@@ -641,7 +646,7 @@ pub trait Intel400xComponentBehavior {
///
/// # Example
///
- /// ```rust
+ /// ```rust,ignore
/// fn on_state_entry(&mut self, state: Intel400xUnifiedState) {
/// match state {
/// Intel400xUnifiedState::AddressPhase => {
@@ -699,7 +704,7 @@ pub trait Intel400xComponentBehavior {
///
/// # Example
///
- /// ```rust
+ /// ```rust,ignore
/// fn execute_state_logic(&mut self, state: Intel400xUnifiedState) {
/// match state {
/// Intel400xUnifiedState::AddressPhase => {
@@ -769,6 +774,12 @@ pub trait Intel400xComponentBehavior {
/// # Example
///
/// ```rust
+/// use std::time::Duration;
+/// use rusty_emu::components::common::intel_400x::{
+/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
+/// Intel400xUnifiedState, process_event_direct
+/// };
+///
/// // Create a state machine for an Intel 4001 ROM chip
/// let mut state_machine = Intel400xStateMachine::new(
/// Intel400xComponentType::Intel4001,
@@ -826,6 +837,11 @@ impl Intel400xStateMachine {
/// # Example
///
/// ```rust
+ /// use std::time::Duration;
+ /// use rusty_emu::components::common::intel_400x::{
+ /// Intel400xStateMachine, Intel400xComponentType, Intel400xUnifiedState
+ /// };
+ ///
/// // Create a state machine for an Intel 4002 RAM chip with 500ns access time
/// let state_machine = Intel400xStateMachine::new(
/// Intel400xComponentType::Intel4002,
@@ -1092,21 +1108,28 @@ impl Intel400xComponentBehavior for NoOpBehavior {
/// # Example
///
/// ```rust
+/// use std::time::Duration;
+/// use rusty_emu::components::common::intel_400x::{
+/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
+/// Intel400xUnifiedState, process_event
+/// };
+///
/// let mut state_machine = Intel400xStateMachine::new(
/// Intel400xComponentType::Intel4001,
/// Duration::from_nanos(500)
/// );
-/// let mut component = Intel4001Component::new();
+/// // Note: Intel4001Component would be imported from its module
+/// // let mut component = Intel4001Component::new();
///
/// // Process a chip select event
-/// let handled = process_event(
-/// &mut state_machine,
-/// Intel400xEvent::ChipSelect,
-/// &mut component
-/// );
+/// // let handled = process_event(
+/// // &mut state_machine,
+/// // Intel400xEvent::ChipSelect,
+/// // &mut component
+/// // );
///
-/// assert!(handled);
-/// assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase);
+/// // assert!(handled);
+/// // assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase);
/// ```
pub fn process_event(
state_machine: &mut Intel400xStateMachine,
@@ -1163,6 +1186,12 @@ pub fn process_event(
/// # Example
///
/// ```rust
+/// use std::time::Duration;
+/// use rusty_emu::components::common::intel_400x::{
+/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
+/// Intel400xUnifiedState, process_event_direct
+/// };
+///
/// let mut state_machine = Intel400xStateMachine::new(
/// Intel400xComponentType::Intel4001,
/// Duration::from_nanos(500)
diff --git a/src/components/cpu/intel_4004.rs b/src/components/cpu/intel_4004.rs
index 992be6e..ad2ebcb 100644
--- a/src/components/cpu/intel_4004.rs
+++ b/src/components/cpu/intel_4004.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -1577,6 +1578,10 @@ impl Component for Intel4004 {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
impl RunnableComponent for Intel4004 {
diff --git a/src/components/cpu/mos_6502.rs b/src/components/cpu/mos_6502.rs
index 5525465..74d8f54 100644
--- a/src/components/cpu/mos_6502.rs
+++ b/src/components/cpu/mos_6502.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -231,6 +232,10 @@ impl Component for MOS6502 {
fn is_running(&self) -> bool {
self.is_running
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
// 6502-specific methods
diff --git a/src/components/cpu/wdc_65c02.rs b/src/components/cpu/wdc_65c02.rs
index 7e551a8..0761457 100644
--- a/src/components/cpu/wdc_65c02.rs
+++ b/src/components/cpu/wdc_65c02.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
@@ -107,6 +108,10 @@ impl Component for WDC65C02 {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
// 65C02-specific enhancements
diff --git a/src/components/memory/intel_4001.rs b/src/components/memory/intel_4001.rs
index 2e353e9..c0b885b 100644
--- a/src/components/memory/intel_4001.rs
+++ b/src/components/memory/intel_4001.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -924,6 +925,10 @@ impl Component for Intel4001 {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
impl RunnableComponent for Intel4001 {}
diff --git a/src/components/memory/intel_4002.rs b/src/components/memory/intel_4002.rs
index 242d76b..3805e60 100644
--- a/src/components/memory/intel_4002.rs
+++ b/src/components/memory/intel_4002.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -47,22 +48,6 @@ pub struct Intel4002 {
pending_operation: PendingOperation,
}
-/// RAM register operations (for WRM/WRR instructions)
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum RamOperation {
- Read, // Read from RAM register
- Write, // Write to RAM register
- WriteOr, // Write to RAM register with OR operation
- WriteAnd, // Write to RAM register with AND operation
-}
-
-/// RAM access modes (for SRC/SRC instructions)
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum RamAccessMode {
- Direct, // Direct access to RAM
- Indirect, // Indirect access via register pair
-}
-
impl Intel400xClockHandling for Intel4002 {
fn get_base(&self) -> &BaseComponent {
&self.base
@@ -403,17 +388,6 @@ impl Intel4002 {
}
}
- /// Read from a specific I/O port
- /// Parameters: port - I/O port number (0-3)
- /// Returns: 4-bit value from I/O port
- pub fn read_io_port(&self, port: usize) -> u8 {
- if port < 4 {
- self.io_ports[port]
- } else {
- 0
- }
- }
-
/// Write to a specific I/O port
/// Parameters: port - I/O port number (0-3), data - 4-bit data to write
/// Returns: Ok(()) on success, Err(String) on failure
@@ -453,25 +427,6 @@ impl Intel4002 {
}
}
- /// Drive status character on status pins
- fn drive_status_character(&self) {
- let status = self.status_character & 0x7F; // Ensure only 7 bits
-
- for i in 0..4 {
- if let Ok(pin) = self.base.get_pin(&format!("S{}", i)) {
- if let Ok(mut pin_guard) = pin.lock() {
- let bit_value = (status >> i) & 1;
- let pin_value = if bit_value == 1 {
- PinValue::High
- } else {
- PinValue::Low
- };
- pin_guard.set_driver(Some(self.base.name()), pin_value);
- }
- }
- }
- }
-
/// Handle Φ1 rising edge - Address and control phase
/// Hardware: Φ1 high = CPU drives bus with address/control information
/// Memory operations start when SYNC goes high during Φ1 rising edge
@@ -699,13 +654,6 @@ impl Intel4002 {
self.on_state_entry(new_state);
}
}
-
- /// Helper function to execute unified state machine logic
- fn execute_state_machine(&mut self) {
- // Execute the current state logic using the unified state machine
- let current_state = self.state_machine.get_state();
- self.execute_state_logic(current_state);
- }
}
impl Component for Intel4002 {
@@ -800,6 +748,10 @@ impl Component for Intel4002 {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
impl RunnableComponent for Intel4002 {}
diff --git a/src/components/memory/intel_4003.rs b/src/components/memory/intel_4003.rs
index e1094ad..455bc12 100644
--- a/src/components/memory/intel_4003.rs
+++ b/src/components/memory/intel_4003.rs
@@ -1,3 +1,4 @@
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -619,6 +620,10 @@ impl Component for Intel4003 {
fn is_running(&self) -> bool {
self.base.is_running()
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
impl RunnableComponent for Intel4003 {}
diff --git a/src/gui/components.rs b/src/gui/components.rs
index b18a462..ed656d2 100644
--- a/src/gui/components.rs
+++ b/src/gui/components.rs
@@ -74,6 +74,8 @@ pub struct GuiComponents {
control_panel: ControlPanel,
/// RAM content inspection and visualization
memory_viewer: MemoryViewer,
+ /// ROM content inspection and visualization
+ rom_viewer: RomViewer,
/// CPU register state display
register_viewer: RegisterViewer,
/// ROM file loading and management
@@ -103,6 +105,7 @@ impl GuiComponents {
Self {
control_panel: ControlPanel::new(),
memory_viewer: MemoryViewer::new(),
+ rom_viewer: RomViewer::new(),
register_viewer: RegisterViewer::new(),
rom_loader: RomLoader::new(),
status_bar: StatusBar::new(),
@@ -128,11 +131,23 @@ impl GuiComponents {
///
/// # 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) {
+ /// * `state` - Mutable reference to GUI state for display and interaction
+ pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
self.memory_viewer.render(ui, state);
}
+ /// Render the ROM viewer component
+ ///
+ /// Displays ROM contents in a tabular format with address selection
+ /// and hex/decimal viewing options.
+ ///
+ /// # Arguments
+ /// * `ui` - egui UI context for rendering
+ /// * `state` - Mutable reference to GUI state for display and interaction
+ pub fn render_rom_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
+ self.rom_viewer.render(ui, state);
+ }
+
/// Render the register viewer component
///
/// Shows CPU register state including accumulator, program counter,
@@ -140,8 +155,8 @@ impl GuiComponents {
///
/// # 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) {
+ /// * `state` - Mutable reference to GUI state for display and interaction
+ pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
self.register_viewer.render(ui, state);
}
@@ -330,8 +345,6 @@ impl ControlPanel {
/// - **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)
#[allow(dead_code)]
// bytes_per_row is currently fixed at 16 but kept for future flexibility
@@ -341,16 +354,12 @@ pub struct MemoryViewer {
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.
+ /// Initializes the viewer with standard layout optimized for the Intel 4002 RAM structure.
///
/// # Returns
/// A new `MemoryViewer` instance ready for RAM content display
pub fn new() -> Self {
- Self {
- show_hex: true,
- bytes_per_row: 16,
- }
+ Self { bytes_per_row: 16 }
}
/// Render the memory viewer interface
@@ -360,7 +369,7 @@ impl MemoryViewer {
///
/// # Arguments
/// * `ui` - egui UI context for rendering
- /// * `state` - Immutable reference to GUI state containing memory data
+ /// * `state` - Mutable reference to GUI state containing memory data
///
/// # Layout Structure
/// ```text
@@ -375,7 +384,7 @@ impl MemoryViewer {
/// │ [03] [55] [66] [77] [88] │
/// └─────────────────────────────────────────────────┘
/// ```
- pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
+ pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
ui.vertical(|ui| {
// Section header
ui.heading("Memory Viewer");
@@ -385,10 +394,9 @@ impl MemoryViewer {
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),
+ egui::DragValue::new(&mut state.memory_state.selected_bank).clamp_range(0..=3),
);
- ui.checkbox(&mut self.show_hex.clone(), "Hex View");
+ ui.checkbox(&mut state.memory_state.show_hex, "Hex View");
});
ui.separator();
@@ -411,7 +419,7 @@ impl MemoryViewer {
// Bank data columns
for bank in 0..4 {
let value = state.memory_state.ram_contents[bank][addr];
- if self.show_hex {
+ if state.memory_state.show_hex {
ui.label(format!("{:02X}", value));
} else {
ui.label(format!("{}", value));
@@ -458,22 +466,19 @@ impl MemoryViewer {
/// - **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,
+ // No internal state - all UI state is stored in GuiState
}
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.
+ /// Initializes the viewer with standard display formatting optimized
+ /// for the Intel 4004 architecture.
///
/// # Returns
/// A new `RegisterViewer` instance ready for CPU state display
pub fn new() -> Self {
- Self {
- selected_register: 0,
- }
+ Self {}
}
/// Render the register viewer interface
@@ -483,7 +488,7 @@ impl RegisterViewer {
///
/// # Arguments
/// * `ui` - egui UI context for rendering
- /// * `state` - Immutable reference to GUI state containing register data
+ /// * `state` - Mutable reference to GUI state containing register data
///
/// # Layout Structure
/// ```text
@@ -499,7 +504,7 @@ impl RegisterViewer {
/// │ Stack Pointer │ 08 │ 8 │
/// └─────────────────────────────────────────────────┘
/// ```
- pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
+ pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
ui.vertical(|ui| {
// Section header
ui.heading("CPU Registers");
@@ -508,7 +513,8 @@ impl RegisterViewer {
ui.horizontal(|ui| {
ui.label("Index Register:");
ui.add(
- egui::DragValue::new(&mut self.selected_register.clone()).clamp_range(0..=15),
+ egui::DragValue::new(&mut state.register_state.selected_register)
+ .clamp_range(0..=15),
);
});
@@ -526,7 +532,7 @@ impl RegisterViewer {
// Core CPU registers
ui.label("Accumulator");
- ui.label(format!("{:02X}", state.register_state.accumulator));
+ ui.label(format!("{:X}", state.register_state.accumulator));
ui.label(format!("{}", state.register_state.accumulator));
ui.end_row();
@@ -551,20 +557,21 @@ impl RegisterViewer {
ui.end_row();
// Selected index register (highlighted)
- ui.label(format!("Index R{}", self.selected_register));
+ let selected = state.register_state.selected_register;
+ ui.label(format!("Index R{}", selected));
ui.label(format!(
- "{:02X}",
- state.register_state.index_registers[self.selected_register]
+ "{:X}",
+ state.register_state.index_registers[selected]
));
ui.label(format!(
"{}",
- state.register_state.index_registers[self.selected_register]
+ state.register_state.index_registers[selected]
));
ui.end_row();
// Stack management
ui.label("Stack Pointer");
- ui.label(format!("{:02X}", state.register_state.stack_pointer));
+ ui.label(format!("{:X}", state.register_state.stack_pointer));
ui.label(format!("{}", state.register_state.stack_pointer));
ui.end_row();
});
@@ -574,6 +581,132 @@ impl RegisterViewer {
}
}
+/// ROM viewer component for displaying ROM contents
+///
+/// The ROM viewer provides comprehensive ROM inspection capabilities,
+/// allowing users to examine program memory contents with flexible
+/// display options and intuitive navigation.
+///
+/// ## Features
+///
+/// - **Program Memory Display**: View ROM contents from Intel 4001 chips
+/// - **Flexible Formatting**: Toggle between hexadecimal and decimal display
+/// - **Address Navigation**: Jump to specific memory addresses
+/// - **Scrollable Interface**: Navigate through memory contents efficiently
+/// - **Real-time Updates**: Live ROM content display
+///
+/// ## Display Format
+///
+/// The ROM viewer shows data in a structured grid:
+/// - **Address Column**: Memory addresses in hexadecimal format
+/// - **Data Columns**: 16 bytes per row for easy viewing
+/// - **Value Display**: Configurable hex or decimal representation
+///
+/// ## Memory Organization
+///
+/// The Intel 4001 ROM has the following structure:
+/// - **256 Bytes**: Total ROM capacity per chip
+/// - **8-bit Values**: Each memory location stores a full byte
+pub struct RomViewer {
+ /// Number of bytes to display per row
+ bytes_per_row: usize,
+}
+
+impl RomViewer {
+ /// Create a new ROM viewer with default configuration
+ ///
+ /// Initializes the viewer with standard layout optimized for the Intel 4001 ROM structure.
+ ///
+ /// # Returns
+ /// A new `RomViewer` instance ready for ROM content display
+ pub fn new() -> Self {
+ Self { bytes_per_row: 16 }
+ }
+
+ /// Render the ROM viewer interface
+ ///
+ /// Creates a comprehensive ROM inspection interface with address navigation,
+ /// display options, and a scrollable memory contents grid.
+ ///
+ /// # Arguments
+ /// * `ui` - egui UI context for rendering
+ /// * `state` - Mutable reference to GUI state containing ROM data
+ ///
+ /// # Layout Structure
+ /// ```text
+ /// ┌─────────────────────────────────────────────────┐
+ /// │ ROM Viewer (Chip: ROM_4001_1) [─] [□] │
+ /// │ □ Hex View │
+ /// ├─────────────────────────────────────────────────┤
+ /// │ Address 00 01 02 03 04 05 06 07 08 09 0A 0B... │
+ /// │ 0x000 D4 C3 B2 A1 00 11 22 33 44 55 66 77... │
+ /// │ 0x010 88 99 AA BB CC DD EE FF 00 11 22 33... │
+ /// │ ... │
+ /// └─────────────────────────────────────────────────┘
+ /// ```
+ pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
+ ui.vertical(|ui| {
+ // Section header with chip name
+ let header = if state.rom_state.chip_name.is_empty() {
+ "ROM Viewer".to_string()
+ } else {
+ format!("ROM Viewer ({})", state.rom_state.chip_name)
+ };
+ ui.heading(header);
+
+ // Control bar with display options
+ ui.horizontal(|ui| {
+ ui.checkbox(&mut state.rom_state.show_hex, "Hex View");
+ });
+
+ ui.separator();
+
+ // ROM contents in scrollable area
+ egui::ScrollArea::vertical()
+ .max_height(200.0)
+ .show(ui, |ui| {
+ egui::Grid::new("rom_grid").striped(true).show(ui, |ui| {
+ // Header row with column labels
+ ui.label("Addr");
+ for i in 0..self.bytes_per_row {
+ ui.label(format!("{:02X}", i));
+ }
+ ui.end_row();
+
+ // ROM contents rows
+ let total_rows = (state.rom_state.rom_contents.len() + self.bytes_per_row
+ - 1)
+ / self.bytes_per_row;
+ for row in 0..total_rows {
+ let addr = row * self.bytes_per_row;
+
+ // Address column
+ ui.label(format!("{:03X}", addr));
+
+ // Data columns
+ for col in 0..self.bytes_per_row {
+ let idx = addr + col;
+ if idx < state.rom_state.rom_contents.len() {
+ let value = state.rom_state.rom_contents[idx];
+ if state.rom_state.show_hex {
+ ui.label(format!("{:02X}", value));
+ } else {
+ ui.label(format!("{:3}", value));
+ }
+ } else {
+ ui.label("--");
+ }
+ }
+ ui.end_row();
+ }
+ });
+ });
+ });
+
+ ui.separator();
+ }
+}
+
/// ROM loader component for file dialog integration and program management
///
/// The ROM loader handles program file selection, loading, and management.
diff --git a/src/gui/console.rs b/src/gui/console.rs
index 951ae52..1521d7e 100644
--- a/src/gui/console.rs
+++ b/src/gui/console.rs
@@ -11,7 +11,7 @@
//! - Graceful interrupt handling
use crossterm::{
- event::{self, Event, KeyCode},
+ event::{self, Event, KeyCode, KeyModifiers},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
@@ -31,6 +31,44 @@ use std::time::{Duration, Instant};
use crate::system_config::ConfigurableSystem;
+/// Display format for memory and register values
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
+pub enum DisplayFormat {
+ #[default]
+ Hex,
+ Decimal,
+ Binary,
+}
+
+impl DisplayFormat {
+ /// Format a byte value according to the display format
+ pub fn format_byte(&self, value: u8) -> String {
+ match self {
+ DisplayFormat::Hex => format!("0x{:02X}", value),
+ DisplayFormat::Decimal => format!("{}", value),
+ DisplayFormat::Binary => format!("0b{:08b}", value),
+ }
+ }
+
+ /// Format a nibble (4-bit) value according to the display format
+ pub fn format_nibble(&self, value: u8) -> String {
+ match self {
+ DisplayFormat::Hex => format!("0x{:X}", value & 0x0F),
+ DisplayFormat::Decimal => format!("{}", value & 0x0F),
+ DisplayFormat::Binary => format!("0b{:04b}", value & 0x0F),
+ }
+ }
+
+ /// Format a 12-bit address according to the display format
+ pub fn format_address(&self, value: u16) -> String {
+ match self {
+ DisplayFormat::Hex => format!("0x{:03X}", value & 0x0FFF),
+ DisplayFormat::Decimal => format!("{}", value & 0x0FFF),
+ DisplayFormat::Binary => format!("0b{:012b}", value & 0x0FFF),
+ }
+ }
+}
+
/// Console configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsoleConfig {
@@ -39,8 +77,12 @@ pub struct ConsoleConfig {
pub show_ram: bool,
pub show_registers: bool,
pub show_system_info: bool,
+ pub show_rom: bool,
+ pub show_cpu_registers: bool,
+ pub display_format: DisplayFormat,
pub ram_banks_per_row: usize,
pub max_ram_rows: usize,
+ pub max_rom_rows: usize,
}
impl Default for ConsoleConfig {
@@ -51,8 +93,12 @@ impl Default for ConsoleConfig {
show_ram: true,
show_registers: true,
show_system_info: true,
+ show_rom: true,
+ show_cpu_registers: true,
+ display_format: DisplayFormat::Hex,
ram_banks_per_row: 4,
max_ram_rows: 5,
+ max_rom_rows: 5,
}
}
}
@@ -99,7 +145,7 @@ impl ConsoleApp {
// 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);
+ self.handle_key_event(key.code, key.modifiers);
}
}
@@ -139,7 +185,18 @@ impl ConsoleApp {
Ok(())
}
- fn handle_key_event(&mut self, key: KeyCode) {
+ fn handle_key_event(&mut self, key: KeyCode, modifiers: KeyModifiers) {
+ // Handle Ctrl-Q to quit
+ if key == KeyCode::Char('q') || key == KeyCode::Char('Q') {
+ if modifiers.contains(KeyModifiers::CONTROL) {
+ self.running = false;
+ if let Ok(mut system) = self.system.lock() {
+ system.stop();
+ }
+ return;
+ }
+ }
+
match key {
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => {
println!("DEBUG: Quit key pressed, stopping console");
@@ -448,16 +505,12 @@ impl ConsoleApp {
.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)",
- )]),
- ];
+ // CPU register display with real data
+ let register_info = if self.config.show_cpu_registers {
+ self.get_cpu_register_info()
+ } else {
+ vec![Line::from(vec![Span::raw("CPU register display disabled")])]
+ };
let register_widget = Paragraph::new(register_info)
.block(
@@ -469,33 +522,101 @@ impl ConsoleApp {
f.render_widget(register_widget, info_chunks[1]);
}
+ /// Fetch real CPU register data from the system
+ fn get_cpu_register_info(&self) -> Vec> {
+ let mut register_info = vec![Line::from(vec![Span::raw("CPU Registers:")])];
+
+ match self.system.lock() {
+ Ok(system) => {
+ // Try to find Intel 4004 CPU and get register data
+ let cpu_data = system.get_cpu_register_data();
+
+ if let Some(cpu) = cpu_data {
+ let fmt = &self.config.display_format;
+
+ register_info.push(Line::from(vec![
+ Span::styled("PC: ", Style::default().fg(Color::Yellow)),
+ Span::raw(fmt.format_address(cpu.program_counter)),
+ ]));
+
+ register_info.push(Line::from(vec![
+ Span::styled("ACC: ", Style::default().fg(Color::Yellow)),
+ Span::raw(fmt.format_nibble(cpu.accumulator)),
+ ]));
+
+ register_info.push(Line::from(vec![
+ Span::styled("CY: ", Style::default().fg(Color::Yellow)),
+ Span::raw(if cpu.carry { "1" } else { "0" }),
+ ]));
+
+ register_info.push(Line::from(vec![
+ Span::styled("SP: ", Style::default().fg(Color::Yellow)),
+ Span::raw(format!("{}", cpu.stack_pointer)),
+ ]));
+
+ // Index registers
+ let mut reg_line = String::from("Index: ");
+ for (i, val) in cpu.index_registers.iter().enumerate() {
+ if i > 0 {
+ reg_line.push_str(", ");
+ }
+ reg_line.push_str(&format!("R{}={}", i, fmt.format_nibble(*val)));
+ }
+ register_info.push(Line::from(vec![Span::raw(reg_line)]));
+ } else {
+ register_info.push(Line::from(vec![Span::raw("No Intel 4004 CPU found")]));
+ }
+ }
+ Err(_) => {
+ register_info.push(Line::from(vec![Span::raw("Unable to access CPU data")]));
+ }
+ }
+
+ register_info
+ }
+
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...")]));
+ if self.config.show_ram {
+ match self.system.lock() {
+ Ok(system) => {
+ let ram_data = system.get_ram_data(self.config.max_ram_rows);
- // 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(' ');
+ if ram_data.is_empty() {
+ ram_info.push(Line::from(vec![Span::raw("No RAM chips found")]));
+ } else {
+ let fmt = &self.config.display_format;
+
+ for (chip_name, banks) in ram_data {
+ // Clone chip_name to avoid lifetime issues
+ let chip_name = chip_name.clone();
+ ram_info.push(Line::from(vec![Span::styled(
+ chip_name,
+ Style::default().fg(Color::Cyan),
+ )]));
+
+ for (bank_idx, data) in banks.iter().enumerate() {
+ let mut bank_line = format!(" Bank {}: [", bank_idx);
+ for (i, nibble) in data.iter().enumerate() {
+ if i > 0 && i % 4 == 0 {
+ bank_line.push(' ');
+ }
+ bank_line.push_str(&fmt.format_nibble(*nibble));
+ }
+ bank_line.push(']');
+ ram_info.push(Line::from(vec![Span::raw(bank_line)]));
+ }
}
- 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")]));
}
}
- Err(_) => {
- ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")]));
- }
+ } else {
+ ram_info.push(Line::from(vec![Span::raw("RAM display disabled")]));
}
let ram_widget = Paragraph::new(ram_info)
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
index 4d7d433..86d940e 100644
--- a/src/gui/mod.rs
+++ b/src/gui/mod.rs
@@ -45,11 +45,9 @@ impl GuiApp {
/// ```rust,no_run
/// use rusty_emu::gui::GuiApp;
///
- /// let app = GuiApp::new(&eframe::CreationContext {
- /// egui_ctx: Default::default(),
- /// integration_info: Default::default(),
- /// storage: None,
- /// });
+ /// // The CreationContext is provided by eframe when the app is launched
+ /// // Use new_for_test() for unit testing without eframe infrastructure
+ /// let app = GuiApp::new_for_test();
/// ```
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
let gui_state = state::GuiState::new();
@@ -97,11 +95,13 @@ impl GuiApp {
/// ```rust,no_run
/// use std::sync::{Arc, Mutex};
/// use rusty_emu::gui::GuiApp;
- /// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
+ /// use rusty_emu::system_config::SystemFactory;
///
- /// let mut app = GuiApp::new(&creation_context);
+ /// let mut app = GuiApp::new_for_test();
/// let factory = SystemFactory::new();
- /// let system = Arc::new(Mutex::new(factory.create_from_json("config.json")?));
+ /// let system = Arc::new(Mutex::new(
+ /// factory.create_from_json("configs/mcs4_basic.json").expect("Failed to create system")
+ /// ));
/// app.set_system(system);
/// ```
pub fn set_system(&mut self, system: Arc>) {
@@ -142,6 +142,21 @@ impl eframe::App for GuiApp {
// Request repaint for smooth real-time updates (60 FPS)
ctx.request_repaint();
+ // Handle CtrlQ keyboard shortcut to close the application
+ let ctrl_q_pressed = ctx.input(|i| i.modifiers.ctrl && i.key_pressed(egui::Key::Q));
+
+ if ctrl_q_pressed {
+ // Stop the system if available
+ if let Some(system) = self.get_system() {
+ if let Ok(mut sys) = system.lock() {
+ sys.stop();
+ }
+ }
+ // Close the GUI window
+ ctx.send_viewport_cmd(egui::ViewportCommand::Close);
+ return;
+ }
+
// Update system state if available - non-blocking operation
if let Some(system) = self.get_system() {
self.gui_state.update_from_system(&system);
@@ -178,8 +193,10 @@ impl GuiApp {
// 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_memory_viewer(ui, &mut self.gui_state);
+ self.components
+ .render_register_viewer(ui, &mut self.gui_state);
self.components.render_status_bar(ui, &self.gui_state);
});
}
@@ -207,13 +224,13 @@ impl GuiApp {
/// ```rust,no_run
/// use std::sync::{Arc, Mutex};
/// use rusty_emu::gui::run_gui;
-/// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
+/// use rusty_emu::system_config::SystemFactory;
///
/// // Create system (optional)
/// let factory = SystemFactory::new();
-/// let system = Some(Arc::new(Mutex::new(
-/// factory.create_from_json("configs/mcs4_basic.json")?
-/// )));
+/// let system = factory.create_from_json("configs/mcs4_basic.json").ok().map(|s| {
+/// Arc::new(Mutex::new(s))
+/// });
///
/// // Launch GUI - this will block until the GUI window is closed
/// if let Err(e) = run_gui(system) {
diff --git a/src/gui/state.rs b/src/gui/state.rs
index 322fe50..95e8bdc 100644
--- a/src/gui/state.rs
+++ b/src/gui/state.rs
@@ -22,6 +22,8 @@ pub struct GuiState {
pub component_states: ComponentStates,
/// Memory state
pub memory_state: MemoryState,
+ /// ROM state
+ pub rom_state: RomState,
/// CPU register state
pub register_state: RegisterState,
/// Last error message
@@ -54,6 +56,18 @@ pub struct MemoryState {
pub ram_contents: Vec<[u8; 4]>, // 4 banks of RAM data
pub selected_bank: usize,
pub selected_address: usize,
+ /// Display mode: true for hexadecimal, false for decimal
+ pub show_hex: bool,
+}
+
+/// ROM state for display
+#[derive(Debug, Clone)]
+pub struct RomState {
+ pub rom_contents: Vec,
+ pub selected_address: usize,
+ pub chip_name: String,
+ /// Display mode: true for hexadecimal, false for decimal
+ pub show_hex: bool,
}
/// CPU register state for display
@@ -62,8 +76,10 @@ 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 index_registers: [u8; 16], // 16 index registers (R0-R15) for Intel 4004
pub stack_pointer: u8,
+ /// Currently selected index register for detailed view (0-15)
+ pub selected_register: usize,
}
impl GuiState {
@@ -84,6 +100,13 @@ impl GuiState {
ram_contents: vec![[0; 4]; 4], // Initialize 4 banks with 4 bytes each
selected_bank: 0,
selected_address: 0,
+ show_hex: true,
+ },
+ rom_state: RomState {
+ rom_contents: vec![0; 256], // Initialize with 256 bytes
+ selected_address: 0,
+ chip_name: String::new(),
+ show_hex: true,
},
register_state: RegisterState {
accumulator: 0,
@@ -91,6 +114,7 @@ impl GuiState {
program_counter: 0,
index_registers: [0; 16],
stack_pointer: 0,
+ selected_register: 0,
},
last_error: None,
}
@@ -110,6 +134,15 @@ impl GuiState {
// Update component states
self.update_component_states(&system_guard);
+ // Update CPU register state from real data
+ self.update_register_state(&system_guard);
+
+ // Update RAM state from real data
+ self.update_ram_state(&system_guard);
+
+ // Update ROM state from real data
+ self.update_rom_state(&system_guard);
+
// Update cycle count (simulate for now)
if self.system_running {
self.cycle_count += 1;
@@ -146,6 +179,50 @@ impl GuiState {
});
}
+ /// Update CPU register state from real component data
+ fn update_register_state(&mut self, system: &ConfigurableSystem) {
+ if let Some(cpu_data) = system.get_cpu_register_data() {
+ self.register_state.accumulator = cpu_data.accumulator;
+ self.register_state.carry_flag = cpu_data.carry;
+ self.register_state.program_counter = cpu_data.program_counter;
+ self.register_state.stack_pointer = cpu_data.stack_pointer;
+ self.register_state
+ .index_registers
+ .copy_from_slice(&cpu_data.index_registers);
+ }
+ }
+
+ /// Update RAM state from real component data
+ fn update_ram_state(&mut self, system: &ConfigurableSystem) {
+ let ram_data = system.get_ram_data(4); // Get up to 4 rows per bank
+
+ if !ram_data.is_empty() {
+ // Take the first RAM chip's data
+ let (_, banks) = &ram_data[0];
+
+ // Convert bank data to our display format
+ self.memory_state.ram_contents.clear();
+ for bank in banks {
+ let mut row = [0u8; 4];
+ for (i, nibble) in bank.iter().take(4).enumerate() {
+ row[i] = *nibble;
+ }
+ self.memory_state.ram_contents.push(row);
+ }
+ }
+ }
+
+ /// Update ROM state from real component data
+ fn update_rom_state(&mut self, system: &ConfigurableSystem) {
+ let rom_data = system.get_rom_data(256); // Get up to 256 bytes
+
+ if !rom_data.is_empty() {
+ let (chip_name, data) = &rom_data[0];
+ self.rom_state.chip_name = chip_name.clone();
+ self.rom_state.rom_contents = data.clone();
+ }
+ }
+
/// Set an error message
pub fn set_error(&mut self, error: String) {
self.last_error = Some(error);
diff --git a/src/main.rs b/src/main.rs
index 30107d8..5e17392 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -488,7 +488,7 @@ fn run_system_demo(system: ConfigurableSystem) {
let timeout = Duration::from_secs(10); // 10 second timeout
loop {
- thread::sleep(Duration::from_millis(500));
+ thread::sleep(Duration::from_millis(100));
// Check for timeout
if start_time.elapsed() >= timeout {
diff --git a/src/system_config.rs b/src/system_config.rs
index 51c69b7..b132cae 100644
--- a/src/system_config.rs
+++ b/src/system_config.rs
@@ -50,10 +50,29 @@
//! ```
use crate::component::Component;
+use crate::components::cpu::intel_4004::Intel4004;
+use crate::components::memory::intel_4001::Intel4001;
+use crate::components::memory::intel_4002::Intel4002;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
+/// CPU register data for display purposes
+#[derive(Debug, Clone, Default)]
+pub struct CpuRegisterData {
+ pub program_counter: u16,
+ pub accumulator: u8,
+ pub carry: bool,
+ pub stack_pointer: u8,
+ pub index_registers: [u8; 16], // Intel 4004 has 16 index registers (R0-R15)
+}
+
+/// RAM data for display purposes (chip name -> banks of nibbles)
+pub type RamData = Vec<(String, Vec>)>;
+
+/// ROM data for display purposes (chip name -> bytes)
+pub type RomData = Vec<(String, Vec)>;
+
/// JSON-based system configuration structures
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemConfig {
@@ -616,6 +635,95 @@ impl ConfigurableSystem {
println!("DEBUG: Program loading completed");
Ok(())
}
+
+ /// Get CPU register data from Intel 4004 CPU
+ /// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise
+ pub fn get_cpu_register_data(&self) -> Option {
+ // Look for Intel 4004 CPU components
+ for (_name, component) in &self.components {
+ if let Ok(comp) = component.lock() {
+ // Try to downcast to Intel4004
+ if let Some(cpu) = comp.as_any().downcast_ref::() {
+ let mut index_registers = [0u8; 16];
+ for i in 0..16 {
+ index_registers[i] = cpu.get_register(i as u8).unwrap_or(0);
+ }
+
+ return Some(CpuRegisterData {
+ program_counter: cpu.get_program_counter(),
+ accumulator: cpu.get_accumulator(),
+ carry: cpu.get_carry(),
+ stack_pointer: cpu.get_stack_pointer(),
+ index_registers,
+ });
+ }
+ }
+ }
+ None
+ }
+
+ /// Get RAM data from Intel 4002 RAM chips
+ /// Parameters: max_rows - Maximum number of rows to return per bank
+ /// Returns: Vector of (chip_name, banks) where each bank is a vector of nibbles
+ pub fn get_ram_data(&self, max_rows: usize) -> RamData {
+ let mut ram_data = Vec::new();
+
+ for (name, component) in &self.components {
+ if let Ok(comp) = component.lock() {
+ // Try to downcast to Intel4002
+ if let Some(ram) = comp.as_any().downcast_ref::() {
+ let mut banks = Vec::new();
+
+ // Intel 4002 has 4 banks of 16 4-bit words each (64 nibbles total per chip)
+ // But actually it has 4 main memory registers, each with 16 4-bit characters
+ // and 4 status characters = 4 x 20 = 80 nibbles
+ for bank in 0..4 {
+ let mut bank_data = Vec::new();
+ // Read up to max_rows * 4 nibbles per bank
+ // The read_ram_direct takes a single address parameter
+ for addr in 0..(max_rows * 4).min(20) {
+ // Calculate the combined address for this bank and offset
+ let full_addr = (bank * 20 + addr) as u8;
+ // Use read_ram_direct to get the nibble at this address
+ let value = ram.read_ram_direct(full_addr).unwrap_or(0);
+ bank_data.push(value);
+ }
+ banks.push(bank_data);
+ }
+
+ ram_data.push((name.clone(), banks));
+ }
+ }
+ }
+
+ ram_data
+ }
+
+ /// Get ROM data from Intel 4001 ROM chips
+ /// Parameters: max_rows - Maximum number of bytes to return per chip
+ /// Returns: Vector of (chip_name, bytes)
+ pub fn get_rom_data(&self, max_rows: usize) -> RomData {
+ let mut rom_data = Vec::new();
+
+ for (name, component) in &self.components {
+ if let Ok(comp) = component.lock() {
+ // Try to downcast to Intel4001
+ if let Some(rom) = comp.as_any().downcast_ref::() {
+ let mut data = Vec::new();
+
+ // Intel 4001 has 256 bytes of ROM
+ for addr in 0..max_rows.min(256) {
+ let value = rom.read_rom(addr as u8).unwrap_or(0);
+ data.push(value);
+ }
+
+ rom_data.push((name.clone(), data));
+ }
+ }
+ }
+
+ rom_data
+ }
}
#[derive(Debug, Clone)]
diff --git a/tests/gui_integration_tests.rs b/tests/gui_integration_tests.rs
index b9c4429..0af389b 100644
--- a/tests/gui_integration_tests.rs
+++ b/tests/gui_integration_tests.rs
@@ -108,7 +108,7 @@ mod gui_system_integration_tests {
// Test that GUI state has proper structure for synchronization
assert_eq!(app.gui_state.memory_state.ram_contents.len(), 4);
- assert_eq!(app.gui_state.register_state.index_registers.len(), 16);
+ assert_eq!(app.gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
}
#[test]
@@ -252,7 +252,7 @@ mod gui_system_integration_tests {
assert_eq!(gui_state.register_state.accumulator, 0);
assert!(!gui_state.register_state.carry_flag);
assert_eq!(gui_state.register_state.program_counter, 0);
- assert_eq!(gui_state.register_state.index_registers.len(), 16);
+ assert_eq!(gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
assert_eq!(gui_state.register_state.stack_pointer, 0);
}
@@ -295,7 +295,7 @@ mod gui_system_integration_tests {
// Test that GUI state is properly initialized for the system
assert_eq!(app.gui_state.memory_state.ram_contents.len(), 4);
- assert_eq!(app.gui_state.register_state.index_registers.len(), 16);
+ assert_eq!(app.gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
}
#[test]
@@ -359,7 +359,7 @@ mod gui_system_integration_tests {
// For now, we verify the state structure remains consistent
assert_eq!(app.gui_state.memory_state.ram_contents.len(), 4);
- assert_eq!(app.gui_state.register_state.index_registers.len(), 16);
+ assert_eq!(app.gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
// Simulate state change
app.gui_state.cycle_count = i;
@@ -565,7 +565,7 @@ mod gui_real_time_tests {
let mut gui_state = rusty_emu::gui::state::GuiState::new();
// Verify register layout matches Intel 4004 structure
- assert_eq!(gui_state.register_state.index_registers.len(), 16); // 16 index registers
+ assert_eq!(gui_state.register_state.index_registers.len(), 16); // Intel 4004 has 16 index registers (R0-R15)
// Test register value bounds
gui_state.register_state.accumulator = 0x0F; // 4-bit value (0-15)
diff --git a/tests/gui_tests.rs b/tests/gui_tests.rs
index 3f56598..2360faa 100644
--- a/tests/gui_tests.rs
+++ b/tests/gui_tests.rs
@@ -307,10 +307,10 @@ mod gui_integration_tests {
components.render_control_panel(&mut create_test_ui(&ctx), &mut state);
// Test memory viewer rendering
- components.render_memory_viewer(&mut create_test_ui(&ctx), &state);
+ components.render_memory_viewer(&mut create_test_ui(&ctx), &mut state);
// Test register viewer rendering
- components.render_register_viewer(&mut create_test_ui(&ctx), &state);
+ components.render_register_viewer(&mut create_test_ui(&ctx), &mut state);
// Test status bar rendering
components.render_status_bar(&mut create_test_ui(&ctx), &state);
diff --git a/tests/intel_4002_state_machine_test.rs b/tests/intel_4002_state_machine_test.rs
index 8d88133..b6c7687 100644
--- a/tests/intel_4002_state_machine_test.rs
+++ b/tests/intel_4002_state_machine_test.rs
@@ -49,42 +49,6 @@ fn wait_for_pin_settlement() {
thread::sleep(Duration::from_nanos(10)); // 10ns settlement time
}
-/// Test helper to set control pins for RAM operation
-/// RAM requires: SYNC=1, CM=1 (chip_select), CI=1 (io_select for RAM vs ROM)
-fn setup_ram_operation(ram: &Intel4002, sync_high: bool, cm_high: bool) {
- let sync_pin = ram.get_pin("SYNC").unwrap();
- let cm_pin = ram.get_pin("CM").unwrap();
- let ci_pin = ram.get_pin("CI").unwrap();
-
- // Set control pins
- {
- let mut sync_guard = sync_pin.lock().unwrap();
- let mut cm_guard = cm_pin.lock().unwrap();
- let mut ci_guard = ci_pin.lock().unwrap();
-
- sync_guard.set_driver(
- Some("TEST".to_string()),
- if sync_high {
- PinValue::High
- } else {
- PinValue::Low
- },
- );
- cm_guard.set_driver(
- Some("TEST".to_string()),
- if cm_high {
- PinValue::High
- } else {
- PinValue::Low
- },
- );
- // CI must be HIGH for RAM operations (distinguishes RAM from ROM)
- ci_guard.set_driver(Some("TEST".to_string()), PinValue::High);
- }
-
- wait_for_pin_settlement();
-}
-
/// Test helper to set data bus value
fn set_data_bus(ram: &Intel4002, value: u8) {
let nibble = value & 0x0F; // Only lower 4 bits
diff --git a/tests/intel_400x_tests.rs b/tests/intel_400x_tests.rs
index 29a5818..27263a2 100644
--- a/tests/intel_400x_tests.rs
+++ b/tests/intel_400x_tests.rs
@@ -2,6 +2,7 @@ use rusty_emu::component::{BaseComponent, Component};
use rusty_emu::components::common::intel_400x::*;
use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode};
use rusty_emu::pin::{Pin, PinValue};
+use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Instant;
@@ -45,6 +46,10 @@ impl Component for MockComponent {
fn is_running(&self) -> bool {
false
}
+
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
}
impl Intel400xAddressHandling for MockComponent {