mirror of https://github.com/PCSX2/pcsx2.git
403 lines
11 KiB
C++
403 lines
11 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2009 PCSX2 Dev Team
|
|
*
|
|
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU Lesser General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with PCSX2.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
#include "Common.h"
|
|
#include "GS.h"
|
|
#include "Gif.h"
|
|
#include "VifDma.h"
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// GIFpath -- the GIFtag Parser
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
struct GSRegSIGBLID
|
|
{
|
|
u32 SIGID;
|
|
u32 LBLID;
|
|
};
|
|
|
|
enum GIF_FLG
|
|
{
|
|
GIF_FLG_PACKED = 0,
|
|
GIF_FLG_REGLIST = 1,
|
|
GIF_FLG_IMAGE = 2,
|
|
GIF_FLG_IMAGE2 = 3
|
|
};
|
|
|
|
enum GIF_REG
|
|
{
|
|
GIF_REG_PRIM = 0x00,
|
|
GIF_REG_RGBA = 0x01,
|
|
GIF_REG_STQ = 0x02,
|
|
GIF_REG_UV = 0x03,
|
|
GIF_REG_XYZF2 = 0x04,
|
|
GIF_REG_XYZ2 = 0x05,
|
|
GIF_REG_TEX0_1 = 0x06,
|
|
GIF_REG_TEX0_2 = 0x07,
|
|
GIF_REG_CLAMP_1 = 0x08,
|
|
GIF_REG_CLAMP_2 = 0x09,
|
|
GIF_REG_FOG = 0x0a,
|
|
GIF_REG_XYZF3 = 0x0c,
|
|
GIF_REG_XYZ3 = 0x0d,
|
|
GIF_REG_A_D = 0x0e,
|
|
GIF_REG_NOP = 0x0f,
|
|
};
|
|
|
|
// GIFTAG
|
|
// Members of this structure are in CAPS to help visually denote that they are representative
|
|
// of actual hw register states of the GIF, unlike the internal tracking vars in GIFPath, which
|
|
// are modified during the GIFtag unpacking process.
|
|
struct GIFTAG
|
|
{
|
|
u32 NLOOP : 15;
|
|
u32 EOP : 1;
|
|
u32 dummy0 : 16;
|
|
u32 dummy1 : 14;
|
|
u32 PRE : 1;
|
|
u32 PRIM : 11;
|
|
u32 FLG : 2;
|
|
u32 NREG : 4;
|
|
u32 REGS[2];
|
|
|
|
GIFTAG() {}
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// GIFPath -- PS2 GIFtag info (one for each path).
|
|
// --------------------------------------------------------------------------------------
|
|
// fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
|
|
// separate paths. But for that to work properly we need also interlocked path sources.
|
|
// That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
|
|
// this is not emulated!
|
|
|
|
struct GIFPath
|
|
{
|
|
const GIFTAG tag; // The "original tag -- modification allowed only by SetTag(), so let's make it const.
|
|
u8 regs[16]; // positioned after tag ensures 16-bit aligned (in case we SSE optimize later)
|
|
|
|
u32 nloop; // local copy nloop counts toward zero, and leaves the tag copy unmodified.
|
|
u32 curreg; // reg we left of on (for traversing through loops)
|
|
u32 numregs; // number of regs (when NREG is 0, numregs is 16)
|
|
|
|
GIFPath();
|
|
|
|
void PrepPackedRegs();
|
|
void SetTag(const void* mem);
|
|
bool StepReg();
|
|
u8 GetReg();
|
|
|
|
int ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size);
|
|
};
|
|
|
|
typedef void (__fastcall *GIFRegHandler)(const u32* data);
|
|
|
|
struct GifPathStruct
|
|
{
|
|
const GIFRegHandler Handlers[0x100-0x60]; // handlers for 0x60->0x100
|
|
GIFPath path[3];
|
|
|
|
__forceinline GIFPath& operator[]( int idx ) { return path[idx]; }
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// SIGNAL / FINISH / LABEL (WIP!!)
|
|
// --------------------------------------------------------------------------------------
|
|
// The current implementation for these is very incomplete, especially SIGNAL, which needs
|
|
// an extra VM-state status var to be handled correctly.
|
|
//
|
|
|
|
// 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
|
|
// direct GS and GSreg accesses, as well as those coming through the GIFpath -- but that
|
|
// behavior isn't confirmed yet). Privlidged writes are still active.
|
|
//
|
|
// Ignorance continues until the SIGNAL bit in CSR is manually cleared by the EE. And here's
|
|
// the tricky part: the interrupt from the second SIGNAL is still pending, and should be
|
|
// raised once the EE has reset the *IMR* mask for SIGNAL -- meaning setting the bit to 1
|
|
// (disabled/masked) and then back to 0 (enabled/unmasked).
|
|
//
|
|
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);
|
|
|
|
GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]);
|
|
|
|
if ((CSRw & 0x1))
|
|
{
|
|
if (!(GSIMR&0x100) )
|
|
{
|
|
gsIrq();
|
|
}
|
|
|
|
GSCSRr |= 1; // signal
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
// As far as I can figure, this feature is meant for EE/GS synchronization when the EE
|
|
// 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
|
|
// PATH3 of the GIF, which means only signaling FINISH if nloop==0.
|
|
//
|
|
static void __fastcall RegHandlerFINISH(const u32* data)
|
|
{
|
|
GIF_LOG("GIFpath FINISH data %x_%x CSRw %x\n", data[0], data[1], CSRw);
|
|
|
|
if ((CSRw & 0x2))
|
|
{
|
|
if (!(GSIMR&0x200))
|
|
gsIrq();
|
|
|
|
GSCSRr |= 2; // finish
|
|
}
|
|
}
|
|
|
|
static void __fastcall RegHandlerLABEL(const u32* data)
|
|
{
|
|
GIF_LOG( "GIFpath LABEL" );
|
|
GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID&~data[1])|(data[0]&data[1]);
|
|
}
|
|
|
|
static void __fastcall RegHandlerUNMAPPED(const u32* data)
|
|
{
|
|
const int regidx = ((u8*)data)[8];
|
|
|
|
// Known "unknowns":
|
|
// the bios likes to write to 0x7f using an EOP giftag with NLOOP set to 4.
|
|
// Not sure what it's trying to accomplish exactly. Ignoring seems to work fine.
|
|
|
|
if( regidx != 0x7f )
|
|
Console.Notice( "Ignoring Unmapped GIFtag Register, Index = %02x", regidx );
|
|
}
|
|
|
|
#define INSERT_UNMAPPED_4 RegHandlerUNMAPPED, RegHandlerUNMAPPED, RegHandlerUNMAPPED, RegHandlerUNMAPPED,
|
|
#define INSERT_UNMAPPED_16 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4
|
|
#define INSERT_UNMAPPED_64 INSERT_UNMAPPED_16 INSERT_UNMAPPED_16 INSERT_UNMAPPED_16 INSERT_UNMAPPED_16
|
|
|
|
static __aligned16 GifPathStruct s_gifPath =
|
|
{
|
|
RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL, RegHandlerUNMAPPED,
|
|
|
|
// Rest are mapped to Unmapped
|
|
INSERT_UNMAPPED_4 INSERT_UNMAPPED_4 INSERT_UNMAPPED_4
|
|
INSERT_UNMAPPED_64 INSERT_UNMAPPED_64 INSERT_UNMAPPED_16
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// GIFPath Method Implementations
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
GIFPath::GIFPath() : tag()
|
|
{
|
|
memzero( *this );
|
|
}
|
|
|
|
__forceinline bool GIFPath::StepReg()
|
|
{
|
|
if ((++curreg & 0xf) == tag.NREG) {
|
|
curreg = 0;
|
|
if (--nloop == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
__forceinline u8 GIFPath::GetReg() { return regs[curreg]; }
|
|
|
|
// unpack the registers - registers are stored as a sequence of 4 bit values in the
|
|
// upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
|
|
// coming in from paths 2 and 3, so we unpack them into an 8 bit array here.
|
|
//
|
|
__forceinline void GIFPath::PrepPackedRegs()
|
|
{
|
|
// Only unpack registers if we're starting a new pack. Otherwise the unpacked
|
|
// array should have already been initialized by a previous partial transfer.
|
|
|
|
if (curreg != 0) return;
|
|
|
|
u32 tempreg = tag.REGS[0];
|
|
numregs = ((tag.NREG-1)&0xf) + 1;
|
|
|
|
for (u32 i = 0; i < numregs; i++) {
|
|
if (i == 8) tempreg = tag.REGS[1];
|
|
regs[i] = tempreg & 0xf;
|
|
tempreg >>= 4;
|
|
}
|
|
}
|
|
|
|
__forceinline void GIFPath::SetTag(const void* mem)
|
|
{
|
|
const_cast<GIFTAG&>(tag) = *((GIFTAG*)mem);
|
|
|
|
nloop = tag.NLOOP;
|
|
curreg = 0;
|
|
}
|
|
|
|
void SaveStateBase::gifPathFreeze()
|
|
{
|
|
FreezeTag( "GIFpath" );
|
|
Freeze( s_gifPath.path );
|
|
}
|
|
|
|
|
|
static __forceinline void gsHandler(const u8* pMem)
|
|
{
|
|
const int handler = pMem[8];
|
|
if (handler >= 0x60)
|
|
{
|
|
// Question: What happens if an app writes to uncharted register space on real PS2
|
|
// hardware (handler 0x63 and higher)? Probably a silent ignorance, but not tested
|
|
// so just guessing... --air
|
|
|
|
s_gifPath.Handlers[handler-0x60]((const u32*)pMem);
|
|
}
|
|
}
|
|
|
|
#define incTag(x, y) do { \
|
|
pMem += (x); \
|
|
size -= (y); \
|
|
if (pMem>=vuMemEnd) pMem -= 0x4000; \
|
|
} while(false)
|
|
|
|
#define aMin(x, y) ((x < y) ? (x) : (y))
|
|
#define subVal(x, y) ((x > y) ? (x-y) : 0 )
|
|
|
|
// Parameters:
|
|
// size (path1) - difference between the end of VU memory and pMem.
|
|
// size (path2/3) - max size of incoming data stream, in qwc (simd128)
|
|
__forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
|
{
|
|
const u8* vuMemEnd = pMem + (size<<4); // End of VU1 Mem
|
|
if (pathidx==GIF_PATH_1) size = 0x400; // VU1 mem size
|
|
const u32 startSize = size; // Start Size
|
|
|
|
while (size > 0) {
|
|
if (!nloop) {
|
|
|
|
SetTag(pMem);
|
|
incTag(16, 1);
|
|
|
|
if (pathidx == GIF_PATH_3) {
|
|
if (tag.FLG&2) Path3progress = IMAGE_MODE;
|
|
else Path3progress = TRANSFER_MODE;
|
|
}
|
|
}
|
|
else {
|
|
switch(tag.FLG) {
|
|
case GIF_FLG_PACKED:
|
|
PrepPackedRegs();
|
|
do {
|
|
if (GetReg() == 0xe) {
|
|
gsHandler(pMem);
|
|
}
|
|
incTag(16, 1);
|
|
} while(StepReg() && size > 0);
|
|
break;
|
|
case GIF_FLG_REGLIST:
|
|
{
|
|
size *= 2;
|
|
|
|
do { incTag(8, 1); }
|
|
while(StepReg() && size > 0);
|
|
|
|
if (size & 1) { incTag(8, 1); }
|
|
size /= 2;
|
|
}
|
|
break;
|
|
case GIF_FLG_IMAGE:
|
|
case GIF_FLG_IMAGE2:
|
|
{
|
|
int len = aMin(size, nloop);
|
|
incTag((len * 16), len);
|
|
nloop -= len;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tag.EOP && !nloop) {
|
|
if (pathidx != GIF_PATH_2) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
size = (startSize - size);
|
|
|
|
if (pathidx == GIF_PATH_3) {
|
|
if (tag.EOP && !nloop) {
|
|
Path3progress = STOPPED_MODE;
|
|
}
|
|
gif->madr += size * 16;
|
|
gif->qwc -= size;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
|
|
// Used to keep interrupts in sync with the EE, while the GS itself
|
|
// runs potentially several frames behind.
|
|
// Parameters:
|
|
// size - max size of incoming data stream, in qwc (simd128)
|
|
__forceinline int GIFPath_ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
|
{
|
|
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
|
static uptr profStartPtr = 0;
|
|
static uptr profEndPtr = 0;
|
|
if (profStartPtr == 0) {
|
|
__asm
|
|
{
|
|
__beginfunc:
|
|
mov profStartPtr, offset __beginfunc;
|
|
mov profEndPtr, offset __endfunc;
|
|
}
|
|
ProfilerRegisterSource( "GSRingBufCopy", (void*)profStartPtr, profEndPtr - profStartPtr );
|
|
}
|
|
#endif
|
|
|
|
int retSize = s_gifPath[pathidx].ParseTag(pathidx, pMem, size);
|
|
|
|
#ifdef PCSX2_GSRING_SAMPLING_STATS
|
|
__asm
|
|
{
|
|
__endfunc:
|
|
nop;
|
|
}
|
|
#endif
|
|
return retSize;
|
|
}
|
|
|
|
// Clears all GIFpath data to zero.
|
|
void GIFPath_Reset()
|
|
{
|
|
memzero( s_gifPath.path );
|
|
}
|
|
|
|
// This is a hackfix tool provided for "canceling" the contents of the GIFpath when
|
|
// invalid GIFdma states are encountered (tpyically needed for PATH3 only).
|
|
__forceinline void GIFPath_Clear( GIF_PATH pathidx )
|
|
{
|
|
memzero(s_gifPath.path[pathidx]);
|
|
if( GSgifSoftReset == NULL ) return;
|
|
mtgsThread.SendSimplePacket( GS_RINGTYPE_SOFTRESET, (1<<pathidx), 0, 0 );
|
|
}
|