mirror of https://github.com/PCSX2/pcsx2.git
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3308 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
06e4ae00b2
commit
c10e47739e
|
@ -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);
|
||||
|
|
168
pcsx2/GS.cpp
168
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();
|
||||
}
|
||||
|
|
249
pcsx2/GS.h
249
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
|
||||
|
|
|
@ -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()
|
||||
|
|
147
pcsx2/MTGS.cpp
147
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<<i,(1<<(i+1))-16,1<<j,ringtx_inf[i][j],ringtx_inf[i][j]/(float)ringtx_c*100);
|
||||
ringtx_inf[i][j]=0;
|
||||
}
|
||||
}
|
||||
if (total_bucket)
|
||||
Console.Warning("GSRingBufCopy :tx [%d,%d] total : count= %d [%.2f%%] [%.2f%%]",1<<i,(1<<(i+1))-16,total_bucket,total_bucket/(float)ringtx_c*100,ringtx_inf_s[i]/(float)ringtx_s*100);
|
||||
ringtx_inf_s[i]=0;
|
||||
}
|
||||
Console.Warning("GSRingBufCopy :tx ulg count =%d [%.2f%%]",ringtx_s_ulg,ringtx_s_ulg/(float)ringtx_s*100);
|
||||
ringtx_s_ulg=0;
|
||||
ringtx_c=0;
|
||||
ringtx_s=0;
|
||||
ringtx_s_min=0xFFFFFFFF;
|
||||
ringtx_s_max=0;
|
||||
}
|
||||
#endif
|
||||
// Note on volatiles: m_WritePos is not modified by the GS thread, so there's no need
|
||||
// to use volatile reads here. We do cache it though, since we know it never changes,
|
||||
// except for calls to RingbufferRestert() -- handled below.
|
||||
|
@ -666,9 +613,9 @@ int SysMtgsThread::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size
|
|||
pxAssert( size < RingBufferSize );
|
||||
pxAssert( writepos < RingBufferSize );
|
||||
|
||||
m_packet_size = GIFPath_ParseTag(pathidx, srcdata, size);
|
||||
size = m_packet_size + 1; // takes into account our command qword.
|
||||
|
||||
m_packet_size = size;
|
||||
++size; // takes into account our RingCommand QWC.
|
||||
|
||||
if( writepos + size < RingBufferSize )
|
||||
{
|
||||
// generic gs wait/stall.
|
||||
|
@ -793,13 +740,25 @@ int SysMtgsThread::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size
|
|||
// length in SIMDs (128 bits).
|
||||
|
||||
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
|
||||
tag.command = pathidx;
|
||||
tag.command = cmd;
|
||||
tag.data[0] = m_packet_size;
|
||||
m_packet_ringpos = m_WritePos + 1;
|
||||
|
||||
return m_packet_size;
|
||||
}
|
||||
|
||||
// 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 )
|
||||
{
|
||||
//m_PacketLocker.Acquire();
|
||||
|
||||
return PrepDataPacket( (MTGS_RingCommand)pathidx, GIFPath_ParseTag(pathidx, srcdata, size) );
|
||||
}
|
||||
|
||||
void SysMtgsThread::RestartRingbuffer( uint packsize )
|
||||
{
|
||||
if( m_WritePos == 0 ) return;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
// the lower 16 bit value. IF the change is breaking of all compatibility with old
|
||||
// states, increment the upper 16 bit value, and clear the lower 16 bits to 0.
|
||||
|
||||
static const u32 g_SaveVersion = 0x8b450000;
|
||||
static const u32 g_SaveVersion = 0x8b460000;
|
||||
|
||||
// this function is meant to be used in the place of GSfreeze, and provides a safe layer
|
||||
// between the GS saving function and the MTGS's needs. :)
|
||||
|
|
|
@ -354,7 +354,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
|
|||
Console.Warning(
|
||||
L"Kbd Accelerator '%s' is mapped multiple times.\n"
|
||||
L"\t'Command %s' is being replaced by '%s'",
|
||||
acode.ToString().c_str(), fromUTF8( result->Id ).c_str(), searchfor
|
||||
acode.ToString().c_str(), fromUTF8( result->Id ).c_str(), fromUTF8( searchfor ).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue