diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs index 066517937fb454254b7d3734afcb4d590ad99a9b..846e1f28ce7571664ed6e4ab7cc601bfe40ee844 100644 --- a/examples/sdl/src/main.rs +++ b/examples/sdl/src/main.rs @@ -43,7 +43,10 @@ impl Emulator { pub fn load_rom(&mut self, path: &str) { let rom = self.system.load_rom_file(path); - println!("==== Cartridge ====\n{}\n===================", rom); + println!( + "========= Cartridge =========\n{}\n=============================\n", + rom + ); } pub fn run(&mut self) { diff --git a/examples/web/index.ts b/examples/web/index.ts index f2e4f018d27232c0e5d8d9a61889cd8809915a3f..b1a010421d4979170b91b566c668980a9aed9cea 100644 --- a/examples/web/index.ts +++ b/examples/web/index.ts @@ -1,4 +1,4 @@ -import { default as wasm, GameBoy, PadKey } from "./lib/boytacean.js"; +import { default as wasm, GameBoy, PadKey, PpuMode } from "./lib/boytacean.js"; import info from "./package.json"; const PIXEL_UNSET_COLOR = 0x1b1a17ff; @@ -41,7 +41,7 @@ const KEYS: Record<string, number> = { }; // @ts-ignore: ts(2580) -const ROM_PATH = require("../../res/roms/firstwhite.gb"); +const ROM_PATH = require("../../res/roms/20y.gb"); // Enumeration that describes the multiple pixel // formats and the associated byte size. @@ -217,6 +217,8 @@ const tick = (currentTime: number) => { let counterTicks = 0; + let lastFrame = -1; + while (true) { // limits the number of ticks to the typical number // of ticks required to do a complete PPU draw @@ -227,11 +229,20 @@ const tick = (currentTime: number) => { // runs the Game Boy clock, this operations should // include the advance of both the CPU and the PPU counterTicks += state.gameBoy.clock(); - } - // updates the canvas object with the new - // visual information coming in - updateCanvas(state.gameBoy.frame_buffer_eager(), PixelFormat.RGB); + // in case the current PPU mode is VBlank and the + // fram is different from the previously rendered + // one then it's time to update the canvas + if ( + state.gameBoy.ppu_mode() == PpuMode.VBlank && + state.gameBoy.ppu_frame() != lastFrame + ) { + // updates the canvas object with the new + // visual information coming in + updateCanvas(state.gameBoy.frame_buffer_eager(), PixelFormat.RGB); + lastFrame = state.gameBoy.ppu_frame(); + } + } // increments the number of frames rendered in the current // section, this value is going to be used to calculate FPS @@ -544,7 +555,7 @@ const registerButtons = () => { /** * Draws the tile at the given index to the proper * vertical offset in the given context and buffer. - * + * * @param index The index of the sprite to be drawn. * @param format The pixel format of the sprite. */ @@ -552,7 +563,7 @@ const registerButtons = () => { index: number, context: CanvasRenderingContext2D, buffer: DataView, - format: PixelFormat = PixelFormat.RGB, + format: PixelFormat = PixelFormat.RGB ) => { const pixels = state.gameBoy.get_tile_buffer(index); const line = Math.floor(index / 16); diff --git a/res/roms/20y.gb b/res/roms/20y.gb new file mode 100644 index 0000000000000000000000000000000000000000..139c684a227553640090a1db0dc765384be07f61 Binary files /dev/null and b/res/roms/20y.gb differ diff --git a/src/gb.rs b/src/gb.rs index 954ecc3798a105c42a61d63291d1ee4dec8e6e8a..7ab861d62f79b26653aae71f579947c68c3ea11d 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -3,7 +3,7 @@ use crate::{ data::{BootRom, DMG_BOOT, DMG_BOOTIX, MGB_BOOTIX, SGB_BOOT}, mmu::Mmu, pad::{Pad, PadKey}, - ppu::{Ppu, Tile, FRAME_BUFFER_SIZE}, + ppu::{Ppu, PpuMode, Tile, FRAME_BUFFER_SIZE}, rom::Cartridge, timer::Timer, util::read_file, @@ -50,6 +50,18 @@ impl GameBoy { self.cpu.pc() } + pub fn ppu_ly(&mut self) -> u8 { + self.ppu().ly() + } + + pub fn ppu_mode(&mut self) -> PpuMode { + self.ppu().mode() + } + + pub fn ppu_frame(&mut self) -> u16 { + self.ppu().frame_index() + } + pub fn clock(&mut self) -> u8 { let cycles = self.cpu_clock(); self.ppu_clock(cycles); diff --git a/src/ppu.rs b/src/ppu.rs index d39e0d4963d6067bbd2bc88b8601459f90c25ac5..1b051ed176221667729f900eed3a2641e25aed7d 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -242,6 +242,11 @@ pub struct Ppu { /// first one, preventing actions. first_frame: bool, + /// Almost unique identifier of the frame that can be used to debug + /// and uniquely identify the frame that is currently ind drawing, + /// the identifier wraps on the u16 edges. + frame_index: u16, + stat_hblank: bool, stat_vblank: bool, stat_oam: bool, @@ -256,6 +261,7 @@ pub struct Ppu { int_stat: bool, } +#[cfg_attr(feature = "wasm", wasm_bindgen)] #[derive(Clone, Copy, PartialEq)] pub enum PpuMode { HBlank = 0, @@ -304,6 +310,7 @@ impl Ppu { window_map: false, switch_lcd: false, first_frame: false, + frame_index: 0, stat_hblank: false, stat_vblank: false, stat_oam: false, @@ -338,6 +345,7 @@ impl Ppu { self.window_map = false; self.switch_lcd = false; self.first_frame = false; + self.frame_index = 0; self.stat_hblank = false; self.stat_vblank = false; self.stat_oam = false; @@ -404,6 +412,7 @@ impl Ppu { self.mode = PpuMode::OamRead; self.ly = 0; self.first_frame = false; + self.frame_index = self.frame_index.wrapping_add(1); } self.mode_clock -= 456; @@ -556,6 +565,18 @@ impl Ppu { self.palette_obj_1 } + pub fn ly(&self) -> u8 { + self.ly + } + + pub fn mode(&self) -> PpuMode { + self.mode + } + + pub fn frame_index(&self) -> u16 { + self.frame_index + } + pub fn int_vblank(&self) -> bool { self.int_vblank }