mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
343 lines
12 KiB
C++
343 lines
12 KiB
C++
#include "pch.h"
|
|
#include "Debugger/PpuTools.h"
|
|
#include "Debugger/CdlManager.h"
|
|
#include "Debugger/DebugTypes.h"
|
|
#include "Debugger/DebugBreakHelper.h"
|
|
#include "Shared/SettingTypes.h"
|
|
#include "SNES/SnesDefaultVideoFilter.h"
|
|
#include "SNES/SnesPpu.h"
|
|
#include "Gameboy/GbTypes.h"
|
|
|
|
PpuTools::PpuTools(Debugger* debugger, Emulator *emu)
|
|
{
|
|
_emu = emu;
|
|
_debugger = debugger;
|
|
}
|
|
|
|
void PpuTools::BlendColors(uint8_t output[4], uint8_t input[4])
|
|
{
|
|
int alpha = input[3] + 1;
|
|
uint8_t invertedAlpha = 256 - input[3];
|
|
output[0] = (uint8_t)((alpha * input[0] + invertedAlpha * output[0]) >> 8);
|
|
output[1] = (uint8_t)((alpha * input[1] + invertedAlpha * output[1]) >> 8);
|
|
output[2] = (uint8_t)((alpha * input[2] + invertedAlpha * output[2]) >> 8);
|
|
output[3] = 0xFF;
|
|
}
|
|
|
|
void PpuTools::GetTileView(GetTileViewOptions options, uint8_t* source, uint32_t srcSize, const uint32_t* colors, uint32_t* outBuffer)
|
|
{
|
|
switch(options.Format) {
|
|
case TileFormat::Bpp2: InternalGetTileView<TileFormat::Bpp2>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::Bpp4: InternalGetTileView<TileFormat::Bpp4>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::Bpp8: InternalGetTileView<TileFormat::Bpp8>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::DirectColor: InternalGetTileView<TileFormat::DirectColor>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::Mode7: InternalGetTileView<TileFormat::Mode7>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::Mode7DirectColor: InternalGetTileView<TileFormat::Mode7DirectColor>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::Mode7ExtBg: InternalGetTileView<TileFormat::Mode7ExtBg>(options, source, srcSize, colors, outBuffer); break;
|
|
|
|
case TileFormat::NesBpp2: InternalGetTileView<TileFormat::NesBpp2>(options, source, srcSize, colors, outBuffer); break;
|
|
|
|
case TileFormat::PceSpriteBpp4: InternalGetTileView<TileFormat::PceSpriteBpp4>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::PceBackgroundBpp2Cg0: InternalGetTileView<TileFormat::PceBackgroundBpp2Cg0>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::PceBackgroundBpp2Cg1: InternalGetTileView<TileFormat::PceBackgroundBpp2Cg1>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::PceSpriteBpp2Sp01: InternalGetTileView<TileFormat::PceSpriteBpp2Sp01>(options, source, srcSize, colors, outBuffer); break;
|
|
case TileFormat::PceSpriteBpp2Sp23: InternalGetTileView<TileFormat::PceSpriteBpp2Sp23>(options, source, srcSize, colors, outBuffer); break;
|
|
}
|
|
}
|
|
|
|
uint32_t PpuTools::GetBackgroundColor(TileBackground bgColor, const uint32_t* colors, uint8_t paletteIndex, uint8_t bpp)
|
|
{
|
|
switch(bgColor) {
|
|
default:
|
|
case TileBackground::Default: return colors[0];
|
|
case TileBackground::PaletteColor: return colors[paletteIndex * (1 << bpp)];
|
|
case TileBackground::Black: return 0xFF000000;
|
|
case TileBackground::White: return 0xFFFFFFFF;
|
|
case TileBackground::Magenta: return 0xFFFF00FF;
|
|
case TileBackground::Transparent: return 0;
|
|
}
|
|
}
|
|
|
|
uint32_t PpuTools::GetSpriteBackgroundColor(SpriteBackground bgColor, const uint32_t* colors, bool useDarkerColor)
|
|
{
|
|
switch(bgColor) {
|
|
default:
|
|
case SpriteBackground::Gray: return useDarkerColor ? 0xFF333333 : 0xFF666666;
|
|
case SpriteBackground::Background: return useDarkerColor ? (((colors[0] >> 1) & 0x7F7F7F) | 0xFF000000) : colors[0];
|
|
case SpriteBackground::Black: return useDarkerColor ? 0xFF000000 : 0xFF202020;
|
|
case SpriteBackground::White: return useDarkerColor ? 0xFFEEEEEE : 0xFFFFFFFF;
|
|
case SpriteBackground::Magenta: return useDarkerColor ? 0xFFCC00CC : 0xFFFF00FF;
|
|
case SpriteBackground::Transparent: return 0;
|
|
}
|
|
}
|
|
|
|
template<TileFormat format>
|
|
void PpuTools::InternalGetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, const uint32_t *colors, uint32_t *outBuffer)
|
|
{
|
|
uint32_t ramMask = srcSize - 1;
|
|
uint8_t* ram = source;
|
|
uint8_t bpp;
|
|
|
|
int rowOffset = 2;
|
|
|
|
int tileWidth = 8;
|
|
int tileHeight = 8;
|
|
switch(options.Format) {
|
|
case TileFormat::Bpp2: bpp = 2; break;
|
|
case TileFormat::Bpp4: bpp = 4; break;
|
|
case TileFormat::DirectColor: bpp = 8; break;
|
|
|
|
case TileFormat::Mode7:
|
|
case TileFormat::Mode7DirectColor:
|
|
case TileFormat::Mode7ExtBg:
|
|
bpp = 16;
|
|
rowOffset = 16;
|
|
break;
|
|
|
|
case TileFormat::NesBpp2: bpp = 2; rowOffset = 1; break;
|
|
|
|
case TileFormat::PceSpriteBpp4: bpp = 4; rowOffset = 2; tileWidth = 16; tileHeight = 16; options.Width /= 2; options.Height /= 2; break;
|
|
case TileFormat::PceSpriteBpp2Sp01: bpp = 4; rowOffset = 2; tileWidth = 16; tileHeight = 16; options.Width /= 2; options.Height /= 2; break;
|
|
case TileFormat::PceSpriteBpp2Sp23: bpp = 4; rowOffset = 2; tileWidth = 16; tileHeight = 16; options.Width /= 2; options.Height /= 2; break;
|
|
|
|
//2BPP, but use BPP=4 because tiles are arranged in the regular 4BPP layout
|
|
case TileFormat::PceBackgroundBpp2Cg0: bpp = 4; break;
|
|
case TileFormat::PceBackgroundBpp2Cg1: bpp = 4; break;
|
|
|
|
default: bpp = 8; break;
|
|
}
|
|
|
|
int bytesPerTile = tileHeight*tileWidth * bpp / 8;
|
|
int tileCount = options.Width * options.Height;
|
|
|
|
uint8_t colorMask = 0xFF;
|
|
if(options.UseGrayscalePalette) {
|
|
options.Palette = 0;
|
|
colors = bpp == 2 ? _grayscaleColorsBpp2 : _grayscaleColorsBpp4;
|
|
colorMask = bpp == 2 ? 0x03 : 0x0F;
|
|
}
|
|
|
|
uint32_t bgColor = GetBackgroundColor(options.Background, colors, options.Palette, bpp);
|
|
|
|
uint32_t outputSize = tileCount * tileWidth * tileHeight;
|
|
for(uint32_t i = 0; i < outputSize; i++) {
|
|
outBuffer[i] = bgColor;
|
|
}
|
|
|
|
int rowCount = (int)std::ceil((double)tileCount / options.Width);
|
|
|
|
for(int row = 0; row < rowCount; row++) {
|
|
uint32_t baseOffset = row * bytesPerTile * options.Width;
|
|
if(baseOffset >= srcSize) {
|
|
break;
|
|
}
|
|
|
|
for(int column = 0; column < options.Width; column++) {
|
|
uint32_t addr = baseOffset + options.StartAddress + bytesPerTile * column;
|
|
|
|
int baseOutputOffset;
|
|
if(options.Layout == TileLayout::SingleLine8x16) {
|
|
int displayColumn = column / 2 + ((row & 0x01) ? options.Width/2 : 0);
|
|
int displayRow = (row & ~0x01) + ((column & 0x01) ? 1 : 0);
|
|
baseOutputOffset = displayRow * options.Width * tileWidth * tileHeight + displayColumn * tileWidth;
|
|
} else if(options.Layout == TileLayout::SingleLine16x16) {
|
|
int displayColumn = (column / 2) + (column & 0x01) + ((row & 0x01) ? options.Width/2 : 0) + ((column & 0x02) ? -1 : 0);
|
|
int displayRow = (row & ~0x01) + ((column & 0x02) ? 1 : 0);
|
|
baseOutputOffset = displayRow * options.Width * tileWidth * tileHeight + displayColumn * tileWidth;
|
|
} else {
|
|
baseOutputOffset = row * options.Width * tileWidth * tileHeight + column * tileWidth;
|
|
}
|
|
|
|
if(IsTileHidden(options.MemType, addr, options)) {
|
|
continue;
|
|
}
|
|
|
|
for(int y = 0; y < tileHeight; y++) {
|
|
uint32_t pixelStart = addr + y * rowOffset;
|
|
for(int x = 0; x < tileWidth; x++) {
|
|
uint8_t color = GetTilePixelColor<format>(ram, ramMask, pixelStart, x);
|
|
if(color != 0 || options.Background == TileBackground::PaletteColor) {
|
|
uint32_t pos = baseOutputOffset + (y * options.Width * tileWidth) + x;
|
|
if(pos < outputSize) {
|
|
outBuffer[pos] = GetRgbPixelColor<format>(colors, color & colorMask, options.Palette);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PpuTools::IsTileHidden(MemoryType memType, uint32_t addr, GetTileViewOptions& options)
|
|
{
|
|
if(options.Filter == TileFilter::None) {
|
|
return false;
|
|
}
|
|
|
|
int16_t cdlFlags = _debugger->GetCdlManager()->GetCdlFlags(memType, addr);
|
|
return (
|
|
(cdlFlags == 0 && options.Filter == TileFilter::HideUnused) ||
|
|
(cdlFlags > 0 && options.Filter == TileFilter::HideUsed)
|
|
);
|
|
}
|
|
|
|
void PpuTools::SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle)
|
|
{
|
|
DebugBreakHelper helper(_debugger);
|
|
|
|
ViewerRefreshConfig cfg;
|
|
cfg.Scanline = scanline;
|
|
cfg.Cycle = cycle;
|
|
_updateTimings[viewerId] = cfg;
|
|
}
|
|
|
|
void PpuTools::RemoveViewer(uint32_t viewerId)
|
|
{
|
|
DebugBreakHelper helper(_debugger);
|
|
_updateTimings.erase(viewerId);
|
|
}
|
|
|
|
int32_t PpuTools::GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y)
|
|
{
|
|
int32_t color = 0;
|
|
GetSetTilePixel(tileAddress, format, x, y, color, true);
|
|
return color;
|
|
}
|
|
|
|
void PpuTools::SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color)
|
|
{
|
|
GetSetTilePixel(tileAddress, format, x, y, color, false);
|
|
}
|
|
|
|
void PpuTools::GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t& color, bool forGet)
|
|
{
|
|
ConsoleMemoryInfo memInfo = _emu->GetMemory(tileAddress.Type);
|
|
if(!memInfo.Memory || memInfo.Size == 0) {
|
|
return;
|
|
}
|
|
|
|
int rowOffset;
|
|
switch(format) {
|
|
default: rowOffset = 2; break;
|
|
case TileFormat::Mode7:
|
|
case TileFormat::Mode7DirectColor:
|
|
case TileFormat::Mode7ExtBg:
|
|
rowOffset = 16;
|
|
break;
|
|
|
|
case TileFormat::NesBpp2: rowOffset = 1; break;
|
|
case TileFormat::PceSpriteBpp4: rowOffset = 2; break;
|
|
}
|
|
|
|
uint8_t* ram = (uint8_t*)memInfo.Memory;
|
|
int rowStart = tileAddress.Address + (y * rowOffset);
|
|
int ramMask = (memInfo.Size - 1);
|
|
|
|
uint8_t shift = (7 - x);
|
|
|
|
auto setBit = [&](uint32_t addr, uint8_t pixelNumber, uint8_t bitNumber) {
|
|
if(forGet) {
|
|
uint8_t bitValue = ((ram[addr & ramMask] >> pixelNumber) & 0x01);
|
|
color |= bitValue << bitNumber;
|
|
} else {
|
|
uint8_t bitValue = (color >> bitNumber) & 0x01;
|
|
ram[addr & ramMask] &= ~(1 << pixelNumber);
|
|
ram[addr & ramMask] |= (bitValue & 0x01) << pixelNumber;
|
|
}
|
|
};
|
|
|
|
switch(format) {
|
|
case TileFormat::Bpp2:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 1, shift, 1);
|
|
break;
|
|
|
|
case TileFormat::NesBpp2:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 8, shift, 1);
|
|
break;
|
|
|
|
case TileFormat::Bpp4:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 1, shift, 1);
|
|
setBit(rowStart + 16, shift, 2);
|
|
setBit(rowStart + 17, shift, 3);
|
|
break;
|
|
|
|
case TileFormat::Bpp8:
|
|
case TileFormat::DirectColor:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 1, shift, 1);
|
|
setBit(rowStart + 16, shift, 2);
|
|
setBit(rowStart + 17, shift, 3);
|
|
setBit(rowStart + 32, shift, 4);
|
|
setBit(rowStart + 33, shift, 5);
|
|
setBit(rowStart + 48, shift, 6);
|
|
setBit(rowStart + 49, shift, 7);
|
|
break;
|
|
|
|
case TileFormat::Mode7:
|
|
case TileFormat::Mode7DirectColor:
|
|
case TileFormat::Mode7ExtBg:
|
|
if(forGet) {
|
|
color = ram[(rowStart + x * 2 + 1) & ramMask];
|
|
} else {
|
|
ram[(rowStart + x * 2 + 1) & ramMask] = color;
|
|
}
|
|
break;
|
|
|
|
case TileFormat::PceSpriteBpp4:
|
|
case TileFormat::PceSpriteBpp2Sp01:
|
|
case TileFormat::PceSpriteBpp2Sp23:
|
|
{
|
|
shift = 15 - x;
|
|
if(shift >= 8) {
|
|
shift -= 8;
|
|
rowStart++;
|
|
}
|
|
|
|
switch(format) {
|
|
case TileFormat::PceSpriteBpp4:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 32, shift, 1);
|
|
setBit(rowStart + 64, shift, 2);
|
|
setBit(rowStart + 96, shift, 3);
|
|
break;
|
|
|
|
case TileFormat::PceSpriteBpp2Sp01:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 32, shift, 1);
|
|
break;
|
|
|
|
case TileFormat::PceSpriteBpp2Sp23:
|
|
setBit(rowStart + 64, shift, 2);
|
|
setBit(rowStart + 96, shift, 3);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TileFormat::PceBackgroundBpp2Cg0:
|
|
setBit(rowStart, shift, 0);
|
|
setBit(rowStart + 1, shift, 1);
|
|
break;
|
|
|
|
case TileFormat::PceBackgroundBpp2Cg1:
|
|
setBit(rowStart + 16, shift, 2);
|
|
setBit(rowStart + 17, shift, 3);
|
|
break;
|
|
|
|
default:
|
|
throw std::runtime_error("unsupported format");
|
|
}
|
|
}
|
|
|
|
void PpuTools::UpdateViewers(uint16_t scanline, uint16_t cycle)
|
|
{
|
|
for(auto updateTiming : _updateTimings) {
|
|
ViewerRefreshConfig cfg = updateTiming.second;
|
|
if(cfg.Cycle == cycle && cfg.Scanline == scanline) {
|
|
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::ViewerRefresh, (void*)(uint64_t)updateTiming.first);
|
|
}
|
|
}
|
|
}
|