// Read Fifo into an ee tag, transfer it to sif2dma, and process it. static __fi bool ProcessEETag() { static __aligned16 u32 tag[4]; tDMA_TAG& ptag(*(tDMA_TAG*)tag); sif2.fifo.read((u32*)&tag[0], 4); // Tag SIF_LOG("SIF2 EE read tag: %x %x %x %x", tag[0], tag[1], tag[2], tag[3]); sif2dma.unsafeTransfer(&ptag); sif2dma.madr = tag[1]; SIF_LOG("SIF2 EE dest chain tag madr:%08X qwc:%04X id:%X irq:%d(%08X_%08X)", sif2dma.madr, sif2dma.qwc, ptag.ID, ptag.IRQ, tag[1], tag[0]); if (sif2dma.chcr.TIE && ptag.IRQ) { //Console.WriteLn("SIF2 TIE"); sif2.ee.end = true; } switch (ptag.ID) { case TAG_CNT: break; case TAG_CNTS: break; case TAG_END: sif2.ee.end = true; break; } return true; }
__fi void dmaSIF2() { DevCon.Warning("SIF2 EE CHCR %x", sif2dma.chcr._u32); SIF_LOG(wxString(L"dmaSIF2" + sif2dma.cmqt_to_str()).To8BitData()); if (sif2.fifo.readPos != sif2.fifo.writePos) { SIF_LOG("warning, sif2.fifoReadPos != sif2.fifoWritePos"); } //if(sif2dma.chcr.MOD == CHAIN_MODE && sif2dma.qwc > 0) DevCon.Warning(L"SIF2 QWC on Chain CHCR " + sif2dma.chcr.desc()); psHu32(SBUS_F240) |= 0x8000; sif2.ee.busy = true; // Okay, this here is needed currently (r3644). // FFX battles in the thunder plains map die otherwise, Phantasy Star 4 as well // These 2 games could be made playable again by increasing the time the EE or the IOP run, // showing that this is very timing sensible. // Doing this DMA unfortunately brings back an old warning in Legend of Legaia though, but it still works. //Updated 23/08/2011: The hangs are caused by the EE suspending SIF1 DMA and restarting it when in the middle //of processing a "REFE" tag, so the hangs can be solved by forcing the ee.end to be false // (as it should always be at the beginning of a DMA). using "if iop is busy" flags breaks Tom Clancy Rainbow Six. // Legend of Legaia doesn't throw a warning either :) //sif2.ee.end = false; SIF2Dma(); }
// Read Fifo into an ee tag, transfer it to sif0ch, and process it. static __fi bool ProcessEETag() { static __aligned16 u32 tag[4]; tDMA_TAG& ptag(*(tDMA_TAG*)tag); sif0.fifo.read((u32*)&tag[0], 4); // Tag SIF_LOG("SIF0 EE read tag: %x %x %x %x", tag[0], tag[1], tag[2], tag[3]); sif0ch.unsafeTransfer(&ptag); sif0ch.madr = tag[1]; SIF_LOG("SIF0 EE dest chain tag madr:%08X qwc:%04X id:%X irq:%d(%08X_%08X)", sif0ch.madr, sif0ch.qwc, ptag.ID, ptag.IRQ, tag[1], tag[0]); if (sif0ch.chcr.TIE && ptag.IRQ) { //Console.WriteLn("SIF0 TIE"); sif0.ee.end = true; } switch (ptag.ID) { case TAG_CNT: break; case TAG_CNTS: if (dmacRegs.ctrl.STS == STS_SIF0) dmacRegs.stadr.ADDR = sif0ch.madr + (sif0ch.qwc * 16); break; case TAG_END: sif0.ee.end = true; break; } return true; }
void FinalizeEERead() { SIF_LOG("Sif0: End EE"); sif0.ee.end = false; sif0.ee.busy = false; SIF_LOG("CPU INT FIRED SIF0"); CPU_INT(DMAC_SIF0, 16); }
s32 PrepareEERead() { static __aligned16 u32 tag[4]; // Process DMA tag at hw_dma9.tadr sif0.iop.data = *(sifData *)iopPhysMem(hw_dma9.tadr); sif0.iop.data.words = (sif0.iop.data.words + 3) & 0xfffffffc; // Round up to nearest 4. memcpy(tag, (u32*)iopPhysMem(hw_dma9.tadr + 8), 16); hw_dma9.tadr += 16; ///hw_dma9.madr + 16 + sif0.sifData.words << 2; // We're only copying the first 24 bits. hw_dma9.madr = sif0data & 0xFFFFFF; sif0.iop.counter = sif0words; if (sif0tag.IRQ || (sif0tag.ID & 4)) sif0.iop.end = true; SIF_LOG("SIF0 IOP to EE Tag: madr=%lx, tadr=%lx, counter=%lx (%08X_%08X)" "\n\tread tag: %x %x %x %x", hw_dma9.madr, hw_dma9.tadr, sif0.iop.counter, sif0words, sif0data, tag[0], tag[1], tag[2], tag[3]); sif0ch.unsafeTransfer(((tDMA_TAG*)(tag))); sif0ch.madr = tag[1]; tDMA_TAG ptag(tag[0]); SIF_LOG("SIF0 EE dest chain tag madr:%08X qwc:%04X id:%X irq:%d(%08X_%08X)", sif0ch.madr, sif0ch.qwc, ptag.ID, ptag.IRQ, tag[1], tag[0]); if (sif0ch.chcr.TIE && ptag.IRQ) { //Console.WriteLn("SIF0 TIE"); sif0.ee.end = true; } switch (ptag.ID) { case TAG_REFE: sif0.ee.end = true; if (dmacRegs.ctrl.STS != NO_STS) dmacRegs.stadr.ADDR = sif0ch.madr + (sif0ch.qwc * 16); break; case TAG_REFS: if (dmacRegs.ctrl.STS != NO_STS) dmacRegs.stadr.ADDR = sif0ch.madr + (sif0ch.qwc * 16); break; case TAG_END: sif0.ee.end = true; break; } return true; }
// Stop transferring ee, and signal an interrupt. static __fi void EndEE() { SIF_LOG("Sif2: End EE"); sif2.ee.end = false; sif2.ee.busy = false; if (sif2.ee.cycles == 0) { SIF_LOG("SIF2 EE: cycles = 0"); sif2.ee.cycles = 1; } CPU_INT(DMAC_SIF2, sif2.ee.cycles*BIAS); }
static __fi void Sif2Init() { SIF_LOG("SIF2 DMA start... free %x iop busy %x", sif2.fifo.sif_free(), sif2.iop.busy); done = false; sif2.ee.cycles = 0; sif2.iop.cycles = 0; }
s32 DoSifRead(u32 iopAvailable) { u32 eeAvailable = PrepareEERead(); u32 transferSizeBytes = min(min(iopAvailable,eeAvailable),fifoSize); u32 transferSizeWords = transferSizeBytes >> 2; u32 transferSizeQWords = transferSizeBytes >> 4; SIF_LOG("Write IOP to EE: +++++++++++ %lX of %lX", transferSizeWords, sif0.iop.counter); tDMA_TAG *ptag = sif0ch.getAddr(sif0ch.madr, DMAC_SIF0, true); if (ptag == NULL) { DevCon.Warning("Write IOP to EE: ptag == NULL"); return false; } memcpy((u32*)ptag, (u32*)iopPhysMem(hw_dma9.madr), transferSizeBytes); // Clearing handled by vtlb memory protection and manual blocks. //Cpu->Clear(sif0ch.madr, readSize*4); sif0ch.madr += transferSizeBytes; sif0.ee.cycles += transferSizeQWords * 2; sif0ch.qwc -= transferSizeQWords; return transferSizeBytes; }
// Read Fifo into an iop tag, and transfer it to hw_dma9. And presumably process it. static __fi bool ProcessIOPTag() { // Process DMA tag at hw_dma9.tadr sif0.iop.data = *(sifData *)iopPhysMem(hw_dma9.tadr); sif0.iop.data.words = (sif0.iop.data.words + 3) & 0xfffffffc; // Round up to nearest 4. // send the EE's side of the DMAtag. The tag is only 64 bits, with the upper 64 bits // ignored by the EE. sif0.fifo.write((u32*)iopPhysMem(hw_dma9.tadr + 8), 2); sif0.fifo.writePos = (sif0.fifo.writePos + 2) & (FIFO_SIF_W - 1); // iggy on the upper 64. sif0.fifo.size += 2; hw_dma9.tadr += 16; ///hw_dma9.madr + 16 + sif0.sifData.words << 2; // We're only copying the first 24 bits. Bits 30 and 31 (checked below) are Stop/IRQ bits. hw_dma9.madr = sif0data & 0xFFFFFF; sif0.iop.counter = sif0words; // IOP tags have an IRQ bit and an End of Transfer bit: if (sif0tag.IRQ || (sif0tag.ID & 4)) sif0.iop.end = true; SIF_LOG("SIF0 IOP Tag: madr=%lx, tadr=%lx, counter=%lx (%08X_%08X)", hw_dma9.madr, hw_dma9.tadr, sif0.iop.counter, sif0words, sif0data); return true; }
// Write from Fifo to EE. static __fi bool WriteFifoToEE() { const int readSize = std::min((s32)sif2dma.qwc, sif2.fifo.size >> 2); tDMA_TAG *ptag; //SIF_LOG(" EE SIF doing transfer %04Xqw to %08X", readSize, sif2dma.madr); SIF_LOG("Write Fifo to EE: ----------- %lX of %lX", readSize << 2, sif2dma.qwc << 2); ptag = sif2dma.getAddr(sif2dma.madr, DMAC_SIF2, true); if (ptag == NULL) { DevCon.Warning("Write Fifo to EE: ptag == NULL"); return false; } sif2.fifo.read((u32*)ptag, readSize << 2); // Clearing handled by vtlb memory protection and manual blocks. //Cpu->Clear(sif2dma.madr, readSize*4); sif2dma.madr += readSize << 4; sif2.ee.cycles += readSize; // fixme : BIAS is factored in above sif2dma.qwc -= readSize; return true; }
// Write from the EE to Fifo. static __fi bool WriteEEtoFifo() { // There's some data ready to transfer into the fifo.. SIF_LOG("Sif 1: Write EE to Fifo"); const int writeSize = min((s32)sif1dma.qwc, sif1.fifo.sif_free() >> 2); tDMA_TAG *ptag; ptag = sif1dma.getAddr(sif1dma.madr, DMAC_SIF1, false); if (ptag == NULL) { DevCon.Warning("Write EE to Fifo: ptag == NULL"); return false; } sif1.fifo.write((u32*)ptag, writeSize << 2); sif1dma.madr += writeSize << 4; hwDmacSrcTadrInc(sif1dma); sif1.ee.cycles += writeSize; // fixme : BIAS is factored in above sif1dma.qwc -= writeSize; return true; }
static __fi void Sif1Init() { SIF_LOG("SIF1 DMA start..."); done = false; sif1.ee.cycles = 0; sif1.iop.cycles = 0; }
// Read from the fifo and write to IOP static __fi bool WriteFifoToIOP() { // If we're reading something, continue to do so. SIF_LOG("Sif1: Write Fifo to IOP"); const int readSize = min (sif1.iop.counter, sif1.fifo.size); SIF_LOG("Sif 1 IOP doing transfer %04X to %08X", readSize, HW_DMA10_MADR); sif1.fifo.read((u32*)iopPhysMem(hw_dma10.madr), readSize); psxCpu->Clear(hw_dma10.madr, readSize); hw_dma10.madr += readSize << 2; sif1.iop.cycles += readSize >> 2; // fixme: should be >> 4 sif1.iop.counter -= readSize; return true; }
// Stop processing EE, and signal an interrupt. static __fi void EndEE() { sif1.ee.end = false; sif1.ee.busy = false; SIF_LOG("Sif 1: End EE"); // Voodoocycles : Okami wants around 100 cycles when booting up // Other games reach like 50k cycles here, but the EE will long have given up by then and just retry. // (Cause of double interrupts on the EE) if (sif1.ee.cycles == 0) { SIF_LOG("SIF1 EE: cycles = 0"); sif1.ee.cycles = 1; } CPU_INT(DMAC_SIF1, /*min((int)(*/sif1.ee.cycles*BIAS/*), 384)*/); }
__fi bool WriteFifoSingleWord() { // There's some data ready to transfer into the fifo.. SIF_LOG("Write Single word to SIF2 Fifo"); sif2.fifo.write((u32*)&psxHu32(HW_PS1_GPU_DATA), 1); if (sif2.fifo.size > 0) psxHu32(0x1000f300) &= ~0x4000000; return true; }
__fi void sif2Interrupt() { if (!sif2.iop.end || sif2.iop.counter > 0) { SIF2Dma(); return; } SIF_LOG("SIF2 IOP Intr end"); HW_DMA2_CHCR &= ~0x01000000; psxDmaInterrupt2(2); }
__fi bool ReadFifoSingleWord() { u32 ptag[4]; //SIF_LOG(" EE SIF doing transfer %04Xqw to %08X", readSize, sif2dma.madr); SIF_LOG("Read Fifo SIF2 Single Word IOP Busy %x Fifo Size %x SIF2 CHCR %x", sif2.iop.busy, sif2.fifo.size, HW_DMA2_CHCR); sif2.fifo.read((u32*)&ptag[0], 1); psHu32(0x1000f3e0) = ptag[0]; if (sif2.fifo.size == 0) psxHu32(0x1000f300) |= 0x4000000; if (sif2.iop.busy && sif2.fifo.size <= 8)SIF2Dma(); return true; }
// Write fifo to data, and put it in IOP. static __fi bool SIFIOPReadTag() { // Read a tag. sif1.fifo.read((u32*)&sif1.iop.data, 4); //sif1words = (sif1words + 3) & 0xfffffffc; // Round up to nearest 4. SIF_LOG("SIF 1 IOP: dest chain tag madr:%08X wc:%04X id:%X irq:%d", sif1data & 0xffffff, sif1words, sif1tag.ID, sif1tag.IRQ); // Only use the first 24 bits. hw_dma10.madr = sif1data & 0xffffff; sif1.iop.counter = sif1words; if (sif1tag.IRQ || (sif1tag.ID & 4)) sif1.iop.end = true; return true; }
// Write IOP to Fifo. static __fi bool WriteIOPtoFifo() { // There's some data ready to transfer into the fifo.. const int writeSize = min(sif0.iop.counter, sif0.fifo.sif_free()); SIF_LOG("Write IOP to Fifo: +++++++++++ %lX of %lX", writeSize, sif0.iop.counter); sif0.fifo.write((u32*)iopPhysMem(hw_dma9.madr), writeSize); hw_dma9.madr += writeSize << 2; // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords). sif0.iop.cycles += (writeSize >> 2)/* * BIAS*/; // fixme : should be >> 4 sif0.iop.counter -= writeSize; return true; }
// Stop transferring iop, and signal an interrupt. static __fi void EndIOP() { SIF_LOG("Sif2: End IOP"); sif2data = 0; //sif2.iop.end = false; sif2.iop.busy = false; if (sif2.iop.cycles == 0) { DevCon.Warning("SIF2 IOP: cycles = 0"); sif2.iop.cycles = 1; } // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords) // So when we're all done, the equation looks like thus: //PSX_INT(IopEvt_SIF2, ( ( sif2.iop.cycles*BIAS ) / 4 ) / 8); PSX_INT(IopEvt_SIF2, sif2.iop.cycles); }
// Write IOP to Fifo. static __fi bool WriteIOPtoFifo() { // There's some data ready to transfer into the fifo.. const int writeSize = std::min(sif2.iop.counter, sif2.fifo.sif_free()); SIF_LOG("Write IOP to Fifo: +++++++++++ %lX of %lX", writeSize, sif2.iop.counter); sif2.fifo.write((u32*)iopPhysMem(hw_dma2.madr), writeSize); hw_dma2.madr += writeSize << 2; // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords). sif2.iop.cycles += (writeSize >> 2)/* * BIAS*/; // fixme : should be >> 4 sif2.iop.counter -= writeSize; //PSX_INT(IopEvt_SIF2, sif2.iop.cycles); if (sif2.iop.counter == 0) hw_dma2.madr = sif2data & 0xffffff; if (sif2.fifo.size > 0) psxHu32(0x1000f300) &= ~0x4000000; return true; }
// Stop processing IOP, and signal an interrupt. static __fi void EndIOP() { sif1data = 0; sif1.iop.end = false; sif1.iop.busy = false; SIF_LOG("Sif 1: End IOP"); //Fixme ( voodoocycles ): //The *24 are needed for ecco the dolphin (CDVD hangs) and silver surfer (Pad not detected) //Greater than *35 break rebooting when trying to play Tekken5 arcade history //Total cycles over 1024 makes SIF too slow to keep up the sound stream in so3... if (sif1.iop.cycles == 0) { DevCon.Warning("SIF1 IOP: cycles = 0"); sif1.iop.cycles = 1; } // iop is 1/8th the clock rate of the EE and psxcycles is in words (not quadwords) PSX_INT(IopEvt_SIF1, /*min((*/sif1.iop.cycles/* * 26*//*), 1024)*/); }
// Get a tag and process it. static __fi bool ProcessEETag() { // Chain mode tDMA_TAG *ptag; SIF_LOG("Sif1: ProcessEETag"); // Process DMA tag at sif1dma.tadr ptag = sif1dma.DMAtransfer(sif1dma.tadr, DMAC_SIF1); if (ptag == NULL) { Console.WriteLn("Sif1 ProcessEETag: ptag = NULL"); return false; } if (sif1dma.chcr.TTE) { Console.WriteLn("SIF1 TTE"); sif1.fifo.write((u32*)ptag + 2, 2); } if (sif1dma.chcr.TIE && ptag->IRQ) { Console.WriteLn("SIF1 TIE"); sif1.ee.end = true; } SIF_LOG(wxString(ptag->tag_to_str()).To8BitData()); switch (ptag->ID) { case TAG_REFE: sif1.ee.end = true; sif1dma.madr = ptag[1]._u32; sif1dma.tadr += 16; break; case TAG_CNT: sif1dma.tadr += 16; sif1dma.madr = sif1dma.tadr; break; case TAG_NEXT: sif1dma.madr = sif1dma.tadr + 16; sif1dma.tadr = ptag[1]._u32; break; case TAG_REF: case TAG_REFS: if(ptag->ID == TAG_REFS && dmacRegs.ctrl.STD == STD_SIF1) DevCon.Warning("SIF1 Drain Stall Control not implemented"); sif1dma.madr = ptag[1]._u32; sif1dma.tadr += 16; break; case TAG_END: sif1.ee.end = true; sif1dma.madr = sif1dma.tadr + 16; //sif1dma.tadr = sif1dma.madr + (sif1dma.qwc << 4); break; default: Console.WriteLn("Bad addr1 source chain"); } return true; }