int IPU1dma() { 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(!ipu1ch.chcr.STR || IPU1Status.DMAMode == 2) { //We MUST stop the IPU from trying to fill the FIFO with more data if the DMA has been suspended //if we don't, we risk causing the data to go out of sync with the fifo and we end up losing some! //This is true for Dragons Quest 8 and probably others which suspend the DMA. DevCon.Warning("IPU1 running when IPU1 DMA disabled! CHCR %x QWC %x", ipu1ch.chcr._u32, ipu1ch.qwc); return 0; } IPU_LOG("IPU1 DMA Called QWC %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr); switch(IPU1Status.DMAMode) { case DMA_MODE_NORMAL: { IPU_LOG("Processing Normal QWC left %x Finished %d In Progress %d", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress); if(IPU1Status.InProgress) totalqwc += IPU1chain(); } break; case DMA_MODE_CHAIN: { if(IPU1Status.InProgress) //No transfer is ready to go so we need to set one up { IPU_LOG("Processing Chain QWC left %x Finished %d In Progress %d", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress); totalqwc += IPU1chain(); } if(!IPU1Status.InProgress && !IPU1Status.DMAFinished) //No transfer is ready to go so we need to set one up { tDMA_TAG* ptag = dmaGetAddr(ipu1ch.tadr, false); //Set memory pointer to TADR if (!ipu1ch.transfer("IPU1", ptag)) { return totalqwc; } ipu1ch.madr = ptag[1]._u32; ipu1cycles += 1; // Add 1 cycles from the QW read for the tag IPU1Status.ChainMode = ptag->ID; if(ipu1ch.chcr.TTE) DevCon.Warning("TTE?"); IPU1Status.DMAFinished = hwDmacSrcChain(ipu1ch, ptag->ID); if(ipu1ch.qwc > 0) IPU1Status.InProgress = true; IPU_LOG("dmaIPU1 dmaChain %8.8x_%8.8x size=%d, addr=%lx, fifosize=%x", ptag[1]._u32, ptag[0]._u32, ipu1ch.qwc, ipu1ch.madr, 8 - g_BP.IFC); if (ipu1ch.chcr.TIE && ptag->IRQ) //Tag Interrupt is set, so schedule the end/interrupt IPU1Status.DMAFinished = true; IPU_LOG("Processing Start Chain QWC left %x Finished %d In Progress %d", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress); totalqwc += IPU1chain(); //Set the TADR forward } } break; } //Do this here to prevent double settings on Chain DMA's if(totalqwc > 0 || ipu1ch.qwc == 0) { IPU_INT_TO(totalqwc * BIAS); IPUProcessInterrupt(); } else { cpuRegs.eCycle[4] = 0x9999;//IPU_INT_TO(2048); } IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr); return totalqwc; }
void IPU0dma() { if(!ipuRegs.ctrl.OFC) { IPU_INT_FROM( 64 ); IPUProcessInterrupt(); return; } int readsize; tDMA_TAG* pMem; if ((!(ipu0ch.chcr.STR) || (cpuRegs.interrupt & (1 << DMAC_FROM_IPU))) || (ipu0ch.qwc == 0)) { DevCon.Warning("How??"); return; } pxAssert(!(ipu0ch.chcr.TTE)); IPU_LOG("dmaIPU0 chcr = %lx, madr = %lx, qwc = %lx", ipu0ch.chcr._u32, ipu0ch.madr, ipu0ch.qwc); pxAssert(ipu0ch.chcr.MOD == NORMAL_MODE); pMem = dmaGetAddr(ipu0ch.madr, true); readsize = std::min(ipu0ch.qwc, (u16)ipuRegs.ctrl.OFC); ipu_fifo.out.read(pMem, readsize); ipu0ch.madr += readsize << 4; ipu0ch.qwc -= readsize; // note: qwc is u16 if (dmacRegs.ctrl.STS == STS_fromIPU) // STS == fromIPU { dmacRegs.stadr.ADDR = ipu0ch.madr; switch (dmacRegs.ctrl.STD) { case NO_STD: break; case STD_GIF: // GIF //DevCon.Warning("GIFSTALL"); g_nDMATransfer.GIFSTALL = true; break; case STD_VIF1: // VIF //DevCon.Warning("VIFSTALL"); g_nDMATransfer.VIFSTALL = true; break; case STD_SIF1: // DevCon.Warning("SIFSTALL"); g_nDMATransfer.SIFSTALL = true; break; } } //Fixme ( voodoocycles ): //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( readsize * BIAS ); if(ipuRegs.ctrl.IFC > 0) IPUProcessInterrupt(); //return readsize; }
int IPU1dma() { 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(ipu1dma.chcr.STR == false || IPU1Status.DMAMode == 2) { //We MUST stop the IPU from trying to fill the FIFO with more data if the DMA has been suspended //if we don't, we risk causing the data to go out of sync with the fifo and we end up losing some! //This is true for Dragons Quest 8 and probably others which suspend the DMA. DevCon.Warning("IPU1 running when IPU1 DMA disabled! CHCR %x QWC %x", ipu1dma.chcr._u32, ipu1dma.qwc); return 0; } IPU_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: { IPU_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 { IPU_LOG("Processing Chain QWC left %x Finished %d In Progress %d", ipu1dma.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress); totalqwc += IPU1chain(); //Set the TADR forward } if(IPU1Status.InProgress == false && IPU1Status.DMAFinished == false) //No transfer is ready to go so we need to set one up { tDMA_TAG* ptag = dmaGetAddr(ipu1dma.tadr, false); //Set memory pointer to TADR if (!ipu1dma.transfer("IPU1", ptag)) { return totalqwc; } ipu1cycles += 1; // Add 1 cycles from the QW read for the tag IPU1Status.ChainMode = ptag->ID; if(ipu1dma.chcr.TTE) DevCon.Warning("TTE?"); switch (IPU1Status.ChainMode) { case TAG_REFE: // refe // do not change tadr //ipu1dma.tadr += 16; ipu1dma.tadr += 16; ipu1dma.madr = ptag[1]._u32; IPU_LOG("Tag should end on %x", ipu1dma.tadr); break; case TAG_CNT: // cnt ipu1dma.tadr += 16; ipu1dma.madr = ipu1dma.tadr; IPU_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 TAG_NEXT: // next ipu1dma.madr = ipu1dma.tadr + 16; IPU1Status.NextMem = ptag[1]._u32; IPU_LOG("Tag should end on %x", IPU1Status.NextMem); //IPU1Status.DMAFinished = false; break; case TAG_REF: // ref ipu1dma.madr = ptag[1]._u32; ipu1dma.tadr += 16; IPU_LOG("Tag should end on %x", ipu1dma.tadr); //IPU1Status.DMAFinished = false; break; case TAG_END: // end // do not change tadr ipu1dma.madr = ipu1dma.tadr + 16; //ipu1dma.tadr += 16; IPU_LOG("Tag should end on %x", ipu1dma.madr + ipu1dma.qwc * 16); break; default: DevCon.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; 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); if (ipu1dma.chcr.TIE && ptag->IRQ) //Tag Interrupt is set, so schedule the end/interrupt IPU1Status.DMAFinished = true; IPU_LOG("Processing Start Chain QWC left %x Finished %d In Progress %d", ipu1dma.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress); totalqwc += IPU1chain(); //Set the TADR forward } } break; } //Do this here to prevent double settings on Chain DMA's if(totalqwc > 0 || ipu1dma.qwc == 0) { IPU_INT_TO(totalqwc * BIAS); IPUProcessInterrupt(); } else { cpuRegs.eCycle[4] = 0x9999;//IPU_INT_TO(2048); } IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1dma.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1dma.tadr); return totalqwc; }