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