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
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
|
|
@ -40,6 +41,11 @@ pub trait Component: Send + Sync {
|
|||
/// Check if the component is currently running
|
||||
/// Returns: true if component is running, false otherwise
|
||||
fn is_running(&self) -> bool;
|
||||
|
||||
/// Get a reference to this component as Any for downcasting
|
||||
/// This allows runtime type inspection to access component-specific methods
|
||||
/// Returns: A reference to self as &dyn Any
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
/// Extended trait for components that can be run in their own threads
|
||||
/// Provides automatic thread spawning functionality for components
|
||||
|
|
@ -157,6 +163,10 @@ impl Component for BaseComponent {
|
|||
fn is_running(&self) -> bool {
|
||||
self.running.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RunnableComponent for BaseComponent {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -171,6 +172,10 @@ impl Component for GenericClock {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Advanced clock features
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -182,4 +183,8 @@ impl Component for TwoPhaseClock {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -592,7 +592,12 @@ pub enum Intel400xEvent {
|
|||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// use std::time::Duration;
|
||||
/// use rusty_emu::components::common::intel_400x::{
|
||||
/// Intel400xComponentBehavior, Intel400xComponentType, Intel400xUnifiedState
|
||||
/// };
|
||||
///
|
||||
/// struct MyCustomComponent {
|
||||
/// // Component fields...
|
||||
/// }
|
||||
|
|
@ -641,7 +646,7 @@ pub trait Intel400xComponentBehavior {
|
|||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// fn on_state_entry(&mut self, state: Intel400xUnifiedState) {
|
||||
/// match state {
|
||||
/// Intel400xUnifiedState::AddressPhase => {
|
||||
|
|
@ -699,7 +704,7 @@ pub trait Intel400xComponentBehavior {
|
|||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// fn execute_state_logic(&mut self, state: Intel400xUnifiedState) {
|
||||
/// match state {
|
||||
/// Intel400xUnifiedState::AddressPhase => {
|
||||
|
|
@ -769,6 +774,12 @@ pub trait Intel400xComponentBehavior {
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::time::Duration;
|
||||
/// use rusty_emu::components::common::intel_400x::{
|
||||
/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
|
||||
/// Intel400xUnifiedState, process_event_direct
|
||||
/// };
|
||||
///
|
||||
/// // Create a state machine for an Intel 4001 ROM chip
|
||||
/// let mut state_machine = Intel400xStateMachine::new(
|
||||
/// Intel400xComponentType::Intel4001,
|
||||
|
|
@ -826,6 +837,11 @@ impl Intel400xStateMachine {
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::time::Duration;
|
||||
/// use rusty_emu::components::common::intel_400x::{
|
||||
/// Intel400xStateMachine, Intel400xComponentType, Intel400xUnifiedState
|
||||
/// };
|
||||
///
|
||||
/// // Create a state machine for an Intel 4002 RAM chip with 500ns access time
|
||||
/// let state_machine = Intel400xStateMachine::new(
|
||||
/// Intel400xComponentType::Intel4002,
|
||||
|
|
@ -1092,21 +1108,28 @@ impl Intel400xComponentBehavior for NoOpBehavior {
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::time::Duration;
|
||||
/// use rusty_emu::components::common::intel_400x::{
|
||||
/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
|
||||
/// Intel400xUnifiedState, process_event
|
||||
/// };
|
||||
///
|
||||
/// let mut state_machine = Intel400xStateMachine::new(
|
||||
/// Intel400xComponentType::Intel4001,
|
||||
/// Duration::from_nanos(500)
|
||||
/// );
|
||||
/// let mut component = Intel4001Component::new();
|
||||
/// // Note: Intel4001Component would be imported from its module
|
||||
/// // let mut component = Intel4001Component::new();
|
||||
///
|
||||
/// // Process a chip select event
|
||||
/// let handled = process_event(
|
||||
/// &mut state_machine,
|
||||
/// Intel400xEvent::ChipSelect,
|
||||
/// &mut component
|
||||
/// );
|
||||
/// // let handled = process_event(
|
||||
/// // &mut state_machine,
|
||||
/// // Intel400xEvent::ChipSelect,
|
||||
/// // &mut component
|
||||
/// // );
|
||||
///
|
||||
/// assert!(handled);
|
||||
/// assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase);
|
||||
/// // assert!(handled);
|
||||
/// // assert_eq!(state_machine.get_state(), Intel400xUnifiedState::AddressPhase);
|
||||
/// ```
|
||||
pub fn process_event<T: Intel400xComponentBehavior>(
|
||||
state_machine: &mut Intel400xStateMachine,
|
||||
|
|
@ -1163,6 +1186,12 @@ pub fn process_event<T: Intel400xComponentBehavior>(
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::time::Duration;
|
||||
/// use rusty_emu::components::common::intel_400x::{
|
||||
/// Intel400xStateMachine, Intel400xComponentType, Intel400xEvent,
|
||||
/// Intel400xUnifiedState, process_event_direct
|
||||
/// };
|
||||
///
|
||||
/// let mut state_machine = Intel400xStateMachine::new(
|
||||
/// Intel400xComponentType::Intel4001,
|
||||
/// Duration::from_nanos(500)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -1577,6 +1578,10 @@ impl Component for Intel4004 {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RunnableComponent for Intel4004 {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -231,6 +232,10 @@ impl Component for MOS6502 {
|
|||
fn is_running(&self) -> bool {
|
||||
self.is_running
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// 6502-specific methods
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
|
|
@ -107,6 +108,10 @@ impl Component for WDC65C02 {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// 65C02-specific enhancements
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -924,6 +925,10 @@ impl Component for Intel4001 {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RunnableComponent for Intel4001 {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -47,22 +48,6 @@ pub struct Intel4002 {
|
|||
pending_operation: PendingOperation,
|
||||
}
|
||||
|
||||
/// RAM register operations (for WRM/WRR instructions)
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RamOperation {
|
||||
Read, // Read from RAM register
|
||||
Write, // Write to RAM register
|
||||
WriteOr, // Write to RAM register with OR operation
|
||||
WriteAnd, // Write to RAM register with AND operation
|
||||
}
|
||||
|
||||
/// RAM access modes (for SRC/SRC instructions)
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RamAccessMode {
|
||||
Direct, // Direct access to RAM
|
||||
Indirect, // Indirect access via register pair
|
||||
}
|
||||
|
||||
impl Intel400xClockHandling for Intel4002 {
|
||||
fn get_base(&self) -> &BaseComponent {
|
||||
&self.base
|
||||
|
|
@ -403,17 +388,6 @@ impl Intel4002 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Read from a specific I/O port
|
||||
/// Parameters: port - I/O port number (0-3)
|
||||
/// Returns: 4-bit value from I/O port
|
||||
pub fn read_io_port(&self, port: usize) -> u8 {
|
||||
if port < 4 {
|
||||
self.io_ports[port]
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Write to a specific I/O port
|
||||
/// Parameters: port - I/O port number (0-3), data - 4-bit data to write
|
||||
/// Returns: Ok(()) on success, Err(String) on failure
|
||||
|
|
@ -453,25 +427,6 @@ impl Intel4002 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Drive status character on status pins
|
||||
fn drive_status_character(&self) {
|
||||
let status = self.status_character & 0x7F; // Ensure only 7 bits
|
||||
|
||||
for i in 0..4 {
|
||||
if let Ok(pin) = self.base.get_pin(&format!("S{}", i)) {
|
||||
if let Ok(mut pin_guard) = pin.lock() {
|
||||
let bit_value = (status >> i) & 1;
|
||||
let pin_value = if bit_value == 1 {
|
||||
PinValue::High
|
||||
} else {
|
||||
PinValue::Low
|
||||
};
|
||||
pin_guard.set_driver(Some(self.base.name()), pin_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle Φ1 rising edge - Address and control phase
|
||||
/// Hardware: Φ1 high = CPU drives bus with address/control information
|
||||
/// Memory operations start when SYNC goes high during Φ1 rising edge
|
||||
|
|
@ -699,13 +654,6 @@ impl Intel4002 {
|
|||
self.on_state_entry(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to execute unified state machine logic
|
||||
fn execute_state_machine(&mut self) {
|
||||
// Execute the current state logic using the unified state machine
|
||||
let current_state = self.state_machine.get_state();
|
||||
self.execute_state_logic(current_state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Intel4002 {
|
||||
|
|
@ -800,6 +748,10 @@ impl Component for Intel4002 {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RunnableComponent for Intel4002 {}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
|
|
@ -619,6 +620,10 @@ impl Component for Intel4003 {
|
|||
fn is_running(&self) -> bool {
|
||||
self.base.is_running()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RunnableComponent for Intel4003 {}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ pub struct GuiComponents {
|
|||
control_panel: ControlPanel,
|
||||
/// RAM content inspection and visualization
|
||||
memory_viewer: MemoryViewer,
|
||||
/// ROM content inspection and visualization
|
||||
rom_viewer: RomViewer,
|
||||
/// CPU register state display
|
||||
register_viewer: RegisterViewer,
|
||||
/// ROM file loading and management
|
||||
|
|
@ -103,6 +105,7 @@ impl GuiComponents {
|
|||
Self {
|
||||
control_panel: ControlPanel::new(),
|
||||
memory_viewer: MemoryViewer::new(),
|
||||
rom_viewer: RomViewer::new(),
|
||||
register_viewer: RegisterViewer::new(),
|
||||
rom_loader: RomLoader::new(),
|
||||
status_bar: StatusBar::new(),
|
||||
|
|
@ -128,11 +131,23 @@ impl GuiComponents {
|
|||
///
|
||||
/// # Arguments
|
||||
/// * `ui` - egui UI context for rendering
|
||||
/// * `state` - Immutable reference to GUI state for display
|
||||
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
|
||||
/// * `state` - Mutable reference to GUI state for display and interaction
|
||||
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
self.memory_viewer.render(ui, state);
|
||||
}
|
||||
|
||||
/// Render the ROM viewer component
|
||||
///
|
||||
/// Displays ROM contents in a tabular format with address selection
|
||||
/// and hex/decimal viewing options.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ui` - egui UI context for rendering
|
||||
/// * `state` - Mutable reference to GUI state for display and interaction
|
||||
pub fn render_rom_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
self.rom_viewer.render(ui, state);
|
||||
}
|
||||
|
||||
/// Render the register viewer component
|
||||
///
|
||||
/// Shows CPU register state including accumulator, program counter,
|
||||
|
|
@ -140,8 +155,8 @@ impl GuiComponents {
|
|||
///
|
||||
/// # Arguments
|
||||
/// * `ui` - egui UI context for rendering
|
||||
/// * `state` - Immutable reference to GUI state for display
|
||||
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
|
||||
/// * `state` - Mutable reference to GUI state for display and interaction
|
||||
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
self.register_viewer.render(ui, state);
|
||||
}
|
||||
|
||||
|
|
@ -330,8 +345,6 @@ impl ControlPanel {
|
|||
/// - **4 Bytes per Bank**: Addresses 0x00-0x03 in each bank
|
||||
/// - **4-bit Values**: Each memory location stores a 4-bit nibble
|
||||
pub struct MemoryViewer {
|
||||
/// Display mode: true for hexadecimal, false for decimal
|
||||
show_hex: bool,
|
||||
/// Number of bytes to display per row (currently fixed at 16)
|
||||
#[allow(dead_code)]
|
||||
// bytes_per_row is currently fixed at 16 but kept for future flexibility
|
||||
|
|
@ -341,16 +354,12 @@ pub struct MemoryViewer {
|
|||
impl MemoryViewer {
|
||||
/// Create a new memory viewer with default configuration
|
||||
///
|
||||
/// Initializes the viewer with hexadecimal display mode and standard
|
||||
/// layout optimized for the Intel 4002 RAM structure.
|
||||
/// Initializes the viewer with standard layout optimized for the Intel 4002 RAM structure.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `MemoryViewer` instance ready for RAM content display
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
show_hex: true,
|
||||
bytes_per_row: 16,
|
||||
}
|
||||
Self { bytes_per_row: 16 }
|
||||
}
|
||||
|
||||
/// Render the memory viewer interface
|
||||
|
|
@ -360,7 +369,7 @@ impl MemoryViewer {
|
|||
///
|
||||
/// # Arguments
|
||||
/// * `ui` - egui UI context for rendering
|
||||
/// * `state` - Immutable reference to GUI state containing memory data
|
||||
/// * `state` - Mutable reference to GUI state containing memory data
|
||||
///
|
||||
/// # Layout Structure
|
||||
/// ```text
|
||||
|
|
@ -375,7 +384,7 @@ impl MemoryViewer {
|
|||
/// │ [03] [55] [66] [77] [88] │
|
||||
/// └─────────────────────────────────────────────────┘
|
||||
/// ```
|
||||
pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
|
||||
pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
ui.vertical(|ui| {
|
||||
// Section header
|
||||
ui.heading("Memory Viewer");
|
||||
|
|
@ -385,10 +394,9 @@ impl MemoryViewer {
|
|||
ui.label("Bank:");
|
||||
// Bank selector (0-3 for Intel 4002)
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut state.memory_state.selected_bank.clone())
|
||||
.clamp_range(0..=3),
|
||||
egui::DragValue::new(&mut state.memory_state.selected_bank).clamp_range(0..=3),
|
||||
);
|
||||
ui.checkbox(&mut self.show_hex.clone(), "Hex View");
|
||||
ui.checkbox(&mut state.memory_state.show_hex, "Hex View");
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
|
@ -411,7 +419,7 @@ impl MemoryViewer {
|
|||
// Bank data columns
|
||||
for bank in 0..4 {
|
||||
let value = state.memory_state.ram_contents[bank][addr];
|
||||
if self.show_hex {
|
||||
if state.memory_state.show_hex {
|
||||
ui.label(format!("{:02X}", value));
|
||||
} else {
|
||||
ui.label(format!("{}", value));
|
||||
|
|
@ -458,22 +466,19 @@ impl MemoryViewer {
|
|||
/// - **Flags**: System status indicators
|
||||
/// - **Index Registers**: General-purpose register file
|
||||
pub struct RegisterViewer {
|
||||
/// Currently selected index register for detailed view (0-15)
|
||||
selected_register: usize,
|
||||
// No internal state - all UI state is stored in GuiState
|
||||
}
|
||||
|
||||
impl RegisterViewer {
|
||||
/// Create a new register viewer with default configuration
|
||||
///
|
||||
/// Initializes the viewer with index register 0 selected and standard
|
||||
/// display formatting optimized for the Intel 4004 architecture.
|
||||
/// Initializes the viewer with standard display formatting optimized
|
||||
/// for the Intel 4004 architecture.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `RegisterViewer` instance ready for CPU state display
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
selected_register: 0,
|
||||
}
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// Render the register viewer interface
|
||||
|
|
@ -483,7 +488,7 @@ impl RegisterViewer {
|
|||
///
|
||||
/// # Arguments
|
||||
/// * `ui` - egui UI context for rendering
|
||||
/// * `state` - Immutable reference to GUI state containing register data
|
||||
/// * `state` - Mutable reference to GUI state containing register data
|
||||
///
|
||||
/// # Layout Structure
|
||||
/// ```text
|
||||
|
|
@ -499,7 +504,7 @@ impl RegisterViewer {
|
|||
/// │ Stack Pointer │ 08 │ 8 │
|
||||
/// └─────────────────────────────────────────────────┘
|
||||
/// ```
|
||||
pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
|
||||
pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
ui.vertical(|ui| {
|
||||
// Section header
|
||||
ui.heading("CPU Registers");
|
||||
|
|
@ -508,7 +513,8 @@ impl RegisterViewer {
|
|||
ui.horizontal(|ui| {
|
||||
ui.label("Index Register:");
|
||||
ui.add(
|
||||
egui::DragValue::new(&mut self.selected_register.clone()).clamp_range(0..=15),
|
||||
egui::DragValue::new(&mut state.register_state.selected_register)
|
||||
.clamp_range(0..=15),
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -526,7 +532,7 @@ impl RegisterViewer {
|
|||
|
||||
// Core CPU registers
|
||||
ui.label("Accumulator");
|
||||
ui.label(format!("{:02X}", state.register_state.accumulator));
|
||||
ui.label(format!("{:X}", state.register_state.accumulator));
|
||||
ui.label(format!("{}", state.register_state.accumulator));
|
||||
ui.end_row();
|
||||
|
||||
|
|
@ -551,20 +557,21 @@ impl RegisterViewer {
|
|||
ui.end_row();
|
||||
|
||||
// Selected index register (highlighted)
|
||||
ui.label(format!("Index R{}", self.selected_register));
|
||||
let selected = state.register_state.selected_register;
|
||||
ui.label(format!("Index R{}", selected));
|
||||
ui.label(format!(
|
||||
"{:02X}",
|
||||
state.register_state.index_registers[self.selected_register]
|
||||
"{:X}",
|
||||
state.register_state.index_registers[selected]
|
||||
));
|
||||
ui.label(format!(
|
||||
"{}",
|
||||
state.register_state.index_registers[self.selected_register]
|
||||
state.register_state.index_registers[selected]
|
||||
));
|
||||
ui.end_row();
|
||||
|
||||
// Stack management
|
||||
ui.label("Stack Pointer");
|
||||
ui.label(format!("{:02X}", state.register_state.stack_pointer));
|
||||
ui.label(format!("{:X}", state.register_state.stack_pointer));
|
||||
ui.label(format!("{}", state.register_state.stack_pointer));
|
||||
ui.end_row();
|
||||
});
|
||||
|
|
@ -574,6 +581,132 @@ impl RegisterViewer {
|
|||
}
|
||||
}
|
||||
|
||||
/// ROM viewer component for displaying ROM contents
|
||||
///
|
||||
/// The ROM viewer provides comprehensive ROM inspection capabilities,
|
||||
/// allowing users to examine program memory contents with flexible
|
||||
/// display options and intuitive navigation.
|
||||
///
|
||||
/// ## Features
|
||||
///
|
||||
/// - **Program Memory Display**: View ROM contents from Intel 4001 chips
|
||||
/// - **Flexible Formatting**: Toggle between hexadecimal and decimal display
|
||||
/// - **Address Navigation**: Jump to specific memory addresses
|
||||
/// - **Scrollable Interface**: Navigate through memory contents efficiently
|
||||
/// - **Real-time Updates**: Live ROM content display
|
||||
///
|
||||
/// ## Display Format
|
||||
///
|
||||
/// The ROM viewer shows data in a structured grid:
|
||||
/// - **Address Column**: Memory addresses in hexadecimal format
|
||||
/// - **Data Columns**: 16 bytes per row for easy viewing
|
||||
/// - **Value Display**: Configurable hex or decimal representation
|
||||
///
|
||||
/// ## Memory Organization
|
||||
///
|
||||
/// The Intel 4001 ROM has the following structure:
|
||||
/// - **256 Bytes**: Total ROM capacity per chip
|
||||
/// - **8-bit Values**: Each memory location stores a full byte
|
||||
pub struct RomViewer {
|
||||
/// Number of bytes to display per row
|
||||
bytes_per_row: usize,
|
||||
}
|
||||
|
||||
impl RomViewer {
|
||||
/// Create a new ROM viewer with default configuration
|
||||
///
|
||||
/// Initializes the viewer with standard layout optimized for the Intel 4001 ROM structure.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `RomViewer` instance ready for ROM content display
|
||||
pub fn new() -> Self {
|
||||
Self { bytes_per_row: 16 }
|
||||
}
|
||||
|
||||
/// Render the ROM viewer interface
|
||||
///
|
||||
/// Creates a comprehensive ROM inspection interface with address navigation,
|
||||
/// display options, and a scrollable memory contents grid.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ui` - egui UI context for rendering
|
||||
/// * `state` - Mutable reference to GUI state containing ROM data
|
||||
///
|
||||
/// # Layout Structure
|
||||
/// ```text
|
||||
/// ┌─────────────────────────────────────────────────┐
|
||||
/// │ ROM Viewer (Chip: ROM_4001_1) [─] [□] │
|
||||
/// │ □ Hex View │
|
||||
/// ├─────────────────────────────────────────────────┤
|
||||
/// │ Address 00 01 02 03 04 05 06 07 08 09 0A 0B... │
|
||||
/// │ 0x000 D4 C3 B2 A1 00 11 22 33 44 55 66 77... │
|
||||
/// │ 0x010 88 99 AA BB CC DD EE FF 00 11 22 33... │
|
||||
/// │ ... │
|
||||
/// └─────────────────────────────────────────────────┘
|
||||
/// ```
|
||||
pub fn render(&self, ui: &mut egui::Ui, state: &mut GuiState) {
|
||||
ui.vertical(|ui| {
|
||||
// Section header with chip name
|
||||
let header = if state.rom_state.chip_name.is_empty() {
|
||||
"ROM Viewer".to_string()
|
||||
} else {
|
||||
format!("ROM Viewer ({})", state.rom_state.chip_name)
|
||||
};
|
||||
ui.heading(header);
|
||||
|
||||
// Control bar with display options
|
||||
ui.horizontal(|ui| {
|
||||
ui.checkbox(&mut state.rom_state.show_hex, "Hex View");
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
|
||||
// ROM contents in scrollable area
|
||||
egui::ScrollArea::vertical()
|
||||
.max_height(200.0)
|
||||
.show(ui, |ui| {
|
||||
egui::Grid::new("rom_grid").striped(true).show(ui, |ui| {
|
||||
// Header row with column labels
|
||||
ui.label("Addr");
|
||||
for i in 0..self.bytes_per_row {
|
||||
ui.label(format!("{:02X}", i));
|
||||
}
|
||||
ui.end_row();
|
||||
|
||||
// ROM contents rows
|
||||
let total_rows = (state.rom_state.rom_contents.len() + self.bytes_per_row
|
||||
- 1)
|
||||
/ self.bytes_per_row;
|
||||
for row in 0..total_rows {
|
||||
let addr = row * self.bytes_per_row;
|
||||
|
||||
// Address column
|
||||
ui.label(format!("{:03X}", addr));
|
||||
|
||||
// Data columns
|
||||
for col in 0..self.bytes_per_row {
|
||||
let idx = addr + col;
|
||||
if idx < state.rom_state.rom_contents.len() {
|
||||
let value = state.rom_state.rom_contents[idx];
|
||||
if state.rom_state.show_hex {
|
||||
ui.label(format!("{:02X}", value));
|
||||
} else {
|
||||
ui.label(format!("{:3}", value));
|
||||
}
|
||||
} else {
|
||||
ui.label("--");
|
||||
}
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
}
|
||||
|
||||
/// ROM loader component for file dialog integration and program management
|
||||
///
|
||||
/// The ROM loader handles program file selection, loading, and management.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
//! - Graceful interrupt handling
|
||||
|
||||
use crossterm::{
|
||||
event::{self, Event, KeyCode},
|
||||
event::{self, Event, KeyCode, KeyModifiers},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
|
|
@ -31,6 +31,44 @@ use std::time::{Duration, Instant};
|
|||
|
||||
use crate::system_config::ConfigurableSystem;
|
||||
|
||||
/// Display format for memory and register values
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
pub enum DisplayFormat {
|
||||
#[default]
|
||||
Hex,
|
||||
Decimal,
|
||||
Binary,
|
||||
}
|
||||
|
||||
impl DisplayFormat {
|
||||
/// Format a byte value according to the display format
|
||||
pub fn format_byte(&self, value: u8) -> String {
|
||||
match self {
|
||||
DisplayFormat::Hex => format!("0x{:02X}", value),
|
||||
DisplayFormat::Decimal => format!("{}", value),
|
||||
DisplayFormat::Binary => format!("0b{:08b}", value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a nibble (4-bit) value according to the display format
|
||||
pub fn format_nibble(&self, value: u8) -> String {
|
||||
match self {
|
||||
DisplayFormat::Hex => format!("0x{:X}", value & 0x0F),
|
||||
DisplayFormat::Decimal => format!("{}", value & 0x0F),
|
||||
DisplayFormat::Binary => format!("0b{:04b}", value & 0x0F),
|
||||
}
|
||||
}
|
||||
|
||||
/// Format a 12-bit address according to the display format
|
||||
pub fn format_address(&self, value: u16) -> String {
|
||||
match self {
|
||||
DisplayFormat::Hex => format!("0x{:03X}", value & 0x0FFF),
|
||||
DisplayFormat::Decimal => format!("{}", value & 0x0FFF),
|
||||
DisplayFormat::Binary => format!("0b{:012b}", value & 0x0FFF),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Console configuration structure
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ConsoleConfig {
|
||||
|
|
@ -39,8 +77,12 @@ pub struct ConsoleConfig {
|
|||
pub show_ram: bool,
|
||||
pub show_registers: bool,
|
||||
pub show_system_info: bool,
|
||||
pub show_rom: bool,
|
||||
pub show_cpu_registers: bool,
|
||||
pub display_format: DisplayFormat,
|
||||
pub ram_banks_per_row: usize,
|
||||
pub max_ram_rows: usize,
|
||||
pub max_rom_rows: usize,
|
||||
}
|
||||
|
||||
impl Default for ConsoleConfig {
|
||||
|
|
@ -51,8 +93,12 @@ impl Default for ConsoleConfig {
|
|||
show_ram: true,
|
||||
show_registers: true,
|
||||
show_system_info: true,
|
||||
show_rom: true,
|
||||
show_cpu_registers: true,
|
||||
display_format: DisplayFormat::Hex,
|
||||
ram_banks_per_row: 4,
|
||||
max_ram_rows: 5,
|
||||
max_rom_rows: 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +145,7 @@ impl ConsoleApp {
|
|||
// Handle input
|
||||
if let Ok(true) = event::poll(Duration::from_millis(10)) {
|
||||
if let Ok(Event::Key(key)) = event::read() {
|
||||
self.handle_key_event(key.code);
|
||||
self.handle_key_event(key.code, key.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +185,18 @@ impl ConsoleApp {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key: KeyCode) {
|
||||
fn handle_key_event(&mut self, key: KeyCode, modifiers: KeyModifiers) {
|
||||
// Handle Ctrl-Q to quit
|
||||
if key == KeyCode::Char('q') || key == KeyCode::Char('Q') {
|
||||
if modifiers.contains(KeyModifiers::CONTROL) {
|
||||
self.running = false;
|
||||
if let Ok(mut system) = self.system.lock() {
|
||||
system.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
match key {
|
||||
KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => {
|
||||
println!("DEBUG: Quit key pressed, stopping console");
|
||||
|
|
@ -448,16 +505,12 @@ impl ConsoleApp {
|
|||
.wrap(Wrap { trim: true });
|
||||
f.render_widget(system_widget, info_chunks[0]);
|
||||
|
||||
// CPU register display (enhanced with execution status)
|
||||
let register_info = vec![
|
||||
Line::from(vec![Span::raw("CPU Registers:")]),
|
||||
Line::from(vec![Span::raw("Status: Running (check console output)")]),
|
||||
Line::from(vec![Span::raw("PC: 0x000 (see DEBUG output)")]),
|
||||
Line::from(vec![Span::raw("ACC: 0x0 (see DEBUG output)")]),
|
||||
Line::from(vec![Span::raw(
|
||||
"Instructions: Executing (see DEBUG output)",
|
||||
)]),
|
||||
];
|
||||
// CPU register display with real data
|
||||
let register_info = if self.config.show_cpu_registers {
|
||||
self.get_cpu_register_info()
|
||||
} else {
|
||||
vec![Line::from(vec![Span::raw("CPU register display disabled")])]
|
||||
};
|
||||
|
||||
let register_widget = Paragraph::new(register_info)
|
||||
.block(
|
||||
|
|
@ -469,33 +522,101 @@ impl ConsoleApp {
|
|||
f.render_widget(register_widget, info_chunks[1]);
|
||||
}
|
||||
|
||||
/// Fetch real CPU register data from the system
|
||||
fn get_cpu_register_info(&self) -> Vec<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) {
|
||||
let mut ram_info = vec![Line::from(vec![Span::raw("RAM Contents:")])];
|
||||
|
||||
// Try to get actual RAM data from the system
|
||||
match self.system.lock() {
|
||||
Ok(_system) => {
|
||||
// This is a simplified version - in a real implementation,
|
||||
// we would need to access the actual RAM components
|
||||
// For now, show a more informative placeholder
|
||||
ram_info.push(Line::from(vec![Span::raw("Reading RAM contents...")]));
|
||||
if self.config.show_ram {
|
||||
match self.system.lock() {
|
||||
Ok(system) => {
|
||||
let ram_data = system.get_ram_data(self.config.max_ram_rows);
|
||||
|
||||
// Show some sample memory ranges
|
||||
for bank in 0..4 {
|
||||
let mut bank_data = format!("Bank {}: [", bank);
|
||||
for i in 0..20 {
|
||||
if i > 0 && i % 4 == 0 {
|
||||
bank_data.push(' ');
|
||||
if ram_data.is_empty() {
|
||||
ram_info.push(Line::from(vec![Span::raw("No RAM chips found")]));
|
||||
} else {
|
||||
let fmt = &self.config.display_format;
|
||||
|
||||
for (chip_name, banks) in ram_data {
|
||||
// Clone chip_name to avoid lifetime issues
|
||||
let chip_name = chip_name.clone();
|
||||
ram_info.push(Line::from(vec![Span::styled(
|
||||
chip_name,
|
||||
Style::default().fg(Color::Cyan),
|
||||
)]));
|
||||
|
||||
for (bank_idx, data) in banks.iter().enumerate() {
|
||||
let mut bank_line = format!(" Bank {}: [", bank_idx);
|
||||
for (i, nibble) in data.iter().enumerate() {
|
||||
if i > 0 && i % 4 == 0 {
|
||||
bank_line.push(' ');
|
||||
}
|
||||
bank_line.push_str(&fmt.format_nibble(*nibble));
|
||||
}
|
||||
bank_line.push(']');
|
||||
ram_info.push(Line::from(vec![Span::raw(bank_line)]));
|
||||
}
|
||||
}
|
||||
bank_data.push_str("00");
|
||||
}
|
||||
bank_data.push(']');
|
||||
ram_info.push(Line::from(vec![Span::raw(bank_data)]));
|
||||
}
|
||||
Err(_) => {
|
||||
ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")]));
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ram_info.push(Line::from(vec![Span::raw("Unable to access RAM data")]));
|
||||
}
|
||||
} else {
|
||||
ram_info.push(Line::from(vec![Span::raw("RAM display disabled")]));
|
||||
}
|
||||
|
||||
let ram_widget = Paragraph::new(ram_info)
|
||||
|
|
|
|||
|
|
@ -45,11 +45,9 @@ impl GuiApp {
|
|||
/// ```rust,no_run
|
||||
/// use rusty_emu::gui::GuiApp;
|
||||
///
|
||||
/// let app = GuiApp::new(&eframe::CreationContext {
|
||||
/// egui_ctx: Default::default(),
|
||||
/// integration_info: Default::default(),
|
||||
/// storage: None,
|
||||
/// });
|
||||
/// // The CreationContext is provided by eframe when the app is launched
|
||||
/// // Use new_for_test() for unit testing without eframe infrastructure
|
||||
/// let app = GuiApp::new_for_test();
|
||||
/// ```
|
||||
pub fn new(_cc: &eframe::CreationContext<'_>) -> Self {
|
||||
let gui_state = state::GuiState::new();
|
||||
|
|
@ -97,11 +95,13 @@ impl GuiApp {
|
|||
/// ```rust,no_run
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use rusty_emu::gui::GuiApp;
|
||||
/// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
|
||||
/// use rusty_emu::system_config::SystemFactory;
|
||||
///
|
||||
/// let mut app = GuiApp::new(&creation_context);
|
||||
/// let mut app = GuiApp::new_for_test();
|
||||
/// let factory = SystemFactory::new();
|
||||
/// let system = Arc::new(Mutex::new(factory.create_from_json("config.json")?));
|
||||
/// let system = Arc::new(Mutex::new(
|
||||
/// factory.create_from_json("configs/mcs4_basic.json").expect("Failed to create system")
|
||||
/// ));
|
||||
/// app.set_system(system);
|
||||
/// ```
|
||||
pub fn set_system(&mut self, system: Arc<Mutex<ConfigurableSystem>>) {
|
||||
|
|
@ -142,6 +142,21 @@ impl eframe::App for GuiApp {
|
|||
// Request repaint for smooth real-time updates (60 FPS)
|
||||
ctx.request_repaint();
|
||||
|
||||
// Handle CtrlQ keyboard shortcut to close the application
|
||||
let ctrl_q_pressed = ctx.input(|i| i.modifiers.ctrl && i.key_pressed(egui::Key::Q));
|
||||
|
||||
if ctrl_q_pressed {
|
||||
// Stop the system if available
|
||||
if let Some(system) = self.get_system() {
|
||||
if let Ok(mut sys) = system.lock() {
|
||||
sys.stop();
|
||||
}
|
||||
}
|
||||
// Close the GUI window
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update system state if available - non-blocking operation
|
||||
if let Some(system) = self.get_system() {
|
||||
self.gui_state.update_from_system(&system);
|
||||
|
|
@ -178,8 +193,10 @@ impl GuiApp {
|
|||
// Main GUI sections - organized for optimal workflow
|
||||
self.components
|
||||
.render_control_panel(ui, &mut self.gui_state);
|
||||
self.components.render_memory_viewer(ui, &self.gui_state);
|
||||
self.components.render_register_viewer(ui, &self.gui_state);
|
||||
self.components
|
||||
.render_memory_viewer(ui, &mut self.gui_state);
|
||||
self.components
|
||||
.render_register_viewer(ui, &mut self.gui_state);
|
||||
self.components.render_status_bar(ui, &self.gui_state);
|
||||
});
|
||||
}
|
||||
|
|
@ -207,13 +224,13 @@ impl GuiApp {
|
|||
/// ```rust,no_run
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use rusty_emu::gui::run_gui;
|
||||
/// use rusty_emu::system_config::{ConfigurableSystem, SystemFactory};
|
||||
/// use rusty_emu::system_config::SystemFactory;
|
||||
///
|
||||
/// // Create system (optional)
|
||||
/// let factory = SystemFactory::new();
|
||||
/// let system = Some(Arc::new(Mutex::new(
|
||||
/// factory.create_from_json("configs/mcs4_basic.json")?
|
||||
/// )));
|
||||
/// let system = factory.create_from_json("configs/mcs4_basic.json").ok().map(|s| {
|
||||
/// Arc::new(Mutex::new(s))
|
||||
/// });
|
||||
///
|
||||
/// // Launch GUI - this will block until the GUI window is closed
|
||||
/// if let Err(e) = run_gui(system) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ pub struct GuiState {
|
|||
pub component_states: ComponentStates,
|
||||
/// Memory state
|
||||
pub memory_state: MemoryState,
|
||||
/// ROM state
|
||||
pub rom_state: RomState,
|
||||
/// CPU register state
|
||||
pub register_state: RegisterState,
|
||||
/// Last error message
|
||||
|
|
@ -54,6 +56,18 @@ pub struct MemoryState {
|
|||
pub ram_contents: Vec<[u8; 4]>, // 4 banks of RAM data
|
||||
pub selected_bank: usize,
|
||||
pub selected_address: usize,
|
||||
/// Display mode: true for hexadecimal, false for decimal
|
||||
pub show_hex: bool,
|
||||
}
|
||||
|
||||
/// ROM state for display
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RomState {
|
||||
pub rom_contents: Vec<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
|
||||
|
|
@ -62,8 +76,10 @@ pub struct RegisterState {
|
|||
pub accumulator: u8,
|
||||
pub carry_flag: bool,
|
||||
pub program_counter: u16,
|
||||
pub index_registers: [u8; 16], // 16 index registers (R0-R15)
|
||||
pub index_registers: [u8; 16], // 16 index registers (R0-R15) for Intel 4004
|
||||
pub stack_pointer: u8,
|
||||
/// Currently selected index register for detailed view (0-15)
|
||||
pub selected_register: usize,
|
||||
}
|
||||
|
||||
impl GuiState {
|
||||
|
|
@ -84,6 +100,13 @@ impl GuiState {
|
|||
ram_contents: vec![[0; 4]; 4], // Initialize 4 banks with 4 bytes each
|
||||
selected_bank: 0,
|
||||
selected_address: 0,
|
||||
show_hex: true,
|
||||
},
|
||||
rom_state: RomState {
|
||||
rom_contents: vec![0; 256], // Initialize with 256 bytes
|
||||
selected_address: 0,
|
||||
chip_name: String::new(),
|
||||
show_hex: true,
|
||||
},
|
||||
register_state: RegisterState {
|
||||
accumulator: 0,
|
||||
|
|
@ -91,6 +114,7 @@ impl GuiState {
|
|||
program_counter: 0,
|
||||
index_registers: [0; 16],
|
||||
stack_pointer: 0,
|
||||
selected_register: 0,
|
||||
},
|
||||
last_error: None,
|
||||
}
|
||||
|
|
@ -110,6 +134,15 @@ impl GuiState {
|
|||
// Update component states
|
||||
self.update_component_states(&system_guard);
|
||||
|
||||
// Update CPU register state from real data
|
||||
self.update_register_state(&system_guard);
|
||||
|
||||
// Update RAM state from real data
|
||||
self.update_ram_state(&system_guard);
|
||||
|
||||
// Update ROM state from real data
|
||||
self.update_rom_state(&system_guard);
|
||||
|
||||
// Update cycle count (simulate for now)
|
||||
if self.system_running {
|
||||
self.cycle_count += 1;
|
||||
|
|
@ -146,6 +179,50 @@ impl GuiState {
|
|||
});
|
||||
}
|
||||
|
||||
/// Update CPU register state from real component data
|
||||
fn update_register_state(&mut self, system: &ConfigurableSystem) {
|
||||
if let Some(cpu_data) = system.get_cpu_register_data() {
|
||||
self.register_state.accumulator = cpu_data.accumulator;
|
||||
self.register_state.carry_flag = cpu_data.carry;
|
||||
self.register_state.program_counter = cpu_data.program_counter;
|
||||
self.register_state.stack_pointer = cpu_data.stack_pointer;
|
||||
self.register_state
|
||||
.index_registers
|
||||
.copy_from_slice(&cpu_data.index_registers);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update RAM state from real component data
|
||||
fn update_ram_state(&mut self, system: &ConfigurableSystem) {
|
||||
let ram_data = system.get_ram_data(4); // Get up to 4 rows per bank
|
||||
|
||||
if !ram_data.is_empty() {
|
||||
// Take the first RAM chip's data
|
||||
let (_, banks) = &ram_data[0];
|
||||
|
||||
// Convert bank data to our display format
|
||||
self.memory_state.ram_contents.clear();
|
||||
for bank in banks {
|
||||
let mut row = [0u8; 4];
|
||||
for (i, nibble) in bank.iter().take(4).enumerate() {
|
||||
row[i] = *nibble;
|
||||
}
|
||||
self.memory_state.ram_contents.push(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update ROM state from real component data
|
||||
fn update_rom_state(&mut self, system: &ConfigurableSystem) {
|
||||
let rom_data = system.get_rom_data(256); // Get up to 256 bytes
|
||||
|
||||
if !rom_data.is_empty() {
|
||||
let (chip_name, data) = &rom_data[0];
|
||||
self.rom_state.chip_name = chip_name.clone();
|
||||
self.rom_state.rom_contents = data.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set an error message
|
||||
pub fn set_error(&mut self, error: String) {
|
||||
self.last_error = Some(error);
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ fn run_system_demo(system: ConfigurableSystem) {
|
|||
let timeout = Duration::from_secs(10); // 10 second timeout
|
||||
|
||||
loop {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
|
||||
// Check for timeout
|
||||
if start_time.elapsed() >= timeout {
|
||||
|
|
|
|||
|
|
@ -50,10 +50,29 @@
|
|||
//! ```
|
||||
|
||||
use crate::component::Component;
|
||||
use crate::components::cpu::intel_4004::Intel4004;
|
||||
use crate::components::memory::intel_4001::Intel4001;
|
||||
use crate::components::memory::intel_4002::Intel4002;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// CPU register data for display purposes
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CpuRegisterData {
|
||||
pub program_counter: u16,
|
||||
pub accumulator: u8,
|
||||
pub carry: bool,
|
||||
pub stack_pointer: u8,
|
||||
pub index_registers: [u8; 16], // Intel 4004 has 16 index registers (R0-R15)
|
||||
}
|
||||
|
||||
/// RAM data for display purposes (chip name -> banks of nibbles)
|
||||
pub type RamData = Vec<(String, Vec<Vec<u8>>)>;
|
||||
|
||||
/// ROM data for display purposes (chip name -> bytes)
|
||||
pub type RomData = Vec<(String, Vec<u8>)>;
|
||||
|
||||
/// JSON-based system configuration structures
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SystemConfig {
|
||||
|
|
@ -616,6 +635,95 @@ impl ConfigurableSystem {
|
|||
println!("DEBUG: Program loading completed");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get CPU register data from Intel 4004 CPU
|
||||
/// Returns: CpuRegisterData if Intel 4004 CPU is found, None otherwise
|
||||
pub fn get_cpu_register_data(&self) -> Option<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)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue