#pragma once #include "pch.h" #include "Debugger/DebugTypes.h" #include "Shared/NotificationManager.h" #include "Shared/Emulator.h" #include "Shared/ColorUtilities.h" class Debugger; struct ViewerRefreshConfig { uint16_t Scanline; uint16_t Cycle; }; enum class SpriteVisibility : uint8_t { Visible = 0, Offscreen = 1, Disabled = 2 }; enum class NullableBoolean : int8_t { Undefined = -1, False = 0, True = 1 }; enum class DebugSpritePriority { Undefined = -1, Number0 = 0, Number1 = 1, Number2 = 2, Number3 = 3, Foreground = 4, Background = 5 }; struct DebugSpriteInfo { int32_t TileIndex; int32_t TileAddress; int32_t PaletteAddress; TileFormat Format; int16_t SpriteIndex; int16_t X; int16_t Y; int16_t RawX; int16_t RawY; int16_t Bpp; int16_t Palette; DebugSpritePriority Priority; uint16_t Width; uint16_t Height; NullableBoolean HorizontalMirror; NullableBoolean VerticalMirror; NullableBoolean MosaicEnabled; NullableBoolean BlendingEnabled; NullableBoolean WindowMode; NullableBoolean TransformEnabled; NullableBoolean DoubleSize; int8_t TransformParamIndex; SpriteVisibility Visibility; bool UseExtendedVram; NullableBoolean UseSecondTable; uint32_t TileCount; uint32_t TileAddresses[8 * 8]; public: void Init() { TileIndex = -1; TileAddress = -1; PaletteAddress = -1; Format = {}; SpriteIndex = -1; X = -1; Y = -1; RawX = -1; RawY = -1; Bpp = 2; Palette = -1; Priority = DebugSpritePriority::Undefined; Width = 0; Height = 0; HorizontalMirror = NullableBoolean::Undefined; VerticalMirror = NullableBoolean::Undefined; MosaicEnabled = NullableBoolean::Undefined; TransformEnabled = NullableBoolean::Undefined; BlendingEnabled = NullableBoolean::Undefined; WindowMode = NullableBoolean::Undefined; DoubleSize = NullableBoolean::Undefined; TransformParamIndex = -1; Visibility = SpriteVisibility::Offscreen; UseExtendedVram = false; UseSecondTable = NullableBoolean::Undefined; TileCount = 0; } }; enum class TilemapMirroring { None, Horizontal, Vertical, SingleScreenA, SingleScreenB, FourScreens, }; struct DebugTilemapInfo { uint32_t Bpp; TileFormat Format; TilemapMirroring Mirroring; uint32_t TileWidth; uint32_t TileHeight; uint32_t ScrollX; uint32_t ScrollWidth; uint32_t ScrollY; uint32_t ScrollHeight; uint32_t RowCount; uint32_t ColumnCount; uint32_t TilemapAddress; uint32_t TilesetAddress; int8_t Priority = -1; }; struct DebugTilemapTileInfo { int32_t Row = -1; int32_t Column = -1; int32_t Width = -1; int32_t Height = -1; int32_t TileMapAddress = -1; int32_t TileIndex = -1; int32_t TileAddress = -1; int32_t PixelData = -1; int32_t PaletteIndex = -1; int32_t PaletteAddress = -1; int32_t BasePaletteIndex = -1; int32_t AttributeAddress = -1; int16_t AttributeData = -1; NullableBoolean HorizontalMirroring = NullableBoolean::Undefined; NullableBoolean VerticalMirroring = NullableBoolean::Undefined; NullableBoolean HighPriority = NullableBoolean::Undefined; }; struct DebugSpritePreviewInfo { uint32_t Width; uint32_t Height; uint32_t SpriteCount; int32_t CoordOffsetX; int32_t CoordOffsetY; uint32_t VisibleX; uint32_t VisibleY; uint32_t VisibleWidth; uint32_t VisibleHeight; bool WrapBottomToTop; bool WrapRightToLeft; }; enum class RawPaletteFormat { Indexed, Rgb555, Rgb333, Rgb222, Rgb444, Bgr444 }; struct DebugPaletteInfo { MemoryType PaletteMemType; uint32_t PaletteMemOffset; bool HasMemType; uint32_t ColorCount; uint32_t BgColorCount; uint32_t SpriteColorCount; uint32_t SpritePaletteOffset; uint32_t ColorsPerPalette; RawPaletteFormat RawFormat; uint32_t RawPalette[512]; uint32_t RgbPalette[512]; }; class PpuTools { protected: static constexpr uint32_t _spritePreviewSize = 128*128; static constexpr uint32_t _grayscaleColorsBpp1[2] = { 0xFF000000, 0xFFFFFFFF }; static constexpr uint32_t _grayscaleColorsBpp2[4] = { 0xFF000000, 0xFF666666, 0xFFBBBBBB, 0xFFFFFFFF }; static constexpr uint32_t _grayscaleColorsBpp4[16] = { 0xFF000000, 0xFF303030, 0xFF404040, 0xFF505050, 0xFF606060, 0xFF707070, 0xFF808080, 0xFF909090, 0xFF989898, 0xFFA0A0A0, 0xFFAAAAAA, 0xFFBBBBBB, 0xFFCCCCCC, 0xFFDDDDDD, 0xFFEEEEEE, 0xFFFFFFFF }; Emulator* _emu; Debugger* _debugger; unordered_map _updateTimings; void BlendColors(uint8_t output[4], uint8_t input[4]); template __forceinline uint32_t GetRgbPixelColor(const uint32_t* colors, uint8_t colorIndex, uint8_t palette); template __forceinline uint8_t GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, uint32_t rowStart, uint8_t pixelIndex); bool IsTileHidden(MemoryType memType, uint32_t addr, GetTileViewOptions& options); uint32_t GetBackgroundColor(TileBackground bgColor, const uint32_t* colors, uint8_t paletteIndex = 0, uint8_t bpp = 0); uint32_t GetSpriteBackgroundColor(SpriteBackground bgColor, const uint32_t* colors, bool useDarkerColor); void GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t& color, bool forGet); public: PpuTools(Debugger* debugger, Emulator *emu); virtual void GetPpuToolsState(BaseState& state) {}; virtual DebugPaletteInfo GetPaletteInfo(GetPaletteInfoOptions options) = 0; void GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, const uint32_t* palette, uint32_t *outBuffer); virtual DebugTilemapTileInfo GetTilemapTileInfo(uint32_t x, uint32_t y, uint8_t* vram, GetTilemapOptions options, BaseState& baseState, BaseState& ppuToolsState) = 0; virtual FrameInfo GetTilemapSize(GetTilemapOptions options, BaseState& state) = 0; virtual DebugTilemapInfo GetTilemap(GetTilemapOptions options, BaseState& state, BaseState& ppuToolsState, uint8_t* vram, uint32_t* palette, uint32_t* outBuffer) = 0; virtual DebugSpritePreviewInfo GetSpritePreviewInfo(GetSpritePreviewOptions options, BaseState& state, BaseState& ppuToolsState) = 0; virtual void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, BaseState& ppuToolsState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[], uint32_t* spritePreviews, uint32_t* screenPreview) = 0; int32_t GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y); void SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color); virtual void SetPaletteColor(int32_t colorIndex, uint32_t color) = 0; virtual void SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle); void RemoveViewer(uint32_t viewerId); void UpdateViewers(uint16_t scanline, uint16_t cycle); __forceinline bool HasOpenedViewer() { return _updateTimings.size() > 0; } template void InternalGetTileView(GetTileViewOptions options, uint8_t* source, uint32_t srcSize, const uint32_t* colors, uint32_t* outBuffer); }; template uint32_t PpuTools::GetRgbPixelColor(const uint32_t* colors, uint8_t colorIndex, uint8_t palette) { switch(format) { case TileFormat::DirectColor: return ColorUtilities::Rgb555ToArgb( ((((colorIndex & 0x07) << 1) | (palette & 0x01)) << 1) | (((colorIndex & 0x38) | ((palette & 0x02) << 1)) << 4) | (((colorIndex & 0xC0) | ((palette & 0x04) << 3)) << 7) ); case TileFormat::NesBpp2: case TileFormat::Bpp2: return colors[palette * 4 + colorIndex]; case TileFormat::Bpp4: case TileFormat::SmsBpp4: case TileFormat::GbaBpp4: case TileFormat::WsBpp4Packed: case TileFormat::PceSpriteBpp4: case TileFormat::PceSpriteBpp2Sp01: case TileFormat::PceSpriteBpp2Sp23: case TileFormat::PceBackgroundBpp2Cg0: case TileFormat::PceBackgroundBpp2Cg1: return colors[palette * 16 + colorIndex]; case TileFormat::Bpp8: case TileFormat::GbaBpp8: case TileFormat::Mode7: case TileFormat::Mode7ExtBg: return colors[palette * 256 + colorIndex]; case TileFormat::Mode7DirectColor: return ColorUtilities::Rgb555ToArgb(((colorIndex & 0x07) << 2) | ((colorIndex & 0x38) << 4) | ((colorIndex & 0xC0) << 7)); case TileFormat::SmsSgBpp1: return colors[palette * 2 + colorIndex]; default: throw std::runtime_error("unsupported format"); } } template __forceinline uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, uint32_t rowStart, uint8_t pixelIndex) { uint8_t shift = (7 - pixelIndex); uint8_t color; switch(format) { case TileFormat::PceSpriteBpp4: case TileFormat::PceSpriteBpp2Sp01: case TileFormat::PceSpriteBpp2Sp23: shift = 15 - pixelIndex; if(shift >= 8) { shift -= 8; rowStart++; } break; default: break; } switch(format) { case TileFormat::PceSpriteBpp4: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 32) & ramMask] >> shift) & 0x01) << 1); color |= (((ram[(rowStart + 64) & ramMask] >> shift) & 0x01) << 2); color |= (((ram[(rowStart + 96) & ramMask] >> shift) & 0x01) << 3); return color; case TileFormat::PceSpriteBpp2Sp01: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 32) & ramMask] >> shift) & 0x01) << 1); return color; case TileFormat::PceSpriteBpp2Sp23: color = (((ram[(rowStart + 64) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 96) & ramMask] >> shift) & 0x01) << 1); return color; case TileFormat::PceBackgroundBpp2Cg0: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1); return color; case TileFormat::PceBackgroundBpp2Cg1: color = (((ram[(rowStart + 16) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 17) & ramMask] >> shift) & 0x01) << 1); return color; case TileFormat::Bpp2: color = (((ram[rowStart & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1); return color; case TileFormat::NesBpp2: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 8) & ramMask] >> shift) & 0x01) << 1); return color; case TileFormat::Bpp4: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1); color |= (((ram[(rowStart + 16) & ramMask] >> shift) & 0x01) << 2); color |= (((ram[(rowStart + 17) & ramMask] >> shift) & 0x01) << 3); return color; case TileFormat::Bpp8: case TileFormat::DirectColor: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1); color |= (((ram[(rowStart + 16) & ramMask] >> shift) & 0x01) << 2); color |= (((ram[(rowStart + 17) & ramMask] >> shift) & 0x01) << 3); color |= (((ram[(rowStart + 32) & ramMask] >> shift) & 0x01) << 4); color |= (((ram[(rowStart + 33) & ramMask] >> shift) & 0x01) << 5); color |= (((ram[(rowStart + 48) & ramMask] >> shift) & 0x01) << 6); color |= (((ram[(rowStart + 49) & ramMask] >> shift) & 0x01) << 7); return color; case TileFormat::Mode7: case TileFormat::Mode7DirectColor: return ram[(rowStart + pixelIndex * 2 + 1) & ramMask]; case TileFormat::Mode7ExtBg: return ram[(rowStart + pixelIndex * 2 + 1) & ramMask] & 0x7F; case TileFormat::SmsBpp4: color = (((ram[(rowStart + 0) & ramMask] >> shift) & 0x01) << 0); color |= (((ram[(rowStart + 1) & ramMask] >> shift) & 0x01) << 1); color |= (((ram[(rowStart + 2) & ramMask] >> shift) & 0x01) << 2); color |= (((ram[(rowStart + 3) & ramMask] >> shift) & 0x01) << 3); return color; case TileFormat::SmsSgBpp1: color = ((ram[rowStart & ramMask] >> shift) & 0x01); return color; case TileFormat::GbaBpp4: { uint8_t pixelOffset = (7 - shift); uint32_t addr = (rowStart + (pixelOffset >> 1)); if(addr <= ramMask) { if(pixelOffset & 0x01) { return ram[addr] >> 4; } else { return ram[addr] & 0x0F; } } else { return 0; } } case TileFormat::GbaBpp8: { uint8_t pixelOffset = (7 - shift); uint32_t addr = rowStart + pixelOffset; if(addr <= ramMask) { return ram[addr]; } else { return 0; } } case TileFormat::WsBpp4Packed: { uint8_t pixelOffset = (7 - shift); uint32_t addr = (rowStart + (pixelOffset >> 1)); if(addr <= ramMask) { if(pixelOffset & 0x01) { return ram[addr] & 0x0F; } else { return ram[addr] >> 4; } } else { return 0; } } default: throw std::runtime_error("unsupported format"); } }