mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
Basic joypad implementation
This commit is contained in:
parent
b5bb582c54
commit
292c7d1507
21
src/bus.rs
21
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::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]);
|
||||||
|
@ -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
91
src/joypad.rs
Normal 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user