Play-/Source/gs/GSHandler.cpp
2018-05-31 12:41:10 -04:00

1684 lines
42 KiB
C++

#include <stdio.h>
#include <string.h>
#include <functional>
#include <boost/scoped_array.hpp>
#include "../AppConfig.h"
#include "../Log.h"
#include "../MemoryStateFile.h"
#include "../RegisterStateFile.h"
#include "../FrameDump.h"
#include "GSHandler.h"
#include "GsPixelFormats.h"
#include "string_format.h"
#define R_REG(a, v, r) \
if((a)&0x4) \
{ \
v = (uint32)(r >> 32); \
} \
else \
{ \
v = (uint32)(r & 0xFFFFFFFF); \
}
#define W_REG(a, v, r) \
if((a)&0x4) \
{ \
(r) &= 0x00000000FFFFFFFFULL; \
(r) |= (uint64)(v) << 32; \
} \
else \
{ \
(r) &= 0xFFFFFFFF00000000ULL; \
(r) |= (v); \
}
#define STATE_RAM ("gs/ram")
#define STATE_REGS ("gs/regs")
#define STATE_TRXCTX ("gs/trxcontext")
#define STATE_PRIVREGS ("gs/privregs.xml")
#define STATE_PRIVREGS_PMODE ("PMODE")
#define STATE_PRIVREGS_SMODE2 ("SMODE2")
#define STATE_PRIVREGS_DISPFB1 ("DISPFB1")
#define STATE_PRIVREGS_DISPLAY1 ("DISPLAY1")
#define STATE_PRIVREGS_DISPFB2 ("DISPFB2")
#define STATE_PRIVREGS_DISPLAY2 ("DISPLAY2")
#define STATE_PRIVREGS_CSR ("CSR")
#define STATE_PRIVREGS_IMR ("IMR")
#define STATE_PRIVREGS_SIGLBLID ("SIGLBLID")
#define STATE_PRIVREGS_CRTMODE ("CrtMode")
#define LOG_NAME ("gs")
struct MASSIVEWRITE_INFO
{
#ifdef DEBUGGER_INCLUDED
CGsPacketMetadata metadata;
#endif
CGSHandler::RegisterWriteList writes;
};
CGSHandler::CGSHandler()
: m_threadDone(false)
, m_drawCallCount(0)
, m_pCLUT(nullptr)
, m_pRAM(nullptr)
, m_frameDump(nullptr)
, m_loggingEnabled(true)
{
RegisterPreferences();
m_presentationParams.mode = static_cast<PRESENTATION_MODE>(CAppConfig::GetInstance().GetPreferenceInteger(PREF_CGSHANDLER_PRESENTATION_MODE));
m_presentationParams.windowWidth = 512;
m_presentationParams.windowHeight = 384;
m_pRAM = new uint8[RAMSIZE];
m_pCLUT = new uint16[CLUTENTRYCOUNT];
for(int i = 0; i < PSM_MAX; i++)
{
m_transferWriteHandlers[i] = &CGSHandler::TransferWriteHandlerInvalid;
m_transferReadHandlers[i] = &CGSHandler::TransferReadHandlerInvalid;
}
m_transferWriteHandlers[PSMCT32] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMCT32>;
m_transferWriteHandlers[PSMCT24] = &CGSHandler::TransferWriteHandlerPSMCT24;
m_transferWriteHandlers[PSMCT16] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMCT16>;
m_transferWriteHandlers[PSMCT16S] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMCT16S>;
m_transferWriteHandlers[PSMT8] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMT8>;
m_transferWriteHandlers[PSMT4] = &CGSHandler::TransferWriteHandlerPSMT4;
m_transferWriteHandlers[PSMT8H] = &CGSHandler::TransferWriteHandlerPSMT8H;
m_transferWriteHandlers[PSMT4HL] = &CGSHandler::TransferWriteHandlerPSMT4H<24, 0x0F000000>;
m_transferWriteHandlers[PSMT4HH] = &CGSHandler::TransferWriteHandlerPSMT4H<28, 0xF0000000>;
m_transferReadHandlers[PSMCT32] = &CGSHandler::TransferReadHandlerGeneric<CGsPixelFormats::STORAGEPSMCT32>;
m_transferReadHandlers[PSMT8] = &CGSHandler::TransferReadHandlerGeneric<CGsPixelFormats::STORAGEPSMT8>;
ResetBase();
m_thread = std::thread([&]() { ThreadProc(); });
}
CGSHandler::~CGSHandler()
{
m_threadDone = true;
m_thread.join();
delete[] m_pRAM;
delete[] m_pCLUT;
}
void CGSHandler::RegisterPreferences()
{
CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_CGSHANDLER_PRESENTATION_MODE, CGSHandler::PRESENTATION_MODE_FIT);
}
void CGSHandler::NotifyPreferencesChanged()
{
m_mailBox.SendCall([this]() { NotifyPreferencesChangedImpl(); });
}
void CGSHandler::Reset()
{
ResetBase();
m_mailBox.SendCall(std::bind(&CGSHandler::ResetImpl, this), true);
}
void CGSHandler::ResetBase()
{
memset(m_nReg, 0, sizeof(uint64) * 0x80);
m_nReg[GS_REG_PRMODECONT] = 1;
memset(m_pRAM, 0, RAMSIZE);
memset(m_pCLUT, 0, CLUTSIZE);
m_nPMODE = 0;
m_nSMODE2 = 0;
m_nDISPFB1.heldValue = 0;
m_nDISPFB1.value.q = 0;
m_nDISPLAY1.heldValue = 0;
m_nDISPLAY1.value.q = 0;
m_nDISPFB2.heldValue = 0;
m_nDISPFB2.value.q = 0;
m_nDISPLAY2.heldValue = 0;
m_nDISPLAY2.value.q = 0;
m_nCSR = CSR_FIFO_EMPTY;
m_nIMR = 0;
m_nSIGLBLID = 0;
m_nCrtMode = 2;
m_nCBP0 = 0;
m_nCBP1 = 0;
m_transferCount = 0;
}
void CGSHandler::ResetImpl()
{
}
void CGSHandler::NotifyPreferencesChangedImpl()
{
}
void CGSHandler::SetPresentationParams(const PRESENTATION_PARAMS& presentationParams)
{
m_presentationParams = presentationParams;
}
void CGSHandler::SaveState(Framework::CZipArchiveWriter& archive)
{
archive.InsertFile(new CMemoryStateFile(STATE_RAM, m_pRAM, RAMSIZE));
archive.InsertFile(new CMemoryStateFile(STATE_REGS, m_nReg, sizeof(uint64) * CGSHandler::REGISTER_MAX));
archive.InsertFile(new CMemoryStateFile(STATE_TRXCTX, &m_trxCtx, sizeof(TRXCONTEXT)));
{
CRegisterStateFile* registerFile = new CRegisterStateFile(STATE_PRIVREGS);
registerFile->SetRegister64(STATE_PRIVREGS_PMODE, m_nPMODE);
registerFile->SetRegister64(STATE_PRIVREGS_SMODE2, m_nSMODE2);
registerFile->SetRegister64(STATE_PRIVREGS_DISPFB1, m_nDISPFB1.value.q);
registerFile->SetRegister64(STATE_PRIVREGS_DISPLAY1, m_nDISPLAY1.value.q);
registerFile->SetRegister64(STATE_PRIVREGS_DISPFB2, m_nDISPFB2.value.q);
registerFile->SetRegister64(STATE_PRIVREGS_DISPLAY2, m_nDISPLAY2.value.q);
registerFile->SetRegister64(STATE_PRIVREGS_CSR, m_nCSR);
registerFile->SetRegister64(STATE_PRIVREGS_IMR, m_nIMR);
registerFile->SetRegister64(STATE_PRIVREGS_SIGLBLID, m_nSIGLBLID);
registerFile->SetRegister32(STATE_PRIVREGS_CRTMODE, m_nCrtMode);
archive.InsertFile(registerFile);
}
}
void CGSHandler::LoadState(Framework::CZipArchiveReader& archive)
{
archive.BeginReadFile(STATE_RAM)->Read(m_pRAM, RAMSIZE);
archive.BeginReadFile(STATE_REGS)->Read(m_nReg, sizeof(uint64) * CGSHandler::REGISTER_MAX);
archive.BeginReadFile(STATE_TRXCTX)->Read(&m_trxCtx, sizeof(TRXCONTEXT));
{
CRegisterStateFile registerFile(*archive.BeginReadFile(STATE_PRIVREGS));
m_nPMODE = registerFile.GetRegister64(STATE_PRIVREGS_PMODE);
m_nSMODE2 = registerFile.GetRegister64(STATE_PRIVREGS_SMODE2);
m_nDISPFB1.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPFB1);
m_nDISPLAY1.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPLAY1);
m_nDISPFB2.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPFB2);
m_nDISPLAY2.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPLAY2);
m_nCSR = registerFile.GetRegister64(STATE_PRIVREGS_CSR);
m_nIMR = registerFile.GetRegister64(STATE_PRIVREGS_IMR);
m_nSIGLBLID = registerFile.GetRegister64(STATE_PRIVREGS_SIGLBLID);
m_nCrtMode = registerFile.GetRegister32(STATE_PRIVREGS_CRTMODE);
}
}
void CGSHandler::SetFrameDump(CFrameDump* frameDump)
{
m_frameDump = frameDump;
}
bool CGSHandler::GetDrawEnabled() const
{
return m_drawEnabled;
}
void CGSHandler::SetDrawEnabled(bool drawEnabled)
{
m_drawEnabled = drawEnabled;
}
void CGSHandler::SetVBlank()
{
{
Flip();
}
std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
m_nCSR |= CSR_VSYNC_INT;
}
void CGSHandler::ResetVBlank()
{
std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
//Alternate current field
m_nCSR ^= CSR_FIELD;
}
int CGSHandler::GetPendingTransferCount() const
{
return m_transferCount;
}
bool CGSHandler::IsInterruptPending()
{
uint32 mask = (~m_nIMR >> 8) & 0x1F;
return (m_nCSR & mask) != 0;
}
uint32 CGSHandler::ReadPrivRegister(uint32 nAddress)
{
uint32 nData = 0;
switch(nAddress & ~0x0F)
{
case GS_CSR:
//Force CSR to have the H-Blank bit set.
{
std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
m_nCSR |= CSR_HSYNC_INT;
R_REG(nAddress, nData, m_nCSR);
}
break;
case GS_IMR:
R_REG(nAddress, nData, m_nIMR);
break;
case GS_SIGLBLID:
R_REG(nAddress, nData, m_nSIGLBLID);
break;
default:
CLog::GetInstance().Warn(LOG_NAME, "Read an unhandled priviledged register (0x%08X).\r\n", nAddress);
nData = 0xCCCCCCCC;
break;
}
return nData;
}
void CGSHandler::WritePrivRegister(uint32 nAddress, uint32 nData)
{
switch(nAddress & ~0x0F)
{
case GS_PMODE:
W_REG(nAddress, nData, m_nPMODE);
if(!(nAddress & 0x4))
{
if((m_nPMODE & 0x01) && (m_nPMODE & 0x02))
{
CLog::GetInstance().Print(LOG_NAME, "Warning. Both read circuits were enabled. Using RC1 for display.\r\n");
// m_nPMODE &= ~0x02;
}
}
break;
case GS_SMODE2:
W_REG(nAddress, nData, m_nSMODE2);
break;
case GS_DISPFB1:
WriteToDelayedRegister(nAddress, nData, m_nDISPFB1);
break;
case GS_DISPLAY1:
WriteToDelayedRegister(nAddress, nData, m_nDISPLAY1);
break;
case GS_DISPFB2:
WriteToDelayedRegister(nAddress, nData, m_nDISPFB2);
break;
case GS_DISPLAY2:
WriteToDelayedRegister(nAddress, nData, m_nDISPLAY2);
break;
case GS_CSR:
{
if(!(nAddress & 0x04))
{
std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
if(nData & CSR_SIGNAL_EVENT)
{
m_nCSR &= ~CSR_SIGNAL_EVENT;
}
if(nData & CSR_FINISH_EVENT)
{
m_nCSR &= ~CSR_FINISH_EVENT;
}
if(nData & CSR_VSYNC_INT)
{
m_nCSR &= ~CSR_VSYNC_INT;
}
if(nData & CSR_RESET)
{
m_nCSR |= CSR_RESET;
}
}
}
break;
case GS_IMR:
W_REG(nAddress, nData, m_nIMR);
break;
case GS_SIGLBLID:
W_REG(nAddress, nData, m_nSIGLBLID);
break;
default:
CLog::GetInstance().Warn(LOG_NAME, "Wrote to an unhandled priviledged register (0x%08X, 0x%08X).\r\n", nAddress, nData);
break;
}
#ifdef _DEBUG
if(nAddress & 0x04)
{
LogPrivateWrite(nAddress);
}
#endif
}
void CGSHandler::Initialize()
{
m_mailBox.SendCall(std::bind(&CGSHandler::InitializeImpl, this), true);
}
void CGSHandler::Release()
{
m_mailBox.SendCall(std::bind(&CGSHandler::ReleaseImpl, this), true);
}
void CGSHandler::Flip(bool showOnly)
{
if(!showOnly)
{
m_mailBox.FlushCalls();
m_mailBox.SendCall(std::bind(&CGSHandler::MarkNewFrame, this));
}
m_mailBox.SendCall(std::bind(&CGSHandler::FlipImpl, this), true);
}
void CGSHandler::FlipImpl()
{
OnFlipComplete();
}
void CGSHandler::MarkNewFrame()
{
OnNewFrame(m_drawCallCount);
m_drawCallCount = 0;
#ifdef _DEBUG
CLog::GetInstance().Print(LOG_NAME, "Frame Done.\r\n---------------------------------------------------------------------------------\r\n");
#endif
}
uint8* CGSHandler::GetRam()
{
return m_pRAM;
}
uint64* CGSHandler::GetRegisters()
{
return m_nReg;
}
uint64 CGSHandler::GetSMODE2() const
{
return m_nSMODE2;
}
void CGSHandler::SetSMODE2(uint64 value)
{
m_nSMODE2 = value;
}
void CGSHandler::WriteRegister(uint8 registerId, uint64 value)
{
m_mailBox.SendCall(std::bind(&CGSHandler::WriteRegisterImpl, this, registerId, value));
}
void CGSHandler::FeedImageData(const void* data, uint32 length)
{
m_transferCount++;
//Allocate 0x10 more bytes to allow transfer handlers
//to read beyond the actual length of the buffer (ie.: PSMCT24)
uint8* buffer = new uint8[length + 0x10];
memcpy(buffer, data, length);
m_mailBox.SendCall(std::bind(&CGSHandler::FeedImageDataImpl, this, buffer, length));
}
void CGSHandler::ReadImageData(void* data, uint32 length)
{
m_mailBox.SendCall([this, data, length]() { ReadImageDataImpl(data, length); }, true);
}
void CGSHandler::WriteRegisterMassively(RegisterWriteList registerWrites, const CGsPacketMetadata* metadata)
{
for(const auto& write : registerWrites)
{
switch(write.first)
{
case GS_REG_SIGNAL:
{
auto signal = make_convertible<SIGNAL>(write.second);
auto siglblid = make_convertible<SIGLBLID>(m_nSIGLBLID);
siglblid.sigid &= ~signal.idmsk;
siglblid.sigid |= signal.id;
m_nSIGLBLID = siglblid;
assert((m_nCSR & CSR_SIGNAL_EVENT) == 0);
m_nCSR |= CSR_SIGNAL_EVENT;
}
break;
case GS_REG_FINISH:
m_nCSR |= CSR_FINISH_EVENT;
break;
case GS_REG_LABEL:
{
auto label = make_convertible<LABEL>(write.second);
auto siglblid = make_convertible<SIGLBLID>(m_nSIGLBLID);
siglblid.lblid &= ~label.idmsk;
siglblid.lblid |= label.id;
m_nSIGLBLID = siglblid;
}
break;
}
}
m_transferCount++;
MASSIVEWRITE_INFO massiveWrite;
massiveWrite.writes = std::move(registerWrites);
#ifdef DEBUGGER_INCLUDED
if(metadata != nullptr)
{
memcpy(&massiveWrite.metadata, metadata, sizeof(CGsPacketMetadata));
}
else
{
massiveWrite.metadata = CGsPacketMetadata();
}
#endif
m_mailBox.SendCall(
[this, massiveWrite = std::move(massiveWrite)]() {
WriteRegisterMassivelyImpl(massiveWrite);
});
}
void CGSHandler::WriteRegisterImpl(uint8 nRegister, uint64 nData)
{
assert(nRegister < REGISTER_MAX);
if(nRegister < REGISTER_MAX)
{
m_nReg[nRegister] = nData;
}
switch(nRegister)
{
case GS_REG_TEX0_1:
case GS_REG_TEX0_2:
{
unsigned int nContext = nRegister - GS_REG_TEX0_1;
assert(nContext == 0 || nContext == 1);
auto tex0 = make_convertible<TEX0>(m_nReg[GS_REG_TEX0_1 + nContext]);
SyncCLUT(tex0);
}
break;
case GS_REG_TEX2_1:
case GS_REG_TEX2_2:
{
//Update TEX0
unsigned int nContext = nRegister - GS_REG_TEX2_1;
assert(nContext == 0 || nContext == 1);
const uint64 nMask = 0xFFFFFFE003F00000ULL;
m_nReg[GS_REG_TEX0_1 + nContext] &= ~nMask;
m_nReg[GS_REG_TEX0_1 + nContext] |= nData & nMask;
auto tex0 = make_convertible<TEX0>(m_nReg[GS_REG_TEX0_1 + nContext]);
SyncCLUT(tex0);
}
break;
case GS_REG_TRXDIR:
BeginTransfer();
break;
}
#ifdef _DEBUG
LogWrite(nRegister, nData);
#endif
}
void CGSHandler::FeedImageDataImpl(const void* pData, uint32 nLength)
{
boost::scoped_array<const uint8> dataPtr(reinterpret_cast<const uint8*>(pData));
#ifdef DEBUGGER_INCLUDED
if(m_frameDump)
{
m_frameDump->AddImagePacket(reinterpret_cast<const uint8*>(pData), nLength);
}
#endif
if(m_trxCtx.nSize == 0)
{
#ifdef _DEBUG
CLog::GetInstance().Warn(LOG_NAME, "Warning. Received image data when no transfer was expected.\r\n");
#endif
}
else
{
if(m_trxCtx.nSize < nLength)
{
nLength = m_trxCtx.nSize;
//assert(0);
//return;
}
auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
m_trxCtx.nDirty |= ((this)->*(m_transferWriteHandlers[bltBuf.nDstPsm]))(pData, nLength);
m_trxCtx.nSize -= nLength;
if(m_trxCtx.nSize == 0)
{
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
//assert(m_trxCtx.nRRY == trxReg.nRRH);
ProcessHostToLocalTransfer();
#ifdef _DEBUG
CLog::GetInstance().Print(LOG_NAME, "Completed image transfer at 0x%08X (dirty = %d).\r\n", bltBuf.GetDstPtr(), m_trxCtx.nDirty);
#endif
}
}
assert(m_transferCount != 0);
m_transferCount--;
}
void CGSHandler::ReadImageDataImpl(void* ptr, uint32 size)
{
assert(m_trxCtx.nSize != 0);
assert(m_trxCtx.nSize == size);
auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
assert(trxPos.nDIR == 0);
((this)->*(m_transferReadHandlers[bltBuf.nSrcPsm]))(ptr, size);
}
void CGSHandler::WriteRegisterMassivelyImpl(const MASSIVEWRITE_INFO& massiveWrite)
{
#ifdef DEBUGGER_INCLUDED
if(m_frameDump)
{
m_frameDump->AddRegisterPacket(massiveWrite.writes.data(), massiveWrite.writes.size(), &massiveWrite.metadata);
}
#endif
for(const auto& write : massiveWrite.writes)
{
WriteRegisterImpl(write.first, write.second);
}
assert(m_transferCount != 0);
m_transferCount--;
}
void CGSHandler::BeginTransfer()
{
uint32 trxDir = m_nReg[GS_REG_TRXDIR] & 0x03;
if(trxDir == 0 || trxDir == 1)
{
//"Host to Local" or "Local to Host"
auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto psm = (trxDir == 0) ? bltBuf.nDstPsm : bltBuf.nSrcPsm;
unsigned int nPixelSize = 0;
//We need to figure out the pixel size of the stream
switch(psm)
{
case PSMCT32:
nPixelSize = 32;
break;
case PSMCT24:
case PSMZ24:
nPixelSize = 24;
break;
case PSMCT16:
case PSMCT16S:
nPixelSize = 16;
break;
case PSMT8:
case PSMT8H:
nPixelSize = 8;
break;
case PSMT4:
case PSMT4HH:
case PSMT4HL:
nPixelSize = 4;
break;
default:
assert(0);
break;
}
//Make sure transfer size is a multiple of 16. Some games (ex.: Gregory Horror Show)
//specify transfer width/height that will give a transfer size that is not a multiple of 16
//and will prevent proper handling of image transfer (texture cache will not be invalidated).
m_trxCtx.nSize = ((trxReg.nRRW * trxReg.nRRH * nPixelSize) / 8) & ~0xF;
m_trxCtx.nRealSize = m_trxCtx.nSize;
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY = 0;
m_trxCtx.nDirty = false;
if(trxDir == 0)
{
CLog::GetInstance().Print(LOG_NAME, "Starting transfer to 0x%08X, buffer size %d, psm: %d, size (%dx%d)\r\n",
bltBuf.GetDstPtr(), bltBuf.GetDstWidth(), bltBuf.nDstPsm, trxReg.nRRW, trxReg.nRRH);
}
else if(trxDir == 1)
{
ProcessLocalToHostTransfer();
CLog::GetInstance().Print(LOG_NAME, "Starting transfer from 0x%08X, buffer size %d, psm: %d, size (%dx%d)\r\n",
bltBuf.GetSrcPtr(), bltBuf.GetSrcWidth(), bltBuf.nSrcPsm, trxReg.nRRW, trxReg.nRRH);
}
}
else if(trxDir == 2)
{
//Local to Local
ProcessLocalToLocalTransfer();
}
}
bool CGSHandler::TransferWriteHandlerInvalid(const void* pData, uint32 nLength)
{
assert(0);
return false;
}
template <typename Storage>
bool CGSHandler::TransferWriteHandlerGeneric(const void* pData, uint32 nLength)
{
bool nDirty = false;
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
nLength /= sizeof(typename Storage::Unit);
CGsPixelFormats::CPixelIndexor<Storage> Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
auto pSrc = reinterpret_cast<const typename Storage::Unit*>(pData);
for(unsigned int i = 0; i < nLength; i++)
{
uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
auto pPixel = Indexor.GetPixelAddress(nX, nY);
if((*pPixel) != pSrc[i])
{
(*pPixel) = pSrc[i];
nDirty = true;
}
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
}
return nDirty;
}
bool CGSHandler::TransferWriteHandlerPSMCT24(const void* pData, uint32 nLength)
{
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
auto pSrc = reinterpret_cast<const uint8*>(pData);
for(unsigned int i = 0; i < nLength; i += 3)
{
uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
uint32* pDstPixel = Indexor.GetPixelAddress(nX, nY);
uint32 nSrcPixel = *reinterpret_cast<const uint32*>(&pSrc[i]) & 0x00FFFFFF;
(*pDstPixel) &= 0xFF000000;
(*pDstPixel) |= nSrcPixel;
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
}
return true;
}
bool CGSHandler::TransferWriteHandlerPSMT4(const void* pData, uint32 nLength)
{
bool dirty = false;
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
CGsPixelFormats::CPixelIndexorPSMT4 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
auto pSrc = reinterpret_cast<const uint8*>(pData);
for(unsigned int i = 0; i < nLength; i++)
{
uint8 nPixel[2];
nPixel[0] = (pSrc[i] >> 0) & 0x0F;
nPixel[1] = (pSrc[i] >> 4) & 0x0F;
for(unsigned int j = 0; j < 2; j++)
{
uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
uint8 currentPixel = Indexor.GetPixel(nX, nY);
if(currentPixel != nPixel[j])
{
Indexor.SetPixel(nX, nY, nPixel[j]);
dirty = true;
}
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
}
}
return dirty;
}
template <uint32 nShift, uint32 nMask>
bool CGSHandler::TransferWriteHandlerPSMT4H(const void* pData, uint32 nLength)
{
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
auto pSrc = reinterpret_cast<const uint8*>(pData);
for(unsigned int i = 0; i < nLength; i++)
{
//Pixel 1
uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
uint8 nSrcPixel = pSrc[i] & 0x0F;
uint32* pDstPixel = Indexor.GetPixelAddress(nX, nY);
(*pDstPixel) &= ~nMask;
(*pDstPixel) |= (nSrcPixel << nShift);
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
//Pixel 2
nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
nSrcPixel = (pSrc[i] & 0xF0);
pDstPixel = Indexor.GetPixelAddress(nX, nY);
(*pDstPixel) &= ~nMask;
(*pDstPixel) |= (nSrcPixel << (nShift - 4));
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
}
return true;
}
bool CGSHandler::TransferWriteHandlerPSMT8H(const void* pData, uint32 nLength)
{
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
auto pSrc = reinterpret_cast<const uint8*>(pData);
for(unsigned int i = 0; i < nLength; i++)
{
uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
uint8 nSrcPixel = pSrc[i];
uint32* pDstPixel = Indexor.GetPixelAddress(nX, nY);
(*pDstPixel) &= ~0xFF000000;
(*pDstPixel) |= (nSrcPixel << 24);
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
}
return true;
}
void CGSHandler::TransferReadHandlerInvalid(void*, uint32)
{
assert(0);
}
template <typename Storage>
void CGSHandler::TransferReadHandlerGeneric(void* buffer, uint32 length)
{
auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
uint32 typedLength = length / sizeof(typename Storage::Unit);
auto typedBuffer = reinterpret_cast<typename Storage::Unit*>(buffer);
CGsPixelFormats::CPixelIndexor<Storage> indexor(m_pRAM, trxBuf.GetSrcPtr(), trxBuf.nSrcWidth);
for(uint32 i = 0; i < typedLength; i++)
{
uint32 x = (m_trxCtx.nRRX + trxPos.nSSAX) % 2048;
uint32 y = (m_trxCtx.nRRY + trxPos.nSSAY) % 2048;
auto pixel = indexor.GetPixel(x, y);
typedBuffer[i] = pixel;
m_trxCtx.nRRX++;
if(m_trxCtx.nRRX == trxReg.nRRW)
{
m_trxCtx.nRRX = 0;
m_trxCtx.nRRY++;
}
}
}
void CGSHandler::SetCrt(bool nIsInterlaced, unsigned int nMode, bool nIsFrameMode)
{
m_nCrtMode = nMode;
SMODE2 smode2;
smode2 <<= 0;
smode2.interlaced = nIsInterlaced ? 1 : 0;
smode2.ffmd = nIsFrameMode ? 1 : 0;
m_nSMODE2 = smode2;
}
unsigned int CGSHandler::GetCrtWidth() const
{
switch(m_nCrtMode)
{
case 0x02:
case 0x03:
case 0x1C:
return 640;
break;
default:
assert(0);
return 640;
break;
}
}
unsigned int CGSHandler::GetCrtHeight() const
{
switch(m_nCrtMode)
{
case 0x02:
return 448;
break;
case 0x03:
return 512;
break;
case 0x1C:
return 480;
break;
default:
assert(0);
return 448;
break;
}
}
bool CGSHandler::GetCrtIsInterlaced() const
{
SMODE2 smode2;
smode2 <<= m_nSMODE2;
return smode2.interlaced;
}
bool CGSHandler::GetCrtIsFrameMode() const
{
SMODE2 smode2;
smode2 <<= m_nSMODE2;
return smode2.ffmd;
}
void CGSHandler::SyncCLUT(const TEX0& tex0)
{
//Sync clut
if(tex0.nCLD != 0)
{
//assert(IsPsmIDTEX(tex0.nPsm));
switch(tex0.nPsm)
{
case PSMT8:
case PSMT8H:
ReadCLUT8(tex0);
break;
case PSMT4:
case PSMT4HH:
case PSMT4HL:
ReadCLUT4(tex0);
break;
}
}
}
template <typename Indexor>
bool CGSHandler::ReadCLUT4_16(const TEX0& tex0)
{
bool changed = false;
assert(tex0.nCSA < 32);
Indexor indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
uint32 clutOffset = tex0.nCSA * 16;
uint16* pDst = m_pCLUT + clutOffset;
for(unsigned int j = 0; j < 2; j++)
{
for(unsigned int i = 0; i < 8; i++)
{
uint16 color = indexor.GetPixel(i, j);
if(*pDst != color)
{
changed = true;
}
(*pDst++) = color;
}
}
return changed;
}
template <typename Indexor>
bool CGSHandler::ReadCLUT8_16(const TEX0& tex0)
{
bool changed = false;
Indexor indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
for(unsigned int j = 0; j < 16; j++)
{
for(unsigned int i = 0; i < 16; i++)
{
uint16 color = indexor.GetPixel(i, j);
uint8 index = i + (j * 16);
index = (index & ~0x18) | ((index & 0x08) << 1) | ((index & 0x10) >> 1);
if(m_pCLUT[index] != color)
{
changed = true;
}
m_pCLUT[index] = color;
}
}
return changed;
}
void CGSHandler::ReadCLUT4(const TEX0& tex0)
{
bool updateNeeded = false;
if(tex0.nCLD == 0)
{
//No changes to CLUT
}
else if(tex0.nCLD == 1)
{
updateNeeded = true;
}
else if(tex0.nCLD == 2)
{
m_nCBP0 = tex0.nCBP;
updateNeeded = true;
}
else if(tex0.nCLD == 3)
{
m_nCBP1 = tex0.nCBP;
updateNeeded = true;
}
else if(tex0.nCLD == 4)
{
updateNeeded = (m_nCBP0 != tex0.nCBP);
m_nCBP0 = tex0.nCBP;
}
else
{
updateNeeded = true;
assert(0);
}
if(updateNeeded)
{
bool changed = false;
if(tex0.nCSM == 0)
{
//CSM1 mode
if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
{
assert(tex0.nCSA < 16);
CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
uint32 clutOffset = (tex0.nCSA & 0x0F) * 16;
uint16* pDst = m_pCLUT + clutOffset;
for(unsigned int j = 0; j < 2; j++)
{
for(unsigned int i = 0; i < 8; i++)
{
uint32 color = Indexor.GetPixel(i, j);
uint16 colorLo = static_cast<uint16>(color & 0xFFFF);
uint16 colorHi = static_cast<uint16>(color >> 16);
if(
(pDst[0x000] != colorLo) ||
(pDst[0x100] != colorHi))
{
changed = true;
}
pDst[0x000] = colorLo;
pDst[0x100] = colorHi;
pDst++;
}
}
}
else if(tex0.nCPSM == PSMCT16)
{
changed = ReadCLUT4_16<CGsPixelFormats::CPixelIndexorPSMCT16>(tex0);
}
else if(tex0.nCPSM == PSMCT16S)
{
changed = ReadCLUT4_16<CGsPixelFormats::CPixelIndexorPSMCT16S>(tex0);
}
else
{
assert(0);
}
}
else
{
//CSM2 mode
assert(tex0.nCPSM == PSMCT16);
assert(tex0.nCSA == 0);
auto texClut = make_convertible<TEXCLUT>(m_nReg[GS_REG_TEXCLUT]);
CGsPixelFormats::CPixelIndexorPSMCT16 Indexor(m_pRAM, tex0.GetCLUTPtr(), texClut.nCBW);
unsigned int nOffsetX = texClut.GetOffsetU();
unsigned int nOffsetY = texClut.GetOffsetV();
uint16* pDst = m_pCLUT;
for(unsigned int i = 0; i < 0x10; i++)
{
uint16 color = Indexor.GetPixel(nOffsetX + i, nOffsetY);
if(*pDst != color)
{
changed = true;
}
(*pDst++) = color;
}
}
if(changed)
{
ProcessClutTransfer(tex0.nCSA, 0);
}
}
}
void CGSHandler::ReadCLUT8(const TEX0& tex0)
{
assert(tex0.nCSA == 0);
assert(tex0.nCSM == 0);
bool updateNeeded = false;
if(tex0.nCLD == 0)
{
//No changes to CLUT
}
else if(tex0.nCLD == 1)
{
updateNeeded = true;
}
else if(tex0.nCLD == 2)
{
m_nCBP0 = tex0.nCBP;
updateNeeded = true;
}
else if(tex0.nCLD == 3)
{
m_nCBP1 = tex0.nCBP;
updateNeeded = true;
}
else if(tex0.nCLD == 4)
{
updateNeeded = m_nCBP0 != tex0.nCBP;
m_nCBP0 = tex0.nCBP;
}
else
{
updateNeeded = true;
assert(0);
}
if(updateNeeded)
{
bool changed = false;
if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
{
CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
for(unsigned int j = 0; j < 16; j++)
{
for(unsigned int i = 0; i < 16; i++)
{
uint32 color = Indexor.GetPixel(i, j);
uint16 colorLo = static_cast<uint16>(color & 0xFFFF);
uint16 colorHi = static_cast<uint16>(color >> 16);
uint8 index = i + (j * 16);
index = (index & ~0x18) | ((index & 0x08) << 1) | ((index & 0x10) >> 1);
if(
(m_pCLUT[index + 0x000] != colorLo) ||
(m_pCLUT[index + 0x100] != colorHi))
{
changed = true;
}
m_pCLUT[index + 0x000] = colorLo;
m_pCLUT[index + 0x100] = colorHi;
}
}
}
else if(tex0.nCPSM == PSMCT16)
{
changed = ReadCLUT8_16<CGsPixelFormats::CPixelIndexorPSMCT16>(tex0);
}
else if(tex0.nCPSM == PSMCT16S)
{
changed = ReadCLUT8_16<CGsPixelFormats::CPixelIndexorPSMCT16S>(tex0);
}
else
{
assert(0);
}
if(changed)
{
ProcessClutTransfer(tex0.nCSA, 0);
}
}
}
bool CGSHandler::IsCompatibleFramebufferPSM(unsigned int psmFb, unsigned int psmTex)
{
if((psmTex == CGSHandler::PSMCT32) || (psmTex == CGSHandler::PSMCT24))
{
return (psmFb == CGSHandler::PSMCT32) || (psmFb == CGSHandler::PSMCT24);
}
else
{
return (psmFb == psmTex);
}
}
void CGSHandler::MakeLinearCLUT(const TEX0& tex0, std::array<uint32, 256>& clut) const
{
static const auto RGBA16ToRGBA32 =
[](uint16 color) {
return ((color & 0x8000) ? 0xFF000000 : 0) | ((color & 0x7C00) << 9) | ((color & 0x03E0) << 6) | ((color & 0x001F) << 3);
};
unsigned int entryCount = CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm) ? 16 : 256;
if(CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm))
{
if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
{
assert(tex0.nCSA < 16);
uint32 clutOffset = (tex0.nCSA & 0xF) * 16;
for(unsigned int i = 0; i < 16; i++)
{
uint32 color =
(static_cast<uint16>(m_pCLUT[i + clutOffset + 0x000])) |
(static_cast<uint16>(m_pCLUT[i + clutOffset + 0x100]) << 16);
clut[i] = color;
}
}
else if(tex0.nCPSM == PSMCT16 || tex0.nCPSM == PSMCT16S)
{
//CSA is 5-bit, shouldn't go over 31
assert(tex0.nCSA < 32);
uint32 clutOffset = tex0.nCSA * 16;
for(unsigned int i = 0; i < 16; i++)
{
clut[i] = RGBA16ToRGBA32(m_pCLUT[i + clutOffset]);
}
}
else
{
assert(false);
}
}
else if(CGsPixelFormats::IsPsmIDTEX8(tex0.nPsm))
{
if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
{
for(unsigned int i = 0; i < 256; i++)
{
uint32 offset = ((tex0.nCSA * 16) + i) & 0xFF;
uint32 color =
(static_cast<uint16>(m_pCLUT[offset + 0x000])) |
(static_cast<uint16>(m_pCLUT[offset + 0x100]) << 16);
clut[i] = color;
}
}
else if(tex0.nCPSM == PSMCT16 || tex0.nCPSM == PSMCT16S)
{
assert(tex0.nCSA == 0);
for(unsigned int i = 0; i < 256; i++)
{
clut[i] = RGBA16ToRGBA32(m_pCLUT[i]);
}
}
else
{
assert(false);
}
}
}
void CGSHandler::SetLoggingEnabled(bool loggingEnabled)
{
m_loggingEnabled = loggingEnabled;
}
std::string CGSHandler::DisassembleWrite(uint8 registerId, uint64 data)
{
std::string result;
switch(registerId)
{
case GS_REG_PRIM:
{
auto pr = make_convertible<PRIM>(data);
result = string_format("PRIM(PRI: %i, IIP: %i, TME: %i, FGE: %i, ABE: %i, AA1: %i, FST: %i, CTXT: %i, FIX: %i)",
pr.nType, pr.nShading, pr.nTexture, pr.nFog, pr.nAlpha, pr.nAntiAliasing, pr.nUseUV, pr.nContext, pr.nUseFloat);
}
break;
case GS_REG_RGBAQ:
{
auto rgbaq = make_convertible<RGBAQ>(data);
result = string_format("RGBAQ(R: 0x%02X, G: 0x%02X, B: 0x%02X, A: 0x%02X, Q: %f)",
rgbaq.nR, rgbaq.nG, rgbaq.nB, rgbaq.nA, rgbaq.nQ);
}
break;
case GS_REG_ST:
{
auto st = make_convertible<ST>(data);
result = string_format("ST(S: %f, T: %f)",
st.nS, st.nT);
}
break;
case GS_REG_UV:
{
auto uv = make_convertible<UV>(data);
result = string_format("UV(U: %f, V: %f)",
uv.GetU(), uv.GetV());
}
break;
case GS_REG_XYZ2:
case GS_REG_XYZ3:
{
auto xyz = make_convertible<XYZ>(data);
result = string_format("%s(%f, %f, %f)",
(registerId == GS_REG_XYZ2) ? "XYZ2" : "XYZ3", xyz.GetX(), xyz.GetY(), xyz.GetZ());
}
break;
case GS_REG_XYZF2:
case GS_REG_XYZF3:
{
auto xyzf = make_convertible<XYZF>(data);
result = string_format("%s(%f, %f, %i, %i)",
(registerId == GS_REG_XYZF2) ? "XYZF2" : "XYZF3", xyzf.GetX(), xyzf.GetY(), xyzf.nZ, xyzf.nF);
}
break;
case GS_REG_FOG:
{
auto fog = static_cast<uint8>(data >> 56);
result = string_format("FOG(F: %d)", fog);
}
break;
case GS_REG_TEX0_1:
case GS_REG_TEX0_2:
{
auto tex0 = make_convertible<TEX0>(data);
result = string_format("TEX0_%i(TBP: 0x%08X, TBW: %i, PSM: %i, TW: %i, TH: %i, TCC: %i, TFX: %i, CBP: 0x%08X, CPSM: %i, CSM: %i, CSA: %i, CLD: %i)",
(registerId == GS_REG_TEX0_1) ? 1 : 2, tex0.GetBufPtr(), tex0.GetBufWidth(), tex0.nPsm, tex0.GetWidth(), tex0.GetHeight(), tex0.nColorComp,
tex0.nFunction, tex0.GetCLUTPtr(), tex0.nCPSM, tex0.nCSM, tex0.nCSA, tex0.nCLD);
}
break;
case GS_REG_CLAMP_1:
case GS_REG_CLAMP_2:
{
auto clamp = make_convertible<CLAMP>(data);
result = string_format("CLAMP_%i(WMS: %i, WMT: %i, MINU: %i, MAXU: %i, MINV: %i, MAXV: %i)",
(registerId == GS_REG_CLAMP_1 ? 1 : 2), clamp.nWMS, clamp.nWMT, clamp.GetMinU(), clamp.GetMaxU(), clamp.GetMinV(), clamp.GetMaxV());
}
break;
case GS_REG_TEX1_1:
case GS_REG_TEX1_2:
{
auto tex1 = make_convertible<TEX1>(data);
result = string_format("TEX1_%i(LCM: %i, MXL: %i, MMAG: %i, MMIN: %i, MTBA: %i, L: %i, K: %i)",
(registerId == GS_REG_TEX1_1) ? 1 : 2, tex1.nLODMethod, tex1.nMaxMip, tex1.nMagFilter, tex1.nMinFilter, tex1.nMipBaseAddr, tex1.nLODL, tex1.nLODK);
}
break;
case GS_REG_TEX2_1:
case GS_REG_TEX2_2:
{
auto tex2 = make_convertible<TEX2>(data);
result = string_format("TEX2_%i(PSM: %i, CBP: 0x%08X, CPSM: %i, CSM: %i, CSA: %i, CLD: %i)",
(registerId == GS_REG_TEX2_1) ? 1 : 2, tex2.nPsm, tex2.GetCLUTPtr(), tex2.nCPSM, tex2.nCSM, tex2.nCSA, tex2.nCLD);
}
break;
case GS_REG_XYOFFSET_1:
case GS_REG_XYOFFSET_2:
result = string_format("XYOFFSET_%i(%i, %i)",
(registerId == GS_REG_XYOFFSET_1) ? 1 : 2, static_cast<uint32>(data >> 0), static_cast<uint32>(data >> 32));
break;
case GS_REG_PRMODECONT:
result = string_format("PRMODECONT(AC: %i)", data & 1);
break;
case GS_REG_PRMODE:
{
auto prm = make_convertible<PRMODE>(data);
result = string_format("PRMODE(IIP: %i, TME: %i, FGE: %i, ABE: %i, AA1: %i, FST: %i, CTXT: %i, FIX: %i)",
prm.nShading, prm.nTexture, prm.nFog, prm.nAlpha, prm.nAntiAliasing, prm.nUseUV, prm.nContext, prm.nUseFloat);
}
break;
case GS_REG_TEXCLUT:
{
auto clut = make_convertible<TEXCLUT>(data);
result = string_format("TEXCLUT(CBW: %i, COU: %i, COV: %i)",
clut.nCBW, clut.nCOU, clut.nCOV);
}
break;
case GS_REG_MIPTBP1_1:
case GS_REG_MIPTBP1_2:
{
auto miptbp1 = make_convertible<MIPTBP1>(data);
result = string_format("MIPTBP1_%d(TBP1: 0x%08X, TBW1: %d, TBP2: 0x%08X, TBW2: %d, TBP3: 0x%08X, TBW3: %d)",
(registerId == GS_REG_MIPTBP1_1) ? 1 : 2,
miptbp1.GetTbp1(), miptbp1.GetTbw1(),
miptbp1.GetTbp2(), miptbp1.GetTbw2(),
miptbp1.GetTbp3(), miptbp1.GetTbw3());
}
break;
case GS_REG_MIPTBP2_1:
case GS_REG_MIPTBP2_2:
{
auto miptbp2 = make_convertible<MIPTBP2>(data);
result = string_format("MIPTBP2_%d(TBP4: 0x%08X, TBW4: %d, TBP5: 0x%08X, TBW5: %d, TBP6: 0x%08X, TBW6: %d)",
(registerId == GS_REG_MIPTBP2_1) ? 1 : 2,
miptbp2.GetTbp4(), miptbp2.GetTbw4(),
miptbp2.GetTbp5(), miptbp2.GetTbw5(),
miptbp2.GetTbp6(), miptbp2.GetTbw6());
}
break;
case GS_REG_TEXA:
{
auto texa = make_convertible<TEXA>(data);
result = string_format("TEXA(TA0: 0x%02X, AEM: %i, TA1: 0x%02X)",
texa.nTA0, texa.nAEM, texa.nTA1);
}
break;
case GS_REG_FOGCOL:
{
auto fogcol = make_convertible<FOGCOL>(data);
result = string_format("FOGCOL(R: 0x%02X, G: 0x%02X, B: 0x%02X)",
fogcol.nFCR, fogcol.nFCG, fogcol.nFCB);
}
break;
case GS_REG_TEXFLUSH:
result = "TEXFLUSH()";
break;
case GS_REG_ALPHA_1:
case GS_REG_ALPHA_2:
{
auto alpha = make_convertible<ALPHA>(data);
result = string_format("ALPHA_%i(A: %i, B: %i, C: %i, D: %i, FIX: 0x%02X)",
(registerId == GS_REG_ALPHA_1) ? 1 : 2, alpha.nA, alpha.nB, alpha.nC, alpha.nD, alpha.nFix);
}
break;
case GS_REG_SCISSOR_1:
case GS_REG_SCISSOR_2:
{
auto scissor = make_convertible<SCISSOR>(data);
result = string_format("SCISSOR_%i(SCAX0: %i, SCAX1: %i, SCAY0: %i, SCAY1: %i)",
(registerId == GS_REG_SCISSOR_1) ? 1 : 2, scissor.scax0, scissor.scax1, scissor.scay0, scissor.scay1);
}
break;
case GS_REG_COLCLAMP:
result = string_format("COLCLAMP(CLAMP: %d)", data & 1);
break;
case GS_REG_TEST_1:
case GS_REG_TEST_2:
{
auto tst = make_convertible<TEST>(data);
result = string_format("TEST_%i(ATE: %i, ATST: %i, AREF: 0x%02X, AFAIL: %i, DATE: %i, DATM: %i, ZTE: %i, ZTST: %i)",
(registerId == GS_REG_TEST_1) ? 1 : 2, tst.nAlphaEnabled, tst.nAlphaMethod, tst.nAlphaRef, tst.nAlphaFail,
tst.nDestAlphaEnabled, tst.nDestAlphaMode, tst.nDepthEnabled, tst.nDepthMethod);
}
break;
case GS_REG_PABE:
{
auto value = static_cast<uint8>(data & 1);
result = string_format("PABE(PABE: %d)", value);
}
break;
case GS_REG_FBA_1:
case GS_REG_FBA_2:
{
auto value = static_cast<uint8>(data & 1);
result = string_format("FBA_%d(FBA: %d)", (registerId == GS_REG_FBA_1) ? 1 : 2, value);
}
break;
case GS_REG_FRAME_1:
case GS_REG_FRAME_2:
{
auto fr = make_convertible<FRAME>(data);
result = string_format("FRAME_%i(FBP: 0x%08X, FBW: %d, PSM: %d, FBMSK: 0x%08X)",
(registerId == GS_REG_FRAME_1) ? 1 : 2, fr.GetBasePtr(), fr.GetWidth(), fr.nPsm, fr.nMask);
}
break;
case GS_REG_ZBUF_1:
case GS_REG_ZBUF_2:
{
auto zbuf = make_convertible<ZBUF>(data);
result = string_format("ZBUF_%i(ZBP: 0x%08X, PSM: %i, ZMSK: %i)",
(registerId == GS_REG_ZBUF_1) ? 1 : 2, zbuf.GetBasePtr(), zbuf.nPsm, zbuf.nMask);
}
break;
case GS_REG_BITBLTBUF:
{
auto buf = make_convertible<BITBLTBUF>(data);
result = string_format("BITBLTBUF(SBP: 0x%08X, SBW: %i, SPSM: %i, DBP: 0x%08X, DBW: %i, DPSM: %i)",
buf.GetSrcPtr(), buf.GetSrcWidth(), buf.nSrcPsm, buf.GetDstPtr(), buf.GetDstWidth(), buf.nDstPsm);
}
break;
case GS_REG_TRXPOS:
{
auto trxPos = make_convertible<TRXPOS>(data);
result = string_format("TRXPOS(SSAX: %i, SSAY: %i, DSAX: %i, DSAY: %i, DIR: %i)",
trxPos.nSSAX, trxPos.nSSAY, trxPos.nDSAX, trxPos.nDSAY, trxPos.nDIR);
}
break;
case GS_REG_TRXREG:
{
auto trxReg = make_convertible<TRXREG>(data);
result = string_format("TRXREG(RRW: %i, RRH: %i)",
trxReg.nRRW, trxReg.nRRH);
}
break;
case GS_REG_TRXDIR:
result = string_format("TRXDIR(XDIR: %i)", data & 0x03);
break;
case GS_REG_HWREG:
result = string_format("HWREG(DATA: 0x%016llX)", data);
break;
case GS_REG_SIGNAL:
{
auto signal = make_convertible<SIGNAL>(data);
result = string_format("SIGNAL(IDMSK: 0x%08X, ID: 0x%08X)",
signal.idmsk, signal.id);
}
break;
case GS_REG_FINISH:
result = "FINISH()";
break;
case GS_REG_LABEL:
{
auto label = make_convertible<LABEL>(data);
result = string_format("LABEL(IDMSK: 0x%08X, ID: 0x%08X)",
label.idmsk, label.id);
}
break;
default:
result = string_format("(Unknown register: 0x%02X)", registerId);
break;
}
return result;
}
void CGSHandler::LogWrite(uint8 registerId, uint64 data)
{
if(!m_loggingEnabled) return;
auto disassembledWrite = DisassembleWrite(registerId, data);
CLog::GetInstance().Print(LOG_NAME, "%s\r\n", disassembledWrite.c_str());
}
void CGSHandler::LogPrivateWrite(uint32 address)
{
assert((address & 0x04) != 0);
uint32 regAddress = address & ~0x0F;
switch(regAddress)
{
case GS_PMODE:
CLog::GetInstance().Print(LOG_NAME, "PMODE(0x%08X);\r\n", m_nPMODE);
break;
case GS_SMODE2:
{
SMODE2 smode2;
smode2 <<= m_nSMODE2;
CLog::GetInstance().Print(LOG_NAME, "SMODE2(inter = %d, ffmd = %d, dpms = %d);\r\n",
smode2.interlaced,
smode2.ffmd,
smode2.dpms);
}
break;
case GS_DISPFB1:
case GS_DISPFB2:
{
DISPFB dispfb;
dispfb <<= (regAddress == GS_DISPFB1) ? m_nDISPFB1.value.q : m_nDISPFB2.value.q;
CLog::GetInstance().Print(LOG_NAME, "DISPFB%d(FBP: 0x%08X, FBW: %d, PSM: %d, DBX: %d, DBY: %d);\r\n",
(regAddress == GS_DISPFB1) ? 1 : 2,
dispfb.GetBufPtr(),
dispfb.GetBufWidth(),
dispfb.nPSM,
dispfb.nX,
dispfb.nY);
}
break;
case GS_DISPLAY1:
case GS_DISPLAY2:
{
DISPLAY display;
display <<= (regAddress == GS_DISPLAY1) ? m_nDISPLAY1.value.q : m_nDISPLAY2.value.q;
CLog::GetInstance().Print(LOG_NAME, "DISPLAY%d(DX: %d, DY: %d, MAGH: %d, MAGV: %d, DW: %d, DH: %d);\r\n",
(regAddress == GS_DISPLAY1) ? 1 : 2,
display.nX,
display.nY,
display.nMagX,
display.nMagY,
display.nW,
display.nH);
}
break;
case GS_CSR:
//CSR
break;
case GS_IMR:
//IMR
break;
}
}
void CGSHandler::WriteToDelayedRegister(uint32 address, uint32 value, DELAYED_REGISTER& delayedRegister)
{
if(address & 0x04)
{
std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
delayedRegister.value.d0 = delayedRegister.heldValue;
delayedRegister.value.d1 = value;
}
else
{
delayedRegister.heldValue = value;
}
}
void CGSHandler::ThreadProc()
{
while(!m_threadDone)
{
m_mailBox.WaitForCall(100);
while(m_mailBox.IsPending())
{
m_mailBox.ReceiveCall();
}
}
}
Framework::CBitmap CGSHandler::GetScreenshot()
{
throw std::runtime_error("Screenshot feature is not implemented in current backend.");
}