/[pcsx2_0.9.7]/trunk/pcsx2/ps2/GIFpath.cpp
ViewVC logotype

Contents of /trunk/pcsx2/ps2/GIFpath.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 62 - (show annotations) (download)
Tue Sep 7 11:08:22 2010 UTC (10 years, 4 months ago) by william
File size: 28960 byte(s)
Auto Commited Import of: pcsx2-0.9.7-r3738-debug in ./trunk
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17 #include "Common.h"
18 #include "GS.h"
19 #include "Gif.h"
20 #include "Vif_Dma.h"
21 #include "Vif.h"
22 #include <xmmintrin.h>
23
24 // --------------------------------------------------------------------------------------
25 // GIFpath -- the GIFtag Parser
26 // --------------------------------------------------------------------------------------
27
28 enum GIF_FLG
29 {
30 GIF_FLG_PACKED = 0,
31 GIF_FLG_REGLIST = 1,
32 GIF_FLG_IMAGE = 2,
33 GIF_FLG_IMAGE2 = 3
34 };
35
36 enum GIF_REG
37 {
38 GIF_REG_PRIM = 0x00,
39 GIF_REG_RGBA = 0x01,
40 GIF_REG_STQ = 0x02,
41 GIF_REG_UV = 0x03,
42 GIF_REG_XYZF2 = 0x04,
43 GIF_REG_XYZ2 = 0x05,
44 GIF_REG_TEX0_1 = 0x06,
45 GIF_REG_TEX0_2 = 0x07,
46 GIF_REG_CLAMP_1 = 0x08,
47 GIF_REG_CLAMP_2 = 0x09,
48 GIF_REG_FOG = 0x0a,
49 GIF_REG_XYZF3 = 0x0c,
50 GIF_REG_XYZ3 = 0x0d,
51 GIF_REG_A_D = 0x0e,
52 GIF_REG_NOP = 0x0f,
53 };
54
55 // GIFTAG
56 // Members of this structure are in CAPS to help visually denote that they are representative
57 // of actual hw register states of the GIF, unlike the internal tracking vars in GIFPath, which
58 // are modified during the GIFtag unpacking process.
59 struct GIFTAG
60 {
61 u32 NLOOP : 15;
62 u32 EOP : 1;
63 u32 _dummy0 : 16;
64 u32 _dummy1 : 14;
65 u32 PRE : 1;
66 u32 PRIM : 11;
67 u32 FLG : 2;
68 u32 NREG : 4;
69 u32 REGS[2];
70
71 GIFTAG() {}
72 };
73
74 // --------------------------------------------------------------------------------------
75 // GIFPath -- PS2 GIFtag info (one for each path).
76 // --------------------------------------------------------------------------------------
77 // fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
78 // separate paths. But for that to work properly we need also interlocked path sources.
79 // That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
80 // this is not emulated!
81
82 struct GIFPath
83 {
84 const GIFTAG tag; // A copy of the "original" tag -- modification allowed only by SetTag(), so let's make it const.
85 u8 regs[16]; // positioned after tag ensures 16-bit aligned (in case we SSE optimize later)
86
87 u32 nloop; // local copy nloop counts toward zero, and leaves the tag copy unmodified.
88 u32 curreg; // reg we left of on (for traversing through loops)
89 u32 numregs; // number of regs (when NREG is 0, numregs is 16)
90 u32 DetectE;
91
92 GIFPath();
93
94 void Reset();
95 void PrepPackedRegs();
96 bool StepReg();
97 u8 GetReg();
98 bool IsActive() const;
99
100 template< bool Aligned >
101 void SetTag(const void* mem);
102
103 template< GIF_PATH pathidx, bool Aligned >
104 int CopyTag(const u128* pMem, u32 size);
105
106 int ParseTagQuick(GIF_PATH pathidx, const u8* pMem, u32 size);
107 };
108
109 typedef void (__fastcall *GIFRegHandler)(const u32* data);
110
111 struct GifPathStruct
112 {
113 const GIFRegHandler Handlers[0x100-0x60]; // handlers for 0x60->0x100
114 GIFPath path[3];
115
116 __fi GIFPath& operator[]( int idx ) { return path[idx]; }
117 };
118
119
120 // --------------------------------------------------------------------------------------
121 // SIGNAL / FINISH / LABEL
122 // --------------------------------------------------------------------------------------
123
124 bool SIGNAL_IMR_Pending = false;
125 u32 SIGNAL_Data_Pending[2];
126
127
128 // SIGNAL : This register is a double-throw. If the SIGNAL bit in CSR is clear, set the CSR
129 // and raise a gsIrq. If CSR is already *set*, then do not raise a gsIrq, and ignore all
130 // subsequent drawing operations and writes to general purpose registers to the GS. (note:
131 // I'm pretty sure this includes direct GS and GSreg accesses, as well as those coming
132 // through the GIFpath -- but that behavior isn't confirmed yet). Privileged writes are
133 // still active.
134 //
135 // Ignorance continues until the SIGNAL bit in CSR is manually cleared by the EE. And here's
136 // the tricky part: the interrupt from the second SIGNAL is still pending, and should be
137 // raised once the EE has reset the *IMR* mask for SIGNAL -- meaning setting the bit to 1
138 // (disabled/masked) and then back to 0 (enabled/unmasked). Until the *IMR* is cleared, the
139 // SIGNAL is still in the second throw stage, and will freeze the GS upon being written.
140 //
141 static void __fastcall RegHandlerSIGNAL(const u32* data)
142 {
143 // HACK:
144 // Soul Calibur 3 seems to be doing SIGNALs on PATH2 and PATH3 simultaneously, and isn't
145 // too happy with the results (dies on bootup). It properly clears the SIGNAL interrupt
146 // but seems to get stuck on a VBLANK OVERLAP loop. Fixing SIGNAL so that it properly
147 // stalls the GIF might fix it. Investigating the game's internals more deeply may also
148 // be revealing. --air
149
150 if (CSRreg.SIGNAL)
151 {
152 // Time to ignore all subsequent drawing operations. (which is not yet supported)
153 if (!SIGNAL_IMR_Pending)
154 {
155 //DevCon.WriteLn( Color_StrongOrange, "GS SIGNAL double throw encountered!" );
156 SIGNAL_IMR_Pending = true;
157 SIGNAL_Data_Pending[0] = data[0];
158 SIGNAL_Data_Pending[1] = data[1];
159
160 // [TODO] (SIGNAL) : Disable GIFpath DMAs here!
161 // All PATHs and DMAs should be disabled until the CSR is written and the
162 // SIGNAL bit cleared.
163 }
164 }
165 else
166 {
167 GIF_LOG("GS SIGNAL data=%x_%x IMR=%x CSRr=%x",data[0], data[1], GSIMR, GSCSRr);
168 GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]);
169
170 if (!(GSIMR&0x100))
171 gsIrq();
172
173 CSRreg.SIGNAL = true;
174 }
175 }
176
177 // FINISH : Enables end-of-draw signaling. When FINISH is written it tells the GIF to
178 // raise a gsIrq and set the FINISH bit of CSR when the *current drawing operation* is
179 // finished. Translation: Only after all three logical GIFpaths are in EOP status.
180 //
181 // This feature can be used for both reversing the GS transfer mode (downloading post-
182 // processing effects to the EE), and more importantly for *DMA synch* between the
183 // three logical GIFpaths.
184 //
185 static void __fastcall RegHandlerFINISH(const u32* data)
186 {
187 GIF_LOG("GIFpath FINISH data=%x_%x CSRr=%x", data[0], data[1], GSCSRr);
188
189 // The FINISH bit is set here, and then it will be cleared when all three
190 // logical GIFpaths finish their packets (EOPs) At that time (found below
191 // in the GIFpath_Parser), IMR is tested and a gsIrq() raised if needed.
192
193 CSRreg.FINISH = true;
194 }
195
196 static void __fastcall RegHandlerLABEL(const u32* data)
197 {
198 GIF_LOG( "GIFpath LABEL" );
199 GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID&~data[1])|(data[0]&data[1]);
200 }
201
202 static void __fastcall RegHandlerUNMAPPED(const u32* data)
203 {
204 const int regidx = ((u8*)data)[8];
205
206 // Known "unknowns":
207 // It's possible that anything above 0x63 should just be silently ignored, but in the
208 // offhand chance not, I'm documenting known cases of unknown register use here.
209 //
210 // 0x7F -->
211 // the bios likes to write to 0x7f using an EOP giftag with NLOOP set to 4.
212 // Not sure what it's trying to accomplish exactly. Ignoring seems to work fine,
213 // and is probably the intended behavior (it's likely meant to be a NOP).
214 //
215 // 0xEE -->
216 // .hack Infection [PAL confirmed, NTSC unknown] uses 0xee when you zoom the camera.
217 // The use hasn't been researched yet so parameters are unknown. Everything seems
218 // to work fine as usual -- The 0xEE address in common programming terms is typically
219 // left over uninitialized data, and this might be a case of that, which is to be
220 // silently ignored.
221 //
222 // Guitar Hero 3+ : Massive spamming when using superVU (along with several VIF errors)
223 // Using microVU avoids the GIFtag errors, so probably just one of sVU's hacks conflicting
224 // with one of VIF's hacks, and causing corrupted packet data.
225
226 if( regidx != 0x7f && regidx != 0xee )
227 DbgCon.Warning( "Ignoring Unmapped GIFtag Register, Index = %02x", regidx );
228 }
229
230 #define INSERT_UNMAPPED_4 RegHandlerUNMAPPED, RegHandlerUNMAPPED, RegHandlerUNMAPPED, RegHandlerUNMAPPED,
231 #define INSERT_UNMAPPED_16 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4
232 #define INSERT_UNMAPPED_64 INSERT_UNMAPPED_16 INSERT_UNMAPPED_16 INSERT_UNMAPPED_16 INSERT_UNMAPPED_16
233
234 static __aligned16 GifPathStruct s_gifPath =
235 {
236 RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL, RegHandlerUNMAPPED,
237
238 // Rest are mapped to Unmapped
239 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4
240 INSERT_UNMAPPED_64 INSERT_UNMAPPED_64 INSERT_UNMAPPED_16
241 };
242
243 // --------------------------------------------------------------------------------------
244 // GIFPath Method Implementations
245 // --------------------------------------------------------------------------------------
246
247 GIFPath::GIFPath() : tag()
248 {
249 Reset();
250 }
251
252 __fi void GIFPath::Reset()
253 {
254 memzero(*this);
255 const_cast<GIFTAG&>(tag).EOP = 1;
256 }
257
258 __fi bool GIFPath::StepReg()
259 {
260 if (++curreg >= numregs) {
261 curreg = 0;
262 if (--nloop == 0) {
263 return false;
264 }
265 }
266 return true;
267 }
268
269 __fi u8 GIFPath::GetReg() { return regs[curreg]; }
270
271 // Unpack the registers - registers are stored as a sequence of 4 bit values in the
272 // upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
273 // coming in from paths 2 and 3, so we unpack them into an 8 bit array here.
274 //
275 __fi void GIFPath::PrepPackedRegs()
276 {
277 // Only unpack registers if we're starting a new pack. Otherwise the unpacked
278 // array should have already been initialized by a previous partial transfer.
279
280 if (curreg != 0) return;
281 DetectE = 0;
282 u32 tempreg = tag.REGS[0];
283 numregs = ((tag.NREG-1)&0xf) + 1;
284
285 for (u32 i = 0; i < numregs; i++) {
286 if (i == 8) tempreg = tag.REGS[1];
287 regs[i] = tempreg & 0xf;
288 if(regs[i] == 0xe) DetectE++;
289 tempreg >>= 4;
290 }
291 }
292
293
294 template< bool Aligned >
295 __fi void GIFPath::SetTag(const void* mem)
296 {
297 _mm_store_ps( (float*)&tag, Aligned ? _mm_load_ps((const float*)mem) : _mm_loadu_ps((const float*)mem) );
298
299 nloop = tag.NLOOP;
300 curreg = 0;
301 }
302
303 __fi bool GIFPath::IsActive() const
304 {
305 return (nloop != 0) || !tag.EOP;
306 }
307
308 void SaveStateBase::gifPathFreeze()
309 {
310 FreezeTag( "GIFpath" );
311 Freeze( s_gifPath.path );
312 }
313
314
315 static __fi void gsHandler(const u8* pMem)
316 {
317 const int reg = pMem[8];
318
319 if (reg == 0x50)
320 {
321 vif1.BITBLTBUF._u64 = *(u64*)pMem;
322 }
323 else if (reg == 0x52)
324 {
325 vif1.TRXREG._u64 = *(u64*)pMem;
326 }
327 else if (reg == 0x53)
328 {
329 // local -> host
330 if ((pMem[0] & 3) == 1)
331 {
332 //Onimusha does TRXREG without BLTDIVIDE first, so we "assume" 32bit for this equation, probably isnt important.
333 // ^ WTF, seriously? This is really important (pseudonym)
334 u8 bpp = 32;
335
336 switch(vif1.BITBLTBUF.SPSM & 7)
337 {
338 case 0:
339 bpp = 32;
340 break;
341 case 1:
342 bpp = 24;
343 break;
344 case 2:
345 bpp = 16;
346 break;
347 case 3:
348 bpp = 8;
349 break;
350 // 4 is 4 bit but this is forbidden
351 default:
352 Console.Error("Illegal format for GS upload: SPSM=0%02o", vif1.BITBLTBUF.SPSM);
353 }
354
355 VIF_LOG("GS Download %dx%d SPSM=%x bpp=%d", vif1.TRXREG.RRW, vif1.TRXREG.RRH, vif1.BITBLTBUF.SPSM, bpp);
356
357 // qwords, rounded down; any extra bits are lost
358 // games must take care to ensure transfer rectangles are exact multiples of a qword
359 vif1.GSLastDownloadSize = vif1.TRXREG.RRW * vif1.TRXREG.RRH * bpp >> 7;
360 //DevCon.Warning("GS download in progress");
361 gifRegs.stat.OPH = true;
362 }
363 }
364 if (reg >= 0x60)
365 {
366 // Question: What happens if an app writes to uncharted register space on real PS2
367 // hardware (handler 0x63 and higher)? Probably a silent ignorance, but not tested
368 // so just guessing... --air
369
370 s_gifPath.Handlers[reg-0x60]((const u32*)pMem);
371 }
372 }
373
374 #define incTag(y) do { \
375 pMem += (y*16); \
376 size -= (y); \
377 } while(false)
378
379 #define aMin(x, y) std::min(x, y)
380
381 // Parameters:
382 // size - max size of incoming data stream, in qwc (simd128). If the path is PATH1, and the
383 // path does not terminate (EOP) within the specified size, it is assumed that the path must
384 // loop around to the start of VU memory and continue processing.
385 __fi int GIFPath::ParseTagQuick(GIF_PATH pathidx, const u8* pMem, u32 size)
386 {
387 u32 startSize = size; // Start Size
388
389 while (size > 0) {
390 if (!nloop) {
391
392 SetTag<false>(pMem);
393 incTag(1);
394 }
395 else
396 {
397 switch(tag.FLG) {
398 case GIF_FLG_PACKED:
399 {
400 GIF_LOG("Packed Mode");
401 numregs = ((tag.NREG-1)&0xf) + 1;
402
403 // Note: curreg is *usually* zero here, but can be non-zero if a previous fragment was
404 // handled via this optimized copy code below.
405
406 const u32 listlen = (nloop * numregs) - curreg; // the total length of this packed register list (in QWC)
407 u32 len;
408
409 if(size < listlen)
410 {
411 len = size;
412
413 // We need to calculate both the number of full iterations of regs copied (nloops),
414 // and any remaining registers not copied by this fragment. A div/mod pair should
415 // hopefully be optimized by the compiler into a single x86 div. :)
416
417 const int nloops_copied = len / numregs;
418 const int regs_not_copied = len % numregs;
419
420 // Make sure to add regs_not_copied to curreg, to handle cases of multiple partial fragments.
421 // (example: 3 fragments each of only 2 regs, then curreg should be 0, 2, and then 4 after
422 // each call to GIFPath_Parse; with no change to NLOOP). Because of this we also need to
423 // check for cases where curreg wraps past an nloop.
424
425 nloop -= nloops_copied;
426 curreg += regs_not_copied;
427 if(curreg >= numregs)
428 {
429 --nloop;
430 curreg -= numregs;
431 }
432 }
433 else
434 {
435 len = listlen;
436 curreg = 0;
437 nloop = 0;
438 }
439 incTag(len);
440 }
441 break;
442 case GIF_FLG_REGLIST:
443 {
444 GIF_LOG("Reglist Mode EOP %x", tag.EOP);
445
446 // In reglist mode, the GIF packs 2 registers into each QWC. The nloop however
447 // can be an odd number, in which case the upper half of the final QWC is ignored (skipped).
448
449 numregs = ((tag.NREG-1)&0xf) + 1;
450 const u32 total_reglen = (nloop * numregs) - curreg; // total 'expected length' of this packed register list (in registers)
451 const u32 total_listlen = (total_reglen+1) / 2; // total 'expected length' of the register list, in QWC! (+1 so to round it up)
452
453 u32 len;
454
455 if(size < total_listlen)
456 {
457 //Console.Warning("GIF path %d Fragmented REGLIST! Please report if you experience problems", pathidx + 1);
458
459 len = size;
460 const u32 reglen = len * 2;
461
462 const int nloops_copied = reglen / numregs;
463 const int regs_not_copied = reglen % numregs;
464
465 //DevCon.Warning("Hit it path %d", pathidx + 1);
466 curreg += regs_not_copied;
467 nloop -= nloops_copied;
468
469 if(curreg >= numregs)
470 {
471 --nloop;
472 curreg -= numregs;
473 }
474 }
475 else
476 {
477 len = total_listlen;
478 curreg = 0;
479 nloop = 0;
480 }
481
482 incTag(len);
483 //if(curreg != 0 || (len % numregs) > 0) DevCon.Warning("Oops c %x n %x m %x r %x", curreg, nloop, (len % numregs), numregs);
484 }
485 break;
486 case GIF_FLG_IMAGE:
487 case GIF_FLG_IMAGE2:
488 {
489 GIF_LOG("IMAGE Mode");
490 int len = aMin(size, nloop);
491 incTag(len);
492 nloop -= len;
493 }
494 break;
495 }
496 }
497 if(pathidx == GIF_PATH_1)
498 {
499 if(size == 0 && (!tag.EOP || nloop > 0))
500 {
501 if(startSize < 0x400)
502 {
503 size = 0x400 - startSize;
504 startSize = 0x400;
505 pMem -= 0x4000;
506 }
507 else
508 {
509 // Note: The BIOS does an XGKICK on the VU1 and lets it DMA to the GS without an EOP
510 // (seemingly to loop forever), only to write an EOP later on. No other game is known to
511 // do anything of the sort.
512 // So lets just cap the DMA at 16k, and force it to "look" like it's terminated for now.
513 // (note: truly accurate emulation would mean having the VU1's XGKICK break execution,
514 // split time to EE and other processors, and then resume the kick's DMA later.
515 // ... yea, not happening for a while. ;) -- air
516
517 Console.Warning("GIFTAG error, size exceeded VU memory size %x", startSize);
518 nloop = 0;
519 const_cast<GIFTAG&>(tag).EOP = 1;
520 }
521 }
522 }
523 if (tag.EOP && !nloop) break;
524 }
525
526 size = (startSize - size);
527
528
529 return size;
530 }
531
532 __ri void MemCopy_WrappedDest( const u128* src, u128* destBase, uint& destStart, uint destSize, uint len )
533 {
534 uint endpos = destStart + len;
535 if( endpos < destSize )
536 {
537 memcpy_qwc(&destBase[destStart], src, len );
538 destStart += len;
539 }
540 else
541 {
542 uint firstcopylen = destSize - destStart;
543 memcpy_qwc(&destBase[destStart], src, firstcopylen );
544
545 destStart = endpos % destSize;
546 memcpy_qwc(destBase, src+firstcopylen, destStart );
547 }
548 }
549
550 __ri void MemCopy_WrappedSrc( const u128* srcBase, uint& srcStart, uint srcSize, u128* dest, uint len )
551 {
552 uint endpos = srcStart + len;
553 if( endpos < srcSize )
554 {
555 memcpy_qwc(dest, &srcBase[srcStart], len );
556 srcStart += len;
557 }
558 else
559 {
560 uint firstcopylen = srcSize - srcStart;
561 memcpy_qwc(dest, &srcBase[srcStart], firstcopylen );
562
563 srcStart = endpos % srcSize;
564 memcpy_qwc(dest+firstcopylen, srcBase, srcStart );
565 }
566 }
567
568 #define copyTag() do { \
569 _mm_store_ps( (float*)&RingBuffer.m_Ring[ringpos], Aligned ? _mm_load_ps((float*)pMem128) : _mm_loadu_ps((float*)pMem128)); \
570 ++pMem128; --size; \
571 ringpos = (ringpos+1)&RingBufferMask; \
572 } while(false)
573
574 // Parameters:
575 // size - max size of incoming data stream, in qwc (simd128). If the path is PATH1, and the
576 // path does not terminate (EOP) within the specified size, it is assumed that the path must
577 // loop around to the start of VU memory and continue processing.
578 template< GIF_PATH pathidx, bool Aligned >
579 __fi int GIFPath::CopyTag(const u128* pMem128, u32 size)
580 {
581 uint& ringpos = GetMTGS().m_packet_writepos;
582 const uint original_ringpos = ringpos;
583
584 u32 startSize = size; // Start Size
585
586 while (size > 0) {
587 if (!nloop) {
588
589 SetTag<Aligned>((u8*)pMem128);
590 copyTag();
591
592 if(nloop > 0)
593 {
594 switch(pathidx)
595 {
596 case GIF_PATH_1:
597 if(tag.FLG & 2)GSTransferStatus.PTH1 = IMAGE_MODE;
598 else GSTransferStatus.PTH1 = TRANSFER_MODE;
599 break;
600 case GIF_PATH_2:
601 if(tag.FLG & 2)GSTransferStatus.PTH2 = IMAGE_MODE;
602 else GSTransferStatus.PTH2 = TRANSFER_MODE;
603 break;
604 case GIF_PATH_3:
605 if(vif1Regs.mskpath3 == 1 && GSTransferStatus.PTH3 == STOPPED_MODE)
606 {
607 GSTransferStatus.PTH3 = IDLE_MODE;
608
609 }
610 else
611 {
612 if(tag.FLG & 2) GSTransferStatus.PTH3 = IMAGE_MODE;
613 else GSTransferStatus.PTH3 = TRANSFER_MODE;
614 }
615 break;
616 }
617
618 }
619 if(GSTransferStatus.PTH3 < PENDINGSTOP_MODE || pathidx != 2)
620 {
621 gifRegs.stat.OPH = true;
622 gifRegs.stat.APATH = pathidx + 1;
623 }
624
625 if(pathidx == GIF_PATH_3)
626 {
627 break;
628 }
629 }
630 else
631 {
632 switch(pathidx)
633 {
634 case GIF_PATH_1:
635 if(tag.FLG & 2)GSTransferStatus.PTH1 = IMAGE_MODE;
636 else GSTransferStatus.PTH1 = TRANSFER_MODE;
637 break;
638 case GIF_PATH_2:
639 if(tag.FLG & 2)GSTransferStatus.PTH2 = IMAGE_MODE;
640 else GSTransferStatus.PTH2 = TRANSFER_MODE;
641 break;
642 case GIF_PATH_3:
643 if(tag.FLG & 2) GSTransferStatus.PTH3 = IMAGE_MODE;
644 else GSTransferStatus.PTH3 = TRANSFER_MODE;
645
646 break;
647 }
648 gifRegs.stat.APATH = pathidx + 1;
649 gifRegs.stat.OPH = true;
650
651 switch(tag.FLG) {
652 case GIF_FLG_PACKED:
653 GIF_LOG("Packed Mode EOP %x", tag.EOP);
654 PrepPackedRegs();
655
656 if(DetectE > 0)
657 {
658 do {
659 if (GetReg() == 0xe) {
660 gsHandler((u8*)pMem128);
661 }
662 copyTag();
663 } while(StepReg() && size > 0 && SIGNAL_IMR_Pending == false);
664 }
665 else
666 {
667 //DevCon.WriteLn(Color_Orange, "No E detected on Path%d: nloop=%x, numregs=%x, curreg=%x, size=%x", pathidx + 1, nloop, numregs, curreg, size);
668
669 // Note: curreg is *usually* zero here, but can be non-zero if a previous fragment was
670 // handled via this optimized copy code below.
671
672 const u32 listlen = (nloop * numregs) - curreg; // the total length of this packed register list (in QWC)
673 u32 len;
674
675 if(size < listlen)
676 {
677 len = size;
678
679 // We need to calculate both the number of full iterations of regs copied (nloops),
680 // and any remaining registers not copied by this fragment. A div/mod pair should
681 // hopefully be optimized by the compiler into a single x86 div. :)
682
683 const int nloops_copied = len / numregs;
684 const int regs_not_copied = len % numregs;
685
686 // Make sure to add regs_not_copied to curreg, to handle cases of multiple partial fragments.
687 // (example: 3 fragments each of only 2 regs, then curreg should be 0, 2, and then 4 after
688 // each call to GIFPath_Parse; with no change to NLOOP). Because of this we also need to
689 // check for cases where curreg wraps past an nloop.
690
691 nloop -= nloops_copied;
692 curreg += regs_not_copied;
693 if(curreg >= numregs)
694 {
695 --nloop;
696 curreg -= numregs;
697 }
698 }
699 else
700 {
701 len = listlen;
702 curreg = 0;
703 nloop = 0;
704 }
705
706 MemCopy_WrappedDest( pMem128, RingBuffer.m_Ring, ringpos, RingBufferSize, len );
707 pMem128 += len;
708 size -= len;
709 }
710 break;
711 case GIF_FLG_REGLIST:
712 {
713 GIF_LOG("Reglist Mode EOP %x", tag.EOP);
714
715 // In reglist mode, the GIF packs 2 registers into each QWC. The nloop however
716 // can be an odd number, in which case the upper half of the final QWC is ignored (skipped).
717
718 numregs = ((tag.NREG-1)&0xf) + 1;
719 const u32 total_reglen = (nloop * numregs) - curreg; // total 'expected length' of this packed register list (in registers)
720 const u32 total_listlen = (total_reglen+1) / 2; // total 'expected length' of the register list, in QWC! (+1 so to round it up)
721
722 u32 len;
723
724 if(size < total_listlen)
725 {
726 //Console.Warning("GIF path %d Fragmented REGLIST! Please report if you experience problems", pathidx + 1);
727
728 len = size;
729 const u32 reglen = len * 2;
730
731 const int nloops_copied = reglen / numregs;
732 const int regs_not_copied = reglen % numregs;
733
734 //DevCon.Warning("Hit it path %d", pathidx + 1);
735 curreg += regs_not_copied;
736 nloop -= nloops_copied;
737
738 if(curreg >= numregs)
739 {
740 --nloop;
741 curreg -= numregs;
742 }
743 }
744 else
745 {
746 len = total_listlen;
747 curreg = 0;
748 nloop = 0;
749 }
750
751 MemCopy_WrappedDest( pMem128, RingBuffer.m_Ring, ringpos, RingBufferSize, len );
752 pMem128 += len;
753 size -= len;
754 }
755 break;
756 case GIF_FLG_IMAGE:
757 case GIF_FLG_IMAGE2:
758 {
759 GIF_LOG("IMAGE Mode EOP %x", tag.EOP);
760 int len = aMin(size, nloop);
761
762 MemCopy_WrappedDest( pMem128, RingBuffer.m_Ring, ringpos, RingBufferSize, len );
763
764 pMem128 += len;
765 size -= len;
766 nloop -= len;
767 }
768 break;
769 }
770 }
771
772 if(pathidx == GIF_PATH_1)
773 {
774 if(size == 0 && (!tag.EOP || nloop > 0))
775 {
776 if(startSize < 0x3ff)
777 {
778 size = 0x3ff - startSize;
779 startSize = 0x3ff;
780 pMem128 -= 0x400;
781 }
782 else
783 {
784 // Note: The BIOS does an XGKICK on the VU1 and lets it DMA to the GS without an EOP
785 // (seemingly to loop forever), only to write an EOP later on. No other game is known to
786 // do anything of the sort.
787 // So lets just cap the DMA at 16k, and force it to "look" like it's terminated for now.
788 // (note: truly accurate emulation would mean having the VU1's XGKICK break execution,
789 // split time to EE and other processors, and then resume the kick's DMA later.
790 // ... yea, not happening for a while. ;) -- air
791
792 Console.Warning("GIFTAG error, size exceeded VU memory size %x", startSize);
793 nloop = 0;
794 const_cast<GIFTAG&>(tag).EOP = 1;
795
796 // Don't send the packet to the GS -- its incomplete and might cause the GS plugin
797 // to get confused and die. >_<
798
799 ringpos = original_ringpos;
800 }
801 }
802 }
803
804 if (tag.EOP && !nloop)
805 {
806 if (CSRreg.FINISH)
807 {
808 // IMPORTANT: only signal FINISH if ALL THREE paths are stopped (nloop is zero and EOP is set)
809 // FINISH is *not* a per-path register, and it seems to pretty clearly indicate that all active
810 // drawing *and* image transfer actions must be finished before the IRQ raises.
811
812 if(gifRegs.stat.P1Q || gifRegs.stat.P2Q || gifRegs.stat.P3Q)
813 {
814 //GH3 and possibly others have path data queued waiting for another path to finish! we need to check they are done too
815 //DevCon.Warning("Early FINISH signal! P1 %x P2 %x P3 %x", gifRegs.stat.P1Q, gifRegs.stat.P2Q, gifRegs.stat.P3Q);
816 }
817 else if (!(GSIMR&0x200) && !s_gifPath.path[0].IsActive() && !s_gifPath.path[1].IsActive() && !s_gifPath.path[2].IsActive())
818 {
819 gsIrq();
820 }
821 }
822
823 // [TODO] : DMAC Arbitration rights should select the next queued GIF transfer here.
824
825 break;
826 }
827 if(SIGNAL_IMR_Pending == true)
828 {
829 //DevCon.Warning("Path %x", pathidx + 1);
830 break;
831 }
832 }
833
834 size = (startSize - size);
835
836 if (tag.EOP && nloop == 0) {
837
838 /*if(gifRegs.stat.DIR == 0)gifRegs.stat.OPH = false;
839 gifRegs.stat.APATH = GIF_APATH_IDLE;*/
840 switch(pathidx)
841 {
842 case GIF_PATH_1:
843 GSTransferStatus.PTH1 = STOPPED_MODE;
844 break;
845 case GIF_PATH_2:
846 GSTransferStatus.PTH2 = STOPPED_MODE;
847 break;
848 case GIF_PATH_3:
849 //For huge chunks we may have delay problems, so we need to stall it till the interrupt, else we get desync (Lemmings)
850 if(size > 8) GSTransferStatus.PTH3 = PENDINGSTOP_MODE;
851 else GSTransferStatus.PTH3 = STOPPED_MODE;
852 if (gifch.chcr.STR) { //Make sure we are really doing a DMA and not using FIFO
853 //GIF_LOG("Path3 end EOP %x NLOOP %x Status %x", tag.EOP, nloop, GSTransferStatus.PTH3);
854 gifch.madr += size * 16;
855 gifch.qwc -= size;
856 }
857 break;
858 }
859 }
860 else if(pathidx == 2)
861 {
862 //if(nloop <= 16 && GSTransferStatus.PTH3 == IMAGE_MODE)GSTransferStatus.PTH3 = PENDINGIMAGE_MODE;
863 if (gifch.chcr.STR) { //Make sure we are really doing a DMA and not using FIFO
864 //GIF_LOG("Path3 end EOP %x NLOOP %x Status %x", tag.EOP, nloop, GSTransferStatus.PTH3);
865 gifch.madr += size * 16;
866 gifch.qwc -= size;
867 }
868 }
869
870 return size;
871 }
872
873 // Parameters:
874 // size - max size of incoming data stream, in qwc (simd128). If the path is PATH1, and the
875 // path does not terminate (EOP) within the specified size, it is assumed that the path must
876 // loop around to the start of VU memory and continue processing.
877 __fi int GIFPath_CopyTag(GIF_PATH pathidx, const u128* pMem, u32 size)
878 {
879 switch( pathidx )
880 {
881 case GIF_PATH_1:
882 pxAssertMsg(!s_gifPath[GIF_PATH_2].IsActive(), "GIFpath conflict: Attempted to start PATH1 while PATH2 is already active.");
883 pxAssertMsg(!s_gifPath[GIF_PATH_3].IsActive() || (GSTransferStatus.PTH3 == IMAGE_MODE), "GIFpath conflict: Attempted to start PATH1 while PATH3 is already active.");
884 return s_gifPath[GIF_PATH_1].CopyTag<GIF_PATH_1,true>(pMem, size);
885 case GIF_PATH_2:
886 pxAssertMsg(!s_gifPath[GIF_PATH_1].IsActive(), "GIFpath conflict: Attempted to start PATH2 while PATH1 is already active.");
887 pxAssertMsg(!s_gifPath[GIF_PATH_3].IsActive() || (GSTransferStatus.PTH3 == IMAGE_MODE), "GIFpath conflict: Attempted to start PATH2 while PATH3 is already active.");
888 return s_gifPath[GIF_PATH_2].CopyTag<GIF_PATH_2,false>(pMem, size);
889 case GIF_PATH_3:
890 pxAssertMsg(!s_gifPath[GIF_PATH_1].IsActive(), "GIFpath conflict: Attempted to start PATH3 while PATH1 is already active.");
891 pxAssertMsg(!s_gifPath[GIF_PATH_2].IsActive(), "GIFpath conflict: Attempted to start PATH3 while PATH2 is already active.");
892 return s_gifPath[GIF_PATH_3].CopyTag<GIF_PATH_3,true>(pMem, size);
893
894 jNO_DEFAULT;
895 }
896
897 return 0; // unreachable
898 }
899
900 // Quick version for queuing PATH1 data.
901 // This version calculates the real length of the packet data only. It does not process
902 // IRQs or DMA status updates.
903 __fi int GIFPath_ParseTagQuick(GIF_PATH pathidx, const u8* pMem, u32 size)
904 {
905 int retSize = s_gifPath[pathidx].ParseTagQuick(pathidx, pMem, size);
906 return retSize;
907 }
908
909 // Clears all GIFpath data to zero.
910 void GIFPath_Reset()
911 {
912 for(uint i=0; i<3; ++i )
913 s_gifPath.path[i].Reset();
914 }
915
916 // This is a hackfix tool provided for "canceling" the contents of the GIFpath when
917 // invalid GIFdma states are encountered (typically needed for PATH3 only).
918 __fi void GIFPath_Clear( GIF_PATH pathidx )
919 {
920 memzero(s_gifPath.path[pathidx]);
921 s_gifPath.path[pathidx].Reset();
922
923 GSTransferStatus._u32 &= ~(0xf << (pathidx * 4));
924 GSTransferStatus._u32 |= (0x5 << (pathidx * 4));
925 if( GSgifSoftReset == NULL ) return;
926 GetMTGS().SendSimplePacket( GS_RINGTYPE_SOFTRESET, (1<<pathidx), 0, 0 );
927 }

  ViewVC Help
Powered by ViewVC 1.1.22