sameboy: Add more features for the merciless slave driver
This commit is contained in:
parent
7d2ee60ade
commit
cf8013af4a
|
@ -921,8 +921,8 @@ namespace BizHawk.Client.Common
|
|||
if (!Global.Config.GB_AsSGB)
|
||||
{
|
||||
//core = CoreInventory.Instance["GB", "Pizza Boy"];
|
||||
//core = CoreInventory.Instance["GB", "Gambatte"];
|
||||
core = CoreInventory.Instance["GB", "SameBoy"];
|
||||
core = CoreInventory.Instance["GB", "Gambatte"];
|
||||
//core = CoreInventory.Instance["GB", "SameBoy"];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@ using BizHawk.Emulation.Common.Base_Implementations;
|
|||
using BizHawk.Emulation.Cores.Nintendo.SNES9X;
|
||||
using BizHawk.Emulation.Cores.Consoles.SNK;
|
||||
using BizHawk.Emulation.Cores.Consoles.Sega.PicoDrive;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
|
@ -1744,6 +1745,10 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
sNESToolStripMenuItem.Visible = true;
|
||||
}
|
||||
else if (Emulator is Sameboy)
|
||||
{
|
||||
GBSubMenu.Visible = true;
|
||||
}
|
||||
break;
|
||||
case "Coleco":
|
||||
ColecoSubMenu.Visible = true;
|
||||
|
|
|
@ -9,13 +9,15 @@ using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
|||
using BizHawk.Client.EmuHawk.WinFormExtensions;
|
||||
using System.Collections.Generic;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
{
|
||||
public partial class GBGPUView : Form, IToolFormAutoConfig
|
||||
{
|
||||
[RequiredService]
|
||||
public Gameboy Gb { get; private set; }
|
||||
public IGameboyCommon Gb { get; private set; }
|
||||
|
||||
// TODO: freeze semantics are a bit weird: details for a mouseover or freeze are taken from the current
|
||||
// state, not the state at the last callback (and so can be quite different when update is set to manual).
|
||||
|
@ -32,11 +34,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
// g' = 8.25g
|
||||
// b' = 8.25b
|
||||
|
||||
// gambatte doesn't modify these memory locations unless you reconstruct, so we can store
|
||||
private IntPtr _vram;
|
||||
private IntPtr _bgpal;
|
||||
private IntPtr _sppal;
|
||||
private IntPtr _oam;
|
||||
|
||||
private GPUMemoryAreas _memory;
|
||||
|
||||
private bool _cgb; // set once at start
|
||||
private int _lcdc; // set at each callback
|
||||
|
@ -89,13 +88,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
_cgb = Gb.IsCGBMode();
|
||||
_lcdc = 0;
|
||||
|
||||
// TODO: can this be a required Emulator Service, and let the tool manage the logic of closing?
|
||||
if (!Gb.GetGPUMemoryAreas(out _vram, out _bgpal, out _sppal, out _oam))
|
||||
{
|
||||
if (Visible)
|
||||
Close();
|
||||
}
|
||||
tilespal = _bgpal;
|
||||
_memory = Gb.GetGPU();
|
||||
|
||||
tilespal = _memory.Bgpal;
|
||||
|
||||
if (_cgb)
|
||||
label4.Enabled = true;
|
||||
|
@ -357,112 +352,120 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
#endregion
|
||||
|
||||
void ScanlineCallback(int lcdc)
|
||||
void ScanlineCallback(byte lcdc)
|
||||
{
|
||||
_lcdc = lcdc;
|
||||
// set alpha on all pixels
|
||||
unsafe
|
||||
using (_memory.EnterExit())
|
||||
{
|
||||
int* 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);
|
||||
int c = Spriteback.ToArgb();
|
||||
for (int i = 0; i < 32; i += 4)
|
||||
p[i] = c;
|
||||
}
|
||||
var _bgpal = _memory.Bgpal;
|
||||
var _sppal = _memory.Sppal;
|
||||
var _oam = _memory.Oam;
|
||||
var _vram = _memory.Vram;
|
||||
|
||||
// bg maps
|
||||
if (!_cgb)
|
||||
{
|
||||
DrawBGDMG(
|
||||
bmpViewBG.BMP,
|
||||
_vram + (lcdc.Bit(3) ? 0x1c00 : 0x1800),
|
||||
_vram + (lcdc.Bit(4) ? 0x0000 : 0x1000),
|
||||
!lcdc.Bit(4),
|
||||
_bgpal);
|
||||
_lcdc = lcdc;
|
||||
// set alpha on all pixels
|
||||
// TODO: RE: Spriteback, you can't muck with Sameboy in this way due to how SGB reads stuff...?
|
||||
/*unsafe
|
||||
{
|
||||
int* 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);
|
||||
int c = Spriteback.ToArgb();
|
||||
for (int i = 0; i < 32; i += 4)
|
||||
p[i] = c;
|
||||
}*/
|
||||
|
||||
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);
|
||||
// bg maps
|
||||
if (!_cgb)
|
||||
{
|
||||
DrawBGDMG(
|
||||
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();
|
||||
bmpViewWin.Refresh();
|
||||
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);
|
||||
|
||||
// tile display
|
||||
// TODO: user selects palette to use, instead of fixed palette 0
|
||||
// or possibly "smart" where, if a tile is in use, it's drawn with one of the palettes actually being used with it?
|
||||
DrawTiles(bmpViewTiles1.BMP, _vram, tilespal);
|
||||
bmpViewTiles1.Refresh();
|
||||
if (_cgb)
|
||||
{
|
||||
DrawTiles(bmpViewTiles2.BMP, _vram + 0x2000, tilespal);
|
||||
bmpViewTiles2.Refresh();
|
||||
}
|
||||
DrawBGCGB(
|
||||
bmpViewWin.BMP,
|
||||
_vram + (lcdc.Bit(6) ? 0x1c00 : 0x1800),
|
||||
_vram + 0x1000, // force win to second tile bank???
|
||||
true,
|
||||
_bgpal);
|
||||
}
|
||||
bmpViewBG.Refresh();
|
||||
bmpViewWin.Refresh();
|
||||
|
||||
// palettes
|
||||
if (_cgb)
|
||||
{
|
||||
bmpViewBGPal.ChangeBitmapSize(8, 4);
|
||||
if (bmpViewBGPal.Width != 128)
|
||||
bmpViewBGPal.Width = 128;
|
||||
bmpViewSPPal.ChangeBitmapSize(8, 4);
|
||||
if (bmpViewSPPal.Width != 128)
|
||||
bmpViewSPPal.Width = 128;
|
||||
DrawPal(bmpViewBGPal.BMP, _bgpal, 8);
|
||||
DrawPal(bmpViewSPPal.BMP, _sppal, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
bmpViewBGPal.ChangeBitmapSize(1, 4);
|
||||
if (bmpViewBGPal.Width != 16)
|
||||
bmpViewBGPal.Width = 16;
|
||||
bmpViewSPPal.ChangeBitmapSize(2, 4);
|
||||
if (bmpViewSPPal.Width != 32)
|
||||
bmpViewSPPal.Width = 32;
|
||||
DrawPal(bmpViewBGPal.BMP, _bgpal, 1);
|
||||
DrawPal(bmpViewSPPal.BMP, _sppal, 2);
|
||||
}
|
||||
bmpViewBGPal.Refresh();
|
||||
bmpViewSPPal.Refresh();
|
||||
// tile display
|
||||
// TODO: user selects palette to use, instead of fixed palette 0
|
||||
// or possibly "smart" where, if a tile is in use, it's drawn with one of the palettes actually being used with it?
|
||||
DrawTiles(bmpViewTiles1.BMP, _vram, tilespal);
|
||||
bmpViewTiles1.Refresh();
|
||||
if (_cgb)
|
||||
{
|
||||
DrawTiles(bmpViewTiles2.BMP, _vram + 0x2000, tilespal);
|
||||
bmpViewTiles2.Refresh();
|
||||
}
|
||||
|
||||
// oam
|
||||
if (lcdc.Bit(2)) // 8x16
|
||||
{
|
||||
bmpViewOAM.ChangeBitmapSize(320, 16);
|
||||
if (bmpViewOAM.Height != 16)
|
||||
bmpViewOAM.Height = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
bmpViewOAM.ChangeBitmapSize(320, 8);
|
||||
if (bmpViewOAM.Height != 8)
|
||||
bmpViewOAM.Height = 8;
|
||||
}
|
||||
DrawOam(bmpViewOAM.BMP, _oam, _vram, _sppal, lcdc.Bit(2), _cgb);
|
||||
bmpViewOAM.Refresh();
|
||||
// palettes
|
||||
if (_cgb)
|
||||
{
|
||||
bmpViewBGPal.ChangeBitmapSize(8, 4);
|
||||
if (bmpViewBGPal.Width != 128)
|
||||
bmpViewBGPal.Width = 128;
|
||||
bmpViewSPPal.ChangeBitmapSize(8, 4);
|
||||
if (bmpViewSPPal.Width != 128)
|
||||
bmpViewSPPal.Width = 128;
|
||||
DrawPal(bmpViewBGPal.BMP, _bgpal, 8);
|
||||
DrawPal(bmpViewSPPal.BMP, _sppal, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
bmpViewBGPal.ChangeBitmapSize(1, 4);
|
||||
if (bmpViewBGPal.Width != 16)
|
||||
bmpViewBGPal.Width = 16;
|
||||
bmpViewSPPal.ChangeBitmapSize(2, 4);
|
||||
if (bmpViewSPPal.Width != 32)
|
||||
bmpViewSPPal.Width = 32;
|
||||
DrawPal(bmpViewBGPal.BMP, _bgpal, 1);
|
||||
DrawPal(bmpViewSPPal.BMP, _sppal, 2);
|
||||
}
|
||||
bmpViewBGPal.Refresh();
|
||||
bmpViewSPPal.Refresh();
|
||||
|
||||
// oam
|
||||
if (lcdc.Bit(2)) // 8x16
|
||||
{
|
||||
bmpViewOAM.ChangeBitmapSize(320, 16);
|
||||
if (bmpViewOAM.Height != 16)
|
||||
bmpViewOAM.Height = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
bmpViewOAM.ChangeBitmapSize(320, 8);
|
||||
if (bmpViewOAM.Height != 8)
|
||||
bmpViewOAM.Height = 8;
|
||||
}
|
||||
DrawOam(bmpViewOAM.BMP, _oam, _vram, _sppal, lcdc.Bit(2), _cgb);
|
||||
bmpViewOAM.Refresh();
|
||||
}
|
||||
// try to run the current mouseover, to refresh if the mouse is being held over a pane while the emulator runs
|
||||
// this doesn't really work well; the update rate seems to be throttled
|
||||
MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0);
|
||||
|
@ -514,7 +517,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void buttonRefresh_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (cbscanline == -2 && Gb != null)
|
||||
if (cbscanline == -2)
|
||||
Gb.SetScanlineCallback(ScanlineCallback, -2);
|
||||
}
|
||||
|
||||
|
@ -614,145 +617,177 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private unsafe void PaletteMouseover(int x, int y, bool sprite)
|
||||
{
|
||||
bmpViewDetails.ChangeBitmapSize(8, 10);
|
||||
if (bmpViewDetails.Height != 80)
|
||||
bmpViewDetails.Height = 80;
|
||||
var sb = new StringBuilder();
|
||||
x /= 16;
|
||||
y /= 16;
|
||||
int* pal = (int*)(sprite ? _sppal : _bgpal) + x * 4;
|
||||
int color = pal[y];
|
||||
|
||||
sb.AppendLine(string.Format("Palette {0}", x));
|
||||
sb.AppendLine(string.Format("Color {0}", y));
|
||||
sb.AppendLine(string.Format("(R,G,B) = ({0},{1},{2})", color >> 16 & 255, color >> 8 & 255, color & 255));
|
||||
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, 10), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
int* dest = (int*)lockdata.Scan0;
|
||||
int pitch = lockdata.Stride / sizeof(int);
|
||||
|
||||
for (int py = 0; py < 10; py++)
|
||||
using (_memory.EnterExit())
|
||||
{
|
||||
for (int px = 0; px < 8; px++)
|
||||
var _bgpal = _memory.Bgpal;
|
||||
var _sppal = _memory.Sppal;
|
||||
var _oam = _memory.Oam;
|
||||
var _vram = _memory.Vram;
|
||||
|
||||
bmpViewDetails.ChangeBitmapSize(8, 10);
|
||||
if (bmpViewDetails.Height != 80)
|
||||
bmpViewDetails.Height = 80;
|
||||
var sb = new StringBuilder();
|
||||
x /= 16;
|
||||
y /= 16;
|
||||
int* pal = (int*)(sprite ? _sppal : _bgpal) + x * 4;
|
||||
int color = pal[y];
|
||||
|
||||
sb.AppendLine(string.Format("Palette {0}", x));
|
||||
sb.AppendLine(string.Format("Color {0}", y));
|
||||
sb.AppendLine(string.Format("(R,G,B) = ({0},{1},{2})", color >> 16 & 255, color >> 8 & 255, color & 255));
|
||||
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, 10), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
int* dest = (int*)lockdata.Scan0;
|
||||
int pitch = lockdata.Stride / sizeof(int);
|
||||
|
||||
for (int py = 0; py < 10; py++)
|
||||
{
|
||||
if (py < 8)
|
||||
*dest++ = color;
|
||||
else
|
||||
*dest++ = pal[px / 2];
|
||||
for (int px = 0; px < 8; px++)
|
||||
{
|
||||
if (py < 8)
|
||||
*dest++ = color;
|
||||
else
|
||||
*dest++ = pal[px / 2];
|
||||
}
|
||||
dest -= 8;
|
||||
dest += pitch;
|
||||
}
|
||||
dest -= 8;
|
||||
dest += pitch;
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
|
||||
unsafe void TileMouseover(int x, int y, bool secondbank)
|
||||
{
|
||||
// todo: draw with a specific palette
|
||||
bmpViewDetails.ChangeBitmapSize(8, 8);
|
||||
if (bmpViewDetails.Height != 64)
|
||||
bmpViewDetails.Height = 64;
|
||||
var sb = new StringBuilder();
|
||||
x /= 8;
|
||||
y /= 8;
|
||||
int tileindex = y * 16 + x;
|
||||
int tileoffs = tileindex * 16;
|
||||
if (_cgb)
|
||||
sb.AppendLine(string.Format("Tile #{0} @{2}:{1:x4}", tileindex, tileoffs + 0x8000, secondbank ? 1 : 0));
|
||||
else
|
||||
sb.AppendLine(string.Format("Tile #{0} @{1:x4}", tileindex, tileoffs + 0x8000));
|
||||
using (_memory.EnterExit())
|
||||
{
|
||||
var _bgpal = _memory.Bgpal;
|
||||
var _sppal = _memory.Sppal;
|
||||
var _oam = _memory.Oam;
|
||||
var _vram = _memory.Vram;
|
||||
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, 8), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
DrawTile((byte*)_vram + tileoffs + (secondbank ? 8192 : 0), (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)tilespal);
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
// todo: draw with a specific palette
|
||||
bmpViewDetails.ChangeBitmapSize(8, 8);
|
||||
if (bmpViewDetails.Height != 64)
|
||||
bmpViewDetails.Height = 64;
|
||||
var sb = new StringBuilder();
|
||||
x /= 8;
|
||||
y /= 8;
|
||||
int tileindex = y * 16 + x;
|
||||
int tileoffs = tileindex * 16;
|
||||
if (_cgb)
|
||||
sb.AppendLine(string.Format("Tile #{0} @{2}:{1:x4}", tileindex, tileoffs + 0x8000, secondbank ? 1 : 0));
|
||||
else
|
||||
sb.AppendLine(string.Format("Tile #{0} @{1:x4}", tileindex, tileoffs + 0x8000));
|
||||
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, 8), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
DrawTile((byte*)_vram + tileoffs + (secondbank ? 8192 : 0), (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)tilespal);
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe void TilemapMouseover(int x, int y, bool win)
|
||||
{
|
||||
bmpViewDetails.ChangeBitmapSize(8, 8);
|
||||
if (bmpViewDetails.Height != 64)
|
||||
bmpViewDetails.Height = 64;
|
||||
var sb = new StringBuilder();
|
||||
bool secondmap = win ? _lcdc.Bit(6) : _lcdc.Bit(3);
|
||||
int mapoffs = secondmap ? 0x1c00 : 0x1800;
|
||||
x /= 8;
|
||||
y /= 8;
|
||||
mapoffs += y * 32 + x;
|
||||
byte* mapbase = (byte*)_vram + mapoffs;
|
||||
int tileindex = mapbase[0];
|
||||
if (win || !_lcdc.Bit(4)) // 0x9000 base
|
||||
if (tileindex < 128)
|
||||
tileindex += 256; // compute all if from 0x8000 base
|
||||
int tileoffs = tileindex * 16;
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, 8), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
if (!_cgb)
|
||||
using (_memory.EnterExit())
|
||||
{
|
||||
sb.AppendLine(string.Format("{0} Map ({1},{2}) @{3:x4}", win ? "Win" : "BG", x, y, mapoffs + 0x8000));
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{1:x4}", tileindex, tileoffs + 0x8000));
|
||||
DrawTile((byte*)_vram + tileoffs, (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_bgpal);
|
||||
}
|
||||
else
|
||||
{
|
||||
int tileext = mapbase[8192];
|
||||
var _bgpal = _memory.Bgpal;
|
||||
var _sppal = _memory.Sppal;
|
||||
var _oam = _memory.Oam;
|
||||
var _vram = _memory.Vram;
|
||||
|
||||
sb.AppendLine(string.Format("{0} Map ({1},{2}) @{3:x4}", win ? "Win" : "BG", x, y, mapoffs + 0x8000));
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{2}:{1:x4}", tileindex, tileoffs + 0x8000, tileext.Bit(3) ? 1 : 0));
|
||||
sb.AppendLine(string.Format(" Palette {0}", tileext & 7));
|
||||
sb.AppendLine(string.Format(" Flags {0}{1}{2}", tileext.Bit(5) ? 'H' : ' ', tileext.Bit(6) ? 'V' : ' ', tileext.Bit(7) ? 'P' : ' '));
|
||||
DrawTileHv((byte*)_vram + tileoffs + (tileext.Bit(3) ? 8192 : 0), (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_bgpal + 4 * (tileext & 7), tileext.Bit(5), tileext.Bit(6));
|
||||
bmpViewDetails.ChangeBitmapSize(8, 8);
|
||||
if (bmpViewDetails.Height != 64)
|
||||
bmpViewDetails.Height = 64;
|
||||
var sb = new StringBuilder();
|
||||
bool secondmap = win ? _lcdc.Bit(6) : _lcdc.Bit(3);
|
||||
int mapoffs = secondmap ? 0x1c00 : 0x1800;
|
||||
x /= 8;
|
||||
y /= 8;
|
||||
mapoffs += y * 32 + x;
|
||||
byte* mapbase = (byte*)_vram + mapoffs;
|
||||
int tileindex = mapbase[0];
|
||||
if (win || !_lcdc.Bit(4)) // 0x9000 base
|
||||
if (tileindex < 128)
|
||||
tileindex += 256; // compute all if from 0x8000 base
|
||||
int tileoffs = tileindex * 16;
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, 8), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
if (!_cgb)
|
||||
{
|
||||
sb.AppendLine(string.Format("{0} Map ({1},{2}) @{3:x4}", win ? "Win" : "BG", x, y, mapoffs + 0x8000));
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{1:x4}", tileindex, tileoffs + 0x8000));
|
||||
DrawTile((byte*)_vram + tileoffs, (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_bgpal);
|
||||
}
|
||||
else
|
||||
{
|
||||
int tileext = mapbase[8192];
|
||||
|
||||
sb.AppendLine(string.Format("{0} Map ({1},{2}) @{3:x4}", win ? "Win" : "BG", x, y, mapoffs + 0x8000));
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{2}:{1:x4}", tileindex, tileoffs + 0x8000, tileext.Bit(3) ? 1 : 0));
|
||||
sb.AppendLine(string.Format(" Palette {0}", tileext & 7));
|
||||
sb.AppendLine(string.Format(" Flags {0}{1}{2}", tileext.Bit(5) ? 'H' : ' ', tileext.Bit(6) ? 'V' : ' ', tileext.Bit(7) ? 'P' : ' '));
|
||||
DrawTileHv((byte*)_vram + tileoffs + (tileext.Bit(3) ? 8192 : 0), (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_bgpal + 4 * (tileext & 7), tileext.Bit(5), tileext.Bit(6));
|
||||
}
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
|
||||
unsafe void SpriteMouseover(int x, int y)
|
||||
{
|
||||
bool tall = _lcdc.Bit(2);
|
||||
x /= 8;
|
||||
y /= 8;
|
||||
bmpViewDetails.ChangeBitmapSize(8, tall ? 16 : 8);
|
||||
if (bmpViewDetails.Height != bmpViewDetails.BMP.Height * 8)
|
||||
bmpViewDetails.Height = bmpViewDetails.BMP.Height * 8;
|
||||
var sb = new StringBuilder();
|
||||
using (_memory.EnterExit())
|
||||
{
|
||||
var _bgpal = _memory.Bgpal;
|
||||
var _sppal = _memory.Sppal;
|
||||
var _oam = _memory.Oam;
|
||||
var _vram = _memory.Vram;
|
||||
|
||||
byte* oament = (byte*)_oam + 4 * x;
|
||||
int sy = oament[0];
|
||||
int sx = oament[1];
|
||||
int tilenum = oament[2];
|
||||
int flags = oament[3];
|
||||
bool hflip = flags.Bit(5);
|
||||
bool vflip = flags.Bit(6);
|
||||
if (tall)
|
||||
tilenum = vflip ? tilenum | 1 : tilenum & ~1;
|
||||
int tileoffs = tilenum * 16;
|
||||
sb.AppendLine(string.Format("Sprite #{0} @{1:x4}", x, 4 * x + 0xfe00));
|
||||
sb.AppendLine(string.Format(" (x,y) = ({0},{1})", sx, sy));
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, tall ? 16 : 8), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
if (_cgb)
|
||||
{
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{2}:{1:x4}", y == 1 ? tilenum ^ 1 : tilenum, tileoffs + 0x8000, flags.Bit(3) ? 1 : 0));
|
||||
sb.AppendLine(string.Format(" Palette {0}", flags & 7));
|
||||
DrawTileHv((byte*)_vram + tileoffs + (flags.Bit(3) ? 8192 : 0), (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_sppal + 4 * (flags & 7), hflip, vflip);
|
||||
bool tall = _lcdc.Bit(2);
|
||||
x /= 8;
|
||||
y /= 8;
|
||||
bmpViewDetails.ChangeBitmapSize(8, tall ? 16 : 8);
|
||||
if (bmpViewDetails.Height != bmpViewDetails.BMP.Height * 8)
|
||||
bmpViewDetails.Height = bmpViewDetails.BMP.Height * 8;
|
||||
var sb = new StringBuilder();
|
||||
|
||||
byte* oament = (byte*)_oam + 4 * x;
|
||||
int sy = oament[0];
|
||||
int sx = oament[1];
|
||||
int tilenum = oament[2];
|
||||
int flags = oament[3];
|
||||
bool hflip = flags.Bit(5);
|
||||
bool vflip = flags.Bit(6);
|
||||
if (tall)
|
||||
DrawTileHv((byte*)_vram + (tileoffs ^ 16) + (flags.Bit(3) ? 8192 : 0), (int*)(lockdata.Scan0 + lockdata.Stride * 8), lockdata.Stride / sizeof(int), (int*)_sppal + 4 * (flags & 7), hflip, vflip);
|
||||
tilenum = vflip ? tilenum | 1 : tilenum & ~1;
|
||||
int tileoffs = tilenum * 16;
|
||||
sb.AppendLine(string.Format("Sprite #{0} @{1:x4}", x, 4 * x + 0xfe00));
|
||||
sb.AppendLine(string.Format(" (x,y) = ({0},{1})", sx, sy));
|
||||
var lockdata = bmpViewDetails.BMP.LockBits(new Rectangle(0, 0, 8, tall ? 16 : 8), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
|
||||
if (_cgb)
|
||||
{
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{2}:{1:x4}", y == 1 ? tilenum ^ 1 : tilenum, tileoffs + 0x8000, flags.Bit(3) ? 1 : 0));
|
||||
sb.AppendLine(string.Format(" Palette {0}", flags & 7));
|
||||
DrawTileHv((byte*)_vram + tileoffs + (flags.Bit(3) ? 8192 : 0), (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_sppal + 4 * (flags & 7), hflip, vflip);
|
||||
if (tall)
|
||||
DrawTileHv((byte*)_vram + (tileoffs ^ 16) + (flags.Bit(3) ? 8192 : 0), (int*)(lockdata.Scan0 + lockdata.Stride * 8), lockdata.Stride / sizeof(int), (int*)_sppal + 4 * (flags & 7), hflip, vflip);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{1:x4}", y == 1 ? tilenum ^ 1 : tilenum, tileoffs + 0x8000));
|
||||
sb.AppendLine(string.Format(" Palette {0}", flags.Bit(4) ? 1 : 0));
|
||||
DrawTileHv((byte*)_vram + tileoffs, (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_sppal + (flags.Bit(4) ? 4 : 0), hflip, vflip);
|
||||
if (tall)
|
||||
DrawTileHv((byte*)_vram + (tileoffs ^ 16), (int*)(lockdata.Scan0 + lockdata.Stride * 8), lockdata.Stride / sizeof(int), (int*)_sppal + 4 * (flags.Bit(4) ? 4 : 0), hflip, vflip);
|
||||
}
|
||||
sb.AppendLine(string.Format(" Flags {0}{1}{2}", hflip ? 'H' : ' ', vflip ? 'V' : ' ', flags.Bit(7) ? 'P' : ' '));
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(string.Format(" Tile #{0} @{1:x4}", y == 1 ? tilenum ^ 1 : tilenum, tileoffs + 0x8000));
|
||||
sb.AppendLine(string.Format(" Palette {0}", flags.Bit(4) ? 1 : 0));
|
||||
DrawTileHv((byte*)_vram + tileoffs, (int*)lockdata.Scan0, lockdata.Stride / sizeof(int), (int*)_sppal + (flags.Bit(4) ? 4 : 0), hflip, vflip);
|
||||
if (tall)
|
||||
DrawTileHv((byte*)_vram + (tileoffs ^ 16), (int*)(lockdata.Scan0 + lockdata.Stride * 8), lockdata.Stride / sizeof(int), (int*)_sppal + 4 * (flags.Bit(4) ? 4 : 0), hflip, vflip);
|
||||
}
|
||||
sb.AppendLine(string.Format(" Flags {0}{1}{2}", hflip ? 'H' : ' ', vflip ? 'V' : ' ', flags.Bit(7) ? 'P' : ' '));
|
||||
bmpViewDetails.BMP.UnlockBits(lockdata);
|
||||
labelDetails.Text = sb.ToString();
|
||||
bmpViewDetails.Refresh();
|
||||
}
|
||||
|
||||
private void bmpViewBG_MouseEnter(object sender, EventArgs e)
|
||||
|
@ -882,9 +917,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
else if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
if (sender == bmpViewBGPal)
|
||||
tilespal = _bgpal + e.X / 16 * 16;
|
||||
tilespal = _memory.Bgpal + e.X / 16 * 16;
|
||||
else if (sender == bmpViewSPPal)
|
||||
tilespal = _sppal + e.X / 16 * 16;
|
||||
tilespal = _memory.Sppal + e.X / 16 * 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -439,7 +439,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
|
||||
#region ppudebug
|
||||
|
||||
public bool GetGPUMemoryAreas(out IntPtr vram, out IntPtr bgpal, out IntPtr sppal, out IntPtr oam)
|
||||
public GPUMemoryAreas GetGPU()
|
||||
{
|
||||
IntPtr _vram = IntPtr.Zero;
|
||||
IntPtr _bgpal = IntPtr.Zero;
|
||||
|
@ -451,23 +451,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
|| !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.sppal, ref _sppal, ref unused)
|
||||
|| !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.oam, ref _oam, ref unused))
|
||||
{
|
||||
vram = IntPtr.Zero;
|
||||
bgpal = IntPtr.Zero;
|
||||
sppal = IntPtr.Zero;
|
||||
oam = IntPtr.Zero;
|
||||
return false;
|
||||
throw new InvalidOperationException("Unexpected error in gambatte_getmemoryarea");
|
||||
}
|
||||
vram = _vram;
|
||||
bgpal = _bgpal;
|
||||
sppal = _sppal;
|
||||
oam = _oam;
|
||||
return true;
|
||||
}
|
||||
return new GPUMemoryAreas(_vram, _oam, _sppal, _bgpal);
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="lcdc">current value of register $ff40 (LCDC)</param>
|
||||
public delegate void ScanlineCallback(int lcdc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// set up callback
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -6,8 +8,49 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
||||
{
|
||||
public interface IGameboyCommon
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="lcdc">current value of register $ff40 (LCDC)</param>
|
||||
public delegate void ScanlineCallback(byte lcdc);
|
||||
|
||||
public interface IGameboyCommon : ISpecializedEmulatorService
|
||||
{
|
||||
bool IsCGBMode();
|
||||
GPUMemoryAreas GetGPU();
|
||||
|
||||
/// <summary>
|
||||
/// set up callback
|
||||
/// </summary>
|
||||
/// <param name="line">scanline. -1 = end of frame, -2 = RIGHT NOW</param>
|
||||
void SetScanlineCallback(ScanlineCallback callback, int line);
|
||||
}
|
||||
|
||||
public class GPUMemoryAreas : IMonitor
|
||||
{
|
||||
public IntPtr Vram { get; }
|
||||
public IntPtr Oam { get; }
|
||||
public IntPtr Sppal { get; }
|
||||
public IntPtr Bgpal { get; }
|
||||
|
||||
private readonly IMonitor _monitor;
|
||||
|
||||
public GPUMemoryAreas(IntPtr vram, IntPtr oam, IntPtr sppal, IntPtr bgpal, IMonitor monitor = null)
|
||||
{
|
||||
Vram = vram;
|
||||
Oam = oam;
|
||||
Sppal = sppal;
|
||||
Bgpal = bgpal;
|
||||
_monitor = monitor;
|
||||
}
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
_monitor?.Enter();
|
||||
}
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_monitor?.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,5 +33,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
|
||||
[BizImport(CC)]
|
||||
public abstract bool Init(bool cgb, byte[] spc, int spclen);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void GetGpuMemory(IntPtr[] ptrs);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract void SetScanlineCallback(ScanlineCallback callback, int ly);
|
||||
|
||||
[BizImport(CC)]
|
||||
public abstract byte GetIoReg(byte port);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
{
|
||||
[Core("Pizza Boy", "Davide Berra", true, true, "c7bc6ee376028b3766de8d7a02e60ab794841f45",
|
||||
"https://github.com/davideberra/emu-pizza/", false)]
|
||||
public class Pizza : WaterboxCore, IGameboyCommon
|
||||
public class Pizza : WaterboxCore
|
||||
{
|
||||
private LibPizza _pizza;
|
||||
private readonly bool _sgb;
|
||||
|
|
|
@ -88,6 +88,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
_exe.RemoveReadonlyFile("boot.rom");
|
||||
|
||||
PostInit();
|
||||
|
||||
var scratch = new IntPtr[4];
|
||||
_core.GetGpuMemory(scratch);
|
||||
_gpuMemory = new GPUMemoryAreas(scratch[0], scratch[1], scratch[3], scratch[2], _exe);
|
||||
}
|
||||
|
||||
#region Controller
|
||||
|
@ -152,6 +156,53 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy
|
|||
};
|
||||
}
|
||||
|
||||
protected override void FrameAdvancePost()
|
||||
{
|
||||
if (_scanlineCallback != null && _scanlineCallbackLine == -1)
|
||||
_scanlineCallback(_core.GetIoReg(0x40));
|
||||
}
|
||||
|
||||
protected override void LoadStateBinaryInternal(BinaryReader reader)
|
||||
{
|
||||
UpdateCoreScanlineCallback(false);
|
||||
}
|
||||
|
||||
public bool IsCGBMode() => _cgb;
|
||||
|
||||
private GPUMemoryAreas _gpuMemory;
|
||||
|
||||
public GPUMemoryAreas GetGPU() => _gpuMemory;
|
||||
private ScanlineCallback _scanlineCallback;
|
||||
private int _scanlineCallbackLine;
|
||||
|
||||
public void SetScanlineCallback(ScanlineCallback callback, int line)
|
||||
{
|
||||
_scanlineCallback = callback;
|
||||
_scanlineCallbackLine = line;
|
||||
UpdateCoreScanlineCallback(true);
|
||||
}
|
||||
|
||||
private void UpdateCoreScanlineCallback(bool now)
|
||||
{
|
||||
if (_scanlineCallback == null)
|
||||
{
|
||||
_core.SetScanlineCallback(null, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_scanlineCallbackLine >= 0 && _scanlineCallbackLine <= 153)
|
||||
{
|
||||
_core.SetScanlineCallback(_scanlineCallback, _scanlineCallbackLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
_core.SetScanlineCallback(null, -1);
|
||||
if (_scanlineCallbackLine == -2 && now)
|
||||
{
|
||||
_scanlineCallback(_core.GetIoReg(0x40));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,6 +207,24 @@ ECL_EXPORT void SetInputCallback(void (*callback)())
|
|||
GB_set_input_callback(&GB, callback ? InputCallback : nullptr);
|
||||
}
|
||||
|
||||
ECL_EXPORT void GetGpuMemory(void **p)
|
||||
{
|
||||
p[0] = GB_get_direct_access(&GB, GB_DIRECT_ACCESS_VRAM, nullptr, nullptr);
|
||||
p[1] = GB_get_direct_access(&GB, GB_DIRECT_ACCESS_OAM, nullptr, nullptr);
|
||||
p[2] = GB.background_palettes_rgb;
|
||||
p[3] = GB.sprite_palettes_rgb;
|
||||
}
|
||||
|
||||
ECL_EXPORT void SetScanlineCallback(void (*callback)(uint8_t), int ly)
|
||||
{
|
||||
GB.scanline_callback = callback;
|
||||
GB.scanline_callback_ly = ly;
|
||||
}
|
||||
ECL_EXPORT uint8_t GetIoReg(uint8_t port)
|
||||
{
|
||||
return GB.io_registers[port];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -296,7 +296,10 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||
}
|
||||
bool should_compare_ly = true;
|
||||
uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH;
|
||||
|
||||
|
||||
if (gb->scanline_callback && gb->display_cycles % LINE_LENGTH == 0 && gb->scanline_callback_ly == ly_for_comparison) {
|
||||
gb->scanline_callback(gb->io_registers[GB_IO_LCDC]);
|
||||
}
|
||||
|
||||
/* Handle cycle completion. STAT's initial value depends on model and mode */
|
||||
if (gb->display_cycles == LCDC_PERIOD) {
|
||||
|
@ -380,7 +383,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||
/* Handle STAT changes for lines 0-143 */
|
||||
else if (gb->display_cycles < LINES * LINE_LENGTH) {
|
||||
unsigned position_in_line = gb->display_cycles % LINE_LENGTH;
|
||||
|
||||
|
||||
/* Handle OAM and VRAM blocking */
|
||||
/* Todo: verify CGB timing for write blocking */
|
||||
if (position_in_line == stat_delay - oam_blocking_rush ||
|
||||
|
|
|
@ -172,6 +172,7 @@ typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on);
|
|||
typedef void (*GB_serial_transfer_start_callback_t)(GB_gameboy_t *gb, uint8_t byte_to_send);
|
||||
typedef uint8_t (*GB_serial_transfer_end_callback_t)(GB_gameboy_t *gb);
|
||||
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t sample, uint64_t clock);
|
||||
typedef void (*GB_scanline_callback_t)(uint8_t lcdc);
|
||||
|
||||
typedef struct {
|
||||
bool state;
|
||||
|
@ -400,7 +401,10 @@ struct GB_gameboy_internal_s {
|
|||
GB_serial_transfer_start_callback_t serial_transfer_start_callback;
|
||||
GB_serial_transfer_end_callback_t serial_transfer_end_callback;
|
||||
GB_sample_callback_t sample_callback;
|
||||
|
||||
|
||||
GB_scanline_callback_t scanline_callback;
|
||||
int scanline_callback_ly;
|
||||
|
||||
/* IR */
|
||||
long cycles_since_ir_change;
|
||||
long cycles_since_input_ir_change;
|
||||
|
|
Loading…
Reference in New Issue