From 20287511df3b52ff4695f10fc0a27d5afb8fbfb4 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 7 May 2025 10:12:00 -0400 Subject: [PATCH] added controller support for NON SCROLLING games(pacman works) --- src/bus.rs | 26 ++++++++++---- src/controller.rs | 62 ++++++++++++++++++++++++++++++++ src/lib.rs | 90 ++++++++++++++++++++++++----------------------- src/main.rs | 34 ++++++++++++++++-- 4 files changed, 158 insertions(+), 54 deletions(-) create mode 100644 src/controller.rs diff --git a/src/bus.rs b/src/bus.rs index 6fa1084..9a5aca5 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,5 +1,6 @@ use core::panic; +use crate::controller::Controller; use crate::cpu::Mem; use crate::ppu::PPU; use crate::rom::Rom; @@ -9,11 +10,12 @@ pub struct Bus<'call> { prg_rom: Vec, pub ppu: PPU, pub cycles: usize, // Contains total amount of cpu cycles - gameloop_callback: Box // Box, pointer to heap ddata is managed by the box + gameloop_callback: Box, // Box, pointer to heap ddata is managed by the box + controller1: Controller } impl <'a>Bus<'a> { - pub fn new<'call, F>(rom: Rom, gameloop_callback: F) -> Bus<'call> where F: FnMut(&PPU) + 'call,{ + pub fn new<'call, F>(rom: Rom, gameloop_callback: F) -> Bus<'call> where F: FnMut(&PPU, &mut Controller) + 'call,{ let ppu = PPU::new(rom.chr_rom, rom.screen_mirroring); Bus { cpu_vram: [0; 2048], @@ -21,6 +23,7 @@ impl <'a>Bus<'a> { ppu: ppu, cycles: 7, // Starting with 7 clock cycles gameloop_callback: Box::from(gameloop_callback), + controller1: Controller::new() } } fn read_prg_rom(&self, mut addr: u16) -> u8 { @@ -38,7 +41,7 @@ impl <'a>Bus<'a> { self.cycles += cycles as usize; let new_frame = self.ppu.tick(cycles * 3); if new_frame { - (self.gameloop_callback)(&self.ppu); + (self.gameloop_callback)(&self.ppu, &mut self.controller1); } } @@ -79,8 +82,17 @@ impl Mem for Bus<'_> { let mirror_down_addr = addr & 0x2007; self.mem_read(mirror_down_addr) } - 0x4000..=0x4017 => { - // Ignore APU and joypads + 0x4000..=0x4015 => { + // Ignoring APU + 0 + } + + 0x4016 =>{ + self.controller1.read() + } + + 0x4017 =>{ + // Controller 2 0 } PROGRAM_RAM..=PROGRAM_RAM_END => self.read_prg_rom(addr), @@ -136,11 +148,11 @@ impl Mem for Bus<'_> { } 0x4016 => { - // ignore joypad 1; + self.controller1.write(data); } 0x4017 => { - // ignore joypad 2 + // ignore controller 2 } _ => { println!("Ignoring mem write-access at {}", addr); diff --git a/src/controller.rs b/src/controller.rs new file mode 100644 index 0000000..d80509c --- /dev/null +++ b/src/controller.rs @@ -0,0 +1,62 @@ +// If strobe is off, it will read controller input from highest to lowest bit +// If strobe is on, it will read the states from the previous inputs and the state of the first button + +use bitflags::bitflags; + +bitflags!{ + #[derive(Copy, Clone, Debug)] + pub struct ControllerButton: u8{ + const RIGHT = 0b1000_0000; + const LEFT = 0b0100_0000; + const DOWN = 0b0010_0000; + const UP = 0b0001_0000; + const START = 0b0000_1000; + const SELECT = 0b0000_0100; + const B = 0b0000_0010; + const A = 0b0000_0001; + } +} + +pub struct Controller{ + strobe: bool, // Determines if we are writing input or leaving the read + button_index: u8, + button_status: ControllerButton +} + +impl Controller{ + pub fn new() -> Self { + Controller{ + strobe: false, + button_index: 0, + button_status: ControllerButton::from_bits_truncate(0b0000_0000), + } + } + + pub fn write(&mut self, data: u8){ + println!("writing controller!"); + self.strobe = data & 1 == 1; + if self.strobe{ + println!("resetting strobe!"); + // Starts at the first index + self.button_index = 0 + } + } + + pub fn read(&mut self) -> u8{ + println!("reading controller!"); + if self.button_index > 7{ + return 1; // Indicates that there isn't anything left ot read + } + let response = (self.button_status.bits() & (1 << self.button_index)) >> self.button_index; + if !self.strobe && self.button_index <= 7{ + self.button_index += 1; // Increments the index for the next read + } + println!("Response is {:x}", response); + response + } + + pub fn set_button_pressed_status(&mut self, button: ControllerButton, input: bool){ + println!("Set status to {:?}", button); + self.button_status.set(button, input); + } +} diff --git a/src/lib.rs b/src/lib.rs index 423e859..1b16c1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ pub mod trace; pub mod frame; pub mod palette; pub mod render; - +pub mod controller; use cpu::*; @@ -29,50 +29,52 @@ macro_rules! print_title { }; } -pub fn run_nestest_and_capture() -> Vec { - let sdl_context = sdl2::init().unwrap(); - let video_subsystem = sdl_context.video().unwrap(); - let window = video_subsystem - .window("Snake game", (32.0 * 10.0) as u32, (32.0 * 10.0) as u32) - .position_centered() - .build() - .unwrap(); - let mut canvas = window.into_canvas().present_vsync().build().unwrap(); - let mut event_pump = sdl_context.event_pump().unwrap(); - canvas.set_scale(3.0, 3.0).unwrap(); +// Used with NES test, removing for now due to polishing state of ppu and controllers +// pub fn run_nestest_and_capture() -> Vec { +// let sdl_context = sdl2::init().unwrap(); +// let video_subsystem = sdl_context.video().unwrap(); +// let window = video_subsystem +// .window("Snake game", (32.0 * 10.0) as u32, (32.0 * 10.0) as u32) +// .position_centered() +// .build() +// .unwrap(); - let creator = canvas.texture_creator(); - let mut texture = creator - .create_texture_target(PixelFormatEnum::RGB24, 32, 32) - .unwrap(); - canvas.set_scale(3.0, 3.0).unwrap(); - let game_bytes = std::fs::read("nestest.nes").unwrap(); - let rom = Rom::new(&game_bytes).unwrap(); - let mut frame = Frame::new(); - let bus = Bus::new(rom, move |ppu:&PPU|{ - render::render(ppu, &mut frame); - texture.update(None, &frame.data, 265 * 3).unwrap(); +// let mut canvas = window.into_canvas().present_vsync().build().unwrap(); +// let mut event_pump = sdl_context.event_pump().unwrap(); +// canvas.set_scale(3.0, 3.0).unwrap(); - canvas.copy(&texture, None, None).unwrap(); +// let creator = canvas.texture_creator(); +// let mut texture = creator +// .create_texture_target(PixelFormatEnum::RGB24, 32, 32) +// .unwrap(); +// canvas.set_scale(3.0, 3.0).unwrap(); +// let game_bytes = std::fs::read("nestest.nes").unwrap(); +// let rom = Rom::new(&game_bytes).unwrap(); +// let mut frame = Frame::new(); +// let bus = Bus::new(rom, move |ppu:&PPU|{ +// render::render(ppu, &mut frame); +// texture.update(None, &frame.data, 265 * 3).unwrap(); - canvas.present(); - for event in event_pump.poll_iter() { - match event { - Event::Quit { .. } - | Event::KeyDown { - keycode: Some(Keycode::Escape), - .. - } => std::process::exit(0), - _ => { /* do nothing */ } - } - } - }); - let mut cpu = CPU::new(bus); - cpu.reset(); - let mut output = Vec::new(); - cpu.run_with_callback(|cpu| { - output.push(trace(cpu)); - }); - output -} +// canvas.copy(&texture, None, None).unwrap(); + +// canvas.present(); +// for event in event_pump.poll_iter() { +// match event { +// Event::Quit { .. } +// | Event::KeyDown { +// keycode: Some(Keycode::Escape), +// .. +// } => std::process::exit(0), +// _ => { /* do nothing */ } +// } +// } +// }); +// let mut cpu = CPU::new(bus); +// cpu.reset(); +// let mut output = Vec::new(); +// cpu.run_with_callback(|cpu| { +// output.push(trace(cpu)); +// }); +// output +// } diff --git a/src/main.rs b/src/main.rs index 068eb0a..e0777c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; +use nes::controller::{self, ControllerButton}; use nes::cpu::*; use nes::frame::Frame; @@ -12,6 +14,7 @@ use sdl2::pixels::PixelFormatEnum; use nes::bus::Bus; fn main() { + // Setting up screen and scaling let sdl_context = sdl2::init().unwrap(); let video_subsystem = sdl_context.video().unwrap(); let window = video_subsystem @@ -37,12 +40,24 @@ fn main() { // .filter_level(log::LevelFilter::Debug) // .init(); - let game_bytes = std::fs::read("roms/smb.nes").unwrap(); + // Setting up controllers + let mut input_map = HashMap::new(); + input_map.insert(Keycode::Down, ControllerButton::DOWN); + input_map.insert(Keycode::Up, ControllerButton::UP); + input_map.insert(Keycode::Right, ControllerButton::RIGHT); + input_map.insert(Keycode::Left, ControllerButton::LEFT); + input_map.insert(Keycode::X, ControllerButton::B); + input_map.insert(Keycode::Z, ControllerButton::A); + input_map.insert(Keycode::Return, ControllerButton::START); + input_map.insert(Keycode::Space, ControllerButton::SELECT); + + // Game loading and CPU setup + let game_bytes = std::fs::read("roms/pacman.nes").unwrap(); let rom = Rom::new(&game_bytes).unwrap(); let mut frame = Frame::new(); - let bus = Bus::new(rom, move |ppu:&PPU|{ + let bus = Bus::new(rom, move |ppu:&PPU, controller: &mut controller::Controller|{ render::render(ppu, &mut frame); texture.update(None, &frame.data, 256 * 3).unwrap(); @@ -56,7 +71,20 @@ fn main() { keycode: Some(Keycode::Escape), .. } => std::process::exit(0), - _ => { /* do nothing */ } + Event::KeyDown { keycode, .. } => { + if let Some(key) = input_map.get(&keycode.unwrap_or(Keycode::Ampersand)) { + println!("Pressed button!"); + controller.set_button_pressed_status(*key, true); + } + } + Event::KeyUp { keycode, .. } => { + if let Some(key) = input_map.get(&keycode.unwrap_or(Keycode::Ampersand)) { + println!("Released button!"); + controller.set_button_pressed_status(*key, false); + } + } + + _ => { /* do nothing */ } } } });