__fi void dmaIPU1() // toIPU { IPU_LOG("IPU1DMAStart QWC %x, MADR %x, CHCR %x, TADR %x", ipu1ch.qwc, ipu1ch.madr, ipu1ch.chcr._u32, ipu1ch.tadr); if (ipu1ch.pad != 0) { // Note: pad is the padding right above qwc, so we're testing whether qwc // has overflowed into pad. DevCon.Warning(L"IPU1dma's upper 16 bits set to %x\n", ipu1ch.pad); ipu1ch.qwc = ipu1ch.pad = 0; // If we are going to clear down IPU1, we should end it too. // Going to test this scenario on the PS2 mind - Refraction ipu1ch.chcr.STR = false; hwDmacIrq(DMAC_TO_IPU); } if (ipu1ch.chcr.MOD == NORMAL_MODE && ipu1ch.qwc == 0) //avoids freeze when IPU1 Normal error is triggered { /*ipu1ch.chcr.STR = false; // Hack to force stop IPU ipuRegs.cmd.BUSY = 0; ipuRegs.ctrl.BUSY = 0; ipuRegs.topbusy = 0; // hwDmacIrq(DMAC_TO_IPU);*/ IPU_LOG("IPU1 Normal error fix"); ipu1ch.qwc = 1; } if (ipu1ch.chcr.MOD == CHAIN_MODE) //Chain Mode { IPU_LOG("Setting up IPU1 Chain mode"); if(ipu1ch.qwc == 0) { IPU1Status.InProgress = false; IPU1Status.DMAFinished = false; } else { //Attempting to continue a previous chain IPU_LOG("Resuming DMA TAG %x", (ipu1ch.chcr.TAG >> 12)); //We MUST check the CHCR for the tag it last knew, it can be manipulated! IPU1Status.ChainMode = (ipu1ch.chcr.TAG >> 12) & 0x7; IPU1Status.InProgress = true; if ((ipu1ch.chcr.tag().ID == TAG_REFE) || (ipu1ch.chcr.tag().ID == TAG_END) || (ipu1ch.chcr.tag().IRQ && ipu1ch.chcr.TIE)) { IPU1Status.DMAFinished = true; } else { IPU1Status.DMAFinished = false; } } IPU1Status.DMAMode = DMA_MODE_CHAIN; IPU1dma(); }
void SPRTOinterrupt() { SPR_LOG("SPR1 Interrupt"); if (!spr1finished || spr1ch.qwc > 0) { _dmaSPR1(); return; } DMA_LOG("SPR1 DMA End"); spr1ch.chcr.STR = false; spr1lastqwc = false; hwDmacIrq(DMAC_TO_SPR); }
void SPRFROMinterrupt() { if (!spr0finished || spr0ch.qwc > 0) { _dmaSPR0(); //the qwc check is simply because having data still to transfer from the packet can freak games out if they do a d.tadr == s.madr check //and there is still data to come over (FF12 ingame menu) if(mfifotransferred != 0 && spr0ch.qwc == 0) { switch (dmacRegs.ctrl.MFD) { case MFD_VIF1: // Most common case. { if ((spr0ch.madr & ~dmacRegs.rbsr.RMSK) != dmacRegs.rbor.ADDR) Console.WriteLn("VIF MFIFO Write outside MFIFO area"); spr0ch.madr = dmacRegs.rbor.ADDR + (spr0ch.madr & dmacRegs.rbsr.RMSK); //Console.WriteLn("mfifoVIF1transfer %x madr %x, tadr %x", vif1ch.chcr._u32, vif1ch.madr, vif1ch.tadr); mfifoVIF1transfer(mfifotransferred); mfifotransferred = 0; break; } case MFD_GIF: { if ((spr0ch.madr & ~dmacRegs.rbsr.RMSK) != dmacRegs.rbor.ADDR) Console.WriteLn("GIF MFIFO Write outside MFIFO area"); spr0ch.madr = dmacRegs.rbor.ADDR + (spr0ch.madr & dmacRegs.rbsr.RMSK); //Console.WriteLn("mfifoGIFtransfer %x madr %x, tadr %x", gif->chcr._u32, gif->madr, gif->tadr); mfifoGIFtransfer(mfifotransferred); mfifotransferred = 0; break; } default: break; } } return; } spr0lastqwc = false; spr0ch.chcr.STR = false; hwDmacIrq(DMAC_FROM_SPR); DMA_LOG("SPR0 DMA End"); }
__fi void dmaIPU0() // fromIPU { if (ipu0ch.pad != 0) { // Note: pad is the padding right above qwc, so we're testing whether qwc // has overflowed into pad. DevCon.Warning(L"IPU0dma's upper 16 bits set to %x", ipu0ch.pad); ipu0ch.qwc = ipu0ch.pad = 0; //If we are going to clear down IPU0, we should end it too. Going to test this scenario on the PS2 mind - Refraction ipu0ch.chcr.STR = false; hwDmacIrq(DMAC_FROM_IPU); } //if (dmacRegs.ctrl.STS == STS_fromIPU) DevCon.Warning("DMA Stall enabled on IPU0"); IPU_INT_FROM( 64 ); }
__fi void dmaIPU1() // toIPU { IPU_LOG("IPU1DMAStart QWC %x, MADR %x, CHCR %x, TADR %x", ipu1dma.qwc, ipu1dma.madr, ipu1dma.chcr._u32, ipu1dma.tadr); if (ipu1dma.pad != 0) { // Note: pad is the padding right above qwc, so we're testing whether qwc // has overflowed into pad. DevCon.Warning(L"IPU1dma's upper 16 bits set to %x\n", ipu1dma.pad); ipu1dma.qwc = ipu1dma.pad = 0; // If we are going to clear down IPU1, we should end it too. // Going to test this scenario on the PS2 mind - Refraction ipu1dma.chcr.STR = false; hwDmacIrq(DMAC_TO_IPU); } if (ipu1dma.chcr.MOD == CHAIN_MODE) //Chain Mode { IPU_LOG("Setting up IPU1 Chain mode"); if(ipu1dma.qwc == 0) { IPU1Status.InProgress = false; IPU1Status.DMAFinished = false; } else { //Attempting to continue a previous chain IPU_LOG("Resuming DMA TAG %x", (ipu1dma.chcr.TAG >> 12)); //We MUST check the CHCR for the tag it last knew, it can be manipulated! IPU1Status.ChainMode = (ipu1dma.chcr.TAG >> 12) & 0x7; IPU1Status.InProgress = true; IPU1Status.DMAFinished = ((ipu1dma.chcr.TAG >> 15) && ipu1dma.chcr.TIE) ? true : false; } IPU1Status.DMAMode = DMA_MODE_CHAIN; IPU1dma(); }
__fi void EEsif2Interrupt() { hwDmacIrq(DMAC_SIF2); sif2dma.chcr.STR = false; }
__fi void EEsif1Interrupt() { hwDmacIrq(DMAC_SIF1); sif1dma.chcr.STR = false; }
void GIFdma() { tDMA_TAG *ptag; gscycles = prevcycles; if (gifRegs.ctrl.PSE) { // temporarily stop Console.WriteLn("Gif dma temp paused? (non MFIFO GIF)"); GifDMAInt(16); return; } if ((dmacRegs.ctrl.STD == STD_GIF) && (prevcycles != 0)) { //Console.WriteLn("GS Stall Control Source = %x, Drain = %x\n MADR = %x, STADR = %x", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3, gifch.madr, psHu32(DMAC_STADR)); if ((gifch.madr + (gifch.qwc * 16)) > dmacRegs.stadr.ADDR) { GifDMAInt(4); gscycles = 0; return; } prevcycles = 0; gifch.qwc = 0; } if ((gifch.chcr.MOD == CHAIN_MODE) && (!gspath3done) && gifch.qwc == 0) // Chain Mode { ptag = ReadTag(); if (ptag == NULL) return; //DevCon.Warning("GIF Reading Tag MSK = %x", vif1Regs.mskpath3); GIF_LOG("gifdmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx tadr=%lx", ptag[1]._u32, ptag[0]._u32, gifch.qwc, ptag->ID, gifch.madr, gifch.tadr); if (!CHECK_GIFFIFOHACK)gifRegs.stat.FQC = std::min((u16)0x10, gifch.qwc);// FQC=31, hack ;) (for values of 31 that equal 16) [ used to be 0xE00; // APATH=3] if (dmacRegs.ctrl.STD == STD_GIF) { // there are still bugs, need to also check if gifch.madr +16*qwc >= stadr, if not, stall if ((ptag->ID == TAG_REFS) && ((gifch.madr + (gifch.qwc * 16)) > dmacRegs.stadr.ADDR)) { // stalled. // We really need to test this. Pay attention to prevcycles, as it used to trigger GIFchains in the code above. (rama) //Console.WriteLn("GS Stall Control start Source = %x, Drain = %x\n MADR = %x, STADR = %x", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3,gifch.madr, psHu32(DMAC_STADR)); prevcycles = gscycles; gifch.tadr -= 16; gifch.qwc = 0; hwDmacIrq(DMAC_STALL_SIS); GifDMAInt(gscycles); gscycles = 0; return; } } checkTieBit(ptag); } else if (dmacRegs.ctrl.STD == STD_GIF && gifch.chcr.MOD == NORMAL_MODE) { Console.WriteLn("GIF DMA Stall in Normal mode not implemented - Report which game to PCSX2 Team"); } if (!CHECK_GIFFIFOHACK) { gifRegs.stat.FQC = std::min((u16)0x10, gifch.qwc);// FQC=31, hack ;) (for values of 31 that equal 16) [ used to be 0xE00; // APATH=3] clearFIFOstuff(true); } // Transfer Dn_QWC from Dn_MADR to GIF if (gifch.qwc > 0) // Normal Mode { if (CheckPaths() == false) return; GIFchain(); //Transfers the data set by the switch //if (gscycles < 8) DevCon.Warning("GSCycles = %d", gscycles); GifDMAInt(gscycles); return; } else if(!gspath3done) GIFdma(); //Loop round if there was a blank tag, causes hell otherwise with P3 masking games. //QWC == 0 && gspath3done == true - End of DMA prevcycles = 0; //if (gscycles < 8) DevCon.Warning("1 GSCycles = %d", gscycles); GifDMAInt(16); }
__fi void gifInterrupt() { GIF_LOG("gifInterrupt caught!"); gifCheckPathStatus(); if(gifUnit.gifPath[GIF_PATH_3].state == GIF_PATH_IDLE) { if(vif1Regs.stat.VGW) { //Check if VIF is in a cycle or is currently "idle" waiting for GIF to come back. if(!(cpuRegs.interrupt & (1<<DMAC_VIF1))) CPU_INT(DMAC_VIF1, 1); //Make sure it loops if the GIF packet is empty to prepare for the next packet //or end if it was the end of a packet. //This must trigger after VIF retriggers as VIf might instantly mask Path3 if (!gifUnit.Path3Masked() || gifch.qwc == 0) { GifDMAInt(16); } return; } } if (dmacRegs.ctrl.MFD == MFD_GIF) { // GIF MFIFO //Console.WriteLn("GIF MFIFO"); gifMFIFOInterrupt(); return; } if (CHECK_GIFFIFOHACK) { if (int amtRead = gif_fifo.read(true)) { if (!gifUnit.Path3Masked() || gifRegs.stat.FQC < 16) { GifDMAInt(amtRead * BIAS); return; } } else { if (!gifUnit.CanDoPath3() && gifRegs.stat.FQC == 16) { if (gifch.qwc > 0 || gspath3done == false) { if (!gifUnit.Path3Masked()) { GifDMAInt(128); } return; } } } } if (gifUnit.gsSIGNAL.queued) { GIF_LOG("Path 3 Paused"); GifDMAInt(128); return; } if (!(gifch.chcr.STR)) return; if ((gifch.qwc > 0) || (!gspath3done)) { if (!dmacRegs.ctrl.DMAE) { Console.Warning("gs dma masked, re-scheduling..."); // re-raise the int shortly in the future GifDMAInt( 64 ); return; } GIFdma(); return; } //Double check as we might have read the fifo as it's ending the DMA gifCheckPathStatus(); if (gifUnit.gifPath[GIF_PATH_3].state == GIF_PATH_IDLE) { if (vif1Regs.stat.VGW) { //Check if VIF is in a cycle or is currently "idle" waiting for GIF to come back. if (!(cpuRegs.interrupt & (1 << DMAC_VIF1))) { CPU_INT(DMAC_VIF1, 1); } } } if (!CHECK_GIFFIFOHACK) { gifRegs.stat.FQC = 0; clearFIFOstuff(false); } gscycles = 0; gspath3done = false; gifch.chcr.STR = false; hwDmacIrq(DMAC_GIF); GIF_LOG("GIF DMA End QWC in fifo %x APATH = %x OPH = %x state = %x", gifRegs.stat.FQC, gifRegs.stat.APATH, gifRegs.stat.OPH, gifUnit.gifPath[GIF_PATH_3].state); }
__fi void gifInterrupt() { GIF_LOG("gifInterrupt caught!"); if( gifRegs.stat.APATH == 3 ) { gifRegs.stat.APATH = 0; gifRegs.stat.OPH = 0; if(gifUnit.gifPath[GIF_PATH_3].state == GIF_PATH_IDLE || gifUnit.gifPath[GIF_PATH_3].state == GIF_PATH_WAIT) { if(gifUnit.checkPaths(1,1,0)) gifUnit.Execute(false, true); } } //Required for Path3 Masking timing! if(gifUnit.gifPath[GIF_PATH_3].state == GIF_PATH_WAIT) gifUnit.gifPath[GIF_PATH_3].state = GIF_PATH_IDLE; if(gifUnit.gifPath[GIF_PATH_3].state == GIF_PATH_IDLE) { if(vif1Regs.stat.VGW) { //Check if VIF is in a cycle or is currently "idle" waiting for GIF to come back. if(!(cpuRegs.interrupt & (1<<DMAC_VIF1))) CPU_INT(DMAC_VIF1, 1); //Make sure it loops if the GIF packet is empty to prepare for the next packet //or end if it was the end of a packet. if(!gifUnit.Path3Masked() || gifch.qwc == 0) CPU_INT(DMAC_GIF, 16); return; } } if (dmacRegs.ctrl.MFD == MFD_GIF) { // GIF MFIFO //Console.WriteLn("GIF MFIFO"); gifMFIFOInterrupt(); return; } if (gifUnit.gsSIGNAL.queued) { //DevCon.WriteLn("Path 3 Paused"); CPU_INT(DMAC_GIF, 128); return; } if (!(gifch.chcr.STR)) return; if ((gifch.qwc > 0) || (!gspath3done)) { if (!dmacRegs.ctrl.DMAE) { Console.Warning("gs dma masked, re-scheduling..."); // re-raise the int shortly in the future CPU_INT( DMAC_GIF, 64 ); return; } GIFdma(); return; } gifRegs.stat.FQC = 0; gscycles = 0; gspath3done = false; gifch.chcr.STR = false; clearFIFOstuff(false); hwDmacIrq(DMAC_GIF); DMA_LOG("GIF DMA End"); }
__fi void EEsif0Interrupt() { hwDmacIrq(DMAC_SIF0); sif0dma.chcr.STR = false; }
__fi void vif1Interrupt() { VIF_LOG("vif1Interrupt: %8.8x chcr %x, done %x, qwc %x", cpuRegs.cycle, vif1ch.chcr._u32, vif1.done, vif1ch.qwc); g_vif1Cycles = 0; if( gifRegs.stat.APATH == 2 && gifUnit.gifPath[GIF_PATH_2].isDone()) { gifRegs.stat.APATH = 0; gifRegs.stat.OPH = 0; vif1Regs.stat.VGW = false; //Let vif continue if it's stuck on a flush if(gifUnit.checkPaths(1,0,1)) gifUnit.Execute(false, true); } //Some games (Fahrenheit being one) start vif first, let it loop through blankness while it sets MFIFO mode, so we need to check it here. if (dmacRegs.ctrl.MFD == MFD_VIF1) { //Console.WriteLn("VIFMFIFO\n"); // Test changed because the Final Fantasy 12 opening somehow has the tag in *Undefined* mode, which is not in the documentation that I saw. if (vif1ch.chcr.MOD == NORMAL_MODE) Console.WriteLn("MFIFO mode is normal (which isn't normal here)! %x", vif1ch.chcr._u32); vif1Regs.stat.FQC = min((u16)0x10, vif1ch.qwc); vifMFIFOInterrupt(); return; } // We need to check the direction, if it is downloading // from the GS then we handle that separately (KH2 for testing) if (vif1ch.chcr.DIR) { bool isDirect = (vif1.cmd & 0x7f) == 0x50; bool isDirectHL = (vif1.cmd & 0x7f) == 0x51; if((isDirect && !gifUnit.CanDoPath2()) || (isDirectHL && !gifUnit.CanDoPath2HL())) { GUNIT_WARN("vif1Interrupt() - Waiting for Path 2 to be ready"); CPU_INT(DMAC_VIF1, 128); if(gifRegs.stat.APATH == 3) vif1Regs.stat.VGW = 1; //We're waiting for path 3. Gunslinger II return; } vif1Regs.stat.VGW = 0; //Path 3 isn't busy so we don't need to wait for it. vif1Regs.stat.FQC = min(vif1ch.qwc, (u16)16); //Simulated GS transfer time done, clear the flags } if(vif1.waitforvu == true) { //DevCon.Warning("Waiting on VU1"); //CPU_INT(DMAC_VIF1, 16); return; } if (!vif1ch.chcr.STR) Console.WriteLn("Vif1 running when CHCR == %x", vif1ch.chcr._u32); if (vif1.irq && vif1.tag.size == 0 &&vif1.cmd == 0) { VIF_LOG("VIF IRQ Firing"); vif1Regs.stat.INT = true; hwIntcIrq(VIF1intc); --vif1.irq; if (vif1Regs.stat.test(VIF1_STAT_VSS | VIF1_STAT_VIS | VIF1_STAT_VFS)) { //vif1Regs.stat.FQC = 0; //NFSHPS stalls when the whole packet has gone across (it stalls in the last 32bit cmd) //In this case VIF will end vif1Regs.stat.FQC = min((u16)0x10, vif1ch.qwc); if((vif1ch.qwc > 0 || !vif1.done) && !CHECK_VIF1STALLHACK) { VIF_LOG("VIF1 Stalled"); return; } } } vif1.vifstalled.enabled = false; //Mirroring change to VIF0 if (vif1.cmd) { if (vif1.done && (vif1ch.qwc == 0)) vif1Regs.stat.VPS = VPS_WAITING; } else { vif1Regs.stat.VPS = VPS_IDLE; } if (vif1.inprogress & 0x1) { _VIF1chain(); // VIF_NORMAL_FROM_MEM_MODE is a very slow operation. // Timesplitters 2 depends on this beeing a bit higher than 128. if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = min(vif1ch.qwc, (u16)16); if(!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!) CPU_INT(DMAC_VIF1, g_vif1Cycles); return; } if (!vif1.done) { if (!(dmacRegs.ctrl.DMAE)) { Console.WriteLn("vif1 dma masked"); return; } if ((vif1.inprogress & 0x1) == 0) vif1SetupTransfer(); if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = min(vif1ch.qwc, (u16)16); if(!(vif1Regs.stat.VGW && gifUnit.gifPath[GIF_PATH_3].state != GIF_PATH_IDLE)) //If we're waiting on GIF, stop looping, (can be over 1000 loops!) CPU_INT(DMAC_VIF1, g_vif1Cycles); return; } if (vif1.vifstalled.enabled && vif1.done) { DevCon.WriteLn("VIF1 looping on stall at end\n"); CPU_INT(DMAC_VIF1, 0); return; //Dont want to end if vif is stalled. } #ifdef PCSX2_DEVBUILD if (vif1ch.qwc > 0) Console.WriteLn("VIF1 Ending with %x QWC left", vif1ch.qwc); if (vif1.cmd != 0) Console.WriteLn("vif1.cmd still set %x tag size %x", vif1.cmd, vif1.tag.size); #endif if((vif1ch.chcr.DIR == VIF_NORMAL_TO_MEM_MODE) && vif1.GSLastDownloadSize <= 16) { //Reverse fifo has finished and nothing is left, so lets clear the outputting flag gifRegs.stat.OPH = false; } if (vif1ch.chcr.DIR) vif1Regs.stat.FQC = min(vif1ch.qwc, (u16)16); vif1ch.chcr.STR = false; vif1.vifstalled.enabled = false; vif1.irqoffset.enabled = false; if(vif1.queued_program == true) vifExecQueue(1); g_vif1Cycles = 0; DMA_LOG("VIF1 DMA End"); hwDmacIrq(DMAC_VIF1); }
__fi void vif1SetupTransfer() { tDMA_TAG *ptag; switch (vif1.dmamode) { case VIF_CHAIN_MODE: ptag = dmaGetAddr(vif1ch.tadr, false); //Set memory pointer to TADR if (!(vif1ch.transfer("Vif1 Tag", ptag))) return; vif1ch.madr = ptag[1]._u32; //MADR = ADDR field + SPR g_vif1Cycles += 1; // Add 1 g_vifCycles from the QW read for the tag VIF_LOG("VIF1 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx", ptag[1]._u32, ptag[0]._u32, vif1ch.qwc, ptag->ID, vif1ch.madr, vif1ch.tadr); if (!vif1.done && ((dmacRegs.ctrl.STD == STD_VIF1) && (ptag->ID == TAG_REFS))) // STD == VIF1 { // there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall if ((vif1ch.madr + vif1ch.qwc * 16) >= dmacRegs.stadr.ADDR) { // stalled hwDmacIrq(DMAC_STALL_SIS); return; } } vif1.inprogress &= ~1; if (vif1ch.chcr.TTE) { // Transfer dma tag if tte is set bool ret; static __aligned16 u128 masked_tag; masked_tag._u64[0] = 0; masked_tag._u64[1] = *((u64*)ptag + 1); VIF_LOG("\tVIF1 SrcChain TTE=1, data = 0x%08x.%08x", masked_tag._u32[3], masked_tag._u32[2]); if (vif1.irqoffset.enabled) { ret = VIF1transfer((u32*)&masked_tag + vif1.irqoffset.value, 4 - vif1.irqoffset.value, true); //Transfer Tag on stall //ret = VIF1transfer((u32*)ptag + (2 + vif1.irqoffset), 2 - vif1.irqoffset); //Transfer Tag on stall } else { //Some games (like killzone) do Tags mid unpack, the nops will just write blank data //to the VU's, which breaks stuff, this is where the 128bit packet will fail, so we ignore the first 2 words vif1.irqoffset.value = 2; vif1.irqoffset.enabled = true; ret = VIF1transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag //ret = VIF1transfer((u32*)ptag + 2, 2); //Transfer Tag } if (!ret && vif1.irqoffset.enabled) { vif1.inprogress &= ~1; //Better clear this so it has to do it again (Jak 1) return; //IRQ set by VIFTransfer } } vif1.irqoffset.value = 0; vif1.irqoffset.enabled = false; vif1.done |= hwDmacSrcChainWithStack(vif1ch, ptag->ID); if(vif1ch.qwc > 0) vif1.inprogress |= 1; //Check TIE bit of CHCR and IRQ bit of tag if (vif1ch.chcr.TIE && ptag->IRQ) { VIF_LOG("dmaIrq Set"); //End Transfer vif1.done = true; return; } break; } }
__fi void vif0Interrupt() { VIF_LOG("vif0Interrupt: %8.8x", cpuRegs.cycle); g_vif0Cycles = 0; vif0Regs.stat.FQC = min(vif0ch.qwc, (u16)8); if (!(vif0ch.chcr.STR)) Console.WriteLn("vif0 running when CHCR == %x", vif0ch.chcr._u32); if (vif0.irq && vif0.tag.size == 0 && vif0.cmd == 0) { vif0Regs.stat.INT = true; hwIntcIrq(VIF0intc); --vif0.irq; if (vif0Regs.stat.test(VIF0_STAT_VSS | VIF0_STAT_VIS | VIF0_STAT_VFS)) { //vif0Regs.stat.FQC = 0; // One game doesn't like vif stalling at end, can't remember what. Spiderman isn't keen on it tho //vif0ch.chcr.STR = false; vif0Regs.stat.FQC = min((u16)0x8, vif0ch.qwc); if(vif0ch.qwc > 0 || !vif0.done) { VIF_LOG("VIF0 Stalled"); return; } } } if(vif0.waitforvu == true) { //DevCon.Warning("Waiting on VU0"); //CPU_INT(DMAC_VIF0, 16); return; } vif0.vifstalled.enabled = false; //Must go after the Stall, incase it's still in progress, GTC africa likes to see it still transferring. if (vif0.cmd) { if(vif0.done == true && vif0ch.qwc == 0) vif0Regs.stat.VPS = VPS_WAITING; } else { vif0Regs.stat.VPS = VPS_IDLE; } if (vif0.inprogress & 0x1) { _VIF0chain(); vif0Regs.stat.FQC = min(vif0ch.qwc, (u16)8); CPU_INT(DMAC_VIF0, g_vif0Cycles); return; } if (!vif0.done) { if (!(dmacRegs.ctrl.DMAE)) { Console.WriteLn("vif0 dma masked"); return; } if ((vif0.inprogress & 0x1) == 0) vif0SetupTransfer(); vif0Regs.stat.FQC = min(vif0ch.qwc, (u16)8); CPU_INT(DMAC_VIF0, g_vif0Cycles); return; } if (vif0.vifstalled.enabled && vif0.done) { DevCon.WriteLn("VIF0 looping on stall at end\n"); CPU_INT(DMAC_VIF0, 0); return; //Dont want to end if vif is stalled. } #ifdef PCSX2_DEVBUILD if (vif0ch.qwc > 0) Console.WriteLn("vif0 Ending with %x QWC left"); if (vif0.cmd != 0) Console.WriteLn("vif0.cmd still set %x tag size %x", vif0.cmd, vif0.tag.size); #endif vif0ch.chcr.STR = false; vif0Regs.stat.FQC = min((u16)0x8, vif0ch.qwc); vif0.vifstalled.enabled = false; vif0.irqoffset.enabled = false; if(vif0.queued_program == true) vifExecQueue(0); g_vif0Cycles = 0; hwDmacIrq(DMAC_VIF0); vif0Regs.stat.FQC = 0; DMA_LOG("VIF0 DMA End"); }