feat: Add real-time data display for CPU registers, RAM, and ROM

This commit is contained in:
Jan-Peter von Hunnius 2026-02-26 19:36:59 +01:00
parent 3530ea5f28
commit 6636417d18
22 changed files with 1233 additions and 188 deletions

View file

@ -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.