mirror of https://github.com/PCSX2/pcsx2.git
Pleanty O' Changes:
- IPU 1 DMA rewritten - now much more readable, less hacky. A couple of games are still slightly picky, but overall compatability is similar (if not better in some cases) - Reverse VIF transfers figured out and implemented (mostly)! - There are still some problems with this, will work on it. - Improved GS PATH transfer control, now PATH2 and 3 cant transfer when PATH1 is, PATH3 cant when PATH2 is and so on. - IPU now waits for ended GS transfers, for some reason this was screwing videos (mainly from PATH2) and causing them to be torn, which the original voodoo cycles addition fixed. Note: You may see a lot of "Double IRQ on DMA 4" messages. At the moment these are unavoidable but aren't actually causing any issues. I do know about them :P Feel free to negative is I've seriously broke something lol git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2822 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
8d57ed3eeb
commit
774411e3a4
|
@ -426,7 +426,6 @@ struct Pcsx2Config
|
||||||
BITFIELD32()
|
BITFIELD32()
|
||||||
bool
|
bool
|
||||||
VuAddSubHack :1, // Tri-ace games, they use an encryption algorithm that requires VU ADDI opcode to be bit-accurate.
|
VuAddSubHack :1, // Tri-ace games, they use an encryption algorithm that requires VU ADDI opcode to be bit-accurate.
|
||||||
IpuSliceHack :1, // Legacy IPU1 DMA code, breaks several games but needed for Tri-Ace ones.
|
|
||||||
VuClipFlagHack :1, // Persona games, maybe others. It's to do with the VU clip flag (again).
|
VuClipFlagHack :1, // Persona games, maybe others. It's to do with the VU clip flag (again).
|
||||||
FpuCompareHack :1, // Digimon Rumble Arena 2, fixes spinning/hanging on intro-menu.
|
FpuCompareHack :1, // Digimon Rumble Arena 2, fixes spinning/hanging on intro-menu.
|
||||||
FpuMulHack :1, // Tales of Destiny hangs.
|
FpuMulHack :1, // Tales of Destiny hangs.
|
||||||
|
@ -556,10 +555,9 @@ TraceLogFilters& SetTraceConfig();
|
||||||
#define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().IsRecAvailable_IOP())
|
#define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().IsRecAvailable_IOP())
|
||||||
|
|
||||||
//------------ SPECIAL GAME FIXES!!! ---------------
|
//------------ SPECIAL GAME FIXES!!! ---------------
|
||||||
#define NUM_OF_GAME_FIXES 7
|
#define NUM_OF_GAME_FIXES 6
|
||||||
|
|
||||||
#define CHECK_VUADDSUBHACK (EmuConfig.Gamefixes.VuAddSubHack) // Special Fix for Tri-ace games, they use an encryption algorithm that requires VU addi opcode to be bit-accurate.
|
#define CHECK_VUADDSUBHACK (EmuConfig.Gamefixes.VuAddSubHack) // Special Fix for Tri-ace games, they use an encryption algorithm that requires VU addi opcode to be bit-accurate.
|
||||||
#define CHECK_IPUSLICEHACK (EmuConfig.Gamefixes.IpuSliceHack) // Enables legacy code for IPU1 DMA involving "g_nDMATransfer.ACTV1".
|
|
||||||
#define CHECK_FPUCOMPAREHACK (EmuConfig.Gamefixes.FpuCompareHack) // Special Fix for Digimon Rumble Arena 2, fixes spinning/hanging on intro-menu.
|
#define CHECK_FPUCOMPAREHACK (EmuConfig.Gamefixes.FpuCompareHack) // Special Fix for Digimon Rumble Arena 2, fixes spinning/hanging on intro-menu.
|
||||||
#define CHECK_VUCLIPFLAGHACK (EmuConfig.Gamefixes.VuClipFlagHack) // Special Fix for Persona games, maybe others. It's to do with the VU clip flag (again).
|
#define CHECK_VUCLIPFLAGHACK (EmuConfig.Gamefixes.VuClipFlagHack) // Special Fix for Persona games, maybe others. It's to do with the VU clip flag (again).
|
||||||
#define CHECK_FPUMULHACK (EmuConfig.Gamefixes.FpuMulHack) // Special Fix for Tales of Destiny hangs.
|
#define CHECK_FPUMULHACK (EmuConfig.Gamefixes.FpuMulHack) // Special Fix for Tales of Destiny hangs.
|
||||||
|
|
|
@ -60,10 +60,15 @@ void __fastcall ReadFIFO_page_5(u32 mem, u64 *out)
|
||||||
if (vif1Regs->stat.test(VIF1_STAT_INT | VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS) )
|
if (vif1Regs->stat.test(VIF1_STAT_INT | VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS) )
|
||||||
DevCon.Warning( "Reading from vif1 fifo when stalled" );
|
DevCon.Warning( "Reading from vif1 fifo when stalled" );
|
||||||
|
|
||||||
|
if(vif1Regs->stat.FQC == 0) Console.Warning("FQC = 0 on VIF FIFO READ!");
|
||||||
if (vif1Regs->stat.FDR)
|
if (vif1Regs->stat.FDR)
|
||||||
{
|
{
|
||||||
if (--psHu32(D1_QWC) == 0)
|
if (vif1Regs->stat.FQC > 0)
|
||||||
vif1Regs->stat.FQC = 0;
|
{
|
||||||
|
GetMTGS().WaitGS();
|
||||||
|
GSreadFIFO(&psHu64(VIF1_FIFO));
|
||||||
|
}
|
||||||
|
if(vif1Regs->stat.FQC > 0)--vif1Regs->stat.FQC;
|
||||||
}
|
}
|
||||||
|
|
||||||
out[0] = psHu64(VIF1_FIFO);
|
out[0] = psHu64(VIF1_FIFO);
|
||||||
|
|
|
@ -56,22 +56,7 @@ __forceinline void gsInterrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (GSTransferStatus.PTH3 == STOPPED_MODE)
|
||||||
if ((vif1.cmd & 0x7e) == 0x50) // DIRECT/HL
|
|
||||||
{
|
|
||||||
|
|
||||||
//original behaviour here - if (Path3progress != IMAGE_MODE) vif1Regs->stat.VGW = false;
|
|
||||||
|
|
||||||
// Transfer in progress on VIF and GIF has finished so let VIF do its bit
|
|
||||||
if (Path3progress == STOPPED_MODE)
|
|
||||||
{
|
|
||||||
vif1Regs->stat.VGW = false;
|
|
||||||
CPU_INT( DMAC_GIF, 4 );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Path3progress == STOPPED_MODE)
|
|
||||||
{
|
{
|
||||||
gifRegs->stat.clear_flags(GIF_STAT_APATH3 | GIF_STAT_OPH);
|
gifRegs->stat.clear_flags(GIF_STAT_APATH3 | GIF_STAT_OPH);
|
||||||
}
|
}
|
||||||
|
@ -219,13 +204,16 @@ void GIFdma()
|
||||||
gifRegs->stat.FQC |= 0x10;// FQC=31, hack ;) (for values of 31 that equal 16) [ used to be 0xE00; // OPH=1 | APATH=3]
|
gifRegs->stat.FQC |= 0x10;// FQC=31, hack ;) (for values of 31 that equal 16) [ used to be 0xE00; // OPH=1 | APATH=3]
|
||||||
|
|
||||||
//Path2 gets priority in intermittent mode
|
//Path2 gets priority in intermittent mode
|
||||||
if ((gifRegs->stat.P1Q || (vif1.cmd & 0x7e) == 0x50) && gifRegs->mode.IMT && (Path3progress == STOPPED_MODE))
|
if (GSTransferStatus.PTH1 != STOPPED_MODE || GSTransferStatus.PTH2 != STOPPED_MODE)
|
||||||
{
|
{
|
||||||
// We are in image mode doing DIRECTHL, Path 1 is in queue, and in intermittant mode.
|
// We are in image mode doing DIRECTHL, Path 1 is in queue, and in intermittant mode.
|
||||||
GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", gifRegs->stat.P1Q, (vif1.cmd & 0x7f), gifRegs->mode._u32, Path3progress);
|
GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", gifRegs->stat.P1Q, (vif1.cmd & 0x7f), gifRegs->mode._u32, GSTransferStatus.PTH3);
|
||||||
|
if(GSTransferStatus.PTH3 == STOPPED_MODE)
|
||||||
|
{
|
||||||
CPU_INT(DMAC_GIF, 16);
|
CPU_INT(DMAC_GIF, 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (vif1Regs->mskpath3 || gifRegs->mode.M3R)
|
if (vif1Regs->mskpath3 || gifRegs->mode.M3R)
|
||||||
{
|
{
|
||||||
|
@ -242,8 +230,9 @@ void GIFdma()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Path3progress == STOPPED_MODE) /*|| (vif1Regs->stat._u32 |= VIF1_STAT_VGW) == 0*/
|
if (GSTransferStatus.PTH3 == STOPPED_MODE) /*|| (vif1Regs->stat._u32 |= VIF1_STAT_VGW) == 0*/
|
||||||
{
|
{
|
||||||
|
GIF_LOG("PTH3 MASK Continuing VIF");
|
||||||
vif1Regs->stat.VGW = false;
|
vif1Regs->stat.VGW = false;
|
||||||
if (gif->qwc == 0) CPU_INT(DMAC_GIF, 16);
|
if (gif->qwc == 0) CPU_INT(DMAC_GIF, 16);
|
||||||
return;
|
return;
|
||||||
|
@ -251,6 +240,7 @@ void GIFdma()
|
||||||
|
|
||||||
//Check with Path3 masking games
|
//Check with Path3 masking games
|
||||||
if (gif->qwc > 0) {
|
if (gif->qwc > 0) {
|
||||||
|
GIF_LOG("PTH3 MASK Transferring", ptag[1]._u32, ptag[0]._u32, gif->qwc, ptag->ID, gif->madr);
|
||||||
GIFchain();
|
GIFchain();
|
||||||
CPU_INT(DMAC_GIF, gscycles * BIAS);
|
CPU_INT(DMAC_GIF, gscycles * BIAS);
|
||||||
return;
|
return;
|
||||||
|
@ -333,7 +323,7 @@ void dmaGIF()
|
||||||
//It takes the time of 24 QW for the BUS to become ready - The Punisher And Streetball
|
//It takes the time of 24 QW for the BUS to become ready - The Punisher And Streetball
|
||||||
GIF_LOG("dmaGIFstart chcr = %lx, madr = %lx, qwc = %lx\n tadr = %lx, asr0 = %lx, asr1 = %lx", gif->chcr._u32, gif->madr, gif->qwc, gif->tadr, gif->asr0, gif->asr1);
|
GIF_LOG("dmaGIFstart chcr = %lx, madr = %lx, qwc = %lx\n tadr = %lx, asr0 = %lx, asr1 = %lx", gif->chcr._u32, gif->madr, gif->qwc, gif->tadr, gif->asr0, gif->asr1);
|
||||||
|
|
||||||
Path3progress = STOPPED_MODE;
|
GSTransferStatus.PTH3 = STOPPED_MODE;
|
||||||
gspath3done = false; // For some reason this doesn't clear? So when the system starts the thread, we will clear it :)
|
gspath3done = false; // For some reason this doesn't clear? So when the system starts the thread, we will clear it :)
|
||||||
|
|
||||||
gifRegs->stat.P3Q = true;
|
gifRegs->stat.P3Q = true;
|
||||||
|
@ -546,7 +536,7 @@ void gifMFIFOInterrupt()
|
||||||
//Console.WriteLn("gifMFIFOInterrupt");
|
//Console.WriteLn("gifMFIFOInterrupt");
|
||||||
mfifocycles = 0;
|
mfifocycles = 0;
|
||||||
|
|
||||||
if (Path3progress == STOPPED_MODE)
|
if (GSTransferStatus.PTH3 == STOPPED_MODE)
|
||||||
{
|
{
|
||||||
gifRegs->stat.APATH = GIF_APATH_IDLE;
|
gifRegs->stat.APATH = GIF_APATH_IDLE;
|
||||||
gifRegs->stat.OPH = false;
|
gifRegs->stat.OPH = false;
|
||||||
|
@ -565,9 +555,9 @@ void gifMFIFOInterrupt()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((gifRegs->stat.P1Q || (vif1.cmd & 0x7f) == 0x50) && gifRegs->mode.IMT && Path3progress == IMAGE_MODE) //Path2 gets priority in intermittent mode
|
if ((gifRegs->stat.P1Q || (vif1.cmd & 0x7f) == 0x50) && gifRegs->mode.IMT && GSTransferStatus.PTH3 == IMAGE_MODE) //Path2 gets priority in intermittent mode
|
||||||
{
|
{
|
||||||
//GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", psHu32(GIF_STAT) & 0x100, (vif1.cmd & 0x7f), psHu32(GIF_MODE), Path3progress);
|
//GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", psHu32(GIF_STAT) & 0x100, (vif1.cmd & 0x7f), psHu32(GIF_MODE), GSTransferStatus.PTH3);
|
||||||
CPU_INT(11,mfifocycles);
|
CPU_INT(11,mfifocycles);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
20
pcsx2/Gif.h
20
pcsx2/Gif.h
|
@ -26,13 +26,29 @@ enum gifstate_t
|
||||||
GIF_STATE_EMPTY = 0x10
|
GIF_STATE_EMPTY = 0x10
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Path3Modes //0 = Image Mode (DirectHL), 1 = transferring, 2 = Stopped at End of Packet
|
enum GSTransferModes //0 = Image Mode (DirectHL), 1 = transferring, 2 = Stopped at End of Packet
|
||||||
{
|
{
|
||||||
IMAGE_MODE = 0,
|
IMAGE_MODE = 0,
|
||||||
TRANSFER_MODE = 1,
|
TRANSFER_MODE = 1,
|
||||||
STOPPED_MODE = 2
|
STOPPED_MODE = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union tGSTransferStatus {
|
||||||
|
struct {
|
||||||
|
u32 PTH1 : 2; // Resets Vif(0/1) when written.
|
||||||
|
u32 PTH2 : 2; // Causes a Forcebreak to Vif((0/1) when true. (Stall)
|
||||||
|
u32 PTH3 : 2; // Stops after the end of the Vifcode in progress when true. (Stall)
|
||||||
|
u32 reserved : 26;
|
||||||
|
};
|
||||||
|
u32 _u32;
|
||||||
|
|
||||||
|
tGSTransferStatus(u32 val) { _u32 = val; }
|
||||||
|
bool test (u32 flags) const { return !!(_u32 & flags); }
|
||||||
|
void set_flags (u32 flags) { _u32 |= flags; }
|
||||||
|
void clear_flags(u32 flags) { _u32 &= ~flags; }
|
||||||
|
void reset() { _u32 = 0; }
|
||||||
|
wxString desc() const { return wxsFormat(L"GSTransferStatus.PTH3: 0x%x", _u32); }
|
||||||
|
};
|
||||||
//GIF_STAT
|
//GIF_STAT
|
||||||
enum gif_stat_flags
|
enum gif_stat_flags
|
||||||
{
|
{
|
||||||
|
@ -262,7 +278,7 @@ struct GIFregisters
|
||||||
|
|
||||||
#define gifRegs ((GIFregisters*)(PS2MEM_HW+0x3000))
|
#define gifRegs ((GIFregisters*)(PS2MEM_HW+0x3000))
|
||||||
|
|
||||||
extern Path3Modes Path3progress;
|
extern tGSTransferStatus GSTransferStatus;
|
||||||
|
|
||||||
extern void gsInterrupt();
|
extern void gsInterrupt();
|
||||||
extern int _GIFchain();
|
extern int _GIFchain();
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "yuv2rgb.h"
|
#include "yuv2rgb.h"
|
||||||
#include "Vif.h"
|
#include "Vif.h"
|
||||||
#include "Gif.h"
|
#include "Gif.h"
|
||||||
|
#include "Vif_Dma.h"
|
||||||
|
|
||||||
// Zero cycle IRQ schedules aren't really good, but the IPU uses them.
|
// Zero cycle IRQ schedules aren't really good, but the IPU uses them.
|
||||||
// Better to throw the IRQ inline:
|
// Better to throw the IRQ inline:
|
||||||
|
@ -98,7 +99,7 @@ u8* readbits = _readbits; // always can decrement by one 1qw
|
||||||
|
|
||||||
__forceinline void IPUProcessInterrupt()
|
__forceinline void IPUProcessInterrupt()
|
||||||
{
|
{
|
||||||
if (ipuRegs->ctrl.BUSY) IPUWorker();
|
if (ipuRegs->ctrl.BUSY && g_BP.IFC) IPUWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline void init_g_decoder()
|
__forceinline void init_g_decoder()
|
||||||
|
@ -138,6 +139,9 @@ int ipuInit()
|
||||||
memzero(g_BP);
|
memzero(g_BP);
|
||||||
init_g_decoder();
|
init_g_decoder();
|
||||||
g_nDMATransfer.reset();
|
g_nDMATransfer.reset();
|
||||||
|
IPU1Status.InProgress = false;
|
||||||
|
IPU1Status.DMAMode = DMA_MODE_NORMAL;
|
||||||
|
IPU1Status.DMAFinished = true;
|
||||||
ipu_fifo.init();
|
ipu_fifo.init();
|
||||||
ipu_cmd.clear();
|
ipu_cmd.clear();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -147,6 +151,9 @@ void ipuReset()
|
||||||
{
|
{
|
||||||
memzero(*ipuRegs);
|
memzero(*ipuRegs);
|
||||||
g_nDMATransfer.reset();
|
g_nDMATransfer.reset();
|
||||||
|
IPU1Status.InProgress = false;
|
||||||
|
IPU1Status.DMAMode = DMA_MODE_NORMAL;
|
||||||
|
IPU1Status.DMAFinished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ipuShutdown()
|
void ipuShutdown()
|
||||||
|
@ -1221,37 +1228,7 @@ void __fastcall ipu_copy(const macroblock_8 *mb8, macroblock_16 *mb16)
|
||||||
for (i = 0; i < 64; i++) *d++ = *s++; //Cb bias - 128
|
for (i = 0; i < 64; i++) *d++ = *s++; //Cb bias - 128
|
||||||
}
|
}
|
||||||
|
|
||||||
static __forceinline bool IPU1chain(int &totalqwc)
|
|
||||||
{
|
|
||||||
if (ipu1dma->qwc > 0)
|
|
||||||
{
|
|
||||||
int qwc = ipu1dma->qwc;
|
|
||||||
tDMA_TAG *pMem;
|
|
||||||
|
|
||||||
pMem = dmaGetAddr(ipu1dma->madr);
|
|
||||||
|
|
||||||
if (pMem == NULL)
|
|
||||||
{
|
|
||||||
Console.Error("ipu1dma NULL!");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
qwc = ipu_fifo.in.write((u32*)pMem, qwc);
|
|
||||||
ipu1dma->madr += qwc<< 4;
|
|
||||||
ipu1dma->qwc -= qwc;
|
|
||||||
totalqwc += qwc;
|
|
||||||
|
|
||||||
if (ipu1dma->qwc > 0)
|
|
||||||
{
|
|
||||||
// Needed for a select few games, but breaks many others.
|
|
||||||
// Games known to need it include all 3 Tri-Ace games and Grandia Xtreme.
|
|
||||||
// Breaks Atelier Iris 2, Gundam, GT4, Resident Evil Code Veronica X, Music 3000, etc.
|
|
||||||
if(CHECK_IPUSLICEHACK) g_nDMATransfer.ACTV1 = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static __forceinline bool ipuDmacPartialChain(tDMA_TAG tag)
|
static __forceinline bool ipuDmacPartialChain(tDMA_TAG tag)
|
||||||
{
|
{
|
||||||
|
@ -1269,37 +1246,180 @@ static __forceinline bool ipuDmacPartialChain(tDMA_TAG tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void gsInterrupt();
|
extern void gsInterrupt();
|
||||||
|
extern void vif1Interrupt();
|
||||||
|
|
||||||
static __forceinline bool ipuDmacSrcChain(DMACh *tag, tDMA_TAG *ptag)
|
static __forceinline void ipuDmacSrcChain()
|
||||||
{
|
{
|
||||||
switch (ptag->ID)
|
|
||||||
|
switch (IPU1Status.ChainMode)
|
||||||
{
|
{
|
||||||
case TAG_REFE: // refe
|
case 0x0: // refe
|
||||||
// do not change tadr
|
//if(IPU1Status.InProgress == false) ipu1dma->tadr += 16;
|
||||||
tag->madr = ptag[1]._u32;
|
|
||||||
return true;
|
|
||||||
break;
|
break;
|
||||||
|
case 0x1: // cnt
|
||||||
case TAG_CNT: // cnt
|
|
||||||
tag->madr = tag->tadr + 16;
|
|
||||||
// Set the taddr to the next tag
|
// Set the taddr to the next tag
|
||||||
tag->tadr += 16 + (tag->qwc << 4);
|
ipu1dma->tadr = ipu1dma->madr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TAG_NEXT: // next
|
case 0x2: // next
|
||||||
tag->madr = tag->tadr + 16;
|
ipu1dma->tadr = IPU1Status.NextMem;
|
||||||
tag->tadr = ptag[1]._u32;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TAG_REF: // ref
|
/*case 0x3: // ref
|
||||||
tag->madr = ptag[1]._u32;
|
if(IPU1Status.InProgress == false)ipu1dma->tadr += 16;
|
||||||
tag->tadr += 16;
|
break;*/
|
||||||
break;
|
|
||||||
|
|
||||||
case TAG_END: // end
|
case 0x7: // end
|
||||||
// do not change tadr
|
ipu1dma->tadr = ipu1dma->madr;
|
||||||
tag->madr = tag->tadr + 16;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static __forceinline int IPU1chain() {
|
||||||
|
|
||||||
|
int totalqwc = 0;
|
||||||
|
|
||||||
|
if (ipu1dma->qwc > 0 && IPU1Status.InProgress == true)
|
||||||
|
{
|
||||||
|
|
||||||
|
int qwc = ipu1dma->qwc;
|
||||||
|
u32 *pMem;
|
||||||
|
|
||||||
|
pMem = (u32*)dmaGetAddr(ipu1dma->madr);
|
||||||
|
|
||||||
|
if (pMem == NULL)
|
||||||
|
{
|
||||||
|
Console.Error("ipu1dma NULL!"); return totalqwc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write our data to the fifo
|
||||||
|
qwc = ipu_fifo.in.write(pMem, qwc);
|
||||||
|
ipu1dma->madr += qwc << 4;
|
||||||
|
ipu1dma->qwc -= qwc;
|
||||||
|
totalqwc += qwc;
|
||||||
|
|
||||||
|
if( ipu1dma->qwc == 0 )
|
||||||
|
{
|
||||||
|
//If the transfer has finished or we have room in the FIFO, schedule to the interrupt code.
|
||||||
|
if(IPU1Status.DMAFinished == true || g_BP.IFC < 8)
|
||||||
|
{
|
||||||
|
IPU_INT_TO(totalqwc*BIAS);
|
||||||
|
}
|
||||||
|
//No data left
|
||||||
|
IPU1Status.InProgress = false;
|
||||||
|
} //If we still have data the commands should pull this across when need be.
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalqwc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static __forceinline bool flushGIF()
|
||||||
|
{
|
||||||
|
//Wait for all GS paths to be clear
|
||||||
|
if (GSTransferStatus._u32 != 0x2a)
|
||||||
|
{
|
||||||
|
if(GSTransferStatus.PTH3 != STOPPED_MODE && vif1Regs->mskpath3) return true;
|
||||||
|
DMA_LOG("Waiting for GS transfers to finish %x", GSTransferStatus._u32);
|
||||||
|
IPU_INT_TO(4);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int IPU1dma()
|
||||||
|
{
|
||||||
|
bool done = FALSE;
|
||||||
|
int ipu1cycles = 0;
|
||||||
|
int totalqwc = 0;
|
||||||
|
|
||||||
|
//We need to make sure GIF has flushed before sending IPU data, it seems to REALLY screw FFX videos
|
||||||
|
if(!flushGIF()) return totalqwc;
|
||||||
|
|
||||||
|
DMA_LOG("IPU1 DMA Called QWC %x Finished %d In Progress %d tadr %x", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1dma->tadr);
|
||||||
|
|
||||||
|
switch(IPU1Status.DMAMode)
|
||||||
|
{
|
||||||
|
case DMA_MODE_NORMAL:
|
||||||
|
{
|
||||||
|
DMA_LOG("Processing Normal QWC left %x Finished %d In Progress %d", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress);
|
||||||
|
if(IPU1Status.InProgress == true) totalqwc += IPU1chain();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DMA_MODE_CHAIN:
|
||||||
|
{
|
||||||
|
if(IPU1Status.InProgress == true) //No transfer is ready to go so we need to set one up
|
||||||
|
{
|
||||||
|
DMA_LOG("Processing Chain QWC left %x Finished %d In Progress %d", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress);
|
||||||
|
totalqwc += IPU1chain();
|
||||||
|
//Set the TADR forward
|
||||||
|
ipuDmacSrcChain();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(IPU1Status.InProgress == false && IPU1Status.DMAFinished == false) //No transfer is ready to go so we need to set one up
|
||||||
|
{
|
||||||
|
u32 *ptag;
|
||||||
|
|
||||||
|
|
||||||
|
ptag = (u32*)dmaGetAddr(ipu1dma->tadr); //Set memory pointer to TADR
|
||||||
|
if (ptag == NULL) //Is ptag empty?
|
||||||
|
{
|
||||||
|
Console.Error("IPU1 BUSERR");
|
||||||
|
ipu1dma->chcr._u32 = (ipu1dma->chcr._u32 & 0xFFFF) | ((*ptag) & 0xFFFF0000); //Transfer upper part of tag to CHCR bits 31-15
|
||||||
|
psHu32(DMAC_STAT) |= 1 << 15; //If yes, set BEIS (BUSERR) in DMAC_STAT register
|
||||||
|
return totalqwc;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipu1cycles += 1; // Add 1 cycles from the QW read for the tag
|
||||||
|
|
||||||
|
ipu1dma->chcr._u32 = (ipu1dma->chcr._u32 & 0xFFFF) | ((*ptag) & 0xFFFF0000); //Transfer upper part of tag to CHCR bits 31-15
|
||||||
|
ipu1dma->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag
|
||||||
|
|
||||||
|
IPU1Status.ChainMode = (ptag[0] & 0x70000000) >> 28;
|
||||||
|
|
||||||
|
switch (IPU1Status.ChainMode)
|
||||||
|
{
|
||||||
|
case 0x0: // refe
|
||||||
|
// do not change tadr
|
||||||
|
//ipu1dma->tadr += 16;
|
||||||
|
ipu1dma->tadr += 16;
|
||||||
|
ipu1dma->madr = ptag[1];
|
||||||
|
DMA_LOG("Tag should end on %x", ipu1dma->tadr);
|
||||||
|
IPU1Status.DMAFinished = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x1: // cnt
|
||||||
|
ipu1dma->madr = ipu1dma->tadr + 16;
|
||||||
|
DMA_LOG("Tag should end on %x", ipu1dma->madr + ipu1dma->qwc * 16);
|
||||||
|
//ipu1dma->tadr = ipu1dma->madr + (ipu1dma->qwc * 16);
|
||||||
|
// Set the taddr to the next tag
|
||||||
|
IPU1Status.DMAFinished = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x2: // next
|
||||||
|
ipu1dma->madr = ipu1dma->tadr + 16;
|
||||||
|
IPU1Status.NextMem = ptag[1];
|
||||||
|
DMA_LOG("Tag should end on %x", IPU1Status.NextMem);
|
||||||
|
IPU1Status.DMAFinished = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x3: // ref
|
||||||
|
ipu1dma->madr = ptag[1];
|
||||||
|
ipu1dma->tadr += 16;
|
||||||
|
DMA_LOG("Tag should end on %x", ipu1dma->tadr);
|
||||||
|
IPU1Status.DMAFinished = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x7: // end
|
||||||
|
// do not change tadr
|
||||||
|
ipu1dma->madr = ipu1dma->tadr + 16;
|
||||||
|
ipu1dma->tadr += 16;
|
||||||
|
DMA_LOG("Tag should end on %x", ipu1dma->madr + ipu1dma->qwc * 16);
|
||||||
|
IPU1Status.DMAFinished = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1307,178 +1427,26 @@ static __forceinline bool ipuDmacSrcChain(DMACh *tag, tDMA_TAG *ptag)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
//if(ipu1dma->qwc == 0) Console.Warning("Blank QWC!");
|
||||||
|
if(ipu1dma->qwc > 0) IPU1Status.InProgress = true;
|
||||||
|
DMA_LOG("dmaIPU1 dmaChain %8.8x_%8.8x size=%d, addr=%lx, fifosize=%x",
|
||||||
|
ptag[1], ptag[0], ipu1dma->qwc, ipu1dma->madr, 8 - g_BP.IFC);
|
||||||
|
|
||||||
|
if ((ipu1dma->chcr._u32 & 0x80) && ptag[0] & 0x80000000) //Tag Interrupt is set, so schedule the end/interrupt
|
||||||
|
IPU1Status.DMAFinished = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DMA_LOG("Processing Start Chain QWC left %x Finished %d In Progress %d", ipu1dma->qwc, IPU1Status.DMAFinished, IPU1Status.InProgress);
|
||||||
|
totalqwc += IPU1chain();
|
||||||
|
//Set the TADR forward
|
||||||
|
ipuDmacSrcChain();
|
||||||
}
|
}
|
||||||
|
|
||||||
static __forceinline void flushGIF()
|
|
||||||
{
|
|
||||||
if (dmacRegs->ctrl.STD != STD_GIF || (gif->madr + (gif->qwc * 16)) < dmacRegs->stadr.ADDR)
|
|
||||||
{
|
|
||||||
while(gif->chcr.STR && (vif1Regs->mskpath3 == 0) && Path3progress != STOPPED_MODE)
|
|
||||||
{
|
|
||||||
GIF_LOG("Flushing gif chcr %x tadr %x madr %x qwc %x", gif->chcr._u32, gif->tadr, gif->madr, gif->qwc);
|
|
||||||
gsInterrupt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IPU1dma()
|
|
||||||
{
|
|
||||||
tDMA_TAG *ptag;
|
|
||||||
bool done = false;
|
|
||||||
int ipu1cycles = 0, totalqwc = 0;
|
|
||||||
|
|
||||||
// Note: pad is the padding right above qwc, so we're testing whether qwc
|
|
||||||
// has overflowed into pad.
|
|
||||||
if (ipu1dma->pad != 0)
|
|
||||||
{
|
|
||||||
DevCon.Warning(L"IPU1dma's upper 16 bits set to %x\n", ipu1dma->pad);
|
|
||||||
ipu1dma->qwc = ipu1dma->pad = 0;
|
|
||||||
//return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pxAssert(!ipu1dma->chcr.TTE);
|
|
||||||
|
|
||||||
if (!(ipu1dma->chcr.STR) || (cpuRegs.interrupt & (1 << DMAC_TO_IPU))) return 0;
|
|
||||||
|
|
||||||
pxAssert(g_nDMATransfer.TIE1 == false);
|
|
||||||
|
|
||||||
//We need to make sure GIF has flushed before sending IPU data, it seems to REALLY screw FFX videos
|
|
||||||
flushGIF();
|
|
||||||
|
|
||||||
// in kh, qwc == 0 when dma_actv1 is set << legacy comment that means ..something.. I guess
|
|
||||||
// Note: g_nDMATransfer.ACTV1 seems to handle some kind of HLE data slicing.
|
|
||||||
// Most games hate it and vtlb miss or hang / skip the FMV.
|
|
||||||
if ((g_nDMATransfer.ACTV1) && ipu1dma->qwc > 0)
|
|
||||||
{
|
|
||||||
if (IPU1chain(totalqwc)) return totalqwc;
|
|
||||||
|
|
||||||
//Check TIE bit of CHCR and IRQ bit of tag
|
|
||||||
if (ipu1dma->chcr.TIE && g_nDMATransfer.DOTIE1)
|
|
||||||
{
|
|
||||||
Console.WriteLn("IPU1 TIE");
|
|
||||||
|
|
||||||
IPU_INT_TO(totalqwc * BIAS);
|
|
||||||
g_nDMATransfer.TIE1 = true;
|
|
||||||
g_nDMATransfer.DOTIE1 = false;
|
|
||||||
g_nDMATransfer.ACTV1 = false;
|
|
||||||
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ipu1dma->chcr.MOD == NORMAL_MODE) // If mode is normal mode.
|
|
||||||
{
|
|
||||||
IPU_INT_TO(totalqwc * BIAS);
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Chain mode.
|
|
||||||
tDMA_TAG tag = ipu1dma->chcr._u32; // upper bits describe current tag
|
|
||||||
|
|
||||||
if (ipu1dma->chcr.TIE && tag.IRQ)
|
|
||||||
{
|
|
||||||
ptag = dmaGetAddr(ipu1dma->tadr);
|
|
||||||
|
|
||||||
ipuDmacPartialChain(tag);
|
|
||||||
|
|
||||||
ipu1dma->chcrTransfer(ptag);
|
|
||||||
|
|
||||||
IPU_LOG("IPU dmaIrq Set");
|
|
||||||
IPU_INT_TO(totalqwc * BIAS);
|
|
||||||
g_nDMATransfer.TIE1 = true;
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ipuDmacPartialChain(tag))
|
|
||||||
{
|
|
||||||
IPU_INT_TO((1 + totalqwc)*BIAS);
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_nDMATransfer.DOTIE1 = false;
|
|
||||||
g_nDMATransfer.ACTV1 = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal Mode & qwc is finished
|
|
||||||
if ((ipu1dma->chcr.MOD == NORMAL_MODE) && (ipu1dma->qwc == 0))
|
|
||||||
{
|
|
||||||
Console.Warning("ipu1 normal empty qwc?");
|
|
||||||
// returning totalqwc here is not enough. It seems to be an error,
|
|
||||||
// which may cause hangs, especially in God of War.
|
|
||||||
// Always zero is returned here, what is covered up by other return paths in this function.
|
|
||||||
// Thus, additional variable is set to indicate from where totalqwc comes.
|
|
||||||
g_ipu_dma_error = -1; // saves an error of the ipu1 DMA.
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transfer Dn_QWC from Dn_MADR to GIF
|
|
||||||
if (ipu1dma->qwc > 0)
|
|
||||||
{
|
|
||||||
IPU_LOG("dmaIPU1 Normal size=%d, addr=%lx, fifosize=%x",
|
|
||||||
ipu1dma->qwc, ipu1dma->madr, 8 - g_BP.IFC);
|
|
||||||
|
|
||||||
if (!IPU1chain(totalqwc)) IPU_INT_TO((ipu1cycles + totalqwc) * BIAS);
|
|
||||||
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Chain Mode & ipu1dma->qwc is 0
|
|
||||||
ptag = dmaGetAddr(ipu1dma->tadr); //Set memory pointer to TADR
|
|
||||||
|
|
||||||
// Transfer the tag.
|
|
||||||
if (!ipu1dma->transfer("IPU1", ptag)) return totalqwc;
|
|
||||||
|
|
||||||
ipu1cycles += 1; // Add 1 cycles from the QW read for the tag
|
|
||||||
|
|
||||||
done = ipuDmacSrcChain(ipu1dma, ptag);
|
|
||||||
|
|
||||||
IPU_LOG("dmaIPU1 dmaChain %8.8x_%8.8x size=%d, addr=%lx, fifosize=%x",
|
|
||||||
ptag[1]._u32, ptag[0]._u32, ipu1dma->qwc, ipu1dma->madr, 8 - g_BP.IFC);
|
|
||||||
|
|
||||||
g_nDMATransfer.DOTIE1 = (ipu1dma->chcr.TIE && ptag->IRQ);
|
|
||||||
|
|
||||||
if (ipu1dma->qwc == 0)
|
|
||||||
{
|
|
||||||
//Check TIE bit of CHCR and IRQ bit of tag
|
|
||||||
if (g_nDMATransfer.DOTIE1)
|
|
||||||
{
|
|
||||||
Console.WriteLn("IPU1 TIE");
|
|
||||||
|
|
||||||
if (IPU1chain(totalqwc)) return totalqwc;
|
|
||||||
|
|
||||||
if (done)
|
|
||||||
{
|
|
||||||
ptag = dmaGetAddr(ipu1dma->tadr);
|
|
||||||
|
|
||||||
ipuDmacPartialChain(ptag[0]);
|
|
||||||
|
|
||||||
// Transfer the last of ptag into chcr.
|
|
||||||
ipu1dma->chcrTransfer(ptag);
|
|
||||||
}
|
|
||||||
|
|
||||||
IPU_INT_TO(ipu1cycles + totalqwc * BIAS); // Should it be (ipu1cycles + totalqwc) * BIAS?
|
|
||||||
g_nDMATransfer.TIE1 = true;
|
|
||||||
return totalqwc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Britney Dance beat does a blank NEXT tag, for some odd reason the fix doesnt work if after IPU1Chain O_o
|
|
||||||
if (!done) IPU1dma();
|
|
||||||
if (IPU1chain(totalqwc)) return totalqwc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ipuDmacPartialChain(ptag[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (IPU1chain(totalqwc)) return totalqwc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IPU_INT_TO((ipu1cycles + totalqwc) * BIAS);
|
|
||||||
return totalqwc;
|
return totalqwc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1526,12 +1494,15 @@ int IPU0dma()
|
||||||
case NO_STD:
|
case NO_STD:
|
||||||
break;
|
break;
|
||||||
case STD_GIF: // GIF
|
case STD_GIF: // GIF
|
||||||
|
Console.Warning("GIFSTALL");
|
||||||
g_nDMATransfer.GIFSTALL = true;
|
g_nDMATransfer.GIFSTALL = true;
|
||||||
break;
|
break;
|
||||||
case STD_VIF1: // VIF
|
case STD_VIF1: // VIF
|
||||||
|
Console.Warning("VIFSTALL");
|
||||||
g_nDMATransfer.VIFSTALL = true;
|
g_nDMATransfer.VIFSTALL = true;
|
||||||
break;
|
break;
|
||||||
case STD_SIF1:
|
case STD_SIF1:
|
||||||
|
Console.Warning("SIFSTALL");
|
||||||
g_nDMATransfer.SIFSTALL = true;
|
g_nDMATransfer.SIFSTALL = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1554,9 +1525,50 @@ __forceinline void dmaIPU0() // fromIPU
|
||||||
|
|
||||||
__forceinline void dmaIPU1() // toIPU
|
__forceinline void dmaIPU1() // toIPU
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(ipu1dma->chcr.MOD == 1) //Chain Mode
|
||||||
|
{
|
||||||
|
DMA_LOG("Setting up IPU1 Chain mode");
|
||||||
|
if(ipu1dma->qwc == 0)
|
||||||
|
{
|
||||||
|
IPU1Status.InProgress = false;
|
||||||
|
IPU1Status.TagFollow = IPU1_TAG_NONE;
|
||||||
|
IPU1Status.DMAFinished = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //Attempting to continue a previous chain
|
||||||
|
//Console.Warning("IPU1 continuing previous chain");
|
||||||
|
if(IPU1Status.ChainMode == 0 || IPU1Status.ChainMode == 0x7)IPU1Status.DMAFinished = true;
|
||||||
|
else IPU1Status.DMAFinished = false;
|
||||||
|
IPU1Status.InProgress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPU1Status.DMAMode = DMA_MODE_CHAIN;
|
||||||
|
|
||||||
IPU1dma();
|
IPU1dma();
|
||||||
if (ipuRegs->ctrl.BUSY) IPUWorker();
|
if (ipuRegs->ctrl.BUSY) IPUWorker();
|
||||||
}
|
}
|
||||||
|
else //Normal Mode
|
||||||
|
{
|
||||||
|
if(ipu1dma->qwc == 0)
|
||||||
|
{
|
||||||
|
ipu1dma->chcr.STR = 0;
|
||||||
|
hwDmacIrq(DMAC_TO_IPU);
|
||||||
|
Console.Warning("IPU1 Normal error!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DMA_LOG("Setting up IPU1 Normal mode");
|
||||||
|
IPU1Status.InProgress = true;
|
||||||
|
IPU1Status.DMAFinished = true;
|
||||||
|
IPU1Status.DMAMode = DMA_MODE_NORMAL;
|
||||||
|
IPU1dma();
|
||||||
|
if (ipuRegs->ctrl.BUSY) IPUWorker();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
extern void GIFdma();
|
extern void GIFdma();
|
||||||
|
|
||||||
|
@ -1605,18 +1617,19 @@ void ipu0Interrupt()
|
||||||
|
|
||||||
IPU_FORCEINLINE void ipu1Interrupt()
|
IPU_FORCEINLINE void ipu1Interrupt()
|
||||||
{
|
{
|
||||||
IPU_LOG("ipu1Interrupt %x:", cpuRegs.cycle);
|
DMA_LOG("ipu1Interrupt %x:", cpuRegs.cycle);
|
||||||
|
|
||||||
if (g_nDMATransfer.FIREINT1)
|
if(IPU1Status.DMAFinished == false || IPU1Status.InProgress == true) //Sanity Check
|
||||||
{
|
{
|
||||||
hwIntcIrq(INTC_IPU);
|
//Console.Warning("IPU1 finishing when not finished!");
|
||||||
g_nDMATransfer.FIREINT1 = false;
|
IPU1dma();
|
||||||
|
if (ipuRegs->ctrl.BUSY && g_BP.IFC) IPUWorker();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_nDMATransfer.TIE1)
|
//Console.Warning("Whoops");
|
||||||
g_nDMATransfer.TIE1 = false;
|
|
||||||
else
|
|
||||||
ipu1dma->chcr.STR = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
ipu1dma->chcr.STR = false;
|
||||||
hwDmacIrq(DMAC_TO_IPU);
|
hwDmacIrq(DMAC_TO_IPU);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,28 @@
|
||||||
#define ipumsk( src ) ( (src) & 0xff )
|
#define ipumsk( src ) ( (src) & 0xff )
|
||||||
#define ipucase( src ) case ipumsk(src)
|
#define ipucase( src ) case ipumsk(src)
|
||||||
|
|
||||||
|
struct IPUStatus {
|
||||||
|
bool InProgress;
|
||||||
|
u8 DMAMode;
|
||||||
|
bool DMAFinished;
|
||||||
|
bool IRQTriggered;
|
||||||
|
u8 TagFollow;
|
||||||
|
u32 TagAddr;
|
||||||
|
bool stalled;
|
||||||
|
u8 ChainMode;
|
||||||
|
u32 NextMem;
|
||||||
|
};
|
||||||
|
|
||||||
|
static IPUStatus IPU1Status;
|
||||||
|
|
||||||
|
#define DMA_MODE_NORMAL 0
|
||||||
|
#define DMA_MODE_CHAIN 1
|
||||||
|
|
||||||
|
#define IPU1_TAG_FOLLOW 0
|
||||||
|
#define IPU1_TAG_QWC 1
|
||||||
|
#define IPU1_TAG_ADDR 2
|
||||||
|
#define IPU1_TAG_NONE 3
|
||||||
|
|
||||||
//
|
//
|
||||||
// Bitfield Structures
|
// Bitfield Structures
|
||||||
//
|
//
|
||||||
|
|
|
@ -252,7 +252,6 @@ void Pcsx2Config::GamefixOptions::LoadSave( IniInterface& ini )
|
||||||
IniScopedGroup path( ini, L"Gamefixes" );
|
IniScopedGroup path( ini, L"Gamefixes" );
|
||||||
|
|
||||||
IniBitBool( VuAddSubHack );
|
IniBitBool( VuAddSubHack );
|
||||||
IniBitBool( IpuSliceHack );
|
|
||||||
IniBitBool( VuClipFlagHack );
|
IniBitBool( VuClipFlagHack );
|
||||||
IniBitBool( FpuCompareHack );
|
IniBitBool( FpuCompareHack );
|
||||||
IniBitBool( FpuMulHack );
|
IniBitBool( FpuMulHack );
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
vifStruct vif0;
|
vifStruct vif0;
|
||||||
vifStruct vif1;
|
vifStruct vif1;
|
||||||
Path3Modes Path3progress = STOPPED_MODE;
|
tGSTransferStatus GSTransferStatus = (STOPPED_MODE<<4) | (STOPPED_MODE<<2) | STOPPED_MODE;
|
||||||
|
|
||||||
void vif0Init() { initNewVif(0); }
|
void vif0Init() { initNewVif(0); }
|
||||||
void vif1Init() { initNewVif(1); }
|
void vif1Init() { initNewVif(1); }
|
||||||
|
@ -253,15 +253,23 @@ _f void vif1STAT(u32 value) {
|
||||||
if (vif1Regs->stat.FDR) // Vif transferring to memory.
|
if (vif1Regs->stat.FDR) // Vif transferring to memory.
|
||||||
{
|
{
|
||||||
// Hack but it checks this is true before transfer? (fatal frame)
|
// Hack but it checks this is true before transfer? (fatal frame)
|
||||||
vif1Regs->stat.FQC = 0x1;
|
// Update Refraction: Use of this function has been investigated and understood.
|
||||||
|
// Before this ever happens, a DIRECT/HL command takes place sending the transfer info to the GS
|
||||||
|
// One of the registers told about this is TRXREG which tells us how much data is going to transfer (th x tw) in words
|
||||||
|
// As far as the GS is concerned, the transfer starts as soon as TRXREG is accessed, which is why fatal frame
|
||||||
|
// was expecting data, the GS should already be sending it over (buffering in the FIFO)
|
||||||
|
|
||||||
|
vif1Regs->stat.FQC = max((u32)16, vif1.GSLastTRXPOS);
|
||||||
|
//Console.Warning("Reversing VIF Transfer for %x QWC", vif1.GSLastTRXPOS);
|
||||||
|
|
||||||
}
|
}
|
||||||
else // Memory transferring to Vif.
|
/*else // Memory transferring to Vif.
|
||||||
{
|
{
|
||||||
vif1ch->qwc = 0;
|
vif1ch->qwc = 0;
|
||||||
vif1.vifstalled = false;
|
vif1.vifstalled = false;
|
||||||
vif1.done = true;
|
vif1.done = true;
|
||||||
vif1Regs->stat.FQC = 0;
|
vif1Regs->stat.FQC = 0;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#define caseVif(x) (idx ? VIF1_##x : VIF0_##x)
|
#define caseVif(x) (idx ? VIF1_##x : VIF0_##x)
|
||||||
|
|
|
@ -152,7 +152,7 @@ __forceinline void vif0Interrupt()
|
||||||
// VIF_NORMAL_FROM_MEM_MODE is a very slow operation.
|
// VIF_NORMAL_FROM_MEM_MODE is a very slow operation.
|
||||||
// Timesplitters 2 depends on this beeing a bit higher than 128.
|
// Timesplitters 2 depends on this beeing a bit higher than 128.
|
||||||
if (vif0.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF0, 1024);
|
if (vif0.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF0, 1024);
|
||||||
else CPU_INT(DMAC_VIF0, /*g_vifCycles*/ VifCycleVoodoo);
|
else CPU_INT(DMAC_VIF0, g_vifCycles/*VifCycleVoodoo*/);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ __forceinline void vif0Interrupt()
|
||||||
|
|
||||||
if ((vif0.inprogress & 0x1) == 0) vif0SetupTransfer();
|
if ((vif0.inprogress & 0x1) == 0) vif0SetupTransfer();
|
||||||
|
|
||||||
CPU_INT(DMAC_VIF0, /*g_vifCycles*/ VifCycleVoodoo);
|
CPU_INT(DMAC_VIF0, g_vifCycles /*VifCycleVoodoo*/);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,7 @@ __forceinline void vif1Interrupt()
|
||||||
|
|
||||||
if ((vif1Regs->stat.VGW))
|
if ((vif1Regs->stat.VGW))
|
||||||
{
|
{
|
||||||
if (gif->chcr.STR && (Path3progress != STOPPED_MODE))
|
if ((gif->chcr.STR && (GSTransferStatus.PTH3 != STOPPED_MODE)) || (GSTransferStatus.PTH1 != STOPPED_MODE))
|
||||||
{
|
{
|
||||||
CPU_INT(DMAC_VIF1, 4);
|
CPU_INT(DMAC_VIF1, 4);
|
||||||
return;
|
return;
|
||||||
|
@ -240,8 +240,10 @@ __forceinline void vif1Interrupt()
|
||||||
_VIF1chain();
|
_VIF1chain();
|
||||||
// VIF_NORMAL_FROM_MEM_MODE is a very slow operation.
|
// VIF_NORMAL_FROM_MEM_MODE is a very slow operation.
|
||||||
// Timesplitters 2 depends on this beeing a bit higher than 128.
|
// Timesplitters 2 depends on this beeing a bit higher than 128.
|
||||||
if (vif1.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF1, 1024);
|
|
||||||
else CPU_INT(DMAC_VIF1, /*g_vifCycles*/ VifCycleVoodoo);
|
// Refraction - Removing voodoo timings for now, completely messes a lot of Path3 masked games.
|
||||||
|
/*if (vif1.dmamode == VIF_NORMAL_FROM_MEM_MODE ) CPU_INT(DMAC_VIF1, 1024);
|
||||||
|
else */CPU_INT(DMAC_VIF1, g_vifCycles /*VifCycleVoodoo*/);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +258,7 @@ __forceinline void vif1Interrupt()
|
||||||
|
|
||||||
if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer();
|
if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer();
|
||||||
|
|
||||||
CPU_INT(DMAC_VIF1, /*g_vifCycles*/ VifCycleVoodoo);
|
CPU_INT(DMAC_VIF1, g_vifCycles /*VifCycleVoodoo*/);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +282,7 @@ __forceinline void vif1Interrupt()
|
||||||
//Games effected by setting, Fatal Frame, KH2, Shox, Crash N Burn, GT3/4 possibly
|
//Games effected by setting, Fatal Frame, KH2, Shox, Crash N Burn, GT3/4 possibly
|
||||||
//Im guessing due to the full gs fifo before the reverse? (Refraction)
|
//Im guessing due to the full gs fifo before the reverse? (Refraction)
|
||||||
//Note also this is only the condition for reverse fifo mode, normal direction clears it as normal
|
//Note also this is only the condition for reverse fifo mode, normal direction clears it as normal
|
||||||
if (!vif1Regs->mskpath3 || vif1ch->chcr.DIR) vif1Regs->stat.FQC = 0;
|
if (!vif1Regs->mskpath3 || !vif1ch->chcr.DIR) vif1Regs->stat.FQC = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dmaVIF1()
|
void dmaVIF1()
|
||||||
|
|
|
@ -78,9 +78,9 @@ void Vif1MskPath3() {
|
||||||
|
|
||||||
if (!vif1Regs->mskpath3) {
|
if (!vif1Regs->mskpath3) {
|
||||||
//Let the Gif know it can transfer again (making sure any vif stall isnt unset prematurely)
|
//Let the Gif know it can transfer again (making sure any vif stall isnt unset prematurely)
|
||||||
Path3progress = TRANSFER_MODE;
|
GSTransferStatus.PTH3 = TRANSFER_MODE;
|
||||||
gifRegs->stat.IMT = false;
|
gifRegs->stat.IMT = false;
|
||||||
CPU_INT(DMAC_GIF, 4);
|
if(gif->chcr.STR == true) CPU_INT(DMAC_GIF, 4);
|
||||||
}
|
}
|
||||||
else gifRegs->stat.M3P = true;
|
else gifRegs->stat.M3P = true;
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ template<int idx> _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) {
|
||||||
|
|
||||||
//Should probably do this for both types of transfer seen as the GS hates taking 2 seperate chunks
|
//Should probably do this for both types of transfer seen as the GS hates taking 2 seperate chunks
|
||||||
//if (isDirectHL) {
|
//if (isDirectHL) {
|
||||||
if (gif->chcr.STR && (!vif1Regs->mskpath3 && (Path3progress != STOPPED_MODE)))
|
if (gif->chcr.STR && (!vif1Regs->mskpath3 && (GSTransferStatus.PTH3 != STOPPED_MODE)))
|
||||||
{
|
{
|
||||||
/*if(!isDirectHL) DevCon.WriteLn("Direct: Waiting for Path3 to finish!");
|
/*if(!isDirectHL) DevCon.WriteLn("Direct: Waiting for Path3 to finish!");
|
||||||
else DevCon.WriteLn("DirectHL: Waiting for Path3 to finish!");*/
|
else DevCon.WriteLn("DirectHL: Waiting for Path3 to finish!");*/
|
||||||
|
@ -125,7 +125,7 @@ template<int idx> _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) {
|
||||||
Registers::Freeze();
|
Registers::Freeze();
|
||||||
nVifStruct& v = nVif[1];
|
nVifStruct& v = nVif[1];
|
||||||
const int ret = aMin(vif1.vifpacketsize, vif1.tag.size);
|
const int ret = aMin(vif1.vifpacketsize, vif1.tag.size);
|
||||||
s32 size = ret << 2;
|
u32 size = ret << 2;
|
||||||
|
|
||||||
if (ret == v.vif->tag.size) { // Full Transfer
|
if (ret == v.vif->tag.size) { // Full Transfer
|
||||||
if (v.bSize) { // Last transfer was partial
|
if (v.bSize) { // Last transfer was partial
|
||||||
|
@ -138,13 +138,14 @@ template<int idx> _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) {
|
||||||
const uint count = GetMTGS().PrepDataPacket(GIF_PATH_2, data, size >> 4);
|
const uint count = GetMTGS().PrepDataPacket(GIF_PATH_2, data, size >> 4);
|
||||||
memcpy_fast(GetMTGS().GetDataPacketPtr(), data, count << 4);
|
memcpy_fast(GetMTGS().GetDataPacketPtr(), data, count << 4);
|
||||||
GetMTGS().SendDataPacket();
|
GetMTGS().SendDataPacket();
|
||||||
|
if((count << 4) < size) Console.Warning("PATH2 end early, count %x, size %x", count << 4, size);
|
||||||
vif1.tag.size = 0;
|
vif1.tag.size = 0;
|
||||||
vif1.cmd = 0;
|
vif1.cmd = 0;
|
||||||
v.bSize = 0;
|
v.bSize = 0;
|
||||||
gifRegs->stat.clear_flags(GIF_STAT_APATH2 | GIF_STAT_OPH);
|
gifRegs->stat.clear_flags(GIF_STAT_APATH2 | GIF_STAT_OPH);
|
||||||
}
|
}
|
||||||
else { // Partial Transfer
|
else { // Partial Transfer
|
||||||
//DevCon.WriteLn("DirectHL: Partial Transfer [%d]", size);
|
DevCon.WriteLn("DirectHL: Partial Transfer [%d]", size);
|
||||||
gifRegs->stat.set_flags(GIF_STAT_APATH2 | GIF_STAT_OPH);
|
gifRegs->stat.set_flags(GIF_STAT_APATH2 | GIF_STAT_OPH);
|
||||||
memcpy_fast(&v.buffer[v.bSize], data, size);
|
memcpy_fast(&v.buffer[v.bSize], data, size);
|
||||||
v.bSize += size;
|
v.bSize += size;
|
||||||
|
@ -180,7 +181,7 @@ vifOp(vifCode_FlushA) {
|
||||||
vif1Only();
|
vif1Only();
|
||||||
pass1 {
|
pass1 {
|
||||||
// Gif is already transferring so wait for it.
|
// Gif is already transferring so wait for it.
|
||||||
if (((Path3progress != STOPPED_MODE) || !vif1Regs->mskpath3) && gif->chcr.STR) {
|
if (((GSTransferStatus.PTH3 != STOPPED_MODE) || !vif1Regs->mskpath3) && gif->chcr.STR) {
|
||||||
//DevCon.WriteLn("FlushA path3 Wait!");
|
//DevCon.WriteLn("FlushA path3 Wait!");
|
||||||
vif1Regs->stat.VGW = true;
|
vif1Regs->stat.VGW = true;
|
||||||
vifX.vifstalled = true;
|
vifX.vifstalled = true;
|
||||||
|
|
|
@ -24,6 +24,18 @@ struct vifCode {
|
||||||
u16 cl;
|
u16 cl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union tBITBLT {
|
||||||
|
struct {
|
||||||
|
u32 reserved : 8;
|
||||||
|
u32 BLTDIVIDE : 8; // This is the value we want to work out the divider for the reverse transfer
|
||||||
|
u32 reserved2 : 6;
|
||||||
|
u32 TRXPOS : 16;
|
||||||
|
};
|
||||||
|
u32 _u32;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
// NOTE, if debugging vif stalls, use sega classics, spyro, gt4, and taito
|
// NOTE, if debugging vif stalls, use sega classics, spyro, gt4, and taito
|
||||||
struct vifStruct {
|
struct vifStruct {
|
||||||
vifCode tag;
|
vifCode tag;
|
||||||
|
@ -36,6 +48,8 @@ struct vifStruct {
|
||||||
bool done;
|
bool done;
|
||||||
bool vifstalled;
|
bool vifstalled;
|
||||||
bool stallontag;
|
bool stallontag;
|
||||||
|
tBITBLT TRXPOS; //used for reversed fifo operations, sometimes only the GS knows how big (like on HW register fifo read)!
|
||||||
|
u32 GSLastTRXPOS;
|
||||||
|
|
||||||
u8 irqoffset; // 32bit offset where next vif code is
|
u8 irqoffset; // 32bit offset where next vif code is
|
||||||
u32 savedtag; // need this for backwards compat with save states
|
u32 savedtag; // need this for backwards compat with save states
|
||||||
|
|
|
@ -74,6 +74,7 @@ _vifT void vifTransferLoop(u32* &data) {
|
||||||
iBit = data[0] >> 31;
|
iBit = data[0] >> 31;
|
||||||
|
|
||||||
vifXCode[vifX.cmd & 0x7f](0, data);
|
vifXCode[vifX.cmd & 0x7f](0, data);
|
||||||
|
VIF_LOG("New Vif%d CMD, CMD = %x, iBit = %x, data %x", idx, vifX.cmd, iBit, data[0]);
|
||||||
data++; pSize--;
|
data++; pSize--;
|
||||||
if (analyzeIbit<idx>(data, iBit)) break;
|
if (analyzeIbit<idx>(data, iBit)) break;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -38,10 +38,6 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow* parent ) :
|
||||||
_("VU Add Hack - fixes Tri-Ace games boot crash (Enable IPU hack as well.)"),
|
_("VU Add Hack - fixes Tri-Ace games boot crash (Enable IPU hack as well.)"),
|
||||||
_("Games that need this hack to boot:\n * Star Ocean 3\n * Radiata Stories\n * Valkyrie Profile 2")
|
_("Games that need this hack to boot:\n * Star Ocean 3\n * Radiata Stories\n * Valkyrie Profile 2")
|
||||||
},
|
},
|
||||||
{
|
|
||||||
_("IPU slices Hack - for Tri-Ace game's videos (Breaks most other games!)"),
|
|
||||||
_("Fixes videos not playing in:\n * Star Ocean 3\n * Radiata Stories\n * Valkyrie Profile 2")
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
_("VU Clip Flag Hack - for Persona games (SuperVU recompiler only!)"),
|
_("VU Clip Flag Hack - for Persona games (SuperVU recompiler only!)"),
|
||||||
wxEmptyString
|
wxEmptyString
|
||||||
|
|
|
@ -236,7 +236,7 @@ __forceinline bool GIFPath::StepReg()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
__forceinline u8 GIFPath::GetReg() { return regs[curreg]; }
|
__forceinline u8 GIFPath::GetReg() { GIF_LOG("Checking reg %x", regs[curreg]); return regs[curreg]; }
|
||||||
|
|
||||||
// Unpack the registers - registers are stored as a sequence of 4 bit values in the
|
// 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
|
// upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
|
||||||
|
@ -277,6 +277,40 @@ void SaveStateBase::gifPathFreeze()
|
||||||
static __forceinline void gsHandler(const u8* pMem)
|
static __forceinline void gsHandler(const u8* pMem)
|
||||||
{
|
{
|
||||||
const int handler = pMem[8];
|
const int handler = pMem[8];
|
||||||
|
|
||||||
|
if(handler == 0x50)
|
||||||
|
{
|
||||||
|
const u16* pMem16 = (const u16*)pMem;
|
||||||
|
|
||||||
|
vif1.TRXPOS._u32 = pMem16[1];
|
||||||
|
//Console.Warning("BLITBUF = %x %x_%x_%x_%x", vif1.TRXPOS.BLTDIVIDE, pMem16[0], pMem16[1], pMem16[2], pMem16[3]);
|
||||||
|
switch(vif1.TRXPOS.BLTDIVIDE & 0x3)
|
||||||
|
{
|
||||||
|
case 0x3:
|
||||||
|
//Console.Warning("8bit");
|
||||||
|
vif1.TRXPOS.BLTDIVIDE = 16; //8bit
|
||||||
|
break;
|
||||||
|
case 0x2:
|
||||||
|
//Console.Warning("16bit");
|
||||||
|
vif1.TRXPOS.BLTDIVIDE = 8; //16bit
|
||||||
|
break;
|
||||||
|
case 0x1:
|
||||||
|
//Console.Warning("16bit");
|
||||||
|
vif1.TRXPOS.BLTDIVIDE = 6; //16bit
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//Console.Warning("32bit");
|
||||||
|
vif1.TRXPOS.BLTDIVIDE = 4; //32bit
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(handler == 0x52)
|
||||||
|
{
|
||||||
|
const u16* pMem16 = (const u16*)pMem;
|
||||||
|
//Console.Warning("TRX REG = %x_%x_%x_%x", pMem16[0], pMem16[1], pMem16[2], pMem16[3]);
|
||||||
|
vif1.GSLastTRXPOS = (pMem16[0] * pMem16[2]) / (u8)vif1.TRXPOS.BLTDIVIDE;
|
||||||
|
|
||||||
|
}
|
||||||
if (handler >= 0x60)
|
if (handler >= 0x60)
|
||||||
{
|
{
|
||||||
// Question: What happens if an app writes to uncharted register space on real PS2
|
// Question: What happens if an app writes to uncharted register space on real PS2
|
||||||
|
@ -310,14 +344,28 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
||||||
SetTag(pMem);
|
SetTag(pMem);
|
||||||
incTag(16, 1);
|
incTag(16, 1);
|
||||||
|
|
||||||
if (pathidx == GIF_PATH_3) {
|
//if (pathidx == GIF_PATH_3) {
|
||||||
if (tag.FLG&2) Path3progress = IMAGE_MODE;
|
switch(pathidx)
|
||||||
else Path3progress = TRANSFER_MODE;
|
{
|
||||||
|
case GIF_PATH_1:
|
||||||
|
if (tag.FLG&2) GSTransferStatus.PTH1 = IMAGE_MODE;
|
||||||
|
else GSTransferStatus.PTH1 = TRANSFER_MODE;
|
||||||
|
break;
|
||||||
|
case GIF_PATH_2:
|
||||||
|
if (tag.FLG&2) GSTransferStatus.PTH2 = IMAGE_MODE;
|
||||||
|
else GSTransferStatus.PTH2 = TRANSFER_MODE;
|
||||||
|
break;
|
||||||
|
case GIF_PATH_3:
|
||||||
|
if (tag.FLG&2) GSTransferStatus.PTH3 = IMAGE_MODE;
|
||||||
|
else GSTransferStatus.PTH3 = TRANSFER_MODE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
switch(tag.FLG) {
|
switch(tag.FLG) {
|
||||||
case GIF_FLG_PACKED:
|
case GIF_FLG_PACKED:
|
||||||
|
GIF_LOG("Packed Mode");
|
||||||
PrepPackedRegs();
|
PrepPackedRegs();
|
||||||
do {
|
do {
|
||||||
if (GetReg() == 0xe) {
|
if (GetReg() == 0xe) {
|
||||||
|
@ -328,6 +376,7 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
||||||
break;
|
break;
|
||||||
case GIF_FLG_REGLIST:
|
case GIF_FLG_REGLIST:
|
||||||
{
|
{
|
||||||
|
GIF_LOG("Reglist Mode");
|
||||||
size *= 2;
|
size *= 2;
|
||||||
|
|
||||||
do { incTag(8, 1); }
|
do { incTag(8, 1); }
|
||||||
|
@ -340,6 +389,7 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
||||||
case GIF_FLG_IMAGE:
|
case GIF_FLG_IMAGE:
|
||||||
case GIF_FLG_IMAGE2:
|
case GIF_FLG_IMAGE2:
|
||||||
{
|
{
|
||||||
|
GIF_LOG("IMAGE Mode");
|
||||||
int len = aMin(size, nloop);
|
int len = aMin(size, nloop);
|
||||||
incTag(( len * 16 ), len);
|
incTag(( len * 16 ), len);
|
||||||
nloop -= len;
|
nloop -= len;
|
||||||
|
@ -357,10 +407,23 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
|
||||||
|
|
||||||
size = (startSize - size);
|
size = (startSize - size);
|
||||||
|
|
||||||
if (pathidx == GIF_PATH_3) {
|
|
||||||
if (tag.EOP && !nloop) {
|
if (tag.EOP && !nloop) {
|
||||||
Path3progress = STOPPED_MODE;
|
//Console.Warning("Finishing path %x", pathidx);
|
||||||
|
switch(pathidx)
|
||||||
|
{
|
||||||
|
case GIF_PATH_1:
|
||||||
|
GSTransferStatus.PTH1 = STOPPED_MODE;
|
||||||
|
break;
|
||||||
|
case GIF_PATH_2:
|
||||||
|
GSTransferStatus.PTH2 = STOPPED_MODE;
|
||||||
|
break;
|
||||||
|
case GIF_PATH_3:
|
||||||
|
GSTransferStatus.PTH3 = STOPPED_MODE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (pathidx == GIF_PATH_3) {
|
||||||
gif->madr += size * 16;
|
gif->madr += size * 16;
|
||||||
gif->qwc -= size;
|
gif->qwc -= size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -777,10 +777,6 @@
|
||||||
<Filter
|
<Filter
|
||||||
Name="DMAC"
|
Name="DMAC"
|
||||||
>
|
>
|
||||||
<File
|
|
||||||
RelativePath="..\..\Gif.cpp"
|
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<Filter
|
<Filter
|
||||||
Name="Sif"
|
Name="Sif"
|
||||||
>
|
>
|
||||||
|
@ -905,6 +901,18 @@
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter
|
||||||
|
Name="Gif"
|
||||||
|
>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\Gif.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\..\Gif.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
</Filter>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="EE"
|
Name="EE"
|
||||||
|
|
Loading…
Reference in New Issue