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;
pub use crate::processor::memory::mmio::joypad::JoypadState;
pub use renderer::Renderer;

View file

@ -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;
}

View file

@ -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;

View file

@ -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<Box<dyn Renderer>> = 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<Gilrs>,
joypad_state: JoypadState,
}
impl WindowRenderer {
fn new(factor: usize) -> Self {
fn new(factor: usize, gamepad_handler: Option<Gilrs>) -> 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
}
}

View file

@ -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);

View file

@ -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(),
}
}
}

View file

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

View file

@ -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,
}
}