input handling in renderer
This commit is contained in:
parent
a36d161939
commit
e70e8d6ce5
|
@ -1,2 +1,3 @@
|
|||
mod renderer;
|
||||
pub use crate::processor::memory::mmio::joypad::JoypadState;
|
||||
pub use renderer::Renderer;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
71
src/main.rs
71
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<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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod apu;
|
||||
pub(crate) mod gpu;
|
||||
mod joypad;
|
||||
pub(crate) mod joypad;
|
||||
mod serial;
|
||||
mod timer;
|
||||
pub use apu::Apu;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue