diff --git a/frontends/libretro/src/lib.rs b/frontends/libretro/src/lib.rs index 5a29f8310f8934c84382fff5d523312636bb6eb0..cfc457f4d18a1cee92da6540e0466fd9e62995a7 100644 --- a/frontends/libretro/src/lib.rs +++ b/frontends/libretro/src/lib.rs @@ -4,17 +4,25 @@ use std::os::raw::{c_char, c_float, c_uint, c_void}; use boytacean::{ gb::GameBoy, - ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH}, + ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, RGB1555_SIZE}, }; const RETRO_API_VERSION: u32 = 1; const REGION_NTSC: u32 = 0; +//const RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: u32 = 10; + +//const RETRO_PIXEL_FORMAT_0RGB1555: usize = 0; +//const RETRO_PIXEL_FORMAT_XRGB8888: usize = 1; +//const RETRO_PIXEL_FORMAT_RGB565: usize = 2; + //const RETRO_MEMORY_SAVE_RAM: u32 = 0; //const RETRO_MEMORY_SYSTEM_RAM: u32 = 0; +static mut EMULATOR: Option<GameBoy> = None; + static mut ENVIRONMENT_CALLBACK: Option<extern "C" fn(u32, *const c_void) -> bool> = None; -static mut VIDEO_REFRESH_CALLBACK: Option<extern "C" fn(*const u8)> = None; +static mut VIDEO_REFRESH_CALLBACK: Option<extern "C" fn(*const u8, c_uint, c_uint, usize)> = None; static mut AUDIO_SAMPLE_CALLBACK: Option<extern "C" fn(i16, i16)> = None; static mut AUDIO_SAMPLE_BATCH_CALLBACK: Option<extern "C" fn(*const i16, usize)> = None; static mut INPUT_POLL_CALLBACK: Option<extern "C" fn()> = None; @@ -63,6 +71,10 @@ pub struct RetroSystemTiming { #[no_mangle] pub extern "C" fn retro_init() { println!("retro_init()"); + unsafe { + EMULATOR = Some(GameBoy::new(None)); + EMULATOR.as_mut().unwrap().load(true); + } } #[no_mangle] @@ -118,7 +130,9 @@ pub extern "C" fn retro_set_environment( } #[no_mangle] -pub extern "C" fn retro_set_video_refresh(callback: Option<extern "C" fn(*const u8)>) { +pub extern "C" fn retro_set_video_refresh( + callback: Option<extern "C" fn(*const u8, c_uint, c_uint, usize)>, +) { println!("retro_set_video_refresh()"); unsafe { VIDEO_REFRESH_CALLBACK = callback; @@ -176,7 +190,35 @@ pub extern "C" fn retro_set_controller_port_device() { #[no_mangle] pub extern "C" fn retro_run() { - println!("retro_run()"); + let emulator = unsafe { EMULATOR.as_mut().unwrap() }; + + let mut counter_cycles = 0_u32; + let cycle_limit = 4194304 / 60; //@TODO this is super tricky + + loop { + // limits the number of ticks to the typical number + // of cycles expected for the current logic cycle + if counter_cycles >= cycle_limit { + //pending_cycles = counter_cycles - cycle_limit; + break; + } + + // runs the Game Boy clock, this operation should + // include the advance of both the CPU, PPU, APU + // and any other frequency based component of the system + counter_cycles += emulator.clock() as u32; + } + + let frame_buffer = emulator.frame_buffer_rgb1555(); + + unsafe { + VIDEO_REFRESH_CALLBACK.unwrap()( + frame_buffer.as_ptr(), + DISPLAY_WIDTH as u32, + DISPLAY_HEIGHT as u32, + DISPLAY_WIDTH * RGB1555_SIZE, + ); + } } #[no_mangle] @@ -186,8 +228,12 @@ pub extern "C" fn retro_get_region() -> u32 { } #[no_mangle] -pub extern "C" fn retro_load_game(_game: *const RetroGameInfo) -> bool { +pub extern "C" fn retro_load_game(game: *const RetroGameInfo) -> bool { println!("retro_load_game()"); + unsafe { + let data_buffer = std::slice::from_raw_parts((*game).data as *const u8, (*game).size); + EMULATOR.as_mut().unwrap().load_rom(data_buffer, None); + } return true; } diff --git a/src/gb.rs b/src/gb.rs index 130d82b3b799dcff69d79bb61b4d0b8d0685fd50..b95a09996b0e936442d87a96ae87d4a30764c55b 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -14,7 +14,10 @@ use crate::{ gen::{COMPILATION_DATE, COMPILATION_TIME, COMPILER, COMPILER_VERSION, VERSION}, mmu::Mmu, pad::{Pad, PadKey}, - ppu::{Ppu, PpuMode, Tile, DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_SIZE}, + ppu::{ + Ppu, PpuMode, Tile, DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_RGB155_SIZE, + FRAME_BUFFER_SIZE, + }, rom::{Cartridge, RamSize}, serial::{NullDevice, Serial, SerialDevice}, timer::Timer, @@ -950,6 +953,10 @@ impl GameBoy { &(self.ppu().frame_buffer) } + pub fn frame_buffer_rgb1555(&mut self) -> [u8; FRAME_BUFFER_RGB155_SIZE] { + self.ppu().frame_buffer_rgb1555() + } + pub fn audio_buffer(&mut self) -> &VecDeque<u8> { self.apu().audio_buffer() } diff --git a/src/ppu.rs b/src/ppu.rs index 34823467f510bc1ae69fb85b7c33874516bcd5da..e711b2abda4e8472de1b3c6e4e045b72787475ae 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -23,6 +23,7 @@ pub const OAM_SIZE: usize = 260; pub const PALETTE_SIZE: usize = 4; pub const RGB_SIZE: usize = 3; pub const RGBA_SIZE: usize = 4; +pub const RGB1555_SIZE: usize = 2; pub const TILE_WIDTH: usize = 8; pub const TILE_HEIGHT: usize = 8; pub const TILE_WIDTH_I: usize = 7; @@ -46,13 +47,19 @@ pub const DISPLAY_WIDTH: usize = 160; /// The height of the Game Boy screen in pixels. pub const DISPLAY_HEIGHT: usize = 144; +/// The size in pixels of the display. +pub const DISPLAY_SIZE: usize = DISPLAY_WIDTH * DISPLAY_HEIGHT; + /// The size to be used by the buffer of colors /// for the Game Boy screen the values there should /// range from 0 to 3. -pub const COLOR_BUFFER_SIZE: usize = DISPLAY_WIDTH * DISPLAY_HEIGHT; +pub const COLOR_BUFFER_SIZE: usize = DISPLAY_SIZE; /// The size of the RGB frame buffer in bytes. -pub const FRAME_BUFFER_SIZE: usize = DISPLAY_WIDTH * DISPLAY_HEIGHT * RGB_SIZE; +pub const FRAME_BUFFER_SIZE: usize = DISPLAY_SIZE * RGB_SIZE; + +/// The size of the RGB1555 frame buffer in bytes. +pub const FRAME_BUFFER_RGB155_SIZE: usize = DISPLAY_SIZE * RGB1555_SIZE; /// The base colors to be used to populate the /// custom palettes of the Game Boy. @@ -74,6 +81,10 @@ pub type Pixel = [u8; RGB_SIZE]; /// with the size of RGBA (4 bytes). pub type PixelAlpha = [u8; RGBA_SIZE]; +/// Defines a pixel with 5 bits per channel plus a padding +/// bit at the beginning. +pub type PixelRgb1555 = [u8; RGB1555_SIZE]; + /// Defines a type that represents a color palette /// within the Game Boy context. pub type Palette = [Pixel; PALETTE_SIZE]; @@ -896,6 +907,21 @@ impl Ppu { } } + pub fn frame_buffer_rgb1555(&self) -> [u8; FRAME_BUFFER_RGB155_SIZE] { + let mut buffer = [0u8; FRAME_BUFFER_RGB155_SIZE]; + for index in 0..DISPLAY_SIZE { + let (r, g, b) = ( + self.frame_buffer[index * 3], + self.frame_buffer[index * 3 + 1], + self.frame_buffer[index * 3 + 2], + ); + let rgb1555 = Self::rgb888_to_rgb1555(r, g, b); + buffer[index * 2] = rgb1555[0]; + buffer[index * 2 + 1] = rgb1555[1]; + } + buffer + } + pub fn vram(&self) -> &[u8; VRAM_SIZE] { &self.vram } @@ -1709,6 +1735,16 @@ impl Ppu { let b = ((second & 0x7c) >> 2) << 3; [r, g, b] } + + fn rgb888_to_rgb1555(first: u8, second: u8, third: u8) -> PixelRgb1555 { + let r = (first as u16 >> 3) & 0x1F; + let g = (second as u16 >> 3) & 0x1F; + let b = (third as u16 >> 3) & 0x1F; + let a = 1; + + let pixel = (a << 15) | (b << 10) | (g << 5) | r; + [pixel as u8, (pixel >> 8) as u8] + } } impl Default for Ppu {