From e70e8d6ce51a1b6034c60a2f7d661eeba836fefa Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Sun, 26 Feb 2023 11:56:45 +1100 Subject: [PATCH] input handling in renderer --- src/connect/mod.rs | 1 + src/connect/renderer.rs | 4 + src/lib.rs | 2 - src/main.rs | 71 +++++++++++-- src/processor/memory.rs | 14 ++- src/processor/memory/mmio/joypad.rs | 154 ++++++++++++---------------- src/processor/memory/mmio/mod.rs | 2 +- src/processor/mod.rs | 5 +- 8 files changed, 144 insertions(+), 109 deletions(-) diff --git a/src/connect/mod.rs b/src/connect/mod.rs index 98e066e..3928b09 100644 --- a/src/connect/mod.rs +++ b/src/connect/mod.rs @@ -1,2 +1,3 @@ mod renderer; +pub use crate::processor::memory::mmio::joypad::JoypadState; pub use renderer::Renderer; diff --git a/src/connect/renderer.rs b/src/connect/renderer.rs index db6c4bd..51d20ad 100644 --- a/src/connect/renderer.rs +++ b/src/connect/renderer.rs @@ -1,7 +1,11 @@ +use super::JoypadState; + pub trait Renderer { fn prepare(&mut self, width: usize, height: usize); fn display(&mut self, buffer: &[u32]); fn set_title(&mut self, _title: String) {} + + fn latest_joypad_state(&mut self) -> JoypadState; } diff --git a/src/lib.rs b/src/lib.rs index b182935..14c2c12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,6 @@ use crate::{ util::{pause, print_cycles}, }; use connect::Renderer; -use gilrs::Gilrs; use once_cell::sync::OnceCell; use processor::{memory::Rom, Cpu}; use std::{ @@ -76,7 +75,6 @@ pub fn init( let mut cpu = Cpu::new( Memory::init(bootrom, rom, window, options.connect_serial, tile_window), bootrom_enabled, - Gilrs::new().unwrap(), ); let mut cycle_num = 0; diff --git a/src/main.rs b/src/main.rs index 496dc48..d062ce8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,10 @@ use clap::{ArgGroup, Parser}; -use gb_emu::{connect::Renderer, util::scale_buffer}; - -use minifb::{Window, WindowOptions}; +use gb_emu::{ + connect::{JoypadState, Renderer}, + util::scale_buffer, +}; +use gilrs::{Button, Gilrs}; +use minifb::{Key, Window, WindowOptions}; /// Gameboy (DMG-A/B/C) emulator #[derive(Parser, Debug)] @@ -60,12 +63,16 @@ fn main() { }; let tile_window: Option> = if args.tile_window { - Some(Box::new(WindowRenderer::new(factor))) + Some(Box::new(WindowRenderer::new(factor, None))) } else { None }; - gb_emu::init(options, Box::new(WindowRenderer::new(factor)), tile_window); + gb_emu::init( + options, + Box::new(WindowRenderer::new(factor, Some(Gilrs::new().unwrap()))), + tile_window, + ); } struct WindowRenderer { @@ -74,16 +81,20 @@ struct WindowRenderer { width: usize, height: usize, factor: usize, + gamepad_handler: Option, + joypad_state: JoypadState, } impl WindowRenderer { - fn new(factor: usize) -> Self { + fn new(factor: usize, gamepad_handler: Option) -> Self { Self { window: None, scaled_buf: vec![], width: 0, height: 0, factor, + gamepad_handler, + joypad_state: JoypadState::default(), } } } @@ -121,4 +132,52 @@ impl Renderer for WindowRenderer { window.set_title(&title); } } + + fn latest_joypad_state(&mut self) -> JoypadState { + self.joypad_state.reset(); + + if let Some(ref mut gamepad_handler) = self.gamepad_handler { + while let Some(event) = gamepad_handler.next_event() { + if let gilrs::EventType::ButtonPressed(button, _) = event.event { + match button { + Button::DPadDown => self.joypad_state.down = true, + Button::DPadUp => self.joypad_state.up = true, + Button::DPadLeft => self.joypad_state.left = true, + Button::DPadRight => self.joypad_state.right = true, + Button::Start => self.joypad_state.start = true, + Button::Select => self.joypad_state.select = true, + Button::East => self.joypad_state.a = true, + Button::South => self.joypad_state.b = true, + _ => {} + } + } + } + + for (_, pad) in gamepad_handler.gamepads() { + self.joypad_state.down |= pad.is_pressed(Button::DPadDown); + self.joypad_state.up |= pad.is_pressed(Button::DPadUp); + self.joypad_state.left |= pad.is_pressed(Button::DPadLeft); + self.joypad_state.right |= pad.is_pressed(Button::DPadRight); + self.joypad_state.start |= pad.is_pressed(Button::Start); + self.joypad_state.select |= pad.is_pressed(Button::Select); + self.joypad_state.a |= pad.is_pressed(Button::East); + self.joypad_state.b |= pad.is_pressed(Button::South); + } + } + + if let Some(window) = &self.window { + let keys = window.get_keys(); + + self.joypad_state.down |= keys.contains(&Key::Down) || keys.contains(&Key::S); + self.joypad_state.up |= keys.contains(&Key::Up) || keys.contains(&Key::W); + self.joypad_state.left |= keys.contains(&Key::Left) || keys.contains(&Key::A); + self.joypad_state.right |= keys.contains(&Key::Right) || keys.contains(&Key::D); + self.joypad_state.start |= keys.contains(&Key::Equal); + self.joypad_state.select |= keys.contains(&Key::Minus); + self.joypad_state.a |= keys.contains(&Key::Apostrophe); + self.joypad_state.b |= keys.contains(&Key::Semicolon); + } + + self.joypad_state + } } diff --git a/src/processor/memory.rs b/src/processor/memory.rs index 78dab22..9c0b264 100644 --- a/src/processor/memory.rs +++ b/src/processor/memory.rs @@ -1,7 +1,10 @@ use self::mmio::{Apu, Gpu, Joypad, Serial, Timer}; pub use self::rom::Rom; -use crate::{connect::Renderer, processor::SplitRegister, verbose_println, Cpu}; -use gilrs::Gilrs; +use crate::{ + connect::{JoypadState, Renderer}, + processor::SplitRegister, + verbose_println, Cpu, +}; mod interrupts; pub use interrupts::{Interrupt, Interrupts}; @@ -184,8 +187,8 @@ impl Memory { } } - pub fn update_pressed_keys(&mut self, gamepads: &mut Gilrs) -> bool { - self.joypad.update_pressed_keys(gamepads) + pub fn update_pressed_keys(&mut self, latest_state: JoypadState) -> bool { + self.joypad.update_pressed_keys(latest_state) } pub(super) fn cpu_ram_init(&mut self) { @@ -246,7 +249,8 @@ impl Cpu { .set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat); if gpu_interrupts.vblank { - let joypad_interrupt = self.memory.update_pressed_keys(&mut self.gamepad_handler); + let latest_state = self.memory.gpu.window.latest_joypad_state(); + let joypad_interrupt = self.memory.update_pressed_keys(latest_state); self.memory .interrupts .set_interrupt(Interrupt::Joypad, joypad_interrupt); diff --git a/src/processor/memory/mmio/joypad.rs b/src/processor/memory/mmio/joypad.rs index 5b0a7a7..8a19095 100644 --- a/src/processor/memory/mmio/joypad.rs +++ b/src/processor/memory/mmio/joypad.rs @@ -1,95 +1,26 @@ use crate::util::{clear_bit, get_bit}; -use gilrs::{Button, Gilrs}; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Joypad { sel_action: bool, sel_direction: bool, - down: bool, - up: bool, - left: bool, - right: bool, - start: bool, - select: bool, - b: bool, - a: bool, + state: JoypadState, } -impl Joypad { - pub fn as_register(&self) -> u8 { - let mut reg = 0xFF; - if self.sel_action { - reg = clear_bit(reg, 5); - if self.start { - reg = clear_bit(reg, 3); - } - if self.select { - reg = clear_bit(reg, 2); - } - if self.b { - reg = clear_bit(reg, 1); - } - if self.a { - reg = clear_bit(reg, 0); - } - } - if self.sel_direction { - reg = clear_bit(reg, 4); - if self.down { - reg = clear_bit(reg, 3); - } - if self.up { - reg = clear_bit(reg, 2); - } - if self.left { - reg = clear_bit(reg, 1); - } - if self.right { - reg = clear_bit(reg, 0); - } - } - reg - } +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct JoypadState { + pub down: bool, + pub up: bool, + pub left: bool, + pub right: bool, + pub start: bool, + pub select: bool, + pub b: bool, + pub a: bool, +} - pub fn mmio_write(&mut self, data: u8) { - self.sel_action = !get_bit(data, 5); - self.sel_direction = !get_bit(data, 4); - } - - pub fn update_pressed_keys(&mut self, gamepad_handler: &mut Gilrs) -> bool { - let old = *self; - self.clear_buttons(); - - while let Some(event) = gamepad_handler.next_event() { - if let gilrs::EventType::ButtonPressed(button, _) = event.event { - match button { - Button::DPadDown => self.down = true, - Button::DPadUp => self.up = true, - Button::DPadLeft => self.left = true, - Button::DPadRight => self.right = true, - Button::Start => self.start = true, - Button::Select => self.select = true, - Button::East => self.a = true, - Button::South => self.b = true, - _ => {} - } - } - } - - for (_, pad) in gamepad_handler.gamepads() { - self.down |= pad.is_pressed(Button::DPadDown); - self.up |= pad.is_pressed(Button::DPadUp); - self.left |= pad.is_pressed(Button::DPadLeft); - self.right |= pad.is_pressed(Button::DPadRight); - self.start |= pad.is_pressed(Button::Start); - self.select |= pad.is_pressed(Button::Select); - self.a |= pad.is_pressed(Button::East); - self.b |= pad.is_pressed(Button::South); - } - *self != old - } - - fn clear_buttons(&mut self) { +impl JoypadState { + pub fn reset(&mut self) { self.down = false; self.up = false; self.left = false; @@ -101,19 +32,60 @@ impl Joypad { } } +impl Joypad { + pub fn as_register(&self) -> u8 { + let mut reg = 0xFF; + if self.sel_action { + reg = clear_bit(reg, 5); + if self.state.start { + reg = clear_bit(reg, 3); + } + if self.state.select { + reg = clear_bit(reg, 2); + } + if self.state.b { + reg = clear_bit(reg, 1); + } + if self.state.a { + reg = clear_bit(reg, 0); + } + } + if self.sel_direction { + reg = clear_bit(reg, 4); + if self.state.down { + reg = clear_bit(reg, 3); + } + if self.state.up { + reg = clear_bit(reg, 2); + } + if self.state.left { + reg = clear_bit(reg, 1); + } + if self.state.right { + reg = clear_bit(reg, 0); + } + } + reg + } + + pub fn mmio_write(&mut self, data: u8) { + self.sel_action = !get_bit(data, 5); + self.sel_direction = !get_bit(data, 4); + } + + pub fn update_pressed_keys(&mut self, latest_state: JoypadState) -> bool { + let should_interrupt = self.state != latest_state; + self.state = latest_state; + should_interrupt + } +} + impl Default for Joypad { fn default() -> Self { Self { sel_action: true, sel_direction: true, - down: false, - up: false, - left: false, - right: false, - start: false, - select: false, - b: false, - a: false, + state: JoypadState::default(), } } } diff --git a/src/processor/memory/mmio/mod.rs b/src/processor/memory/mmio/mod.rs index 7f65850..ea93165 100644 --- a/src/processor/memory/mmio/mod.rs +++ b/src/processor/memory/mmio/mod.rs @@ -1,6 +1,6 @@ mod apu; pub(crate) mod gpu; -mod joypad; +pub(crate) mod joypad; mod serial; mod timer; pub use apu::Apu; diff --git a/src/processor/mod.rs b/src/processor/mod.rs index d4ff4c1..3a0d73b 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -1,6 +1,5 @@ use self::memory::{Interrupt, Memory}; use crate::verbose_println; -use gilrs::Gilrs; mod instructions; pub mod memory; @@ -26,11 +25,10 @@ pub struct Cpu { last_instruction_addr: u16, halted: bool, should_halt_bug: bool, - gamepad_handler: Gilrs, } impl Cpu { - pub fn new(mut memory: Memory, run_bootrom: bool, gamepad_handler: Gilrs) -> Self { + pub fn new(mut memory: Memory, run_bootrom: bool) -> Self { if !run_bootrom { memory.cpu_ram_init(); } @@ -41,7 +39,6 @@ impl Cpu { last_instruction_addr: 0x0, halted: false, should_halt_bug: false, - gamepad_handler, } }