diff --git a/frontends/libretro/src/lib.rs b/frontends/libretro/src/lib.rs index 5f7d582832906815c172fd439c31ab9add8c0284..d71cfaf4c66f85404b27395c8c8514d63ae6b471 100644 --- a/frontends/libretro/src/lib.rs +++ b/frontends/libretro/src/lib.rs @@ -6,16 +6,17 @@ use boytacean::{ debugln, gb::{AudioProvider, GameBoy}, pad::PadKey, - ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_RGB155_SIZE, RGB1555_SIZE}, + ppu::{DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_RGB565_SIZE, RGB565_SIZE}, rom::Cartridge, }; use consts::{ - RETRO_DEVICE_ID_JOYPAD_A, RETRO_DEVICE_ID_JOYPAD_B, RETRO_DEVICE_ID_JOYPAD_DOWN, - RETRO_DEVICE_ID_JOYPAD_L, RETRO_DEVICE_ID_JOYPAD_L2, RETRO_DEVICE_ID_JOYPAD_L3, - RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_R, RETRO_DEVICE_ID_JOYPAD_R2, - RETRO_DEVICE_ID_JOYPAD_R3, RETRO_DEVICE_ID_JOYPAD_RIGHT, RETRO_DEVICE_ID_JOYPAD_SELECT, - RETRO_DEVICE_ID_JOYPAD_START, RETRO_DEVICE_ID_JOYPAD_UP, RETRO_DEVICE_ID_JOYPAD_X, - RETRO_DEVICE_ID_JOYPAD_Y, RETRO_DEVICE_JOYPAD, + REGION_NTSC, RETRO_API_VERSION, RETRO_DEVICE_ID_JOYPAD_A, RETRO_DEVICE_ID_JOYPAD_B, + RETRO_DEVICE_ID_JOYPAD_DOWN, RETRO_DEVICE_ID_JOYPAD_L, RETRO_DEVICE_ID_JOYPAD_L2, + RETRO_DEVICE_ID_JOYPAD_L3, RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_R, + RETRO_DEVICE_ID_JOYPAD_R2, RETRO_DEVICE_ID_JOYPAD_R3, RETRO_DEVICE_ID_JOYPAD_RIGHT, + RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_DEVICE_ID_JOYPAD_START, RETRO_DEVICE_ID_JOYPAD_UP, + RETRO_DEVICE_ID_JOYPAD_X, RETRO_DEVICE_ID_JOYPAD_Y, RETRO_DEVICE_JOYPAD, + RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, RETRO_PIXEL_FORMAT_RGB565, }; use std::{ collections::HashMap, @@ -25,11 +26,9 @@ use std::{ slice::from_raw_parts, }; -use crate::consts::{REGION_NTSC, RETRO_API_VERSION}; - static mut EMULATOR: Option<GameBoy> = None; static mut KEY_STATES: Option<HashMap<RetroJoypad, bool>> = None; -static mut FRAME_BUFFER: [u8; FRAME_BUFFER_RGB155_SIZE] = [0x00; FRAME_BUFFER_RGB155_SIZE]; +static mut FRAME_BUFFER: [u8; FRAME_BUFFER_RGB565_SIZE] = [0x00; FRAME_BUFFER_RGB565_SIZE]; static mut AUDIO_BUFFER: Option<Vec<i16>> = None; static mut PENDING_CYCLES: u32 = 0_u32; @@ -198,6 +197,12 @@ pub unsafe extern "C" fn retro_get_system_av_info(info: *mut RetroSystemAvInfo) (*info).geometry.aspect_ratio = DISPLAY_WIDTH as f32 / DISPLAY_HEIGHT as f32; (*info).timing.fps = GameBoy::VISUAL_FREQ as f64; (*info).timing.sample_rate = EMULATOR.as_ref().unwrap().audio_sampling_rate() as f64; + + let environment_cb = unsafe { ENVIRONMENT_CALLBACK.as_ref().unwrap() }; + environment_cb( + RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, + &RETRO_PIXEL_FORMAT_RGB565 as *const _ as *const c_void, + ); } #[no_mangle] @@ -249,14 +254,14 @@ pub extern "C" fn retro_run() { // in case a new frame is available in the emulator // then the frame must be pushed into display if emulator.ppu_frame() != last_frame { - let frame_buffer = emulator.frame_buffer_rgb1555(); + let frame_buffer = emulator.frame_buffer_rgb565(); unsafe { FRAME_BUFFER.copy_from_slice(&frame_buffer); video_refresh_cb( FRAME_BUFFER.as_ptr(), DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32, - DISPLAY_WIDTH * RGB1555_SIZE, + DISPLAY_WIDTH * RGB565_SIZE, ); } diff --git a/src/gb.rs b/src/gb.rs index 6627cdee6680bdc44e18888c2ebb09d7835b4fe9..d6b05e18bd410ae0e0f5e6e911ce30be93bfcfb1 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -16,8 +16,8 @@ use crate::{ mmu::Mmu, pad::{Pad, PadKey}, ppu::{ - Ppu, PpuMode, Tile, DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_RGB155_SIZE, - FRAME_BUFFER_SIZE, + Ppu, PpuMode, Tile, DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_RGB1555_SIZE, + FRAME_BUFFER_RGB565_SIZE, FRAME_BUFFER_SIZE, }, rom::{Cartridge, RamSize}, serial::{NullDevice, Serial, SerialDevice}, @@ -962,10 +962,14 @@ impl GameBoy { &(self.ppu().frame_buffer) } - pub fn frame_buffer_rgb1555(&mut self) -> [u8; FRAME_BUFFER_RGB155_SIZE] { + pub fn frame_buffer_rgb1555(&mut self) -> [u8; FRAME_BUFFER_RGB1555_SIZE] { self.ppu().frame_buffer_rgb1555() } + pub fn frame_buffer_rgb565(&mut self) -> [u8; FRAME_BUFFER_RGB565_SIZE] { + self.ppu().frame_buffer_rgb565() + } + pub fn audio_buffer(&mut self) -> &VecDeque<u8> { self.apu().audio_buffer() } diff --git a/src/ppu.rs b/src/ppu.rs index 587d93fdd084bca442168fa02134b7e4fdf7ef79..18e768540f656c07911507433246747fd074e862 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -24,6 +24,7 @@ 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 RGB565_SIZE: usize = 2; pub const TILE_WIDTH: usize = 8; pub const TILE_HEIGHT: usize = 8; pub const TILE_WIDTH_I: usize = 7; @@ -59,7 +60,10 @@ pub const COLOR_BUFFER_SIZE: usize = DISPLAY_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; +pub const FRAME_BUFFER_RGB1555_SIZE: usize = DISPLAY_SIZE * RGB1555_SIZE; + +/// The size of the RGB565 frame buffer in bytes. +pub const FRAME_BUFFER_RGB565_SIZE: usize = DISPLAY_SIZE * RGB565_SIZE; /// The base colors to be used to populate the /// custom palettes of the Game Boy. @@ -85,6 +89,10 @@ pub type PixelAlpha = [u8; RGBA_SIZE]; /// bit at the beginning. pub type PixelRgb1555 = [u8; RGB1555_SIZE]; +/// Defines a pixel with 5 bits per channel except for the +/// green channel which uses 6 bits. +pub type PixelRgb565 = [u8; RGB565_SIZE]; + /// Defines a type that represents a color palette /// within the Game Boy context. pub type Palette = [Pixel; PALETTE_SIZE]; @@ -907,8 +915,8 @@ impl Ppu { } } - pub fn frame_buffer_rgb1555(&self) -> [u8; FRAME_BUFFER_RGB155_SIZE] { - let mut buffer = [0u8; FRAME_BUFFER_RGB155_SIZE]; + pub fn frame_buffer_rgb1555(&self) -> [u8; FRAME_BUFFER_RGB1555_SIZE] { + let mut buffer = [0u8; FRAME_BUFFER_RGB1555_SIZE]; for index in 0..DISPLAY_SIZE { let (r, g, b) = ( self.frame_buffer[index * 3], @@ -922,6 +930,21 @@ impl Ppu { buffer } + pub fn frame_buffer_rgb565(&self) -> [u8; FRAME_BUFFER_RGB565_SIZE] { + let mut buffer = [0u8; FRAME_BUFFER_RGB565_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 rgb565 = Self::rgb888_to_rgb565(r, g, b); + buffer[index * 2] = rgb565[0]; + buffer[index * 2 + 1] = rgb565[1]; + } + buffer + } + pub fn vram(&self) -> &[u8; VRAM_SIZE] { &self.vram } @@ -1745,6 +1768,15 @@ impl Ppu { let pixel = (a << 15) | (r << 10) | (g << 5) | b; [pixel as u8, (pixel >> 8) as u8] } + + fn rgb888_to_rgb565(first: u8, second: u8, third: u8) -> PixelRgb565 { + let r = (first as u16 >> 3) & 0x1f; + let g = (second as u16 >> 2) & 0x3f; + let b = (third as u16 >> 3) & 0x1f; + + let pixel = (r << 11) | (g << 5) | b; + [pixel as u8, (pixel >> 8) as u8] + } } impl Default for Ppu {