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