Basic joypad implementation

This commit is contained in:
Franco Colmenarez 2021-10-31 21:02:09 -05:00
parent b5bb582c54
commit 292c7d1507
5 changed files with 190 additions and 6 deletions

View File

@ -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::ppu::{PPU, LCDStatus, LCDStatusModeFlag, LCD_STATUS_ADDRESS, LCD_CONTROL_ADDRESS, LCD_Y_ADDRESS};
use crate::cpu::{Interrupt}; use crate::cpu::{Interrupt};
use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS}; use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS};
use crate::joypad::{Joypad, JOYPAD_ADDRESS};
pub struct AddressRange { pub struct AddressRange {
begin: u16, begin: u16,
@ -50,7 +51,7 @@ pub struct Bus {
impl Bus { impl Bus {
pub fn new() -> Self { 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.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/01-special.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/02-interrupts.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]) // _ => ROM::from_bytes(&[0; 0xFFFF])
_ => panic!("Could not read ROM"), _ => panic!("Could not read ROM"),
}; };
let mut data = [0x00; 0x10000];
data[JOYPAD_ADDRESS as usize] = 0b11001111;
Self { Self {
data: [0x00; 0x10000], data,
game_rom, game_rom,
} }
} }
pub fn read(&self, address: u16) -> u8 { pub fn read(&self, address: u16) -> u8 {
if address == 0xFF00 {
return 0xFF;
}
if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) { if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) {
return self.game_rom.read(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] self.data[address as usize]
} }
@ -112,11 +115,19 @@ impl Bus {
} else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) { } else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) {
self.data[address as usize] = data; self.data[address as usize] = data;
self.data[LCD_Y_ADDRESS as usize] = 0x00; 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 { } else {
self.data[address as usize] = data; 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) { pub fn write_16bit(&mut self, address: u16, data: u16) {
let bytes = data.to_le_bytes(); let bytes = data.to_le_bytes();
self.write(address, bytes[0]); self.write(address, bytes[0]);

View File

@ -1,24 +1,103 @@
use std::{thread, time}; 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::ppu::PPU;
use crate::bus::Bus; use crate::bus::Bus;
use crate::timer::Timer; use crate::timer::Timer;
use crate::joypad::{Joypad, Button, JOYPAD_ADDRESS};
pub struct Emulator { pub struct Emulator {
cpu: CPU, cpu: CPU,
ppu: PPU, ppu: PPU,
bus: Bus, bus: Bus,
timer: Timer, timer: Timer,
joypad: Joypad,
} }
impl Emulator { impl Emulator {
pub fn new() -> Self { pub fn new() -> Self {
let mut joypad: Joypad = Joypad::new();
Self { Self {
cpu: CPU::new(), cpu: CPU::new(),
ppu: PPU::new(), ppu: PPU::new(),
bus: Bus::new(), bus: Bus::new(),
timer: Timer::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);
} }
} }

91
src/joypad.rs Normal file
View File

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

View File

@ -4,5 +4,6 @@ pub mod ppu;
pub mod timer; pub mod timer;
pub mod rom; pub mod rom;
pub mod bus; pub mod bus;
pub mod joypad;
pub mod emulator; pub mod emulator;
pub mod render; pub mod render;

View File

@ -47,6 +47,8 @@ pub fn start_eventloop() {
return; return;
} }
emulator.handle_input(&input);
// Resize the window // Resize the window
if let Some(size) = input.window_resized() { if let Some(size) = input.window_resized() {
pixels.resize_surface(size.width, size.height); pixels.resize_surface(size.width, size.height);