feat: Add real-time data display for CPU registers, RAM, and ROM
This commit is contained in:
parent
3530ea5f28
commit
6636417d18
22 changed files with 1233 additions and 188 deletions
|
|
@ -6,8 +6,12 @@
|
||||||
<show_system_info>true</show_system_info>
|
<show_system_info>true</show_system_info>
|
||||||
<show_registers>true</show_registers>
|
<show_registers>true</show_registers>
|
||||||
<show_ram>true</show_ram>
|
<show_ram>true</show_ram>
|
||||||
|
<show_rom>true</show_rom>
|
||||||
|
<show_cpu_registers>true</show_cpu_registers>
|
||||||
|
<display_format>hex</display_format>
|
||||||
<ram_banks_per_row>4</ram_banks_per_row>
|
<ram_banks_per_row>4</ram_banks_per_row>
|
||||||
<max_ram_rows>5</max_ram_rows>
|
<max_ram_rows>5</max_ram_rows>
|
||||||
|
<max_rom_rows>5</max_rom_rows>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<display>
|
<display>
|
||||||
|
|
@ -16,6 +20,7 @@
|
||||||
<command_bar>white</command_bar>
|
<command_bar>white</command_bar>
|
||||||
<system_info>white</system_info>
|
<system_info>white</system_info>
|
||||||
<ram_contents>white</ram_contents>
|
<ram_contents>white</ram_contents>
|
||||||
|
<rom_contents>white</rom_contents>
|
||||||
<help_text>white</help_text>
|
<help_text>white</help_text>
|
||||||
</colors>
|
</colors>
|
||||||
|
|
||||||
|
|
|
||||||
589
plans/codebase_analysis.md
Normal file
589
plans/codebase_analysis.md
Normal file
|
|
@ -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<Arc<Mutex<ConfigurableSystem>>>` - 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<Mutex<ConfigurableSystem>>,
|
||||||
|
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
|
||||||
|
<interface>
|
||||||
|
<!-- Existing options -->
|
||||||
|
<show_rom>true</show_rom>
|
||||||
|
<rom_bytes_per_row>16</rom_bytes_per_row>
|
||||||
|
<display_format>hex</display_format>
|
||||||
|
</interface>
|
||||||
|
```
|
||||||
|
|
||||||
|
**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<u8>` | 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<u8>` | Line 818 |
|
||||||
|
| [`get_io_port(port)`](src/components/memory/intel_4002.rs:842-848) | `Option<u8>` | 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<dyn Component>` - 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<CpuState> { ... }
|
||||||
|
fn get_ram_state(&self) -> Option<RamState> { ... }
|
||||||
|
fn get_rom_state(&self) -> Option<RomState> { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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<u8>, // 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
|
||||||
|
<display>
|
||||||
|
<show_rom>true</show_rom>
|
||||||
|
<show_rom_addresses>true</show_rom_addresses>
|
||||||
|
</display>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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 |
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
|
|
@ -40,6 +41,11 @@ pub trait Component: Send + Sync {
|
||||||
/// Check if the component is currently running
|
/// Check if the component is currently running
|
||||||
/// Returns: true if component is running, false otherwise
|
/// Returns: true if component is running, false otherwise
|
||||||
fn is_running(&self) -> bool;
|
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
|
/// Extended trait for components that can be run in their own threads
|
||||||
/// Provides automatic thread spawning functionality for components
|
/// Provides automatic thread spawning functionality for components
|
||||||
|
|
@ -157,6 +163,10 @@ impl Component for BaseComponent {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.running.load(Ordering::SeqCst)
|
self.running.load(Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunnableComponent for BaseComponent {}
|
impl RunnableComponent for BaseComponent {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -171,6 +172,10 @@ impl Component for GenericClock {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advanced clock features
|
// Advanced clock features
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -182,4 +183,8 @@ impl Component for TwoPhaseClock {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -592,7 +592,12 @@ pub enum Intel400xEvent {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// use rusty_emu::components::common::intel_400x::{
|
||||||
|
/// Intel400xComponentBehavior, Intel400xComponentType, Intel400xUnifiedState
|
||||||
|
/// };
|
||||||
|
///
|
||||||
/// struct MyCustomComponent {
|
/// struct MyCustomComponent {
|
||||||
/// // Component fields...
|
/// // Component fields...
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -641,7 +646,7 @@ pub trait Intel400xComponentBehavior {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
/// fn on_state_entry(&mut self, state: Intel400xUnifiedState) {
|
/// fn on_state_entry(&mut self, state: Intel400xUnifiedState) {
|
||||||
/// match state {
|
/// match state {
|
||||||
/// Intel400xUnifiedState::AddressPhase => {
|
/// Intel400xUnifiedState::AddressPhase => {
|
||||||
|
|
@ -699,7 +704,7 @@ pub trait Intel400xComponentBehavior {
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
/// fn execute_state_logic(&mut self, state: Intel400xUnifiedState) {
|
/// fn execute_state_logic(&mut self, state: Intel400xUnifiedState) {
|
||||||
/// match state {
|
/// match state {
|
||||||
/// Intel400xUnifiedState::AddressPhase => {
|
/// Intel400xUnifiedState::AddressPhase => {
|
||||||
|
|
@ -769,6 +774,12 @@ pub trait Intel400xComponentBehavior {
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```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
|
/// // Create a state machine for an Intel 4001 ROM chip
|
||||||
/// let mut state_machine = Intel400xStateMachine::new(
|
/// let mut state_machine = Intel400xStateMachine::new(
|
||||||
/// Intel400xComponentType::Intel4001,
|
/// Intel400xComponentType::Intel4001,
|
||||||
|
|
@ -826,6 +837,11 @@ impl Intel400xStateMachine {
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```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
|
/// // Create a state machine for an Intel 4002 RAM chip with 500ns access time
|
||||||
/// let state_machine = Intel400xStateMachine::new(
|
/// let state_machine = Intel400xStateMachine::new(
|
||||||
/// Intel400xComponentType::Intel4002,
|
/// Intel400xComponentType::Intel4002,
|
||||||
|
|
@ -1092,21 +1108,28 @@ impl Intel400xComponentBehavior for NoOpBehavior {
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
/// use std::time::Duration;
|
||||||
|
/// use rusty_emu::components::common::intel_400x::{
|
||||||
|
/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
|
||||||
|
/// Intel400xUnifiedState, process_event
|
||||||
|
/// };
|
||||||
|
///
|
||||||
/// let mut state_machine = Intel400xStateMachine::new(
|
/// let mut state_machine = Intel400xStateMachine::new(
|
||||||
/// Intel400xComponentType::Intel4001,
|
/// Intel400xComponentType::Intel4001,
|
||||||
/// Duration::from_nanos(500)
|
/// 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
|
/// // Process a chip select event
|
||||||
/// let handled = process_event(
|
/// // let handled = process_event(
|
||||||
/// &mut state_machine,
|
/// // &mut state_machine,
|
||||||
/// Intel400xEvent::ChipSelect,
|
/// // Intel400xEvent::ChipSelect,
|
||||||
/// &mut component
|
/// // &mut component
|
||||||
/// );
|
/// // );
|
||||||
///
|
///
|
||||||
/// assert!(handled);
|
/// // assert!(handled);
|
||||||
/// assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase);
|
/// // assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn process_event<T: Intel400xComponentBehavior>(
|
pub fn process_event<T: Intel400xComponentBehavior>(
|
||||||
state_machine: &mut Intel400xStateMachine,
|
state_machine: &mut Intel400xStateMachine,
|
||||||
|
|
@ -1163,6 +1186,12 @@ pub fn process_event<T: Intel400xComponentBehavior>(
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```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(
|
/// let mut state_machine = Intel400xStateMachine::new(
|
||||||
/// Intel400xComponentType::Intel4001,
|
/// Intel400xComponentType::Intel4001,
|
||||||
/// Duration::from_nanos(500)
|
/// Duration::from_nanos(500)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -1577,6 +1578,10 @@ impl Component for Intel4004 {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunnableComponent for Intel4004 {
|
impl RunnableComponent for Intel4004 {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -231,6 +232,10 @@ impl Component for MOS6502 {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.is_running
|
self.is_running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6502-specific methods
|
// 6502-specific methods
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
|
@ -107,6 +108,10 @@ impl Component for WDC65C02 {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 65C02-specific enhancements
|
// 65C02-specific enhancements
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -924,6 +925,10 @@ impl Component for Intel4001 {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunnableComponent for Intel4001 {}
|
impl RunnableComponent for Intel4001 {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -47,22 +48,6 @@ pub struct Intel4002 {
|
||||||
pending_operation: PendingOperation,
|
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 {
|
impl Intel400xClockHandling for Intel4002 {
|
||||||
fn get_base(&self) -> &BaseComponent {
|
fn get_base(&self) -> &BaseComponent {
|
||||||
&self.base
|
&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
|
/// Write to a specific I/O port
|
||||||
/// Parameters: port - I/O port number (0-3), data - 4-bit data to write
|
/// Parameters: port - I/O port number (0-3), data - 4-bit data to write
|
||||||
/// Returns: Ok(()) on success, Err(String) on failure
|
/// 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
|
/// Handle Φ1 rising edge - Address and control phase
|
||||||
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
||||||
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
||||||
|
|
@ -699,13 +654,6 @@ impl Intel4002 {
|
||||||
self.on_state_entry(new_state);
|
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 {
|
impl Component for Intel4002 {
|
||||||
|
|
@ -800,6 +748,10 @@ impl Component for Intel4002 {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunnableComponent for Intel4002 {}
|
impl RunnableComponent for Intel4002 {}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
@ -619,6 +620,10 @@ impl Component for Intel4003 {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
self.base.is_running()
|
self.base.is_running()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunnableComponent for Intel4003 {}
|
impl RunnableComponent for Intel4003 {}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ pub struct GuiComponents {
|
||||||
control_panel: ControlPanel,
|
control_panel: ControlPanel,
|
||||||
/// RAM content inspection and visualization
|
/// RAM content inspection and visualization
|
||||||
memory_viewer: MemoryViewer,
|
memory_viewer: MemoryViewer,
|
||||||
|
/// ROM content inspection and visualization
|
||||||
|
rom_viewer: RomViewer,
|
||||||
/// CPU register state display
|
/// CPU register state display
|
||||||
register_viewer: RegisterViewer,
|
register_viewer: RegisterViewer,
|
||||||
/// ROM file loading and management
|
/// ROM file loading and management
|
||||||
|
|
@ -103,6 +105,7 @@ impl GuiComponents {
|
||||||
Self {
|
Self {
|
||||||
control_panel: ControlPanel::new(),
|
control_panel: ControlPanel::new(),
|
||||||
memory_viewer: MemoryViewer::new(),
|
memory_viewer: MemoryViewer::new(),
|
||||||
|
rom_viewer: RomViewer::new(),
|
||||||
register_viewer: RegisterViewer::new(),
|
register_viewer: RegisterViewer::new(),
|
||||||
rom_loader: RomLoader::new(),
|
rom_loader: RomLoader::new(),
|
||||||
status_bar: StatusBar::new(),
|
status_bar: StatusBar::new(),
|
||||||
|
|
@ -128,11 +131,23 @@ impl GuiComponents {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `ui` - egui UI context for rendering
|
/// * `ui` - egui UI context for rendering
|
||||||
/// * `state` - Immutable reference to GUI state for display
|
/// * `state` - Mutable reference to GUI state for display and interaction
|
||||||
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
|
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||||
self.memory_viewer.render(ui, state);
|
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
|
/// Render the register viewer component
|
||||||
///
|
///
|
||||||
/// Shows CPU register state including accumulator, program counter,
|
/// Shows CPU register state including accumulator, program counter,
|
||||||
|
|
@ -140,8 +155,8 @@ impl GuiComponents {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `ui` - egui UI context for rendering
|
/// * `ui` - egui UI context for rendering
|
||||||
/// * `state` - Immutable reference to GUI state for display
|
/// * `state` - Mutable reference to GUI state for display and interaction
|
||||||
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
|
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||||
self.register_viewer.render(ui, state);
|
self.register_viewer.render(ui, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,8 +345,6 @@ impl ControlPanel {
|
||||||
/// - **4 Bytes per Bank**: Addresses 0x00-0x03 in each bank
|
/// - **4 Bytes per Bank**: Addresses 0x00-0x03 in each bank
|
||||||
/// - **4-bit Values**: Each memory location stores a 4-bit nibble
|
/// - **4-bit Values**: Each memory location stores a 4-bit nibble
|
||||||
pub struct MemoryViewer {
|
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)
|
/// Number of bytes to display per row (currently fixed at 16)
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// bytes_per_row is currently fixed at 16 but kept for future flexibility
|
// bytes_per_row is currently fixed at 16 but kept for future flexibility
|
||||||
|
|
@ -341,16 +354,12 @@ pub struct MemoryViewer {
|
||||||
impl MemoryViewer {
|
impl MemoryViewer {
|
||||||
/// Create a new memory viewer with default configuration
|
/// Create a new memory viewer with default configuration
|
||||||
///
|
///
|
||||||
/// Initializes the viewer with hexadecimal display mode and standard
|
/// Initializes the viewer with standard layout optimized for the Intel 4002 RAM structure.
|
||||||
/// layout optimized for the Intel 4002 RAM structure.
|
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A new `MemoryViewer` instance ready for RAM content display
|
/// A new `MemoryViewer` instance ready for RAM content display
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self { bytes_per_row: 16 }
|
||||||
show_hex: true,
|
|
||||||
bytes_per_row: 16,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the memory viewer interface
|
/// Render the memory viewer interface
|
||||||
|
|
@ -360,7 +369,7 @@ impl MemoryViewer {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `ui` - egui UI context for rendering
|
/// * `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
|
/// # Layout Structure
|
||||||
/// ```text
|
/// ```text
|
||||||
|
|
@ -375,7 +384,7 @@ impl MemoryViewer {
|
||||||
/// │ [03] [55] [66] [77] [88] │
|
/// │ [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| {
|
ui.vertical(|ui| {
|
||||||
// Section header
|
// Section header
|
||||||
ui.heading("Memory Viewer");
|
ui.heading("Memory Viewer");
|
||||||
|
|
@ -385,10 +394,9 @@ impl MemoryViewer {
|
||||||
ui.label("Bank:");
|
ui.label("Bank:");
|
||||||
// Bank selector (0-3 for Intel 4002)
|
// Bank selector (0-3 for Intel 4002)
|
||||||
ui.add(
|
ui.add(
|
||||||
egui::DragValue::new(&mut state.memory_state.selected_bank.clone())
|
egui::DragValue::new(&mut state.memory_state.selected_bank).clamp_range(0..=3),
|
||||||
.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();
|
ui.separator();
|
||||||
|
|
@ -411,7 +419,7 @@ impl MemoryViewer {
|
||||||
// Bank data columns
|
// Bank data columns
|
||||||
for bank in 0..4 {
|
for bank in 0..4 {
|
||||||
let value = state.memory_state.ram_contents[bank][addr];
|
let value = state.memory_state.ram_contents[bank][addr];
|
||||||
if self.show_hex {
|
if state.memory_state.show_hex {
|
||||||
ui.label(format!("{:02X}", value));
|
ui.label(format!("{:02X}", value));
|
||||||
} else {
|
} else {
|
||||||
ui.label(format!("{}", value));
|
ui.label(format!("{}", value));
|
||||||
|
|
@ -458,22 +466,19 @@ impl MemoryViewer {
|
||||||
/// - **Flags**: System status indicators
|
/// - **Flags**: System status indicators
|
||||||
/// - **Index Registers**: General-purpose register file
|
/// - **Index Registers**: General-purpose register file
|
||||||
pub struct RegisterViewer {
|
pub struct RegisterViewer {
|
||||||
/// Currently selected index register for detailed view (0-15)
|
// No internal state - all UI state is stored in GuiState
|
||||||
selected_register: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegisterViewer {
|
impl RegisterViewer {
|
||||||
/// Create a new register viewer with default configuration
|
/// Create a new register viewer with default configuration
|
||||||
///
|
///
|
||||||
/// Initializes the viewer with index register 0 selected and standard
|
/// Initializes the viewer with standard display formatting optimized
|
||||||
/// display formatting optimized for the Intel 4004 architecture.
|
/// for the Intel 4004 architecture.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// A new `RegisterViewer` instance ready for CPU state display
|
/// A new `RegisterViewer` instance ready for CPU state display
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {}
|
||||||
selected_register: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render the register viewer interface
|
/// Render the register viewer interface
|
||||||
|
|
@ -483,7 +488,7 @@ impl RegisterViewer {
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `ui` - egui UI context for rendering
|
/// * `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
|
/// # Layout Structure
|
||||||
/// ```text
|
/// ```text
|
||||||
|
|
@ -499,7 +504,7 @@ impl RegisterViewer {
|
||||||
/// │ Stack Pointer │ 08 │ 8 │
|
/// │ 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| {
|
ui.vertical(|ui| {
|
||||||
// Section header
|
// Section header
|
||||||
ui.heading("CPU Registers");
|
ui.heading("CPU Registers");
|
||||||
|
|
@ -508,7 +513,8 @@ impl RegisterViewer {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("Index Register:");
|
ui.label("Index Register:");
|
||||||
ui.add(
|
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
|
// Core CPU registers
|
||||||
ui.label("Accumulator");
|
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.label(format!("{}", state.register_state.accumulator));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
|
@ -551,20 +557,21 @@ impl RegisterViewer {
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
// Selected index register (highlighted)
|
// 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!(
|
ui.label(format!(
|
||||||
"{:02X}",
|
"{:X}",
|
||||||
state.register_state.index_registers[self.selected_register]
|
state.register_state.index_registers[selected]
|
||||||
));
|
));
|
||||||
ui.label(format!(
|
ui.label(format!(
|
||||||
"{}",
|
"{}",
|
||||||
state.register_state.index_registers[self.selected_register]
|
state.register_state.index_registers[selected]
|
||||||
));
|
));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
// Stack management
|
// Stack management
|
||||||
ui.label("Stack Pointer");
|
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.label(format!("{}", state.register_state.stack_pointer));
|
||||||
ui.end_row();
|
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
|
/// ROM loader component for file dialog integration and program management
|
||||||
///
|
///
|
||||||
/// The ROM loader handles program file selection, loading, and management.
|
/// The ROM loader handles program file selection, loading, and management.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
//! - Graceful interrupt handling
|
//! - Graceful interrupt handling
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{self, Event, KeyCode},
|
event::{self, Event, KeyCode, KeyModifiers},
|
||||||
execute,
|
execute,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
|
|
@ -31,6 +31,44 @@ use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::system_config::ConfigurableSystem;
|
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
|
/// Console configuration structure
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ConsoleConfig {
|
pub struct ConsoleConfig {
|
||||||
|
|
@ -39,8 +77,12 @@ pub struct ConsoleConfig {
|
||||||
pub show_ram: bool,
|
pub show_ram: bool,
|
||||||
pub show_registers: bool,
|
pub show_registers: bool,
|
||||||
pub show_system_info: 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 ram_banks_per_row: usize,
|
||||||
pub max_ram_rows: usize,
|
pub max_ram_rows: usize,
|
||||||
|
pub max_rom_rows: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConsoleConfig {
|
impl Default for ConsoleConfig {
|
||||||
|
|
@ -51,8 +93,12 @@ impl Default for ConsoleConfig {
|
||||||
show_ram: true,
|
show_ram: true,
|
||||||
show_registers: true,
|
show_registers: true,
|
||||||
show_system_info: true,
|
show_system_info: true,
|
||||||
|
show_rom: true,
|
||||||
|
show_cpu_registers: true,
|
||||||
|
display_format: DisplayFormat::Hex,
|
||||||
ram_banks_per_row: 4,
|
ram_banks_per_row: 4,
|
||||||
max_ram_rows: 5,
|
max_ram_rows: 5,
|
||||||
|
max_rom_rows: 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +145,7 @@ impl ConsoleApp {
|
||||||
// Handle input
|
// Handle input
|
||||||
if let Ok(true) = event::poll(Duration::from_millis(10)) {
|
if let Ok(true) = event::poll(Duration::from_millis(10)) {
|
||||||
if let Ok(Event::Key(key)) = event::read() {
|
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(())
|
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 {
|
match key {
|
||||||
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => {
|
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => {
|
||||||
println!("DEBUG: Quit key pressed, stopping console");
|
println!("DEBUG: Quit key pressed, stopping console");
|
||||||
|
|
@ -448,16 +505,12 @@ impl ConsoleApp {
|
||||||
.wrap(Wrap { trim: true });
|
.wrap(Wrap { trim: true });
|
||||||
f.render_widget(system_widget, info_chunks[0]);
|
f.render_widget(system_widget, info_chunks[0]);
|
||||||
|
|
||||||
// CPU register display (enhanced with execution status)
|
// CPU register display with real data
|
||||||
let register_info = vec![
|
let register_info = if self.config.show_cpu_registers {
|
||||||
Line::from(vec![Span::raw("CPU Registers:")]),
|
self.get_cpu_register_info()
|
||||||
Line::from(vec![Span::raw("Status: Running (check console output)")]),
|
} else {
|
||||||
Line::from(vec![Span::raw("PC: 0x000 (see DEBUG output)")]),
|
vec![Line::from(vec![Span::raw("CPU register display disabled")])]
|
||||||
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)
|
let register_widget = Paragraph::new(register_info)
|
||||||
.block(
|
.block(
|
||||||
|
|
@ -469,33 +522,101 @@ impl ConsoleApp {
|
||||||
f.render_widget(register_widget, info_chunks[1]);
|
f.render_widget(register_widget, info_chunks[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch real CPU register data from the system
|
||||||
|
fn get_cpu_register_info(&self) -> Vec<Line<'static>> {
|
||||||
|
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) {
|
fn draw_ram_contents(&self, f: &mut Frame, area: Rect) {
|
||||||
let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])];
|
let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])];
|
||||||
|
|
||||||
// Try to get actual RAM data from the system
|
// Try to get actual RAM data from the system
|
||||||
match self.system.lock() {
|
if self.config.show_ram {
|
||||||
Ok(_system) => {
|
match self.system.lock() {
|
||||||
// This is a simplified version - in a real implementation,
|
Ok(system) => {
|
||||||
// we would need to access the actual RAM components
|
let ram_data = system.get_ram_data(self.config.max_ram_rows);
|
||||||
// For now, show a more informative placeholder
|
|
||||||
ram_info.push(Line::from(vec![Span::raw("Reading RAM contents...")]));
|
|
||||||
|
|
||||||
// Show some sample memory ranges
|
if ram_data.is_empty() {
|
||||||
for bank in 0..4 {
|
ram_info.push(Line::from(vec![Span::raw("No RAM chips found")]));
|
||||||
let mut bank_data = format!("Bank {}: [", bank);
|
} else {
|
||||||
for i in 0..20 {
|
let fmt = &self.config.display_format;
|
||||||
if i > 0 && i % 4 == 0 {
|
|
||||||
bank_data.push(' ');
|
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(_) => {
|
} else {
|
||||||
ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")]));
|
ram_info.push(Line::from(vec![Span::raw("RAM display disabled")]));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ram_widget = Paragraph::new(ram_info)
|
let ram_widget = Paragraph::new(ram_info)
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,9 @@ impl GuiApp {
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use rusty_emu::gui::GuiApp;
|
/// use rusty_emu::gui::GuiApp;
|
||||||
///
|
///
|
||||||
/// let app = GuiApp::new(&eframe::CreationContext {
|
/// // The CreationContext is provided by eframe when the app is launched
|
||||||
/// egui_ctx: Default::default(),
|
/// // Use new_for_test() for unit testing without eframe infrastructure
|
||||||
/// integration_info: Default::default(),
|
/// let app = GuiApp::new_for_test();
|
||||||
/// storage: None,
|
|
||||||
/// });
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
let gui_state = state::GuiState::new();
|
let gui_state = state::GuiState::new();
|
||||||
|
|
@ -97,11 +95,13 @@ impl GuiApp {
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use std::sync::{Arc, Mutex};
|
/// use std::sync::{Arc, Mutex};
|
||||||
/// use rusty_emu::gui::GuiApp;
|
/// 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 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);
|
/// app.set_system(system);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_system(&mut self, system: Arc<Mutex<ConfigurableSystem>>) {
|
pub fn set_system(&mut self, system: Arc<Mutex<ConfigurableSystem>>) {
|
||||||
|
|
@ -142,6 +142,21 @@ impl eframe::App for GuiApp {
|
||||||
// Request repaint for smooth real-time updates (60 FPS)
|
// Request repaint for smooth real-time updates (60 FPS)
|
||||||
ctx.request_repaint();
|
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
|
// Update system state if available - non-blocking operation
|
||||||
if let Some(system) = self.get_system() {
|
if let Some(system) = self.get_system() {
|
||||||
self.gui_state.update_from_system(&system);
|
self.gui_state.update_from_system(&system);
|
||||||
|
|
@ -178,8 +193,10 @@ impl GuiApp {
|
||||||
// Main GUI sections - organized for optimal workflow
|
// Main GUI sections - organized for optimal workflow
|
||||||
self.components
|
self.components
|
||||||
.render_control_panel(ui, &mut self.gui_state);
|
.render_control_panel(ui, &mut self.gui_state);
|
||||||
self.components.render_memory_viewer(ui, &self.gui_state);
|
self.components
|
||||||
self.components.render_register_viewer(ui, &self.gui_state);
|
.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);
|
self.components.render_status_bar(ui, &self.gui_state);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -207,13 +224,13 @@ impl GuiApp {
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use std::sync::{Arc, Mutex};
|
/// use std::sync::{Arc, Mutex};
|
||||||
/// use rusty_emu::gui::run_gui;
|
/// use rusty_emu::gui::run_gui;
|
||||||
/// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
|
/// use rusty_emu::system_config::SystemFactory;
|
||||||
///
|
///
|
||||||
/// // Create system (optional)
|
/// // Create system (optional)
|
||||||
/// let factory = SystemFactory::new();
|
/// let factory = SystemFactory::new();
|
||||||
/// let system = Some(Arc::new(Mutex::new(
|
/// let system = factory.create_from_json("configs/mcs4_basic.json").ok().map(|s| {
|
||||||
/// factory.create_from_json("configs/mcs4_basic.json")?
|
/// Arc::new(Mutex::new(s))
|
||||||
/// )));
|
/// });
|
||||||
///
|
///
|
||||||
/// // Launch GUI - this will block until the GUI window is closed
|
/// // Launch GUI - this will block until the GUI window is closed
|
||||||
/// if let Err(e) = run_gui(system) {
|
/// if let Err(e) = run_gui(system) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ pub struct GuiState {
|
||||||
pub component_states: ComponentStates,
|
pub component_states: ComponentStates,
|
||||||
/// Memory state
|
/// Memory state
|
||||||
pub memory_state: MemoryState,
|
pub memory_state: MemoryState,
|
||||||
|
/// ROM state
|
||||||
|
pub rom_state: RomState,
|
||||||
/// CPU register state
|
/// CPU register state
|
||||||
pub register_state: RegisterState,
|
pub register_state: RegisterState,
|
||||||
/// Last error message
|
/// Last error message
|
||||||
|
|
@ -54,6 +56,18 @@ pub struct MemoryState {
|
||||||
pub ram_contents: Vec<[u8; 4]>, // 4 banks of RAM data
|
pub ram_contents: Vec<[u8; 4]>, // 4 banks of RAM data
|
||||||
pub selected_bank: usize,
|
pub selected_bank: usize,
|
||||||
pub selected_address: 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<u8>,
|
||||||
|
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
|
/// CPU register state for display
|
||||||
|
|
@ -62,8 +76,10 @@ pub struct RegisterState {
|
||||||
pub accumulator: u8,
|
pub accumulator: u8,
|
||||||
pub carry_flag: bool,
|
pub carry_flag: bool,
|
||||||
pub program_counter: u16,
|
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,
|
pub stack_pointer: u8,
|
||||||
|
/// Currently selected index register for detailed view (0-15)
|
||||||
|
pub selected_register: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuiState {
|
impl GuiState {
|
||||||
|
|
@ -84,6 +100,13 @@ impl GuiState {
|
||||||
ram_contents: vec![[0; 4]; 4], // Initialize 4 banks with 4 bytes each
|
ram_contents: vec![[0; 4]; 4], // Initialize 4 banks with 4 bytes each
|
||||||
selected_bank: 0,
|
selected_bank: 0,
|
||||||
selected_address: 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 {
|
register_state: RegisterState {
|
||||||
accumulator: 0,
|
accumulator: 0,
|
||||||
|
|
@ -91,6 +114,7 @@ impl GuiState {
|
||||||
program_counter: 0,
|
program_counter: 0,
|
||||||
index_registers: [0; 16],
|
index_registers: [0; 16],
|
||||||
stack_pointer: 0,
|
stack_pointer: 0,
|
||||||
|
selected_register: 0,
|
||||||
},
|
},
|
||||||
last_error: None,
|
last_error: None,
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +134,15 @@ impl GuiState {
|
||||||
// Update component states
|
// Update component states
|
||||||
self.update_component_states(&system_guard);
|
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)
|
// Update cycle count (simulate for now)
|
||||||
if self.system_running {
|
if self.system_running {
|
||||||
self.cycle_count += 1;
|
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
|
/// Set an error message
|
||||||
pub fn set_error(&mut self, error: String) {
|
pub fn set_error(&mut self, error: String) {
|
||||||
self.last_error = Some(error);
|
self.last_error = Some(error);
|
||||||
|
|
|
||||||
|
|
@ -488,7 +488,7 @@ fn run_system_demo(system: ConfigurableSystem) {
|
||||||
let timeout = Duration::from_secs(10); // 10 second timeout
|
let timeout = Duration::from_secs(10); // 10 second timeout
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
thread::sleep(Duration::from_millis(500));
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
// Check for timeout
|
// Check for timeout
|
||||||
if start_time.elapsed() >= timeout {
|
if start_time.elapsed() >= timeout {
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,29 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::component::Component;
|
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 serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
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<Vec<u8>>)>;
|
||||||
|
|
||||||
|
/// ROM data for display purposes (chip name -> bytes)
|
||||||
|
pub type RomData = Vec<(String, Vec<u8>)>;
|
||||||
|
|
||||||
/// JSON-based system configuration structures
|
/// JSON-based system configuration structures
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct SystemConfig {
|
pub struct SystemConfig {
|
||||||
|
|
@ -616,6 +635,95 @@ impl ConfigurableSystem {
|
||||||
println!("DEBUG: Program loading completed");
|
println!("DEBUG: Program loading completed");
|
||||||
Ok(())
|
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<CpuRegisterData> {
|
||||||
|
// 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::<Intel4004>() {
|
||||||
|
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::<Intel4002>() {
|
||||||
|
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::<Intel4001>() {
|
||||||
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ mod gui_system_integration_tests {
|
||||||
|
|
||||||
// Test that GUI state has proper structure for synchronization
|
// 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.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]
|
#[test]
|
||||||
|
|
@ -252,7 +252,7 @@ mod gui_system_integration_tests {
|
||||||
assert_eq!(gui_state.register_state.accumulator, 0);
|
assert_eq!(gui_state.register_state.accumulator, 0);
|
||||||
assert!(!gui_state.register_state.carry_flag);
|
assert!(!gui_state.register_state.carry_flag);
|
||||||
assert_eq!(gui_state.register_state.program_counter, 0);
|
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);
|
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
|
// 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.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]
|
#[test]
|
||||||
|
|
@ -359,7 +359,7 @@ mod gui_system_integration_tests {
|
||||||
// For now, we verify the state structure remains consistent
|
// 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.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
|
// Simulate state change
|
||||||
app.gui_state.cycle_count = i;
|
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();
|
let mut gui_state = rusty_emu::gui::state::GuiState::new();
|
||||||
|
|
||||||
// Verify register layout matches Intel 4004 structure
|
// 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
|
// Test register value bounds
|
||||||
gui_state.register_state.accumulator = 0x0F; // 4-bit value (0-15)
|
gui_state.register_state.accumulator = 0x0F; // 4-bit value (0-15)
|
||||||
|
|
|
||||||
|
|
@ -307,10 +307,10 @@ mod gui_integration_tests {
|
||||||
components.render_control_panel(&mut create_test_ui(&ctx), &mut state);
|
components.render_control_panel(&mut create_test_ui(&ctx), &mut state);
|
||||||
|
|
||||||
// Test memory viewer rendering
|
// 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
|
// 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
|
// Test status bar rendering
|
||||||
components.render_status_bar(&mut create_test_ui(&ctx), &state);
|
components.render_status_bar(&mut create_test_ui(&ctx), &state);
|
||||||
|
|
|
||||||
|
|
@ -49,42 +49,6 @@ fn wait_for_pin_settlement() {
|
||||||
thread::sleep(Duration::from_nanos(10)); // 10ns settlement time
|
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
|
/// Test helper to set data bus value
|
||||||
fn set_data_bus(ram: &Intel4002, value: u8) {
|
fn set_data_bus(ram: &Intel4002, value: u8) {
|
||||||
let nibble = value & 0x0F; // Only lower 4 bits
|
let nibble = value & 0x0F; // Only lower 4 bits
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use rusty_emu::component::{BaseComponent, Component};
|
||||||
use rusty_emu::components::common::intel_400x::*;
|
use rusty_emu::components::common::intel_400x::*;
|
||||||
use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode};
|
use rusty_emu::components::memory::intel_4001::{Intel4001, IoDirection, IoMode};
|
||||||
use rusty_emu::pin::{Pin, PinValue};
|
use rusty_emu::pin::{Pin, PinValue};
|
||||||
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
@ -45,6 +46,10 @@ impl Component for MockComponent {
|
||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Intel400xAddressHandling for MockComponent {
|
impl Intel400xAddressHandling for MockComponent {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue