#include #include #include #include #include "../AppConfig.h" #include "../Log.h" #include "../states/MemoryStateFile.h" #include "../states/RegisterStateFile.h" #include "../FrameDump.h" #include "../ee/INTC.h" #include "GSHandler.h" #include "GsPixelFormats.h" #include "string_format.h" //Shadow Hearts 2 looks for this specific value #define GS_REVISION (7) #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(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; m_transferWriteHandlers[PSMCT24] = &CGSHandler::TransferWriteHandlerPSMCT24; m_transferWriteHandlers[PSMCT16] = &CGSHandler::TransferWriteHandlerGeneric; m_transferWriteHandlers[PSMCT16S] = &CGSHandler::TransferWriteHandlerGeneric; m_transferWriteHandlers[PSMT8] = &CGSHandler::TransferWriteHandlerGeneric; 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; m_transferReadHandlers[PSMT8] = &CGSHandler::TransferReadHandlerGeneric; ResetBase(); m_thread = std::thread([&]() { ThreadProc(); }); } CGSHandler::~CGSHandler() { m_mailBox.SendCall([this]() { 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::SetIntc(CINTC* intc) { m_intc = intc; } 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 | (GS_REVISION << 16); 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 registerMutexLock(m_registerMutex); m_nCSR |= CSR_VSYNC_INT; NotifyEvent(CSR_VSYNC_INT); } void CGSHandler::ResetVBlank() { std::lock_guard registerMutexLock(m_registerMutex); //Alternate current field m_nCSR ^= CSR_FIELD; } int CGSHandler::GetPendingTransferCount() const { return m_transferCount; } void CGSHandler::NotifyEvent(uint32 eventBit) { uint32 mask = (~m_nIMR >> 8) & 0x1F; bool hasPendingInterrupt = (eventBit & mask) != 0; if(m_intc && hasPendingInterrupt) { m_intc->AssertLine(CINTC::INTC_LINE_GS); } } 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 registerMutexLock(m_registerMutex); m_nCSR |= CSR_HSYNC_INT; NotifyEvent(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 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); if(!(nAddress & 0x04)) { //Some games (Soul Calibur 2, Crash Nitro Kart) will unmask interrupts //right after starting a transfer that sets a SIGNAL... Let the //interrupt go through even though it's not supposed to work that way NotifyEvent(m_nCSR & 0x1F); } 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(write.second); auto siglblid = make_convertible(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; NotifyEvent(CSR_SIGNAL_EVENT); } break; case GS_REG_FINISH: m_nCSR |= CSR_FINISH_EVENT; NotifyEvent(CSR_FINISH_EVENT); break; case GS_REG_LABEL: { auto label = make_convertible