diff options
author | Astatin <[email protected]> | 2025-03-12 18:15:09 +0900 |
---|---|---|
committer | Astatin <[email protected]> | 2025-03-12 18:15:09 +0900 |
commit | 3ac23c20f0d29cfad1963aa117941fb9c2827126 (patch) | |
tree | 500acfe7f7d98e5313de61484524207fc71f8f25 /src | |
parent | df5a1c83d8c5d680e1bd4ef1c6793db964ebebea (diff) |
Replace minifb with pixels+winit
Diffstat (limited to 'src')
-rw-r--r-- | src/display.rs | 27 | ||||
-rw-r--r-- | src/gamepad.rs | 47 | ||||
-rw-r--r-- | src/interrupts_timers.rs | 1 | ||||
-rw-r--r-- | src/io.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 20 | ||||
-rw-r--r-- | src/opcodes.rs | 1 | ||||
-rw-r--r-- | src/serial.rs | 15 | ||||
-rw-r--r-- | src/window.rs | 124 |
8 files changed, 180 insertions, 56 deletions
diff --git a/src/display.rs b/src/display.rs index ef98835..917f330 100644 --- a/src/display.rs +++ b/src/display.rs @@ -2,7 +2,6 @@ use crate::consts::DISPLAY_UPDATE_SLEEP_TIME_MICROS; use crate::state::MemError; -use minifb::{Window, WindowOptions, ScaleMode, Scale}; use std::time::SystemTime; const COLORS: [u32; 4] = [0x00e0f8d0, 0x0088c070, 0x346856, 0x00081820]; @@ -29,7 +28,6 @@ pub enum DisplayInterrupt { #[derive(Debug)] pub struct Display { - pub window: Window, framebuffer: [u32; 160 * 144], bg_buffer: [u8; 160 * 144], @@ -58,25 +56,13 @@ pub struct Display { last_dt: SystemTime, pub stat: u64, + + pub redraw_request: Option<[u32; 160 * 144]>, } impl Display { pub fn new() -> Self { Self { - window: Window::new( - "Gameboy Emulator", - 512, 461, - /*1200, 1080,*/ - /* 160,144, */ - WindowOptions { - // borderless: true, - // resize: true, - // scale_mode: ScaleMode::AspectRatioStretch, - // scale: Scale::FitScreen, - ..WindowOptions::default() - }, - ) - .unwrap(), framebuffer: [0; 160 * 144], bg_buffer: [0; 160 * 144], tiledata: [0; 0x3000], @@ -99,6 +85,7 @@ impl Display { lyc: 0, cgb_mode: false, lcd_interrupt_mode: 0xff, + redraw_request: None, } } @@ -106,12 +93,6 @@ impl Display { self.framebuffer = [COLORS[0]; 160 * 144]; } - pub fn update(&mut self) { - self.window - .update_with_buffer(&self.framebuffer, 160, 144) - .unwrap(); - } - pub fn color_palette(&self, color_byte: u8, palette: u8, cgb_mode: bool) -> u32 { if cgb_mode { let color_pointer = palette * 8 + color_byte * 2; @@ -353,7 +334,7 @@ impl Display { .as_micros() > DISPLAY_UPDATE_SLEEP_TIME_MICROS as u128 { - self.update(); + self.redraw_request = Some(self.framebuffer); self.last_dt = SystemTime::now(); } diff --git a/src/gamepad.rs b/src/gamepad.rs index f59577a..993c100 100644 --- a/src/gamepad.rs +++ b/src/gamepad.rs @@ -1,9 +1,10 @@ use crate::state; +use crate::window::Keys; use gilrs::{Button, GamepadId, Gilrs}; use state::GBState; use std::fs::File; -use std::io::{Write, Read, ErrorKind}; -use minifb::Key; +use std::io::{ErrorKind, Read, Write}; +use winit::keyboard::KeyCode; pub struct Gamepad { gilrs: Gilrs, @@ -18,7 +19,7 @@ pub trait Input { impl Gamepad { pub fn new() -> Self { - let mut gilrs = Gilrs::new().unwrap(); + let gilrs = Gilrs::new().unwrap(); let gamepad_id = if let Some((gamepad_id, _gamepad)) = gilrs.gamepads().next() { println!("Found Gamepad id: {:?}", gamepad_id); @@ -99,36 +100,39 @@ impl Input for Gamepad { } pub struct Keyboard { + keys: Keys, action_reg: u8, direction_reg: u8, } impl Keyboard { - pub fn new() -> Self { + pub fn new(keys: Keys) -> Self { Self { + keys, action_reg: 0, - direction_reg: 0 + direction_reg: 0, } } } impl Input for Keyboard { - fn update_events(&mut self, _cycles: u128, state: &GBState) { + fn update_events(&mut self, _cycles: u128, _state: &GBState) { let mut res = 0xf; + let keys = self.keys.borrow(); - if state.mem.display.window.is_key_down(Key::A) { + if keys.contains(&KeyCode::KeyA) { res &= 0b1110; } - if state.mem.display.window.is_key_down(Key::B) { + if keys.contains(&KeyCode::KeyB) { res &= 0b1101; } - if state.mem.display.window.is_key_down(Key::Backspace) { + if keys.contains(&KeyCode::Backspace) { res &= 0b1011; } - if state.mem.display.window.is_key_down(Key::Enter) { + if keys.contains(&KeyCode::Enter) { res &= 0b0111; } @@ -136,19 +140,19 @@ impl Input for Keyboard { let mut res = 0xf; - if state.mem.display.window.is_key_down(Key::Right) { + if keys.contains(&KeyCode::ArrowRight) { res &= 0b1110; } - if state.mem.display.window.is_key_down(Key::Left) { + if keys.contains(&KeyCode::ArrowLeft) { res &= 0b1101; } - if state.mem.display.window.is_key_down(Key::Up) { + if keys.contains(&KeyCode::ArrowUp) { res &= 0b1011; } - if state.mem.display.window.is_key_down(Key::Down) { + if keys.contains(&KeyCode::ArrowDown) { res &= 0b0111; } @@ -190,11 +194,17 @@ impl Input for GamepadRecorder { let new_direction_reg = self.input.get_direction_gamepad_reg(); if self.action_reg != new_action_reg || self.direction_reg != new_direction_reg { - println!("input update on cycle {} ! 0x{:02x} 0x{:02x}", cycles, new_action_reg, new_direction_reg); + println!( + "input update on cycle {} ! 0x{:02x} 0x{:02x}", + cycles, new_action_reg, new_direction_reg + ); if let Err(err) = self.record_file.write_all(&cycles.to_le_bytes()) { eprintln!("Failed to write to record file: {}", err); }; - if let Err(err) = self.record_file.write_all(&[new_action_reg, new_direction_reg]) { + if let Err(err) = self + .record_file + .write_all(&[new_action_reg, new_direction_reg]) + { eprintln!("Failed to write to record file: {}", err); } if let Err(err) = self.record_file.flush() { @@ -249,7 +259,9 @@ impl Input for GamepadReplay { if cycles > next_cycle_update { let mut inputs: [u8; 2] = [0; 2]; - self.record_file.read_exact(&mut inputs).expect("Unexpected EOF after cycle but before input"); + self.record_file + .read_exact(&mut inputs) + .expect("Unexpected EOF after cycle but before input"); self.action_reg = inputs[0]; self.direction_reg = inputs[1]; @@ -273,4 +285,3 @@ impl Input for GamepadReplay { self.direction_reg } } - diff --git a/src/interrupts_timers.rs b/src/interrupts_timers.rs index 4ba07be..95a0757 100644 --- a/src/interrupts_timers.rs +++ b/src/interrupts_timers.rs @@ -1,5 +1,4 @@ use crate::display::DisplayInterrupt; -use crate::serial::Serial; use crate::state::{GBState, MemError}; const TIMA_TIMER_SPEEDS: [u64; 4] = [1024, 16, 64, 256]; @@ -1,4 +1,3 @@ -use crate::serial::Serial; use crate::state::{MemError, Memory}; impl Memory { diff --git a/src/main.rs b/src/main.rs index 13381bc..71e58d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,9 @@ pub mod io; pub mod opcodes; pub mod serial; pub mod state; +pub mod window; -use crate::gamepad::{Gamepad, Input, Keyboard, GamepadRecorder, GamepadReplay}; +use crate::gamepad::{Gamepad, GamepadRecorder, GamepadReplay, Input, Keyboard}; use crate::state::GBState; use clap::Parser; use std::time::SystemTime; @@ -59,7 +60,6 @@ fn main() { _ => panic!("If using fifo serial, both input and output should be set"), }; - let save_file = format!("{}.sav", &cli.rom); state.mem.load_rom(&cli.rom).unwrap(); @@ -71,10 +71,12 @@ fn main() { ); } + let mut window = window::Window::new().unwrap(); + let mut gamepad: Box<dyn Input> = if let Some(record_file) = cli.replay_input { Box::new(GamepadReplay::new(record_file)) } else if cli.keyboard { - Box::new(Keyboard::new()) + Box::new(Keyboard::new(window.keys.clone())) } else { Box::new(Gamepad::new()) }; @@ -117,9 +119,15 @@ fn main() { gamepad.update_events(total_cycle_counter, &state); let (action_button_reg, direction_button_reg) = ( - gamepad.get_action_gamepad_reg(), - gamepad.get_direction_gamepad_reg(), - ); + gamepad.get_action_gamepad_reg(), + gamepad.get_direction_gamepad_reg(), + ); + + if let Some(fb) = state.mem.display.redraw_request { + if let Some(window::WindowSignal::Exit) = window.update(&fb) { + break; + } + } // gamepad.check_special_actions(&mut state.is_debug); if state.mem.joypad_is_action diff --git a/src/opcodes.rs b/src/opcodes.rs index b937386..6c41449 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -1,4 +1,3 @@ -use crate::serial::Serial; use crate::state::{flag, reg, GBState, MemError}; // The opcodes functions are returning the number of cycles used. diff --git a/src/serial.rs b/src/serial.rs index 577723d..65c8bfe 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,7 +1,6 @@ use std::fs::File; use std::io::{Read, Write}; use std::sync::mpsc::{self, Receiver, Sender}; -use std::sync::{Arc, Mutex}; use std::thread; pub trait Serial { @@ -35,7 +34,7 @@ impl Serial for UnconnectedSerial { false } - fn set_clock_master(&mut self, clock_master: bool) {} + fn set_clock_master(&mut self, _clock_master: bool) {} } pub struct FIFOPackage { @@ -94,10 +93,12 @@ impl FIFOSerial { impl Serial for FIFOSerial { fn write(&mut self, byte: u8) { println!("Writing {} to fifo serial", byte); - self.output.send(FIFOPackage { + if let Err(err) = self.output.send(FIFOPackage { t: true, value: byte, - }); + }) { + eprintln!("Error while sending serial package: {}", err); + }; } fn read(&mut self) -> u8 { @@ -128,9 +129,11 @@ impl Serial for FIFOSerial { fn set_clock_master(&mut self, clock_master: bool) { self.clock_master = clock_master; - self.output.send(FIFOPackage { + if let Err(err) = self.output.send(FIFOPackage { t: false, value: (if clock_master { 1 } else { 0 }), - }); + }) { + eprintln!("Error while sending serial package: {}", err); + } } } diff --git a/src/window.rs b/src/window.rs new file mode 100644 index 0000000..d139f1e --- /dev/null +++ b/src/window.rs @@ -0,0 +1,124 @@ +use pixels::{Error, Pixels, SurfaceTexture}; +use std::cell::RefCell; +use std::collections::HashSet; +use std::rc::Rc; +use std::sync::Arc; +use std::time::Duration; +use winit::dpi::LogicalSize; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::EventLoop; +use winit::keyboard::{KeyCode, PhysicalKey}; +use winit::platform::pump_events::EventLoopExtPumpEvents; +use winit::window::{Window as WinitWindow, WindowBuilder}; +use winit_input_helper::WinitInputHelper; + +const WIDTH: u32 = 160; +const HEIGHT: u32 = 144; + +pub type Keys = Rc<RefCell<HashSet<KeyCode>>>; + +pub struct Window<'a> { + event_loop: EventLoop<()>, + input: WinitInputHelper, + window: Arc<WinitWindow>, + pixels: Pixels<'a>, + pub keys: Keys, +} + +fn draw(frame: &mut [u8], fb: &[u32; 160 * 144]) { + for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { + pixel.copy_from_slice(&((fb[i] << 8) | 0xff).to_be_bytes()) + } +} + +pub enum WindowSignal { + Exit, +} + +impl<'a> Window<'a> { + pub fn new() -> Result<Self, Error> { + let event_loop = EventLoop::new().unwrap(); + let input = WinitInputHelper::new(); + let window = Arc::new({ + let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64); + WindowBuilder::new() + .with_title("Gameboy Emulator") + .with_inner_size(size) + .with_min_inner_size(size) + .build(&event_loop) + .unwrap() + }); + + let pixels = { + let window_size = window.inner_size(); + let surface_texture = + SurfaceTexture::new(window_size.width, window_size.height, window.clone()); + Pixels::new(WIDTH, HEIGHT, surface_texture)? + }; + + Ok(Self { + event_loop, + input, + window, + pixels, + keys: Rc::new(HashSet::new().into()), + }) + } + + pub fn update(&mut self, fb: &[u32; 160 * 144]) -> Option<WindowSignal> { + let mut res = None; + let mut keys = (*self.keys).borrow_mut(); + self.event_loop + .pump_events(Some(Duration::ZERO), |event, elwt| { + if let Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } = event + { + draw(self.pixels.frame_mut(), fb); + if let Err(err) = self.pixels.render() { + eprintln!("Error during render: {}", err); + return; + } + } + + if let Event::WindowEvent { + window_id: _, + event: + WindowEvent::KeyboardInput { + device_id: _, + event: ref keyboard_event, + is_synthetic: _, + }, + } = event + { + if let PhysicalKey::Code(keycode) = keyboard_event.physical_key { + if keyboard_event.state.is_pressed() { + keys.insert(keycode); + } else { + keys.remove(&keycode); + } + } + } + + if self.input.update(&event) { + if self.input.close_requested() { + elwt.exit(); + res = Some(WindowSignal::Exit); + return; + } + + if let Some(size) = self.input.window_resized() { + if let Err(err) = self.pixels.resize_surface(size.width, size.height) { + eprintln!("Error during resize: {}", err); + return; + } + } + + self.window.request_redraw(); + } + }); + + res + } +} |