1 #include <cassert>
2 #include <cstring>
3 #include <climits>
4 #include <stdexcept>
5 #include "string_format.h"
6 #include "../Log.h"
7 #include "../Ps2Const.h"
8 #include "../states/RegisterStateFile.h"
9 #include "../states/MemoryStateFile.h"
10 #include "Vpu.h"
11 #include "Vif.h"
12 #include "INTC.h"
13
14 #define LOG_NAME ("ee_vif")
15
16 #define STATE_PATH_REGS_FORMAT ("vpu/vif_%d.xml")
17 #define STATE_PATH_FIFO_FORMAT ("vpu/vif_%d_fifo")
18
19 #define STATE_REGS_STAT ("STAT")
20 #define STATE_REGS_CODE ("CODE")
21 #define STATE_REGS_CYCLE ("CYCLE")
22 #define STATE_REGS_NUM ("NUM")
23 #define STATE_REGS_MASK ("MASK")
24 #define STATE_REGS_MODE ("MODE")
25 #define STATE_REGS_ROW0 ("ROW0")
26 #define STATE_REGS_ROW1 ("ROW1")
27 #define STATE_REGS_ROW2 ("ROW2")
28 #define STATE_REGS_ROW3 ("ROW3")
29 #define STATE_REGS_COL0 ("COL0")
30 #define STATE_REGS_COL1 ("COL1")
31 #define STATE_REGS_COL2 ("COL2")
32 #define STATE_REGS_COL3 ("COL3")
33 #define STATE_REGS_MARK ("MARK")
34 #define STATE_REGS_ITOP ("ITOP")
35 #define STATE_REGS_ITOPS ("ITOPS")
36 #define STATE_REGS_READTICK ("readTick")
37 #define STATE_REGS_WRITETICK ("writeTick")
38 #define STATE_REGS_PENDINGMICROPROGRAM ("pendingMicroProgram")
39 #define STATE_REGS_FIFOINDEX ("fifoIndex")
40 #define STATE_REGS_INCOMINGFIFODELAY ("incomingFifoDelay")
41
CVif(unsigned int number,CVpu & vpu,CINTC & intc,uint8 * ram,uint8 * spr)42 CVif::CVif(unsigned int number, CVpu& vpu, CINTC& intc, uint8* ram, uint8* spr)
43 : m_number(number)
44 , m_ram(ram)
45 , m_spr(spr)
46 , m_intc(intc)
47 , m_stream(ram, spr)
48 , m_vpu(vpu)
49 , m_vifProfilerZone(CProfiler::GetInstance().RegisterZone(string_format("VIF%d", number).c_str()))
50 {
51 }
52
Reset()53 void CVif::Reset()
54 {
55 memset(&m_STAT, 0, sizeof(STAT));
56 memset(&m_CODE, 0, sizeof(CODE));
57 memset(&m_CYCLE, 0, sizeof(CYCLE));
58 memset(&m_R, 0, sizeof(m_R));
59 memset(&m_C, 0, sizeof(m_C));
60 memset(&m_fifoBuffer, 0, sizeof(m_fifoBuffer));
61 m_CYCLE.nCL = 1;
62 m_CYCLE.nWL = 1;
63 m_fifoIndex = 0;
64 m_MODE = 0;
65 m_NUM = 0;
66 m_MASK = 0;
67 m_MARK = 0;
68 m_ITOP = 0;
69 m_ITOPS = 0;
70 m_readTick = 0;
71 m_writeTick = 0;
72 m_stream.Reset();
73 m_pendingMicroProgram = -1;
74 m_incomingFifoDelay = 0;
75 }
76
GetRegister(uint32 address)77 uint32 CVif::GetRegister(uint32 address)
78 {
79 uint32 result = 0;
80 switch(address)
81 {
82 case VIF0_STAT:
83 case VIF1_STAT:
84 result = m_STAT;
85 if(m_STAT.nFDR != 0)
86 {
87 //When FDR is set, it usually means the game is trying to
88 //read data from GS and that FIFO has some data in it
89 //Set FQC (amount of quadwords in FIFO) to something to
90 //let the game know the transfer is being executed
91 //Games sensitive to this behavior:
92 //- Serious Sam: Takes screen shots at checkpoints, waits for FQC to become 0
93 //- PS2PSXe: Takes a screen shot of what is currently on screen, waits for FQC to become 0
94 //- There are games that will check that it's not zero once FDR is set.
95 result |= (m_incomingFifoDelay << 24);
96 if(m_incomingFifoDelay != 0)
97 {
98 m_incomingFifoDelay--;
99 }
100 }
101 break;
102 case VIF0_ERR:
103 case VIF1_ERR:
104 result = m_ERR;
105 break;
106 case VIF0_MARK:
107 case VIF1_MARK:
108 result = m_MARK;
109 break;
110 case VIF0_CYCLE:
111 case VIF1_CYCLE:
112 result = m_CYCLE;
113 break;
114 case VIF0_MODE:
115 case VIF1_MODE:
116 result = m_MODE;
117 break;
118 case VIF0_NUM:
119 case VIF1_NUM:
120 result = m_NUM;
121 break;
122 case VIF0_MASK:
123 case VIF1_MASK:
124 result = m_MASK;
125 break;
126 case VIF0_CODE:
127 case VIF1_CODE:
128 result = m_CODE;
129 break;
130 case VIF0_R0:
131 case VIF1_R0:
132 result = m_R[0];
133 break;
134 case VIF0_R1:
135 case VIF1_R1:
136 result = m_R[1];
137 break;
138 case VIF0_R2:
139 case VIF1_R2:
140 result = m_R[2];
141 break;
142 case VIF0_R3:
143 case VIF1_R3:
144 result = m_R[3];
145 break;
146 default:
147 CLog::GetInstance().Warn(LOG_NAME, "Reading unknown register 0x%08X.\r\n", address);
148 break;
149 }
150 #ifdef _DEBUG
151 DisassembleGet(address);
152 #endif
153 return result;
154 }
155
SetRegister(uint32 address,uint32 value)156 void CVif::SetRegister(uint32 address, uint32 value)
157 {
158 if(
159 (address >= VIF0_FIFO_START && address < VIF0_FIFO_END) ||
160 (address >= VIF1_FIFO_START && address < VIF1_FIFO_END))
161 {
162 ProcessFifoWrite(address, value);
163 }
164 else
165 {
166 switch(address)
167 {
168 case VIF1_STAT:
169 m_STAT.nFDR = ((value & STAT_FDR) != 0) ? 1 : 0;
170 if(m_STAT.nFDR)
171 {
172 m_incomingFifoDelay = 0x1F;
173 }
174 break;
175 case VIF0_FBRST:
176 case VIF1_FBRST:
177 if(value & FBRST_RST)
178 {
179 //TODO: Reset FIFO
180 m_CODE <<= 0;
181 m_STAT <<= 0;
182 m_NUM = 0;
183 }
184 if(value & FBRST_FBK || value & FBRST_STP)
185 {
186 // TODO: We need to properly handle this!
187 // But I lack games which leverage it.
188 assert(0);
189 }
190 if(value & FBRST_STC)
191 {
192 m_STAT.nVSS = 0;
193 m_STAT.nVFS = 0;
194 m_STAT.nVIS = 0;
195 m_STAT.nINT = 0;
196 m_STAT.nER0 = 0;
197 m_STAT.nER1 = 0;
198 }
199 break;
200 case VIF0_ERR:
201 case VIF1_ERR:
202 m_ERR <<= value;
203 break;
204 case VIF0_MARK:
205 case VIF1_MARK:
206 m_MARK = value;
207 m_STAT.nMRK = 0;
208 break;
209 default:
210 CLog::GetInstance().Warn(LOG_NAME, "Writing unknown register 0x%08X, 0x%08X.\r\n", address, value);
211 break;
212 }
213 }
214 #ifdef _DEBUG
215 DisassembleSet(address, value);
216 #endif
217 }
218
SaveState(Framework::CZipArchiveWriter & archive)219 void CVif::SaveState(Framework::CZipArchiveWriter& archive)
220 {
221 {
222 auto path = string_format(STATE_PATH_REGS_FORMAT, m_number);
223 auto registerFile = new CRegisterStateFile(path.c_str());
224 registerFile->SetRegister32(STATE_REGS_STAT, m_STAT);
225 registerFile->SetRegister32(STATE_REGS_CODE, m_CODE);
226 registerFile->SetRegister32(STATE_REGS_CYCLE, m_CYCLE);
227 registerFile->SetRegister32(STATE_REGS_NUM, m_NUM);
228 registerFile->SetRegister32(STATE_REGS_MODE, m_MODE);
229 registerFile->SetRegister32(STATE_REGS_MASK, m_MASK);
230 registerFile->SetRegister32(STATE_REGS_MARK, m_MARK);
231 registerFile->SetRegister32(STATE_REGS_ROW0, m_R[0]);
232 registerFile->SetRegister32(STATE_REGS_ROW1, m_R[1]);
233 registerFile->SetRegister32(STATE_REGS_ROW2, m_R[2]);
234 registerFile->SetRegister32(STATE_REGS_ROW3, m_R[3]);
235 registerFile->SetRegister32(STATE_REGS_COL0, m_C[0]);
236 registerFile->SetRegister32(STATE_REGS_COL1, m_C[1]);
237 registerFile->SetRegister32(STATE_REGS_COL2, m_C[2]);
238 registerFile->SetRegister32(STATE_REGS_COL3, m_C[3]);
239 registerFile->SetRegister32(STATE_REGS_ITOP, m_ITOP);
240 registerFile->SetRegister32(STATE_REGS_ITOPS, m_ITOPS);
241 registerFile->SetRegister32(STATE_REGS_READTICK, m_readTick);
242 registerFile->SetRegister32(STATE_REGS_WRITETICK, m_writeTick);
243 registerFile->SetRegister32(STATE_REGS_PENDINGMICROPROGRAM, m_pendingMicroProgram);
244 registerFile->SetRegister32(STATE_REGS_FIFOINDEX, m_fifoIndex);
245 registerFile->SetRegister32(STATE_REGS_INCOMINGFIFODELAY, m_incomingFifoDelay);
246 archive.InsertFile(registerFile);
247 }
248 {
249 auto path = string_format(STATE_PATH_FIFO_FORMAT, m_number);
250 archive.InsertFile(new CMemoryStateFile(path.c_str(), &m_fifoBuffer, sizeof(m_fifoBuffer)));
251 }
252 }
253
LoadState(Framework::CZipArchiveReader & archive)254 void CVif::LoadState(Framework::CZipArchiveReader& archive)
255 {
256 {
257 auto path = string_format(STATE_PATH_REGS_FORMAT, m_number);
258 CRegisterStateFile registerFile(*archive.BeginReadFile(path.c_str()));
259 m_STAT <<= registerFile.GetRegister32(STATE_REGS_STAT);
260 m_CODE <<= registerFile.GetRegister32(STATE_REGS_CODE);
261 m_CYCLE <<= registerFile.GetRegister32(STATE_REGS_CYCLE);
262 m_NUM = static_cast<uint8>(registerFile.GetRegister32(STATE_REGS_NUM));
263 m_MODE = registerFile.GetRegister32(STATE_REGS_MODE);
264 m_MASK = registerFile.GetRegister32(STATE_REGS_MASK);
265 m_MARK = registerFile.GetRegister32(STATE_REGS_MARK);
266 m_R[0] = registerFile.GetRegister32(STATE_REGS_ROW0);
267 m_R[1] = registerFile.GetRegister32(STATE_REGS_ROW1);
268 m_R[2] = registerFile.GetRegister32(STATE_REGS_ROW2);
269 m_R[3] = registerFile.GetRegister32(STATE_REGS_ROW3);
270 m_C[0] = registerFile.GetRegister32(STATE_REGS_COL0);
271 m_C[1] = registerFile.GetRegister32(STATE_REGS_COL1);
272 m_C[2] = registerFile.GetRegister32(STATE_REGS_COL2);
273 m_C[3] = registerFile.GetRegister32(STATE_REGS_COL3);
274 m_ITOP = registerFile.GetRegister32(STATE_REGS_ITOP);
275 m_ITOPS = registerFile.GetRegister32(STATE_REGS_ITOPS);
276 m_readTick = registerFile.GetRegister32(STATE_REGS_READTICK);
277 m_writeTick = registerFile.GetRegister32(STATE_REGS_WRITETICK);
278 m_pendingMicroProgram = registerFile.GetRegister32(STATE_REGS_PENDINGMICROPROGRAM);
279 m_fifoIndex = registerFile.GetRegister32(STATE_REGS_FIFOINDEX);
280 m_incomingFifoDelay = registerFile.GetRegister32(STATE_REGS_INCOMINGFIFODELAY);
281 }
282 {
283 auto path = string_format(STATE_PATH_FIFO_FORMAT, m_number);
284 archive.BeginReadFile(path.c_str())->Read(&m_fifoBuffer, sizeof(m_fifoBuffer));
285 }
286 }
287
GetTOP() const288 uint32 CVif::GetTOP() const
289 {
290 throw std::exception();
291 }
292
GetITOP() const293 uint32 CVif::GetITOP() const
294 {
295 return m_ITOP;
296 }
297
ReceiveDMA(uint32 address,uint32 qwc,uint32 unused,bool tagIncluded)298 uint32 CVif::ReceiveDMA(uint32 address, uint32 qwc, uint32 unused, bool tagIncluded)
299 {
300 if(m_STAT.nVEW && m_vpu.IsVuRunning())
301 {
302 //Is waiting for program end, don't bother
303 return 0;
304 }
305
306 #ifdef PROFILE
307 CProfilerZone profilerZone(m_vifProfilerZone);
308 #endif
309
310 #ifdef _DEBUG
311 CLog::GetInstance().Print(LOG_NAME, "vif%i : Processing packet @ 0x%08X, qwc = 0x%X, tagIncluded = %i\r\n",
312 m_number, address, qwc, static_cast<int>(tagIncluded));
313 #endif
314
315 m_stream.SetDmaParams(address, qwc * 0x10, tagIncluded);
316
317 ProcessPacket(m_stream);
318
319 uint32 remainingSize = m_stream.GetRemainingDmaTransferSize();
320 assert((remainingSize & 0x0F) == 0);
321 remainingSize /= 0x10;
322
323 return qwc - remainingSize;
324 }
325
IsWaitingForProgramEnd() const326 bool CVif::IsWaitingForProgramEnd() const
327 {
328 return (m_STAT.nVEW != 0);
329 }
330
ProcessFifoWrite(uint32 address,uint32 value)331 void CVif::ProcessFifoWrite(uint32 address, uint32 value)
332 {
333 assert(m_fifoIndex != FIFO_SIZE);
334 if(m_fifoIndex == FIFO_SIZE)
335 {
336 return;
337 }
338 uint32 index = (address & 0xF) / 4;
339 *reinterpret_cast<uint32*>(m_fifoBuffer + m_fifoIndex + index * 4) = value;
340 if(index == 3)
341 {
342 m_fifoIndex += 0x10;
343 m_stream.SetFifoParams(m_fifoBuffer, m_fifoIndex);
344 ProcessPacket(m_stream);
345 uint32 newIndex = m_stream.GetRemainingDmaTransferSize();
346 uint32 discardSize = m_fifoIndex - newIndex;
347 memmove(m_fifoBuffer, m_fifoBuffer + discardSize, newIndex);
348 m_fifoIndex = newIndex;
349 }
350 }
351
ProcessPacket(StreamType & stream)352 void CVif::ProcessPacket(StreamType& stream)
353 {
354 while(stream.GetAvailableReadBytes())
355 {
356 if(m_STAT.nVPS == 1)
357 {
358 //Command is waiting for more data...
359 ExecuteCommand(stream, m_CODE);
360
361 if((m_STAT.nVPS == 1) && (stream.GetAvailableReadBytes() != 0))
362 {
363 //We have data in our FIFO but we still need more than what's available
364 break;
365 }
366 else
367 {
368 continue;
369 }
370 }
371 if(m_STAT.nVEW == 1)
372 {
373 if(m_vpu.IsVuRunning()) break;
374 m_STAT.nVEW = 0;
375 //Command is waiting for micro-program to end.
376 ExecuteCommand(stream, m_CODE);
377 continue;
378 }
379
380 if(m_STAT.nVIS)
381 {
382 break;
383 }
384
385 stream.Read(&m_CODE, sizeof(CODE));
386
387 if(m_CODE.nI != 0)
388 {
389 //Next command will be stalled (if not MARK)
390 if(m_CODE.nCMD != CODE_CMD_MARK)
391 {
392 m_STAT.nVIS = 1;
393 }
394 m_STAT.nINT = 1;
395 m_intc.AssertLine(CINTC::INTC_LINE_VIF0 + m_number);
396 }
397
398 m_NUM = m_CODE.nNUM;
399
400 ExecuteCommand(stream, m_CODE);
401 }
402
403 if(stream.GetAvailableReadBytes() == 0)
404 {
405 ResumeDelayedMicroProgram();
406 }
407 }
408
ExecuteCommand(StreamType & stream,CODE nCommand)409 void CVif::ExecuteCommand(StreamType& stream, CODE nCommand)
410 {
411 #ifdef _DEBUG
412 if(m_number == 0)
413 {
414 DisassembleCommand(nCommand);
415 }
416 #endif
417 if(nCommand.nCMD >= 0x60)
418 {
419 Cmd_UNPACK(stream, nCommand, (nCommand.nIMM & 0x03FF));
420 return;
421 }
422 switch(nCommand.nCMD)
423 {
424 case 0:
425 //NOP
426 break;
427 case 0x01:
428 //STCYCL
429 m_CYCLE <<= nCommand.nIMM;
430 break;
431 case 0x04:
432 //ITOP
433 if(ResumeDelayedMicroProgram())
434 {
435 m_STAT.nVEW = 1;
436 return;
437 }
438 m_ITOPS = nCommand.nIMM & 0x3FF;
439 break;
440 case 0x05:
441 //STMOD
442 m_MODE = nCommand.nIMM & 0x03;
443 break;
444 case CODE_CMD_MARK:
445 m_MARK = nCommand.nIMM;
446 m_STAT.nMRK = 1;
447 break;
448 case 0x10:
449 //FLUSHE
450 if(m_vpu.IsVuRunning())
451 {
452 m_STAT.nVEW = 1;
453 }
454 else
455 {
456 m_STAT.nVEW = 0;
457 }
458 if(ResumeDelayedMicroProgram())
459 {
460 m_STAT.nVEW = 1;
461 return;
462 }
463 break;
464 case 0x14:
465 //MSCAL
466 if(ResumeDelayedMicroProgram())
467 {
468 m_STAT.nVEW = 1;
469 return;
470 }
471 StartDelayedMicroProgram(nCommand.nIMM * 8);
472 break;
473 case 0x15:
474 //MSCALF
475 //TODO: Wait for GIF PATH 1 and 2 transfers to be over
476 if(ResumeDelayedMicroProgram())
477 {
478 m_STAT.nVEW = 1;
479 return;
480 }
481 StartMicroProgram(nCommand.nIMM * 8);
482 break;
483 case 0x17:
484 //MSCNT
485 if(ResumeDelayedMicroProgram())
486 {
487 m_STAT.nVEW = 1;
488 return;
489 }
490 StartMicroProgram(m_vpu.GetContext().m_State.nPC);
491 break;
492 case 0x20:
493 //STMASK
494 Cmd_STMASK(stream, nCommand);
495 break;
496 case 0x30:
497 //STROW
498 Cmd_STROW(stream, nCommand);
499 break;
500 case 0x31:
501 //STCOL
502 Cmd_STCOL(stream, nCommand);
503 break;
504 case 0x4A:
505 //MPG
506 Cmd_MPG(stream, nCommand);
507 break;
508 default:
509 CLog::GetInstance().Warn(LOG_NAME, "Executed invalid command %d.\r\n", nCommand.nCMD);
510 m_STAT.nER1 = 1;
511 break;
512 }
513 }
514
Cmd_MPG(StreamType & stream,CODE nCommand)515 void CVif::Cmd_MPG(StreamType& stream, CODE nCommand)
516 {
517 uint32 nSize = stream.GetAvailableReadBytes();
518
519 uint32 nNum = (m_NUM == 0) ? (256) : (m_NUM);
520 uint32 nCodeNum = (m_CODE.nNUM == 0) ? (256) : (m_CODE.nNUM);
521 uint32 nTransfered = (nCodeNum - nNum) * 8;
522
523 nCodeNum *= 8;
524 nNum *= 8;
525
526 nSize = std::min<uint32>(nNum, nSize);
527
528 uint32 nDstAddr = (m_CODE.nIMM * 8) + nTransfered;
529 nDstAddr &= (m_vpu.GetMicroMemorySize() - 1);
530
531 //Check if microprogram is running
532 if(m_vpu.IsVuRunning())
533 {
534 m_STAT.nVEW = 1;
535 return;
536 }
537
538 if(nSize != 0)
539 {
540 auto microMem = m_vpu.GetMicroMemory();
541 auto copyToMicroMem =
542 [&](const uint8* microProgramPtr, uint32 start, uint32 size) {
543 //Check if there's a change
544 if(memcmp(microMem + start, microProgramPtr, size) != 0)
545 {
546 m_vpu.InvalidateMicroProgram(start, start + size);
547 memcpy(microMem + start, microProgramPtr, size);
548 }
549 };
550
551 uint8* microProgram = reinterpret_cast<uint8*>(alloca(nSize));
552 stream.Read(microProgram, nSize);
553
554 assert(nSize <= m_vpu.GetMicroMemorySize());
555
556 //Check if the copy's destination address will wrap around
557 if((nDstAddr + nSize) > m_vpu.GetMicroMemorySize())
558 {
559 uint32 start1 = nDstAddr;
560 uint32 size1 = m_vpu.GetMicroMemorySize() - nDstAddr;
561
562 uint32 start2 = 0;
563 uint32 size2 = nSize - size1;
564
565 copyToMicroMem(microProgram, start1, size1);
566 copyToMicroMem(microProgram + size1, start2, size2);
567 }
568 else
569 {
570 copyToMicroMem(microProgram, nDstAddr, nSize);
571 }
572 }
573
574 m_NUM -= static_cast<uint8>(nSize / 8);
575 if((m_NUM == 0) && (nSize != 0))
576 {
577 m_STAT.nVPS = 0;
578 }
579 else
580 {
581 m_STAT.nVPS = 1;
582 }
583 }
584
Cmd_STROW(StreamType & stream,CODE nCommand)585 void CVif::Cmd_STROW(StreamType& stream, CODE nCommand)
586 {
587 if(m_NUM == 0)
588 {
589 m_NUM = 4;
590 }
591
592 while(m_NUM != 0 && stream.GetAvailableReadBytes())
593 {
594 assert(m_NUM <= 4);
595 stream.Read(&m_R[4 - m_NUM], 4);
596 m_NUM--;
597 }
598
599 if(m_NUM == 0)
600 {
601 m_STAT.nVPS = 0;
602 }
603 else
604 {
605 m_STAT.nVPS = 1;
606 }
607 }
608
Cmd_STCOL(StreamType & stream,CODE nCommand)609 void CVif::Cmd_STCOL(StreamType& stream, CODE nCommand)
610 {
611 if(m_NUM == 0)
612 {
613 m_NUM = 4;
614 }
615
616 while(m_NUM != 0 && stream.GetAvailableReadBytes())
617 {
618 assert(m_NUM <= 4);
619 stream.Read(&m_C[4 - m_NUM], 4);
620 m_NUM--;
621 }
622
623 if(m_NUM == 0)
624 {
625 m_STAT.nVPS = 0;
626 }
627 else
628 {
629 m_STAT.nVPS = 1;
630 }
631 }
632
Cmd_STMASK(StreamType & stream,CODE command)633 void CVif::Cmd_STMASK(StreamType& stream, CODE command)
634 {
635 if(m_NUM == 0)
636 {
637 m_NUM = 1;
638 }
639
640 while(m_NUM != 0 && stream.GetAvailableReadBytes())
641 {
642 stream.Read(&m_MASK, 4);
643 m_NUM--;
644 }
645
646 if(m_NUM == 0)
647 {
648 m_STAT.nVPS = 0;
649 }
650 else
651 {
652 m_STAT.nVPS = 1;
653 }
654 }
655
Cmd_UNPACK(StreamType & stream,CODE nCommand,uint32 nDstAddr)656 void CVif::Cmd_UNPACK(StreamType& stream, CODE nCommand, uint32 nDstAddr)
657 {
658 assert((nCommand.nCMD & 0x60) == 0x60);
659
660 const auto vuMem = m_vpu.GetVuMemory();
661 const auto vuMemSize = m_vpu.GetVuMemorySize();
662 bool usn = (m_CODE.nIMM & 0x4000) != 0;
663 bool useMask = (nCommand.nCMD & 0x10) != 0;
664 uint32 cl = m_CYCLE.nCL;
665 uint32 wl = m_CYCLE.nWL;
666 if(wl == 0)
667 {
668 wl = UINT_MAX;
669 cl = 0;
670 }
671
672 if(m_NUM == nCommand.nNUM)
673 {
674 m_readTick = 0;
675 m_writeTick = 0;
676 }
677
678 uint32 currentNum = (m_NUM == 0) ? 256 : m_NUM;
679 uint32 codeNum = (m_CODE.nNUM == 0) ? 256 : m_CODE.nNUM;
680 uint32 transfered = codeNum - currentNum;
681
682 if(cl > wl)
683 {
684 nDstAddr += cl * (transfered / wl) + (transfered % wl);
685 }
686 else
687 {
688 nDstAddr += transfered;
689 }
690
691 nDstAddr *= 0x10;
692 assert(nDstAddr < vuMemSize);
693 nDstAddr &= (vuMemSize - 1);
694
695 while(currentNum != 0)
696 {
697 bool mustWrite = false;
698 uint128 writeValue;
699 memset(&writeValue, 0, sizeof(writeValue));
700
701 if(cl >= wl)
702 {
703 if(m_readTick < wl)
704 {
705 bool success = Unpack_ReadValue(nCommand, stream, writeValue, usn);
706 if(!success) break;
707 mustWrite = true;
708 }
709 }
710 else
711 {
712 if(m_writeTick < cl)
713 {
714 bool success = Unpack_ReadValue(nCommand, stream, writeValue, usn);
715 if(!success) break;
716 }
717
718 mustWrite = true;
719 }
720
721 if(mustWrite)
722 {
723 auto dst = reinterpret_cast<uint128*>(vuMem + nDstAddr);
724
725 for(unsigned int i = 0; i < 4; i++)
726 {
727 uint32 maskOp = useMask ? GetMaskOp(i, m_writeTick) : MASK_DATA;
728
729 if(maskOp == MASK_DATA)
730 {
731 if(m_MODE == MODE_OFFSET)
732 {
733 writeValue.nV[i] += m_R[i];
734 }
735 else if(m_MODE == MODE_DIFFERENCE)
736 {
737 writeValue.nV[i] += m_R[i];
738 m_R[i] = writeValue.nV[i];
739 }
740
741 dst->nV[i] = writeValue.nV[i];
742 }
743 else if(maskOp == MASK_ROW)
744 {
745 dst->nV[i] = m_R[i];
746 }
747 else if(maskOp == MASK_COL)
748 {
749 int index = (m_writeTick > 3) ? 3 : m_writeTick;
750 dst->nV[i] = m_C[index];
751 }
752 else if(maskOp == MASK_MASK)
753 {
754 //Don't write anything
755 }
756 else
757 {
758 assert(0);
759 }
760 }
761
762 currentNum--;
763 }
764
765 if(cl >= wl)
766 {
767 m_writeTick = std::min<uint32>(m_writeTick + 1, wl);
768 m_readTick = std::min<uint32>(m_readTick + 1, cl);
769
770 if(m_readTick == cl)
771 {
772 m_writeTick = 0;
773 m_readTick = 0;
774 }
775 }
776 else
777 {
778 m_writeTick = std::min<uint32>(m_writeTick + 1, wl);
779 m_readTick = std::min<uint32>(m_readTick + 1, cl);
780
781 if(m_writeTick == wl)
782 {
783 m_writeTick = 0;
784 m_readTick = 0;
785 }
786 }
787
788 nDstAddr += 0x10;
789 nDstAddr &= (vuMemSize - 1);
790 }
791
792 if(currentNum != 0)
793 {
794 m_STAT.nVPS = 1;
795 }
796 else
797 {
798 stream.Align32();
799 m_STAT.nVPS = 0;
800 }
801
802 m_NUM = static_cast<uint8>(currentNum);
803 }
804
Unpack_ReadValue(const CODE & nCommand,StreamType & stream,uint128 & writeValue,bool usn)805 bool CVif::Unpack_ReadValue(const CODE& nCommand, StreamType& stream, uint128& writeValue, bool usn)
806 {
807 bool success = false;
808 switch(nCommand.nCMD & 0x0F)
809 {
810 case 0x00:
811 //S-32
812 success = Unpack_S32(stream, writeValue);
813 break;
814 case 0x01:
815 //S-16
816 success = Unpack_S16(stream, writeValue, usn);
817 break;
818 case 0x02:
819 //S-8
820 success = Unpack_S8(stream, writeValue, usn);
821 break;
822 case 0x04:
823 //V2-32
824 success = Unpack_V32(stream, writeValue, 2);
825 break;
826 case 0x05:
827 //V2-16
828 success = Unpack_V16(stream, writeValue, 2, usn);
829 break;
830 case 0x06:
831 //V2-8
832 success = Unpack_V8(stream, writeValue, 2, usn);
833 break;
834 case 0x08:
835 //V3-32
836 success = Unpack_V32(stream, writeValue, 3);
837 break;
838 case 0x09:
839 //V3-16
840 success = Unpack_V16(stream, writeValue, 3, usn);
841 break;
842 case 0x0A:
843 //V3-8
844 success = Unpack_V8(stream, writeValue, 3, usn);
845 break;
846 case 0x0C:
847 //V4-32
848 success = Unpack_V32(stream, writeValue, 4);
849 break;
850 case 0x0D:
851 //V4-16
852 success = Unpack_V16(stream, writeValue, 4, usn);
853 break;
854 case 0x0E:
855 //V4-8
856 success = Unpack_V8(stream, writeValue, 4, usn);
857 break;
858 case 0x0F:
859 //V4-5
860 success = Unpack_V45(stream, writeValue);
861 break;
862 default:
863 assert(0);
864 break;
865 }
866 return success;
867 }
868
Unpack_S32(StreamType & stream,uint128 & result)869 bool CVif::Unpack_S32(StreamType& stream, uint128& result)
870 {
871 if(stream.GetAvailableReadBytes() < 4) return false;
872
873 uint32 word = 0;
874 stream.Read(&word, 4);
875
876 for(unsigned int i = 0; i < 4; i++)
877 {
878 result.nV[i] = word;
879 }
880
881 return true;
882 }
883
Unpack_S16(StreamType & stream,uint128 & result,bool zeroExtend)884 bool CVif::Unpack_S16(StreamType& stream, uint128& result, bool zeroExtend)
885 {
886 if(stream.GetAvailableReadBytes() < 2) return false;
887
888 uint32 temp = 0;
889 stream.Read(&temp, 2);
890 if(!zeroExtend)
891 {
892 temp = static_cast<int16>(temp);
893 }
894
895 for(unsigned int i = 0; i < 4; i++)
896 {
897 result.nV[i] = temp;
898 }
899
900 return true;
901 }
902
Unpack_S8(StreamType & stream,uint128 & result,bool zeroExtend)903 bool CVif::Unpack_S8(StreamType& stream, uint128& result, bool zeroExtend)
904 {
905 if(stream.GetAvailableReadBytes() < 1) return false;
906
907 uint32 temp = 0;
908 stream.Read(&temp, 1);
909 if(!zeroExtend)
910 {
911 temp = static_cast<int8>(temp);
912 }
913
914 for(unsigned int i = 0; i < 4; i++)
915 {
916 result.nV[i] = temp;
917 }
918
919 return true;
920 }
921
Unpack_V8(StreamType & stream,uint128 & result,unsigned int fields,bool zeroExtend)922 bool CVif::Unpack_V8(StreamType& stream, uint128& result, unsigned int fields, bool zeroExtend)
923 {
924 if(stream.GetAvailableReadBytes() < (fields)) return false;
925
926 for(unsigned int i = 0; i < fields; i++)
927 {
928 uint32 temp = 0;
929 stream.Read(&temp, 1);
930 if(!zeroExtend)
931 {
932 temp = static_cast<int8>(temp);
933 }
934
935 result.nV[i] = temp;
936 }
937
938 return true;
939 }
940
Unpack_V16(StreamType & stream,uint128 & result,unsigned int fields,bool zeroExtend)941 bool CVif::Unpack_V16(StreamType& stream, uint128& result, unsigned int fields, bool zeroExtend)
942 {
943 if(stream.GetAvailableReadBytes() < (fields * 2)) return false;
944
945 for(unsigned int i = 0; i < fields; i++)
946 {
947 uint32 temp = 0;
948 stream.Read(&temp, 2);
949 if(!zeroExtend)
950 {
951 temp = static_cast<int16>(temp);
952 }
953
954 result.nV[i] = temp;
955 }
956
957 return true;
958 }
959
Unpack_V32(StreamType & stream,uint128 & result,unsigned int fields)960 bool CVif::Unpack_V32(StreamType& stream, uint128& result, unsigned int fields)
961 {
962 if(stream.GetAvailableReadBytes() < (fields * 4)) return false;
963
964 stream.Read(&result, (fields * 4));
965
966 return true;
967 }
968
Unpack_V45(StreamType & stream,uint128 & result)969 bool CVif::Unpack_V45(StreamType& stream, uint128& result)
970 {
971 if(stream.GetAvailableReadBytes() < 2) return false;
972
973 uint16 nColor = 0;
974 stream.Read(&nColor, 2);
975
976 result.nV0 = ((nColor >> 0) & 0x1F) << 3;
977 result.nV1 = ((nColor >> 5) & 0x1F) << 3;
978 result.nV2 = ((nColor >> 10) & 0x1F) << 3;
979 result.nV3 = ((nColor >> 15) & 0x01) << 7;
980
981 return true;
982 }
983
GetMaskOp(unsigned int row,unsigned int col) const984 uint32 CVif::GetMaskOp(unsigned int row, unsigned int col) const
985 {
986 if(col > 3) col = 3;
987 assert(row < 4);
988 unsigned int index = (col * 4) + row;
989 return (m_MASK >> (index * 2)) & 0x03;
990 }
991
PrepareMicroProgram()992 void CVif::PrepareMicroProgram()
993 {
994 m_ITOP = m_ITOPS;
995 }
996
StartMicroProgram(uint32 address)997 void CVif::StartMicroProgram(uint32 address)
998 {
999 if(m_vpu.IsVuRunning())
1000 {
1001 m_STAT.nVEW = 1;
1002 return;
1003 }
1004
1005 assert(!m_STAT.nVEW);
1006 PrepareMicroProgram();
1007 m_vpu.ExecuteMicroProgram(address);
1008 }
1009
StartDelayedMicroProgram(uint32 address)1010 void CVif::StartDelayedMicroProgram(uint32 address)
1011 {
1012 //Snowblind Studio games start a VU microprogram and issues an UNPACK command
1013 //which has data needed by the microprogram. We simulate the microprogram
1014 //starting a bit later to let the UNPACK command execute
1015 if(m_vpu.IsVuRunning())
1016 {
1017 m_STAT.nVEW = 1;
1018 return;
1019 }
1020
1021 assert(!m_STAT.nVEW);
1022 PrepareMicroProgram();
1023 m_pendingMicroProgram = address;
1024 }
1025
ResumeDelayedMicroProgram()1026 bool CVif::ResumeDelayedMicroProgram()
1027 {
1028 if(m_pendingMicroProgram != -1)
1029 {
1030 assert(!IsWaitingForProgramEnd());
1031 assert(!m_vpu.IsVuRunning());
1032 m_vpu.ExecuteMicroProgram(m_pendingMicroProgram);
1033 m_pendingMicroProgram = -1;
1034 return true;
1035 }
1036 else
1037 {
1038 return false;
1039 }
1040 }
1041
DisassembleGet(uint32 address)1042 void CVif::DisassembleGet(uint32 address)
1043 {
1044 #define LOG_GET(registerId) \
1045 case registerId: \
1046 CLog::GetInstance().Print(LOG_NAME, "= " #registerId ".\r\n"); \
1047 break;
1048
1049 switch(address)
1050 {
1051 LOG_GET(VIF0_STAT)
1052 LOG_GET(VIF0_ERR)
1053 LOG_GET(VIF0_MARK)
1054 LOG_GET(VIF0_CYCLE)
1055 LOG_GET(VIF0_MODE)
1056 LOG_GET(VIF0_NUM)
1057 LOG_GET(VIF0_MASK)
1058 LOG_GET(VIF0_CODE)
1059 LOG_GET(VIF0_R0)
1060 LOG_GET(VIF0_R1)
1061 LOG_GET(VIF0_R2)
1062 LOG_GET(VIF0_R3)
1063
1064 LOG_GET(VIF1_STAT)
1065 LOG_GET(VIF1_ERR)
1066 LOG_GET(VIF1_MARK)
1067 LOG_GET(VIF1_CYCLE)
1068 LOG_GET(VIF1_MODE)
1069 LOG_GET(VIF1_NUM)
1070 LOG_GET(VIF1_MASK)
1071 LOG_GET(VIF1_CODE)
1072 LOG_GET(VIF1_R0)
1073 LOG_GET(VIF1_R1)
1074 LOG_GET(VIF1_R2)
1075 LOG_GET(VIF1_R3)
1076
1077 default:
1078 CLog::GetInstance().Print(LOG_NAME, "Reading unknown register 0x%08X.\r\n", address);
1079 break;
1080 }
1081
1082 #undef LOG_GET
1083 }
1084
DisassembleSet(uint32 address,uint32 value)1085 void CVif::DisassembleSet(uint32 address, uint32 value)
1086 {
1087 if((address >= VIF0_FIFO_START) && (address < VIF0_FIFO_END))
1088 {
1089 CLog::GetInstance().Print(LOG_NAME, "VIF0_FIFO(0x%03X) = 0x%08X.\r\n", address & 0xFFF, value);
1090 }
1091 else if((address >= VIF1_FIFO_START) && (address < VIF1_FIFO_END))
1092 {
1093 CLog::GetInstance().Print(LOG_NAME, "VIF1_FIFO(0x%03X) = 0x%08X.\r\n", address & 0xFFF, value);
1094 }
1095 else
1096 {
1097 #define LOG_SET(registerId) \
1098 case registerId: \
1099 CLog::GetInstance().Print(LOG_NAME, #registerId " = 0x%08X.\r\n", value); \
1100 break;
1101
1102 switch(address)
1103 {
1104 LOG_SET(VIF0_FBRST)
1105 LOG_SET(VIF0_MARK)
1106 LOG_SET(VIF0_ERR)
1107
1108 LOG_SET(VIF1_FBRST)
1109 LOG_SET(VIF1_MARK)
1110 LOG_SET(VIF1_ERR)
1111
1112 default:
1113 CLog::GetInstance().Print(LOG_NAME, "Writing unknown register 0x%08X, 0x%08X.\r\n", address, value);
1114 break;
1115 }
1116
1117 #undef LOG_SET
1118 }
1119 }
1120
DisassembleCommand(CODE code)1121 void CVif::DisassembleCommand(CODE code)
1122 {
1123 if(m_STAT.nVPS != 0) return;
1124
1125 CLog::GetInstance().Print(LOG_NAME, "vif%i : ", m_number);
1126
1127 if(code.nI)
1128 {
1129 CLog::GetInstance().Print(LOG_NAME, "(I) ");
1130 }
1131
1132 if(code.nCMD >= 0x60)
1133 {
1134 static const char* packFormats[16] =
1135 {
1136 "S-32",
1137 "S-16",
1138 "S-8",
1139 "(Unknown)",
1140 "V2-32",
1141 "V2-16",
1142 "V2-8",
1143 "(Unknown)",
1144 "V3-32",
1145 "V3-16",
1146 "V3-8",
1147 "(Unknown)",
1148 "V4-32",
1149 "V4-16",
1150 "V4-8",
1151 "V4-5"};
1152 CLog::GetInstance().Print(LOG_NAME, "UNPACK(format = %s, imm = 0x%x, num = 0x%x);\r\n",
1153 packFormats[code.nCMD & 0x0F], code.nIMM, code.nNUM);
1154 }
1155 else
1156 {
1157 switch(code.nCMD)
1158 {
1159 case 0x00:
1160 CLog::GetInstance().Print(LOG_NAME, "NOP\r\n");
1161 break;
1162 case 0x01:
1163 CLog::GetInstance().Print(LOG_NAME, "STCYCL(imm = 0x%x);\r\n", code.nIMM);
1164 break;
1165 case 0x02:
1166 CLog::GetInstance().Print(LOG_NAME, "OFFSET(imm = 0x%x);\r\n", code.nIMM);
1167 break;
1168 case 0x03:
1169 CLog::GetInstance().Print(LOG_NAME, "BASE(imm = 0x%x);\r\n", code.nIMM);
1170 break;
1171 case 0x04:
1172 CLog::GetInstance().Print(LOG_NAME, "ITOP(imm = 0x%x);\r\n", code.nIMM);
1173 break;
1174 case 0x05:
1175 CLog::GetInstance().Print(LOG_NAME, "STMOD(imm = 0x%x);\r\n", code.nIMM);
1176 break;
1177 case 0x06:
1178 CLog::GetInstance().Print(LOG_NAME, "MSKPATH3(mask = %d);\r\n", (code.nIMM & 0x8000) ? 1 : 0);
1179 break;
1180 case 0x07:
1181 CLog::GetInstance().Print(LOG_NAME, "MARK(imm = 0x%x);\r\n", code.nIMM);
1182 break;
1183 case 0x10:
1184 CLog::GetInstance().Print(LOG_NAME, "FLUSHE();\r\n");
1185 break;
1186 case 0x11:
1187 CLog::GetInstance().Print(LOG_NAME, "FLUSH();\r\n");
1188 break;
1189 case 0x13:
1190 CLog::GetInstance().Print(LOG_NAME, "FLUSHA();\r\n");
1191 break;
1192 case 0x14:
1193 CLog::GetInstance().Print(LOG_NAME, "MSCAL(imm = 0x%x);\r\n", code.nIMM);
1194 break;
1195 case 0x15:
1196 CLog::GetInstance().Print(LOG_NAME, "MSCALF(imm = 0x%x);\r\n", code.nIMM);
1197 break;
1198 case 0x17:
1199 CLog::GetInstance().Print(LOG_NAME, "MSCNT();\r\n");
1200 break;
1201 case 0x20:
1202 CLog::GetInstance().Print(LOG_NAME, "STMASK();\r\n");
1203 break;
1204 case 0x30:
1205 CLog::GetInstance().Print(LOG_NAME, "STROW();\r\n");
1206 break;
1207 case 0x31:
1208 CLog::GetInstance().Print(LOG_NAME, "STCOL();\r\n");
1209 break;
1210 case 0x4A:
1211 CLog::GetInstance().Print(LOG_NAME, "MPG(imm = 0x%x, num = 0x%x);\r\n", code.nIMM, code.nNUM);
1212 break;
1213 case 0x50:
1214 CLog::GetInstance().Print(LOG_NAME, "DIRECT(imm = 0x%x);\r\n", code.nIMM);
1215 break;
1216 case 0x51:
1217 CLog::GetInstance().Print(LOG_NAME, "DIRECTHL(imm = 0x%x);\r\n", code.nIMM);
1218 break;
1219 default:
1220 CLog::GetInstance().Print(LOG_NAME, "Unknown command (0x%x).\r\n", code.nCMD);
1221 break;
1222 }
1223 }
1224 }
1225
1226 //CFifoStream
1227 //--------------------------------------------------
1228
CFifoStream(uint8 * ram,uint8 * spr)1229 CVif::CFifoStream::CFifoStream(uint8* ram, uint8* spr)
1230 : m_ram(ram)
1231 , m_spr(spr)
1232 {
1233 }
1234
Reset()1235 void CVif::CFifoStream::Reset()
1236 {
1237 m_bufferPosition = BUFFERSIZE;
1238 m_startAddress = 0;
1239 m_nextAddress = 0;
1240 m_endAddress = 0;
1241 m_tagIncluded = false;
1242 m_source = nullptr;
1243 }
1244
Read(void * buffer,uint32 size)1245 void CVif::CFifoStream::Read(void* buffer, uint32 size)
1246 {
1247 assert(m_source != NULL);
1248 uint8* readBuffer = reinterpret_cast<uint8*>(buffer);
1249 while(size != 0)
1250 {
1251 SyncBuffer();
1252 uint32 read = std::min<uint32>(size, BUFFERSIZE - m_bufferPosition);
1253 if(readBuffer != NULL)
1254 {
1255 memcpy(readBuffer, reinterpret_cast<uint8*>(&m_buffer) + m_bufferPosition, read);
1256 readBuffer += read;
1257 }
1258 m_bufferPosition += read;
1259 size -= read;
1260 }
1261 }
1262
Flush()1263 void CVif::CFifoStream::Flush()
1264 {
1265 m_bufferPosition = BUFFERSIZE;
1266 }
1267
SetDmaParams(uint32 address,uint32 size,bool tagIncluded)1268 void CVif::CFifoStream::SetDmaParams(uint32 address, uint32 size, bool tagIncluded)
1269 {
1270 if(address & 0x80000000)
1271 {
1272 m_source = m_spr;
1273 address &= (PS2::EE_SPR_SIZE - 1);
1274 assert((address + size) <= PS2::EE_SPR_SIZE);
1275 }
1276 else
1277 {
1278 m_source = m_ram;
1279 address &= (PS2::EE_RAM_SIZE - 1);
1280 assert((address + size) <= PS2::EE_RAM_SIZE);
1281 }
1282 m_startAddress = address;
1283 m_nextAddress = address;
1284 m_endAddress = address + size;
1285 m_tagIncluded = tagIncluded;
1286 SyncBuffer();
1287 }
1288
SetFifoParams(uint8 * source,uint32 size)1289 void CVif::CFifoStream::SetFifoParams(uint8* source, uint32 size)
1290 {
1291 m_source = source;
1292 m_startAddress = 0;
1293 m_nextAddress = 0;
1294 m_endAddress = size;
1295 m_tagIncluded = false;
1296 SyncBuffer();
1297 }
1298
GetAvailableReadBytes() const1299 uint32 CVif::CFifoStream::GetAvailableReadBytes() const
1300 {
1301 return GetRemainingDmaTransferSize() + (BUFFERSIZE - m_bufferPosition);
1302 }
1303
GetRemainingDmaTransferSize() const1304 uint32 CVif::CFifoStream::GetRemainingDmaTransferSize() const
1305 {
1306 return m_endAddress - m_nextAddress;
1307 }
1308
Align32()1309 void CVif::CFifoStream::Align32()
1310 {
1311 unsigned int remainBytes = m_bufferPosition & 0x03;
1312 if(remainBytes == 0) return;
1313 Read(NULL, 4 - remainBytes);
1314 assert((m_bufferPosition & 0x03) == 0);
1315 }
1316
GetDirectPointer() const1317 uint8* CVif::CFifoStream::GetDirectPointer() const
1318 {
1319 assert(!m_tagIncluded);
1320 if(m_bufferPosition == BUFFERSIZE)
1321 {
1322 return m_source + m_nextAddress;
1323 }
1324 else
1325 {
1326 assert((m_nextAddress - m_startAddress) >= 0x10);
1327 return m_source + m_nextAddress + m_bufferPosition - 0x10;
1328 }
1329 }
1330
Advance(uint32 size)1331 void CVif::CFifoStream::Advance(uint32 size)
1332 {
1333 assert((size & 0x0F) == 0);
1334 assert(!m_tagIncluded);
1335 //If buffer was untouched, we can do as if we read from it directly
1336 if(m_bufferPosition == 0)
1337 {
1338 assert(size >= 0x10);
1339 size -= 0x10;
1340 m_bufferPosition = BUFFERSIZE;
1341 }
1342 assert((m_nextAddress + size) <= m_endAddress);
1343 m_nextAddress += size;
1344 if(m_bufferPosition != BUFFERSIZE)
1345 {
1346 //Update buffer
1347 assert((m_nextAddress - m_startAddress) >= 0x10);
1348 m_buffer = *reinterpret_cast<uint128*>(&m_source[m_nextAddress - 0x10]);
1349 }
1350 }
1351
SyncBuffer()1352 void CVif::CFifoStream::SyncBuffer()
1353 {
1354 assert(m_bufferPosition <= BUFFERSIZE);
1355 if(m_bufferPosition >= BUFFERSIZE)
1356 {
1357 if(m_nextAddress >= m_endAddress)
1358 {
1359 throw std::exception();
1360 }
1361 m_buffer = *reinterpret_cast<uint128*>(&m_source[m_nextAddress]);
1362 m_nextAddress += 0x10;
1363 m_bufferPosition = 0;
1364 if(m_tagIncluded)
1365 {
1366 //Skip next 8 bytes
1367 m_tagIncluded = false;
1368 m_bufferPosition += 8;
1369 }
1370 }
1371 }
1372