diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
index 41f6bfac7c..b0e1c719e5 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs
@@ -149,10 +149,15 @@ namespace BizHawk.Emulation.Consoles.GB
if (scanlinecallback != null)
{
IntPtr vram = IntPtr.Zero;
- int vramlength = 0;
- if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref vram, ref vramlength))
+ IntPtr bgpal = IntPtr.Zero;
+ IntPtr sppal = IntPtr.Zero;
+ int unused = 0;
+ if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref vram, ref unused)
+ || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.bgpal, ref bgpal, ref unused)
+ || !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.sppal, ref sppal, ref unused))
throw new Exception();
- scanlinecallback(vram, vramlength, LibGambatte.gambatte_cpuread(GambatteState, 0xff40));
+
+ scanlinecallback(vram, IsCGBMode(), LibGambatte.gambatte_cpuread(GambatteState, 0xff40), bgpal, sppal);
}
LibGambatte.gambatte_runfor(GambatteState, VideoBuffer, 160, soundbuff, ref nsamp);
@@ -587,12 +592,14 @@ namespace BizHawk.Emulation.Consoles.GB
#region ppudebug
///
- /// a callback to be registered at a particular scanline
+ ///
///
///
- /// length of vram in bytes
- /// current LCDC status
- public delegate void ScanlineCallback(IntPtr vram, int vramlength, int lcdc);
+ ///
+ ///
+ ///
+ ///
+ public delegate void ScanlineCallback(IntPtr vram, bool cgb, int lcdc, IntPtr bgpal, IntPtr sppal);
ScanlineCallback scanlinecallback;
diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
index 0edce548ee..6fee608d6f 100644
--- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
+++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs
@@ -315,7 +315,10 @@ namespace BizHawk.Emulation.Consoles.GB
wram = 2,
cartram = 3,
oam = 4,
- hram = 5
+ hram = 5,
+ // these last two aren't returning native memory area data
+ bgpal = 6,
+ sppal = 7
}
///
diff --git a/BizHawk.MultiClient/GBtools/GBGPUView.cs b/BizHawk.MultiClient/GBtools/GBGPUView.cs
index 9d72d3e6f2..b71250a429 100644
--- a/BizHawk.MultiClient/GBtools/GBGPUView.cs
+++ b/BizHawk.MultiClient/GBtools/GBGPUView.cs
@@ -45,7 +45,7 @@ namespace BizHawk.MultiClient.GBtools
gb.SetScanlineCallback(null, 0);
}
- static unsafe void DrawTile(byte* tile, int* dest, int pitch)
+ static unsafe void DrawTileDMG(byte* tile, int* dest, int pitch, int *pal)
{
for (int y = 0; y < 8; y++)
{
@@ -55,10 +55,8 @@ namespace BizHawk.MultiClient.GBtools
dest += 7;
for (int x = 0; x < 8; x++) // right to left
{
- int palcolor = loplane & 1 | hiplane & 2;
- // todo: palette transformation
- int color = palcolor * 0x555555 | unchecked((int)0xff000000);
- *dest-- = color;
+ int color = loplane & 1 | hiplane & 2;
+ *dest-- = pal[color];
loplane >>= 1;
hiplane >>= 1;
}
@@ -67,28 +65,65 @@ namespace BizHawk.MultiClient.GBtools
}
}
- static unsafe void DrawBG(Bitmap b, IntPtr _map, IntPtr _tiles, bool wrap)
+ static unsafe void DrawTileCGB(byte* tile, int* dest, int pitch, int* pal, bool hflip, bool vflip)
+ {
+ if (vflip)
+ dest += pitch * 7;
+ for (int y = 0; y < 8; y++)
+ {
+ int loplane = *tile++;
+ int hiplane = *tile++;
+ hiplane <<= 1; // msb
+ if (!hflip)
+ dest += 7;
+ for (int x = 0; x < 8; x++) // right to left
+ {
+ int color = loplane & 1 | hiplane & 2;
+ *dest = pal[color];
+ if (!hflip)
+ dest--;
+ else
+ dest++;
+ loplane >>= 1;
+ hiplane >>= 1;
+ }
+ if (!hflip)
+ dest++;
+ else
+ dest -= 8;
+ if (!vflip)
+ dest += pitch;
+ else
+ dest -= pitch;
+ }
+ }
+
+ static unsafe void DrawBGCGB(Bitmap b, IntPtr _map, IntPtr _tiles, bool wrap, IntPtr _pal)
{
if (b.Width != 256 || b.Height != 256)
throw new Exception("GPUView screwed up.");
var lockdata = b.LockBits(new Rectangle(0, 0, 256, 256), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
-
byte* map = (byte*)_map;
-
int* dest = (int*)lockdata.Scan0;
-
int pitch = lockdata.Stride / sizeof(int); // in int*s, not bytes
-
+ int* pal = (int*)_pal;
+
for (int ty = 0; ty < 32; ty++)
- {
+ {
for (int tx = 0; tx < 32; tx++)
{
int tileindex = map[0];
+ int tileext = map[8192];
if (wrap && tileindex >= 128)
tileindex -= 256;
byte* tile = (byte*)(_tiles + tileindex * 16);
- DrawTile(tile, dest, pitch);
+ if (tileext.Bit(3)) // second bank
+ tile += 8192;
+
+ int* thispal = pal + 4 * (tileext & 7);
+
+ DrawTileCGB(tile, dest, pitch, thispal, tileext.Bit(5), tileext.Bit(6));
map++;
dest += 8;
}
@@ -98,29 +133,83 @@ namespace BizHawk.MultiClient.GBtools
b.UnlockBits(lockdata);
}
- ///
- /// core calls this on scanlines
- ///
- ///
- ///
- ///
- void ScanlineCallback(IntPtr vram, int vramlength, int lcdc)
+ static unsafe void DrawBGDMG(Bitmap b, IntPtr _map, IntPtr _tiles, bool wrap, IntPtr _pal)
{
+ if (b.Width != 256 || b.Height != 256)
+ throw new Exception("GPUView screwed up.");
- DrawBG(
- bmpViewBG.bmp,
- vram + (lcdc.Bit(3) ? 0x1c00 : 0x1800),
- vram + (lcdc.Bit(4) ? 0x0000 : 0x1000),
- !lcdc.Bit(4));
+ var lockdata = b.LockBits(new Rectangle(0, 0, 256, 256), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+ byte* map = (byte*)_map;
+ int* dest = (int*)lockdata.Scan0;
+ int pitch = lockdata.Stride / sizeof(int); // in int*s, not bytes
+ int* pal = (int*)_pal;
+
+ for (int ty = 0; ty < 32; ty++)
+ {
+ for (int tx = 0; tx < 32; tx++)
+ {
+ int tileindex = map[0];
+ if (wrap && tileindex >= 128)
+ tileindex -= 256;
+ byte* tile = (byte*)(_tiles + tileindex * 16);
+ DrawTileDMG(tile, dest, pitch, pal);
+ map++;
+ dest += 8;
+ }
+ dest -= 256;
+ dest += pitch * 8;
+ }
+ b.UnlockBits(lockdata);
+ }
+
+ void ScanlineCallback(IntPtr vram, bool cgb, int lcdc, IntPtr bgpal, IntPtr sppal)
+ {
+ // set alpha on all pixels
+ unsafe
+ {
+ int* p;
+ p = (int*)bgpal;
+ for (int i = 0; i < 32; i++)
+ p[i] |= unchecked((int)0xff000000);
+ p = (int*)sppal;
+ for (int i = 0; i < 32; i++)
+ p[i] |= unchecked((int)0xff000000);
+ }
+
+ if (!cgb)
+ {
+ DrawBGDMG(
+ bmpViewBG.bmp,
+ vram + (lcdc.Bit(3) ? 0x1c00 : 0x1800),
+ vram + (lcdc.Bit(4) ? 0x0000 : 0x1000),
+ !lcdc.Bit(4),
+ bgpal);
+
+ DrawBGDMG(
+ bmpViewWin.bmp,
+ vram + (lcdc.Bit(6) ? 0x1c00 : 0x1800),
+ vram + 0x1000, // force win to second tile bank???
+ true,
+ bgpal);
+ }
+ else
+ {
+ DrawBGCGB(
+ bmpViewBG.bmp,
+ vram + (lcdc.Bit(3) ? 0x1c00 : 0x1800),
+ vram + (lcdc.Bit(4) ? 0x0000 : 0x1000),
+ !lcdc.Bit(4),
+ bgpal);
+
+ DrawBGCGB(
+ bmpViewWin.bmp,
+ vram + (lcdc.Bit(6) ? 0x1c00 : 0x1800),
+ vram + 0x1000, // force win to second tile bank???
+ true,
+ bgpal);
+ }
bmpViewBG.Refresh();
-
- DrawBG(
- bmpViewWin.bmp,
- vram + (lcdc.Bit(6) ? 0x1c00 : 0x1800),
- vram + 0x1000, // force win to second tile bank???
- true);
bmpViewWin.Refresh();
-
}
private void GBGPUView_FormClosed(object sender, FormClosedEventArgs e)
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index 8c9bb06b91..b17de0f216 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -1742,6 +1742,7 @@ namespace BizHawk.MultiClient
NESPPU1.Restart();
NESNameTableViewer1.Restart();
NESDebug1.Restart();
+ GBGPUView1.Restart();
PCEBGViewer1.Restart();
TI83KeyPad1.Restart();
TAStudio1.Restart();
diff --git a/BizHawk.MultiClient/output/dll/libgambatte.dll b/BizHawk.MultiClient/output/dll/libgambatte.dll
index 94c1c8beae..3ba2573d65 100644
Binary files a/BizHawk.MultiClient/output/dll/libgambatte.dll and b/BizHawk.MultiClient/output/dll/libgambatte.dll differ
diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp
index 72e99c495b..2405711180 100644
--- a/libgambatte/src/memory.cpp
+++ b/libgambatte/src/memory.cpp
@@ -1019,6 +1019,14 @@ bool Memory::getMemoryArea(int which, unsigned char **data, int *length) {
*data = &ioamhram[384];
*length = 127;
return true;
+ case 6: // bgpal
+ *data = (unsigned char *)display.bgPalette();
+ *length = 32;
+ return true;
+ case 7: // sppal
+ *data = (unsigned char *)display.spPalette();
+ *length = 32;
+ return true;
default: // pass to cartridge
return cart.getMemoryArea(which, data, length);
}
diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h
index b7790f9208..e58c910f28 100644
--- a/libgambatte/src/video.h
+++ b/libgambatte/src/video.h
@@ -250,6 +250,9 @@ public:
bool isCgb() const { return ppu.cgb(); }
bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); }
+
+ unsigned long *bgPalette() { return ppu.bgPalette(); }
+ unsigned long *spPalette() { return ppu.spPalette(); }
};
}