diff --git a/bsnes/Makefile b/bsnes/Makefile
index 7ad84c06..beb37c5c 100755
--- a/bsnes/Makefile
+++ b/bsnes/Makefile
@@ -75,6 +75,6 @@ clean:
 	-@$(call delete,*.manifest)
 
 archive-all:
-	tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-gameboy ui-libsnes ui-snes Makefile cc.bat clean.bat sync.sh
+	tar -cjf bsnes.tar.bz2 data gameboy libco nall nes obj out phoenix ruby snes ui ui-libsnes ui-snes Makefile cc.bat clean.bat sync.sh
 
 help:;
diff --git a/bsnes/nes/interface/interface.hpp b/bsnes/nes/interface/interface.hpp
index 2e22c488..4618bb57 100755
--- a/bsnes/nes/interface/interface.hpp
+++ b/bsnes/nes/interface/interface.hpp
@@ -1,5 +1,5 @@
 struct Interface {
-  virtual void video_refresh(const uint32_t *data) {}
+  virtual void video_refresh(const uint16_t *data) {}
   virtual void audio_sample(int16_t sample) {}
   virtual int16_t input_poll(bool port, unsigned device, unsigned id) { return 0; }
 
diff --git a/bsnes/nes/nes.hpp b/bsnes/nes/nes.hpp
index 727476f1..565f6d9c 100755
--- a/bsnes/nes/nes.hpp
+++ b/bsnes/nes/nes.hpp
@@ -4,7 +4,7 @@
 namespace NES {
   namespace Info {
     static const char Name[] = "bnes";
-    static const char Version[] = "000.06";
+    static const char Version[] = "000.07";
   }
 }
 
diff --git a/bsnes/nes/ppu/ppu.cpp b/bsnes/nes/ppu/ppu.cpp
index 6dfc2da4..83ad9663 100755
--- a/bsnes/nes/ppu/ppu.cpp
+++ b/bsnes/nes/ppu/ppu.cpp
@@ -38,25 +38,6 @@ void PPU::frame_edge() {
 }
 
 void PPU::power() {
-  paletteRGB = {
-    0x7c7c7c, 0x0000fc, 0x0000bc, 0x4428bc,
-    0x940084, 0xa80020, 0xa81000, 0x881400,
-    0x503000, 0x007800, 0x006800, 0x005800,
-    0x004058, 0x000000, 0x000000, 0x000000,
-    0xbcbcbc, 0x0078f8, 0x0058f8, 0x6844fc,
-    0xd800cc, 0xe40058, 0xf83800, 0xe45c10,
-    0xac7c00, 0x00b800, 0x00a800, 0x00a844,
-    0x008888, 0x000000, 0x000000, 0x000000,
-    0xf8f8f8, 0x3cbcfc, 0x6888fc, 0x9878f8,
-    0xf878f8, 0xf85898, 0xf87858, 0xfca044,
-    0xf8b800, 0xb8f818, 0x58d854, 0x58f898,
-    0x00e8d8, 0x787878, 0x000000, 0x000000,
-    0xfcfcfc, 0xa4e4fc, 0xb8b8b8, 0xd8d8f8,
-    0xf8b8f8, 0xf8a4c0, 0xf0d0b0, 0xfce0a8,
-    0xf8d878, 0xd8f878, 0xb8f8b8, 0xb8f8d8,
-    0x00fcfc, 0xf8d8f8, 0x000000, 0x000000,
-  };
-
   reset();
 }
 
@@ -82,9 +63,7 @@ void PPU::reset() {
   status.vram_increment = 1;
 
   //$2001
-  status.intensify_blue = false;
-  status.intensify_green = false;
-  status.intensify_red = false;
+  status.emphasis = 0;
   status.sprite_enable = false;
   status.bg_enable = false;
   status.sprite_edge_enable = false;
@@ -132,7 +111,8 @@ uint8 PPU::read(uint16 addr) {
       result = status.bus_data;
       status.bus_data = cartridge.ciram_read(addr);
     } else if(addr <= 0x3fff) {
-      result = status.bus_data = cgram_read(addr);
+      result = cgram_read(addr);
+      status.bus_data = cartridge.ciram_read(addr);
     }
     status.vaddr += status.vram_increment;
     break;
@@ -156,9 +136,7 @@ void PPU::write(uint16 addr, uint8 data) {
     status.taddr = (status.taddr & 0x73ff) | ((data & 0x03) << 10);
     return;
   case 1:  //PPUMASK
-    status.intensify_blue = data & 0x80;
-    status.intensify_green = data & 0x40;
-    status.intensify_red = data & 0x20;
+    status.emphasis = data >> 5;
     status.sprite_enable = data & 0x10;
     status.bg_enable = data & 0x08;
     status.sprite_edge_enable = data & 0x04;
@@ -215,7 +193,9 @@ void PPU::ciram_write(uint16 addr, uint8 data) {
 
 uint8 PPU::cgram_read(uint16 addr) {
   if((addr & 0x13) == 0x10) addr &= ~0x10;
-  return cgram[addr & 0x1f];
+  uint8 data = cgram[addr & 0x1f];
+  if(status.grayscale) data &= 0x30;
+  return data;
 }
 
 void PPU::cgram_write(uint16 addr, uint8 data) {
@@ -233,7 +213,7 @@ void PPU::cgram_write(uint16 addr, uint8 data) {
 //XXXXX = X nametable (x:d3-d7)
 
 bool PPU::raster_enable() const {
-  return status.bg_enable || status.sprite_enable;
+  return (status.bg_enable || status.sprite_enable);
 }
 
 unsigned PPU::nametable_addr() const {
@@ -248,6 +228,22 @@ unsigned PPU::scrolly() const {
   return (((status.vaddr >> 5) & 0x1f) << 3) | ((status.vaddr >> 12) & 7);
 }
 
+unsigned PPU::sprite_height() const {
+  return status.sprite_size == 0 ? 8 : 16;
+}
+
+//
+
+uint8 PPU::chr_load(uint16 addr) {
+  if(raster_enable() == false) return 0x00;
+  return cartridge.chr_read(addr);
+}
+
+uint8 PPU::ciram_load(uint16 addr) {
+  if(raster_enable() == false) return 0x00;
+  return cartridge.ciram_read(addr);
+}
+
 //
 
 void PPU::ly_increment() {
@@ -259,6 +255,7 @@ void PPU::ly_increment() {
 }
 
 void PPU::scrollx_increment() {
+  if(raster_enable() == false) return;
   status.vaddr = (status.vaddr & 0x7fe0) | ((status.vaddr + 0x0001) & 0x001f);
   if((status.vaddr & 0x001f) == 0x0000) {
     status.vaddr ^= 0x0400;
@@ -266,6 +263,7 @@ void PPU::scrollx_increment() {
 }
 
 void PPU::scrolly_increment() {
+  if(raster_enable() == false) return;
   status.vaddr = (status.vaddr & 0x0fff) | ((status.vaddr + 0x1000) & 0x7000);
   if((status.vaddr & 0x7000) == 0x0000) {
     status.vaddr = (status.vaddr & 0x7c1f) | ((status.vaddr + 0x0020) & 0x03e0);
@@ -279,10 +277,11 @@ void PPU::scrolly_increment() {
 //
 
 void PPU::raster_pixel(unsigned x) {
-  uint32 *output = buffer + status.ly * 256;
+  uint16 *output = buffer + status.ly * 256;
 
   unsigned mask = 0x8000 >> (status.xaddr + x);
-  unsigned palette = 0;
+  unsigned palette = 0, object_palette = 0;
+  bool object_priority = 0;
   palette |= (raster.tiledatalo & mask) ? 1 : 0;
   palette |= (raster.tiledatahi & mask) ? 2 : 0;
   if(palette) {
@@ -293,7 +292,7 @@ void PPU::raster_pixel(unsigned x) {
 
   if(status.bg_edge_enable == false && status.lx < 8) palette = 0;
 
-  for(unsigned sprite = 0; sprite < 8; sprite++) {
+  for(signed sprite = 7; sprite >= 0; sprite--) {
     if(status.sprite_edge_enable == false && status.lx < 8) continue;
     if(raster.oam[sprite].id == 64) continue;
 
@@ -310,14 +309,37 @@ void PPU::raster_pixel(unsigned x) {
     if(raster.oam[sprite].id == 0 && palette) status.sprite_zero_hit = 1;
     sprite_palette |= (raster.oam[sprite].attr & 3) << 2;
 
-    if((raster.oam[sprite].attr & 0x20) == 0 || palette == 0) {
-      palette = 16 + sprite_palette;
-      break;
-    }
+    object_priority = raster.oam[sprite].attr & 0x20;
+    object_palette = 16 + sprite_palette;
+  }
+
+  if(object_palette) {
+    if(palette == 0 || object_priority == 0) palette = object_palette;
   }
 
   if(raster_enable() == false) palette = 0;
-  output[status.lx++] = paletteRGB[cgram[palette]];
+  output[status.lx++] = (status.emphasis << 6) | cgram_read(palette);
+}
+
+void PPU::raster_sprite() {
+  if(status.sprite_enable == false) return;
+
+  unsigned n = raster.oam_iterator++;
+  signed ly = (status.ly == 261 ? -1 : status.ly);
+  unsigned y = ly - oam[(n * 4) + 0];
+
+  if(y >= sprite_height()) return;
+  if(raster.oam_counter == 8) {
+    status.sprite_overflow = 1;
+    return;
+  }
+
+  raster.soam[raster.oam_counter].id = n;
+  raster.soam[raster.oam_counter].y = y;
+  raster.soam[raster.oam_counter].tile = oam[(n * 4) + 1];
+  raster.soam[raster.oam_counter].attr = oam[(n * 4) + 2];
+  raster.soam[raster.oam_counter].x = oam[(n * 4) + 3];
+  raster.oam_counter++;
 }
 
 void PPU::raster_scanline() {
@@ -327,12 +349,20 @@ void PPU::raster_scanline() {
     return ly_increment();
   }
 
-  uint32 *output = buffer + status.ly * 256;
   signed lx = 0, ly = (status.ly == 261 ? -1 : status.ly);
   status.lx = 0;
 
+  raster.oam_iterator = 0;
+  raster.oam_counter = 0;
+
+  for(unsigned n = 0; n < 8; n++) {
+    raster.soam[n].id = 64;
+    raster.soam[n].tiledatalo = 0;
+    raster.soam[n].tiledatahi = 0;
+  }
+
   for(unsigned tile = 0; tile < 32; tile++) {  //  0-255
-    unsigned nametable = cartridge.ciram_read((uint13)status.vaddr);
+    unsigned nametable = ciram_load(status.vaddr & 0x0fff);
     unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
     raster_pixel(0);
     tick();
@@ -340,31 +370,31 @@ void PPU::raster_scanline() {
     raster_pixel(1);
     tick();
 
-    unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
+    unsigned attribute = ciram_load(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
     if(scrolly() & 16) attribute >>= 4;
     if(scrollx() & 16) attribute >>= 2;
     raster_pixel(2);
     tick();
 
-    if(raster_enable()) {
-      scrollx_increment();
-      if(tile == 31) scrolly_increment();
-    }
+    scrollx_increment();
+    if(tile == 31) scrolly_increment();
     raster_pixel(3);
+    raster_sprite();
     tick();
 
-    unsigned tiledatalo = cartridge.chr_read(tileaddr + 0);
+    unsigned tiledatalo = chr_load(tileaddr + 0);
     raster_pixel(4);
     tick();
 
     raster_pixel(5);
     tick();
 
-    unsigned tiledatahi = cartridge.chr_read(tileaddr + 8);
+    unsigned tiledatahi = chr_load(tileaddr + 8);
     raster_pixel(6);
     tick();
 
     raster_pixel(7);
+    raster_sprite();
     tick();
 
     raster.nametable = (raster.nametable << 8) | nametable;
@@ -373,54 +403,32 @@ void PPU::raster_scanline() {
     raster.tiledatahi = (raster.tiledatahi << 8) | tiledatahi;
   }
 
-  for(unsigned n = 0; n < 8; n++) {
-    raster.oam[n].id = 64;
-    raster.oam[n].tiledatalo = 0;
-    raster.oam[n].tiledatahi = 0;
-  }
-
-  //should occur every 4 cycles during main screen rendering
-  unsigned counter = 0;
-  unsigned sprite_height = status.sprite_size ? 16 : 8;
-  if(status.sprite_enable) for(unsigned n = 0; n < 64; n++) {
-    unsigned y = ly - oam[(n * 4) + 0];
-    if(y >= sprite_height) continue;
-    if(counter == 8) {
-      status.sprite_overflow = 1;
-      break;
-    }
-
-    raster.oam[counter].id = n;
-    raster.oam[counter].y = y;
-    raster.oam[counter].tile = oam[(n * 4) + 1];
-    raster.oam[counter].attr = oam[(n * 4) + 2];
-    raster.oam[counter].x = oam[(n * 4) + 3];
-    counter++;
-  }
+  for(unsigned n = 0; n < 8; n++) raster.oam[n] = raster.soam[n];
 
   for(unsigned sprite = 0; sprite < 8; sprite++) {  //256-319
-    unsigned nametable = cartridge.ciram_read((uint13)status.vaddr);
+    unsigned nametable = ciram_load(status.vaddr & 0x0fff);
     tick();
 
     if(raster_enable() && sprite == 0) status.vaddr = (status.vaddr & 0x7be0) | (status.taddr & 0x041f);  //257
     tick();
 
-    unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
-    unsigned tileaddr = (sprite_height == 8)
+    unsigned attribute = ciram_load(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
+    unsigned tileaddr = (sprite_height() == 8)
     ? status.sprite_addr + raster.oam[sprite].tile * 16
     : ((raster.oam[sprite].tile & ~1) * 16) + ((raster.oam[sprite].tile & 1) * 0x1000);
     tick();
     tick();
 
     unsigned spritey = raster.oam[sprite].y;
-    if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height - 1);
-    if(spritey & 8) spritey += 8;
+    if(raster.oam[sprite].attr & 0x80) spritey ^= (sprite_height() - 1);
+    tileaddr += spritey + (spritey & 8);
+    if(raster.oam[sprite].id == 64) tileaddr = status.sprite_addr;
 
-    raster.oam[sprite].tiledatalo = cartridge.chr_read(tileaddr + spritey + 0);
+    raster.oam[sprite].tiledatalo = chr_load(tileaddr + 0);
     tick();
     tick();
 
-    raster.oam[sprite].tiledatahi = cartridge.chr_read(tileaddr + spritey + 8);
+    raster.oam[sprite].tiledatahi = chr_load(tileaddr + 8);
     tick();
     tick();
 
@@ -428,26 +436,24 @@ void PPU::raster_scanline() {
   }
 
   for(unsigned tile = 0; tile < 2; tile++) {  //320-335
-    unsigned nametable = cartridge.ciram_read((uint13)status.vaddr & 0x1fff);
+    unsigned nametable = ciram_load(status.vaddr & 0x0fff);
     unsigned tileaddr = status.bg_addr + (nametable << 4) + (scrolly() & 7);
     tick();
     tick();
 
-    unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
+    unsigned attribute = ciram_load(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
     if(scrolly() & 16) attribute >>= 4;
     if(scrollx() & 16) attribute >>= 2;
     tick();
 
-    if(raster_enable()) {
-      scrollx_increment();
-    }
+    scrollx_increment();
     tick();
 
-    unsigned tiledatalo = cartridge.chr_read(tileaddr + 0);
+    unsigned tiledatalo = chr_load(tileaddr + 0);
     tick();
     tick();
 
-    unsigned tiledatahi = cartridge.chr_read(tileaddr + 8);
+    unsigned tiledatahi = chr_load(tileaddr + 8);
     tick();
     tick();
 
@@ -458,11 +464,11 @@ void PPU::raster_scanline() {
   }
 
   //336-339
-  unsigned nametable = cartridge.ciram_read((uint13)status.vaddr & 0x1fff);
+  unsigned nametable = ciram_load(status.vaddr & 0x0fff);
   tick();
   tick();
 
-  unsigned attribute = cartridge.ciram_read(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
+  unsigned attribute = ciram_load(0x03c0 | (status.vaddr & 0x0fc0) | ((scrolly() >> 5) << 3) | (scrollx() >> 5));
   tick();
   tick();
 
diff --git a/bsnes/nes/ppu/ppu.hpp b/bsnes/nes/ppu/ppu.hpp
index 55f12433..f16d85e1 100755
--- a/bsnes/nes/ppu/ppu.hpp
+++ b/bsnes/nes/ppu/ppu.hpp
@@ -22,12 +22,17 @@ struct PPU : Processor {
   unsigned nametable_addr() const;
   unsigned scrollx() const;
   unsigned scrolly() const;
+  unsigned sprite_height() const;
+
+  uint8 chr_load(uint16 addr);
+  uint8 ciram_load(uint16 addr);
 
   void ly_increment();
   void scrollx_increment();
   void scrolly_increment();
 
   void raster_pixel(unsigned x);
+  void raster_sprite();
   void raster_scanline();
 
   struct Status {
@@ -43,7 +48,7 @@ struct PPU : Processor {
 
     uint15 vaddr;
     uint15 taddr;
-    uint8  xaddr;
+    uint8 xaddr;
 
     //$2000
     bool nmi_enable;
@@ -54,9 +59,7 @@ struct PPU : Processor {
     unsigned vram_increment;
 
     //$2001
-    bool intensify_blue;
-    bool intensify_green;
-    bool intensify_red;
+    uint3 emphasis;
     bool sprite_enable;
     bool bg_enable;
     bool sprite_edge_enable;
@@ -78,6 +81,9 @@ struct PPU : Processor {
     uint16 tiledatalo;
     uint16 tiledatahi;
 
+    unsigned oam_iterator;
+    unsigned oam_counter;
+
     struct OAM {
       uint8 id;
       uint8 y;
@@ -87,11 +93,10 @@ struct PPU : Processor {
 
       uint8 tiledatalo;
       uint8 tiledatahi;
-    } oam[8];
+    } oam[8], soam[8];
   } raster;
 
-  uint32 buffer[256 * 262];
-  uint32 paletteRGB[64];
+  uint16 buffer[256 * 262];
   uint8 ciram[2048];
   uint8 cgram[32];
   uint8 oam[256];
diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp
index 84bee861..0c3549dc 100755
--- a/bsnes/snes/snes.hpp
+++ b/bsnes/snes/snes.hpp
@@ -4,7 +4,7 @@
 namespace SNES {
   namespace Info {
     static const char Name[] = "bsnes";
-    static const char Version[] = "082.10";
+    static const char Version[] = "082.11";
     static const unsigned SerializerVersion = 21;
   }
 }
diff --git a/bsnes/ui-gameboy/Makefile b/bsnes/ui-gameboy/Makefile
deleted file mode 100755
index 00911327..00000000
--- a/bsnes/ui-gameboy/Makefile
+++ /dev/null
@@ -1,91 +0,0 @@
-include $(gameboy)/Makefile
-
-ui_objects := ui-main ui-utility
-ui_objects += ruby phoenix
-
-# platform
-ifeq ($(platform),x)
-  ifeq ($(phoenix),gtk)
-    phoenix_compile = $(call compile,-DPHOENIX_GTK `pkg-config --cflags gtk+-2.0`)
-    link += `pkg-config --libs gtk+-2.0`
-  else
-    phoenix_compile = $(call compile,-DPHOENIX_QT `pkg-config --cflags QtCore QtGui`)
-    link += `pkg-config --libs QtCore QtGui`
-  endif
-
-  ruby := video.glx video.xv video.sdl
-  ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao
-  ruby += input.sdl input.x
-
-  link += $(if $(findstring audio.openal,$(ruby)),-lopenal)
-else ifeq ($(platform),osx)
-  phoenix_compile = $(call compile,-DPHOENIX_QT)
-  link +=
-
-  ruby :=
-  ruby += audio.openal
-  ruby += input.carbon
-
-  link += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL)
-else ifeq ($(platform),win)
-  phoenix_compile = $(call compile,-DPHOENIX_WINDOWS)
-  link +=
-
-  ruby := video.direct3d video.wgl video.directdraw video.gdi
-  ruby += audio.directsound audio.xaudio2
-  ruby += input.rawinput input.directinput
-
-  link += $(if $(findstring audio.openal,$(ruby)),-lopenal32)
-endif
-
-# ruby
-rubyflags := $(if $(finstring .sdl,$(ruby)),`sdl-config --cflags`)
-
-link += $(if $(findstring .sdl,$(ruby)),`sdl-config --libs`)
-link += $(if $(findstring video.direct3d,$(ruby)),-ld3d9)
-link += $(if $(findstring video.directdraw,$(ruby)),-lddraw)
-link += $(if $(findstring video.glx,$(ruby)),-lGL)
-link += $(if $(findstring video.wgl,$(ruby)),-lopengl32)
-link += $(if $(findstring video.xv,$(ruby)),-lXv)
-link += $(if $(findstring audio.alsa,$(ruby)),-lasound)
-link += $(if $(findstring audio.ao,$(ruby)),-lao)
-link += $(if $(findstring audio.directsound,$(ruby)),-ldsound)
-link += $(if $(findstring audio.pulseaudio,$(ruby)),-lpulse)
-link += $(if $(findstring audio.pulseaudiosimple,$(ruby)),-lpulse-simple)
-link += $(if $(findstring audio.xaudio2,$(ruby)),-lole32)
-link += $(if $(findstring input.directinput,$(ruby)),-ldinput8 -ldxguid)
-link += $(if $(findstring input.rawinput,$(ruby)),-ldinput8 -ldxguid)
-
-rubydef := $(foreach c,$(subst .,_,$(call strupper,$(ruby))),-D$c)
-
-# rules
-objects := $(ui_objects) $(objects)
-objects := $(patsubst %,obj/%.o,$(objects))
-
-obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/*.hpp) $(call rwildcard,$(ui)/*)
-obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/utility/*)
-
-obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
-	$(call compile,$(rubydef) $(rubyflags))
-
-obj/phoenix.o: phoenix/phoenix.cpp $(call rwildcard,phoenix/*)
-	$(phoenix_compile)
-
-# targets
-build: $(objects)
-ifeq ($(platform),osx)
-	test -d ../bgameboy.app || mkdir -p ../bgameboy.app/Contents/MacOS
-	$(strip $(cpp) -o ../bgameboy.app/Contents/MacOS/bgameboy $(objects) $(link))
-else
-	$(strip $(cpp) -o out/bgameboy $(objects) $(link))
-endif
-
-install:
-ifeq ($(platform),x)
-	install -D -m 755 out/bgameboy $(DESTDIR)$(prefix)/bin/bgameboy
-endif
-
-uninstall:
-ifeq ($(platform),x)
-	rm $(DESTDIR)$(prefix)/bin/bgameboy
-endif
diff --git a/bsnes/ui-gameboy/base.hpp b/bsnes/ui-gameboy/base.hpp
deleted file mode 100755
index 840925ff..00000000
--- a/bsnes/ui-gameboy/base.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <nall/dsp.hpp>
-#include <nall/file.hpp>
-#include <nall/foreach.hpp>
-#include <nall/stdint.hpp>
-#include <nall/string.hpp>
-#include <nall/gameboy/cartridge.hpp>
-using namespace nall;
-
-#include <ruby/ruby.hpp>
-using namespace ruby;
-
-#include <phoenix/phoenix.hpp>
-using namespace phoenix;
-
-#include <gameboy/gameboy.hpp>
-
-#include "interface.hpp"
-
-#include "general/main-window.hpp"
-
-#include "utility/utility.hpp"
-
-struct Application {
-  bool quit;
-
-  string proportionalFont;
-  string proportionalFontBold;
-  string monospaceFont;
-
-  void main(int argc, char **argv);
-};
-
-extern nall::DSP dspaudio;
-extern Application application;
diff --git a/bsnes/ui-gameboy/general/main-window.cpp b/bsnes/ui-gameboy/general/main-window.cpp
deleted file mode 100755
index c64ba9a4..00000000
--- a/bsnes/ui-gameboy/general/main-window.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-MainWindow mainWindow;
-
-void MainWindow::create() {
-  setTitle({ GameBoy::Info::Name, " v", GameBoy::Info::Version });
-  setResizable(false);
-  setMenuFont(application.proportionalFont);
-  setWidgetFont(application.proportionalFont);
-  setStatusFont(application.proportionalFontBold);
-
-  system.setText("System");
-  append(system);
-
-  systemLoadCartridge.setText("Load Cartridge ...");
-  system.append(systemLoadCartridge);
-
-  system.append(systemSeparator1);
-
-  systemPower.setText("Power Cycle");
-  system.append(systemPower);
-
-  settings.setText("Settings");
-  append(settings);
-
-  settingsVideoSync.setText("Synchronize Video");
-  settingsVideoSync.setChecked(false);
-  settings.append(settingsVideoSync);
-
-  settingsAudioSync.setText("Synchronize Audio");
-  settingsAudioSync.setChecked(true);
-  settings.append(settingsAudioSync);
-
-  tools.setText("Tools");
-  append(tools);
-
-  toolsSaveState.setText("Save State");
-  tools.append(toolsSaveState);
-
-  toolsSaveState1.setText("Slot 1");
-  toolsSaveState.append(toolsSaveState1);
-
-  toolsSaveState2.setText("Slot 2");
-  toolsSaveState.append(toolsSaveState2);
-
-  toolsSaveState3.setText("Slot 3");
-  toolsSaveState.append(toolsSaveState3);
-
-  toolsSaveState4.setText("Slot 4");
-  toolsSaveState.append(toolsSaveState4);
-
-  toolsSaveState5.setText("Slot 5");
-  toolsSaveState.append(toolsSaveState5);
-
-  toolsLoadState.setText("Load State");
-  tools.append(toolsLoadState);
-
-  toolsLoadState1.setText("Slot 1");
-  toolsLoadState.append(toolsLoadState1);
-
-  toolsLoadState2.setText("Slot 2");
-  toolsLoadState.append(toolsLoadState2);
-
-  toolsLoadState3.setText("Slot 3");
-  toolsLoadState.append(toolsLoadState3);
-
-  toolsLoadState4.setText("Slot 4");
-  toolsLoadState.append(toolsLoadState4);
-
-  toolsLoadState5.setText("Slot 5");
-  toolsLoadState.append(toolsLoadState5);
-
-  tools.append(toolsSeparator1);
-
-  toolsTraceCPU.setText("Trace CPU");
-  tools.append(toolsTraceCPU);
-
-  help.setText("Help");
-  append(help);
-
-  helpAbout.setText("About ...");
-  help.append(helpAbout);
-
-  layout.append(viewport, { 0, 0, 160 * 2, 144 * 2 });
-  append(layout);
-  setGeometry({ 128, 128, 160 * 2, 144 * 2 });
-
-  setMenuVisible(true);
-  setStatusVisible(true);
-
-  onClose = []() {
-    application.quit = true;
-  };
-
-  systemLoadCartridge.onTick = []() {
-    string filename = OS::fileLoad(mainWindow, "/media/sdb1/root/gameboy_images/", "Game Boy cartridges (*.gb,*.gbc)");
-    if(filename != "") utility.loadCartridge(filename);
-  };
-
-  systemPower.onTick = []() {
-    if(GameBoy::cartridge.loaded()) GameBoy::system.power();
-  };
-
-  settingsVideoSync.onTick = []() {
-    video.set(Video::Synchronize, mainWindow.settingsVideoSync.checked());
-  };
-
-  settingsAudioSync.onTick = []() {
-    audio.set(Audio::Synchronize, mainWindow.settingsAudioSync.checked());
-  };
-
-  toolsSaveState1.onTick = []() { utility.saveState(1); };
-  toolsSaveState2.onTick = []() { utility.saveState(2); };
-  toolsSaveState3.onTick = []() { utility.saveState(3); };
-  toolsSaveState4.onTick = []() { utility.saveState(4); };
-  toolsSaveState5.onTick = []() { utility.saveState(5); };
-
-  toolsLoadState1.onTick = []() { utility.loadState(1); };
-  toolsLoadState2.onTick = []() { utility.loadState(2); };
-  toolsLoadState3.onTick = []() { utility.loadState(3); };
-  toolsLoadState4.onTick = []() { utility.loadState(4); };
-  toolsLoadState5.onTick = []() { utility.loadState(5); };
-
-  toolsTraceCPU.onTick = []() {
-    GameBoy::cpu.trace = mainWindow.toolsTraceCPU.checked();
-  };
-
-  helpAbout.onTick = []() {
-    MessageWindow::information(mainWindow, {
-      "bgameboy\n\n",
-      "Version: ", GameBoy::Info::Version, "\n",
-      "Author: byuu\n",
-      "Homepage: http://byuu.org/"
-    });
-  };
-}
diff --git a/bsnes/ui-gameboy/general/main-window.hpp b/bsnes/ui-gameboy/general/main-window.hpp
deleted file mode 100755
index 4c1bd71d..00000000
--- a/bsnes/ui-gameboy/general/main-window.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-struct MainWindow : Window {
-  Menu system;
-  Item systemLoadCartridge;
-  Separator systemSeparator1;
-  Item systemPower;
-
-  Menu settings;
-  CheckItem settingsVideoSync;
-  CheckItem settingsAudioSync;
-
-  Menu tools;
-  Menu toolsSaveState;
-  Item toolsSaveState1;
-  Item toolsSaveState2;
-  Item toolsSaveState3;
-  Item toolsSaveState4;
-  Item toolsSaveState5;
-  Menu toolsLoadState;
-  Item toolsLoadState1;
-  Item toolsLoadState2;
-  Item toolsLoadState3;
-  Item toolsLoadState4;
-  Item toolsLoadState5;
-  Separator toolsSeparator1;
-  CheckItem toolsTraceCPU;
-
-  Menu help;
-  Item helpAbout;
-
-  FixedLayout layout;
-  Viewport viewport;
-
-  void create();
-};
-
-extern MainWindow mainWindow;
diff --git a/bsnes/ui-gameboy/interface.cpp b/bsnes/ui-gameboy/interface.cpp
deleted file mode 100755
index 77c13052..00000000
--- a/bsnes/ui-gameboy/interface.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-Interface interface;
-
-void Interface::video_refresh(const uint8_t *data) {
-  uint32_t *buffer;
-  unsigned pitch;
-  if(video.lock(buffer, pitch, 160, 144)) {
-    for(unsigned y = 0; y < 144; y++) {
-      uint32_t *line = buffer + y * (pitch >> 2);
-      const uint8_t *source = data + y * 160;
-      for(unsigned x = 0; x < 160; x++) {
-        uint32_t color = *source++;
-        *line++ = (color << 16) | (color << 8) | (color << 0);
-      }
-    }
-    video.unlock();
-    video.refresh();
-  }
-
-  static unsigned frameCounter = 0;
-  static time_t timeCounter = time(0);
-
-  frameCounter++;
-  time_t currentTime = time(0);
-  if(currentTime != timeCounter) {
-    timeCounter = currentTime;
-    mainWindow.setStatusText({ "FPS: ", frameCounter });
-    frameCounter = 0;
-  }
-}
-
-void Interface::audio_sample(int16_t center, int16_t lchannel, int16_t rchannel) {
-  dspaudio.sample(lchannel, rchannel);
-  while(dspaudio.pending()) {
-    signed lsample, rsample;
-    dspaudio.read(lsample, rsample);
-    audio.sample(lsample, rsample);
-  }
-}
-
-void Interface::input_poll() {
-  input.poll(inputState);
-}
-
-bool Interface::input_poll(unsigned id) {
-  switch((GameBoy::Input)id) {
-    case GameBoy::Input::Up: return inputState[keyboard(0)[Keyboard::Up]];
-    case GameBoy::Input::Down: return inputState[keyboard(0)[Keyboard::Down]];
-    case GameBoy::Input::Left: return inputState[keyboard(0)[Keyboard::Left]];
-    case GameBoy::Input::Right: return inputState[keyboard(0)[Keyboard::Right]];
-    case GameBoy::Input::B: return inputState[keyboard(0)[Keyboard::Z]];
-    case GameBoy::Input::A: return inputState[keyboard(0)[Keyboard::X]];
-    case GameBoy::Input::Select: return inputState[keyboard(0)[Keyboard::Apostrophe]];
-    case GameBoy::Input::Start: return inputState[keyboard(0)[Keyboard::Return]];
-  }
-
-  return false;
-}
-
-void Interface::message(const string &text) {
-  MessageWindow::information(mainWindow, text);
-}
diff --git a/bsnes/ui-gameboy/interface.hpp b/bsnes/ui-gameboy/interface.hpp
deleted file mode 100755
index efeeca5f..00000000
--- a/bsnes/ui-gameboy/interface.hpp
+++ /dev/null
@@ -1,12 +0,0 @@
-struct Interface : public GameBoy::Interface {
-  int16_t inputState[Scancode::Limit];
-
-  void video_refresh(const uint8_t *data);
-  void audio_sample(int16_t center, int16_t left, int16_t right);
-  void input_poll();
-  bool input_poll(unsigned id);
-
-  void message(const string &text);
-};
-
-extern Interface interface;
diff --git a/bsnes/ui-gameboy/main.cpp b/bsnes/ui-gameboy/main.cpp
deleted file mode 100755
index 9a1f27b9..00000000
--- a/bsnes/ui-gameboy/main.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "base.hpp"
-nall::DSP dspaudio;
-Application application;
-
-#include "interface.cpp"
-
-#include "general/main-window.cpp"
-
-void Application::main(int argc, char **argv) {
-  quit = false;
-
-  #if defined(PLATFORM_WIN)
-  proportionalFont = "Tahoma, 8";
-  proportionalFontBold = "Tahoma, 8, Bold";
-  monospaceFont = "Lucida Console, 8";
-  #else
-  proportionalFont = "Sans, 8";
-  proportionalFontBold = "Sans, 8, Bold";
-  monospaceFont = "Liberation Mono, 8";
-  #endif
-
-  mainWindow.create();
-  mainWindow.setVisible();
-  OS::processEvents();
-
-  #if defined(PLATFORM_WIN)
-  video.driver("Direct3D");
-  #else
-  video.driver("OpenGL");
-  #endif
-  video.set(Video::Handle, (uintptr_t)mainWindow.viewport.handle());
-  video.set(Video::Synchronize, false);
-  video.set(Video::Filter, (unsigned)0);
-  video.init();
-
-  #if defined(PLATFORM_WIN)
-  audio.driver("XAudio2");
-  #else
-  audio.driver("ALSA");
-  #endif
-  audio.set(Audio::Handle, (uintptr_t)mainWindow.viewport.handle());
-  audio.set(Audio::Synchronize, true);
-  audio.set(Audio::Latency, 80u);
-  audio.set(Audio::Frequency, 44100u);
-  audio.init();
-
-  dspaudio.setPrecision(16);
-  dspaudio.setVolume(1.0);
-  dspaudio.setBalance(0.0);
-  dspaudio.setFrequency(4194304.0);
-  dspaudio.setResampler(DSP::Resampler::Average);
-  dspaudio.setResamplerFrequency(44100.0);
-
-  #if defined(PLATFORM_WIN)
-  input.driver("RawInput");
-  #else
-  input.driver("SDL");
-  #endif
-  input.set(Input::Handle, (uintptr_t)mainWindow.viewport.handle());
-  input.init();
-
-  GameBoy::system.init(&interface);
-
-  while(quit == false) {
-    OS::processEvents();
-
-    if(GameBoy::cartridge.loaded()) {
-      do {
-        GameBoy::system.run();
-      } while(GameBoy::scheduler.exit_reason() != GameBoy::Scheduler::ExitReason::FrameEvent);
-    }
-  }
-}
-
-int main(int argc, char **argv) {
-  application.main(argc, argv);
-  return 0;
-}
diff --git a/bsnes/ui-gameboy/utility/utility.cpp b/bsnes/ui-gameboy/utility/utility.cpp
deleted file mode 100755
index 183ffa73..00000000
--- a/bsnes/ui-gameboy/utility/utility.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "../base.hpp"
-Utility utility;
-
-void Utility::loadCartridge(const char *filename) {
-  file fp;
-  if(fp.open(filename, file::mode::read)) {
-    unsigned size = fp.size();
-    uint8_t *data = new uint8_t[size];
-    fp.read(data, size);
-    fp.close();
-
-    cartridge.basename = nall::basename(filename);
-
-    GameBoyCartridge info(data, size);
-    GameBoy::cartridge.load(info.xml, data, size);
-    delete[] data;
-    GameBoy::system.power();
-  }
-}
-
-bool Utility::saveState(unsigned slot) {
-  GameBoy::system.runtosave();
-  serializer s = GameBoy::system.serialize();
-
-  file fp;
-  if(fp.open(string(cartridge.basename, "-", slot, ".bst"), file::mode::write)) {
-    fp.write(s.data(), s.size());
-    fp.close();
-    return true;
-  }
-
-  return false;
-}
-
-bool Utility::loadState(unsigned slot) {
-  file fp;
-  if(fp.open(string(cartridge.basename, "-", slot, ".bst"), file::mode::read)) {
-    unsigned size = fp.size();
-    uint8_t *data = new uint8_t[size];
-    fp.read(data, size);
-    fp.close();
-    serializer s(data, size);
-    GameBoy::system.power();
-    return GameBoy::system.unserialize(s);
-  }
-
-  return false;
-}
diff --git a/bsnes/ui-gameboy/utility/utility.hpp b/bsnes/ui-gameboy/utility/utility.hpp
deleted file mode 100755
index f8f39a31..00000000
--- a/bsnes/ui-gameboy/utility/utility.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-struct Utility {
-  struct Cartridge {
-    string basename;
-  } cartridge;
-
-  void loadCartridge(const char *filename);
-  bool saveState(unsigned slot);
-  bool loadState(unsigned slot);
-};
-
-extern Utility utility;
diff --git a/bsnes/ui/interface/nes.cpp b/bsnes/ui/interface/nes.cpp
index 7f073a78..43d59add 100755
--- a/bsnes/ui/interface/nes.cpp
+++ b/bsnes/ui/interface/nes.cpp
@@ -26,17 +26,17 @@ void InterfaceNES::setCheatCodes(const lstring &list) {
 
 //
 
-void InterfaceNES::video_refresh(const uint32_t *data) {
+void InterfaceNES::video_refresh(const uint16_t *data) {
   interface->video_refresh();
 
   uint32_t *output;
   unsigned outpitch;
   if(video.lock(output, outpitch, 256, 240)) {
     for(unsigned y = 0; y < 240; y++) {
-      const uint32_t *sp = data + y * 256;
+      const uint16_t *sp = data + y * 256;
       uint32_t *dp = output + y * (outpitch >> 2);
       for(unsigned x = 0; x < 256; x++) {
-        *dp++ = *sp++;
+        *dp++ = palette[*sp++];
       }
     }
 
@@ -70,3 +70,38 @@ int16_t InterfaceNES::input_poll(bool port, unsigned device, unsigned id) {
 
   return 0;
 }
+
+InterfaceNES::InterfaceNES() {
+  palette = {
+    0x7c7c7c, 0x0000fc, 0x0000bc, 0x4428bc,
+    0x940084, 0xa80020, 0xa81000, 0x881400,
+    0x503000, 0x007800, 0x006800, 0x005800,
+    0x004058, 0x000000, 0x000000, 0x000000,
+    0xbcbcbc, 0x0078f8, 0x0058f8, 0x6844fc,
+    0xd800cc, 0xe40058, 0xf83800, 0xe45c10,
+    0xac7c00, 0x00b800, 0x00a800, 0x00a844,
+    0x008888, 0x000000, 0x000000, 0x000000,
+    0xf8f8f8, 0x3cbcfc, 0x6888fc, 0x9878f8,
+    0xf878f8, 0xf85898, 0xf87858, 0xfca044,
+    0xf8b800, 0xb8f818, 0x58d854, 0x58f898,
+    0x00e8d8, 0x787878, 0x000000, 0x000000,
+    0xfcfcfc, 0xa4e4fc, 0xb8b8b8, 0xd8d8f8,
+    0xf8b8f8, 0xf8a4c0, 0xf0d0b0, 0xfce0a8,
+    0xf8d878, 0xd8f878, 0xb8f8b8, 0xb8f8d8,
+    0x00fcfc, 0xf8d8f8, 0x000000, 0x000000,
+  };
+
+  for(unsigned e = 1; e < 8; e++) {
+    static const double rfactor[8] = { 1.000, 1.239, 0.794, 1.019, 0.905, 1.023, 0.741, 0.750 };
+    static const double gfactor[8] = { 1.000, 0.915, 1.086, 0.980, 1.026, 0.908, 0.987, 0.750 };
+    static const double bfactor[8] = { 1.000, 0.743, 0.882, 0.653, 1.277, 0.979, 0.101, 0.750 };
+    for(unsigned n = 0; n < 64; n++) {
+      unsigned c = palette[n];
+      uint8_t r = c >> 16, g = c >> 8, b = c >> 0;
+      r = uclamp<8>((unsigned)(r * rfactor[e]));
+      g = uclamp<8>((unsigned)(g * gfactor[e]));
+      b = uclamp<8>((unsigned)(b * bfactor[e]));
+      palette[e * 64 + n] = (r << 16) | (g << 8) | (b << 0);
+    }
+  }
+}
diff --git a/bsnes/ui/interface/nes.hpp b/bsnes/ui/interface/nes.hpp
index 72e1e980..e3db78c6 100755
--- a/bsnes/ui/interface/nes.hpp
+++ b/bsnes/ui/interface/nes.hpp
@@ -4,7 +4,12 @@ struct InterfaceNES : NES::Interface {
 
   void setCheatCodes(const lstring &list);
 
-  void video_refresh(const uint32_t *data);
+  void video_refresh(const uint16_t *data);
   void audio_sample(int16_t sample);
   int16_t input_poll(bool port, unsigned device, unsigned id);
+
+  InterfaceNES();
+
+private:
+  unsigned palette[512];
 };