input handling in renderer

This commit is contained in:
Alex Janka 2023-02-26 11:56:45 +11:00
parent a36d161939
commit e70e8d6ce5
8 changed files with 144 additions and 109 deletions

View file

@ -1,2 +1,3 @@
mod renderer; mod renderer;
pub use crate::processor::memory::mmio::joypad::JoypadState;
pub use renderer::Renderer; pub use renderer::Renderer;

View file

@ -1,7 +1,11 @@
use super::JoypadState;
pub trait Renderer { pub trait Renderer {
fn prepare(&mut self, width: usize, height: usize); fn prepare(&mut self, width: usize, height: usize);
fn display(&mut self, buffer: &[u32]); fn display(&mut self, buffer: &[u32]);
fn set_title(&mut self, _title: String) {} fn set_title(&mut self, _title: String) {}
fn latest_joypad_state(&mut self) -> JoypadState;
} }

View file

@ -11,7 +11,6 @@ use crate::{
util::{pause, print_cycles}, util::{pause, print_cycles},
}; };
use connect::Renderer; use connect::Renderer;
use gilrs::Gilrs;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use processor::{memory::Rom, Cpu}; use processor::{memory::Rom, Cpu};
use std::{ use std::{
@ -76,7 +75,6 @@ pub fn init(
let mut cpu = Cpu::new( let mut cpu = Cpu::new(
Memory::init(bootrom, rom, window, options.connect_serial, tile_window), Memory::init(bootrom, rom, window, options.connect_serial, tile_window),
bootrom_enabled, bootrom_enabled,
Gilrs::new().unwrap(),
); );
let mut cycle_num = 0; let mut cycle_num = 0;

View file

@ -1,7 +1,10 @@
use clap::{ArgGroup, Parser}; use clap::{ArgGroup, Parser};
use gb_emu::{connect::Renderer, util::scale_buffer}; use gb_emu::{
connect::{JoypadState, Renderer},
use minifb::{Window, WindowOptions}; util::scale_buffer,
};
use gilrs::{Button, Gilrs};
use minifb::{Key, Window, WindowOptions};
/// Gameboy (DMG-A/B/C) emulator /// Gameboy (DMG-A/B/C) emulator
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -60,12 +63,16 @@ fn main() {
}; };
let tile_window: Option<Box<dyn Renderer>> = if args.tile_window { let tile_window: Option<Box<dyn Renderer>> = if args.tile_window {
Some(Box::new(WindowRenderer::new(factor))) Some(Box::new(WindowRenderer::new(factor, None)))
} else { } else {
None 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 { struct WindowRenderer {
@ -74,16 +81,20 @@ struct WindowRenderer {
width: usize, width: usize,
height: usize, height: usize,
factor: usize, factor: usize,
gamepad_handler: Option<Gilrs>,
joypad_state: JoypadState,
} }
impl WindowRenderer { impl WindowRenderer {
fn new(factor: usize) -> Self { fn new(factor: usize, gamepad_handler: Option<Gilrs>) -> Self {
Self { Self {
window: None, window: None,
scaled_buf: vec![], scaled_buf: vec![],
width: 0, width: 0,
height: 0, height: 0,
factor, factor,
gamepad_handler,
joypad_state: JoypadState::default(),
} }
} }
} }
@ -121,4 +132,52 @@ impl Renderer for WindowRenderer {
window.set_title(&title); 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
}
} }

View file

@ -1,7 +1,10 @@
use self::mmio::{Apu, Gpu, Joypad, Serial, Timer}; use self::mmio::{Apu, Gpu, Joypad, Serial, Timer};
pub use self::rom::Rom; pub use self::rom::Rom;
use crate::{connect::Renderer, processor::SplitRegister, verbose_println, Cpu}; use crate::{
use gilrs::Gilrs; connect::{JoypadState, Renderer},
processor::SplitRegister,
verbose_println, Cpu,
};
mod interrupts; mod interrupts;
pub use interrupts::{Interrupt, Interrupts}; pub use interrupts::{Interrupt, Interrupts};
@ -184,8 +187,8 @@ impl Memory {
} }
} }
pub fn update_pressed_keys(&mut self, gamepads: &mut Gilrs) -> bool { pub fn update_pressed_keys(&mut self, latest_state: JoypadState) -> bool {
self.joypad.update_pressed_keys(gamepads) self.joypad.update_pressed_keys(latest_state)
} }
pub(super) fn cpu_ram_init(&mut self) { pub(super) fn cpu_ram_init(&mut self) {
@ -246,7 +249,8 @@ impl Cpu {
.set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat); .set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat);
if gpu_interrupts.vblank { 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 self.memory
.interrupts .interrupts
.set_interrupt(Interrupt::Joypad, joypad_interrupt); .set_interrupt(Interrupt::Joypad, joypad_interrupt);

View file

@ -1,95 +1,26 @@
use crate::util::{clear_bit, get_bit}; use crate::util::{clear_bit, get_bit};
use gilrs::{Button, Gilrs};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Joypad { pub struct Joypad {
sel_action: bool, sel_action: bool,
sel_direction: bool, sel_direction: bool,
down: bool, state: JoypadState,
up: bool,
left: bool,
right: bool,
start: bool,
select: bool,
b: bool,
a: bool,
} }
impl Joypad { #[derive(Debug, Clone, Copy, PartialEq, Default)]
pub fn as_register(&self) -> u8 { pub struct JoypadState {
let mut reg = 0xFF; pub down: bool,
if self.sel_action { pub up: bool,
reg = clear_bit(reg, 5); pub left: bool,
if self.start { pub right: bool,
reg = clear_bit(reg, 3); pub start: bool,
} pub select: bool,
if self.select { pub b: bool,
reg = clear_bit(reg, 2); pub a: bool,
} }
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
}
pub fn mmio_write(&mut self, data: u8) { impl JoypadState {
self.sel_action = !get_bit(data, 5); pub fn reset(&mut self) {
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) {
self.down = false; self.down = false;
self.up = false; self.up = false;
self.left = 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 { impl Default for Joypad {
fn default() -> Self { fn default() -> Self {
Self { Self {
sel_action: true, sel_action: true,
sel_direction: true, sel_direction: true,
down: false, state: JoypadState::default(),
up: false,
left: false,
right: false,
start: false,
select: false,
b: false,
a: false,
} }
} }
} }

View file

@ -1,6 +1,6 @@
mod apu; mod apu;
pub(crate) mod gpu; pub(crate) mod gpu;
mod joypad; pub(crate) mod joypad;
mod serial; mod serial;
mod timer; mod timer;
pub use apu::Apu; pub use apu::Apu;

View file

@ -1,6 +1,5 @@
use self::memory::{Interrupt, Memory}; use self::memory::{Interrupt, Memory};
use crate::verbose_println; use crate::verbose_println;
use gilrs::Gilrs;
mod instructions; mod instructions;
pub mod memory; pub mod memory;
@ -26,11 +25,10 @@ pub struct Cpu {
last_instruction_addr: u16, last_instruction_addr: u16,
halted: bool, halted: bool,
should_halt_bug: bool, should_halt_bug: bool,
gamepad_handler: Gilrs,
} }
impl Cpu { 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 { if !run_bootrom {
memory.cpu_ram_init(); memory.cpu_ram_init();
} }
@ -41,7 +39,6 @@ impl Cpu {
last_instruction_addr: 0x0, last_instruction_addr: 0x0,
halted: false, halted: false,
should_halt_bug: false, should_halt_bug: false,
gamepad_handler,
} }
} }