void vif1TransferToMemory() { u128* pMem = (u128*)dmaGetAddr(vif1ch.madr, false); // VIF from gsMemory if (pMem == NULL) { // Is vif0ptag empty? Console.WriteLn("Vif1 Tag BUSERR"); dmacRegs.stat.BEIS = true; // Bus Error vif1Regs.stat.FQC = 0; vif1ch.qwc = 0; vif1.done = true; CPU_INT(DMAC_VIF1, 0); return; // An error has occurred. } // MTGS concerns: The MTGS is inherently disagreeable with the idea of downloading // stuff from the GS. The *only* way to handle this case safely is to flush the GS // completely and execute the transfer there-after. //Console.Warning("Real QWC %x", vif1ch.qwc); const u32 size = min(vif1.GSLastDownloadSize, (u32)vif1ch.qwc); const u128* pMemEnd = vif1.GSLastDownloadSize + pMem; if (size) { // Checking if any crazy game does a partial // gs primitive and then does a gs download... Gif_Path& p1 = gifUnit.gifPath[GIF_PATH_1]; Gif_Path& p2 = gifUnit.gifPath[GIF_PATH_2]; Gif_Path& p3 = gifUnit.gifPath[GIF_PATH_3]; pxAssert(p1.isDone() || !p1.gifTag.isValid); pxAssert(p2.isDone() || !p2.gifTag.isValid); pxAssert(p3.isDone() || !p3.gifTag.isValid); } GetMTGS().WaitGS(); GSreadFIFO2((u64*)pMem, size); pMem += size; if(pMem < pMemEnd) { //DevCon.Warning("GS Transfer < VIF QWC, Clearing end of space"); __m128 zeroreg = _mm_setzero_ps(); do { _mm_store_ps((float*)pMem, zeroreg); } while (++pMem < pMemEnd); } g_vif1Cycles += vif1ch.qwc * 2; vif1ch.madr += vif1ch.qwc * 16; // mgs3 scene changes if (vif1.GSLastDownloadSize >= vif1ch.qwc) { vif1.GSLastDownloadSize -= vif1ch.qwc; vif1Regs.stat.FQC = min((u32)16, vif1.GSLastDownloadSize); } else { vif1Regs.stat.FQC = 0; vif1.GSLastDownloadSize = 0; } vif1ch.qwc = 0; }
static __fi int IPU1chain() { int totalqwc = 0; if (ipu1ch.qwc > 0 && IPU1Status.InProgress) { int qwc = ipu1ch.qwc; u32 *pMem; pMem = (u32*)dmaGetAddr(ipu1ch.madr, false); if (pMem == NULL) { Console.Error("ipu1dma NULL!"); return totalqwc; } //Write our data to the fifo qwc = ipu_fifo.in.write(pMem, qwc); ipu1ch.madr += qwc << 4; ipu1ch.qwc -= qwc; totalqwc += qwc; } //Update TADR etc hwDmacSrcTadrInc(ipu1ch); if( ipu1ch.qwc == 0) IPU1Status.InProgress = false; return totalqwc; }
static __fi int IPU1chain() { int totalqwc = 0; if (ipu1dma.qwc > 0 && IPU1Status.InProgress == true) { int qwc = ipu1dma.qwc; u32 *pMem; pMem = (u32*)dmaGetAddr(ipu1dma.madr, false); 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; } //Update TADR etc if(IPU1Status.DMAMode == DMA_MODE_CHAIN) ipuDmacSrcChain(); if( ipu1dma.qwc == 0) IPU1Status.InProgress = false; return totalqwc; }
bool _VIF0chain() { u32 *pMem; if (vif0ch.qwc == 0) { vif0.inprogress = 0; return true; } pMem = (u32*)dmaGetAddr(vif0ch.madr, false); if (pMem == NULL) { vif0.cmd = 0; vif0.tag.size = 0; vif0ch.qwc = 0; return true; } VIF_LOG("VIF0chain size=%d, madr=%lx, tadr=%lx", vif0ch.qwc, vif0ch.madr, vif0ch.tadr); if (vif0.irqoffset.enabled) return VIF0transfer(pMem + vif0.irqoffset.value, vif0ch.qwc * 4 - vif0.irqoffset.value); else return VIF0transfer(pMem, vif0ch.qwc * 4); }
static __fi tDMA_TAG* ReadTag2() { tDMA_TAG* ptag = dmaGetAddr(gifch.tadr, false); //Set memory pointer to TADR gifch.unsafeTransfer(ptag); gifch.madr = ptag[1]._u32; gspath3done = hwDmacSrcChainWithStack(gifch, ptag->ID); return ptag; }
static __fi tDMA_TAG* ReadTag() { tDMA_TAG* ptag = dmaGetAddr(gifch.tadr, false); //Set memory pointer to TADR if (!(gifch.transfer("Gif", ptag))) return NULL; gifch.madr = ptag[1]._u32; //MADR = ADDR field + SPR gscycles += 2; // Add 1 cycles from the QW read for the tag gspath3done = hwDmacSrcChainWithStack(gifch, ptag->ID); return ptag; }
tDMA_TAG *DMACh::getAddr(u32 addr, u32 num, bool write) { tDMA_TAG *ptr = dmaGetAddr(addr, write); if (ptr == NULL) { throwBusError("dmaGetAddr"); setDmacStat(num); chcr.STR = false; } return ptr; }
int _GIFchain() { tDMA_TAG *pMem; pMem = dmaGetAddr(gifch.madr, false); if (pMem == NULL) { //must increment madr and clear qwc, else it loops gifch.madr += gifch.qwc * 16; gifch.qwc = 0; Console.Warning("Hackfix - NULL GIFchain"); return -1; } return WRITERING_DMA((u32*)pMem, gifch.qwc); }
int _GIFchain() { tDMA_TAG *pMem; pMem = dmaGetAddr(gifch.madr, false); if (pMem == NULL) { #if USE_OLD_GIF == 1 // d // reset path3, fixes dark cloud 2 GIFPath_Clear( GIF_PATH_3 ); #endif //must increment madr and clear qwc, else it loops gifch.madr += gifch.qwc * 16; gifch.qwc = 0; Console.Warning("Hackfix - NULL GIFchain"); return -1; } return WRITERING_DMA((u32*)pMem, gifch.qwc); }
bool _VIF1chain() { u32 *pMem; if (vif1ch.qwc == 0) { vif1.inprogress &= ~1; vif1.irqoffset.value = 0; vif1.irqoffset.enabled = false; return true; } // Clarification - this is TO memory mode, for some reason i used the other way round >.< if (vif1.dmamode == VIF_NORMAL_TO_MEM_MODE) { vif1TransferToMemory(); vif1.inprogress &= ~1; return true; } pMem = (u32*)dmaGetAddr(vif1ch.madr, !vif1ch.chcr.DIR); if (pMem == NULL) { vif1.cmd = 0; vif1.tag.size = 0; vif1ch.qwc = 0; return true; } VIF_LOG("VIF1chain size=%d, madr=%lx, tadr=%lx", vif1ch.qwc, vif1ch.madr, vif1ch.tadr); if (vif1.irqoffset.enabled) return VIF1transfer(pMem + vif1.irqoffset.value, vif1ch.qwc * 4 - vif1.irqoffset.value, false); else return VIF1transfer(pMem, vif1ch.qwc * 4, false); }
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(!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; }
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; }
__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 vif0SetupTransfer() { tDMA_TAG *ptag; switch (vif0.dmamode) { case VIF_NORMAL_TO_MEM_MODE: vif0.inprogress = 1; vif0.done = true; g_vif0Cycles = 2; break; case VIF_CHAIN_MODE: ptag = dmaGetAddr(vif0ch.tadr, false); //Set memory pointer to TADR if (!(vif0ch.transfer("vif0 Tag", ptag))) return; vif0ch.madr = ptag[1]._u32; //MADR = ADDR field + SPR g_vif0Cycles += 1; // Add 1 g_vifCycles from the QW read for the tag // Transfer dma tag if tte is set VIF_LOG("vif0 Tag %8.8x_%8.8x size=%d, id=%d, madr=%lx, tadr=%lx", ptag[1]._u32, ptag[0]._u32, vif0ch.qwc, ptag->ID, vif0ch.madr, vif0ch.tadr); vif0.inprogress = 0; if (vif0ch.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("\tVIF0 SrcChain TTE=1, data = 0x%08x.%08x", masked_tag._u32[3], masked_tag._u32[2]); if (vif0.irqoffset.enabled) { ret = VIF0transfer((u32*)&masked_tag + vif0.irqoffset.value, 4 - vif0.irqoffset.value, true); //Transfer Tag on stall //ret = VIF0transfer((u32*)ptag + (2 + vif0.irqoffset), 2 - vif0.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 vif0.irqoffset.value = 2; vif0.irqoffset.enabled = true; ret = VIF0transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag //ret = VIF0transfer((u32*)ptag + 2, 2); //Transfer Tag } if (!ret && vif0.irqoffset.enabled) { vif0.inprogress = 0; //Better clear this so it has to do it again (Jak 1) return; //IRQ set by VIFTransfer } } vif0.irqoffset.value = 0; vif0.irqoffset.enabled = false; vif0.done |= hwDmacSrcChainWithStack(vif0ch, ptag->ID); if(vif0ch.qwc > 0) vif0.inprogress = 1; //Check TIE bit of CHCR and IRQ bit of tag if (vif0ch.chcr.TIE && ptag->IRQ) { VIF_LOG("dmaIrq Set"); //End Transfer vif0.done = true; return; } break; } }