From c10e47739e401df4fbb3e1a3d31a285bf7d75afb Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Sat, 26 Jun 2010 07:26:43 +0000 Subject: [PATCH] git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3308 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/Counters.cpp | 8 +- pcsx2/GS.cpp | 168 ++++++++++++++--------- pcsx2/GS.h | 249 ++++++++++++++++++++++------------- pcsx2/Gif.cpp | 6 +- pcsx2/MTGS.cpp | 147 ++++++++------------- pcsx2/SaveState.h | 2 +- pcsx2/gui/GlobalCommands.cpp | 2 +- pcsx2/ps2/GIFpath.cpp | 35 +++-- 8 files changed, 343 insertions(+), 274 deletions(-) diff --git a/pcsx2/Counters.cpp b/pcsx2/Counters.cpp index 6bcd9d9e58..789dea70e5 100644 --- a/pcsx2/Counters.cpp +++ b/pcsx2/Counters.cpp @@ -350,13 +350,13 @@ static __forceinline void VSyncStart(u32 sCycle) CpuVU0->Vsync(); CpuVU1->Vsync(); - if ((CSRw & 0x8)) + if (!CSRreg.VSINT) { + CSRreg.VSINT = true; if (!(GSIMR&0x800)) { gsIrq(); } - GSCSRr|= 0x8; } hwIntcIrq(INTC_VBLANK_S); @@ -423,13 +423,13 @@ __forceinline void rcntUpdate_hScanline() hsyncCounter.Mode = MODE_HRENDER; } else { //HBLANK END / HRENDER Begin - if (CSRw & 0x4) + if (!CSRreg.HSINT) { + CSRreg.HSINT = true; if (!(GSIMR&0x400)) { gsIrq(); } - GSCSRr |= 4; // signal } if (gates) rcntEndGate(false, hsyncCounter.sCycle); if (psxhblankgate) psxCheckEndGate16(0); diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 7356a3af61..97a6f5c4ef 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -25,10 +25,7 @@ using namespace Threading; using namespace R5900; -u32 CSRw; - __aligned16 u8 g_RealGSMem[0x2000]; -extern int m_nCounters[]; void gsOnModeChanged( Fixed100 framerate, u32 newTickrate ) { @@ -54,20 +51,7 @@ void gsInit() memzero(g_RealGSMem); } -void gsReset() -{ - GetMTGS().ResetGS(); - - UpdateVSyncRate(); - GSTransferStatus = (STOPPED_MODE<<8) | (STOPPED_MODE<<4) | STOPPED_MODE; - memzero(g_RealGSMem); - - GSCSRr = 0x551B4000; // Set the FINISH bit to 1 for now - GSIMR = 0x7f00; - gifRegs->stat.reset(); - gifRegs->ctrl.reset(); - gifRegs->mode.reset(); -} +extern bool CSR_SIGNAL_Pending; void gsGIFReset() { @@ -76,9 +60,27 @@ void gsGIFReset() gifRegs->mode.reset(); } -void gsCSRwrite(u32 value) +void gsReset() { - if (value & 0x200) { // resetGS + GetMTGS().ResetGS(); + + UpdateVSyncRate(); + GSTransferStatus = (STOPPED_MODE<<8) | (STOPPED_MODE<<4) | STOPPED_MODE; + memzero(g_RealGSMem); + + CSR_SIGNAL_Pending = false; + + CSRreg.Reset(); + GSIMR = 0x7f00; + + // FIXME: This really doesn't belong here, and I seriously doubt it's needed. + // If it is needed it should be in the GIF portion of hwReset(). --air + gsGIFReset(); +} + +static __forceinline void gsCSRwrite( const tGS_CSR& csr ) +{ + if (csr.RESET) { // perform a soft reset -- which is a clearing of all GIFpaths -- and fall back to doing // a full reset if the plugin doesn't support soft resets. @@ -94,53 +96,74 @@ void gsCSRwrite(u32 value) GetMTGS().SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 ); } - CSRw |= 0x1f; - GSCSRr = 0x551B4000; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib) - GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1 + CSR_SIGNAL_Pending = false; + CSRreg.Reset(); + GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1 } - if( value & 0x100 ) // FLUSH + + if(csr.FLUSH) { // Our emulated GS has no FIFO, but if it did, it would flush it here... //Console.WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr); } - CSRw |= value & 0x1f; - GetMTGS().SendSimplePacket( GS_RINGTYPE_WRITECSR, CSRw, 0, 0 ); - GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f); + if(csr.SIGNAL) + { + // SIGNAL has special behavior. If a SIGNAL has occurred twice in a row, we need + // to raise the second one pending immediately. CSR_SIGNAL_Pending is only set true + // if a second SIGNAL is pending (and thus drawing ops are disabled). + // (note: PS2 apps are expected to write a successive 1 and 0 to the IMR in order to + // trigger the gsInt and clear the second pending SIGNAL -- if they fail to do so, the + // GS will freeze again upon the very next SIGNAL). + + CSRreg.SIGNAL = CSR_SIGNAL_Pending; + if( CSRreg.SIGNAL ) + CSR_SIGNAL_Pending = false; + } + + if(csr.FINISH) CSRreg.FINISH = false; + if(csr.HSINT) CSRreg.HSINT = false; + if(csr.VSINT) CSRreg.VSINT = false; + if(csr.EDWINT) CSRreg.EDWINT = false; } -static void IMRwrite(u32 value) +static __forceinline void IMRwrite(u32 value) { GSIMR = (value & 0x1f00)|0x6000; - if((GSCSRr & 0x1f) & (~(GSIMR >> 8) & 0x1f)) - { + if(CSRreg.GetInterruptMask() & (~(GSIMR >> 8) & 0x1f)) gsIrq(); - } - // don't update mtgs mem } __forceinline void gsWrite8(u32 mem, u8 value) { switch (mem) { + // CSR 8-bit write handlers. + // I'm quite sure these whould just write the CSR portion with the other + // bits set to 0 (no action). The previous implementation masked the 8-bit + // write value against the previous CSR write value, but that really doesn't + // make any sense, given that the real hardware's CSR circuit probably has no + // real "memory" where it saves anything. (for example, you can't write to + // and change the GS revision or ID portions -- they're all hard wired.) --air + case GS_CSR: // GS_CSR - gsCSRwrite((CSRw & ~0x000000ff) | value); break; + gsCSRwrite( tGS_CSR((u32)value) ); break; case GS_CSR + 1: // GS_CSR - gsCSRwrite((CSRw & ~0x0000ff00) | (value << 8)); break; + gsCSRwrite( tGS_CSR(((u32)value) << 8) ); break; case GS_CSR + 2: // GS_CSR - gsCSRwrite((CSRw & ~0x00ff0000) | (value << 16)); break; + gsCSRwrite( tGS_CSR(((u32)value) << 16) ); break; case GS_CSR + 3: // GS_CSR - gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break; + gsCSRwrite( tGS_CSR(((u32)value) << 24) ); break; + default: *PS2GS_BASE(mem) = value; - GetMTGS().SendSimplePacket(GS_RINGTYPE_MEMWRITE8, mem&0x13ff, value, 0); } GIF_LOG("GS write 8 at %8.8lx with data %8.8lx", mem, value); } -__forceinline void _gsSMODEwrite( u32 mem, u32 value ) +static __forceinline void _gsSMODEwrite( u32 mem, u32 value ) { switch (mem) { @@ -165,12 +188,15 @@ __forceinline void gsWrite16(u32 mem, u16 value) switch (mem) { + // See note above about CSR 8 bit writes, and handling them as zero'd bits + // for all but the written parts. + case GS_CSR: - gsCSRwrite( (CSRw&0xffff0000) | value); + gsCSRwrite( tGS_CSR((u32)value) ); return; // do not write to MTGS memory case GS_CSR+2: - gsCSRwrite( (CSRw&0xffff) | ((u32)value<<16)); + gsCSRwrite( tGS_CSR(((u32)value) << 16) ); return; // do not write to MTGS memory case GS_IMR: @@ -179,7 +205,6 @@ __forceinline void gsWrite16(u32 mem, u16 value) } *(u16*)PS2GS_BASE(mem) = value; - GetMTGS().SendSimplePacket(GS_RINGTYPE_MEMWRITE16, mem&0x13ff, value, 0); } ////////////////////////////////////////////////////////////////////////// @@ -187,7 +212,7 @@ __forceinline void gsWrite16(u32 mem, u16 value) __forceinline void gsWrite32(u32 mem, u32 value) { - jASSUME( (mem & 3) == 0 ); + pxAssume( (mem & 3) == 0 ); GIF_LOG("GS write 32 at %8.8lx with data %8.8lx", mem, value); _gsSMODEwrite( mem, value ); @@ -195,7 +220,7 @@ __forceinline void gsWrite32(u32 mem, u32 value) switch (mem) { case GS_CSR: - gsCSRwrite(value); + gsCSRwrite(tGS_CSR(value)); return; case GS_IMR: @@ -204,12 +229,19 @@ __forceinline void gsWrite32(u32 mem, u32 value) } *(u32*)PS2GS_BASE(mem) = value; - GetMTGS().SendSimplePacket(GS_RINGTYPE_MEMWRITE32, mem&0x13ff, value, 0); } ////////////////////////////////////////////////////////////////////////// // GS Write 64 bit +void __fastcall gsWrite64_generic( u32 mem, const mem64_t* value ) +{ + const u32* const srcval32 = (u32*)value; + GIF_LOG("GS Write64 at %8.8lx with data %8.8x_%8.8x", mem, srcval32[1], srcval32[0]); + + *(u64*)PS2GS_BASE(mem) = *value; +} + void __fastcall gsWrite64_page_00( u32 mem, const mem64_t* value ) { gsWrite64_generic( mem, value ); @@ -223,18 +255,29 @@ void __fastcall gsWrite64_page_01( u32 mem, const mem64_t* value ) switch( mem ) { case 0x12001040: //busdir - gsWrite64_generic( mem, value ); //This is probably a complete hack, however writing to BUSDIR "should" start a transfer (Bleach Blade Battlers) //Only problem is it kills killzone :( leaving it commented out for now. + // (yes it *is* a complete hack; both lines here in fact --air) //========================================================================= //gifRegs->stat.OPH = true; //========================================================================= gifRegs->stat.DIR = (u32)value; - + + // BUSDIR INSANITY !! MTGS FLUSH NEEDED + // + // Yup folks. BUSDIR is evil. The only safe way to handle it is to flush the whole MTGS + // and ensure complete MTGS and EEcore thread synchronization This is very slow, no doubt, + // but on the birght side BUSDIR is used quite rately, indeed. + + // Important: writeback to gsRegs area *prior* to flushing the MTGS. The flush will sync + // the GS and MTGS register states, and upload our screwy busdir register in the process. :) + gsWrite64_generic( mem, value ); + GetMTGS().WaitGS(); return; + case GS_CSR: - gsCSRwrite((u32)value[0]); + gsCSRwrite(tGS_CSR(*value)); return; case GS_IMR: @@ -245,15 +288,6 @@ void __fastcall gsWrite64_page_01( u32 mem, const mem64_t* value ) gsWrite64_generic( mem, value ); } -void __fastcall gsWrite64_generic( u32 mem, const mem64_t* value ) -{ - const u32* const srcval32 = (u32*)value; - GIF_LOG("GS Write64 at %8.8lx with data %8.8x_%8.8x", mem, srcval32[1], srcval32[0]); - - *(u64*)PS2GS_BASE(mem) = *value; - GetMTGS().SendSimplePacket(GS_RINGTYPE_MEMWRITE64, mem&0x13ff, srcval32[0], srcval32[1]); -} - ////////////////////////////////////////////////////////////////////////// // GS Write 128 bit @@ -287,13 +321,10 @@ void __fastcall gsWrite128_generic( u32 mem, const mem128_t* value ) srcval32[3], srcval32[2], srcval32[1], srcval32[0]); const uint masked_mem = mem & 0x13ff; - u64* writeTo = (u64*)(&g_RealGSMem[masked_mem]); + u64* writeTo = (u64*)(&PS2MEM_GS[masked_mem]); writeTo[0] = value[0]; writeTo[1] = value[1]; - - GetMTGS().SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem, srcval32[0], srcval32[1]); - GetMTGS().SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem+8, srcval32[2], srcval32[3]); } __forceinline u8 gsRead8(u32 mem) @@ -347,13 +378,22 @@ void gsIrq() { __forceinline void gsFrameSkip() { - - if( !EmuConfig.GS.FrameSkipEnable ) return; - static int consec_skipped = 0; static int consec_drawn = 0; static bool isSkipping = false; + if( !EmuConfig.GS.FrameSkipEnable ) + { + if( isSkipping ) + { + // Frameskipping disabled on-the-fly .. make sure the GS is restored to non-skip + // behavior. + GSsetFrameSkip( false ); + isSkipping = false; + } + return; + } + GSsetFrameSkip( isSkipping ); if( isSkipping ) @@ -378,7 +418,7 @@ __forceinline void gsFrameSkip() void gsPostVsyncEnd() { - *(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field + CSRreg.SwapField(); GetMTGS().PostVsyncEnd(); } @@ -396,6 +436,6 @@ void gsResetFrameSkip() void SaveStateBase::gsFreeze() { FreezeMem(PS2MEM_GS, 0x2000); - Freeze(CSRw); + Freeze(CSR_SIGNAL_Pending); gifPathFreeze(); } diff --git a/pcsx2/GS.h b/pcsx2/GS.h index 0b60c8a163..f65302cab5 100644 --- a/pcsx2/GS.h +++ b/pcsx2/GS.h @@ -20,129 +20,201 @@ extern __aligned16 u8 g_RealGSMem[Ps2MemSize::GSregs]; -enum CSRfifoState +enum CSR_FifoState { - CSR_FIFO_NORMAL = 0, // Neither empty or almost full. - CSR_FIFO_EMPTY, // Empty - CSR_FIFO_FULL, // Almost Full - CSR_FIFO_RESERVED + CSR_FIFO_NORMAL = 0, // Somwhere in between (Neither empty or almost full). + CSR_FIFO_EMPTY, // Empty + CSR_FIFO_FULL, // Almost Full + CSR_FIFO_RESERVED // Reserved / Unused. }; -union tGS_CSRw -{ - struct - { - u32 SIGNAL : 1; // SIGNAL event - u32 FINISH : 1; // FINISH event - u32 HSINT : 1; // HSYNC Interrupt - u32 VSINT : 1; // VSYNC Interrupt - u32 EDWINT : 1; // Rect Area Write Termination Interrupt - }; - u32 _u32; - - void reset() { _u32 = 0; } - void fill() - { - SIGNAL = true; - FINISH = true; - HSINT = true; - VSINT = true; - EDWINT = true; - } -}; - -// I'm initializing this as 64 bit because GSCSRr is 64 bit. There only appeared to be 32 bits worth of fields, -// and CSRw is 32 bit, though, so I'm not sure if that's correct. +// -------------------------------------------------------------------------------------- +// tGS_CSR +// -------------------------------------------------------------------------------------- +// This is the Control Register for the GS. It is a dual-instance register that returns +// distinctly different values for most fields when read and written. In PCSX2 we house +// the written version in the gsRegs buffer, and generate the readback version on-demand +// from various other PCSX2 system statuses. union tGS_CSR { - struct - { - // Start Interrupts. - // If reading, 1 means a signal has been generated. - u64 SIGNAL : 1; // SIGNAL event - u64 FINISH : 1; // FINISH event - u64 HSINT : 1; // HSYNC Interrupt - u64 VSINT : 1; // VSYNC Interrupt - u64 EDWINT : 1; // Rect Area Write Termination Interrupt - // End of Interrupts. Those 5 fields together are 0x1f. + struct + { + // Write: + // 0 - No action; + // 1 - Old event is cleared and event is enabled. + // Read: + // 0 - No SIGNAL pending. + // 1 - SIGNAL has been generated. + u64 SIGNAL :1; + + // Write: + // 0 - No action; + // 1 - FINISH event is enabled. + // Read: + // 0 - No FINISH event pending. + // 1 - FINISH event has been generated. + u64 FINISH :1; + + // Hsync Interrupt Control + // Write: + // 0 - No action; + // 1 - Hsync interrupt is enabled. + // Read: + // 0 - No Hsync interrupt pending. + // 1 - Hsync interrupt has been generated. + u64 HSINT :1; + + // Vsync Interrupt Control + // Write: + // 0 - No action; + // 1 - Vsync interrupt is enabled. + // Read: + // 0 - No Vsync interrupt pending. + // 1 - Vsync interrupt has been generated. + u64 VSINT :1; + + // Rect Area Write Termination Control + // 0 - No action; + // 1 - Rect area write interrupt is enabled. + // Read: + // 0 - No RAWrite interrupt pending. + // 1 - RAWrite interrupt has been generated. + u64 EDWINT :1; + + u64 _zero1 :1; + u64 _zero2 :1; + u64 pad1 :1; + + // FLUSH (write-only!) + // Write: + // 0 - Resume drawing if suspended (?) + // 1 - Flush the GS FIFO and suspend drawing + // Read: Always returns 0. (?) + u64 FLUSH :1; + + // RESET (write-only!) + // Write: + // 0 - Do nothing. + // 1 - GS soft system reset. Clears FIFOs and resets IMR to all 1's. + // (PCSX2 implementation also clears GIFpaths, though that behavior may differ on real HW). + // Read: Always returns 0. (?) + u64 RESET :1; + + u64 _pad2 :2; + + // (I have no idea what this reg is-- air) + // Output value is updated by sampling the VSync. (?) + u64 NFIELD :1; + + // Current Field of Display [page flipping] (read-only?) + // 0 - EVEN + // 1 - ODD + u64 FIELD :1; + + // GS FIFO Status (read-only) + // 00 - Somewhere in between + // 01 - Empty + // 10 - Almost Full + // 11 - Reserved (unused) + // Assign values using the CSR_FifoState enum. + u64 FIFO :2; + + // Revision number of the GS (fairly arbitrary) + u64 REV :8; + + // ID of the GS (also fairly arbitrary) + u64 ID :8; + }; - u64 undefined : 2; // Should both be 0. - u64 reserved1 : 1; - u64 FLUSH : 1; // Drawing Suspend And FIFO Clear - u64 RESET : 1; // GS System Reset - u64 reserved2 : 2; - u64 NFIELD : 1; - u64 FIELD : 1; // If the field currently displayed in Interlace mode is even or odd - u64 FIFO : 2; - u64 REV : 8; // The GS's Revision number - u64 ID : 8; // The GS's Id. - u64 reserved3 : 32; - }; u64 _u64; - - void reset() + + struct { - _u64 = 0; - FIFO = CSR_FIFO_EMPTY; - REV = 0x1D; // GS Revision - ID = 0x55; // GS ID + u32 _u32; // lower 32 bits (all useful content!) + u32 _unused32; // upper 32 bits (unused -- should probably be 0) + }; + + void SwapField() + { + _u32 ^= 0x2000; + } + + void Reset() + { + _u64 = 0; + FIFO = CSR_FIFO_EMPTY; + REV = 0x1B; // GS Revision + ID = 0x55; // GS ID } - void set(u64 value) - { - _u64 = value; - } + bool HasAnyInterrupts() const { return (SIGNAL || FINISH || HSINT || VSINT || EDWINT); } - bool interrupts() { return (SIGNAL || FINISH || HSINT || VSINT || EDWINT); } + u32 GetInterruptMask() const + { + return _u32 & 0x1f; + } - void setAllInterrupts(bool value) + void SetAllInterrupts(bool value=true) { SIGNAL = FINISH = HSINT = VSINT = EDWINT = value; } tGS_CSR(u64 val) { _u64 = val; } - tGS_CSR(u32 val) { _u64 = (u64)val; } - tGS_CSR() { reset(); } + tGS_CSR(u32 val) { _u32 = val; } + tGS_CSR() { Reset(); } }; +// -------------------------------------------------------------------------------------- +// tGS_IMR +// -------------------------------------------------------------------------------------- union tGS_IMR { struct { - u32 reserved1 : 8; - u32 SIGMSK : 1; - u32 FINISHMSK : 1; - u32 HSMSK : 1; - u32 VSMSK : 1; - u32 EDWMSK : 1; - u32 undefined : 2; // Should both be set to 1. - u32 reserved2 : 17; + u32 _reserved1 : 8; + u32 SIGMSK : 1; + u32 FINISHMSK : 1; + u32 HSMSK : 1; + u32 VSMSK : 1; + u32 EDWMSK : 1; + u32 _undefined : 2; // Should both be set to 1. + u32 _reserved2 : 17; }; u32 _u32; + void reset() { _u32 = 0; SIGMSK = FINISHMSK = HSMSK = VSMSK = EDWMSK = true; - undefined = 0x3; + _undefined = 0x3; } void set(u32 value) { _u32 = (value & 0x1f00); // Set only the interrupt mask fields. - undefined = 0x3; // These should always be set. + _undefined = 0x3; // These should always be set. } - bool masked() { return (SIGMSK || FINISHMSK || HSMSK || VSMSK || EDWMSK); } + bool masked() const { return (SIGMSK || FINISHMSK || HSMSK || VSMSK || EDWMSK); } +}; + +// -------------------------------------------------------------------------------------- +// GSRegSIGBLID +// -------------------------------------------------------------------------------------- +struct GSRegSIGBLID +{ + u32 SIGID; + u32 LBLID; }; #define PS2MEM_GS g_RealGSMem -#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff)) +#define PS2GS_BASE(mem) (PS2MEM_GS+(mem&0x13ff)) -#define GSCSRregs ((tGS_CSR&)*(g_RealGSMem+0x1000)) -#define GSIMRregs ((tGS_IMR&)*(g_RealGSMem+0x1010)) +#define CSRreg ((tGS_CSR&)*(PS2MEM_GS+0x1000)) +#define GSIMRregs ((tGS_IMR&)*(PS2MEM_GS+0x1010)) -#define GSCSRr ((u64&)*(g_RealGSMem+0x1000)) -#define GSIMR ((u32&)*(g_RealGSMem+0x1010)) -#define GSSIGLBLID ((GSRegSIGBLID&)*(g_RealGSMem+0x1080)) +#define GSCSRr ((u32&)*(PS2MEM_GS+0x1000)) +#define GSIMR ((u32&)*(PS2MEM_GS+0x1010)) +#define GSSIGLBLID ((GSRegSIGBLID&)*(PS2MEM_GS+0x1080)) enum GS_RegionMode { @@ -176,19 +248,12 @@ enum MTGS_RingCommand GS_RINGTYPE_P1 , GS_RINGTYPE_P2 , GS_RINGTYPE_P3 -, GS_RINGTYPE_MEMWRITE64 - -, GS_RINGTYPE_MEMWRITE8 -, GS_RINGTYPE_MEMWRITE16 -, GS_RINGTYPE_MEMWRITE32 - , GS_RINGTYPE_RESTART , GS_RINGTYPE_VSYNC , GS_RINGTYPE_FRAMESKIP , GS_RINGTYPE_FREEZE , GS_RINGTYPE_RESET // issues a GSreset() command. , GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF -, GS_RINGTYPE_WRITECSR , GS_RINGTYPE_MODECHANGE // for issued mode changes. , GS_RINGTYPE_CRC }; @@ -252,6 +317,7 @@ public: void WaitGS(); void ResetGS(); + int PrepDataPacket( MTGS_RingCommand cmd, u32 size ); int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size ); int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size ); void SendDataPacket(); @@ -313,7 +379,6 @@ extern void _gs_ResetFrameskip(); // used for resetting GIF fifo extern void gsGIFReset(); -extern void gsCSRwrite(u32 value); extern void gsWrite8(u32 mem, u8 value); extern void gsWrite16(u32 mem, u16 value); @@ -334,7 +399,7 @@ extern u64 gsRead64(u32 mem); void gsIrq(); -extern u32 CSRw; +extern tGS_CSR CSRr; // GS Playback enum gsrun diff --git a/pcsx2/Gif.cpp b/pcsx2/Gif.cpp index 375f57a121..89dd5918d9 100644 --- a/pcsx2/Gif.cpp +++ b/pcsx2/Gif.cpp @@ -42,12 +42,10 @@ u32 Path1ReadPos = 0; static __forceinline void clearFIFOstuff(bool full) { - GSCSRr &= ~0xC000; //Clear FIFO stuff - if (full) - GSCSRr |= 0x8000; //FIFO full + CSRreg.FIFO = CSR_FIFO_FULL; else - GSCSRr |= 0x4000; //FIFO empty + CSRreg.FIFO = CSR_FIFO_EMPTY; } void gsPath1Interrupt() diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index b29db9a312..cb7e84f668 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -140,9 +140,28 @@ void SysMtgsThread::ResetGS() GIFPath_Reset(); } +struct RingCmdPacket_Vsync +{ + u8 regset1[0x100]; + u32 csr; + u32 imr; + GSRegSIGBLID siglblid; +}; + void SysMtgsThread::PostVsyncEnd() { - SendSimplePacket( GS_RINGTYPE_VSYNC, (*(u32*)(PS2MEM_GS+0x1000)&0x2000), 0, 0 ); + // Optimization note: Typically regset1 isn't needed. The regs in that area are typically + // changed infrequently, usually during video mode changes. However, on modern systems the + // 256-byte copy is only a few dozen cycles -- executed 60 times a second -- so probably + // not worth the effort or overhead of trying to selectively avoid it. + + PrepDataPacket(GS_RINGTYPE_VSYNC, sizeof(RingCmdPacket_Vsync)); + RingCmdPacket_Vsync& local( *(RingCmdPacket_Vsync*)GetDataPacketPtr() ); + memcpy_fast( local.regset1, PS2MEM_GS, sizeof(local.regset1) ); + local.csr = GSCSRr; + local.imr = GSIMR; + local.siglblid = GSSIGLBLID; + SendDataPacket(); // Alter-frame flushing! Restarts the ringbuffer (wraps) on every other frame. This is a // mandatory feature that prevents the MTGS from queuing more than 2 frames at any time. @@ -235,7 +254,6 @@ void SysMtgsThread::OpenPlugin() m_PluginOpened = true; m_sem_OpenDone.Post(); - GSCSRr = 0x551B4000; // 0x55190000 GSsetGameCRC( ElfCRC, 0 ); } @@ -346,11 +364,6 @@ void SysMtgsThread::ExecuteTaskInThread() } break; - case GS_RINGTYPE_MEMWRITE64: - MTGS_LOG( "(MTGS Packet Read) ringtype=Write64, data=%lu", *(u64*)&tag.data[1] ); - *(u64*)(RingBuffer.Regs+tag.data[0]) = *(u64*)&tag.data[1]; - break; - default: { switch( tag.command ) @@ -362,8 +375,20 @@ void SysMtgsThread::ExecuteTaskInThread() case GS_RINGTYPE_VSYNC: { + const int qsize = tag.data[0]; + ringposinc += qsize; + MTGS_LOG( "(MTGS Packet Read) ringtype=Vsync, field=%u, skip=%s", tag.data[0], tag.data[1] ? "true" : "false" ); - GSvsync(tag.data[0]); + + // Mail in the important GS registers. + RingCmdPacket_Vsync& local((RingCmdPacket_Vsync&)RingBuffer[m_RingPos+1]); + + memcpy_fast( RingBuffer.Regs, local.regset1, sizeof(local.regset1)); + ((u32&)RingBuffer.Regs[0x1000]) = local.csr; + ((u32&)RingBuffer.Regs[0x1010]) = local.imr; + ((GSRegSIGBLID&)RingBuffer.Regs[0x1080]) = local.siglblid; + + GSvsync(!(local.csr & 0x2000)); gsFrameSkip(); // if we're not using GSOpen2, then the GS window is on this thread (MTGS thread), @@ -380,21 +405,6 @@ void SysMtgsThread::ExecuteTaskInThread() _gs_ResetFrameskip(); break; - case GS_RINGTYPE_MEMWRITE8: - MTGS_LOG( "(MTGS Packet Read) ringtype=Write8, addr=0x%08x, data=0x%02x", tag.data[0], (u8)tag.data[1] ); - *(u8*)RingBuffer.Regs[tag.data[0]] = (u8)tag.data[1]; - break; - - case GS_RINGTYPE_MEMWRITE16: - MTGS_LOG( "(MTGS Packet Read) ringtype=Write8, addr=0x%08x, data=0x%04x", tag.data[0], (u16)tag.data[1] ); - *(u16*)(RingBuffer.Regs+tag.data[0]) = (u16)tag.data[1]; - break; - - case GS_RINGTYPE_MEMWRITE32: - MTGS_LOG( "(MTGS Packet Read) ringtype=Write8, addr=0x%08x, data=0x%08x", tag.data[0], (u32)tag.data[1] ); - *(u32*)(RingBuffer.Regs+tag.data[0]) = tag.data[1]; - break; - case GS_RINGTYPE_FREEZE: { MTGS_FreezeData* data = (MTGS_FreezeData*)(*(uptr*)&tag.data[1]); @@ -416,11 +426,6 @@ void SysMtgsThread::ExecuteTaskInThread() } break; - case GS_RINGTYPE_WRITECSR: - MTGS_LOG( "(MTGS Packet Read) ringtype=WriteCSR, value=%08x", tag.data[0] ); - GSwriteCSR( tag.data[0] ); - break; - case GS_RINGTYPE_MODECHANGE: // [TODO] some frameskip sync logic might be needed here! break; @@ -522,6 +527,9 @@ void SysMtgsThread::WaitGS() RethrowException(); } while( volatize(m_RingPos) != m_WritePos ); } + + // Completely synchronize GS and MTGS register states. + memcpy_fast( RingBuffer.Regs, PS2MEM_GS, sizeof(RingBuffer.Regs) ); } // Sets the gsEvent flag and releases a timeslice. @@ -591,69 +599,8 @@ int SysMtgsThread::PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 siz return PrepDataPacket( pathidx, (u8*)srcdata, size ); } -#ifdef PCSX2_GSRING_TX_STATS -static u32 ringtx_s=0; -static u32 ringtx_s_ulg=0; -static u32 ringtx_s_min=0xFFFFFFFF; -static u32 ringtx_s_max=0; -static u32 ringtx_c=0; -static u32 ringtx_inf[32][32]; -static u32 ringtx_inf_s[32]; -#endif - -// Returns the amount of giftag data processed (in simd128 values). -// Return value is used by VU1's XGKICK instruction to wrap the data -// around VU memory instead of having buffer overflow... -// Parameters: -// size - size of the packet data, in smd128's -int SysMtgsThread::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size ) +int SysMtgsThread::PrepDataPacket( MTGS_RingCommand cmd, u32 size ) { - //m_PacketLocker.Acquire(); - -#ifdef PCSX2_GSRING_TX_STATS - ringtx_s += size; - ringtx_s_ulg += size&0x7F; - ringtx_s_min = min(ringtx_s_min,size); - ringtx_s_max = max(ringtx_s_max,size); - ringtx_c++; - u32 tx_sz; - - if (_BitScanReverse(&tx_sz,size)) - { - u32 tx_algn; - _BitScanForward(&tx_algn,size); - ringtx_inf[tx_sz][tx_algn]++; - ringtx_inf_s[tx_sz]+=size; - } - if (ringtx_s>=128*1024*1024) - { - Console.Status("GSRingBufCopy:128MB in %d tx -> b/tx: AVG = %.2f , max = %d, min = %d",ringtx_c,ringtx_s/(float)ringtx_c,ringtx_s_max,ringtx_s_min); - for (int i=0;i<32;i++) - { - u32 total_bucket=0; - u32 bucket_subitems=0; - for (int j=0;j<32;j++) - { - if (ringtx_inf[i][j]) - { - total_bucket+=ringtx_inf[i][j]; - bucket_subitems++; - Console.Warning("GSRingBufCopy :tx [%d,%d] algn %d : count= %d [%.2f%%]",1<Id ).c_str(), searchfor + acode.ToString().c_str(), fromUTF8( result->Id ).c_str(), fromUTF8( searchfor ).c_str() ); } diff --git a/pcsx2/ps2/GIFpath.cpp b/pcsx2/ps2/GIFpath.cpp index 95be10c5ab..a74b15dcb9 100644 --- a/pcsx2/ps2/GIFpath.cpp +++ b/pcsx2/ps2/GIFpath.cpp @@ -24,12 +24,6 @@ // GIFpath -- the GIFtag Parser // -------------------------------------------------------------------------------------- -struct GSRegSIGBLID -{ - u32 SIGID; - u32 LBLID; -}; - enum GIF_FLG { GIF_FLG_PACKED = 0, @@ -115,6 +109,7 @@ struct GifPathStruct __forceinline GIFPath& operator[]( int idx ) { return path[idx]; } }; + // -------------------------------------------------------------------------------------- // SIGNAL / FINISH / LABEL (WIP!!) // -------------------------------------------------------------------------------------- @@ -122,6 +117,10 @@ struct GifPathStruct // an extra VM-state status var to be handled correctly. // +// [TODO] -- Apparently all gs writes should be ignored when this little flag is TRUE. +// (not that any game's emulation accuracy probably depends on such a 'feature') --air +bool CSR_SIGNAL_Pending = false; + // 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 writes to general purpose registers to the GS. (note: I'm pretty sure this includes @@ -135,18 +134,26 @@ struct GifPathStruct // static void __fastcall RegHandlerSIGNAL(const u32* data) { - GIF_LOG("MTGS SIGNAL data %x_%x CSRw %x IMR %x CSRr\n",data[0], data[1], CSRw, GSIMR, GSCSRr); + GIF_LOG("GS SIGNAL data=%x_%x IMR=%x CSRr=%x\n",data[0], data[1], GSIMR, GSCSRr); GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]); - if ((CSRw & 0x1)) + if (!(GSIMR&0x100) ) { - if (!(GSIMR&0x100) ) + if (CSRreg.SIGNAL) { + // Time to ignore all subsequent drawing operations. + if (!CSR_SIGNAL_Pending) + { + DevCon.WriteLn( Color_StrongOrange, "GS SIGNAL double throw encountered!" ); + CSR_SIGNAL_Pending = true; + } + } + else + { + CSRreg.SIGNAL = true; gsIrq(); } - - GSCSRr |= 1; // signal } } @@ -161,14 +168,14 @@ static void __fastcall RegHandlerSIGNAL(const u32* data) // static void __fastcall RegHandlerFINISH(const u32* data) { - GIF_LOG("GIFpath FINISH data %x_%x CSRw %x\n", data[0], data[1], CSRw); + GIF_LOG("GIFpath FINISH data=%x_%x CSRr=%x\n", data[0], data[1], GSCSRr); - if ((CSRw & 0x2)) + if (!CSRreg.FINISH) { if (!(GSIMR&0x200)) gsIrq(); - GSCSRr |= 2; // finish + CSRreg.FINISH = true; } }