Debugger: GB - Added sprite viewer
This commit is contained in:
parent
6bccfa874f
commit
4db6c08822
8 changed files with 151 additions and 21 deletions
|
@ -412,3 +412,67 @@ void PpuTools::GetGameboyTilemap(uint8_t* vram, GbPpuState& state, uint16_t offs
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PpuTools::GetGameboySpritePreview(GetSpritePreviewOptions options, GbPpuState state, uint8_t* vram, uint8_t* oamRam, uint32_t* outBuffer)
|
||||
{
|
||||
std::fill(outBuffer, outBuffer + 256 * 256, 0xFF333311);
|
||||
for(int i = 16; i < 16 + 144; i++) {
|
||||
std::fill(outBuffer + i * 256 + 8, outBuffer + i * 256 + 168, 0xFF888866);
|
||||
}
|
||||
|
||||
bool isCgb = state.CgbEnabled;
|
||||
bool largeSprites = state.LargeSprites;
|
||||
uint8_t height = largeSprites ? 16 : 8;
|
||||
constexpr uint8_t width = 8;
|
||||
|
||||
uint8_t filled[256];
|
||||
for(int row = 0; row < 256; row++) {
|
||||
std::fill(filled, filled + 256, 0xFF);
|
||||
|
||||
for(int i = 0; i < 0xA0; i += 4) {
|
||||
uint8_t sprY = oamRam[i];
|
||||
if(sprY > row || sprY + height <= row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int y = row - sprY;
|
||||
uint8_t sprX = oamRam[i + 1];
|
||||
uint8_t tileIndex = oamRam[i + 2];
|
||||
uint8_t attributes = oamRam[i + 3];
|
||||
|
||||
uint16_t tileBank = isCgb ? ((attributes & 0x08) ? 0x2000 : 0x0000) : 0;
|
||||
uint8_t palette = isCgb ? (attributes & 0x07) << 2 : 0;
|
||||
bool hMirror = (attributes & 0x20) != 0;
|
||||
bool vMirror = (attributes & 0x40) != 0;
|
||||
|
||||
if(largeSprites) {
|
||||
tileIndex &= 0xFE;
|
||||
}
|
||||
|
||||
uint16_t tileStart = tileIndex * 16;
|
||||
tileStart |= tileBank;
|
||||
|
||||
uint16_t pixelStart = tileStart + (vMirror ? (height - 1 - y) : y) * 2;
|
||||
for(int x = 0; x < width; x++) {
|
||||
if(sprX + x >= 256) {
|
||||
break;
|
||||
} else if(filled[sprX + x] < sprX) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t shift = hMirror ? (x & 0x07) : (7 - (x & 0x07));
|
||||
uint8_t color = GetTilePixelColor(vram, 0x3FFF, 2, pixelStart, shift);
|
||||
|
||||
if(color > 0) {
|
||||
if(!isCgb) {
|
||||
color = (((attributes & 0x10) ? state.ObjPalette1 : state.ObjPalette0) >> (color * 2)) & 0x03;
|
||||
}
|
||||
|
||||
uint32_t outOffset = (row * 256) + sprX + x;
|
||||
outBuffer[outOffset] = DefaultVideoFilter::ToArgb(state.CgbObjPalettes[palette + color]);
|
||||
filled[sprX + x] = sprX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,4 +31,5 @@ public:
|
|||
void UpdateViewers(uint16_t scanline, uint16_t cycle);
|
||||
|
||||
void GetGameboyTilemap(uint8_t* vram, GbPpuState& state, uint16_t offset, uint32_t* outBuffer);
|
||||
void GetGameboySpritePreview(GetSpritePreviewOptions options, GbPpuState state, uint8_t* vram, uint8_t* oamRam, uint32_t* outBuffer);
|
||||
};
|
|
@ -93,6 +93,7 @@ extern "C"
|
|||
DllExport void __stdcall SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle) { GetDebugger()->GetPpuTools()->SetViewerUpdateTiming(viewerId, scanline, cycle); }
|
||||
|
||||
DllExport void __stdcall GetGameboyTilemap(uint8_t* vram, GbPpuState state, uint16_t offset, uint32_t* buffer) { GetDebugger()->GetPpuTools()->GetGameboyTilemap(vram, state, offset, buffer); }
|
||||
DllExport void __stdcall GetGameboySpritePreview(GetSpritePreviewOptions options, GbPpuState state, uint8_t* vram, uint8_t* oamRam, uint32_t* buffer) { GetDebugger()->GetPpuTools()->GetGameboySpritePreview(options, state, vram, oamRam, buffer); }
|
||||
|
||||
DllExport void __stdcall GetDebugEvents(DebugEventInfo *infoArray, uint32_t &maxEventCount) { GetDebugger()->GetEventManager()->GetEvents(infoArray, maxEventCount); }
|
||||
DllExport uint32_t __stdcall GetDebugEventCount(EventViewerDisplayOptions options) { return GetDebugger()->GetEventManager()->GetEventCount(options); }
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace Mesen.GUI.Debugger.PpuViewer
|
|||
public bool VerticalMirror;
|
||||
public bool UseSecondTable;
|
||||
|
||||
private bool GameboyMode;
|
||||
|
||||
public Rectangle GetBounds()
|
||||
{
|
||||
return new Rectangle(X, Y, Width, Height);
|
||||
|
@ -32,6 +34,10 @@ namespace Mesen.GUI.Debugger.PpuViewer
|
|||
|
||||
public bool IsVisible()
|
||||
{
|
||||
if(GameboyMode) {
|
||||
return !(X == 0 || X >= 168 || Y+Height <= 16 || Y >= 160);
|
||||
}
|
||||
|
||||
if(X + Width <= 0 || X > 255) {
|
||||
return false;
|
||||
}
|
||||
|
@ -44,6 +50,33 @@ namespace Mesen.GUI.Debugger.PpuViewer
|
|||
return true;
|
||||
}
|
||||
|
||||
public static SpriteInfo GetGbSpriteInfo(byte[] oamRam, int spriteIndex, bool largeSprite, bool cgbEnabled)
|
||||
{
|
||||
SpriteInfo sprite = new SpriteInfo();
|
||||
int addr = spriteIndex << 2;
|
||||
sprite.Index = spriteIndex;
|
||||
sprite.Y = oamRam[addr];
|
||||
|
||||
sprite.LargeSprite = largeSprite;
|
||||
sprite.Height = largeSprite ? 16 : 8;
|
||||
|
||||
sprite.Width = 8;
|
||||
sprite.X = oamRam[addr + 1];
|
||||
sprite.TileIndex = oamRam[addr + 2];
|
||||
|
||||
byte attributes = oamRam[addr + 3];
|
||||
sprite.Flags = attributes;
|
||||
sprite.UseSecondTable = cgbEnabled ? ((attributes & 0x08) != 0) : false;
|
||||
sprite.Palette = cgbEnabled ? (attributes & 0x07) : ((attributes & 0x10) >> 4);
|
||||
sprite.Priority = (attributes & 0x80) != 0 ? 1 : 0;
|
||||
sprite.HorizontalMirror = (attributes & 0x20) != 0;
|
||||
sprite.VerticalMirror = (attributes & 0x40) != 0;
|
||||
|
||||
sprite.GameboyMode = true;
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
public static SpriteInfo GetSpriteInfo(byte[] oamRam, int oamMode, int spriteIndex)
|
||||
{
|
||||
SpriteInfo sprite = new SpriteInfo();
|
||||
|
|
|
@ -16,8 +16,10 @@ namespace Mesen.GUI.Debugger.PpuViewer
|
|||
public event SpriteSelectedHandler SpriteSelected;
|
||||
|
||||
private List<SpriteInfo> _sprites;
|
||||
private DebugState _state;
|
||||
private byte[] _oamRam;
|
||||
private int _oamMode;
|
||||
private bool _isGameboyMode;
|
||||
private int _selectedIndex;
|
||||
private int _sortOrder = 1;
|
||||
private DateTime _lastRefresh = DateTime.MinValue;
|
||||
|
@ -63,14 +65,16 @@ namespace Mesen.GUI.Debugger.PpuViewer
|
|||
}
|
||||
}
|
||||
|
||||
public void SetData(byte[] oamRam, int oamMode)
|
||||
public void SetData(DebugState state, byte[] oamRam, int oamMode, bool gameboyMode)
|
||||
{
|
||||
if(_oamRam == oamRam) {
|
||||
return;
|
||||
}
|
||||
|
||||
_state = state;
|
||||
_oamRam = oamRam;
|
||||
_oamMode = oamMode;
|
||||
_isGameboyMode = gameboyMode;
|
||||
if((DateTime.Now - _lastRefresh).TotalMilliseconds > 200) {
|
||||
RefreshList();
|
||||
}
|
||||
|
@ -79,8 +83,14 @@ namespace Mesen.GUI.Debugger.PpuViewer
|
|||
private void RefreshList()
|
||||
{
|
||||
List<SpriteInfo> sprites = new List<SpriteInfo>();
|
||||
for(int i = 0; i < 128; i++) {
|
||||
SpriteInfo sprite = SpriteInfo.GetSpriteInfo(_oamRam, _oamMode, i);
|
||||
for(int i = 0; i < (_isGameboyMode ? 40 : 128); i++) {
|
||||
SpriteInfo sprite;
|
||||
if(_isGameboyMode) {
|
||||
sprite = SpriteInfo.GetGbSpriteInfo(_oamRam, i, _state.Gameboy.Ppu.LargeSprites, _state.Gameboy.Ppu.CgbEnabled);
|
||||
} else {
|
||||
sprite = SpriteInfo.GetSpriteInfo(_oamRam, _state.Ppu.OamMode, i);
|
||||
}
|
||||
|
||||
if(!chkHideOffscreenSprites.Checked || sprite.IsVisible()) {
|
||||
sprites.Add(sprite);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Mesen.GUI.Debugger
|
|||
{
|
||||
public partial class frmSpriteViewer : BaseForm, IRefresh
|
||||
{
|
||||
private PpuState _state;
|
||||
private DebugState _state;
|
||||
private byte[] _vram;
|
||||
private byte[] _cgram;
|
||||
private byte[] _oamRam;
|
||||
|
@ -26,6 +26,7 @@ namespace Mesen.GUI.Debugger
|
|||
private Bitmap _previewImage;
|
||||
private GetSpritePreviewOptions _options = new GetSpritePreviewOptions();
|
||||
private WindowRefreshManager _refreshManager;
|
||||
private bool _isGameboyMode = false;
|
||||
|
||||
public ctrlScanlineCycleSelect ScanlineCycleSelect { get { return this.ctrlScanlineCycleSelect; } }
|
||||
|
||||
|
@ -41,11 +42,6 @@ namespace Mesen.GUI.Debugger
|
|||
return;
|
||||
}
|
||||
|
||||
_previewData = new byte[256 * 240 * 4];
|
||||
_previewImage = new Bitmap(256, 240, PixelFormat.Format32bppPArgb);
|
||||
ctrlImagePanel.ImageSize = new Size(256, 240);
|
||||
ctrlImagePanel.Image = _previewImage;
|
||||
|
||||
InitShortcuts();
|
||||
|
||||
SpriteViewerConfig config = ConfigManager.Config.Debug.SpriteViewer;
|
||||
|
@ -102,27 +98,40 @@ namespace Mesen.GUI.Debugger
|
|||
|
||||
public void RefreshData()
|
||||
{
|
||||
_state = DebugApi.GetState().Ppu;
|
||||
_vram = DebugApi.GetMemoryState(SnesMemoryType.VideoRam);
|
||||
_oamRam = DebugApi.GetMemoryState(SnesMemoryType.SpriteRam);
|
||||
_isGameboyMode = EmuApi.GetRomInfo().CoprocessorType == CoprocessorType.Gameboy;
|
||||
_state = DebugApi.GetState();
|
||||
_vram = DebugApi.GetMemoryState(_isGameboyMode ? SnesMemoryType.GbVideoRam : SnesMemoryType.VideoRam);
|
||||
_oamRam = DebugApi.GetMemoryState(_isGameboyMode ? SnesMemoryType.GbSpriteRam : SnesMemoryType.SpriteRam);
|
||||
_cgram = DebugApi.GetMemoryState(SnesMemoryType.CGRam);
|
||||
}
|
||||
|
||||
public void RefreshViewer()
|
||||
{
|
||||
ctrlSpriteList.SetData(_oamRam, _state.OamMode);
|
||||
int height = _isGameboyMode ? 256 : 240;
|
||||
if(_previewImage == null || _previewImage.Height != height) {
|
||||
_previewData = new byte[256 * height * 4];
|
||||
_previewImage = new Bitmap(256, height, PixelFormat.Format32bppPArgb);
|
||||
ctrlImagePanel.ImageSize = new Size(256, height);
|
||||
ctrlImagePanel.Image = _previewImage;
|
||||
}
|
||||
|
||||
DebugApi.GetSpritePreview(_options, _state, _vram, _oamRam, _cgram, _previewData);
|
||||
ctrlSpriteList.SetData(_state, _oamRam, _state.Ppu.OamMode, _isGameboyMode);
|
||||
|
||||
if(_isGameboyMode) {
|
||||
DebugApi.GetGameboySpritePreview(_options, _state.Gameboy.Ppu, _vram, _oamRam, _previewData);
|
||||
} else {
|
||||
DebugApi.GetSpritePreview(_options, _state.Ppu, _vram, _oamRam, _cgram, _previewData);
|
||||
}
|
||||
|
||||
using(Graphics g = Graphics.FromImage(_previewImage)) {
|
||||
GCHandle handle = GCHandle.Alloc(_previewData, GCHandleType.Pinned);
|
||||
Bitmap source = new Bitmap(256, 240, 4 * 256, PixelFormat.Format32bppPArgb, handle.AddrOfPinnedObject());
|
||||
Bitmap source = new Bitmap(256, height, 4 * 256, PixelFormat.Format32bppPArgb, handle.AddrOfPinnedObject());
|
||||
g.DrawImage(source, 0, 0);
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
if(_options.SelectedSprite >= 0) {
|
||||
ctrlImagePanel.Selection = SpriteInfo.GetSpriteInfo(_oamRam, _state.OamMode, _options.SelectedSprite).GetBounds();
|
||||
ctrlImagePanel.Selection = GetSpriteInfo(_options.SelectedSprite).GetBounds();
|
||||
} else {
|
||||
ctrlImagePanel.Selection = Rectangle.Empty;
|
||||
}
|
||||
|
@ -157,8 +166,8 @@ namespace Mesen.GUI.Debugger
|
|||
int y = e.Y / ctrlImagePanel.ImageScale;
|
||||
|
||||
SpriteInfo match = null;
|
||||
for(int i = 0; i < 128; i++) {
|
||||
SpriteInfo sprite = SpriteInfo.GetSpriteInfo(_oamRam, _state.OamMode, i);
|
||||
for(int i = 0; i < (_isGameboyMode ? 40 : 128); i++) {
|
||||
SpriteInfo sprite = GetSpriteInfo(i);
|
||||
if(x >= sprite.X && x <= sprite.X + sprite.Width) {
|
||||
int endY = (sprite.Y + sprite.Height) & 0xFF;
|
||||
bool visible = (y >= sprite.Y && y < endY) || (endY < sprite.Y && y < endY);
|
||||
|
@ -172,6 +181,18 @@ namespace Mesen.GUI.Debugger
|
|||
SelectSprite(match);
|
||||
}
|
||||
|
||||
private SpriteInfo GetSpriteInfo(int index)
|
||||
{
|
||||
SpriteInfo sprite;
|
||||
if(_isGameboyMode) {
|
||||
sprite = SpriteInfo.GetGbSpriteInfo(_oamRam, index, _state.Gameboy.Ppu.LargeSprites, _state.Gameboy.Ppu.CgbEnabled);
|
||||
} else {
|
||||
sprite = SpriteInfo.GetSpriteInfo(_oamRam, _state.Ppu.OamMode, index);
|
||||
}
|
||||
|
||||
return sprite;
|
||||
}
|
||||
|
||||
private void SelectSprite(SpriteInfo sprite)
|
||||
{
|
||||
if(sprite != null) {
|
||||
|
|
|
@ -481,8 +481,6 @@ namespace Mesen.GUI.Forms
|
|||
mnuDebugger.Visible = !isGameboyMode;
|
||||
mnuSpcDebugger.Enabled = !isGameboyMode;
|
||||
mnuSpcDebugger.Visible = !isGameboyMode;
|
||||
mnuSpriteViewer.Enabled = !isGameboyMode;
|
||||
mnuSpriteViewer.Visible = !isGameboyMode;
|
||||
mnuAssembler.Enabled = !isGameboyMode;
|
||||
mnuAssembler.Visible = !isGameboyMode;
|
||||
sepCoprocessors.Visible = !isGameboyMode;
|
||||
|
|
|
@ -88,10 +88,12 @@ namespace Mesen.GUI
|
|||
}
|
||||
|
||||
[DllImport(DllPath)] public static extern void GetTilemap(GetTilemapOptions options, PpuState state, byte[] vram, byte[] cgram, [In, Out] byte[] buffer);
|
||||
[DllImport(DllPath)] public static extern void GetGameboyTilemap(byte[] vram, GbPpuState state, UInt16 offset, [In, Out] byte[] buffer);
|
||||
[DllImport(DllPath)] public static extern void GetTileView(GetTileViewOptions options, byte[] source, int srcSize, byte[] cgram, [In, Out] byte[] buffer);
|
||||
[DllImport(DllPath)] public static extern void GetSpritePreview(GetSpritePreviewOptions options, PpuState state, byte[] vram, byte[] oamRam, byte[] cgram, [In, Out] byte[] buffer);
|
||||
|
||||
[DllImport(DllPath)] public static extern void GetGameboyTilemap(byte[] vram, GbPpuState state, UInt16 offset, [In, Out] byte[] buffer);
|
||||
[DllImport(DllPath)] public static extern void GetGameboySpritePreview(GetSpritePreviewOptions options, GbPpuState state, byte[] vram, byte[] oamRam, [In, Out] byte[] buffer);
|
||||
|
||||
[DllImport(DllPath)] public static extern void SetViewerUpdateTiming(Int32 viewerId, Int32 scanline, Int32 cycle);
|
||||
|
||||
[DllImport(DllPath)] private static extern UInt32 GetDebugEventCount(EventViewerDisplayOptions options);
|
||||
|
|
Loading…
Add table
Reference in a new issue