mirror of https://github.com/PCSX2/pcsx2.git
657 lines
22 KiB
C++
657 lines
22 KiB
C++
/* PCSX2 - PS2 Emulator for PCs
|
|
* Copyright (C) 2002-2010 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/>.
|
|
*/
|
|
|
|
#pragma once
|
|
#include <deque>
|
|
#include "System/SysThreads.h"
|
|
#include "Gif.h"
|
|
struct GS_Packet;
|
|
extern void Gif_MTGS_Wait(bool isMTVU);
|
|
extern void Gif_FinishIRQ();
|
|
extern bool Gif_HandlerAD(u8* pMem);
|
|
extern bool Gif_HandlerAD_Debug(u8* pMem);
|
|
extern void Gif_AddBlankGSPacket(u32 size, GIF_PATH path);
|
|
extern void Gif_AddGSPacketMTVU (GS_Packet& gsPack, GIF_PATH path);
|
|
extern void Gif_AddCompletedGSPacket(GS_Packet& gsPack, GIF_PATH path);
|
|
extern void Gif_ParsePacket(u8* data, u32 size, GIF_PATH path);
|
|
extern void Gif_ParsePacket(GS_Packet& gsPack, GIF_PATH path);
|
|
|
|
struct Gif_Tag {
|
|
struct HW_Gif_Tag {
|
|
u16 NLOOP : 15;
|
|
u16 EOP : 1;
|
|
u16 _dummy0 : 16;
|
|
u32 _dummy1 : 14;
|
|
u32 PRE : 1;
|
|
u32 PRIM : 11;
|
|
u32 FLG : 2;
|
|
u32 NREG : 4;
|
|
u32 REGS[2];
|
|
} tag;
|
|
|
|
u32 nLoop; // NLOOP left to process
|
|
u32 nRegs; // NREG (1~16)
|
|
u32 nRegIdx; // Current nReg Index (packed mode processing)
|
|
u32 len; // Packet Length in Bytes (not including tag)
|
|
u32 cycles; // Time needed to process packet data in ee-cycles
|
|
u8 regs[16]; // Regs
|
|
bool hasAD; // Has an A+D Write
|
|
bool isValid; // Tag is valid
|
|
|
|
Gif_Tag() { Reset(); }
|
|
Gif_Tag(u8* pMem, bool analyze = false) {
|
|
setTag (pMem, analyze);
|
|
}
|
|
|
|
void Reset() { memzero(*this); }
|
|
u8 curReg() { return regs[nRegIdx&0xf]; }
|
|
|
|
void packedStep() {
|
|
if (nLoop > 0) {
|
|
nRegIdx++;
|
|
if (nRegIdx >= nRegs) {
|
|
nRegIdx = 0;
|
|
nLoop--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setTag(u8* pMem, bool analyze = false) {
|
|
tag = *(HW_Gif_Tag*)pMem;
|
|
nLoop = tag.NLOOP;
|
|
hasAD = false;
|
|
nRegIdx = 0;
|
|
isValid = 1;
|
|
switch(tag.FLG) {
|
|
case GIF_FLG_PACKED:
|
|
nRegs = ((tag.NREG-1)&0xf) + 1;
|
|
len = (nRegs * tag.NLOOP)* 16;
|
|
cycles = len << 1; // Packed Mode takes 2 ee-cycles
|
|
if (analyze) analyzeTag();
|
|
break;
|
|
case GIF_FLG_REGLIST:
|
|
nRegs = ((tag.NREG-1)&0xf) + 1;
|
|
len =((nRegs * tag.NLOOP + 1) >> 1) * 16;
|
|
cycles = len << 2; // Reg-list Mode takes 4 ee-cycles
|
|
break;
|
|
case GIF_FLG_IMAGE:
|
|
case GIF_FLG_IMAGE2:
|
|
nRegs = 0;
|
|
len = tag.NLOOP * 16;
|
|
cycles = len << 2; // Image Mode takes 4 ee-cycles
|
|
tag.FLG = GIF_FLG_IMAGE;
|
|
break;
|
|
jNO_DEFAULT;
|
|
}
|
|
}
|
|
|
|
void analyzeTag() {
|
|
hasAD = false;
|
|
u32 t = tag.REGS[0];
|
|
u32 i = 0;
|
|
u32 j = std::min<u32>(nRegs, 8);
|
|
for(; i < j; i++) {
|
|
regs[i] = t & 0xf;
|
|
hasAD |= (regs[i] == GIF_REG_A_D);
|
|
t >>= 4;
|
|
}
|
|
t = tag.REGS[1];
|
|
j = nRegs;
|
|
for(; i < j; i++) {
|
|
regs[i] = t & 0xf;
|
|
hasAD |= (regs[i] == GIF_REG_A_D);
|
|
t >>= 4;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct GS_Packet {
|
|
u32 offset; // Path buffer offset for start of packet
|
|
u32 size; // Full size of GS-Packet
|
|
s32 cycles; // EE Cycles taken to process this GS packet
|
|
s32 readAmount; // Dummy read-amount data needed for proper buffer calculations
|
|
bool done; // 0 = Incomplete, 1 = Complete
|
|
GS_Packet() { Reset(); }
|
|
void Reset() { memzero(*this); }
|
|
};
|
|
|
|
struct GS_SIGNAL {
|
|
u32 data[2];
|
|
bool queued;
|
|
void Reset() { memzero(*this); }
|
|
};
|
|
|
|
static __fi void incTag(u32& offset, u32& size, u32 incAmount) {
|
|
size += incAmount;
|
|
offset += incAmount;
|
|
}
|
|
|
|
struct Gif_Path_MTVU {
|
|
u32 fakePackets; // Fake packets pending to be sent to MTGS
|
|
Mutex gsPackMutex; // Used for atomic access to gsPackQueue
|
|
std::deque<GS_Packet> gsPackQueue; // VU1 programs' XGkick(s)
|
|
Gif_Path_MTVU() { Reset(); }
|
|
void Reset() { fakePackets = 0; gsPackQueue.clear(); }
|
|
};
|
|
|
|
struct Gif_Path {
|
|
__aligned(4) volatile s32 readAmount; // Amount of data MTGS still needs to read
|
|
u8* buffer; // Path packet buffer
|
|
u32 buffSize; // Full size of buffer
|
|
u32 buffLimit; // Cut off limit to wrap around
|
|
u32 curSize; // Used buffer in bytes
|
|
u32 curOffset; // Offset of current gifTag
|
|
u32 dmaRewind; // Used by path3 when only part of a DMA chain is used
|
|
Gif_Tag gifTag; // Current GS Primitive tag
|
|
GS_Packet gsPack; // Current GS Packet info
|
|
GIF_PATH idx; // Gif Path Index
|
|
GIF_PATH_STATE state; // Path State
|
|
Gif_Path_MTVU mtvu; // Must be last for saved states
|
|
|
|
Gif_Path() {}
|
|
~Gif_Path() { _aligned_free(buffer); }
|
|
|
|
void Init(GIF_PATH _idx, u32 _buffSize, u32 _buffSafeZone) {
|
|
idx = _idx;
|
|
buffSize = _buffSize;
|
|
buffLimit = _buffSize - _buffSafeZone;
|
|
buffer = (u8*)_aligned_malloc(buffSize, 16);
|
|
Reset();
|
|
}
|
|
|
|
void Reset(bool softReset = false) {
|
|
state = GIF_PATH_IDLE;
|
|
if (softReset) {
|
|
GUNIT_WARN("Gif Path %d - Soft Reset", idx+1);
|
|
//gifTag.Reset();
|
|
//gsPack.Reset();
|
|
//curOffset = curSize;
|
|
return;
|
|
}
|
|
mtvu.Reset();
|
|
curSize = 0;
|
|
curOffset = 0;
|
|
readAmount = 0;
|
|
gifTag.Reset();
|
|
gsPack.Reset();
|
|
}
|
|
|
|
bool isMTVU() { return !idx && THREAD_VU1; }
|
|
s32 getReadAmount() { return AtomicRead(readAmount) + gsPack.readAmount; }
|
|
bool hasDataRemaining() { return curOffset < curSize; }
|
|
bool isDone() { return isMTVU() ? !mtvu.fakePackets
|
|
: (!hasDataRemaining() && (state == GIF_PATH_IDLE || state == GIF_PATH_WAIT)); }
|
|
|
|
// Waits on the MTGS to process gs packets
|
|
void mtgsReadWait() {
|
|
if (IsDevBuild) {
|
|
DevCon.WriteLn(Color_Red, "Gif Path[%d] - MTGS Wait! [r=0x%x]", idx+1, getReadAmount());
|
|
Gif_MTGS_Wait(isMTVU());
|
|
DevCon.WriteLn(Color_Green, "Gif Path[%d] - MTGS Wait! [r=0x%x]", idx+1, getReadAmount());
|
|
return;
|
|
}
|
|
Gif_MTGS_Wait(isMTVU());
|
|
}
|
|
|
|
// Moves packet data to start of buffer
|
|
void RealignPacket() {
|
|
GUNIT_LOG("Path Buffer: Realigning packet!");
|
|
s32 offset = curOffset - gsPack.size;
|
|
s32 sizeToAdd = curSize - offset;
|
|
s32 intersect = sizeToAdd - offset;
|
|
if (intersect < 0) intersect = 0;
|
|
for(;;) {
|
|
s32 frontFree = offset - getReadAmount();
|
|
if (frontFree >= sizeToAdd - intersect) break;
|
|
mtgsReadWait();
|
|
}
|
|
if (offset < (s32)buffLimit) { // Needed for correct readAmount values
|
|
if (isMTVU()) gsPack.readAmount += buffLimit - offset;
|
|
else Gif_AddBlankGSPacket(buffLimit - offset, idx);
|
|
}
|
|
//DevCon.WriteLn("Realign Packet [%d]", curSize - offset);
|
|
if (intersect) memmove(buffer, &buffer[offset], curSize - offset);
|
|
else memcpy_fast(buffer, &buffer[offset], curSize - offset);
|
|
curSize -= offset;
|
|
curOffset = gsPack.size;
|
|
gsPack.offset = 0;
|
|
}
|
|
|
|
void CopyGSPacketData(u8* pMem, u32 size, bool aligned = false) {
|
|
if (curSize + size > buffSize) { // Move gsPack to front of buffer
|
|
GUNIT_LOG("CopyGSPacketData: Realigning packet!");
|
|
RealignPacket();
|
|
}
|
|
for(;;) {
|
|
s32 offset = curOffset - gsPack.size;
|
|
s32 readPos = offset - getReadAmount();
|
|
if (readPos >= 0) break; // MTGS is reading in back of curOffset
|
|
if ((s32)buffLimit + readPos > (s32)curSize + (s32)size) break; // Enough free front space
|
|
mtgsReadWait(); // Let MTGS run to free up buffer space
|
|
}
|
|
pxAssertDev(curSize+size<=buffSize, "Gif Path Buffer Overflow!");
|
|
if (aligned) memcpy_qwc (&buffer[curSize], pMem, size/16);
|
|
else memcpy_fast(&buffer[curSize], pMem, size);
|
|
curSize += size;
|
|
}
|
|
|
|
// If completed a GS packet (with EOP) then returned GS_Packet.done = 1
|
|
// MTVU: This function only should be called called on EE thread
|
|
GS_Packet ExecuteGSPacket() {
|
|
if (mtvu.fakePackets) { // For MTVU mode...
|
|
mtvu.fakePackets--;
|
|
GS_Packet fakePack;
|
|
fakePack.done = 1; // Fake packets don't get processed by pcsx2
|
|
fakePack.size =~0u; // Used to indicate that its a fake packet
|
|
return fakePack;
|
|
}
|
|
pxAssert(!isMTVU());
|
|
for(;;) {
|
|
if (!gifTag.isValid) { // Need new Gif Tag
|
|
// We don't have enough data for a Gif Tag
|
|
if (curOffset + 16 > curSize) {
|
|
//GUNIT_LOG("Path Buffer: Not enough data for gif tag! [%d]", curSize-curOffset);
|
|
return gsPack;
|
|
}
|
|
|
|
// Move packet to start of buffer
|
|
if (curOffset > buffLimit) {
|
|
RealignPacket();
|
|
}
|
|
|
|
gifTag.setTag(&buffer[curOffset], 1);
|
|
state = (GIF_PATH_STATE)(gifTag.tag.FLG + 1);
|
|
|
|
// We don't have enough data for a complete GS packet
|
|
if(!gifTag.hasAD && curOffset + 16 + gifTag.len > curSize) {
|
|
gifTag.isValid = false; // So next time we test again
|
|
return gsPack;
|
|
}
|
|
|
|
incTag(curOffset, gsPack.size, 16); // Tag Size
|
|
gsPack.cycles += 2 + gifTag.cycles; // Tag + Len ee-cycles
|
|
}
|
|
|
|
if (gifTag.hasAD) { // Only can be true if GIF_FLG_PACKED
|
|
bool dblSIGNAL = false;
|
|
while(gifTag.nLoop && !dblSIGNAL) {
|
|
if (curOffset + 16 > curSize) return gsPack; // Exit Early
|
|
if (gifTag.curReg() == GIF_REG_A_D) {
|
|
if (!isMTVU()) dblSIGNAL = Gif_HandlerAD(&buffer[curOffset]);
|
|
}
|
|
incTag(curOffset, gsPack.size, 16); // 1 QWC
|
|
gifTag.packedStep();
|
|
}
|
|
if (dblSIGNAL && !(gifTag.tag.EOP && !gifTag.nLoop)) return gsPack; // Exit Early
|
|
}
|
|
else incTag(curOffset, gsPack.size, gifTag.len); // Data length
|
|
|
|
// Reload gif tag next loop
|
|
gifTag.isValid = false;
|
|
|
|
if (gifTag.tag.EOP) {
|
|
GS_Packet t = gsPack;
|
|
t.done = 1;
|
|
|
|
|
|
dmaRewind = 0;
|
|
|
|
gsPack.Reset();
|
|
gsPack.offset = curOffset;
|
|
|
|
//Path 3 Masking is timing sensitive, we need to simulate its length! (NFSU2/Outrun 2006)
|
|
|
|
if((gifRegs.stat.APATH-1) == GIF_PATH_3)
|
|
{
|
|
state = GIF_PATH_WAIT;
|
|
|
|
if(curSize - curOffset > 0 && (gifRegs.stat.M3R || gifRegs.stat.M3P))
|
|
{
|
|
//Including breaking packets early (Rewind DMA to pick up where left off)
|
|
//but only do this when the path is masked, else we're pointlessly slowing things down.
|
|
dmaRewind = curSize - curOffset;
|
|
curSize = curOffset;
|
|
}
|
|
}
|
|
else
|
|
state = GIF_PATH_IDLE;
|
|
|
|
return t; // Complete GS packet
|
|
}
|
|
}
|
|
}
|
|
|
|
// MTVU: Gets called on VU XGkicks on MTVU thread
|
|
void ExecuteGSPacketMTVU() {
|
|
// Move packet to start of buffer
|
|
if (curOffset > buffLimit) {
|
|
RealignPacket();
|
|
}
|
|
if (IsDevBuild) { // We check the packet to see if it actually
|
|
for(;;) { // needed to be processed by pcsx2...
|
|
if (curOffset + 16 > curSize) break;
|
|
gifTag.setTag(&buffer[curOffset], 1);
|
|
|
|
if(!gifTag.hasAD && curOffset + 16 + gifTag.len > curSize) break;
|
|
incTag(curOffset, gsPack.size, 16); // Tag Size
|
|
|
|
if (gifTag.hasAD) { // Only can be true if GIF_FLG_PACKED
|
|
while(gifTag.nLoop) {
|
|
if (curOffset + 16 > curSize) break; // Exit Early
|
|
if (gifTag.curReg() == GIF_REG_A_D) {
|
|
pxAssert(!Gif_HandlerAD_Debug(&buffer[curOffset]));
|
|
}
|
|
incTag(curOffset, gsPack.size, 16); // 1 QWC
|
|
gifTag.packedStep();
|
|
}
|
|
}
|
|
else incTag(curOffset, gsPack.size, gifTag.len); // Data length
|
|
if (curOffset >= curSize) break;
|
|
if (gifTag.tag.EOP) break;
|
|
}
|
|
pxAssert(curOffset == curSize);
|
|
gifTag.isValid = false;
|
|
}
|
|
else {
|
|
// We assume every packet is a full GS Packet
|
|
// And we don't process anything on pcsx2 side
|
|
gsPack.size += curSize - curOffset;
|
|
curOffset = curSize;
|
|
}
|
|
}
|
|
|
|
// MTVU: Gets called after VU1 execution on MTVU thread
|
|
void FinishGSPacketMTVU() {
|
|
if (1) {
|
|
ScopedLock lock(mtvu.gsPackMutex);
|
|
AtomicExchangeAdd(readAmount, gsPack.size + gsPack.readAmount);
|
|
mtvu.gsPackQueue.push_back(gsPack);
|
|
}
|
|
gsPack.Reset();
|
|
gsPack.offset = curOffset;
|
|
}
|
|
|
|
// MTVU: Gets called by MTGS thread
|
|
GS_Packet GetGSPacketMTVU() {
|
|
ScopedLock lock(mtvu.gsPackMutex);
|
|
if (mtvu.gsPackQueue.size()) {
|
|
GS_Packet t = mtvu.gsPackQueue[0];
|
|
return t; // XGkick GS packet(s)
|
|
}
|
|
Console.Error("MTVU: Expected gsPackQueue to have elements!");
|
|
pxAssert(0);
|
|
return GS_Packet(); // gsPack.size will be 0
|
|
}
|
|
|
|
// MTVU: Gets called by MTGS thread
|
|
void PopGSPacketMTVU() {
|
|
ScopedLock lock(mtvu.gsPackMutex);
|
|
if (mtvu.gsPackQueue.size()) {
|
|
mtvu.gsPackQueue.pop_front();
|
|
}
|
|
}
|
|
|
|
// MTVU: Returns the amount of pending
|
|
// GS Packets that MTGS hasn't yet processed
|
|
u32 GetPendingGSPackets() {
|
|
ScopedLock lock(mtvu.gsPackMutex);
|
|
u32 t = mtvu.gsPackQueue.size();
|
|
return t;
|
|
}
|
|
};
|
|
|
|
struct Gif_Unit {
|
|
Gif_Path gifPath[3];
|
|
GS_SIGNAL gsSIGNAL; // Stalling Signal
|
|
tGIF_STAT& stat;
|
|
GIF_TRANSFER_TYPE lastTranType; // Last Transfer Type
|
|
|
|
Gif_Unit() : stat(gifRegs.stat) {
|
|
gifPath[0].Init(GIF_PATH_1, _1mb*9, _1mb + _1kb);
|
|
gifPath[1].Init(GIF_PATH_2, _1mb*9, _1mb + _1kb);
|
|
gifPath[2].Init(GIF_PATH_3, _1mb*9, _1mb + _1kb);
|
|
}
|
|
|
|
// Enable softReset when resetting during game emulation
|
|
void Reset(bool softReset = false) {
|
|
GUNIT_WARN(Color_Red, "Gif Unit Reset!!! [soft=%d]", softReset);
|
|
ResetRegs();
|
|
gsSIGNAL.Reset();
|
|
gifPath[0].Reset(softReset);
|
|
gifPath[1].Reset(softReset);
|
|
gifPath[2].Reset(softReset);
|
|
if(!softReset) {
|
|
lastTranType = GIF_TRANS_INVALID;
|
|
}
|
|
}
|
|
|
|
// Resets Gif HW Regs
|
|
void ResetRegs() {
|
|
gifRegs.stat.reset();
|
|
gifRegs.ctrl.reset();
|
|
gifRegs.mode.reset();
|
|
}
|
|
|
|
// Adds a finished GS Packet to the MTGS ring buffer
|
|
__fi void AddCompletedGSPacket(GS_Packet& gsPack, GIF_PATH path) {
|
|
if (gsPack.size==~0u) Gif_AddGSPacketMTVU (gsPack, path);
|
|
else Gif_AddCompletedGSPacket(gsPack, path);
|
|
if (PRINT_GIF_PACKET) Gif_ParsePacket(gsPack, path);
|
|
}
|
|
|
|
// Returns GS Packet Size in bytes
|
|
u32 GetGSPacketSize(GIF_PATH pathIdx, u8* pMem, u32 offset = 0, u32 size = ~0u) {
|
|
u32 memMask = pathIdx ? ~0u : 0x3fffu;
|
|
u32 curSize = 0;
|
|
for(;;) {
|
|
Gif_Tag gifTag(&pMem[offset & memMask]);
|
|
incTag(offset, curSize, 16 + gifTag.len); // Tag + Data length
|
|
if (pathIdx == GIF_PATH_1 && curSize >= 0x4000) {
|
|
DevCon.Warning("Gif Unit - GS packet size exceeded VU memory size!");
|
|
return 0; // Bios does this... (Fixed if you delay vu1's xgkick by 103 vu cycles)
|
|
}
|
|
if (curSize >= size) return size;
|
|
if (gifTag.tag.EOP) return curSize;
|
|
}
|
|
}
|
|
|
|
// Specify the transfer type you are initiating
|
|
// The return value is the amount of data (in bytes) that was processed
|
|
// If transfer cannot take place at this moment the return value is 0
|
|
u32 TransferGSPacketData(GIF_TRANSFER_TYPE tranType, u8* pMem, u32 size, bool aligned=false) {
|
|
|
|
if (THREAD_VU1) {
|
|
Gif_Path& path1 = gifPath[GIF_PATH_1];
|
|
if (tranType == GIF_TRANS_XGKICK) { // This is on the MTVU thread
|
|
path1.CopyGSPacketData(pMem, size, aligned);
|
|
path1.ExecuteGSPacketMTVU();
|
|
return size;
|
|
}
|
|
if (tranType == GIF_TRANS_MTVU) { // This is on the EE thread
|
|
path1.mtvu.fakePackets++;
|
|
if (CanDoGif()) Execute(false, true);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
GUNIT_LOG("%s - [path=%d][size=%d]", Gif_TransferStr[(tranType>>8)&0xf], (tranType&3)+1, size);
|
|
if (size == 0) { GUNIT_WARN("Gif Unit - Size == 0"); return 0; }
|
|
if(!CanDoGif()) { GUNIT_WARN("Gif Unit - Signal or PSE Set or Dir = GS to EE"); }
|
|
//pxAssertDev((stat.APATH==0) || checkPaths(1,1,1), "Gif Unit - APATH wasn't cleared?");
|
|
lastTranType = tranType;
|
|
|
|
if (tranType == GIF_TRANS_FIFO) {
|
|
if(!CanDoPath3()) DevCon.Warning("Gif Unit - Path 3 FIFO transfer while !CanDoPath3()");
|
|
}
|
|
if (tranType == GIF_TRANS_DMA) {
|
|
if(!CanDoPath3()) { if (!Path3Masked()) stat.P3Q = 1; return 0; } // DMA Stall
|
|
//if (stat.P2Q) DevCon.WriteLn("P2Q while path 3");
|
|
}
|
|
if (tranType == GIF_TRANS_XGKICK) {
|
|
if(!CanDoPath1()) { stat.P1Q = 1; } // We always buffer path1 packets
|
|
}
|
|
if (tranType == GIF_TRANS_DIRECT) {
|
|
if(!CanDoPath2()) { stat.P2Q = 1; return 0; } // Direct Stall
|
|
}
|
|
if (tranType == GIF_TRANS_DIRECTHL) {
|
|
if(!CanDoPath2HL()) { stat.P2Q = 1; return 0; } // DirectHL Stall
|
|
}
|
|
|
|
gifPath[tranType&3].CopyGSPacketData(pMem, size, aligned);
|
|
size -= Execute(tranType == GIF_TRANS_DMA, false);
|
|
return size;
|
|
}
|
|
|
|
// Checks path activity for the given paths
|
|
// Returns an int with a bit enabled if the corresponding
|
|
// path is not finished (needs more data/processing for an EOP)
|
|
__fi int checkPaths(bool p1, bool p2, bool p3, bool checkQ=false) {
|
|
int ret = 0;
|
|
ret |= (p1 && !gifPath[GIF_PATH_1].isDone()) << 0;
|
|
ret |= (p2 && !gifPath[GIF_PATH_2].isDone()) << 1;
|
|
ret |= (p3 && !gifPath[GIF_PATH_3].isDone()) << 2;
|
|
return ret | (checkQ ? checkQueued(p1,p2,p3) : 0);
|
|
}
|
|
|
|
__fi int checkQueued(bool p1, bool p2, bool p3) {
|
|
int ret = 0;
|
|
ret |= (p1 && stat.P1Q) << 0;
|
|
ret |= (p2 && stat.P2Q) << 1;
|
|
ret |= (p3 && stat.P3Q) << 2;
|
|
return ret;
|
|
}
|
|
|
|
// Send processed GS Primitive(s) to the MTGS thread
|
|
// Note: Only does so if current path fully completed all
|
|
// of its given gs primitives (but didn't upload them yet)
|
|
void FlushToMTGS() {
|
|
if (!stat.APATH) return;
|
|
Gif_Path& path = gifPath[stat.APATH-1];
|
|
if (path.gsPack.size && !path.gifTag.isValid) {
|
|
AddCompletedGSPacket(path.gsPack, (GIF_PATH)(stat.APATH-1));
|
|
path.gsPack.offset = path.curOffset;
|
|
path.gsPack.size = 0;
|
|
}
|
|
}
|
|
|
|
// Processes gif packets and performs path arbitration
|
|
// on EOPs or on Path 3 Images when IMT is set.
|
|
int Execute(bool isPath3, bool isResume) {
|
|
if (!CanDoGif()) { DevCon.Error("Gif Unit - Signal or PSE Set or Dir = GS to EE"); return 0; }
|
|
bool didPath3 = false;
|
|
int curPath = stat.APATH > 0 ? stat.APATH-1 : 0; //Init to zero if no path is already set.
|
|
gifPath[2].dmaRewind = 0;
|
|
stat.OPH = 1;
|
|
|
|
for(;;) {
|
|
if (stat.APATH) { // Some Transfer is happening
|
|
Gif_Path& path = gifPath[stat.APATH-1];
|
|
GS_Packet gsPack = path.ExecuteGSPacket();
|
|
if(!gsPack.done) {
|
|
if (stat.APATH == 3 && CanDoP3Slice() && !gsSIGNAL.queued) {
|
|
if(!didPath3 && /*!Path3Masked() &&*/ checkPaths(1,1,0)) { // Path3 slicing
|
|
didPath3 = true;
|
|
stat.APATH = 0;
|
|
stat.IP3 = 1;
|
|
GUNIT_LOG(Color_Magenta, "Gif Unit - Path 3 slicing arbitration");
|
|
if (gsPack.size > 16) { // Packet had other tags which we already processed
|
|
u32 subOffset = path.gifTag.isValid ? 16 : 0; // if isValid, image-primitive not finished
|
|
gsPack.size -= subOffset; // Remove the image-tag (should be last thing read)
|
|
AddCompletedGSPacket(gsPack, GIF_PATH_3); // Consider current packet complete
|
|
path.gsPack.Reset(); // Reset gs packet info
|
|
path.curOffset -= subOffset; // Start the next GS packet at the image-tag
|
|
path.gsPack.offset = path.curOffset; // Set to image-tag
|
|
path.gifTag.isValid = false; // Reload tag next ExecuteGSPacket()
|
|
pxAssert((s32)path.curOffset >= 0);
|
|
pxAssert(path.state == GIF_PATH_IMAGE);
|
|
GUNIT_LOG(Color_Magenta, "Gif Unit - Sending path 3 sliced gs packet!");
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
//FlushToMTGS();
|
|
//DevCon.WriteLn("Incomplete GS Packet for path %d, size=%d", stat.APATH, gsPack.size);
|
|
break; // Not finished with GS packet
|
|
}
|
|
//DevCon.WriteLn("Adding GS Packet for path %d", stat.APATH);
|
|
AddCompletedGSPacket(gsPack, (GIF_PATH)(stat.APATH-1));
|
|
}
|
|
if (!gsSIGNAL.queued && !gifPath[0].isDone()) { stat.APATH = 1; stat.P1Q = 0; curPath = 0; }
|
|
elif (!gsSIGNAL.queued && !gifPath[1].isDone()) { stat.APATH = 2; stat.P2Q = 0; curPath = 1; }
|
|
elif (!gsSIGNAL.queued && !gifPath[2].isDone() && !Path3Masked())
|
|
{ stat.APATH = 3; stat.P3Q = 0; stat.IP3 = 0; curPath = 2; }
|
|
else { if(isResume || curPath == 0) { stat.APATH = 0; stat.OPH = 0; } break; }
|
|
}
|
|
|
|
//Some loaders/Refresh Rate selectors and things dont issue "End of Packet" commands
|
|
//So we look and see if the end of the last tag is all there, if so, stick it in the buffer for the GS :)
|
|
//(Invisible Screens on Terminator 3 and Growlanser 2/3)
|
|
if(gifPath[curPath].curOffset == gifPath[curPath].curSize)
|
|
{
|
|
FlushToMTGS();
|
|
}
|
|
|
|
Gif_FinishIRQ();
|
|
|
|
//Path3 can rewind the DMA, so we send back the amount we go back!
|
|
if(isPath3)
|
|
return gifPath[2].dmaRewind;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// XGkick
|
|
bool CanDoPath1() { return (stat.APATH == 0 || stat.APATH == 1
|
|
|| (stat.APATH == 3 && CanDoP3Slice()))
|
|
&& (CanDoGif() == 1); }
|
|
// Direct
|
|
bool CanDoPath2() { return (stat.APATH == 0 || stat.APATH == 2
|
|
|| (stat.APATH == 3 && CanDoP3Slice()))
|
|
&& (CanDoGif() == 1); }
|
|
// DirectHL
|
|
bool CanDoPath2HL() { return (stat.APATH == 0 || stat.APATH == 2)
|
|
&& (CanDoGif() == 1); }
|
|
// Gif DMA - CHECK_GIFREVERSEHACK is a hack for Hot Wheels.
|
|
bool CanDoPath3() { return((stat.APATH == 0 && !Path3Masked())
|
|
|| stat.APATH == 3)
|
|
&& (CanDoGif() == 1); }
|
|
|
|
bool CanDoP3Slice() { return stat.IMT == 1 && gifPath[GIF_PATH_3].state == GIF_PATH_IMAGE; }
|
|
bool CanDoGif() { return stat.PSE == 0 && (CHECK_GIFREVERSEHACK ? 1 : stat.DIR == 0) && gsSIGNAL.queued == 0; }
|
|
//Mask stops the next packet which hasnt started from transferring
|
|
bool Path3Masked() { return ((stat.M3R || stat.M3P) && (gifPath[GIF_PATH_3].state == GIF_PATH_IDLE || gifPath[GIF_PATH_3].state == GIF_PATH_WAIT)); }
|
|
|
|
void PrintInfo(bool printP1=1, bool printP2=1, bool printP3=1) {
|
|
u32 a = checkPaths(1,1,1), b = checkQueued(1,1,1);
|
|
GUNIT_LOG("Gif Unit - LastTransfer = %s, Paths = [%d,%d,%d], Queued = [%d,%d,%d]",
|
|
Gif_TransferStr[(lastTranType>>8)&0xf],
|
|
!!(a&1),!!(a&2),!!(a&4),!!(b&1),!!(b&2),!!(b&4));
|
|
GUNIT_LOG("Gif Unit - [APATH = %d][Signal = %d][PSE = %d][DIR = %d]",
|
|
stat.APATH, gsSIGNAL.queued, stat.PSE, stat.DIR);
|
|
GUNIT_LOG("Gif Unit - [CanDoGif = %d][CanDoPath3 = %d][CanDoP3Slice = %d]",
|
|
CanDoGif(), CanDoPath3(), CanDoP3Slice());
|
|
if (printP1) PrintPathInfo(GIF_PATH_1);
|
|
if (printP2) PrintPathInfo(GIF_PATH_2);
|
|
if (printP3) PrintPathInfo(GIF_PATH_3);
|
|
}
|
|
|
|
void PrintPathInfo(GIF_PATH path) {
|
|
GUNIT_LOG("Gif Path %d - [hasData = %d][state = %d]", path,
|
|
gifPath[path].hasDataRemaining(), gifPath[path].state);
|
|
}
|
|
};
|
|
|
|
extern Gif_Unit gifUnit;
|