Correct handling of FINISH events on the GIFpath; the FINISH is not posted until *after* all GIFpaths have been drained (nloop is 0 and EOP is 1). This fixes Ys6 bootup hangs.

Note: I'm reasonably sure this is the correct implementation for FINISH.  There's a *good* chance this could cause regressions in some other games that use PATH3 or other DMA tricks.  This will likely be due to hacks or bugs in the GIF/VIF dma code, and not FINISH.  That is, we've been catering to a broken FINISH tag this entire time -- basically telling games the GS paths are clear when they're not. ;)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3320 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-06-27 02:24:00 +00:00
parent 2787e6e2a1
commit f1fb7810be
1 changed files with 79 additions and 37 deletions

View File

@ -90,10 +90,12 @@ struct GIFPath
GIFPath(); GIFPath();
void Reset();
void PrepPackedRegs(); void PrepPackedRegs();
void SetTag(const void* mem); void SetTag(const void* mem);
bool StepReg(); bool StepReg();
u8 GetReg(); u8 GetReg();
bool IsActive() const;
int ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size); int ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size);
int ParseTagQuick(GIF_PATH pathidx, const u8* pMem, u32 size); int ParseTagQuick(GIF_PATH pathidx, const u8* pMem, u32 size);
@ -121,6 +123,7 @@ struct GifPathStruct
// (not that any game's emulation accuracy probably depends on such a 'feature') --air // (not that any game's emulation accuracy probably depends on such a 'feature') --air
bool CSR_SIGNAL_Pending = false; bool CSR_SIGNAL_Pending = false;
// SIGNAL : This register is a double-throw. If the SIGNAL bit in CSR is clear, set the CSR // SIGNAL : This register is a double-throw. If the SIGNAL bit in CSR is clear, set the CSR
// and raise a gsIrq. If CSR is already *set*, then ignore all subsequent drawing operations // and raise a gsIrq. If CSR is already *set*, then ignore all subsequent drawing operations
// and writes to general purpose registers to the GS. (note: I'm pretty sure this includes // and writes to general purpose registers to the GS. (note: I'm pretty sure this includes
@ -139,7 +142,9 @@ static void __fastcall RegHandlerSIGNAL(const u32* data)
GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]); GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]);
// This is not working yet for some reason. Will have to troubleshoot it later. // This is not working yet for some reason. Will have to troubleshoot it later.
// For now having the SIGNAL behave like other interrpts seems to be fine. --air // For now having the SIGNAL behave like other interrupts seems to be fine. --air
// (note: use Soul Calibur 3 for testing double-throw Signals!)
/*if (!(GSIMR&0x100) ) /*if (!(GSIMR&0x100) )
{ {
if (CSRreg.SIGNAL) if (CSRreg.SIGNAL)
@ -171,25 +176,22 @@ static void __fastcall RegHandlerSIGNAL(const u32* data)
} }
// FINISH : Enables end-of-draw signaling. When FINISH is written it tells the GIF to // FINISH : Enables end-of-draw signaling. When FINISH is written it tells the GIF to
// raise a gsIrq and set the FINISH bit of CSR when the current operation is finished. // raise a gsIrq and set the FINISH bit of CSR when the *current drawing operation* is
// As far as I can figure, this feature is meant for EE/GS synchronization when the EE // finished. Translation: Only after all three logical GIFpaths are in EOP status.
// wants to utilize GS post-processing effects. We don't need to emulate that part of
// it since we flush/interlock the GS for those specific read operations.
// //
// However! We should properly emulate handling partial-DMA transfers on PATH2 and // This feature can be used for both reversing the GS transfer mode (downloading post-
// PATH3 of the GIF, which means only signaling FINISH if nloop==0. // processing effects to the EE), and more importantly for *DMA synch* between the
// three logical GIFpaths.
// //
static void __fastcall RegHandlerFINISH(const u32* data) static void __fastcall RegHandlerFINISH(const u32* data)
{ {
GIF_LOG("GIFpath FINISH data=%x_%x CSRr=%x\n", data[0], data[1], GSCSRr); GIF_LOG("GIFpath FINISH data=%x_%x CSRr=%x\n", data[0], data[1], GSCSRr);
if (!CSRreg.FINISH) // The FINISH bit is set here, and then it will be cleared when all three
{ // logical GIFpaths finish their packets (EOPs) At that time (found below
CSRreg.FINISH = true; // in the GIFpath_Parser), IMR is tested and a gsIrq() raised if needed.
if (!(GSIMR&0x200)) CSRreg.FINISH = true;
gsIrq();
}
} }
static void __fastcall RegHandlerLABEL(const u32* data) static void __fastcall RegHandlerLABEL(const u32* data)
@ -244,8 +246,14 @@ static __aligned16 GifPathStruct s_gifPath =
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
GIFPath::GIFPath() : tag() GIFPath::GIFPath() : tag()
{
Reset();
}
__forceinline void GIFPath::Reset()
{ {
memzero(*this); memzero(*this);
const_cast<GIFTAG&>(tag).EOP = 1;
} }
__forceinline bool GIFPath::StepReg() __forceinline bool GIFPath::StepReg()
@ -292,6 +300,11 @@ __forceinline void GIFPath::SetTag(const void* mem)
curreg = 0; curreg = 0;
} }
__forceinline bool GIFPath::IsActive() const
{
return (nloop != 0) || !tag.EOP;
}
void SaveStateBase::gifPathFreeze() void SaveStateBase::gifPathFreeze()
{ {
FreezeTag( "GIFpath" ); FreezeTag( "GIFpath" );
@ -518,13 +531,39 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
break; break;
} }
} }
if (tag.EOP && !nloop) break;
if (tag.EOP && !nloop)
{
if (CSRreg.FINISH)
{
// To resolve potential confusion or false assumptions on when to FINISH:
//
// Our choices are:
// 1. only signal FINISH if ALL THREE paths are stopped (nloop is zero and EOP is set)
// 2. signal FINISH when the current path is stopped.
//
// #1 makes a lot more sense. FINISH is *not* a per-path register, and it seems to pretty
// clearly indicate that all active drawing *and* image transfer actions must be finished
// before the IRQ raises. Furthermore, the Real PS2 has only a single physical GIFpath that
// has the ability to stall Path3 in favor of Path1 and Path2 transfers, so it wouldn't
// really make sense that it would maintain anything other than #1 behavior.
//
// But! If things are working suspiciously, you can always test a hackfix by commenting out
// the conditional below; and see what happens.
if (!s_gifPath.path[0].IsActive() && !s_gifPath.path[1].IsActive() && !s_gifPath.path[2].IsActive())
{
CSRreg.FINISH = false;
if (!(GSIMR&0x200))
gsIrq();
}
}
break;
}
} }
size = (startSize - size); size = (startSize - size);
if (tag.EOP && nloop <= 16) { if (tag.EOP && nloop <= 16) {
if(pathidx == 2 && nloop > 0) if(pathidx == 2 && nloop > 0)
{ {
@ -617,7 +656,8 @@ __forceinline int GIFPath_ParseTagQuick(GIF_PATH pathidx, const u8* pMem, u32 si
// Clears all GIFpath data to zero. // Clears all GIFpath data to zero.
void GIFPath_Reset() void GIFPath_Reset()
{ {
memzero( s_gifPath.path ); for(uint i=0; i<3; ++i )
s_gifPath.path[i].Reset();
} }
// This is a hackfix tool provided for "canceling" the contents of the GIFpath when // This is a hackfix tool provided for "canceling" the contents of the GIFpath when
@ -625,6 +665,8 @@ void GIFPath_Reset()
__forceinline void GIFPath_Clear( GIF_PATH pathidx ) __forceinline void GIFPath_Clear( GIF_PATH pathidx )
{ {
memzero(s_gifPath.path[pathidx]); memzero(s_gifPath.path[pathidx]);
s_gifPath.path[pathidx].Reset();
GSTransferStatus._u32 &= ~(0xf << (pathidx * 4)); GSTransferStatus._u32 &= ~(0xf << (pathidx * 4));
GSTransferStatus._u32 |= (0x5 << (pathidx * 4)); GSTransferStatus._u32 |= (0x5 << (pathidx * 4));
if( GSgifSoftReset == NULL ) return; if( GSgifSoftReset == NULL ) return;