From 292c7d1507476fc632aaf6fd6ff34b7f037c956c Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Sun, 31 Oct 2021 21:02:09 -0500 Subject: [PATCH] Basic joypad implementation --- src/bus.rs | 21 +++++++++--- src/emulator.rs | 81 ++++++++++++++++++++++++++++++++++++++++++- src/joypad.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/render.rs | 2 ++ 5 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 src/joypad.rs diff --git a/src/bus.rs b/src/bus.rs index e46548a..73ce70a 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -8,6 +8,7 @@ use crate::rom::ROM; use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag, LCD_STATUS_ADDRESS, LCD_CONTROL_ADDRESS, LCD_Y_ADDRESS}; use crate::cpu::{Interrupt}; use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS}; +use crate::joypad::{Joypad, JOYPAD_ADDRESS}; pub struct AddressRange { begin: u16, @@ -50,7 +51,7 @@ pub struct Bus { impl Bus { pub fn new() -> Self { - let game_rom = match ROM::load_file("ignore/dr-mario.gb".to_string()) { + let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/02-interrupts.gb".to_string()) { @@ -67,19 +68,21 @@ impl Bus { // _ => ROM::from_bytes(&[0; 0xFFFF]) _ => panic!("Could not read ROM"), }; + let mut data = [0x00; 0x10000]; + data[JOYPAD_ADDRESS as usize] = 0b11001111; Self { - data: [0x00; 0x10000], + data, game_rom, } } pub fn read(&self, address: u16) -> u8 { - if address == 0xFF00 { - return 0xFF; - } if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) { return self.game_rom.read(address); } + if address == JOYPAD_ADDRESS { + println!("Joypad read {:08b}", self.data[address as usize]); + } self.data[address as usize] } @@ -112,11 +115,19 @@ impl Bus { } else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) { self.data[address as usize] = data; self.data[LCD_Y_ADDRESS as usize] = 0x00; + } else if address == JOYPAD_ADDRESS { + println!("Joypad write: {:08b}", data); + let byte = self.data[JOYPAD_ADDRESS as usize]; + self.data[JOYPAD_ADDRESS as usize] = (data & 0b00110000) | 0b11000000 | (byte & 0b00001111); } else { self.data[address as usize] = data; } } + pub fn force_write(&mut self, address: u16, data: u8) { + self.data[address as usize] = data; + } + pub fn write_16bit(&mut self, address: u16, data: u16) { let bytes = data.to_le_bytes(); self.write(address, bytes[0]); diff --git a/src/emulator.rs b/src/emulator.rs index 35b71d3..33fda55 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -1,24 +1,103 @@ use std::{thread, time}; +use winit_input_helper::WinitInputHelper; +use winit::event::{VirtualKeyCode}; -use crate::cpu::{CPU, Cycles}; +use crate::cpu::{CPU, Cycles, Interrupt}; use crate::ppu::PPU; use crate::bus::Bus; use crate::timer::Timer; +use crate::joypad::{Joypad, Button, JOYPAD_ADDRESS}; pub struct Emulator { cpu: CPU, ppu: PPU, bus: Bus, timer: Timer, + joypad: Joypad, } impl Emulator { pub fn new() -> Self { + let mut joypad: Joypad = Joypad::new(); Self { cpu: CPU::new(), ppu: PPU::new(), bus: Bus::new(), timer: Timer::new(), + joypad: Joypad::new(), + } + } + + pub fn handle_input(&mut self, input: &WinitInputHelper) { + let mut change = false; + if input.key_pressed(VirtualKeyCode::K) { + change = true; + self.joypad.press(Button::A); + } + if input.key_pressed(VirtualKeyCode::J) { + change = true; + self.joypad.press(Button::B); + } + if input.key_pressed(VirtualKeyCode::W) { + change = true; + self.joypad.press(Button::Up); + } + if input.key_pressed(VirtualKeyCode::S) { + change = true; + self.joypad.press(Button::Down); + } + if input.key_pressed(VirtualKeyCode::A) { + change = true; + self.joypad.press(Button::Left); + } + if input.key_pressed(VirtualKeyCode::D) { + change = true; + self.joypad.press(Button::Right); + } + if input.key_pressed(VirtualKeyCode::N) { + change = true; + self.joypad.press(Button::Start); + } + if input.key_pressed(VirtualKeyCode::B) { + change = true; + self.joypad.press(Button::Select); + } + + if input.key_released(VirtualKeyCode::K) { + change = true; + self.joypad.release(Button::A); + } + if input.key_released(VirtualKeyCode::J) { + change = true; + self.joypad.release(Button::B); + } + if input.key_released(VirtualKeyCode::W) { + change = true; + self.joypad.release(Button::Up); + } + if input.key_released(VirtualKeyCode::S) { + change = true; + self.joypad.release(Button::Down); + } + if input.key_released(VirtualKeyCode::A) { + change = true; + self.joypad.release(Button::Left); + } + if input.key_released(VirtualKeyCode::D) { + change = true; + self.joypad.release(Button::Right); + } + if input.key_released(VirtualKeyCode::N) { + change = true; + self.joypad.release(Button::Start); + } + if input.key_released(VirtualKeyCode::B) { + change = true; + self.joypad.release(Button::Select); + } + if change { + self.bus.force_write(JOYPAD_ADDRESS, self.joypad.get(&self.bus)); + self.bus.set_interrupt_flag(Interrupt::Joypad, true); } } diff --git a/src/joypad.rs b/src/joypad.rs new file mode 100644 index 0000000..30b7e67 --- /dev/null +++ b/src/joypad.rs @@ -0,0 +1,91 @@ +use crate::bus::{Bus}; +use crate::utils::{BitIndex, get_bit}; + +pub const JOYPAD_ADDRESS: u16 = 0xFF00; + +#[derive(Debug, Copy, Clone)] +pub enum Button { + A, + B, + Up, + Down, + Left, + Right, + Start, + Select +} + +pub struct Joypad { + a: bool, + b: bool, + up: bool, + down: bool, + left: bool, + right: bool, + start: bool, + select: bool, +} + +impl Joypad { + pub fn new() -> Self { + Self { + a: false, + b: false, + up: false, + down: false, + left: false, + right: false, + start: false, + select: false, + } + } + + pub fn press(&mut self, button: Button) { + println!("{:?} pressed", button); + match button { + Button::A => self.a = true, + Button::B => self.b = true, + Button::Up => self.up = true, + Button::Down => self.down = true, + Button::Left => self.left = true, + Button::Right => self.right = true, + Button::Start => self.start = true, + Button::Select => self.select = true, + }; + } + + pub fn release(&mut self, button: Button) { + println!("{:?} released", button); + match button { + Button::A => self.a = false, + Button::B => self.b = false, + Button::Up => self.up = false, + Button::Down => self.down = false, + Button::Left => self.left = false, + Button::Right => self.right = false, + Button::Start => self.start = false, + Button::Select => self.select = false, + }; + } + + pub fn get(&self, bus: &Bus) -> u8 { + let byte = bus.read(JOYPAD_ADDRESS); + let direction = !get_bit(byte, BitIndex::I4); + let action = !get_bit(byte, BitIndex::I5); + + let action = true; + let direction = true; + + 0b11000000 | + (byte & 0b00110000) | + ( + (!((direction && self.down) || (action && self.start)) as u8) << 3 + ) | ( + (!((direction && self.up) || (action && self.select)) as u8) << 2 + ) | ( + (!((direction && self.left) || (action && self.b)) as u8) << 1 + ) | ( + (!((direction && self.right) || (action && self.a)) as u8) + ) + } +} diff --git a/src/lib.rs b/src/lib.rs index 4cc3aa3..b53c180 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,5 +4,6 @@ pub mod ppu; pub mod timer; pub mod rom; pub mod bus; +pub mod joypad; pub mod emulator; pub mod render; diff --git a/src/render.rs b/src/render.rs index f644768..65f97cc 100644 --- a/src/render.rs +++ b/src/render.rs @@ -47,6 +47,8 @@ pub fn start_eventloop() { return; } + emulator.handle_input(&input); + // Resize the window if let Some(size) = input.window_resized() { pixels.resize_surface(size.width, size.height);