1 #include <stdio.h>
2 #include <string.h>
3 #include <functional>
4 #include "../AppConfig.h"
5 #include "../Log.h"
6 #include "../states/MemoryStateFile.h"
7 #include "../states/RegisterStateFile.h"
8 #include "../FrameDump.h"
9 #include "../ee/INTC.h"
10 #include "GSHandler.h"
11 #include "GsPixelFormats.h"
12 #include "string_format.h"
13 
14 //Shadow Hearts 2 looks for this specific value
15 #define GS_REVISION (7)
16 
17 #define R_REG(a, v, r)                \
18 	if((a)&0x4)                       \
19 	{                                 \
20 		v = (uint32)(r >> 32);        \
21 	}                                 \
22 	else                              \
23 	{                                 \
24 		v = (uint32)(r & 0xFFFFFFFF); \
25 	}
26 
27 #define W_REG(a, v, r)                \
28 	if((a)&0x4)                       \
29 	{                                 \
30 		(r) &= 0x00000000FFFFFFFFULL; \
31 		(r) |= (uint64)(v) << 32;     \
32 	}                                 \
33 	else                              \
34 	{                                 \
35 		(r) &= 0xFFFFFFFF00000000ULL; \
36 		(r) |= (v);                   \
37 	}
38 
39 #define STATE_RAM ("gs/ram")
40 #define STATE_REGS ("gs/regs")
41 #define STATE_TRXCTX ("gs/trxcontext")
42 #define STATE_PRIVREGS ("gs/privregs.xml")
43 
44 #define STATE_PRIVREGS_PMODE ("PMODE")
45 #define STATE_PRIVREGS_SMODE2 ("SMODE2")
46 #define STATE_PRIVREGS_DISPFB1 ("DISPFB1")
47 #define STATE_PRIVREGS_DISPLAY1 ("DISPLAY1")
48 #define STATE_PRIVREGS_DISPFB2 ("DISPFB2")
49 #define STATE_PRIVREGS_DISPLAY2 ("DISPLAY2")
50 #define STATE_PRIVREGS_CSR ("CSR")
51 #define STATE_PRIVREGS_IMR ("IMR")
52 #define STATE_PRIVREGS_BUSDIR ("BUSDIR")
53 #define STATE_PRIVREGS_SIGLBLID ("SIGLBLID")
54 #define STATE_PRIVREGS_CRTMODE ("CrtMode")
55 
56 #define STATE_REG_CBP0 ("cbp0")
57 #define STATE_REG_CBP1 ("cbp1")
58 
59 #define LOG_NAME ("gs")
60 
CGSHandler(bool gsThreaded)61 CGSHandler::CGSHandler(bool gsThreaded)
62     : m_threadDone(false)
63     , m_drawCallCount(0)
64     , m_pCLUT(nullptr)
65     , m_pRAM(nullptr)
66     , m_frameDump(nullptr)
67     , m_loggingEnabled(true)
68     , m_gsThreaded(gsThreaded)
69 {
70 	RegisterPreferences();
71 
72 	m_presentationParams.mode = static_cast<PRESENTATION_MODE>(CAppConfig::GetInstance().GetPreferenceInteger(PREF_CGSHANDLER_PRESENTATION_MODE));
73 	m_presentationParams.windowWidth = 512;
74 	m_presentationParams.windowHeight = 384;
75 
76 	m_pRAM = new uint8[RAMSIZE];
77 	m_pCLUT = new uint16[CLUTENTRYCOUNT];
78 	m_writeBuffer = new RegisterWrite[REGISTERWRITEBUFFER_SIZE];
79 
80 	for(int i = 0; i < PSM_MAX; i++)
81 	{
82 		m_transferWriteHandlers[i] = &CGSHandler::TransferWriteHandlerInvalid;
83 		m_transferReadHandlers[i] = &CGSHandler::TransferReadHandlerInvalid;
84 	}
85 
86 	m_transferWriteHandlers[PSMCT32] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMCT32>;
87 	m_transferWriteHandlers[PSMCT24] = &CGSHandler::TransferWriteHandlerPSMCT24;
88 	m_transferWriteHandlers[PSMCT16] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMCT16>;
89 	m_transferWriteHandlers[PSMCT16S] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMCT16S>;
90 	m_transferWriteHandlers[PSMT8] = &CGSHandler::TransferWriteHandlerGeneric<CGsPixelFormats::STORAGEPSMT8>;
91 	m_transferWriteHandlers[PSMT4] = &CGSHandler::TransferWriteHandlerPSMT4;
92 	m_transferWriteHandlers[PSMT8H] = &CGSHandler::TransferWriteHandlerPSMT8H;
93 	m_transferWriteHandlers[PSMT4HL] = &CGSHandler::TransferWriteHandlerPSMT4H<24, 0x0F000000>;
94 	m_transferWriteHandlers[PSMT4HH] = &CGSHandler::TransferWriteHandlerPSMT4H<28, 0xF0000000>;
95 
96 	m_transferReadHandlers[PSMCT32] = &CGSHandler::TransferReadHandlerGeneric<CGsPixelFormats::STORAGEPSMCT32>;
97 	m_transferReadHandlers[PSMCT24] = &CGSHandler::TransferReadHandlerPSMCT24;
98 	m_transferReadHandlers[PSMCT16] = &CGSHandler::TransferReadHandlerGeneric<CGsPixelFormats::STORAGEPSMCT16>;
99 	m_transferReadHandlers[PSMT8] = &CGSHandler::TransferReadHandlerGeneric<CGsPixelFormats::STORAGEPSMT8>;
100 
101 	ResetBase();
102 
103 	if(m_gsThreaded)
104 	{
105 		m_thread = std::thread([&]() { ThreadProc(); });
106 	}
107 }
108 
~CGSHandler()109 CGSHandler::~CGSHandler()
110 {
111 	if(m_gsThreaded)
112 	{
113 		SendGSCall([this]() { m_threadDone = true; });
114 		m_thread.join();
115 	}
116 	delete[] m_pRAM;
117 	delete[] m_pCLUT;
118 	delete[] m_writeBuffer;
119 }
120 
RegisterPreferences()121 void CGSHandler::RegisterPreferences()
122 {
123 	CAppConfig::GetInstance().RegisterPreferenceInteger(PREF_CGSHANDLER_PRESENTATION_MODE, CGSHandler::PRESENTATION_MODE_FIT);
124 	CAppConfig::GetInstance().RegisterPreferenceBoolean(PREF_CGSHANDLER_WIDESCREEN, false);
125 }
126 
NotifyPreferencesChanged()127 void CGSHandler::NotifyPreferencesChanged()
128 {
129 	SendGSCall([this]() { NotifyPreferencesChangedImpl(); });
130 }
131 
SetIntc(CINTC * intc)132 void CGSHandler::SetIntc(CINTC* intc)
133 {
134 	m_intc = intc;
135 }
136 
Reset()137 void CGSHandler::Reset()
138 {
139 	ResetBase();
140 	SendGSCall(std::bind(&CGSHandler::ResetImpl, this), true);
141 }
142 
ResetBase()143 void CGSHandler::ResetBase()
144 {
145 	memset(m_nReg, 0, sizeof(uint64) * 0x80);
146 	m_nReg[GS_REG_PRMODECONT] = 1;
147 	memset(m_pRAM, 0, RAMSIZE);
148 	memset(m_pCLUT, 0, CLUTSIZE);
149 	m_nPMODE = 0;
150 	m_nSMODE2 = 0x3; // Interlacing with fullframe
151 	m_nDISPFB1.heldValue = 0;
152 	m_nDISPFB1.value.q = 0;
153 	m_nDISPLAY1.heldValue = 0;
154 	m_nDISPLAY1.value.q = 0x1BF9FF72617467;
155 	m_nDISPFB2.heldValue = 0;
156 	m_nDISPFB2.value.q = 0;
157 	m_nDISPLAY2.heldValue = 0;
158 	m_nDISPLAY2.value.q = 0x1BF9FF72617467;
159 	m_nCSR = CSR_FIFO_EMPTY | (GS_REVISION << 16);
160 	m_nIMR = ~0;
161 	m_nBUSDIR = 0;
162 	m_nSIGLBLID = 0;
163 	m_crtMode = CRT_MODE_NTSC;
164 	m_nCBP0 = 0;
165 	m_nCBP1 = 0;
166 	m_transferCount = 0;
167 }
168 
ResetImpl()169 void CGSHandler::ResetImpl()
170 {
171 }
172 
NotifyPreferencesChangedImpl()173 void CGSHandler::NotifyPreferencesChangedImpl()
174 {
175 }
176 
SetPresentationParams(const PRESENTATION_PARAMS & presentationParams)177 void CGSHandler::SetPresentationParams(const PRESENTATION_PARAMS& presentationParams)
178 {
179 	m_presentationParams = presentationParams;
180 }
181 
GetPresentationViewport() const182 CGSHandler::PRESENTATION_VIEWPORT CGSHandler::GetPresentationViewport() const
183 {
184 	PRESENTATION_VIEWPORT viewport;
185 	auto sourceSize = std::make_pair(GetCrtWidth(), GetCrtHeight());
186 	if(CAppConfig::GetInstance().GetPreferenceBoolean(PREF_CGSHANDLER_WIDESCREEN))
187 	{
188 		sourceSize = std::make_pair(1920, 1080);
189 	}
190 	switch(m_presentationParams.mode)
191 	{
192 	case PRESENTATION_MODE_FILL:
193 		viewport.offsetX = 0;
194 		viewport.offsetY = 0;
195 		viewport.width = m_presentationParams.windowWidth;
196 		viewport.height = m_presentationParams.windowHeight;
197 		break;
198 	case PRESENTATION_MODE_FIT:
199 	{
200 		int viewportWidth[2];
201 		int viewportHeight[2];
202 		{
203 			viewportWidth[0] = m_presentationParams.windowWidth;
204 			viewportHeight[0] = (sourceSize.first != 0) ? (m_presentationParams.windowWidth * sourceSize.second) / sourceSize.first : 0;
205 		}
206 		{
207 			viewportWidth[1] = (sourceSize.second != 0) ? (m_presentationParams.windowHeight * sourceSize.first) / sourceSize.second : 0;
208 			viewportHeight[1] = m_presentationParams.windowHeight;
209 		}
210 		int selectedViewport = 0;
211 		if(
212 		    (viewportWidth[0] > static_cast<int>(m_presentationParams.windowWidth)) ||
213 		    (viewportHeight[0] > static_cast<int>(m_presentationParams.windowHeight)))
214 		{
215 			selectedViewport = 1;
216 			assert(
217 			    viewportWidth[1] <= static_cast<int>(m_presentationParams.windowWidth) &&
218 			    viewportHeight[1] <= static_cast<int>(m_presentationParams.windowHeight));
219 		}
220 		int offsetX = static_cast<int>(m_presentationParams.windowWidth - viewportWidth[selectedViewport]) / 2;
221 		int offsetY = static_cast<int>(m_presentationParams.windowHeight - viewportHeight[selectedViewport]) / 2;
222 		viewport.offsetX = offsetX;
223 		viewport.offsetY = offsetY;
224 		viewport.width = viewportWidth[selectedViewport];
225 		viewport.height = viewportHeight[selectedViewport];
226 	}
227 	break;
228 	case PRESENTATION_MODE_ORIGINAL:
229 	{
230 		int offsetX = static_cast<int>(m_presentationParams.windowWidth - sourceSize.first) / 2;
231 		int offsetY = static_cast<int>(m_presentationParams.windowHeight - sourceSize.second) / 2;
232 		viewport.offsetX = offsetX;
233 		viewport.offsetY = offsetY;
234 		viewport.width = sourceSize.first;
235 		viewport.height = sourceSize.second;
236 	}
237 	break;
238 	}
239 	return viewport;
240 }
241 
SaveState(Framework::CZipArchiveWriter & archive)242 void CGSHandler::SaveState(Framework::CZipArchiveWriter& archive)
243 {
244 	archive.InsertFile(new CMemoryStateFile(STATE_RAM, GetRam(), RAMSIZE));
245 	archive.InsertFile(new CMemoryStateFile(STATE_REGS, m_nReg, sizeof(uint64) * CGSHandler::REGISTER_MAX));
246 	archive.InsertFile(new CMemoryStateFile(STATE_TRXCTX, &m_trxCtx, sizeof(TRXCONTEXT)));
247 
248 	{
249 		CRegisterStateFile* registerFile = new CRegisterStateFile(STATE_PRIVREGS);
250 
251 		registerFile->SetRegister64(STATE_PRIVREGS_PMODE, m_nPMODE);
252 		registerFile->SetRegister64(STATE_PRIVREGS_SMODE2, m_nSMODE2);
253 		registerFile->SetRegister64(STATE_PRIVREGS_DISPFB1, m_nDISPFB1.value.q);
254 		registerFile->SetRegister64(STATE_PRIVREGS_DISPLAY1, m_nDISPLAY1.value.q);
255 		registerFile->SetRegister64(STATE_PRIVREGS_DISPFB2, m_nDISPFB2.value.q);
256 		registerFile->SetRegister64(STATE_PRIVREGS_DISPLAY2, m_nDISPLAY2.value.q);
257 		registerFile->SetRegister64(STATE_PRIVREGS_CSR, m_nCSR);
258 		registerFile->SetRegister64(STATE_PRIVREGS_IMR, m_nIMR);
259 		registerFile->SetRegister64(STATE_PRIVREGS_BUSDIR, m_nBUSDIR);
260 		registerFile->SetRegister64(STATE_PRIVREGS_SIGLBLID, m_nSIGLBLID);
261 		registerFile->SetRegister32(STATE_PRIVREGS_CRTMODE, m_crtMode);
262 		registerFile->SetRegister32(STATE_REG_CBP0, m_nCBP0);
263 		registerFile->SetRegister32(STATE_REG_CBP1, m_nCBP1);
264 
265 		archive.InsertFile(registerFile);
266 	}
267 }
268 
LoadState(Framework::CZipArchiveReader & archive)269 void CGSHandler::LoadState(Framework::CZipArchiveReader& archive)
270 {
271 	archive.BeginReadFile(STATE_RAM)->Read(GetRam(), RAMSIZE);
272 	archive.BeginReadFile(STATE_REGS)->Read(m_nReg, sizeof(uint64) * CGSHandler::REGISTER_MAX);
273 	archive.BeginReadFile(STATE_TRXCTX)->Read(&m_trxCtx, sizeof(TRXCONTEXT));
274 
275 	{
276 		CRegisterStateFile registerFile(*archive.BeginReadFile(STATE_PRIVREGS));
277 		m_nPMODE = registerFile.GetRegister64(STATE_PRIVREGS_PMODE);
278 		m_nSMODE2 = registerFile.GetRegister64(STATE_PRIVREGS_SMODE2);
279 		m_nDISPFB1.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPFB1);
280 		m_nDISPLAY1.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPLAY1);
281 		m_nDISPFB2.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPFB2);
282 		m_nDISPLAY2.value.q = registerFile.GetRegister64(STATE_PRIVREGS_DISPLAY2);
283 		m_nCSR = registerFile.GetRegister64(STATE_PRIVREGS_CSR);
284 		m_nIMR = registerFile.GetRegister64(STATE_PRIVREGS_IMR);
285 		m_nBUSDIR = registerFile.GetRegister64(STATE_PRIVREGS_BUSDIR);
286 		m_nSIGLBLID = registerFile.GetRegister64(STATE_PRIVREGS_SIGLBLID);
287 		m_crtMode = static_cast<CRT_MODE>(registerFile.GetRegister32(STATE_PRIVREGS_CRTMODE));
288 		m_nCBP0 = registerFile.GetRegister32(STATE_REG_CBP0);
289 		m_nCBP1 = registerFile.GetRegister32(STATE_REG_CBP1);
290 	}
291 }
292 
Copy(const CGSHandler * gs)293 void CGSHandler::Copy(const CGSHandler* gs)
294 {
295 	memcpy(GetRam(), gs->GetRam(), RAMSIZE);
296 	memcpy(m_nReg, gs->m_nReg, sizeof(uint64) * CGSHandler::REGISTER_MAX);
297 	m_trxCtx = gs->m_trxCtx;
298 
299 	{
300 		m_nPMODE = gs->m_nPMODE;
301 		m_nSMODE2 = gs->m_nSMODE2;
302 		m_nDISPFB1.value.q = gs->m_nDISPFB1.value.q;
303 		m_nDISPLAY1.value.q = gs->m_nDISPLAY1.value.q;
304 		m_nDISPFB2.value.q = gs->m_nDISPFB2.value.q;
305 		m_nDISPLAY2.value.q = gs->m_nDISPLAY2.value.q;
306 		m_nCSR = gs->m_nCSR;
307 		m_nIMR = gs->m_nIMR;
308 		m_nBUSDIR = gs->m_nBUSDIR;
309 		m_nSIGLBLID = gs->m_nSIGLBLID;
310 		m_crtMode = gs->m_crtMode;
311 		m_nCBP0 = gs->m_nCBP0;
312 		m_nCBP1 = gs->m_nCBP1;
313 	}
314 }
315 
SetFrameDump(CFrameDump * frameDump)316 void CGSHandler::SetFrameDump(CFrameDump* frameDump)
317 {
318 	m_frameDump = frameDump;
319 }
320 
GetDrawEnabled() const321 bool CGSHandler::GetDrawEnabled() const
322 {
323 	return m_drawEnabled;
324 }
325 
SetDrawEnabled(bool drawEnabled)326 void CGSHandler::SetDrawEnabled(bool drawEnabled)
327 {
328 	m_drawEnabled = drawEnabled;
329 }
330 
SetVBlank()331 void CGSHandler::SetVBlank()
332 {
333 	{
334 		Finish();
335 	}
336 
337 	std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
338 	m_nCSR |= CSR_VSYNC_INT;
339 	NotifyEvent(CSR_VSYNC_INT);
340 }
341 
ResetVBlank()342 void CGSHandler::ResetVBlank()
343 {
344 	std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
345 
346 	//Alternate current field
347 	m_nCSR ^= CSR_FIELD;
348 }
349 
GetPendingTransferCount() const350 int CGSHandler::GetPendingTransferCount() const
351 {
352 	return m_transferCount;
353 }
354 
NotifyEvent(uint32 eventBit)355 void CGSHandler::NotifyEvent(uint32 eventBit)
356 {
357 	uint32 mask = (~m_nIMR >> 8) & 0x1F;
358 	bool hasPendingInterrupt = (eventBit & mask) != 0;
359 	if(m_intc && hasPendingInterrupt)
360 	{
361 		m_intc->AssertLine(CINTC::INTC_LINE_GS);
362 	}
363 }
364 
ReadPrivRegister(uint32 nAddress)365 uint32 CGSHandler::ReadPrivRegister(uint32 nAddress)
366 {
367 	uint32 nData = 0;
368 	switch(nAddress & ~0x0F)
369 	{
370 	case GS_CSR:
371 	case GS_CSR_ALT:
372 		//Force CSR to have the H-Blank bit set.
373 		{
374 			std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
375 			m_nCSR |= CSR_HSYNC_INT;
376 			NotifyEvent(CSR_HSYNC_INT);
377 			R_REG(nAddress, nData, m_nCSR);
378 		}
379 		break;
380 	case GS_IMR:
381 		R_REG(nAddress, nData, m_nIMR);
382 		break;
383 	case GS_SIGLBLID:
384 		R_REG(nAddress, nData, m_nSIGLBLID);
385 		break;
386 	default:
387 		CLog::GetInstance().Warn(LOG_NAME, "Read an unhandled priviledged register (0x%08X).\r\n", nAddress);
388 		nData = 0xCCCCCCCC;
389 		break;
390 	}
391 	return nData;
392 }
393 
WritePrivRegister(uint32 nAddress,uint32 nData)394 void CGSHandler::WritePrivRegister(uint32 nAddress, uint32 nData)
395 {
396 	switch(nAddress & ~0x0F)
397 	{
398 	case GS_PMODE:
399 		W_REG(nAddress, nData, m_nPMODE);
400 		if(!(nAddress & 0x4))
401 		{
402 			if((m_nPMODE & 0x01) && (m_nPMODE & 0x02))
403 			{
404 				CLog::GetInstance().Print(LOG_NAME, "Warning. Both read circuits were enabled. Using RC1 for display.\r\n");
405 				//				m_nPMODE &= ~0x02;
406 			}
407 		}
408 		break;
409 	case GS_SMODE2:
410 		W_REG(nAddress, nData, m_nSMODE2);
411 		break;
412 	case GS_DISPFB1:
413 		WriteToDelayedRegister(nAddress, nData, m_nDISPFB1);
414 		break;
415 	case GS_DISPLAY1:
416 		WriteToDelayedRegister(nAddress, nData, m_nDISPLAY1);
417 		break;
418 	case GS_DISPFB2:
419 		WriteToDelayedRegister(nAddress, nData, m_nDISPFB2);
420 		break;
421 	case GS_DISPLAY2:
422 		WriteToDelayedRegister(nAddress, nData, m_nDISPLAY2);
423 		break;
424 	case GS_CSR:
425 	case GS_CSR_ALT:
426 	{
427 		if(!(nAddress & 0x04))
428 		{
429 			std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
430 			if(nData & CSR_SIGNAL_EVENT)
431 			{
432 				m_nCSR &= ~CSR_SIGNAL_EVENT;
433 			}
434 			if(nData & CSR_FINISH_EVENT)
435 			{
436 				m_nCSR &= ~CSR_FINISH_EVENT;
437 			}
438 			if(nData & CSR_VSYNC_INT)
439 			{
440 				m_nCSR &= ~CSR_VSYNC_INT;
441 			}
442 			if(nData & CSR_RESET)
443 			{
444 				m_nCSR |= CSR_RESET;
445 			}
446 		}
447 	}
448 	break;
449 	case GS_IMR:
450 		W_REG(nAddress, nData, m_nIMR);
451 		if(!(nAddress & 0x04))
452 		{
453 			//Some games (Soul Calibur 2, Crash Nitro Kart) will unmask interrupts
454 			//right after starting a transfer that sets a SIGNAL... Let the
455 			//interrupt go through even though it's not supposed to work that way
456 			NotifyEvent(m_nCSR & 0x1F);
457 		}
458 		break;
459 	case GS_BUSDIR:
460 		W_REG(nAddress, nData, m_nBUSDIR);
461 		break;
462 	case GS_SIGLBLID:
463 		W_REG(nAddress, nData, m_nSIGLBLID);
464 		break;
465 	default:
466 		CLog::GetInstance().Warn(LOG_NAME, "Wrote to an unhandled priviledged register (0x%08X, 0x%08X).\r\n", nAddress, nData);
467 		break;
468 	}
469 
470 #ifdef _DEBUG
471 	if(nAddress & 0x04)
472 	{
473 		LogPrivateWrite(nAddress);
474 	}
475 #endif
476 }
477 
Initialize()478 void CGSHandler::Initialize()
479 {
480 	SendGSCall(std::bind(&CGSHandler::InitializeImpl, this), true);
481 }
482 
Release()483 void CGSHandler::Release()
484 {
485 	SendGSCall(std::bind(&CGSHandler::ReleaseImpl, this), true);
486 }
487 
Finish()488 void CGSHandler::Finish()
489 {
490 	FlushWriteBuffer();
491 	SendGSCall(std::bind(&CGSHandler::MarkNewFrame, this));
492 	Flip(true);
493 }
494 
Flip(bool waitForCompletion)495 void CGSHandler::Flip(bool waitForCompletion)
496 {
497 	SendGSCall(std::bind(&CGSHandler::FlipImpl, this), waitForCompletion, waitForCompletion);
498 }
499 
FlipImpl()500 void CGSHandler::FlipImpl()
501 {
502 	OnFlipComplete();
503 	m_flipped = true;
504 }
505 
MarkNewFrame()506 void CGSHandler::MarkNewFrame()
507 {
508 	OnNewFrame(m_drawCallCount);
509 	m_drawCallCount = 0;
510 #ifdef _DEBUG
511 	CLog::GetInstance().Print(LOG_NAME, "Frame Done.\r\n---------------------------------------------------------------------------------\r\n");
512 #endif
513 }
514 
GetRam() const515 uint8* CGSHandler::GetRam() const
516 {
517 	return m_pRAM;
518 }
519 
GetRegisters()520 uint64* CGSHandler::GetRegisters()
521 {
522 	return m_nReg;
523 }
524 
GetSMODE2() const525 uint64 CGSHandler::GetSMODE2() const
526 {
527 	return m_nSMODE2;
528 }
529 
SetSMODE2(uint64 value)530 void CGSHandler::SetSMODE2(uint64 value)
531 {
532 	m_nSMODE2 = value;
533 }
534 
GetBUSDIR() const535 uint64 CGSHandler::GetBUSDIR() const
536 {
537 	return m_nBUSDIR;
538 }
539 
FeedImageData(const void * data,uint32 length)540 void CGSHandler::FeedImageData(const void* data, uint32 length)
541 {
542 	assert(m_writeBufferProcessIndex == m_writeBufferSize);
543 	SubmitWriteBuffer();
544 
545 	m_transferCount++;
546 
547 	//Allocate 0x10 more bytes to allow transfer handlers
548 	//to read beyond the actual length of the buffer (ie.: PSMCT24)
549 
550 	std::vector<uint8> imageData(length + 0x10);
551 	memcpy(imageData.data(), data, length);
552 #ifdef DEBUGGER_INCLUDED
553 	if(m_frameDump)
554 	{
555 		m_frameDump->AddImagePacket(imageData.data(), length);
556 	}
557 #endif
558 	SendGSCall(
559 	    [this, imageData = std::move(imageData), length]() {
560 		    FeedImageDataImpl(imageData.data(), length);
561 	    });
562 }
563 
ReadImageData(void * data,uint32 length)564 void CGSHandler::ReadImageData(void* data, uint32 length)
565 {
566 	assert(m_writeBufferProcessIndex == m_writeBufferSize);
567 	SubmitWriteBuffer();
568 	SendGSCall([this, data, length]() { ReadImageDataImpl(data, length); }, true);
569 }
570 
ProcessWriteBuffer(const CGsPacketMetadata * metadata)571 void CGSHandler::ProcessWriteBuffer(const CGsPacketMetadata* metadata)
572 {
573 	assert(m_writeBufferProcessIndex <= m_writeBufferSize);
574 	assert(m_writeBufferSubmitIndex <= m_writeBufferProcessIndex);
575 #ifdef DEBUGGER_INCLUDED
576 	if(m_frameDump)
577 	{
578 		uint32 packetSize = m_writeBufferSize - m_writeBufferProcessIndex;
579 		if(packetSize != 0)
580 		{
581 			m_frameDump->AddRegisterPacket(m_writeBuffer + m_writeBufferProcessIndex, packetSize, metadata);
582 		}
583 	}
584 #endif
585 	for(uint32 writeIndex = m_writeBufferProcessIndex; writeIndex < m_writeBufferSize; writeIndex++)
586 	{
587 		const auto& write = m_writeBuffer[writeIndex];
588 		switch(write.first)
589 		{
590 		case GS_REG_SIGNAL:
591 		{
592 			auto signal = make_convertible<SIGNAL>(write.second);
593 			auto siglblid = make_convertible<SIGLBLID>(m_nSIGLBLID);
594 			siglblid.sigid &= ~signal.idmsk;
595 			siglblid.sigid |= signal.id;
596 			m_nSIGLBLID = siglblid;
597 			assert((m_nCSR & CSR_SIGNAL_EVENT) == 0);
598 			m_nCSR |= CSR_SIGNAL_EVENT;
599 			NotifyEvent(CSR_SIGNAL_EVENT);
600 		}
601 		break;
602 		case GS_REG_FINISH:
603 			m_nCSR |= CSR_FINISH_EVENT;
604 			NotifyEvent(CSR_FINISH_EVENT);
605 			break;
606 		case GS_REG_LABEL:
607 		{
608 			auto label = make_convertible<LABEL>(write.second);
609 			auto siglblid = make_convertible<SIGLBLID>(m_nSIGLBLID);
610 			siglblid.lblid &= ~label.idmsk;
611 			siglblid.lblid |= label.id;
612 			m_nSIGLBLID = siglblid;
613 		}
614 		break;
615 		}
616 	}
617 	m_writeBufferProcessIndex = m_writeBufferSize;
618 	uint32 submitPending = m_writeBufferProcessIndex - m_writeBufferSubmitIndex;
619 	if(submitPending >= REGISTERWRITEBUFFER_SUBMIT_THRESHOLD)
620 	{
621 		SubmitWriteBuffer();
622 	}
623 }
624 
SubmitWriteBuffer()625 void CGSHandler::SubmitWriteBuffer()
626 {
627 	assert(m_writeBufferSubmitIndex <= m_writeBufferSize);
628 	if(m_writeBufferSubmitIndex == m_writeBufferSize) return;
629 
630 	m_transferCount++;
631 	uint32 bufferStartIndex = m_writeBufferSubmitIndex;
632 	uint32 bufferEndIndex = m_writeBufferSize;
633 
634 	SendGSCall(
635 	    [this, bufferStartIndex, bufferEndIndex]() {
636 		    SubmitWriteBufferImpl(bufferStartIndex, bufferEndIndex);
637 	    });
638 
639 	m_writeBufferSubmitIndex = m_writeBufferSize;
640 }
641 
FlushWriteBuffer()642 void CGSHandler::FlushWriteBuffer()
643 {
644 	//Everything should be processed at this point
645 	assert(m_writeBufferProcessIndex == m_writeBufferSize);
646 	//Make sure everything is submitted
647 	SubmitWriteBuffer();
648 	m_writeBufferSize = 0;
649 	m_writeBufferProcessIndex = 0;
650 	m_writeBufferSubmitIndex = 0;
651 	//Nothing should be written to the buffer after that
652 }
653 
WriteRegisterImpl(uint8 nRegister,uint64 nData)654 void CGSHandler::WriteRegisterImpl(uint8 nRegister, uint64 nData)
655 {
656 	nRegister &= REGISTER_MAX - 1;
657 	m_nReg[nRegister] = nData;
658 
659 	switch(nRegister)
660 	{
661 	case GS_REG_TEX0_1:
662 	case GS_REG_TEX0_2:
663 	{
664 		unsigned int nContext = nRegister - GS_REG_TEX0_1;
665 		assert(nContext == 0 || nContext == 1);
666 		auto tex0 = make_convertible<TEX0>(m_nReg[GS_REG_TEX0_1 + nContext]);
667 		SyncCLUT(tex0);
668 	}
669 	break;
670 
671 	case GS_REG_TEX2_1:
672 	case GS_REG_TEX2_2:
673 	{
674 		//Update TEX0
675 		unsigned int nContext = nRegister - GS_REG_TEX2_1;
676 		assert(nContext == 0 || nContext == 1);
677 
678 		const uint64 nMask = 0xFFFFFFE003F00000ULL;
679 		m_nReg[GS_REG_TEX0_1 + nContext] &= ~nMask;
680 		m_nReg[GS_REG_TEX0_1 + nContext] |= nData & nMask;
681 
682 		auto tex0 = make_convertible<TEX0>(m_nReg[GS_REG_TEX0_1 + nContext]);
683 		SyncCLUT(tex0);
684 	}
685 	break;
686 
687 	case GS_REG_TRXDIR:
688 		BeginTransfer();
689 		break;
690 
691 	case GS_REG_HWREG:
692 		m_transferCount++;
693 		FeedImageDataImpl(reinterpret_cast<const uint8*>(&nData), 8);
694 		break;
695 	}
696 
697 #ifdef _DEBUG
698 	LogWrite(nRegister, nData);
699 #endif
700 }
701 
FeedImageDataImpl(const uint8 * imageData,uint32 length)702 void CGSHandler::FeedImageDataImpl(const uint8* imageData, uint32 length)
703 {
704 	if(m_trxCtx.nSize == 0)
705 	{
706 #ifdef _DEBUG
707 		CLog::GetInstance().Warn(LOG_NAME, "Warning. Received image data when no transfer was expected.\r\n");
708 #endif
709 	}
710 	else
711 	{
712 		if(m_trxCtx.nSize < length)
713 		{
714 			length = m_trxCtx.nSize;
715 			//assert(0);
716 			//return;
717 		}
718 
719 		TransferWrite(imageData, length);
720 		m_trxCtx.nSize -= length;
721 
722 		if(m_trxCtx.nSize == 0)
723 		{
724 			auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
725 			//assert(m_trxCtx.nRRY == trxReg.nRRH);
726 			ProcessHostToLocalTransfer();
727 
728 #ifdef _DEBUG
729 			auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
730 			CLog::GetInstance().Print(LOG_NAME, "Completed image transfer at 0x%08X (dirty = %d).\r\n", bltBuf.GetDstPtr(), m_trxCtx.nDirty);
731 #endif
732 		}
733 	}
734 
735 	assert(m_transferCount != 0);
736 	m_transferCount--;
737 }
738 
ReadImageDataImpl(void * ptr,uint32 size)739 void CGSHandler::ReadImageDataImpl(void* ptr, uint32 size)
740 {
741 	assert(m_trxCtx.nSize != 0);
742 	assert(m_trxCtx.nSize == size);
743 	auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
744 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
745 
746 	assert(trxPos.nDIR == 0);
747 	((this)->*(m_transferReadHandlers[bltBuf.nSrcPsm]))(ptr, size);
748 }
749 
SubmitWriteBufferImpl(uint32 bufferStartIndex,uint32 bufferEndIndex)750 void CGSHandler::SubmitWriteBufferImpl(uint32 bufferStartIndex, uint32 bufferEndIndex)
751 {
752 	for(uint32 i = bufferStartIndex; i < bufferEndIndex; i++)
753 	{
754 		const auto& write = m_writeBuffer[i];
755 		WriteRegisterImpl(write.first, write.second);
756 	}
757 
758 	assert(m_transferCount != 0);
759 	m_transferCount--;
760 }
761 
GetTransferInvalidationRange(const BITBLTBUF & bltBuf,const TRXREG & trxReg,const TRXPOS & trxPos)762 std::pair<uint32, uint32> CGSHandler::GetTransferInvalidationRange(const BITBLTBUF& bltBuf, const TRXREG& trxReg, const TRXPOS& trxPos)
763 {
764 	uint32 transferAddress = bltBuf.GetDstPtr();
765 
766 	//Find the pages that are touched by this transfer
767 	auto transferPageSize = CGsPixelFormats::GetPsmPageSize(bltBuf.nDstPsm);
768 
769 	// DBZ Budokai Tenkaichi 2 and 3 use invalid (empty) buffer sizes
770 	// Account for that, by assuming trxReg.nRRW.
771 	auto width = bltBuf.GetDstWidth();
772 	if(width == 0)
773 	{
774 		width = trxReg.nRRW;
775 	}
776 
777 	//Espgaluda uses an offset into a big memory area. The Y offset is not necessarily
778 	//a multiple of the page height. We need to make sure to take this into account.
779 	uint32 intraPageOffsetY = trxPos.nDSAY % transferPageSize.second;
780 
781 	uint32 pageCountX = (width + transferPageSize.first - 1) / transferPageSize.first;
782 	uint32 pageCountY = (intraPageOffsetY + trxReg.nRRH + transferPageSize.second - 1) / transferPageSize.second;
783 
784 	uint32 pageCount = pageCountX * pageCountY;
785 	uint32 transferSize = pageCount * CGsPixelFormats::PAGESIZE;
786 	uint32 transferOffset = (trxPos.nDSAY / transferPageSize.second) * pageCountX * CGsPixelFormats::PAGESIZE;
787 
788 	return std::make_pair(transferAddress + transferOffset, transferSize);
789 }
790 
BeginTransfer()791 void CGSHandler::BeginTransfer()
792 {
793 	uint32 trxDir = m_nReg[GS_REG_TRXDIR] & 0x03;
794 	if(trxDir == 0 || trxDir == 1)
795 	{
796 		//"Host to Local" or "Local to Host"
797 		auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
798 		auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
799 		auto psm = (trxDir == 0) ? bltBuf.nDstPsm : bltBuf.nSrcPsm;
800 		unsigned int nPixelSize = 0;
801 
802 		//We need to figure out the pixel size of the stream
803 		switch(psm)
804 		{
805 		case PSMCT32:
806 		case PSMZ32:
807 			nPixelSize = 32;
808 			break;
809 		case PSMCT24:
810 		case PSMZ24:
811 			nPixelSize = 24;
812 			break;
813 		case PSMCT16:
814 		case PSMCT16S:
815 		case PSMZ16S:
816 			nPixelSize = 16;
817 			break;
818 		case PSMT8:
819 		case PSMT8H:
820 			nPixelSize = 8;
821 			break;
822 		case PSMT4:
823 		case PSMT4HH:
824 		case PSMT4HL:
825 			nPixelSize = 4;
826 			break;
827 		default:
828 			assert(0);
829 			break;
830 		}
831 
832 		//Make sure transfer size is a multiple of 16. Some games (ex.: Gregory Horror Show)
833 		//specify transfer width/height that will give a transfer size that is not a multiple of 16
834 		//and will prevent proper handling of image transfer (texture cache will not be invalidated).
835 		m_trxCtx.nSize = ((trxReg.nRRW * trxReg.nRRH * nPixelSize) / 8) & ~0xF;
836 		m_trxCtx.nRealSize = m_trxCtx.nSize;
837 		m_trxCtx.nRRX = 0;
838 		m_trxCtx.nRRY = 0;
839 
840 		if(trxDir == 0)
841 		{
842 			BeginTransferWrite();
843 			CLog::GetInstance().Print(LOG_NAME, "Starting transfer to 0x%08X, buffer size %d, psm: %d, size (%dx%d)\r\n",
844 			                          bltBuf.GetDstPtr(), bltBuf.GetDstWidth(), bltBuf.nDstPsm, trxReg.nRRW, trxReg.nRRH);
845 		}
846 		else if(trxDir == 1)
847 		{
848 			ProcessLocalToHostTransfer();
849 			CLog::GetInstance().Print(LOG_NAME, "Starting transfer from 0x%08X, buffer size %d, psm: %d, size (%dx%d)\r\n",
850 			                          bltBuf.GetSrcPtr(), bltBuf.GetSrcWidth(), bltBuf.nSrcPsm, trxReg.nRRW, trxReg.nRRH);
851 		}
852 	}
853 	else if(trxDir == 2)
854 	{
855 		//Local to Local
856 		ProcessLocalToLocalTransfer();
857 	}
858 }
859 
BeginTransferWrite()860 void CGSHandler::BeginTransferWrite()
861 {
862 	m_trxCtx.nDirty = false;
863 }
864 
TransferWrite(const uint8 * imageData,uint32 length)865 void CGSHandler::TransferWrite(const uint8* imageData, uint32 length)
866 {
867 	auto bltBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
868 	m_trxCtx.nDirty |= ((this)->*(m_transferWriteHandlers[bltBuf.nDstPsm]))(imageData, length);
869 }
870 
TransferWriteHandlerInvalid(const void * pData,uint32 nLength)871 bool CGSHandler::TransferWriteHandlerInvalid(const void* pData, uint32 nLength)
872 {
873 	assert(0);
874 	return false;
875 }
876 
877 template <typename Storage>
TransferWriteHandlerGeneric(const void * pData,uint32 nLength)878 bool CGSHandler::TransferWriteHandlerGeneric(const void* pData, uint32 nLength)
879 {
880 	bool nDirty = false;
881 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
882 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
883 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
884 
885 	nLength /= sizeof(typename Storage::Unit);
886 
887 	CGsPixelFormats::CPixelIndexor<Storage> Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
888 
889 	auto pSrc = reinterpret_cast<const typename Storage::Unit*>(pData);
890 
891 	for(unsigned int i = 0; i < nLength; i++)
892 	{
893 		uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
894 		uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
895 
896 		auto pPixel = Indexor.GetPixelAddress(nX, nY);
897 
898 		if((*pPixel) != pSrc[i])
899 		{
900 			(*pPixel) = pSrc[i];
901 			nDirty = true;
902 		}
903 
904 		m_trxCtx.nRRX++;
905 		if(m_trxCtx.nRRX == trxReg.nRRW)
906 		{
907 			m_trxCtx.nRRX = 0;
908 			m_trxCtx.nRRY++;
909 		}
910 	}
911 
912 	return nDirty;
913 }
914 
TransferWriteHandlerPSMCT24(const void * pData,uint32 nLength)915 bool CGSHandler::TransferWriteHandlerPSMCT24(const void* pData, uint32 nLength)
916 {
917 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
918 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
919 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
920 
921 	CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
922 
923 	auto pSrc = reinterpret_cast<const uint8*>(pData);
924 
925 	for(unsigned int i = 0; i < nLength; i += 3)
926 	{
927 		uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
928 		uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
929 
930 		uint32* pDstPixel = Indexor.GetPixelAddress(nX, nY);
931 		uint32 nSrcPixel = *reinterpret_cast<const uint32*>(&pSrc[i]) & 0x00FFFFFF;
932 		(*pDstPixel) &= 0xFF000000;
933 		(*pDstPixel) |= nSrcPixel;
934 
935 		m_trxCtx.nRRX++;
936 		if(m_trxCtx.nRRX == trxReg.nRRW)
937 		{
938 			m_trxCtx.nRRX = 0;
939 			m_trxCtx.nRRY++;
940 		}
941 	}
942 
943 	return true;
944 }
945 
TransferWriteHandlerPSMT4(const void * pData,uint32 nLength)946 bool CGSHandler::TransferWriteHandlerPSMT4(const void* pData, uint32 nLength)
947 {
948 	bool dirty = false;
949 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
950 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
951 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
952 
953 	CGsPixelFormats::CPixelIndexorPSMT4 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
954 
955 	auto pSrc = reinterpret_cast<const uint8*>(pData);
956 
957 	for(unsigned int i = 0; i < nLength; i++)
958 	{
959 		uint8 nPixel[2];
960 
961 		nPixel[0] = (pSrc[i] >> 0) & 0x0F;
962 		nPixel[1] = (pSrc[i] >> 4) & 0x0F;
963 
964 		for(unsigned int j = 0; j < 2; j++)
965 		{
966 			uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
967 			uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
968 
969 			uint8 currentPixel = Indexor.GetPixel(nX, nY);
970 			if(currentPixel != nPixel[j])
971 			{
972 				Indexor.SetPixel(nX, nY, nPixel[j]);
973 				dirty = true;
974 			}
975 
976 			m_trxCtx.nRRX++;
977 			if(m_trxCtx.nRRX == trxReg.nRRW)
978 			{
979 				m_trxCtx.nRRX = 0;
980 				m_trxCtx.nRRY++;
981 			}
982 		}
983 	}
984 
985 	return dirty;
986 }
987 
988 template <uint32 nShift, uint32 nMask>
TransferWriteHandlerPSMT4H(const void * pData,uint32 nLength)989 bool CGSHandler::TransferWriteHandlerPSMT4H(const void* pData, uint32 nLength)
990 {
991 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
992 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
993 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
994 
995 	CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
996 
997 	auto pSrc = reinterpret_cast<const uint8*>(pData);
998 
999 	for(unsigned int i = 0; i < nLength; i++)
1000 	{
1001 		//Pixel 1
1002 		uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
1003 		uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
1004 
1005 		uint8 nSrcPixel = pSrc[i] & 0x0F;
1006 
1007 		uint32* pDstPixel = Indexor.GetPixelAddress(nX, nY);
1008 		(*pDstPixel) &= ~nMask;
1009 		(*pDstPixel) |= (nSrcPixel << nShift);
1010 
1011 		m_trxCtx.nRRX++;
1012 		if(m_trxCtx.nRRX == trxReg.nRRW)
1013 		{
1014 			m_trxCtx.nRRX = 0;
1015 			m_trxCtx.nRRY++;
1016 		}
1017 
1018 		//Pixel 2
1019 		nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
1020 		nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
1021 
1022 		nSrcPixel = (pSrc[i] & 0xF0);
1023 
1024 		pDstPixel = Indexor.GetPixelAddress(nX, nY);
1025 		(*pDstPixel) &= ~nMask;
1026 		(*pDstPixel) |= (nSrcPixel << (nShift - 4));
1027 
1028 		m_trxCtx.nRRX++;
1029 		if(m_trxCtx.nRRX == trxReg.nRRW)
1030 		{
1031 			m_trxCtx.nRRX = 0;
1032 			m_trxCtx.nRRY++;
1033 		}
1034 	}
1035 
1036 	return true;
1037 }
1038 
TransferWriteHandlerPSMT8H(const void * pData,uint32 nLength)1039 bool CGSHandler::TransferWriteHandlerPSMT8H(const void* pData, uint32 nLength)
1040 {
1041 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
1042 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
1043 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
1044 
1045 	CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, trxBuf.GetDstPtr(), trxBuf.nDstWidth);
1046 
1047 	auto pSrc = reinterpret_cast<const uint8*>(pData);
1048 
1049 	for(unsigned int i = 0; i < nLength; i++)
1050 	{
1051 		uint32 nX = (m_trxCtx.nRRX + trxPos.nDSAX) % 2048;
1052 		uint32 nY = (m_trxCtx.nRRY + trxPos.nDSAY) % 2048;
1053 
1054 		uint8 nSrcPixel = pSrc[i];
1055 
1056 		uint32* pDstPixel = Indexor.GetPixelAddress(nX, nY);
1057 		(*pDstPixel) &= ~0xFF000000;
1058 		(*pDstPixel) |= (nSrcPixel << 24);
1059 
1060 		m_trxCtx.nRRX++;
1061 		if(m_trxCtx.nRRX == trxReg.nRRW)
1062 		{
1063 			m_trxCtx.nRRX = 0;
1064 			m_trxCtx.nRRY++;
1065 		}
1066 	}
1067 
1068 	return true;
1069 }
1070 
TransferReadHandlerInvalid(void *,uint32)1071 void CGSHandler::TransferReadHandlerInvalid(void*, uint32)
1072 {
1073 	assert(0);
1074 }
1075 
1076 template <typename Storage>
TransferReadHandlerGeneric(void * buffer,uint32 length)1077 void CGSHandler::TransferReadHandlerGeneric(void* buffer, uint32 length)
1078 {
1079 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
1080 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
1081 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
1082 
1083 	uint32 typedLength = length / sizeof(typename Storage::Unit);
1084 	auto typedBuffer = reinterpret_cast<typename Storage::Unit*>(buffer);
1085 
1086 	CGsPixelFormats::CPixelIndexor<Storage> indexor(GetRam(), trxBuf.GetSrcPtr(), trxBuf.nSrcWidth);
1087 	for(uint32 i = 0; i < typedLength; i++)
1088 	{
1089 		uint32 x = (m_trxCtx.nRRX + trxPos.nSSAX) % 2048;
1090 		uint32 y = (m_trxCtx.nRRY + trxPos.nSSAY) % 2048;
1091 		auto pixel = indexor.GetPixel(x, y);
1092 		typedBuffer[i] = pixel;
1093 		m_trxCtx.nRRX++;
1094 		if(m_trxCtx.nRRX == trxReg.nRRW)
1095 		{
1096 			m_trxCtx.nRRX = 0;
1097 			m_trxCtx.nRRY++;
1098 		}
1099 	}
1100 }
1101 
TransferReadHandlerPSMCT24(void * buffer,uint32 length)1102 void CGSHandler::TransferReadHandlerPSMCT24(void* buffer, uint32 length)
1103 {
1104 	auto trxPos = make_convertible<TRXPOS>(m_nReg[GS_REG_TRXPOS]);
1105 	auto trxReg = make_convertible<TRXREG>(m_nReg[GS_REG_TRXREG]);
1106 	auto trxBuf = make_convertible<BITBLTBUF>(m_nReg[GS_REG_BITBLTBUF]);
1107 
1108 	auto dst = reinterpret_cast<uint8*>(buffer);
1109 
1110 	CGsPixelFormats::CPixelIndexorPSMCT32 indexor(GetRam(), trxBuf.GetSrcPtr(), trxBuf.nSrcWidth);
1111 	for(uint32 i = 0; i < length; i += 3)
1112 	{
1113 		uint32 x = (m_trxCtx.nRRX + trxPos.nSSAX) % 2048;
1114 		uint32 y = (m_trxCtx.nRRY + trxPos.nSSAY) % 2048;
1115 		auto pixel = indexor.GetPixel(x, y);
1116 		dst[i + 0] = (pixel >> 0) & 0xFF;
1117 		dst[i + 1] = (pixel >> 8) & 0xFF;
1118 		dst[i + 2] = (pixel >> 16) & 0xFF;
1119 		m_trxCtx.nRRX++;
1120 		if(m_trxCtx.nRRX == trxReg.nRRW)
1121 		{
1122 			m_trxCtx.nRRX = 0;
1123 			m_trxCtx.nRRY++;
1124 		}
1125 	}
1126 }
1127 
SetCrt(bool nIsInterlaced,unsigned int nMode,bool nIsFrameMode)1128 void CGSHandler::SetCrt(bool nIsInterlaced, unsigned int nMode, bool nIsFrameMode)
1129 {
1130 	m_crtMode = static_cast<CRT_MODE>(nMode);
1131 
1132 	SMODE2 smode2;
1133 	smode2 <<= 0;
1134 	smode2.interlaced = nIsInterlaced ? 1 : 0;
1135 	smode2.ffmd = nIsFrameMode ? 1 : 0;
1136 	m_nSMODE2 = smode2;
1137 }
1138 
GetCrtWidth() const1139 unsigned int CGSHandler::GetCrtWidth() const
1140 {
1141 	switch(m_crtMode)
1142 	{
1143 	default:
1144 		assert(false);
1145 		[[fallthrough]];
1146 	case CRT_MODE_NTSC:
1147 	case CRT_MODE_PAL:
1148 	case CRT_MODE_VGA_640_75:
1149 		return 640;
1150 	}
1151 }
1152 
GetCrtHeight() const1153 unsigned int CGSHandler::GetCrtHeight() const
1154 {
1155 	switch(m_crtMode)
1156 	{
1157 	default:
1158 		assert(false);
1159 		[[fallthrough]];
1160 	case CRT_MODE_NTSC:
1161 		return 448;
1162 	case CRT_MODE_PAL:
1163 		return 512;
1164 	case CRT_MODE_VGA_640_75:
1165 		return 480;
1166 	}
1167 }
1168 
GetCrtFrameRate() const1169 uint32 CGSHandler::GetCrtFrameRate() const
1170 {
1171 	switch(m_crtMode)
1172 	{
1173 	default:
1174 		assert(false);
1175 		[[fallthrough]];
1176 	case CRT_MODE_NTSC:
1177 		return 60;
1178 	case CRT_MODE_PAL:
1179 		return 50;
1180 	}
1181 }
1182 
GetCrtHSyncFrequency() const1183 uint32 CGSHandler::GetCrtHSyncFrequency() const
1184 {
1185 	switch(m_crtMode)
1186 	{
1187 	default:
1188 		assert(false);
1189 		[[fallthrough]];
1190 	case CRT_MODE_NTSC:
1191 		return PS2::GS_NTSC_HSYNC_FREQ;
1192 	case CRT_MODE_PAL:
1193 		return PS2::GS_PAL_HSYNC_FREQ;
1194 	}
1195 }
1196 
GetCrtIsInterlaced() const1197 bool CGSHandler::GetCrtIsInterlaced() const
1198 {
1199 	SMODE2 smode2;
1200 	smode2 <<= m_nSMODE2;
1201 	return smode2.interlaced;
1202 }
1203 
GetCrtIsFrameMode() const1204 bool CGSHandler::GetCrtIsFrameMode() const
1205 {
1206 	SMODE2 smode2;
1207 	smode2 <<= m_nSMODE2;
1208 	return smode2.ffmd;
1209 }
1210 
GetCurrentDisplayInfo()1211 std::pair<uint64, uint64> CGSHandler::GetCurrentDisplayInfo()
1212 {
1213 	std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
1214 	unsigned int readCircuit = GetCurrentReadCircuit();
1215 	switch(readCircuit)
1216 	{
1217 	default:
1218 	case 0:
1219 		return {m_nDISPFB1.value.q, m_nDISPLAY1.value.q};
1220 	case 1:
1221 		return {m_nDISPFB2.value.q, m_nDISPLAY2.value.q};
1222 	}
1223 }
1224 
GetCurrentReadCircuit()1225 unsigned int CGSHandler::GetCurrentReadCircuit()
1226 {
1227 	uint32 rcMode = m_nPMODE & 0x03;
1228 	switch(rcMode)
1229 	{
1230 	default:
1231 	case 0:
1232 		//No read circuit enabled?
1233 		return 0;
1234 	case 1:
1235 		return 0;
1236 	case 2:
1237 		return 1;
1238 	case 3:
1239 	{
1240 		//Both are enabled... See if we can find out which one is good
1241 		//This happens in Capcom Classics Collection Vol. 2
1242 		std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
1243 		bool fb1Null = (m_nDISPFB1.value.q == 0);
1244 		bool fb2Null = (m_nDISPFB2.value.q == 0);
1245 		if(!fb1Null && fb2Null)
1246 		{
1247 			return 0;
1248 		}
1249 		if(fb1Null && !fb2Null)
1250 		{
1251 			return 1;
1252 		}
1253 		return 0;
1254 	}
1255 	break;
1256 	}
1257 }
1258 
SyncCLUT(const TEX0 & tex0)1259 void CGSHandler::SyncCLUT(const TEX0& tex0)
1260 {
1261 	if(!ProcessCLD(tex0)) return;
1262 
1263 	//assert(IsPsmIDTEX(tex0.nPsm));
1264 	switch(tex0.nPsm)
1265 	{
1266 	case PSMT8:
1267 	case PSMT8H:
1268 		ReadCLUT8(tex0);
1269 		break;
1270 	case PSMT4:
1271 	case PSMT4HH:
1272 	case PSMT4HL:
1273 		ReadCLUT4(tex0);
1274 		break;
1275 	}
1276 }
1277 
ProcessCLD(const TEX0 & tex0)1278 bool CGSHandler::ProcessCLD(const TEX0& tex0)
1279 {
1280 	switch(tex0.nCLD)
1281 	{
1282 	case 0:
1283 		//No changes to CLUT
1284 		return false;
1285 	default:
1286 		assert(false);
1287 	case 1:
1288 		return true;
1289 	case 2:
1290 		m_nCBP0 = tex0.nCBP;
1291 		return true;
1292 	case 3:
1293 		m_nCBP1 = tex0.nCBP;
1294 		return true;
1295 	case 4:
1296 		if(m_nCBP0 == tex0.nCBP) return false;
1297 		m_nCBP0 = tex0.nCBP;
1298 		return true;
1299 	case 5:
1300 		if(m_nCBP1 == tex0.nCBP) return false;
1301 		m_nCBP1 = tex0.nCBP;
1302 		return true;
1303 	}
1304 }
1305 
1306 template <typename Indexor>
ReadCLUT4_16(const TEX0 & tex0)1307 bool CGSHandler::ReadCLUT4_16(const TEX0& tex0)
1308 {
1309 	bool changed = false;
1310 
1311 	assert(tex0.nCSA < 32);
1312 
1313 	Indexor indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
1314 	uint32 clutOffset = tex0.nCSA * 16;
1315 	uint16* pDst = m_pCLUT + clutOffset;
1316 
1317 	for(unsigned int j = 0; j < 2; j++)
1318 	{
1319 		for(unsigned int i = 0; i < 8; i++)
1320 		{
1321 			uint16 color = indexor.GetPixel(i, j);
1322 
1323 			if(*pDst != color)
1324 			{
1325 				changed = true;
1326 			}
1327 
1328 			(*pDst++) = color;
1329 		}
1330 	}
1331 
1332 	return changed;
1333 }
1334 
1335 template <typename Indexor>
ReadCLUT8_16(const TEX0 & tex0)1336 bool CGSHandler::ReadCLUT8_16(const TEX0& tex0)
1337 {
1338 	bool changed = false;
1339 
1340 	Indexor indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
1341 
1342 	for(unsigned int j = 0; j < 16; j++)
1343 	{
1344 		for(unsigned int i = 0; i < 16; i++)
1345 		{
1346 			uint16 color = indexor.GetPixel(i, j);
1347 
1348 			uint8 index = i + (j * 16);
1349 			index = (index & ~0x18) | ((index & 0x08) << 1) | ((index & 0x10) >> 1);
1350 
1351 			if(m_pCLUT[index] != color)
1352 			{
1353 				changed = true;
1354 			}
1355 
1356 			m_pCLUT[index] = color;
1357 		}
1358 	}
1359 
1360 	return changed;
1361 }
1362 
ReadCLUT4(const TEX0 & tex0)1363 void CGSHandler::ReadCLUT4(const TEX0& tex0)
1364 {
1365 	bool changed = false;
1366 
1367 	if(tex0.nCSM == 0)
1368 	{
1369 		//CSM1 mode
1370 		if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
1371 		{
1372 			assert(tex0.nCSA < 16);
1373 
1374 			CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
1375 			uint32 clutOffset = (tex0.nCSA & 0x0F) * 16;
1376 			uint16* pDst = m_pCLUT + clutOffset;
1377 
1378 			for(unsigned int j = 0; j < 2; j++)
1379 			{
1380 				for(unsigned int i = 0; i < 8; i++)
1381 				{
1382 					uint32 color = Indexor.GetPixel(i, j);
1383 					uint16 colorLo = static_cast<uint16>(color & 0xFFFF);
1384 					uint16 colorHi = static_cast<uint16>(color >> 16);
1385 
1386 					if(
1387 					    (pDst[0x000] != colorLo) ||
1388 					    (pDst[0x100] != colorHi))
1389 					{
1390 						changed = true;
1391 					}
1392 
1393 					pDst[0x000] = colorLo;
1394 					pDst[0x100] = colorHi;
1395 					pDst++;
1396 				}
1397 			}
1398 		}
1399 		else if(tex0.nCPSM == PSMCT16)
1400 		{
1401 			changed = ReadCLUT4_16<CGsPixelFormats::CPixelIndexorPSMCT16>(tex0);
1402 		}
1403 		else if(tex0.nCPSM == PSMCT16S)
1404 		{
1405 			changed = ReadCLUT4_16<CGsPixelFormats::CPixelIndexorPSMCT16S>(tex0);
1406 		}
1407 		else
1408 		{
1409 			assert(0);
1410 		}
1411 	}
1412 	else
1413 	{
1414 		//CSM2 mode
1415 		assert(tex0.nCPSM == PSMCT16);
1416 		assert(tex0.nCSA == 0);
1417 
1418 		auto texClut = make_convertible<TEXCLUT>(m_nReg[GS_REG_TEXCLUT]);
1419 
1420 		CGsPixelFormats::CPixelIndexorPSMCT16 Indexor(m_pRAM, tex0.GetCLUTPtr(), texClut.nCBW);
1421 		unsigned int nOffsetX = texClut.GetOffsetU();
1422 		unsigned int nOffsetY = texClut.GetOffsetV();
1423 		uint16* pDst = m_pCLUT;
1424 
1425 		for(unsigned int i = 0; i < 0x10; i++)
1426 		{
1427 			uint16 color = Indexor.GetPixel(nOffsetX + i, nOffsetY);
1428 
1429 			if(*pDst != color)
1430 			{
1431 				changed = true;
1432 			}
1433 
1434 			(*pDst++) = color;
1435 		}
1436 	}
1437 
1438 	if(changed)
1439 	{
1440 		ProcessClutTransfer(tex0.nCSA, 0);
1441 	}
1442 }
1443 
ReadCLUT8(const TEX0 & tex0)1444 void CGSHandler::ReadCLUT8(const TEX0& tex0)
1445 {
1446 	assert(tex0.nCSA == 0);
1447 
1448 	bool changed = false;
1449 
1450 	if(tex0.nCSM == 0)
1451 	{
1452 		if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
1453 		{
1454 			CGsPixelFormats::CPixelIndexorPSMCT32 Indexor(m_pRAM, tex0.GetCLUTPtr(), 1);
1455 
1456 			for(unsigned int j = 0; j < 16; j++)
1457 			{
1458 				for(unsigned int i = 0; i < 16; i++)
1459 				{
1460 					uint32 color = Indexor.GetPixel(i, j);
1461 					uint16 colorLo = static_cast<uint16>(color & 0xFFFF);
1462 					uint16 colorHi = static_cast<uint16>(color >> 16);
1463 
1464 					uint8 index = i + (j * 16);
1465 					index = (index & ~0x18) | ((index & 0x08) << 1) | ((index & 0x10) >> 1);
1466 
1467 					if(
1468 					    (m_pCLUT[index + 0x000] != colorLo) ||
1469 					    (m_pCLUT[index + 0x100] != colorHi))
1470 					{
1471 						changed = true;
1472 					}
1473 
1474 					m_pCLUT[index + 0x000] = colorLo;
1475 					m_pCLUT[index + 0x100] = colorHi;
1476 				}
1477 			}
1478 		}
1479 		else if(tex0.nCPSM == PSMCT16)
1480 		{
1481 			changed = ReadCLUT8_16<CGsPixelFormats::CPixelIndexorPSMCT16>(tex0);
1482 		}
1483 		else if(tex0.nCPSM == PSMCT16S)
1484 		{
1485 			changed = ReadCLUT8_16<CGsPixelFormats::CPixelIndexorPSMCT16S>(tex0);
1486 		}
1487 		else
1488 		{
1489 			assert(0);
1490 		}
1491 	}
1492 	else
1493 	{
1494 		//CSM2 mode
1495 		assert(tex0.nCPSM == PSMCT16);
1496 
1497 		auto texClut = make_convertible<TEXCLUT>(m_nReg[GS_REG_TEXCLUT]);
1498 
1499 		CGsPixelFormats::CPixelIndexorPSMCT16 indexor(m_pRAM, tex0.GetCLUTPtr(), texClut.nCBW);
1500 		unsigned int offsetX = texClut.GetOffsetU();
1501 		unsigned int offsetY = texClut.GetOffsetV();
1502 		uint16* dst = m_pCLUT;
1503 
1504 		for(unsigned int i = 0; i < 0x100; i++)
1505 		{
1506 			uint16 color = indexor.GetPixel(offsetX + i, offsetY);
1507 
1508 			if(*dst != color)
1509 			{
1510 				changed = true;
1511 			}
1512 
1513 			(*dst++) = color;
1514 		}
1515 	}
1516 
1517 	if(changed)
1518 	{
1519 		ProcessClutTransfer(tex0.nCSA, 0);
1520 	}
1521 }
1522 
IsCompatibleFramebufferPSM(unsigned int psmFb,unsigned int psmTex)1523 bool CGSHandler::IsCompatibleFramebufferPSM(unsigned int psmFb, unsigned int psmTex)
1524 {
1525 	if((psmTex == CGSHandler::PSMCT32) || (psmTex == CGSHandler::PSMCT24))
1526 	{
1527 		return (psmFb == CGSHandler::PSMCT32) || (psmFb == CGSHandler::PSMCT24);
1528 	}
1529 	else
1530 	{
1531 		return (psmFb == psmTex);
1532 	}
1533 }
1534 
MakeLinearCLUT(const TEX0 & tex0,std::array<uint32,256> & clut) const1535 void CGSHandler::MakeLinearCLUT(const TEX0& tex0, std::array<uint32, 256>& clut) const
1536 {
1537 	static const auto RGBA16ToRGBA32 =
1538 	    [](uint16 color) {
1539 		    return ((color & 0x8000) ? 0xFF000000 : 0) | ((color & 0x7C00) << 9) | ((color & 0x03E0) << 6) | ((color & 0x001F) << 3);
1540 	    };
1541 
1542 	unsigned int entryCount = CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm) ? 16 : 256;
1543 
1544 	if(CGsPixelFormats::IsPsmIDTEX4(tex0.nPsm))
1545 	{
1546 		if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
1547 		{
1548 			assert(tex0.nCSA < 16);
1549 			uint32 clutOffset = (tex0.nCSA & 0xF) * 16;
1550 
1551 			for(unsigned int i = 0; i < 16; i++)
1552 			{
1553 				uint32 color =
1554 				    (static_cast<uint16>(m_pCLUT[i + clutOffset + 0x000])) |
1555 				    (static_cast<uint16>(m_pCLUT[i + clutOffset + 0x100]) << 16);
1556 				clut[i] = color;
1557 			}
1558 		}
1559 		else if(tex0.nCPSM == PSMCT16 || tex0.nCPSM == PSMCT16S)
1560 		{
1561 			//CSA is 5-bit, shouldn't go over 31
1562 			assert(tex0.nCSA < 32);
1563 			uint32 clutOffset = tex0.nCSA * 16;
1564 
1565 			for(unsigned int i = 0; i < 16; i++)
1566 			{
1567 				clut[i] = RGBA16ToRGBA32(m_pCLUT[i + clutOffset]);
1568 			}
1569 		}
1570 		else
1571 		{
1572 			assert(false);
1573 		}
1574 	}
1575 	else if(CGsPixelFormats::IsPsmIDTEX8(tex0.nPsm))
1576 	{
1577 		if(tex0.nCPSM == PSMCT32 || tex0.nCPSM == PSMCT24)
1578 		{
1579 			for(unsigned int i = 0; i < 256; i++)
1580 			{
1581 				uint32 offset = ((tex0.nCSA * 16) + i) & 0xFF;
1582 				uint32 color =
1583 				    (static_cast<uint16>(m_pCLUT[offset + 0x000])) |
1584 				    (static_cast<uint16>(m_pCLUT[offset + 0x100]) << 16);
1585 				clut[i] = color;
1586 			}
1587 		}
1588 		else if(tex0.nCPSM == PSMCT16 || tex0.nCPSM == PSMCT16S)
1589 		{
1590 			assert(tex0.nCSA == 0);
1591 			for(unsigned int i = 0; i < 256; i++)
1592 			{
1593 				clut[i] = RGBA16ToRGBA32(m_pCLUT[i]);
1594 			}
1595 		}
1596 		else
1597 		{
1598 			assert(false);
1599 		}
1600 	}
1601 }
1602 
SetLoggingEnabled(bool loggingEnabled)1603 void CGSHandler::SetLoggingEnabled(bool loggingEnabled)
1604 {
1605 	m_loggingEnabled = loggingEnabled;
1606 }
1607 
DisassembleWrite(uint8 registerId,uint64 data)1608 std::string CGSHandler::DisassembleWrite(uint8 registerId, uint64 data)
1609 {
1610 	std::string result;
1611 
1612 	switch(registerId)
1613 	{
1614 	case GS_REG_PRIM:
1615 	{
1616 		auto pr = make_convertible<PRIM>(data);
1617 		result = string_format("PRIM(PRI: %i, IIP: %i, TME: %i, FGE: %i, ABE: %i, AA1: %i, FST: %i, CTXT: %i, FIX: %i)",
1618 		                       pr.nType, pr.nShading, pr.nTexture, pr.nFog, pr.nAlpha, pr.nAntiAliasing, pr.nUseUV, pr.nContext, pr.nUseFloat);
1619 	}
1620 	break;
1621 	case GS_REG_RGBAQ:
1622 	{
1623 		auto rgbaq = make_convertible<RGBAQ>(data);
1624 		result = string_format("RGBAQ(R: 0x%02X, G: 0x%02X, B: 0x%02X, A: 0x%02X, Q: %f)",
1625 		                       rgbaq.nR, rgbaq.nG, rgbaq.nB, rgbaq.nA, rgbaq.nQ);
1626 	}
1627 	break;
1628 	case GS_REG_ST:
1629 	{
1630 		auto st = make_convertible<ST>(data);
1631 		result = string_format("ST(S: %f, T: %f)",
1632 		                       st.nS, st.nT);
1633 	}
1634 	break;
1635 	case GS_REG_UV:
1636 	{
1637 		auto uv = make_convertible<UV>(data);
1638 		result = string_format("UV(U: %f, V: %f)",
1639 		                       uv.GetU(), uv.GetV());
1640 	}
1641 	break;
1642 	case GS_REG_XYZ2:
1643 	case GS_REG_XYZ3:
1644 	{
1645 		auto xyz = make_convertible<XYZ>(data);
1646 		result = string_format("%s(%f, %f, %f)",
1647 		                       (registerId == GS_REG_XYZ2) ? "XYZ2" : "XYZ3", xyz.GetX(), xyz.GetY(), xyz.GetZ());
1648 	}
1649 	break;
1650 	case GS_REG_XYZF2:
1651 	case GS_REG_XYZF3:
1652 	{
1653 		auto xyzf = make_convertible<XYZF>(data);
1654 		result = string_format("%s(%f, %f, %i, %i)",
1655 		                       (registerId == GS_REG_XYZF2) ? "XYZF2" : "XYZF3", xyzf.GetX(), xyzf.GetY(), xyzf.nZ, xyzf.nF);
1656 	}
1657 	break;
1658 	case GS_REG_FOG:
1659 	{
1660 		auto fog = static_cast<uint8>(data >> 56);
1661 		result = string_format("FOG(F: %d)", fog);
1662 	}
1663 	break;
1664 	case GS_REG_TEX0_1:
1665 	case GS_REG_TEX0_2:
1666 	{
1667 		auto tex0 = make_convertible<TEX0>(data);
1668 		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)",
1669 		                       (registerId == GS_REG_TEX0_1) ? 1 : 2, tex0.GetBufPtr(), tex0.GetBufWidth(), tex0.nPsm, tex0.GetWidth(), tex0.GetHeight(), tex0.nColorComp,
1670 		                       tex0.nFunction, tex0.GetCLUTPtr(), tex0.nCPSM, tex0.nCSM, tex0.nCSA, tex0.nCLD);
1671 	}
1672 	break;
1673 	case GS_REG_CLAMP_1:
1674 	case GS_REG_CLAMP_2:
1675 	{
1676 		auto clamp = make_convertible<CLAMP>(data);
1677 		result = string_format("CLAMP_%i(WMS: %i, WMT: %i, MINU: %i, MAXU: %i, MINV: %i, MAXV: %i)",
1678 		                       (registerId == GS_REG_CLAMP_1 ? 1 : 2), clamp.nWMS, clamp.nWMT, clamp.GetMinU(), clamp.GetMaxU(), clamp.GetMinV(), clamp.GetMaxV());
1679 	}
1680 	break;
1681 	case GS_REG_TEX1_1:
1682 	case GS_REG_TEX1_2:
1683 	{
1684 		auto tex1 = make_convertible<TEX1>(data);
1685 		result = string_format("TEX1_%i(LCM: %i, MXL: %i, MMAG: %i, MMIN: %i, MTBA: %i, L: %i, K: %i)",
1686 		                       (registerId == GS_REG_TEX1_1) ? 1 : 2, tex1.nLODMethod, tex1.nMaxMip, tex1.nMagFilter, tex1.nMinFilter, tex1.nMipBaseAddr, tex1.nLODL, tex1.nLODK);
1687 	}
1688 	break;
1689 	case GS_REG_TEX2_1:
1690 	case GS_REG_TEX2_2:
1691 	{
1692 		auto tex2 = make_convertible<TEX2>(data);
1693 		result = string_format("TEX2_%i(PSM: %i, CBP: 0x%08X, CPSM: %i, CSM: %i, CSA: %i, CLD: %i)",
1694 		                       (registerId == GS_REG_TEX2_1) ? 1 : 2, tex2.nPsm, tex2.GetCLUTPtr(), tex2.nCPSM, tex2.nCSM, tex2.nCSA, tex2.nCLD);
1695 	}
1696 	break;
1697 	case GS_REG_XYOFFSET_1:
1698 	case GS_REG_XYOFFSET_2:
1699 		result = string_format("XYOFFSET_%i(%i, %i)",
1700 		                       (registerId == GS_REG_XYOFFSET_1) ? 1 : 2, static_cast<uint32>(data >> 0), static_cast<uint32>(data >> 32));
1701 		break;
1702 	case GS_REG_PRMODECONT:
1703 		result = string_format("PRMODECONT(AC: %i)", data & 1);
1704 		break;
1705 	case GS_REG_PRMODE:
1706 	{
1707 		auto prm = make_convertible<PRMODE>(data);
1708 		result = string_format("PRMODE(IIP: %i, TME: %i, FGE: %i, ABE: %i, AA1: %i, FST: %i, CTXT: %i, FIX: %i)",
1709 		                       prm.nShading, prm.nTexture, prm.nFog, prm.nAlpha, prm.nAntiAliasing, prm.nUseUV, prm.nContext, prm.nUseFloat);
1710 	}
1711 	break;
1712 	case GS_REG_TEXCLUT:
1713 	{
1714 		auto clut = make_convertible<TEXCLUT>(data);
1715 		result = string_format("TEXCLUT(CBW: %i, COU: %i, COV: %i)",
1716 		                       clut.nCBW, clut.nCOU, clut.nCOV);
1717 	}
1718 	break;
1719 	case GS_REG_MIPTBP1_1:
1720 	case GS_REG_MIPTBP1_2:
1721 	{
1722 		auto miptbp1 = make_convertible<MIPTBP1>(data);
1723 		result = string_format("MIPTBP1_%d(TBP1: 0x%08X, TBW1: %d, TBP2: 0x%08X, TBW2: %d, TBP3: 0x%08X, TBW3: %d)",
1724 		                       (registerId == GS_REG_MIPTBP1_1) ? 1 : 2,
1725 		                       miptbp1.GetTbp1(), miptbp1.GetTbw1(),
1726 		                       miptbp1.GetTbp2(), miptbp1.GetTbw2(),
1727 		                       miptbp1.GetTbp3(), miptbp1.GetTbw3());
1728 	}
1729 	break;
1730 	case GS_REG_MIPTBP2_1:
1731 	case GS_REG_MIPTBP2_2:
1732 	{
1733 		auto miptbp2 = make_convertible<MIPTBP2>(data);
1734 		result = string_format("MIPTBP2_%d(TBP4: 0x%08X, TBW4: %d, TBP5: 0x%08X, TBW5: %d, TBP6: 0x%08X, TBW6: %d)",
1735 		                       (registerId == GS_REG_MIPTBP2_1) ? 1 : 2,
1736 		                       miptbp2.GetTbp4(), miptbp2.GetTbw4(),
1737 		                       miptbp2.GetTbp5(), miptbp2.GetTbw5(),
1738 		                       miptbp2.GetTbp6(), miptbp2.GetTbw6());
1739 	}
1740 	break;
1741 	case GS_REG_TEXA:
1742 	{
1743 		auto texa = make_convertible<TEXA>(data);
1744 		result = string_format("TEXA(TA0: 0x%02X, AEM: %i, TA1: 0x%02X)",
1745 		                       texa.nTA0, texa.nAEM, texa.nTA1);
1746 	}
1747 	break;
1748 	case GS_REG_FOGCOL:
1749 	{
1750 		auto fogcol = make_convertible<FOGCOL>(data);
1751 		result = string_format("FOGCOL(R: 0x%02X, G: 0x%02X, B: 0x%02X)",
1752 		                       fogcol.nFCR, fogcol.nFCG, fogcol.nFCB);
1753 	}
1754 	break;
1755 	case GS_REG_TEXFLUSH:
1756 		result = "TEXFLUSH()";
1757 		break;
1758 	case GS_REG_ALPHA_1:
1759 	case GS_REG_ALPHA_2:
1760 	{
1761 		auto alpha = make_convertible<ALPHA>(data);
1762 		result = string_format("ALPHA_%i(A: %i, B: %i, C: %i, D: %i, FIX: 0x%02X)",
1763 		                       (registerId == GS_REG_ALPHA_1) ? 1 : 2, alpha.nA, alpha.nB, alpha.nC, alpha.nD, alpha.nFix);
1764 	}
1765 	break;
1766 	case GS_REG_SCISSOR_1:
1767 	case GS_REG_SCISSOR_2:
1768 	{
1769 		auto scissor = make_convertible<SCISSOR>(data);
1770 		result = string_format("SCISSOR_%i(SCAX0: %i, SCAX1: %i, SCAY0: %i, SCAY1: %i)",
1771 		                       (registerId == GS_REG_SCISSOR_1) ? 1 : 2, scissor.scax0, scissor.scax1, scissor.scay0, scissor.scay1);
1772 	}
1773 	break;
1774 	case GS_REG_COLCLAMP:
1775 		result = string_format("COLCLAMP(CLAMP: %d)", data & 1);
1776 		break;
1777 	case GS_REG_TEST_1:
1778 	case GS_REG_TEST_2:
1779 	{
1780 		auto tst = make_convertible<TEST>(data);
1781 		result = string_format("TEST_%i(ATE: %i, ATST: %i, AREF: 0x%02X, AFAIL: %i, DATE: %i, DATM: %i, ZTE: %i, ZTST: %i)",
1782 		                       (registerId == GS_REG_TEST_1) ? 1 : 2, tst.nAlphaEnabled, tst.nAlphaMethod, tst.nAlphaRef, tst.nAlphaFail,
1783 		                       tst.nDestAlphaEnabled, tst.nDestAlphaMode, tst.nDepthEnabled, tst.nDepthMethod);
1784 	}
1785 	break;
1786 	case GS_REG_PABE:
1787 	{
1788 		auto value = static_cast<uint8>(data & 1);
1789 		result = string_format("PABE(PABE: %d)", value);
1790 	}
1791 	break;
1792 	case GS_REG_FBA_1:
1793 	case GS_REG_FBA_2:
1794 	{
1795 		auto value = static_cast<uint8>(data & 1);
1796 		result = string_format("FBA_%d(FBA: %d)", (registerId == GS_REG_FBA_1) ? 1 : 2, value);
1797 	}
1798 	break;
1799 	case GS_REG_FRAME_1:
1800 	case GS_REG_FRAME_2:
1801 	{
1802 		auto fr = make_convertible<FRAME>(data);
1803 		result = string_format("FRAME_%i(FBP: 0x%08X, FBW: %d, PSM: %d, FBMSK: 0x%08X)",
1804 		                       (registerId == GS_REG_FRAME_1) ? 1 : 2, fr.GetBasePtr(), fr.GetWidth(), fr.nPsm, fr.nMask);
1805 	}
1806 	break;
1807 	case GS_REG_ZBUF_1:
1808 	case GS_REG_ZBUF_2:
1809 	{
1810 		auto zbuf = make_convertible<ZBUF>(data);
1811 		result = string_format("ZBUF_%i(ZBP: 0x%08X, PSM: %i, ZMSK: %i)",
1812 		                       (registerId == GS_REG_ZBUF_1) ? 1 : 2, zbuf.GetBasePtr(), zbuf.nPsm, zbuf.nMask);
1813 	}
1814 	break;
1815 	case GS_REG_BITBLTBUF:
1816 	{
1817 		auto buf = make_convertible<BITBLTBUF>(data);
1818 		result = string_format("BITBLTBUF(SBP: 0x%08X, SBW: %i, SPSM: %i, DBP: 0x%08X, DBW: %i, DPSM: %i)",
1819 		                       buf.GetSrcPtr(), buf.GetSrcWidth(), buf.nSrcPsm, buf.GetDstPtr(), buf.GetDstWidth(), buf.nDstPsm);
1820 	}
1821 	break;
1822 	case GS_REG_TRXPOS:
1823 	{
1824 		auto trxPos = make_convertible<TRXPOS>(data);
1825 		result = string_format("TRXPOS(SSAX: %i, SSAY: %i, DSAX: %i, DSAY: %i, DIR: %i)",
1826 		                       trxPos.nSSAX, trxPos.nSSAY, trxPos.nDSAX, trxPos.nDSAY, trxPos.nDIR);
1827 	}
1828 	break;
1829 	case GS_REG_TRXREG:
1830 	{
1831 		auto trxReg = make_convertible<TRXREG>(data);
1832 		result = string_format("TRXREG(RRW: %i, RRH: %i)",
1833 		                       trxReg.nRRW, trxReg.nRRH);
1834 	}
1835 	break;
1836 	case GS_REG_TRXDIR:
1837 		result = string_format("TRXDIR(XDIR: %i)", data & 0x03);
1838 		break;
1839 	case GS_REG_HWREG:
1840 		result = string_format("HWREG(DATA: 0x%016llX)", data);
1841 		break;
1842 	case GS_REG_SIGNAL:
1843 	{
1844 		auto signal = make_convertible<SIGNAL>(data);
1845 		result = string_format("SIGNAL(IDMSK: 0x%08X, ID: 0x%08X)",
1846 		                       signal.idmsk, signal.id);
1847 	}
1848 	break;
1849 	case GS_REG_FINISH:
1850 		result = "FINISH()";
1851 		break;
1852 	case GS_REG_LABEL:
1853 	{
1854 		auto label = make_convertible<LABEL>(data);
1855 		result = string_format("LABEL(IDMSK: 0x%08X, ID: 0x%08X)",
1856 		                       label.idmsk, label.id);
1857 	}
1858 	break;
1859 	default:
1860 		result = string_format("(Unknown register: 0x%02X)", registerId);
1861 		break;
1862 	}
1863 
1864 	return result;
1865 }
1866 
LogWrite(uint8 registerId,uint64 data)1867 void CGSHandler::LogWrite(uint8 registerId, uint64 data)
1868 {
1869 	if(!m_loggingEnabled) return;
1870 	auto disassembledWrite = DisassembleWrite(registerId, data);
1871 	CLog::GetInstance().Print(LOG_NAME, "%s\r\n", disassembledWrite.c_str());
1872 }
1873 
LogPrivateWrite(uint32 address)1874 void CGSHandler::LogPrivateWrite(uint32 address)
1875 {
1876 	assert((address & 0x04) != 0);
1877 
1878 	uint32 regAddress = address & ~0x0F;
1879 	switch(regAddress)
1880 	{
1881 	case GS_PMODE:
1882 		CLog::GetInstance().Print(LOG_NAME, "PMODE(0x%08X);\r\n", m_nPMODE);
1883 		break;
1884 	case GS_SMODE2:
1885 	{
1886 		SMODE2 smode2;
1887 		smode2 <<= m_nSMODE2;
1888 		CLog::GetInstance().Print(LOG_NAME, "SMODE2(inter = %d, ffmd = %d, dpms = %d);\r\n",
1889 		                          smode2.interlaced,
1890 		                          smode2.ffmd,
1891 		                          smode2.dpms);
1892 	}
1893 	break;
1894 	case GS_DISPFB1:
1895 	case GS_DISPFB2:
1896 	{
1897 		DISPFB dispfb;
1898 		dispfb <<= (regAddress == GS_DISPFB1) ? m_nDISPFB1.value.q : m_nDISPFB2.value.q;
1899 		CLog::GetInstance().Print(LOG_NAME, "DISPFB%d(FBP: 0x%08X, FBW: %d, PSM: %d, DBX: %d, DBY: %d);\r\n",
1900 		                          (regAddress == GS_DISPFB1) ? 1 : 2,
1901 		                          dispfb.GetBufPtr(),
1902 		                          dispfb.GetBufWidth(),
1903 		                          dispfb.nPSM,
1904 		                          dispfb.nX,
1905 		                          dispfb.nY);
1906 	}
1907 	break;
1908 	case GS_DISPLAY1:
1909 	case GS_DISPLAY2:
1910 	{
1911 		DISPLAY display;
1912 		display <<= (regAddress == GS_DISPLAY1) ? m_nDISPLAY1.value.q : m_nDISPLAY2.value.q;
1913 		CLog::GetInstance().Print(LOG_NAME, "DISPLAY%d(DX: %d, DY: %d, MAGH: %d, MAGV: %d, DW: %d, DH: %d);\r\n",
1914 		                          (regAddress == GS_DISPLAY1) ? 1 : 2,
1915 		                          display.nX,
1916 		                          display.nY,
1917 		                          display.nMagX,
1918 		                          display.nMagY,
1919 		                          display.nW,
1920 		                          display.nH);
1921 	}
1922 	break;
1923 	case GS_CSR:
1924 	case GS_CSR_ALT:
1925 		//CSR
1926 		break;
1927 	case GS_IMR:
1928 		CLog::GetInstance().Print(LOG_NAME, "IMR(0x%08X);\r\n", m_nIMR);
1929 		break;
1930 	}
1931 }
1932 
WriteToDelayedRegister(uint32 address,uint32 value,DELAYED_REGISTER & delayedRegister)1933 void CGSHandler::WriteToDelayedRegister(uint32 address, uint32 value, DELAYED_REGISTER& delayedRegister)
1934 {
1935 	if(address & 0x04)
1936 	{
1937 		std::lock_guard<std::recursive_mutex> registerMutexLock(m_registerMutex);
1938 		delayedRegister.value.d0 = delayedRegister.heldValue;
1939 		delayedRegister.value.d1 = value;
1940 	}
1941 	else
1942 	{
1943 		delayedRegister.heldValue = value;
1944 	}
1945 }
1946 
ThreadProc()1947 void CGSHandler::ThreadProc()
1948 {
1949 	while(!m_threadDone)
1950 	{
1951 		m_mailBox.WaitForCall();
1952 		while(m_mailBox.IsPending())
1953 		{
1954 			m_mailBox.ReceiveCall();
1955 		}
1956 	}
1957 }
1958 
SendGSCall(const CMailBox::FunctionType & function,bool waitForCompletion,bool forceWaitForCompletion)1959 void CGSHandler::SendGSCall(const CMailBox::FunctionType& function, bool waitForCompletion, bool forceWaitForCompletion)
1960 {
1961 	if(!m_gsThreaded)
1962 	{
1963 		waitForCompletion = false;
1964 	}
1965 	waitForCompletion |= forceWaitForCompletion;
1966 	m_mailBox.SendCall(function, waitForCompletion);
1967 }
1968 
SendGSCall(CMailBox::FunctionType && function)1969 void CGSHandler::SendGSCall(CMailBox::FunctionType&& function)
1970 {
1971 	m_mailBox.SendCall(std::move(function));
1972 }
1973 
ProcessSingleFrame()1974 void CGSHandler::ProcessSingleFrame()
1975 {
1976 	assert(!m_gsThreaded);
1977 	assert(!m_flipped);
1978 	while(!m_flipped)
1979 	{
1980 		m_mailBox.WaitForCall();
1981 		while(m_mailBox.IsPending() && !m_flipped)
1982 		{
1983 			m_mailBox.ReceiveCall();
1984 		}
1985 	}
1986 	m_flipped = false;
1987 }
1988 
GetScreenshot()1989 Framework::CBitmap CGSHandler::GetScreenshot()
1990 {
1991 	throw std::runtime_error("Screenshot feature is not implemented in current backend.");
1992 }
1993