diff --git a/pcsx2/Config.h b/pcsx2/Config.h index aef4ef37a9..dcb3c587a7 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -426,7 +426,6 @@ struct Pcsx2Config BITFIELD32() bool 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). FpuCompareHack :1, // Digimon Rumble Arena 2, fixes spinning/hanging on intro-menu. FpuMulHack :1, // Tales of Destiny hangs. @@ -556,10 +555,9 @@ TraceLogFilters& SetTraceConfig(); #define CHECK_IOPREC (EmuConfig.Cpu.Recompiler.EnableIOP && GetSysCoreAlloc().IsRecAvailable_IOP()) //------------ 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_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_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. diff --git a/pcsx2/FiFo.cpp b/pcsx2/FiFo.cpp index d7a40fa090..90ac8c6d01 100644 --- a/pcsx2/FiFo.cpp +++ b/pcsx2/FiFo.cpp @@ -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) ) 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 (--psHu32(D1_QWC) == 0) - vif1Regs->stat.FQC = 0; + if (vif1Regs->stat.FQC > 0) + { + GetMTGS().WaitGS(); + GSreadFIFO(&psHu64(VIF1_FIFO)); + } + if(vif1Regs->stat.FQC > 0)--vif1Regs->stat.FQC; } out[0] = psHu64(VIF1_FIFO); diff --git a/pcsx2/Gif.cpp b/pcsx2/Gif.cpp index 9e9e76c691..939f7084d2 100644 --- a/pcsx2/Gif.cpp +++ b/pcsx2/Gif.cpp @@ -56,22 +56,7 @@ __forceinline void gsInterrupt() } - - 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) + if (GSTransferStatus.PTH3 == STOPPED_MODE) { gifRegs->stat.clear_flags(GIF_STAT_APATH3 | GIF_STAT_OPH); } @@ -219,12 +204,15 @@ void GIFdma() 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 - 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. - GIF_LOG("Waiting VU %x, PATH2 %x, GIFMODE %x Progress %x", gifRegs->stat.P1Q, (vif1.cmd & 0x7f), gifRegs->mode._u32, Path3progress); - CPU_INT(DMAC_GIF, 16); - return; + 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); + return; + } } 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; if (gif->qwc == 0) CPU_INT(DMAC_GIF, 16); return; @@ -251,6 +240,7 @@ void GIFdma() //Check with Path3 masking games if (gif->qwc > 0) { + GIF_LOG("PTH3 MASK Transferring", ptag[1]._u32, ptag[0]._u32, gif->qwc, ptag->ID, gif->madr); GIFchain(); CPU_INT(DMAC_GIF, gscycles * BIAS); return; @@ -333,7 +323,7 @@ void dmaGIF() //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); - 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 :) gifRegs->stat.P3Q = true; @@ -546,7 +536,7 @@ void gifMFIFOInterrupt() //Console.WriteLn("gifMFIFOInterrupt"); mfifocycles = 0; - if (Path3progress == STOPPED_MODE) + if (GSTransferStatus.PTH3 == STOPPED_MODE) { gifRegs->stat.APATH = GIF_APATH_IDLE; gifRegs->stat.OPH = false; @@ -565,9 +555,9 @@ void gifMFIFOInterrupt() 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); return; } diff --git a/pcsx2/Gif.h b/pcsx2/Gif.h index ed3ad9b0bc..c79a1cc949 100644 --- a/pcsx2/Gif.h +++ b/pcsx2/Gif.h @@ -26,13 +26,29 @@ enum gifstate_t 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, TRANSFER_MODE = 1, 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 enum gif_stat_flags { @@ -262,7 +278,7 @@ struct GIFregisters #define gifRegs ((GIFregisters*)(PS2MEM_HW+0x3000)) -extern Path3Modes Path3progress; +extern tGSTransferStatus GSTransferStatus; extern void gsInterrupt(); extern int _GIFchain(); diff --git a/pcsx2/IPU/IPU.cpp b/pcsx2/IPU/IPU.cpp index dfb4f4db3c..4abbd82f2b 100644 --- a/pcsx2/IPU/IPU.cpp +++ b/pcsx2/IPU/IPU.cpp @@ -26,6 +26,7 @@ #include "yuv2rgb.h" #include "Vif.h" #include "Gif.h" +#include "Vif_Dma.h" // Zero cycle IRQ schedules aren't really good, but the IPU uses them. // Better to throw the IRQ inline: @@ -98,7 +99,7 @@ u8* readbits = _readbits; // always can decrement by one 1qw __forceinline void IPUProcessInterrupt() { - if (ipuRegs->ctrl.BUSY) IPUWorker(); + if (ipuRegs->ctrl.BUSY && g_BP.IFC) IPUWorker(); } __forceinline void init_g_decoder() @@ -138,6 +139,9 @@ int ipuInit() memzero(g_BP); init_g_decoder(); g_nDMATransfer.reset(); + IPU1Status.InProgress = false; + IPU1Status.DMAMode = DMA_MODE_NORMAL; + IPU1Status.DMAFinished = true; ipu_fifo.init(); ipu_cmd.clear(); return 0; @@ -147,6 +151,9 @@ void ipuReset() { memzero(*ipuRegs); g_nDMATransfer.reset(); + IPU1Status.InProgress = false; + IPU1Status.DMAMode = DMA_MODE_NORMAL; + IPU1Status.DMAFinished = true; } 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 } -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) { @@ -1269,216 +1246,207 @@ static __forceinline bool ipuDmacPartialChain(tDMA_TAG tag) } extern void gsInterrupt(); +extern void vif1Interrupt(); -static __forceinline bool ipuDmacSrcChain(DMACh *tag, tDMA_TAG *ptag) +static __forceinline void ipuDmacSrcChain() { - switch (ptag->ID) - { - case TAG_REFE: // refe - // do not change tadr - tag->madr = ptag[1]._u32; - return true; - break; - - case TAG_CNT: // cnt - tag->madr = tag->tadr + 16; - // Set the taddr to the next tag - tag->tadr += 16 + (tag->qwc << 4); - break; - - case TAG_NEXT: // next - tag->madr = tag->tadr + 16; - tag->tadr = ptag[1]._u32; - break; - - case TAG_REF: // ref - tag->madr = ptag[1]._u32; - tag->tadr += 16; - break; - - case TAG_END: // end - // do not change tadr - tag->madr = tag->tadr + 16; - return true; - break; - - default: - Console.Error("IPU ERROR: different transfer mode!, Please report to PCSX2 Team"); - break; - } - - return false; -} - -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) + + switch (IPU1Status.ChainMode) { - GIF_LOG("Flushing gif chcr %x tadr %x madr %x qwc %x", gif->chcr._u32, gif->tadr, gif->madr, gif->qwc); - gsInterrupt(); + case 0x0: // refe + //if(IPU1Status.InProgress == false) ipu1dma->tadr += 16; + break; + case 0x1: // cnt + // Set the taddr to the next tag + ipu1dma->tadr = ipu1dma->madr; + break; + + case 0x2: // next + ipu1dma->tadr = IPU1Status.NextMem; + break; + + /*case 0x3: // ref + if(IPU1Status.InProgress == false)ipu1dma->tadr += 16; + break;*/ + + case 0x7: // end + ipu1dma->tadr = ipu1dma->madr; + 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; +} + + 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); + 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 - flushGIF(); + if(!flushGIF()) return totalqwc; - // 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) + 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) { - 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) + case DMA_MODE_NORMAL: { - 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; + 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; - if (ipuDmacPartialChain(tag)) + case DMA_MODE_CHAIN: { - 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) + if(IPU1Status.InProgress == true) //No transfer is ready to go so we need to set one up { - ptag = dmaGetAddr(ipu1dma->tadr); - - ipuDmacPartialChain(ptag[0]); - - // Transfer the last of ptag into chcr. - ipu1dma->chcrTransfer(ptag); + 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(); + } + - 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; - } + if(IPU1Status.InProgress == false && IPU1Status.DMAFinished == false) //No transfer is ready to go so we need to set one up + { + u32 *ptag; - ipuDmacPartialChain(ptag[0]); - } - else - { - if (IPU1chain(totalqwc)) return totalqwc; - } + + 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; + + default: + Console.Error("IPU ERROR: different transfer mode!, Please report to PCSX2 Team"); + break; + } + + //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(); + } + + } + break; } - - IPU_INT_TO((ipu1cycles + totalqwc) * BIAS); + return totalqwc; } @@ -1526,12 +1494,15 @@ int IPU0dma() case NO_STD: break; case STD_GIF: // GIF + Console.Warning("GIFSTALL"); g_nDMATransfer.GIFSTALL = true; break; case STD_VIF1: // VIF + Console.Warning("VIFSTALL"); g_nDMATransfer.VIFSTALL = true; break; case STD_SIF1: + Console.Warning("SIFSTALL"); g_nDMATransfer.SIFSTALL = true; break; } @@ -1540,7 +1511,7 @@ int IPU0dma() //This was IPU_INT_FROM(readsize*BIAS ); //This broke vids in Digital Devil Saga //Note that interrupting based on totalsize is just guessing.. - IPU_INT_FROM(totalsize*BIAS ); + IPU_INT_FROM( totalsize * BIAS ); totalsize = 0; } @@ -1554,8 +1525,49 @@ __forceinline void dmaIPU0() // fromIPU __forceinline void dmaIPU1() // toIPU { - IPU1dma(); - if (ipuRegs->ctrl.BUSY) IPUWorker(); + + 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(); + 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(); @@ -1605,18 +1617,19 @@ void ipu0Interrupt() 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); - g_nDMATransfer.FIREINT1 = false; + //Console.Warning("IPU1 finishing when not finished!"); + IPU1dma(); + if (ipuRegs->ctrl.BUSY && g_BP.IFC) IPUWorker(); + return; } - if (g_nDMATransfer.TIE1) - g_nDMATransfer.TIE1 = false; - else - ipu1dma->chcr.STR = false; + //Console.Warning("Whoops"); + + ipu1dma->chcr.STR = false; hwDmacIrq(DMAC_TO_IPU); } diff --git a/pcsx2/IPU/IPU.h b/pcsx2/IPU/IPU.h index 0e08c8a358..5fa0e65bf9 100644 --- a/pcsx2/IPU/IPU.h +++ b/pcsx2/IPU/IPU.h @@ -43,6 +43,28 @@ #define ipumsk( src ) ( (src) & 0xff ) #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 // diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index f79eba2df5..15e5746e8e 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -252,7 +252,6 @@ void Pcsx2Config::GamefixOptions::LoadSave( IniInterface& ini ) IniScopedGroup path( ini, L"Gamefixes" ); IniBitBool( VuAddSubHack ); - IniBitBool( IpuSliceHack ); IniBitBool( VuClipFlagHack ); IniBitBool( FpuCompareHack ); IniBitBool( FpuMulHack ); diff --git a/pcsx2/Vif.cpp b/pcsx2/Vif.cpp index 7fa267910b..eec195c605 100644 --- a/pcsx2/Vif.cpp +++ b/pcsx2/Vif.cpp @@ -23,7 +23,7 @@ vifStruct vif0; vifStruct vif1; -Path3Modes Path3progress = STOPPED_MODE; +tGSTransferStatus GSTransferStatus = (STOPPED_MODE<<4) | (STOPPED_MODE<<2) | STOPPED_MODE; void vif0Init() { initNewVif(0); } void vif1Init() { initNewVif(1); } @@ -253,15 +253,23 @@ _f void vif1STAT(u32 value) { if (vif1Regs->stat.FDR) // Vif transferring to memory. { // 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; vif1.vifstalled = false; vif1.done = true; vif1Regs->stat.FQC = 0; - } + }*/ } #define caseVif(x) (idx ? VIF1_##x : VIF0_##x) diff --git a/pcsx2/Vif0_Dma.cpp b/pcsx2/Vif0_Dma.cpp index 4fe446f57a..0c5f563716 100644 --- a/pcsx2/Vif0_Dma.cpp +++ b/pcsx2/Vif0_Dma.cpp @@ -152,7 +152,7 @@ __forceinline void vif0Interrupt() // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. // Timesplitters 2 depends on this beeing a bit higher than 128. 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; } @@ -167,7 +167,7 @@ __forceinline void vif0Interrupt() if ((vif0.inprogress & 0x1) == 0) vif0SetupTransfer(); - CPU_INT(DMAC_VIF0, /*g_vifCycles*/ VifCycleVoodoo); + CPU_INT(DMAC_VIF0, g_vifCycles /*VifCycleVoodoo*/); return; } diff --git a/pcsx2/Vif1_Dma.cpp b/pcsx2/Vif1_Dma.cpp index b167be4573..6e5d7bac94 100644 --- a/pcsx2/Vif1_Dma.cpp +++ b/pcsx2/Vif1_Dma.cpp @@ -195,12 +195,12 @@ __forceinline void vif1Interrupt() VIF_LOG("vif1Interrupt: %8.8x", cpuRegs.cycle); g_vifCycles = 0; - + if (schedulepath3msk) Vif1MskPath3(); 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); return; @@ -240,8 +240,10 @@ __forceinline void vif1Interrupt() _VIF1chain(); // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. // 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; } @@ -256,7 +258,7 @@ __forceinline void vif1Interrupt() if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer(); - CPU_INT(DMAC_VIF1, /*g_vifCycles*/ VifCycleVoodoo); + CPU_INT(DMAC_VIF1, g_vifCycles /*VifCycleVoodoo*/); return; } @@ -280,7 +282,7 @@ __forceinline void vif1Interrupt() //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) //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() diff --git a/pcsx2/Vif_Codes.cpp b/pcsx2/Vif_Codes.cpp index d0dd399350..c44b312a8c 100644 --- a/pcsx2/Vif_Codes.cpp +++ b/pcsx2/Vif_Codes.cpp @@ -78,9 +78,9 @@ void Vif1MskPath3() { if (!vif1Regs->mskpath3) { //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; - CPU_INT(DMAC_GIF, 4); + if(gif->chcr.STR == true) CPU_INT(DMAC_GIF, 4); } else gifRegs->stat.M3P = true; @@ -111,7 +111,7 @@ template _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 //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!"); else DevCon.WriteLn("DirectHL: Waiting for Path3 to finish!");*/ @@ -125,7 +125,7 @@ template _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) { Registers::Freeze(); nVifStruct& v = nVif[1]; 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 (v.bSize) { // Last transfer was partial @@ -138,13 +138,14 @@ template _f int _vifCode_Direct(int pass, u8* data, bool isDirectHL) { const uint count = GetMTGS().PrepDataPacket(GIF_PATH_2, data, size >> 4); memcpy_fast(GetMTGS().GetDataPacketPtr(), data, count << 4); GetMTGS().SendDataPacket(); + if((count << 4) < size) Console.Warning("PATH2 end early, count %x, size %x", count << 4, size); vif1.tag.size = 0; vif1.cmd = 0; v.bSize = 0; gifRegs->stat.clear_flags(GIF_STAT_APATH2 | GIF_STAT_OPH); } 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); memcpy_fast(&v.buffer[v.bSize], data, size); v.bSize += size; @@ -180,7 +181,7 @@ vifOp(vifCode_FlushA) { vif1Only(); pass1 { // 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!"); vif1Regs->stat.VGW = true; vifX.vifstalled = true; diff --git a/pcsx2/Vif_Dma.h b/pcsx2/Vif_Dma.h index 315455f05a..13c8d19bb6 100644 --- a/pcsx2/Vif_Dma.h +++ b/pcsx2/Vif_Dma.h @@ -24,6 +24,18 @@ struct vifCode { 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 struct vifStruct { vifCode tag; @@ -36,6 +48,8 @@ struct vifStruct { bool done; bool vifstalled; 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 u32 savedtag; // need this for backwards compat with save states diff --git a/pcsx2/Vif_Transfer.cpp b/pcsx2/Vif_Transfer.cpp index e4c9425b59..26f58da1c1 100644 --- a/pcsx2/Vif_Transfer.cpp +++ b/pcsx2/Vif_Transfer.cpp @@ -74,6 +74,7 @@ _vifT void vifTransferLoop(u32* &data) { iBit = data[0] >> 31; 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--; if (analyzeIbit(data, iBit)) break; continue; diff --git a/pcsx2/gui/Panels/GameFixesPanel.cpp b/pcsx2/gui/Panels/GameFixesPanel.cpp index 0e1d7da958..e55522c10e 100644 --- a/pcsx2/gui/Panels/GameFixesPanel.cpp +++ b/pcsx2/gui/Panels/GameFixesPanel.cpp @@ -38,10 +38,6 @@ Panels::GameFixesPanel::GameFixesPanel( wxWindow* parent ) : _("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") }, - { - _("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!)"), wxEmptyString diff --git a/pcsx2/ps2/GIFpath.cpp b/pcsx2/ps2/GIFpath.cpp index d320fa2739..93b11c0b4f 100644 --- a/pcsx2/ps2/GIFpath.cpp +++ b/pcsx2/ps2/GIFpath.cpp @@ -236,7 +236,7 @@ __forceinline bool GIFPath::StepReg() 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 // 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) { 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) { // 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); incTag(16, 1); - if (pathidx == GIF_PATH_3) { - if (tag.FLG&2) Path3progress = IMAGE_MODE; - else Path3progress = TRANSFER_MODE; + //if (pathidx == GIF_PATH_3) { + switch(pathidx) + { + 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 { switch(tag.FLG) { case GIF_FLG_PACKED: + GIF_LOG("Packed Mode"); PrepPackedRegs(); do { if (GetReg() == 0xe) { @@ -328,6 +376,7 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size) break; case GIF_FLG_REGLIST: { + GIF_LOG("Reglist Mode"); size *= 2; 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_IMAGE2: { + GIF_LOG("IMAGE Mode"); int len = aMin(size, nloop); incTag(( len * 16 ), len); nloop -= len; @@ -357,14 +407,27 @@ __forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size) size = (startSize - size); - if (pathidx == GIF_PATH_3) { + 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->qwc -= size; - } - + } + return size; } diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index d34f30f4a1..0a0b3762e3 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -777,10 +777,6 @@ - - @@ -905,6 +901,18 @@ > + + + + + +