From 1bdd53a6c505ba96fdb29001508a70cf15036040 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Tue, 15 Oct 2024 17:19:31 +0100 Subject: [PATCH] DEV9: Don't fake the FIFO --- pcsx2/DEV9/ATA/ATA.h | 4 +- pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp | 46 ++--- pcsx2/DEV9/DEV9.cpp | 275 +++++++++++++++++++------ pcsx2/DEV9/DEV9.h | 9 +- 4 files changed, 240 insertions(+), 94 deletions(-) diff --git a/pcsx2/DEV9/ATA/ATA.h b/pcsx2/DEV9/ATA/ATA.h index e49d6425a1..b4b8d2d150 100644 --- a/pcsx2/DEV9/ATA/ATA.h +++ b/pcsx2/DEV9/ATA/ATA.h @@ -167,8 +167,8 @@ public: void Async(u32 cycles); - void ATAreadDMA8Mem(u8* pMem, int size); - void ATAwriteDMA8Mem(u8* pMem, int size); + int ReadDMAToFIFO(u8* buffer, int space); + int WriteDMAFromFIFO(u8* buffer, int available); u16 ATAreadPIO(); //ATAwritePIO; diff --git a/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp b/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp index 8b3ace0c03..f3208a5905 100644 --- a/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp +++ b/pcsx2/DEV9/ATA/Commands/ATA_CmdDMA.cpp @@ -10,7 +10,7 @@ void ATA::DRQCmdDMADataToHost() regStatus &= ~ATA_STAT_BUSY; regStatus |= ATA_STAT_DRQ; dmaReady = true; - _DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1); + DEV9runFIFO(); //PCSX2 will Start DMA } void ATA::PostCmdDMADataToHost() @@ -22,11 +22,9 @@ void ATA::PostCmdDMADataToHost() regStatus &= ~ATA_STAT_BUSY; dmaReady = false; - dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA; pendingInterrupt = true; if (regControlEnableIRQ) _DEV9irq(ATA_INTR_INTRQ, 1); - //PCSX2 Will Start DMA } void ATA::DRQCmdDMADataFromHost() @@ -44,7 +42,7 @@ void ATA::DRQCmdDMADataFromHost() regStatus &= ~ATA_STAT_BUSY; regStatus |= ATA_STAT_DRQ; dmaReady = true; - _DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1); + DEV9runFIFO(); //PCSX2 will Start DMA } void ATA::PostCmdDMADataFromHost() @@ -62,8 +60,6 @@ void ATA::PostCmdDMADataFromHost() regStatus &= ~ATA_STAT_DRQ; dmaReady = false; - dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA; - if (fetWriteCacheEnabled) { regStatus &= ~ATA_STAT_BUSY; @@ -77,18 +73,16 @@ void ATA::PostCmdDMADataFromHost() Async(-1); } -void ATA::ATAreadDMA8Mem(u8* pMem, int size) +int ATA::ReadDMAToFIFO(u8* buffer, int space) { - if ((udmaMode >= 0 || mdmaMode >= 0) && - (dev9.if_ctrl & SPD_IF_ATA_DMAEN) != 0) + if (udmaMode >= 0 || mdmaMode >= 0) { - if (size == 0 || nsector == -1) - return; - DevCon.WriteLn("DEV9: DMA read, size %i, transferred %i, total size %i", size, rdTransferred, nsector * 512); + if (space == 0 || nsector == -1) + return 0; - //read - size = std::min(size, nsector * 512 - rdTransferred); - memcpy(pMem, &readBuffer[rdTransferred], size); + // Read to FIFO + const int size = std::min(space, nsector * 512 - rdTransferred); + memcpy(buffer, &readBuffer[rdTransferred], size); rdTransferred += size; @@ -100,21 +94,22 @@ void ATA::ATAreadDMA8Mem(u8* pMem, int size) rdTransferred = 0; PostCmdDMADataToHost(); } + + return size; } + return 0; } -void ATA::ATAwriteDMA8Mem(u8* pMem, int size) +int ATA::WriteDMAFromFIFO(u8* buffer, int available) { - if ((udmaMode >= 0 || mdmaMode >= 0) && - (dev9.if_ctrl & SPD_IF_ATA_DMAEN) != 0) + if (udmaMode >= 0 || mdmaMode >= 0) { - if (nsector == -1) - return; - DevCon.WriteLn("DEV9: DMA write, size %i, transferred %i, total size %i", size, wrTransferred, nsector * 512); + if (available == 0 || nsector == -1) + return 0; - //write - size = std::min(size, nsector * 512 - wrTransferred); - memcpy(¤tWrite[wrTransferred], pMem, size); + // Write to FIFO + const int size = std::min(available, nsector * 512 - wrTransferred); + memcpy(¤tWrite[wrTransferred], buffer, size); wrTransferred += size; @@ -126,7 +121,10 @@ void ATA::ATAwriteDMA8Mem(u8* pMem, int size) wrTransferred = 0; PostCmdDMADataFromHost(); } + + return size; } + return 0; } //GENRAL FEATURE SET diff --git a/pcsx2/DEV9/DEV9.cpp b/pcsx2/DEV9/DEV9.cpp index 4d9b0aabd6..1b0d3d8faa 100644 --- a/pcsx2/DEV9/DEV9.cpp +++ b/pcsx2/DEV9/DEV9.cpp @@ -202,6 +202,7 @@ void DEV9close() { DevCon.WriteLn("DEV9: DEV9close"); + dev9.dma_iop_ptr = nullptr; dev9.ata->Close(); TermNet(); isRunning = false; @@ -228,49 +229,124 @@ void _DEV9irq(int cause, int cycles) dev9Irq(cycles); } -//Fakes SPEED FIFO +// SPEED <-> HDD FIFO void HDDWriteFIFO() { - if (dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN)) - { - const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read); - const int spaceSectors = (SPD_DBUF_AVAIL_MAX * 512 - unread) / 512; - if (spaceSectors < 0) - { - Console.Error("DEV9: No Space on SPEED FIFO"); - pxAssert(false); - abort(); - } + pxAssert(dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN)); + pxAssert((dev9.if_ctrl & SPD_IF_READ)); - const int readSectors = dev9.ata->nsectorLeft < spaceSectors ? dev9.ata->nsectorLeft : spaceSectors; - dev9.fifo_bytes_write += readSectors * 512; - dev9.ata->nsectorLeft -= readSectors; + const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read); + const int space = (SPD_DBUF_AVAIL_MAX * 512 - unread); + const int base = dev9.fifo_bytes_write % (SPD_DBUF_AVAIL_MAX * 512); + + pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512); + + int read; + if (base + space > SPD_DBUF_AVAIL_MAX * 512) + { + const int was = SPD_DBUF_AVAIL_MAX * 512 - base; + read = dev9.ata->ReadDMAToFIFO(dev9.fifo + base, was); + if (read == was) + read += dev9.ata->ReadDMAToFIFO(dev9.fifo, space - was); } - //FIFOIntr(); + else + { + read = dev9.ata->ReadDMAToFIFO(dev9.fifo + base, space); + } + + dev9.fifo_bytes_write += read; } void HDDReadFIFO() { - if (dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN)) + pxAssert(dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN)); + pxAssert((dev9.if_ctrl & SPD_IF_READ) == 0); + + const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read); + const int base = dev9.fifo_bytes_read % (SPD_DBUF_AVAIL_MAX * 512); + + pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512); + + int write; + if (base + unread > SPD_DBUF_AVAIL_MAX * 512) { - const int writeSectors = (dev9.fifo_bytes_write - dev9.fifo_bytes_read) / 512; - dev9.fifo_bytes_read += writeSectors * 512; - dev9.ata->nsectorLeft -= writeSectors; + const int was = SPD_DBUF_AVAIL_MAX * 512 - base; + write = dev9.ata->WriteDMAFromFIFO(dev9.fifo + base, was); + if (write == was) + write += dev9.ata->WriteDMAFromFIFO(dev9.fifo, unread - was); } - //FIFOIntr(); + else + { + write = dev9.ata->WriteDMAFromFIFO(dev9.fifo + base, unread); + } + + dev9.fifo_bytes_read += write; } -void IOPReadFIFO(int bytes) +void IOPReadFIFO() { - dev9.fifo_bytes_read += bytes; + pxAssert((dev9.dma_iop_ptr != nullptr) && (dev9.xfr_ctrl & SPD_XFR_DMAEN)); + pxAssert((dev9.xfr_ctrl & SPD_XFR_WRITE) == 0); + + const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read); + const int base = dev9.fifo_bytes_read % (SPD_DBUF_AVAIL_MAX * 512); + + const int remain = dev9.dma_iop_size - dev9.dma_iop_transfered; + const int read = std::min(remain, unread); + + pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512); + + if (read == 0) + return; + + if (base + read > SPD_DBUF_AVAIL_MAX * 512) + { + const int was = SPD_DBUF_AVAIL_MAX * 512 - base; + + std::memcpy(dev9.dma_iop_ptr + dev9.dma_iop_transfered, dev9.fifo + base, was); + std::memcpy(dev9.dma_iop_ptr + dev9.dma_iop_transfered + was, dev9.fifo, read - was); + } + else + { + std::memcpy(dev9.dma_iop_ptr + dev9.dma_iop_transfered, dev9.fifo + base, read); + } + + dev9.dma_iop_transfered += read; + dev9.fifo_bytes_read += read; if (dev9.fifo_bytes_read > dev9.fifo_bytes_write) Console.Error("DEV9: UNDERFLOW BY IOP"); - //FIFOIntr(); } -void IOPWriteFIFO(int bytes) +void IOPWriteFIFO() { - dev9.fifo_bytes_write += bytes; - if (dev9.fifo_bytes_write - SPD_DBUF_AVAIL_MAX * 512 > dev9.fifo_bytes_read) + pxAssert((dev9.dma_iop_ptr != nullptr) && (dev9.xfr_ctrl & SPD_XFR_DMAEN)); + pxAssert(dev9.xfr_ctrl & SPD_XFR_WRITE); + + const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read); + const int space = (SPD_DBUF_AVAIL_MAX * 512 - unread); + const int base = dev9.fifo_bytes_write % (SPD_DBUF_AVAIL_MAX * 512); + + const int remain = dev9.dma_iop_size - dev9.dma_iop_transfered; + const int write = std::min(remain, space); + + pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512); + + if (write == 0) + return; + + if (base + write > SPD_DBUF_AVAIL_MAX * 512) + { + const int was = SPD_DBUF_AVAIL_MAX * 512 - base; + + std::memcpy(dev9.fifo + base, dev9.dma_iop_ptr + dev9.dma_iop_transfered, was); + std::memcpy(dev9.fifo + base, dev9.dma_iop_ptr + dev9.dma_iop_transfered + was, write - was); + } + else + { + std::memcpy(dev9.fifo + base, dev9.dma_iop_ptr + dev9.dma_iop_transfered, write); + } + + dev9.dma_iop_transfered += write; + dev9.fifo_bytes_write += write; + if ((dev9.fifo_bytes_write - dev9.fifo_bytes_read) > SPD_DBUF_AVAIL_MAX * 512) Console.Error("DEV9: OVERFLOW BY IOP"); - //FIFOIntr(); } void FIFOIntr() { @@ -279,14 +355,83 @@ void FIFOIntr() if (unread == 0) { + dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA; if ((dev9.irqcause & SPD_INTR_ATA_FIFO_EMPTY) == 0) _DEV9irq(SPD_INTR_ATA_FIFO_EMPTY, 1); } + else + { + dev9.irqcause &= ~SPD_INTR_ATA_FIFO_EMPTY; + if ((dev9.irqcause & SPD_INTR_ATA_FIFO_DATA) == 0) + _DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1); + } + if (unread == SPD_DBUF_AVAIL_MAX * 512) { - //Log_Error("FIFO Full"); - //INTR Full? + if ((dev9.irqcause & SPD_INTR_ATA_FIFO_FULL) == 0) + _DEV9irq(SPD_INTR_ATA_FIFO_FULL, 1); } + else + { + dev9.irqcause &= ~SPD_INTR_ATA_FIFO_FULL; + } + + // is DMA finished + if ((dev9.dma_iop_ptr != nullptr) && + (dev9.dma_iop_transfered == dev9.dma_iop_size)) + { + dev9.dma_iop_ptr = nullptr; + psxDMA8Interrupt(); + } +} + +// FIFO counters operate based on the direction set in SPD_R_XFR_CTRL +// Both might have to set to the same direction for (SPEED <-> HDD) to work +void DEV9runFIFO() +{ + const bool iopWrite = dev9.xfr_ctrl & SPD_XFR_WRITE; // IOP writes to FIFO + const bool hddRead = dev9.if_ctrl & SPD_IF_READ; // HDD writes to FIFO + + const bool iopXfer = (dev9.dma_iop_ptr != nullptr) && (dev9.xfr_ctrl & SPD_XFR_DMAEN); + const bool hddXfer = dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN); + + // Order operations based on iopWrite to ensure DMA has data/space to work with. + if (iopWrite) + { + // Perform DMA from IOP. + if (iopXfer) + IOPWriteFIFO(); + + // Drain the FIFO + if (hddXfer && !hddRead) + { + HDDReadFIFO(); + } + } + else + { + // Ensure FIFO has data. + if (hddXfer && hddRead) + { + HDDWriteFIFO(); + } + + if (iopXfer) + { + // Perform DMA to IOP. + IOPReadFIFO(); + + // Refill FIFO after DMA. + // Need to recheck dmaReady incase prior + // HDDWriteFIFO competed the transfer from HDD + if (hddXfer && hddRead && dev9.ata->dmaReady) + { + HDDWriteFIFO(); + } + } + } + + FIFOIntr(); } u16 SpeedRead(u32 addr, int width) @@ -359,18 +504,8 @@ u16 SpeedRead(u32 addr, int width) return dev9.xfr_ctrl; case SPD_R_DBUF_STAT: { - if (dev9.if_ctrl & SPD_IF_READ) // Semi async - { - HDDWriteFIFO(); // Yes this is not a typo - } - else - { - HDDReadFIFO(); - } - FIFOIntr(); - const u8 count = static_cast((dev9.fifo_bytes_write - dev9.fifo_bytes_read) / 512); - if (dev9.xfr_ctrl & SPD_XFR_WRITE) // or ifRead? + if (dev9.xfr_ctrl & SPD_XFR_WRITE) { hard = static_cast(SPD_DBUF_AVAIL_MAX - count); hard |= (count == 0) ? SPD_DBUF_STAT_1 : static_cast(0); @@ -518,9 +653,14 @@ void SpeedWrite(u32 addr, u16 value, int width) break; case SPD_R_XFR_CTRL: + { //DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL %dbit write %x", width, value); + const u16 oldValue = dev9.xfr_ctrl; dev9.xfr_ctrl = value; + if ((value & SPD_XFR_WRITE) != (oldValue & SPD_XFR_WRITE)) + DEV9runFIFO(); + //if (value & SPD_XFR_WRITE) // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL Set Write"); //else @@ -532,8 +672,11 @@ void SpeedWrite(u32 addr, u16 value, int width) //if ((value & (1 << 2)) != 0) // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL Unknown Bit 2"); - //if (value & SPD_XFR_DMAEN) - // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Enabled"); + if (value & SPD_XFR_DMAEN) + { + //DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Enabled"); + DEV9runFIFO(); + } //else // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Disabled"); @@ -541,6 +684,7 @@ void SpeedWrite(u32 addr, u16 value, int width) Console.Error("DEV9: SPD_R_XFR_CTRL Unknown value written %x", value); break; + } case SPD_R_DBUF_STAT: //DevCon.WriteLn("DEV9: SPD_R_DBUF_STAT %dbit write %x", width, value); @@ -560,13 +704,19 @@ void SpeedWrite(u32 addr, u16 value, int width) break; case SPD_R_IF_CTRL: + { //DevCon.WriteLn("DEV9: SPD_R_IF_CTRL %dbit write %x", width, value); + const u16 oldValue = dev9.if_ctrl; dev9.if_ctrl = value; //if (value & SPD_IF_UDMA) // DevCon.WriteLn("DEV9: IF_CTRL UDMA Enabled"); //else // DevCon.WriteLn("DEV9: IF_CTRL UDMA Disabled"); + + if ((value & SPD_IF_READ) != (oldValue & SPD_IF_READ)) + DEV9runFIFO(); + //if (value & SPD_IF_READ) // DevCon.WriteLn("DEV9: IF_CTRL DMA Is ATA Read"); //else @@ -575,15 +725,7 @@ void SpeedWrite(u32 addr, u16 value, int width) if (value & SPD_IF_ATA_DMAEN) { //DevCon.WriteLn("DEV9: IF_CTRL ATA DMA Enabled"); - if (value & SPD_IF_READ) //Semi async - { - HDDWriteFIFO(); //Yes this is not a typo - } - else - { - HDDReadFIFO(); - } - FIFOIntr(); + DEV9runFIFO(); } //else // DevCon.WriteLn("DEV9: IF_CTRL ATA DMA Disabled"); @@ -617,6 +759,7 @@ void SpeedWrite(u32 addr, u16 value, int width) Console.Error("DEV9: IF_CTRL Unknown Bit(s) %x", (value & 0xFF00)); break; + } case SPD_R_PIO_MODE: //ATA only? or includes EEPROM? //DevCon.WriteLn("DEV9: SPD_R_PIO_MODE 16bit %dbit write %x", width, value); dev9.pio_mode = value; @@ -924,14 +1067,14 @@ void DEV9readDMA8Mem(u32* pMem, int size) } else { - if (dev9.xfr_ctrl & SPD_XFR_DMAEN && - !(dev9.xfr_ctrl & SPD_XFR_WRITE)) + if (!(dev9.xfr_ctrl & SPD_XFR_WRITE)) { - HDDWriteFIFO(); - IOPReadFIFO(size); - dev9.ata->ATAreadDMA8Mem((u8*)pMem, size); - FIFOIntr(); - psxDMA8Interrupt(); + pxAssert(size <= SPD_DBUF_AVAIL_MAX * 512); + dev9.dma_iop_ptr = reinterpret_cast(pMem); + dev9.dma_iop_size = size; + dev9.dma_iop_transfered = 0; + + DEV9runFIFO(); } } @@ -954,18 +1097,16 @@ void DEV9writeDMA8Mem(u32* pMem, int size) } else { - if (dev9.xfr_ctrl & SPD_XFR_DMAEN && - dev9.xfr_ctrl & SPD_XFR_WRITE) + if (dev9.xfr_ctrl & SPD_XFR_WRITE) { - IOPWriteFIFO(size); - HDDReadFIFO(); - dev9.ata->ATAwriteDMA8Mem((u8*)pMem, size); - FIFOIntr(); - psxDMA8Interrupt(); + pxAssert(size <= SPD_DBUF_AVAIL_MAX * 512); + dev9.dma_iop_ptr = reinterpret_cast(pMem); + dev9.dma_iop_size = size; + dev9.dma_iop_transfered = 0; + + DEV9runFIFO(); } } - - //TODO, track if write was successful } void DEV9async(u32 cycles) diff --git a/pcsx2/DEV9/DEV9.h b/pcsx2/DEV9/DEV9.h index 0de5d7d2bd..261a051af2 100644 --- a/pcsx2/DEV9/DEV9.h +++ b/pcsx2/DEV9/DEV9.h @@ -69,9 +69,15 @@ typedef struct u16 mdma_mode; u16 udma_mode; - //Non-Regs + // FIFO int fifo_bytes_read; int fifo_bytes_write; + u8 fifo[16 * 512]; + + // DMA + u8* dma_iop_ptr; + int dma_iop_transfered; + int dma_iop_size; } dev9Struct; //EEPROM states @@ -672,6 +678,7 @@ void FLASHwrite32(u32 addr, u32 value, int size); void _DEV9irq(int cause, int cycles); int DEV9irqHandler(void); void DEV9async(u32 cycles); +void DEV9runFIFO(); void DEV9writeDMA8Mem(u32* pMem, int size); void DEV9readDMA8Mem(u32* pMem, int size); u8 DEV9read8(u32 addr);