From 2b723440cbd42b7745d05fe2957bf86d8e12e12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Sun, 10 Jul 2022 11:41:57 +0100 Subject: [PATCH] feat: greatly simplified SDL emulator structure --- examples/sdl/Cargo.toml | 1 - examples/sdl/src/main.rs | 283 ++++++++++----------- examples/sdl/src/util.rs | 72 ++++++ res/roms/paradius/mem_timing/mem_timing.gb | Bin 0 -> 65536 bytes src/rom.rs | 6 +- 5 files changed, 215 insertions(+), 147 deletions(-) create mode 100644 examples/sdl/src/util.rs create mode 100644 res/roms/paradius/mem_timing/mem_timing.gb diff --git a/examples/sdl/Cargo.toml b/examples/sdl/Cargo.toml index 6a029ada..203f0936 100644 --- a/examples/sdl/Cargo.toml +++ b/examples/sdl/Cargo.toml @@ -9,7 +9,6 @@ edition = "2018" [dependencies.boytacean] path = "../.." -features = ["debug"] [dependencies.sdl2] version = "0.35" diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs index 2fbc7a1c..f7a1ec40 100644 --- a/examples/sdl/src/main.rs +++ b/examples/sdl/src/main.rs @@ -1,111 +1,167 @@ pub mod data; +pub mod util; use boytacean::{ gb::GameBoy, pad::PadKey, ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}, }; -use sdl2::{ - event::Event, keyboard::Keycode, pixels::PixelFormatEnum, rwops::RWops, surface::Surface, - sys::image, video::Window, AudioSubsystem, EventPump, TimerSubsystem, VideoSubsystem, -}; +use sdl2::{event::Event, keyboard::Keycode, pixels::PixelFormatEnum}; +use util::Graphics; + +use crate::util::surface_from_bytes; + +const VISUAL_HZ: u32 = 60; +const SCREEN_SCALE: f32 = 2.0; /// The base title to be used in the window. static TITLE: &'static str = "Boytacean"; -pub struct Graphics { - window: Window, - video_subsystem: VideoSubsystem, - timer_subsystem: TimerSubsystem, - audio_subsystem: AudioSubsystem, - event_pump: EventPump, -} - -fn start_sdl() -> Graphics { - // initializes the SDL sub-system, making it ready to be - // used for display of graphics and audio - let sdl = sdl2::init().unwrap(); - let video_subsystem = sdl.video().unwrap(); - let timer_subsystem = sdl.timer().unwrap(); - let audio_subsystem = sdl.audio().unwrap(); - let event_pump = sdl.event_pump().unwrap(); - - // initialized the fonts context to be used - // in the loading of fonts - let ttf_context = sdl2::ttf::init().unwrap(); - - // creates the system window that is going to be used to - // show the emulator and sets it to the central are o screen - let window = video_subsystem - .window( - TITLE, - 2 as u32 * DISPLAY_WIDTH as u32, //@todo check screen scale - 2 as u32 * DISPLAY_HEIGHT as u32, //@todo check screen scale - ) - .resizable() - .position_centered() - .opengl() - .build() - .unwrap(); - - Graphics { - window: window, - video_subsystem: video_subsystem, - timer_subsystem: timer_subsystem, - audio_subsystem: audio_subsystem, - event_pump: event_pump, - } +pub struct Emulator { + system: GameBoy, + graphics: Graphics, + visual_frequency: u32, + next_tick_time: f32, + next_tick_time_i: u32, } -pub fn surface_from_bytes(bytes: &[u8]) -> Surface { - unsafe { - let rw_ops = RWops::from_bytes(bytes).unwrap(); - let raw_surface = image::IMG_Load_RW(rw_ops.raw(), 0); - Surface::from_ll(raw_surface) +impl Emulator { + pub fn new(system: GameBoy, screen_scale: f32) -> Self { + Self { + system: system, + graphics: Graphics::new( + TITLE, + DISPLAY_WIDTH as u32, + DISPLAY_HEIGHT as u32, + screen_scale, + ), + visual_frequency: VISUAL_HZ, + next_tick_time: 0.0, + next_tick_time_i: 0, + } } -} -fn main() { - let mut graphics = start_sdl(); - - // updates the icon of the window to reflect the image - // and style of the emulator - let surface = surface_from_bytes(&data::ICON); - graphics.window.set_icon(&surface); + pub fn run(&mut self) { + // updates the icon of the window to reflect the image + // and style of the emulator + let surface = surface_from_bytes(&data::ICON); + self.graphics.window_mut().set_icon(&surface); + + // creates an accelerated canvas to be used in the drawing + // then clears it and presents it + self.graphics.canvas.present(); + + // creates a texture creator for the current canvas, required + // for the creation of dynamic and static textures + let texture_creator = self.graphics.canvas.texture_creator(); + + // creates the texture streaming that is going to be used + // as the target for the pixel buffer + let mut texture = texture_creator + .create_texture_streaming( + PixelFormatEnum::RGB24, + DISPLAY_WIDTH as u32, + DISPLAY_HEIGHT as u32, + ) + .unwrap(); - let mut canvas = graphics.window.into_canvas().accelerated().build().unwrap(); - canvas.clear(); - canvas.present(); + // allocates space for the loop ticks counter to be used in each + // iteration cycle + let mut counter = 0u32; + + // the main loop to execute the multiple machine clocks + 'main: loop { + // increments the counter that will keep track + // on the number of visual ticks since beginning + counter = counter.wrapping_add(1); + + // obtains an event from the SDL sub-system to be + // processed under the current emulation context + while let Some(event) = self.graphics.event_pump.poll_event() { + match event { + Event::Quit { .. } => break 'main, + + Event::KeyDown { + keycode: Some(keycode), + .. + } => match key_to_pad(keycode) { + Some(key) => self.system.key_press(key), + None => (), + }, + + Event::KeyUp { + keycode: Some(keycode), + .. + } => match key_to_pad(keycode) { + Some(key) => self.system.key_lift(key), + None => (), + }, + + _ => (), + } + } - let texture_creator = canvas.texture_creator(); + let current_time = self.graphics.timer_subsystem.ticks(); + + let mut counter_ticks = 0u32; + + if current_time >= self.next_tick_time_i { + loop { + // limits the number of ticks to the typical number + // of ticks required to do a complete PPU draw + if counter_ticks >= 70224 { + break; + } + + // runs the Game Boy clock, this operations should + // include the advance of both the CPU and the PPU + counter_ticks += self.system.clock() as u32; + } + + // obtains the frame buffer of the Game Boy PPU and uses it + // to update the stream texture, copying it then to the canvas + let frame_buffer = self.system.frame_buffer().as_ref(); + texture + .update(None, frame_buffer, DISPLAY_WIDTH as usize * 3) + .unwrap(); + self.graphics.canvas.copy(&texture, None, None).unwrap(); + + // presents the canvas effectively updating the screen + // information presented to the user + self.graphics.canvas.present(); + + // updates the next update time reference to the current + // time so that it can be used from game loop control + self.next_tick_time += 1000.0 / self.visual_frequency as f32; + self.next_tick_time_i = self.next_tick_time.ceil() as u32; + } - // creates the texture streaming that is going to be used - // as the target for the pixel buffer - let mut texture = texture_creator - .create_texture_streaming( - PixelFormatEnum::RGB24, - DISPLAY_WIDTH as u32, - DISPLAY_HEIGHT as u32, - ) - .unwrap(); + let current_time = self.graphics.timer_subsystem.ticks(); + let pending_time = self.next_tick_time_i.saturating_sub(current_time); + self.graphics.timer_subsystem.delay(pending_time); + } + } +} +fn main() { // creates a new Game Boy instance and loads both the boot ROM // and the initial game ROM to "start the engine" let mut game_boy = GameBoy::new(); game_boy.load_boot_default(); - //game_boy.load_rom_file("../../res/roms.prop/tetris.gb"); - //game_boy.load_rom_file("../../res/roms.prop/dr_mario.gb"); - //game_boy.load_rom_file("../../res/roms.prop/alleyway.gb"); - //game_boy.load_rom_file("../../res/roms.prop/super_mario.gb"); + //let rom = game_boy.load_rom_file("../../res/roms.prop/tetris.gb"); + //let rom = game_boy.load_rom_file("../../res/roms.prop/dr_mario.gb"); + //let rom = game_boy.load_rom_file("../../res/roms.prop/alleyway.gb"); + let rom = game_boy.load_rom_file("../../res/roms.prop/super_mario.gb"); //let rom = game_boy.load_rom_file("../../res/roms.prop/super_mario_2.gb"); - //game_boy.load_rom_file("../../res/roms/firstwhite.gb"); - //game_boy.load_rom_file("../../res/roms/opus5.gb"); + //let rom = game_boy.load_rom_file("../../res/roms/firstwhite.gb"); + //let rom = game_boy.load_rom_file("../../res/roms/opus5.gb"); //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/cpu_instrs.gb"); // CRASHED //let rom = game_boy.load_rom_file("../../res/roms/paradius/interrupt_time/interrupt_time.gb"); // FAILED - let rom = game_boy.load_rom_file("../../res/roms/paradius/instr_timing/instr_timing.gb"); // FAILED + //let rom = game_boy.load_rom_file("../../res/roms/paradius/instr_timing/instr_timing.gb"); // FAILED + //let rom = game_boy.load_rom_file("../../res/roms/paradius/mem_timing/mem_timing.gb"); // NO FINISH //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/01-special.gb"); // PASSED //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/02-interrupts.gb"); // PASSED //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/03-op sp,hl.gb"); // PASSED @@ -115,73 +171,12 @@ fn main() { //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/07-jr,jp,call,ret,rst.gb"); // PASSED //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/08-misc instrs.gb"); // PASSED //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/09-op r,r.gb"); // PASSED - //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/10-bit ops.gb"); // - //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/11-op a,(hl).gb"); //let rom PASSED - + //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/10-bit ops.gb"); // PASSED + //let rom = game_boy.load_rom_file("../../res/roms/paradius/cpu/11-op a,(hl).gb"); // PASSED println!("==== Cartridge ====\n{}\n===================", rom); - let mut counter = 0u32; - - 'main: loop { - // increments the counter that will keep track - // on the number of visual ticks since beginning - counter = counter.wrapping_add(1); - - // obtains an event from the SDL sub-system to be - // processed under the current emulation context - while let Some(event) = graphics.event_pump.poll_event() { - match event { - Event::Quit { .. } => break 'main, - - Event::KeyDown { - keycode: Some(keycode), - .. - } => match key_to_pad(keycode) { - Some(key) => game_boy.key_press(key), - None => (), - }, - - Event::KeyUp { - keycode: Some(keycode), - .. - } => match key_to_pad(keycode) { - Some(key) => game_boy.key_lift(key), - None => (), - }, - - _ => (), - } - } - - let mut counter_ticks = 0u32; - - loop { - // limits the number of ticks to the typical number - // of ticks required to do a complete PPU draw - if counter_ticks >= 70224 { - break; - } - - // runs the Game Boy clock, this operations should - // include the advance of both the CPU and the PPU - counter_ticks += game_boy.clock() as u32; - } - - // obtains the frame buffer of the Game Boy PPU and uses it - // to update the stream texture, copying it then to the canvas - let frame_buffer = game_boy.frame_buffer().as_ref(); - texture - .update(None, frame_buffer, DISPLAY_WIDTH as usize * 3) - .unwrap(); - canvas.copy(&texture, None, None).unwrap(); - - // presents the canvas effectively updating the screen - // information presented to the user - canvas.present(); - - // @todo this must be improved with proper timestamps - graphics.timer_subsystem.delay(17); - } + let mut emulator = Emulator::new(game_boy, SCREEN_SCALE); + emulator.run(); } fn key_to_pad(keycode: Keycode) -> Option<PadKey> { diff --git a/examples/sdl/src/util.rs b/examples/sdl/src/util.rs new file mode 100644 index 00000000..2f613a9d --- /dev/null +++ b/examples/sdl/src/util.rs @@ -0,0 +1,72 @@ +use sdl2::{ + render::Canvas, rwops::RWops, surface::Surface, sys::image, ttf::Sdl2TtfContext, video::Window, + AudioSubsystem, EventPump, TimerSubsystem, VideoSubsystem, +}; + +pub struct Graphics { + pub canvas: Canvas<Window>, + pub video_subsystem: VideoSubsystem, + pub timer_subsystem: TimerSubsystem, + pub audio_subsystem: AudioSubsystem, + pub event_pump: EventPump, + pub ttf_context: Sdl2TtfContext, +} + +impl Graphics { + /// Start the SDL sub-system and all of its structure and returns + /// a structure with all the needed stuff to handle SDL graphics + /// and sound. + pub fn new(title: &str, width: u32, height: u32, scale: f32) -> Self { + // initializes the SDL sub-system, making it ready to be + // used for display of graphics and audio + let sdl = sdl2::init().unwrap(); + let video_subsystem = sdl.video().unwrap(); + let timer_subsystem = sdl.timer().unwrap(); + let audio_subsystem = sdl.audio().unwrap(); + let event_pump = sdl.event_pump().unwrap(); + + // initialized the fonts context to be used + // in the loading of fonts + let ttf_context = sdl2::ttf::init().unwrap(); + + // creates the system window that is going to be used to + // show the emulator and sets it to the central are o screen + let window = video_subsystem + .window(title, scale as u32 * width, scale as u32 * height) + .resizable() + .position_centered() + .opengl() + .build() + .unwrap(); + + // creates an accelerated canvas to be used in the drawing + // then clears it so that is can be presented empty initially + let mut canvas = window.into_canvas().accelerated().build().unwrap(); + canvas.clear(); + + Self { + canvas: canvas, + video_subsystem: video_subsystem, + timer_subsystem: timer_subsystem, + audio_subsystem: audio_subsystem, + event_pump: event_pump, + ttf_context: ttf_context, + } + } + + pub fn window(&self) -> &Window { + self.canvas.window() + } + + pub fn window_mut(&mut self) -> &mut Window { + self.canvas.window_mut() + } +} + +pub fn surface_from_bytes(bytes: &[u8]) -> Surface { + unsafe { + let rw_ops = RWops::from_bytes(bytes).unwrap(); + let raw_surface = image::IMG_Load_RW(rw_ops.raw(), 0); + Surface::from_ll(raw_surface) + } +} diff --git a/res/roms/paradius/mem_timing/mem_timing.gb b/res/roms/paradius/mem_timing/mem_timing.gb new file mode 100644 index 0000000000000000000000000000000000000000..78766b579f74433332c546c9cf06f8b972a4c2d8 GIT binary patch literal 65536 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 Q0;3@?8UmvsFa$#Y0RA)CQvd(} literal 0 HcmV?d00001 diff --git a/src/rom.rs b/src/rom.rs index 4eef24f6..86c89671 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -349,10 +349,12 @@ pub static MBC1: Mbc = Mbc { rom.set_rom_bank(rom_bank); } 0x4000 | 0x5000 => { - println!("SETTING UPPER BITS {}", value); + let ram_bank = value & 0x03; + rom.set_ram_bank(ram_bank); } + // ROM mode selection 0x6000 | 0x7000 => { - println!("SETTING MODE {}", value); + debugln!("SETTING MODE {}", value); } _ => panic!("Writing to unknown Cartridge ROM location 0x{:04x}", addr), } -- GitLab