mirror of
https://github.com/FranLMSP/snes.git
synced 2026-01-01 07:21:35 -05:00
General improvements (#10)
* Makefile * Restart emulation as soon as a ROM is loaded * upgrade eframe and wgpu * adjust the tv output image size according to the current snes resolution * fix bug when loading different roms
This commit is contained in:
12
Makefile
Normal file
12
Makefile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
test:
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
cargo clippy --all-targets --all-features
|
||||||
|
|
||||||
|
run:
|
||||||
|
cargo run --bin snes-frontend
|
||||||
|
|
||||||
|
run-release:
|
||||||
|
cargo run --bin snes-frontend --release
|
||||||
|
|
||||||
@@ -35,6 +35,12 @@ impl Bus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hard_reset(&mut self) {
|
||||||
|
self.wram = [0; 0x10000];
|
||||||
|
self.internal_registers = InternalRegisters::new();
|
||||||
|
self.dma = DMA::new();
|
||||||
|
}
|
||||||
|
|
||||||
fn read_wram(&self, address: u32) -> u8 {
|
fn read_wram(&self, address: u32) -> u8 {
|
||||||
self.wram[(address & 0xFFFF) as usize]
|
self.wram[(address & 0xFFFF) as usize]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,15 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset_vector(&mut self) {
|
||||||
self.cpu.reset_vector(&mut self.bus);
|
self.cpu.reset_vector(&mut self.bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hard_reset(&mut self) {
|
||||||
|
self.cpu = CPU::new();
|
||||||
|
self.bus.hard_reset();
|
||||||
|
self.reset_vector();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Emulator {
|
impl Default for Emulator {
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ impl PPU {
|
|||||||
// ----
|
// ----
|
||||||
// possible optimizations:
|
// possible optimizations:
|
||||||
// - Fetch all of the necessary data before starting to render the scanline
|
// - Fetch all of the necessary data before starting to render the scanline
|
||||||
|
// - Pre-render each background in a framebuffer and only update the framebuffer if either the tileset or charset changes
|
||||||
let tileset_vram_base_address = self.registers.get_bg_tile_base_address(background) as usize;
|
let tileset_vram_base_address = self.registers.get_bg_tile_base_address(background) as usize;
|
||||||
let charset_vram_base_address = self.registers.get_bg_char_base_address(background) as usize;
|
let charset_vram_base_address = self.registers.get_bg_char_base_address(background) as usize;
|
||||||
let (bg_size_width, _) = self.registers.get_bg_size(background).to_usize();
|
let (bg_size_width, _) = self.registers.get_bg_size(background).to_usize();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ impl LoROM {
|
|||||||
|
|
||||||
impl ROM for LoROM {
|
impl ROM for LoROM {
|
||||||
fn load(&mut self, filename: &str) -> std::io::Result<bool> {
|
fn load(&mut self, filename: &str) -> std::io::Result<bool> {
|
||||||
|
self.data = vec![];
|
||||||
load_rom(filename, &mut self.data)
|
load_rom(filename, &mut self.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ edition = "2021"
|
|||||||
snes-core = { path = "../snes-core" }
|
snes-core = { path = "../snes-core" }
|
||||||
|
|
||||||
# Frontend stuff
|
# Frontend stuff
|
||||||
eframe = "0.26.0"
|
eframe = "0.29.1"
|
||||||
env_logger = "0.10.1"
|
env_logger = "0.10.1"
|
||||||
rfd = "0.12.1"
|
rfd = "0.12.1"
|
||||||
regex = "1.10.2"
|
regex = "1.10.2"
|
||||||
wgpu = "0.19.0"
|
wgpu = "23.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
wgpu = ["eframe/wgpu"]
|
wgpu = ["eframe/wgpu"]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub fn build_cpu_debug_controls(ctx: &egui::Context, cpu_debug_options: &mut CPU
|
|||||||
ui.monospace("Vectors:");
|
ui.monospace("Vectors:");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.button("Reset").clicked() {
|
if ui.button("Reset").clicked() {
|
||||||
emulator.reset();
|
emulator.reset_vector();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ fn paint_texture(ui: &mut Ui, texture: &mut Option<TextureHandle>, framebuffer:
|
|||||||
if let Some(txt) = texture {
|
if let Some(txt) = texture {
|
||||||
txt.set(
|
txt.set(
|
||||||
ColorImage::from_rgba_premultiplied([width, height], framebuffer),
|
ColorImage::from_rgba_premultiplied([width, height], framebuffer),
|
||||||
TextureOptions::default(),
|
TextureOptions::LINEAR,
|
||||||
);
|
);
|
||||||
let (whole_rect, _) =
|
let (whole_rect, _) =
|
||||||
ui.allocate_exact_size(Vec2::from([(width * 2) as f32, (height * 2) as f32]), egui::Sense::focusable_noninteractive());
|
ui.allocate_exact_size(Vec2::from([(width * 2) as f32, (height * 2) as f32]), egui::Sense::focusable_noninteractive());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use eframe::egui;
|
use eframe::egui::{self, Pos2, Rect};
|
||||||
use eframe::{egui::Context, epaint::ColorImage};
|
use eframe::{egui::Context, epaint::ColorImage};
|
||||||
use eframe::egui::{TextureOptions, Ui};
|
use eframe::egui::{TextureOptions, Ui};
|
||||||
use eframe::epaint::{Vec2, TextureHandle};
|
use eframe::epaint::{Vec2, TextureHandle};
|
||||||
@@ -12,11 +12,12 @@ pub fn build_game_window(
|
|||||||
ctx: &egui::Context,
|
ctx: &egui::Context,
|
||||||
game_tv_texture: &mut Option<TextureHandle>,
|
game_tv_texture: &mut Option<TextureHandle>,
|
||||||
framebuffer: &[u8],
|
framebuffer: &[u8],
|
||||||
|
current_res: (u16, u16),
|
||||||
) {
|
) {
|
||||||
egui::Window::new("TV")
|
egui::Window::new("TV")
|
||||||
.collapsible(false)
|
.collapsible(false)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
paint_game_texture(ui, game_tv_texture, framebuffer);
|
paint_game_texture(ui, game_tv_texture, framebuffer, current_res);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,21 +34,34 @@ pub fn initialize_game_texture(ctx: &Context, game_tv_texture: &mut Option<Textu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_game_texture(ui: &mut Ui, texture: &mut Option<TextureHandle>, framebuffer: &[u8]) {
|
fn paint_game_texture(ui: &mut Ui, texture: &mut Option<TextureHandle>, framebuffer: &[u8], current_res: (u16, u16)) {
|
||||||
if let Some(txt) = texture {
|
if let Some(txt) = texture {
|
||||||
txt.set(
|
txt.set(
|
||||||
ColorImage::from_rgba_premultiplied([MAX_TV_WIDTH, MAX_TV_HEIGHT], framebuffer),
|
ColorImage::from_rgba_premultiplied([MAX_TV_WIDTH, MAX_TV_HEIGHT], framebuffer),
|
||||||
TextureOptions::default(),
|
TextureOptions::LINEAR,
|
||||||
);
|
);
|
||||||
let (whole_rect, _) =
|
let (whole_rect, _) =
|
||||||
ui.allocate_exact_size(
|
ui.allocate_exact_size(
|
||||||
Vec2::from([(MAX_TV_WIDTH) as f32, (MAX_TV_HEIGHT) as f32]),
|
Vec2::from([(MAX_TV_WIDTH) as f32, (MAX_TV_HEIGHT) as f32]),
|
||||||
egui::Sense::focusable_noninteractive(),
|
egui::Sense::focusable_noninteractive(),
|
||||||
);
|
);
|
||||||
|
let current_resolution_scale = calculate_resolution_scale(current_res);
|
||||||
egui::Image::new((
|
egui::Image::new((
|
||||||
txt.id(),
|
txt.id(),
|
||||||
txt.size_vec2(),
|
txt.size_vec2(),
|
||||||
))
|
))
|
||||||
|
.texture_options(TextureOptions::LINEAR)
|
||||||
|
.uv(Rect::from_two_pos(
|
||||||
|
Pos2::new(0.0,0.0),
|
||||||
|
Pos2::new(current_resolution_scale.0, current_resolution_scale.1))
|
||||||
|
)
|
||||||
.paint_at(ui, whole_rect);
|
.paint_at(ui, whole_rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_resolution_scale(current_res: (u16, u16)) -> (f32, f32) {
|
||||||
|
(
|
||||||
|
current_res.0 as f32 / MAX_TV_WIDTH as f32,
|
||||||
|
current_res.1 as f32 / MAX_TV_HEIGHT as f32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,10 +10,16 @@ pub fn build_menu_bar(emulator: &mut Emulator, ui: &mut egui::Ui, state: &mut Ap
|
|||||||
if ui.button("Load ROM file").clicked() {
|
if ui.button("Load ROM file").clicked() {
|
||||||
if let Some(path) = rfd::FileDialog::new().pick_file() {
|
if let Some(path) = rfd::FileDialog::new().pick_file() {
|
||||||
let picked_path = path.display().to_string();
|
let picked_path = path.display().to_string();
|
||||||
|
// TODO: replace this load function by an external function as each ROM may not always be LoROM
|
||||||
match emulator.bus.rom.load(&picked_path) {
|
match emulator.bus.rom.load(&picked_path) {
|
||||||
Ok(_) => println!("Loaded ROM"),
|
Ok(_) => {
|
||||||
|
emulator.hard_reset();
|
||||||
|
state.emulation_state.is_paused = false;
|
||||||
|
state.emulation_state.one_tick_per_frame = false;
|
||||||
|
println!("Loaded ROM");
|
||||||
|
},
|
||||||
Err(err) => println!("Error loading the ROM: {}", err),
|
Err(err) => println!("Error loading the ROM: {}", err),
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ impl eframe::App for SnesEmulatorApp {
|
|||||||
ctx,
|
ctx,
|
||||||
&mut self.state.game_tv_texture,
|
&mut self.state.game_tv_texture,
|
||||||
self.emulator.bus.ppu.framebuffer(),
|
self.emulator.bus.ppu.framebuffer(),
|
||||||
|
self.emulator.bus.ppu.registers.get_current_res(),
|
||||||
);
|
);
|
||||||
emu_ui::debug::build_all_debug_options(ctx, &mut self.state.debug_options, &mut self.state.emulation_state, &mut self.emulator);
|
emu_ui::debug::build_all_debug_options(ctx, &mut self.state.debug_options, &mut self.state.emulation_state, &mut self.emulator);
|
||||||
if !self.state.emulation_state.is_paused {
|
if !self.state.emulation_state.is_paused {
|
||||||
@@ -53,6 +54,6 @@ fn main() -> eframe::Result<()> {
|
|||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"SNES Emulator",
|
"SNES Emulator",
|
||||||
native_options,
|
native_options,
|
||||||
Box::new(|cc| Box::new(SnesEmulatorApp::new(cc))),
|
Box::new(|cc| Ok(Box::new(SnesEmulatorApp::new(cc)))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user