BUGFIX: gemmaegui: Memory window was showing 16 bytes beyond 'limit'

This commit is contained in:
Trevor Merritt 2025-05-27 10:16:31 -04:00
parent fdf09daf0f
commit 0f40002053
6 changed files with 1 additions and 434 deletions

View File

@ -111,7 +111,7 @@ impl GemmaEguiSupport {
pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) {
ui.label("Memory View");
for i in (0..=0x200).step_by(16) {
for i in (0..0x200).step_by(16) {
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
for y in 0..16 {
ui.label(format!("{:02x}", system.memory.peek((i + y) as u16)).as_str());

View File

@ -1,14 +0,0 @@
[package]
name = "gemmasdl2"
version = "0.1.0"
edition = "2021"
[dependencies]
gemma = { path = "../gemma" }
egui_sdl2_platform = "0.3.0"
gl = "0.14"
egui = "0.27"
egui_glow = "0.27"
pollster = "0.2"
anyhow = "1.0"

View File

@ -1,171 +0,0 @@
mod support;
use crate::support::gemma_egui_support::GemmaEguiSupport;
use anyhow::Context;
use egui::TextBuffer;
use egui_glow::glow::{HasContext, COLOR_BUFFER_BIT};
use egui_sdl2_platform::sdl2;
use gemma::chip8::computer::Chip8Computer;
use sdl2::event::{Event, WindowEvent};
use std::{sync::Arc, time::Instant};
use support::timestep::TimeStep;
const SCREEN_WIDTH: u32 = 800;
const SCREEN_HEIGHT: u32 = 480;
async fn run() -> anyhow::Result<String> {
// Initialize SDL2 and video subsystem
let sdl = sdl2::init().map_err(|e| anyhow::anyhow!("Failed to create SDL context: {}", e))?;
let mut video = sdl
.video()
.map_err(|e| anyhow::anyhow!("Failed to initialize SDL video subsystem: {}", e))?;
// Create SDL2 window and OpenGL context
let window = video
.window("Window", SCREEN_WIDTH, SCREEN_HEIGHT)
.opengl()
.position_centered()
.build()?;
let _gl_context = window
.gl_create_context()
.expect("Failed to create GL context");
// Load OpenGL functions
let gl = unsafe {
egui_glow::painter::Context::from_loader_function(|name| {
video.gl_get_proc_address(name) as *const _
})
};
let mut painter = egui_glow::Painter::new(Arc::new(gl), "", None)?;
// Setup Egui and SDL2 platform
let mut platform = egui_sdl2_platform::Platform::new(window.size())?;
let mut event_pump = sdl
.event_pump()
.map_err(|e| anyhow::anyhow!("Failed to get SDL event pump: {}", e))?;
// Initial settings
let color = [0.0, 0.0, 0.0, 1.0]; // Background color
let start_time = Instant::now();
let mut timestep = TimeStep::new();
let mut computer = Chip8Computer::new();
let mut is_running: bool = false;
computer.load_bytes_to_memory(0x200, &std::fs::read("resources/roms/3-corax+.ch8")?);
// Main loop
'main: loop {
// Update the Egui platform with the current time
platform.update_time(start_time.elapsed().as_secs_f64());
// Begin Egui frame
let ctx = platform.context();
if is_running {
computer.step_system();
}
egui::Window::new("Hello, world!").show(&ctx, |ui| {
GemmaEguiSupport::video_view(&computer, ui);
GemmaEguiSupport::memory_view(&computer, ui);
GemmaEguiSupport::registers_view(&computer, ui);
});
// Process Egui frame
let full_output = platform.end_frame(&mut video)?;
let paint_jobs = platform.tessellate(&full_output);
// Clear the screen with the current color
unsafe {
painter.gl().clear_color(color[0], color[1], color[2], 1.0);
painter.gl().clear(COLOR_BUFFER_BIT);
}
// Paint Egui outputs and update textures
let size = window.size();
painter.paint_and_update_textures(
[size.0, size.1],
1.0,
paint_jobs.as_slice(),
&full_output.textures_delta,
);
window.gl_swap_window();
// Run the timestep logic
timestep.run_this(|_| {});
// Handle SDL2 events
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(sdl2::keyboard::Keycode::Escape),
..
} => break 'main,
Event::Window {
window_id,
win_event,
..
} if window_id == window.id() => {
if let WindowEvent::Close = win_event {
break 'main;
}
}
Event::KeyUp {
keycode: Some(sdl2::keyboard::Keycode::F3),
..
} => {
println!("USER PRESSED F3 -> running");
is_running = true;
}
Event::KeyUp {
keycode: Some(sdl2::keyboard::Keycode::F4),
..
} => {
println!("USER PRESSED F4 -> stopping");
is_running = false;
}
Event::KeyDown {
keycode: Some(sdl2::keyboard::Keycode::F5),
..
} => {
println!("USER PRESSED F5 -> Step");
computer.step_system();
}
Event::KeyDown {
keycode: Some(sdl2::keyboard::Keycode::F6),
..
} => {
println!("USER PRESSED F6 -> RESET");
computer.reset(computer.quirk_mode.clone());
}
Event::ControllerButtonDown { which, .. } => {
println!("PLAYER {which} DOWN");
}
Event::JoyButtonDown { button_idx, .. } => {
println!("JoyButtonDown {}", button_idx);
}
Event::JoyAxisMotion {
which, axis_idx, ..
} => {
println!("JoyAxismotion {which} {axis_idx}");
}
_ => platform.handle_event(&event, &sdl, &video),
}
}
// Optionally log the frame rate
if let Some(fps) = timestep.frame_rate() {
println!("{:?} fps", fps);
}
let num_js = sdl.joystick().unwrap().num_joysticks().unwrap();
println!("NUM JS = {num_js}");
}
Ok(("").parse()?)
}
fn main() -> anyhow::Result<()> {
pollster::block_on(run())?;
Ok(())
}

View File

@ -1,151 +0,0 @@
use crate::Chip8Computer;
use egui::Rect;
use egui::Ui;
use egui::Vec2;
use egui::{Align, Color32, ComboBox, Pos2};
use std::fs::read_dir;
use std::path::PathBuf;
const CELL_WIDTH: f32 = 5.0;
const CELL_HEIGHT: f32 = 5.0;
pub struct EGuiFileList {}
impl EGuiFileList {
pub fn display_path(root: PathBuf, selected_filename: &mut String, ui: &mut Ui) {
let working_filename = selected_filename.clone();
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
// ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path")));
ComboBox::from_label("Select ROM")
.selected_text(selected_filename.clone())
.show_ui(ui, |ui| {
let mut sorted_options = vec![];
for option in read_dir(root.as_path()).unwrap() {
let to_push = option
.unwrap()
.file_name()
.into_string()
.unwrap_or(String::new());
sorted_options.push(to_push);
}
sorted_options.sort();
for item in sorted_options {
// Add each option to the ComboBox
if ui
.selectable_label(selected_filename.eq(&item.as_str()), item.clone())
.clicked()
{
*selected_filename = item;
}
}
});
// Display the selected option
});
}
}
pub struct GemmaEguiSupport {}
impl GemmaEguiSupport {
pub fn controls_view(system: &mut Chip8Computer, ui: &mut Ui) {
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
if ui.button("Start").clicked() {
println!("Start");
// state.is_running = true;
}
if ui.button("Step").clicked() {
system.step_system();
}
if ui.button("Stop").clicked() {
println!("STOP");
// state.is_running = false;
}
if ui.button("Reset").clicked() {
system.reset(system.quirk_mode.clone());
// state.is_running = false;
}
});
// if ui.button(format!("Load {}", state.selected_rom_filename)).clicked() {
// load the bin...
// let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_rom_filename))).unwrap();
// ...then feed the system.
// system.load_bytes_to_memory(0x200, &read_bin);
// println!("Loaded {}", state.selected_rom_filename);
// }
// EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_rom_filename, ui);
ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| {
// ui.checkbox(&mut state.display_memory, "Display Memory");
// ui.checkbox(&mut state.display_video, "Display Video");
// ui.checkbox(&mut state.display_registers, "Display Registers");
});
}
pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) {
ui.label(format!(
"V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
system.registers.peek(0x00),
system.registers.peek(0x01),
system.registers.peek(0x02),
system.registers.peek(0x03),
system.registers.peek(0x04),
system.registers.peek(0x05),
system.registers.peek(0x06),
system.registers.peek(0x07)
));
ui.label(format!(
"V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ",
system.registers.peek(0x08),
system.registers.peek(0x09),
system.registers.peek(0x0A),
system.registers.peek(0x0B),
system.registers.peek(0x0C),
system.registers.peek(0x0D),
system.registers.peek(0x0E),
system.registers.peek(0x0F)
));
ui.label(format!(
"PC: {:04x}\tI: {:04x}",
system.registers.peek_pc(),
system.registers.peek_i()
));
}
pub fn video_view(system: &Chip8Computer, ui: &mut Ui) {
let (_resp, painter) = ui.allocate_painter(Vec2::new(350.0, 165.0), egui::Sense::hover());
for current_row in 0..32 {
for current_col in 0..64 {
let data_offset = current_row * 64 + current_col;
let x_offset = current_col as f32 * CELL_WIDTH;
let y_offset = current_row as f32 * CELL_HEIGHT;
let origin = Pos2::new(x_offset, y_offset);
let colour = if system.video_memory.peek(data_offset) {
Color32::RED
} else {
Color32::WHITE
};
let rect = Rect::from_min_size(origin, Vec2::new(CELL_WIDTH, CELL_HEIGHT));
painter.rect_filled(rect, 0.0, colour);
// println!("Cell {current_col}x{current_row} at {}x{} -> {}",
// origin.x, origin.y,
// system.video_memory.peek(data_offset));
}
}
// thread::sleep(Duration::from_secs(1));
}
pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) {
ui.label("Memory View");
for i in (0..=0x200).step_by(16) {
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
for y in 0..16 {
ui.label(format!("{:02x}", system.memory.peek((i + y) as u16)).as_str());
}
});
}
ui.label("Should have **something** to adjust the 'memory window'");
}
}

View File

@ -1,2 +0,0 @@
pub mod timestep;
pub mod gemma_egui_support;

View File

@ -1,95 +0,0 @@
//! Gameplay speed control. It attempts to mimic/limit progression to
//! the 30 tics per second Doom used.
use std::{fmt, time::Instant};
const MS_PER_UPDATE: f32 = 28.57;
#[derive(Debug)]
pub struct TimeStep {
last_time: Instant,
delta_time: f32,
frame_count: u32,
frame_time: f32,
run_tics: u32,
last_tics: u32,
lag: f32,
}
#[derive(Debug)]
pub struct FrameData {
pub tics: u32,
pub frames: u32,
}
impl fmt::Display for FrameData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"FrameData (per-second):\n - tics: {}\n - fps: {}",
self.tics, self.frames
))
}
}
impl TimeStep {
pub fn new() -> TimeStep {
TimeStep {
last_time: Instant::now(),
delta_time: 0.0,
frame_count: 0,
frame_time: 0.0,
run_tics: 0,
last_tics: 0,
lag: 0.0,
}
}
pub fn delta(&mut self) -> f32 {
let current_time = Instant::now();
let delta = current_time.duration_since(self.last_time).as_micros() as f32 * 0.001;
self.last_time = current_time;
self.delta_time = delta;
delta
}
/// Increments self time and returns current lag. `run_this` is run only for
/// `n` tics available.
pub fn run_this(&mut self, mut run_this: impl FnMut(f32)) {
let dt = self.delta();
self.lag += dt;
while self.lag >= MS_PER_UPDATE {
run_this(dt);
self.lag -= MS_PER_UPDATE;
self.run_tics += 1;
}
}
pub fn frame_rate(&mut self) -> Option<FrameData> {
self.frame_count += 1;
self.frame_time += self.delta_time;
let tmp;
let tmp2;
// per second
if self.frame_time >= 1000.0 {
tmp = self.frame_count;
tmp2 = self.last_tics;
self.frame_count = 0;
self.frame_time = 0.0;
self.last_tics = self.run_tics;
return Some(FrameData {
tics: self.run_tics - tmp2,
frames: tmp,
});
}
None
}
}
impl Default for TimeStep {
// shutup clippy!
fn default() -> Self {
Self::new()
}
}