CDVD: Buffer up to 16 sectors

This commit is contained in:
refractionpcsx2 2021-10-22 11:27:12 +01:00
parent 05dfe0b0ac
commit bb494af6b0
2 changed files with 107 additions and 49 deletions

View File

@ -49,9 +49,7 @@ static __fi void SetResultSize(u8 size)
static void CDVDSECTORREADY_INT(u32 eCycle) static void CDVDSECTORREADY_INT(u32 eCycle)
{ {
cdvd.nextSectorBuffered = false; if (psxRegs.interrupt & (1 << IopEvt_CdvdSectorReady))
if (!(cdvd.Ready & CDVD_DRIVE_READY))
return; return;
if (EmuConfig.Speedhacks.fastCDVD) if (EmuConfig.Speedhacks.fastCDVD)
@ -1003,6 +1001,8 @@ __fi void cdvdActionInterrupt()
cdvd.Ready |= CDVD_DRIVE_READY; //check (rama) cdvd.Ready |= CDVD_DRIVE_READY; //check (rama)
cdvd.Sector = cdvd.SeekToSector; cdvd.Sector = cdvd.SeekToSector;
cdvd.Status = CDVD_STATUS_READ; cdvd.Status = CDVD_STATUS_READ;
cdvd.nextSectorsBuffered = 0;
cdvd.triggerDataReady = true;
CDVDSECTORREADY_INT(cdvd.ReadTime); CDVDSECTORREADY_INT(cdvd.ReadTime);
break; break;
@ -1012,6 +1012,8 @@ __fi void cdvdActionInterrupt()
cdvd.Ready |= CDVD_DRIVE_READY; //check (rama) cdvd.Ready |= CDVD_DRIVE_READY; //check (rama)
cdvd.Sector = cdvd.SeekToSector; cdvd.Sector = cdvd.SeekToSector;
cdvd.Status = CDVD_STATUS_READ; cdvd.Status = CDVD_STATUS_READ;
cdvd.nextSectorsBuffered = 0;
cdvd.triggerDataReady = true;
CDVDSECTORREADY_INT(cdvd.ReadTime); CDVDSECTORREADY_INT(cdvd.ReadTime);
break; break;
@ -1044,17 +1046,37 @@ __fi void cdvdActionInterrupt()
__fi void cdvdSectorReady() __fi void cdvdSectorReady()
{ {
cdvd.nextSectorBuffered = true; if (cdvd.nextSectorsBuffered < 16)
//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)
{ {
CDVD_LOG("Interrupting to say data ready"); cdvd.nextSectorsBuffered++;
cdvd.PwOff |= (1 << Irq_DataReady); CDVD_LOG("Buffering sector");
psxHu32(0x1070) |= 0x4;
cdvd.Ready |= CDVD_DRIVE_DATARDY; //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. // inlined due to being referenced in only one place.
@ -1065,7 +1087,7 @@ __fi void cdvdReadInterrupt()
cdvd.Ready &= ~CDVD_DRIVE_READY; cdvd.Ready &= ~CDVD_DRIVE_READY;
cdvd.Status = CDVD_STATUS_READ; cdvd.Status = CDVD_STATUS_READ;
cdvd.WaitingDMA = false; cdvd.WaitingDMA = false;
cdvd.nextSectorBuffered = false;
if (!cdvd.Readed) if (!cdvd.Readed)
{ {
// Seeking finished. Process the track we requested before, and // Seeking finished. Process the track we requested before, and
@ -1079,11 +1101,11 @@ __fi void cdvdReadInterrupt()
cdvd.Reading = 1; cdvd.Reading = 1;
cdvd.Readed = 1; cdvd.Readed = 1;
cdvd.Sector = cdvd.SeekToSector; cdvd.Sector = cdvd.SeekToSector;
CDVD_LOG("Cdvd Seek Complete > Scheduling block read interrupt at iopcycle=%8.8x.", CDVD_LOG("Cdvd Seek Complete > Scheduling block read interrupt at iopcycle=%8.8x.",
psxRegs.cycle + cdvd.ReadTime); psxRegs.cycle + cdvd.ReadTime);
CDVDREAD_INT(cdvd.ReadTime); CDVDREAD_INT(cdvd.ReadTime);
cdvd.Status = CDVD_STATUS_READ;
return; return;
} }
else if(cdvd.Reading) else if(cdvd.Reading)
@ -1120,7 +1142,7 @@ __fi void cdvdReadInterrupt()
pxAssert(cdvd.RErr == 0); pxAssert(cdvd.RErr == 0);
} }
if (cdvd.nSectors > 0) if (cdvd.nSectors > 0 && cdvd.nextSectorsBuffered)
{ {
if (cdvdReadSector() == -1) if (cdvdReadSector() == -1)
{ {
@ -1142,31 +1164,49 @@ __fi void cdvdReadInterrupt()
return; return;
} }
cdvd.nextSectorsBuffered--;
CDVDSECTORREADY_INT(cdvd.ReadTime);
cdvd.Sector++; cdvd.Sector++;
cdvd.SeekToSector++; 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 CDVDREAD_INT((cdvd.BlockSize / 4));
// 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
return; return;
} }
cdvd.RetryCntP = 0; cdvd.RetryCntP = 0;
cdvd.Reading = 1; cdvd.Reading = 1;
cdvd.RErr = DoCDVDreadTrack(cdvd.Sector, cdvd.ReadMode); cdvd.RErr = DoCDVDreadTrack(cdvd.Sector, cdvd.ReadMode);
CDVDREAD_INT(cdvd.ReadTime); if (cdvd.nextSectorsBuffered)
CDVDREAD_INT((cdvd.BlockSize / 4));
return; else
CDVDREAD_INT(cdvd.ReadTime + (cdvd.BlockSize / 4));
} }
// Returns the number of IOP cycles until the event completes. // 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; uint seektime;
cdvd.Ready &= ~(CDVD_DRIVE_READY | CDVD_DRIVE_DATARDY); cdvd.Ready &= ~(CDVD_DRIVE_READY | CDVD_DRIVE_DATARDY);
cdvd.Reading = 0; cdvd.Reading = 1;
cdvd.Readed = 0; 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. // 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 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)) // 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. // 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. // We should really run some tests for this behaviour.
cdvd.Status = CDVD_STATUS_PAUSE; cdvd.Status = CDVD_STATUS_PAUSE;
if (!cdvd.Spinning) 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); CDVD_LOG("CdSpinUp > Simulating CdRom Spinup Time, and seek to sector %d", cdvd.SeekToSector);
seektime = PSXCLK / 3; // 333ms delay seektime = PSXCLK / 3; // 333ms delay
cdvd.Spinning = true; cdvd.Spinning = true;
cdvd.nextSectorsBuffered = 0;
CDVDSECTORREADY_INT(seektime + cdvd.ReadTime);
} }
else if ((tbl_ContigiousSeekDelta[mode] == 0) || (delta >= tbl_ContigiousSeekDelta[mode])) else if ((tbl_ContigiousSeekDelta[mode] == 0) || (delta >= tbl_ContigiousSeekDelta[mode]))
{ {
// Select either Full or Fast seek depending on delta: // Select either Full or Fast seek depending on delta:
psxRegs.interrupt &= ~(1 << IopEvt_CdvdSectorReady); psxRegs.interrupt &= ~(1 << IopEvt_CdvdSectorReady);
cdvd.nextSectorBuffered = false; cdvd.nextSectorsBuffered = 0;
if (delta >= tbl_FastSeekDelta[mode]) if (delta >= tbl_FastSeekDelta[mode])
{ {
// Full Seek // 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); CDVD_LOG("CdSeek Begin > Contiguous block without seek - delta=%d sectors", delta);
// seektime is the time it takes to read to the destination block: // if delta > 0 it will read a new sector so the readInterrupt will account for this.
seektime = cdvd.ReadTime + (cdvd.BlockSize / 4); seektime = 0;
if (delta == 0) if (delta == 0)
{ {
//cdvd.Status = CDVD_STATUS_PAUSE; //cdvd.Status = CDVD_STATUS_PAUSE;
cdvd.Status = CDVD_STATUS_READ; // Time Crisis 2 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; cdvd.RetryCntP = 0;
// setting Readed to 0 skips the seek logic, which means the next call to // 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 // cdvdReadInterrupt will load a block. So make sure it's properly scheduled
// based on sector read speeds: // based on sector read speeds:
//seektime = cdvd.ReadTime; //seektime = cdvd.ReadTime;
if (cdvd.nextSectorBuffered)
{ if (!cdvd.nextSectorsBuffered)//Buffering time hasn't completed yet so cancel it and simulate the remaining time
//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 (psxRegs.interrupt & (1 << IopEvt_CdvdSectorReady)) 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]); //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]) + (cdvd.BlockSize / 4);
seektime -= psxRegs.cycle - psxRegs.sCycle[IopEvt_CdvdSectorReady]; }
else
{
CDVDSECTORREADY_INT(cdvd.ReadTime);
seektime = cdvd.ReadTime + (cdvd.BlockSize / 4);
} }
} }
else
seektime = (cdvd.BlockSize / 4);
} }
else else
{ {
psxRegs.interrupt &= ~(1 << IopEvt_CdvdSectorReady); 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()); 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); //DevCon.Warning("%s rotational latency at sector %d is %d cycles", (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? "CAV" : "CLV", cdvd.SeekToSector, rotationalLatency);
seektime += rotationalLatency; seektime += rotationalLatency;
CDVDSECTORREADY_INT(cdvd.ReadTime + seektime + rotationalLatency);
seektime += (cdvd.BlockSize / 4);
} }
return seektime; return seektime;
} }
@ -1535,8 +1581,11 @@ static void cdvdWrite04(u8 rt)
psxRegs.interrupt &= ~(1 << IopEvt_Cdvd); psxRegs.interrupt &= ~(1 << IopEvt_Cdvd);
cdvd.Ready |= CDVD_DRIVE_READY; cdvd.Ready |= CDVD_DRIVE_READY;
cdvdSetIrq(); cdvdSetIrq();
cdvd.nCommand = 0;
//After Pausing needs to buffer the next sector //After Pausing needs to buffer the next sector
cdvd.Status = CDVD_STATUS_READ; cdvd.Status = CDVD_STATUS_READ;
cdvd.nextSectorsBuffered = 0;
cdvd.triggerDataReady = true;
CDVDSECTORREADY_INT(cdvd.ReadTime); CDVDSECTORREADY_INT(cdvd.ReadTime);
break; break;
@ -1762,10 +1811,13 @@ static void cdvdWrite04(u8 rt)
//} //}
cdvdGetToc(iopPhysMem(HW_DMA3_MADR)); cdvdGetToc(iopPhysMem(HW_DMA3_MADR));
cdvdSetIrq(); cdvdSetIrq();
cdvd.nCommand = 0;
HW_DMA3_CHCR &= ~0x01000000; HW_DMA3_CHCR &= ~0x01000000;
psxDmaInterrupt(3); psxDmaInterrupt(3);
//After reading the TOC it needs to go back to buffer the next sector //After reading the TOC it needs to go back to buffer the next sector
cdvd.Status = CDVD_STATUS_READ; cdvd.Status = CDVD_STATUS_READ;
cdvd.nextSectorsBuffered = 0;
cdvd.triggerDataReady = true;
CDVDSECTORREADY_INT(cdvd.ReadTime); CDVDSECTORREADY_INT(cdvd.ReadTime);
break; break;
@ -1778,8 +1830,11 @@ static void cdvdWrite04(u8 rt)
cdvdReadKey(arg0, arg1, arg2, cdvd.Key); cdvdReadKey(arg0, arg1, arg2, cdvd.Key);
cdvd.KeyXor = 0x00; cdvd.KeyXor = 0x00;
cdvdSetIrq(); cdvdSetIrq();
cdvd.nCommand = 0;
//After reading the key it needs to go back to buffer the next sector //After reading the key it needs to go back to buffer the next sector
cdvd.Status = CDVD_STATUS_READ; cdvd.Status = CDVD_STATUS_READ;
cdvd.nextSectorsBuffered = 0;
cdvd.triggerDataReady = true;
CDVDSECTORREADY_INT(cdvd.ReadTime); CDVDSECTORREADY_INT(cdvd.ReadTime);
} }
break; break;
@ -1787,11 +1842,13 @@ static void cdvdWrite04(u8 rt)
case N_CD_CHG_SPDL_CTRL: // CdChgSpdlCtrl case N_CD_CHG_SPDL_CTRL: // CdChgSpdlCtrl
Console.WriteLn("sceCdChgSpdlCtrl(%d)", cdvd.Param[0]); Console.WriteLn("sceCdChgSpdlCtrl(%d)", cdvd.Param[0]);
cdvdSetIrq(); cdvdSetIrq();
cdvd.nCommand = 0;
break; break;
default: default:
Console.Warning("NCMD Unknown %x", rt); Console.Warning("NCMD Unknown %x", rt);
cdvdSetIrq(); cdvdSetIrq();
cdvd.nCommand = 0;
break; break;
} }
cdvd.ParamP = 0; cdvd.ParamP = 0;

View File

@ -152,7 +152,8 @@ struct cdvdStruct
bool Spinning; // indicates if the Cdvd is spinning or needs a spinup delay bool Spinning; // indicates if the Cdvd is spinning or needs a spinup delay
bool mediaChanged; bool mediaChanged;
cdvdTrayTimer Tray; cdvdTrayTimer Tray;
bool nextSectorBuffered; u8 nextSectorsBuffered;
bool triggerDataReady;
}; };
extern cdvdStruct cdvd; extern cdvdStruct cdvd;