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 {