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(); } }; }