mirror of
https://github.com/FranLMSP/rultra64.git
synced 2026-01-01 07:51:34 -05:00
Demo egui app, displaying CPU Registers in a window
This commit is contained in:
1398
Cargo.lock
generated
1398
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,3 +6,4 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
eframe = "0.16.0"
|
||||
@@ -1,3 +1,7 @@
|
||||
fn main() {
|
||||
use rultra64::gui::EmulatorApp;
|
||||
|
||||
fn main() {
|
||||
let app = EmulatorApp::default();
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
eframe::run_native(Box::new(app), native_options);
|
||||
}
|
||||
@@ -99,6 +99,10 @@ impl CPU {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registers(&self) -> &CPURegisters {
|
||||
&self.registers
|
||||
}
|
||||
|
||||
pub fn fetch_opcode(address: i64, mmu: &MMU) -> u32 {
|
||||
let data = mmu.read_virtual(address, 4);
|
||||
let opcode = ((data[0] as u32) << 24) | ((data[1] as u32) << 16) | ((data[2] as u32) << 8) | ((data[3] as u32) << 8);
|
||||
|
||||
@@ -24,4 +24,8 @@ impl Emulator {
|
||||
pub fn tick(&mut self, _framebuffer: &mut [u8]) {
|
||||
self.cpu.fetch_and_exec_opcode(&mut self.mmu);
|
||||
}
|
||||
|
||||
pub fn cpu(&self) -> &CPU {
|
||||
&self.cpu
|
||||
}
|
||||
}
|
||||
93
src/gui.rs
Normal file
93
src/gui.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use eframe::{egui, epi};
|
||||
|
||||
use crate::emulator::Emulator;
|
||||
|
||||
pub struct EmulatorApp {
|
||||
core: Emulator,
|
||||
}
|
||||
|
||||
impl Default for EmulatorApp {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
core: Emulator::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl epi::App for EmulatorApp {
|
||||
fn name(&self) -> &str {
|
||||
"Rultra64"
|
||||
}
|
||||
|
||||
/// Called once before the first frame.
|
||||
fn setup(
|
||||
&mut self,
|
||||
_ctx: &egui::CtxRef,
|
||||
_frame: &epi::Frame,
|
||||
_storage: Option<&dyn epi::Storage>,
|
||||
) {
|
||||
// Load previous app state (if any).
|
||||
// Note that you must enable the `persistence` feature for this to work.
|
||||
#[cfg(feature = "persistence")]
|
||||
if let Some(storage) = _storage {
|
||||
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by the frame work to save state before shutdown.
|
||||
/// Note that you must enable the `persistence` feature for this to work.
|
||||
#[cfg(feature = "persistence")]
|
||||
fn save(&mut self, storage: &mut dyn epi::Storage) {
|
||||
epi::set_value(storage, epi::APP_KEY, self);
|
||||
}
|
||||
|
||||
/// Called each time the UI needs repainting, which may be many times per second.
|
||||
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
||||
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
|
||||
let Self { core: emulator_core } = self;
|
||||
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
// The top panel is often a good place for a menu bar:
|
||||
egui::menu::bar(ui, |ui| {
|
||||
ui.menu_button("File", |ui| {
|
||||
if ui.button("Load ROM").clicked() {
|
||||
}
|
||||
if ui.button("Quit").clicked() {
|
||||
frame.quit();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
build_cpu_registers_window(ctx, &emulator_core);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_cpu_registers_window(ctx: &egui::CtxRef, emulator_core: &Emulator) {
|
||||
egui::Window::new("CPU Registers").vscroll(true).show(ctx, |ui| {
|
||||
|
||||
egui::SidePanel::left("left_panel")
|
||||
// .resizable(true)
|
||||
.default_width(10.0)
|
||||
.show_inside(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading("# | Name");
|
||||
});
|
||||
for (index, name) in crate::registers::CPU_REGISTER_NAMES.into_iter().enumerate() {
|
||||
ui.label(format!("r{} | {}", index, name));
|
||||
}
|
||||
});
|
||||
egui::SidePanel::right("right_panel")
|
||||
.resizable(true)
|
||||
// .default_width(50.0)
|
||||
.show_inside(ui, |ui| {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.heading("Value");
|
||||
});
|
||||
for (_, name) in crate::registers::CPU_REGISTER_NAMES.into_iter().enumerate() {
|
||||
let val = emulator_core.cpu().registers().get_by_name(name);
|
||||
ui.label(format!("{}", val));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -5,3 +5,5 @@ pub mod rom;
|
||||
pub mod rdram;
|
||||
pub mod emulator;
|
||||
pub mod rcp;
|
||||
pub mod utils;
|
||||
pub mod gui;
|
||||
15
src/mmu.rs
15
src/mmu.rs
@@ -45,23 +45,10 @@ pub struct MMU {
|
||||
|
||||
impl MMU {
|
||||
pub fn new() -> Self {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
#[cfg(not(test))]
|
||||
if args.len() < 2 {
|
||||
eprintln!("Please, specify a ROM file");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let rom = match ROM::load_file(&args.get(1).unwrap_or(&"".to_string())) {
|
||||
Ok(rom) => rom,
|
||||
Err(err) => {
|
||||
eprintln!("Could not read ROM: {}", err);
|
||||
std::process::exit(1);
|
||||
},
|
||||
};
|
||||
Self {
|
||||
rdram: RDRAM::new(),
|
||||
rcp: RCP::new(),
|
||||
rom,
|
||||
rom: ROM::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::rdram::RDRAM;
|
||||
use crate::utils::box_array;
|
||||
|
||||
pub struct VideoInterface {
|
||||
registers: [u8; 0x100000],
|
||||
registers: Box<[u8; 0x100000]>,
|
||||
}
|
||||
|
||||
impl VideoInterface {
|
||||
pub fn new() -> Self {
|
||||
let mut registers = [0; 0x100000];
|
||||
let mut registers = box_array![0; 0x100000];
|
||||
// Initialize VI_V_INTR 0x0440 000C: https://n64brew.dev/wiki/Video_Interface#0x0440_000C_-_VI_V_INTR
|
||||
registers[0x0440000C - 0x04400000] = 0xFF;
|
||||
registers[0x0440000B - 0x04400000] = 0x03;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::utils::box_array;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Byte {
|
||||
data: u16,
|
||||
@@ -28,13 +30,13 @@ impl Byte {
|
||||
}
|
||||
|
||||
pub struct RDRAM {
|
||||
data: [Byte; 0x400000],
|
||||
data: Box<[Byte; 0x400000]>,
|
||||
}
|
||||
|
||||
impl RDRAM {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: [Byte::new(); 0x400000],
|
||||
data: box_array![Byte::new(); 0x400000],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ impl ROM {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_file(filename: &str) -> std::io::Result<Self> {
|
||||
pub fn new_from_filename(filename: &str) -> std::io::Result<Self> {
|
||||
let mut file = File::open(filename)?;
|
||||
let mut data = vec![];
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
17
src/utils.rs
Normal file
17
src/utils.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
#[macro_export]
|
||||
macro_rules! box_array {
|
||||
($val:expr ; $len:expr) => {{
|
||||
// Use a generic function so that the pointer cast remains type-safe
|
||||
fn vec_to_boxed_array<T>(vec: Vec<T>) -> Box<[T; $len]> {
|
||||
let boxed_slice = vec.into_boxed_slice();
|
||||
|
||||
let ptr = ::std::boxed::Box::into_raw(boxed_slice) as *mut [T; $len];
|
||||
|
||||
unsafe { Box::from_raw(ptr) }
|
||||
}
|
||||
|
||||
vec_to_boxed_array(vec![$val; $len])
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use box_array;
|
||||
Reference in New Issue
Block a user