From bb494af6b0bb5bfcaa26cbec6b0b9cf2a853e373 Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Fri, 22 Oct 2021 11:27:12 +0100 Subject: [PATCH] CDVD: Buffer up to 16 sectors --- pcsx2/CDVD/CDVD.cpp | 153 ++++++++++++++++++++++++++++++-------------- pcsx2/CDVD/CDVD.h | 3 +- 2 files changed, 107 insertions(+), 49 deletions(-) diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index 2b29a3f182..15ce6d010d 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -49,9 +49,7 @@ static __fi void SetResultSize(u8 size) static void CDVDSECTORREADY_INT(u32 eCycle) { - cdvd.nextSectorBuffered = false; - - if (!(cdvd.Ready & CDVD_DRIVE_READY)) + if (psxRegs.interrupt & (1 << IopEvt_CdvdSectorReady)) return; if (EmuConfig.Speedhacks.fastCDVD) @@ -1003,6 +1001,8 @@ __fi void cdvdActionInterrupt() cdvd.Ready |= CDVD_DRIVE_READY; //check (rama) cdvd.Sector = cdvd.SeekToSector; cdvd.Status = CDVD_STATUS_READ; + cdvd.nextSectorsBuffered = 0; + cdvd.triggerDataReady = true; CDVDSECTORREADY_INT(cdvd.ReadTime); break; @@ -1012,6 +1012,8 @@ __fi void cdvdActionInterrupt() cdvd.Ready |= CDVD_DRIVE_READY; //check (rama) cdvd.Sector = cdvd.SeekToSector; cdvd.Status = CDVD_STATUS_READ; + cdvd.nextSectorsBuffered = 0; + cdvd.triggerDataReady = true; CDVDSECTORREADY_INT(cdvd.ReadTime); break; @@ -1044,17 +1046,37 @@ __fi void cdvdActionInterrupt() __fi void cdvdSectorReady() { - cdvd.nextSectorBuffered = true; - //DevCon.Warning("Bufferred Sector %d cur seek %d ready %x", cdvd.Sector, cdvd.SeekToSector, cdvd.Ready); - if (cdvd.Sector == cdvd.SeekToSector && (cdvd.Ready & 0x42) == 0x40) + if (cdvd.nextSectorsBuffered < 16) { - CDVD_LOG("Interrupting to say data ready"); - cdvd.PwOff |= (1 << Irq_DataReady); - psxHu32(0x1070) |= 0x4; - cdvd.Ready |= CDVD_DRIVE_DATARDY; + cdvd.nextSectorsBuffered++; + CDVD_LOG("Buffering sector"); + + //DevCon.Warning("Bufferred Sector %d cur seek %d ready %x", cdvd.Sector, cdvd.SeekToSector, cdvd.Ready); + if (cdvd.nextSectorsBuffered == 16 && cdvd.triggerDataReady) + { + CDVD_LOG("Interrupting to say data ready"); + if (!(cdvd.PwOff & (1 << Irq_DataReady))) + { + cdvd.PwOff |= (1 << Irq_DataReady); + iopIntcIrq(2); + } + cdvd.Ready |= CDVD_DRIVE_DATARDY; + cdvd.triggerDataReady = false; + } } - cdvd.Status = CDVD_STATUS_PAUSE; // Needed here but could be smth else than Pause (rama) + if (cdvd.nextSectorsBuffered == 16 && (cdvd.Ready & CDVD_DRIVE_READY)) + { + cdvd.Status = CDVD_STATUS_PAUSE; // Needed here but could be smth else than Pause (rama) + } + else + { + if (cdvd.nextSectorsBuffered < 16) + { + CDVDSECTORREADY_INT(cdvd.ReadTime); + cdvd.Status = CDVD_STATUS_READ; + } + } } // inlined due to being referenced in only one place. @@ -1065,7 +1087,7 @@ __fi void cdvdReadInterrupt() cdvd.Ready &= ~CDVD_DRIVE_READY; cdvd.Status = CDVD_STATUS_READ; cdvd.WaitingDMA = false; - cdvd.nextSectorBuffered = false; + if (!cdvd.Readed) { // Seeking finished. Process the track we requested before, and @@ -1079,11 +1101,11 @@ __fi void cdvdReadInterrupt() cdvd.Reading = 1; cdvd.Readed = 1; cdvd.Sector = cdvd.SeekToSector; - CDVD_LOG("Cdvd Seek Complete > Scheduling block read interrupt at iopcycle=%8.8x.", psxRegs.cycle + cdvd.ReadTime); CDVDREAD_INT(cdvd.ReadTime); + cdvd.Status = CDVD_STATUS_READ; return; } else if(cdvd.Reading) @@ -1120,7 +1142,7 @@ __fi void cdvdReadInterrupt() pxAssert(cdvd.RErr == 0); } - if (cdvd.nSectors > 0) + if (cdvd.nSectors > 0 && cdvd.nextSectorsBuffered) { if (cdvdReadSector() == -1) { @@ -1142,31 +1164,49 @@ __fi void cdvdReadInterrupt() return; } + cdvd.nextSectorsBuffered--; + CDVDSECTORREADY_INT(cdvd.ReadTime); + cdvd.Sector++; cdvd.SeekToSector++; - } - if (--cdvd.nSectors <= 0) + if (--cdvd.nSectors <= 0) + { + // Setting the data ready flag fixes a black screen loading issue in + // Street Fighter Ex3 (NTSC-J version). + cdvd.PwOff |= (1 << Irq_CommandComplete) | (1 << Irq_DataReady); + //psxHu32(0x1070) |= 0x4; + iopIntcIrq(2); + cdvd.Ready |= CDVD_DRIVE_READY; + + if (cdvd.nextSectorsBuffered < 16) + cdvd.Status = CDVD_STATUS_READ; + else + cdvd.Status = CDVD_STATUS_PAUSE; + + cdvd.nCommand = 0; + //DevCon.Warning("Scheduling interrupt in %d cycles", cdvd.ReadTime - (cdvd.BlockSize / 4)); + // Timing issues on command end + // Star Ocean (1.1 Japan) expects the DMA to end and interrupt at least 128 or more cycles before the CDVD command ends. + // However the time required seems to increase slowly, so delaying the end of the command is not the solution. + //cdvd.Status = CDVD_STATUS_PAUSE; // Needed here but could be smth else than Pause (rama) + // All done! :D + return; + } + } + else { - // Setting the data ready flag fixes a black screen loading issue in - // Street Fighter Ex3 (NTSC-J version). - cdvd.PwOff |= (1 << Irq_CommandComplete); - //psxHu32(0x1070) |= 0x4; - cdvd.Ready |= CDVD_DRIVE_READY; - cdvd.Status = CDVD_STATUS_READ; - //DevCon.Warning("Scheduling interrupt in %d cycles", cdvd.ReadTime - (cdvd.BlockSize / 4)); - CDVDSECTORREADY_INT(cdvd.ReadTime - (cdvd.BlockSize / 4)); - cdvd.nCommand = 0; - // All done! :D + CDVDREAD_INT((cdvd.BlockSize / 4)); return; } cdvd.RetryCntP = 0; cdvd.Reading = 1; cdvd.RErr = DoCDVDreadTrack(cdvd.Sector, cdvd.ReadMode); - CDVDREAD_INT(cdvd.ReadTime); - - return; + if (cdvd.nextSectorsBuffered) + CDVDREAD_INT((cdvd.BlockSize / 4)); + else + CDVDREAD_INT(cdvd.ReadTime + (cdvd.BlockSize / 4)); } // Returns the number of IOP cycles until the event completes. @@ -1178,14 +1218,15 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode) uint seektime; cdvd.Ready &= ~(CDVD_DRIVE_READY | CDVD_DRIVE_DATARDY); - cdvd.Reading = 0; + cdvd.Reading = 1; cdvd.Readed = 0; - + cdvd.triggerDataReady = false; // Okay so let's explain this, since people keep messing with it in the past and just poking it. // So when the drive is spinning, bit 0x2 is set on the Status, and bit 0x8 is set when the drive is not reading. // So In the case where it's seeking to data it will be Spinning (0x2) not reading (0x8) and Seeking (0x10, but because seeking is also spinning 0x2 is also set)) // Update - Apparently all that was rubbish and some games don't like it. WRC was the one in this scenario which hated SEEK |ZPAUSE, so just putting it back to pause for now. // We should really run some tests for this behaviour. + cdvd.Status = CDVD_STATUS_PAUSE; if (!cdvd.Spinning) @@ -1193,13 +1234,14 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode) CDVD_LOG("CdSpinUp > Simulating CdRom Spinup Time, and seek to sector %d", cdvd.SeekToSector); seektime = PSXCLK / 3; // 333ms delay cdvd.Spinning = true; + cdvd.nextSectorsBuffered = 0; + CDVDSECTORREADY_INT(seektime + cdvd.ReadTime); } else if ((tbl_ContigiousSeekDelta[mode] == 0) || (delta >= tbl_ContigiousSeekDelta[mode])) { // Select either Full or Fast seek depending on delta: psxRegs.interrupt &= ~(1 << IopEvt_CdvdSectorReady); - cdvd.nextSectorBuffered = false; - + cdvd.nextSectorsBuffered = 0; if (delta >= tbl_FastSeekDelta[mode]) { // Full Seek @@ -1216,40 +1258,43 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode) { CDVD_LOG("CdSeek Begin > Contiguous block without seek - delta=%d sectors", delta); - // seektime is the time it takes to read to the destination block: - seektime = cdvd.ReadTime + (cdvd.BlockSize / 4); - + // if delta > 0 it will read a new sector so the readInterrupt will account for this. + seektime = 0; + if (delta == 0) { //cdvd.Status = CDVD_STATUS_PAUSE; cdvd.Status = CDVD_STATUS_READ; // Time Crisis 2 - cdvd.Readed = 1; // Note: 1, not 0, as implied by the next comment. Need to look into this. --arcum42 + cdvd.Readed = 1; // Note: 1, not 0, as implied by the next comment. Need to look into this. --arcum42 + cdvd.Reading = 1; // We don't need to wait for it to read a sector as it's already queued up, or we adjust for it here. cdvd.RetryCntP = 0; // setting Readed to 0 skips the seek logic, which means the next call to // cdvdReadInterrupt will load a block. So make sure it's properly scheduled // based on sector read speeds: - + //seektime = cdvd.ReadTime; - if (cdvd.nextSectorBuffered) - { - //DevCon.Warning("Sector buffered, we can be speedy boi"); - seektime = cdvd.BlockSize / 4; - } - else //Buffering time hasn't completed yet so cancel it and simulate the remaining time + + if (!cdvd.nextSectorsBuffered)//Buffering time hasn't completed yet so cancel it and simulate the remaining time { if (psxRegs.interrupt & (1 << IopEvt_CdvdSectorReady)) { //DevCon.Warning("coming back from ready sector early reducing %d cycles by %d cycles", seektime, psxRegs.cycle - psxRegs.sCycle[IopEvt_CdvdSectorReady]); - psxRegs.interrupt &= ~(1 << IopEvt_CdvdSectorReady); - seektime -= psxRegs.cycle - psxRegs.sCycle[IopEvt_CdvdSectorReady]; + seektime = (psxRegs.cycle - psxRegs.sCycle[IopEvt_CdvdSectorReady]) + (cdvd.BlockSize / 4); + } + else + { + CDVDSECTORREADY_INT(cdvd.ReadTime); + seektime = cdvd.ReadTime + (cdvd.BlockSize / 4); } } + else + seektime = (cdvd.BlockSize / 4); } else { psxRegs.interrupt &= ~(1 << IopEvt_CdvdSectorReady); - cdvd.nextSectorBuffered = false; + cdvd.nextSectorsBuffered = 0; } } @@ -1258,8 +1303,9 @@ static uint cdvdStartSeek(uint newsector, CDVD_MODE_TYPE mode) int rotationalLatency = cdvdRotationalLatency((CDVD_MODE_TYPE)cdvdIsDVD()); //DevCon.Warning("%s rotational latency at sector %d is %d cycles", (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? "CAV" : "CLV", cdvd.SeekToSector, rotationalLatency); seektime += rotationalLatency; + CDVDSECTORREADY_INT(cdvd.ReadTime + seektime + rotationalLatency); + seektime += (cdvd.BlockSize / 4); } - return seektime; } @@ -1535,8 +1581,11 @@ static void cdvdWrite04(u8 rt) psxRegs.interrupt &= ~(1 << IopEvt_Cdvd); cdvd.Ready |= CDVD_DRIVE_READY; cdvdSetIrq(); + cdvd.nCommand = 0; //After Pausing needs to buffer the next sector cdvd.Status = CDVD_STATUS_READ; + cdvd.nextSectorsBuffered = 0; + cdvd.triggerDataReady = true; CDVDSECTORREADY_INT(cdvd.ReadTime); break; @@ -1762,10 +1811,13 @@ static void cdvdWrite04(u8 rt) //} cdvdGetToc(iopPhysMem(HW_DMA3_MADR)); cdvdSetIrq(); + cdvd.nCommand = 0; HW_DMA3_CHCR &= ~0x01000000; psxDmaInterrupt(3); //After reading the TOC it needs to go back to buffer the next sector cdvd.Status = CDVD_STATUS_READ; + cdvd.nextSectorsBuffered = 0; + cdvd.triggerDataReady = true; CDVDSECTORREADY_INT(cdvd.ReadTime); break; @@ -1778,8 +1830,11 @@ static void cdvdWrite04(u8 rt) cdvdReadKey(arg0, arg1, arg2, cdvd.Key); cdvd.KeyXor = 0x00; cdvdSetIrq(); + cdvd.nCommand = 0; //After reading the key it needs to go back to buffer the next sector cdvd.Status = CDVD_STATUS_READ; + cdvd.nextSectorsBuffered = 0; + cdvd.triggerDataReady = true; CDVDSECTORREADY_INT(cdvd.ReadTime); } break; @@ -1787,11 +1842,13 @@ static void cdvdWrite04(u8 rt) case N_CD_CHG_SPDL_CTRL: // CdChgSpdlCtrl Console.WriteLn("sceCdChgSpdlCtrl(%d)", cdvd.Param[0]); cdvdSetIrq(); + cdvd.nCommand = 0; break; default: Console.Warning("NCMD Unknown %x", rt); cdvdSetIrq(); + cdvd.nCommand = 0; break; } cdvd.ParamP = 0; diff --git a/pcsx2/CDVD/CDVD.h b/pcsx2/CDVD/CDVD.h index c39832a931..48ddefc8c3 100644 --- a/pcsx2/CDVD/CDVD.h +++ b/pcsx2/CDVD/CDVD.h @@ -152,7 +152,8 @@ struct cdvdStruct bool Spinning; // indicates if the Cdvd is spinning or needs a spinup delay bool mediaChanged; cdvdTrayTimer Tray; - bool nextSectorBuffered; + u8 nextSectorsBuffered; + bool triggerDataReady; }; extern cdvdStruct cdvd;