CDVD: Implement correct SpindleCtrl handling

Semi decouple end of CDVD DMA's
Fix alternative reading method for reading not picking the correct speeds for DVD's
This commit is contained in:
refractionpcsx2 2021-09-28 19:14:55 +01:00
parent a41ec422d2
commit 8476fc3b5a
6 changed files with 218 additions and 53 deletions

View File

@ -629,37 +629,52 @@ static s32 cdvdReadDvdDualInfo(s32* dualType, u32* layer1Start)
return CDVD->getDualInfo(dualType, layer1Start);
}
static bool cdvdIsDVD()
{
if (cdvd.Type == CDVD_TYPE_DETCTDVDS || cdvd.Type == CDVD_TYPE_DETCTDVDD
|| cdvd.Type == CDVD_TYPE_PS2DVD || cdvd.Type == CDVD_TYPE_DVDV)
return true;
else
return false;
}
static uint cdvdBlockReadTime(CDVD_MODE_TYPE mode)
{
int numSectors = 0;
int offset = 0;
// Sector counts are taken from google for Single layer, Dual layer DVD's and for 700MB CD's
switch (cdvd.Type)
{
case CDVD_TYPE_DETCTDVDS:
case CDVD_TYPE_PS2DVD:
numSectors = 2298496;
break;
case CDVD_TYPE_DETCTDVDD:
numSectors = 4173824 / 2; // Total sectors for both layers, assume half per layer
u32 layer1Start;
s32 dualType;
// Layer 1 needs an offset as it goes back to the middle of the disc
cdvdReadDvdDualInfo(&dualType, &layer1Start);
if (cdvd.Sector >= layer1Start)
offset = layer1Start;
break;
default: // Pretty much every CD format
numSectors = 360000;
break;
// CAV Read speed is roughly 41% in the centre full speed on outer edge. I imagine it's more logarithmic than this
if (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV)
{
// Sector counts are taken from google for Single layer, Dual layer DVD's and for 700MB CD's
switch (cdvd.Type)
{
case CDVD_TYPE_DETCTDVDS:
case CDVD_TYPE_PS2DVD:
numSectors = 2298496;
break;
case CDVD_TYPE_DETCTDVDD:
numSectors = 4173824 / 2; // Total sectors for both layers, assume half per layer
u32 layer1Start;
s32 dualType;
// Layer 1 needs an offset as it goes back to the middle of the disc
cdvdReadDvdDualInfo(&dualType, &layer1Start);
if (cdvd.SeekToSector >= layer1Start)
offset = layer1Start;
break;
default: // Pretty much every CD format
numSectors = 360000;
break;
}
const float sectorSpeed = (((float)(cdvd.SeekToSector - offset) / numSectors) * 0.60f) + 0.40f;
return ((PSXCLK * cdvd.BlockSize) / ((float)(((mode == MODE_CDROM) ? PSX_CD_READSPEED : PSX_DVD_READSPEED) * cdvd.Speed) * sectorSpeed));
}
// Read speed is roughly 37% at lowest and full speed on outer edge. I imagine it's more logarithmic than this
// Required for Shadowman to work
// Use SeekToSector as Sector hasn't been updated yet
const float sectorSpeed = (((float)(cdvd.SeekToSector-offset) / numSectors) * 0.63f) + 0.37f;
//DevCon.Warning("Read speed %f sector %d\n", sectorSpeed, cdvd.Sector);
return ((PSXCLK * cdvd.BlockSize) / ((float)(((mode == MODE_CDROM) ? PSX_CD_READSPEED : PSX_DVD_READSPEED) * cdvd.Speed) * sectorSpeed));
// CLV Read Speed is constant
return ((PSXCLK * cdvd.BlockSize) / (float)(((mode == MODE_CDROM) ? PSX_CD_READSPEED : PSX_DVD_READSPEED) * cdvd.Speed));
}
void cdvdReset()
@ -747,11 +762,11 @@ int cdvdReadSector()
if (bcr < cdvd.BlockSize)
{
CDVD_LOG("READBLOCK: bcr < cdvd.BlockSize; %x < %x", bcr, cdvd.BlockSize);
if (HW_DMA3_CHCR & 0x01000000)
/*if (HW_DMA3_CHCR & 0x01000000)
{
HW_DMA3_CHCR &= ~0x01000000;
psxDmaInterrupt(3);
}
}*/
return -1;
}
@ -885,6 +900,23 @@ __fi void cdvdActionInterrupt()
psxHu32(0x1070) |= 0x4;
}
void cdvdDMAInterrupt()
{
if (HW_DMA3_CHCR & 0x01000000)
{
HW_DMA3_CHCR &= ~0x01000000;
psxDmaInterrupt(3);
if (!cdvd.nSectors)
{
// Setting the data ready flag fixes a black screen loading issue in
// Street Fighter Ex3 (NTSC-J version).
cdvd.PwOff |= (1 << Irq_DataReady) | (1 << Irq_CommandComplete);
psxHu32(0x1070) |= 0x4;
cdvd.Ready = CDVD_READY2;
}
}
}
// inlined due to being referenced in only one place.
__fi void cdvdReadInterrupt()
{
@ -955,9 +987,13 @@ __fi void cdvdReadInterrupt()
// bit and try to read the sector again later.
// An arbitrary delay of some number of cycles probably makes more sense here,
// but for now it's based on the cdvd.ReadTime value. -- air
int DMATime = (cdvd.BlockSize / 2) * 40;
CDVDREAD_INT(DMATime + 100); // Bring it back after the DMA has ended to avoid a nasty loop
pxAssert((int)cdvd.ReadTime > 0);
CDVDREAD_INT(cdvd.ReadTime / 4);
if (HW_DMA3_CHCR & 0x01000000)
PSX_INT(IopEvt_CdvdDMA, DMATime);
return;
}
@ -966,14 +1002,10 @@ __fi void cdvdReadInterrupt()
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_DataReady) | (1 << Irq_CommandComplete);
psxHu32(0x1070) |= 0x4;
int DMATime = (cdvd.BlockSize / 2) * 40;
PSX_INT(IopEvt_CdvdDMA, DMATime);
HW_DMA3_CHCR &= ~0x01000000;
psxDmaInterrupt(3);
cdvd.Ready = CDVD_READY2;
cdvd.Status = CDVD_STATUS_PAUSE; // Needed here but could be smth else than Pause (rama)
// All done! :D
return;
@ -1305,7 +1337,48 @@ static void cdvdWrite04(u8 rt)
cdvd.nSectors = *(u32*)(cdvd.Param + 4);
cdvd.RetryCnt = (cdvd.Param[8] == 0) ? 0x100 : cdvd.Param[8];
cdvd.SpindlCtrl = cdvd.Param[9];
cdvd.Speed = 24;
switch (cdvd.SpindlCtrl & CDVD_SPINDLE_SPEED)
{
case 0: // Will use current speed
break;
case 1: // x1
cdvd.Speed = 1;
break;
case 2: // x2
cdvd.Speed = 2;
break;
case 3: // x4
cdvd.Speed = 4;
break;
case 4: // x12
if (cdvdIsDVD())
{
DevCon.Warning("CDVD Read invalid DVD Speed %d", cdvd.SpindlCtrl & CDVD_SPINDLE_SPEED);
cdvd.Speed = 4;
}
else
cdvd.Speed = 12;
break;
case 5: // x24
if (cdvdIsDVD())
{
DevCon.Warning("CDVD Read invalid DVD Speed %d", cdvd.SpindlCtrl & CDVD_SPINDLE_SPEED);
cdvd.Speed = 4;
}
else
cdvd.Speed = 24;
break;
default:
Console.Error("Unknown CDVD Read Speed SpindleCtrl=%x", cdvd.SpindlCtrl);
if (cdvdIsDVD())
cdvd.Speed = 4; // Just assume 4x for now (DVD)
else
cdvd.Speed = 24; // Just assume 24x for now (CD)
break;
}
switch (cdvd.Param[10])
{
case 2:
@ -1316,20 +1389,85 @@ static void cdvdWrite04(u8 rt)
cdvd.ReadMode = CDVD_MODE_2328;
cdvd.BlockSize = 2328;
break;
case 0:
default:
cdvd.ReadMode = CDVD_MODE_2048;
cdvd.BlockSize = 2048;
break;
}
CDVD_LOG("CdRead > startSector=%d, seekTo=%d, nSectors=%d, RetryCnt=%x, Speed=%x(%x), ReadMode=%x(%x) (1074=%x)",
cdvd.Sector, cdvd.SeekToSector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, cdvd.Param[9], cdvd.ReadMode, cdvd.Param[10], psxHu32(0x1074));
CDVD_LOG("CDRead > startSector=%d, seekTo=%d nSectors=%d, RetryCnt=%x, Speed=%dx(%s), ReadMode=%x(%x) SpindleCtrl=%x",
cdvd.Sector, cdvd.SeekToSector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? L"CAV" : L"CLV", cdvd.ReadMode, cdvd.Param[10], cdvd.SpindlCtrl);
if (EmuConfig.CdvdVerboseReads)
Console.WriteLn(Color_Gray, L"CdRead: Reading Sector %07d (%03d Blocks of Size %d) at Speed=%dx",
cdvd.SeekToSector, cdvd.nSectors, cdvd.BlockSize, cdvd.Speed);
Console.WriteLn(Color_Gray, L"CDRead: Reading Sector %07d (%03d Blocks of Size %d) at Speed=%dx(%s) Spindle=%x",
cdvd.SeekToSector, cdvd.nSectors, cdvd.BlockSize, cdvd.Speed, (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? L"CAV" : L"CLV", cdvd.SpindlCtrl);
cdvd.ReadTime = cdvdBlockReadTime((CDVD_MODE_TYPE)cdvdIsDVD());
CDVDREAD_INT(cdvdStartSeek(cdvd.SeekToSector, (CDVD_MODE_TYPE)cdvdIsDVD()));
// Read-ahead by telling CDVD about the track now.
// This helps improve performance on actual from-cd emulation
// (ie, not using the hard drive)
cdvd.RErr = DoCDVDreadTrack(cdvd.SeekToSector, cdvd.ReadMode);
// Set the reading block flag. If a seek is pending then Readed will
// take priority in the handler anyway. If the read is contiguous then
// this'll skip the seek delay.
cdvd.Reading = 1;
break;
case N_CD_READ_CDDA: // CdReadCDDA
case N_CD_READ_XCDDA: // CdReadXCDDA
// Assign the seek to sector based on cdvd.Param[0]-[3], and the number of sectors based on cdvd.Param[4]-[7].
cdvd.SeekToSector = *(u32*)(cdvd.Param + 0);
cdvd.nSectors = *(u32*)(cdvd.Param + 4);
cdvd.RetryCnt = (cdvd.Param[8] == 0) ? 0x100 : cdvd.Param[8];
cdvd.SpindlCtrl = cdvd.Param[9];
switch (cdvd.SpindlCtrl & CDVD_SPINDLE_SPEED)
{
case 0: // Will use current speed
break;
case 1: // x1
cdvd.Speed = 1;
break;
case 2: // x2
cdvd.Speed = 2;
break;
case 3: // x4
cdvd.Speed = 4;
break;
case 4: // x12
cdvd.Speed = 12;
break;
case 5: // x24
cdvd.Speed = 24;
break;
default:
Console.Error("Unknown CDDA Read Speed SpindleCtrl=%x", cdvd.SpindlCtrl);
cdvd.Speed = 24; // Just assume 24x for now (CD)
break;
}
switch (cdvd.Param[10])
{
case 1:
cdvd.ReadMode = CDVD_MODE_2368;
cdvd.BlockSize = 2368;
break;
default:
cdvd.ReadMode = CDVD_MODE_2352;
cdvd.BlockSize = 2352;
break;
}
CDVD_LOG("CDRead > startSector=%d, seekTo=%d, nSectors=%d, RetryCnt=%x, Speed=%dx(%s), ReadMode=%x(%x) SpindleCtrl=%x",
cdvd.Sector, cdvd.SeekToSector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? L"CAV" : L"CLV", cdvd.ReadMode, cdvd.Param[10], cdvd.SpindlCtrl);
if (EmuConfig.CdvdVerboseReads)
Console.WriteLn(Color_Gray, L"CdAudioRead: Reading Sector %07d (%03d Blocks of Size %d) at Speed=%dx(%s) Spindle=%x",
cdvd.Sector, cdvd.nSectors, cdvd.BlockSize, cdvd.Speed, (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? L"CAV" : L"CLV", cdvd.SpindlCtrl);
cdvd.ReadTime = cdvdBlockReadTime(MODE_CDROM);
CDVDREAD_INT(cdvdStartSeek(cdvd.SeekToSector, MODE_CDROM));
@ -1355,16 +1493,35 @@ static void cdvdWrite04(u8 rt)
cdvd.RetryCnt = cdvd.Param[8];
cdvd.SpindlCtrl = cdvd.Param[9];
cdvd.Speed = 4;
cdvd.ReadMode = CDVD_MODE_2048;
cdvd.BlockSize = 2064; // Why oh why was it 2064
switch (cdvd.SpindlCtrl & CDVD_SPINDLE_SPEED)
{
case 0: // Will use current speed
break;
case 1: // x1
cdvd.Speed = 1;
break;
case 2: // x2
cdvd.Speed = 2;
break;
case 3: // x4
cdvd.Speed = 4;
break;
default:
Console.Error("Unknown DVD Speed SpindleCtrl=%x", cdvd.SpindlCtrl);
cdvd.Speed = 4; // Just assume 4x for now
break;
}
CDVD_LOG("DvdRead > startSector=%d, seekTo=%d nSectors=%d, RetryCnt=%x, Speed=%x(%x), ReadMode=%x(%x) (1074=%x)",
cdvd.Sector, cdvd.SeekToSector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, cdvd.Param[9], cdvd.ReadMode, cdvd.Param[10], psxHu32(0x1074));
cdvd.ReadMode = CDVD_MODE_2048;
cdvd.BlockSize = 2064;
CDVD_LOG("DvdRead > startSector=%d, seekTo=%d nSectors=%d, RetryCnt=%x, Speed=%dx(%s), ReadMode=%x(%x) SpindleCtrl=%x",
cdvd.Sector, cdvd.SeekToSector, cdvd.nSectors, cdvd.RetryCnt, cdvd.Speed, (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? L"CAV" : L"CLV", cdvd.ReadMode, cdvd.Param[10], cdvd.SpindlCtrl);
if (EmuConfig.CdvdVerboseReads)
Console.WriteLn(Color_Gray, L"DvdRead: Reading Sector %07d (%03d Blocks of Size %d) at Speed=%dx",
cdvd.SeekToSector, cdvd.nSectors, cdvd.BlockSize, cdvd.Speed);
Console.WriteLn(Color_Gray, L"DvdRead: Reading Sector %07d (%03d Blocks of Size %d) at Speed=%dx(%s) SpindleCtrl=%x",
cdvd.SeekToSector, cdvd.nSectors, cdvd.BlockSize, cdvd.Speed, (cdvd.SpindlCtrl & CDVD_SPINDLE_CAV) ? L"CAV" : L"CLV", cdvd.SpindlCtrl);
cdvd.ReadTime = cdvdBlockReadTime(MODE_DVDROM);
CDVDREAD_INT(cdvdStartSeek(cdvd.SeekToSector, MODE_DVDROM));

View File

@ -145,6 +145,7 @@ extern void cdvdReset();
extern void cdvdVsync();
extern void cdvdActionInterrupt();
extern void cdvdReadInterrupt();
extern void cdvdDMAInterrupt();
// We really should not have a function with the exact same name as a callback except for case!
extern void cdvdNewDiskCB();

View File

@ -72,9 +72,9 @@ enum cdvdStatus
enum cdvdready
{
CDVD_NOTREADY = 0x00,
CDVD_READY1 = 0x40,
CDVD_READY2 = 0x4e // This is used in a few places for some reason.
CDVD_NOTREADY = 0x06,
CDVD_READY1 = 0x42,
CDVD_READY2 = 0x42 // This is used in a few places for some reason.
//It would be worth checking if this was just a typo made at some point.
};
@ -128,8 +128,8 @@ static const uint tbl_ContigiousSeekDelta[3] =
// concerned with accurate(ish) seek delays and less concerned with actual block read speeds.
// Translation: it's a minor speedhack :D
static const uint PSX_CD_READSPEED = 153600; // 1 Byte Time @ x1 (150KB = cd x 1)
static const uint PSX_DVD_READSPEED = 1382400; // 1 Byte Time @ x1 (1350KB = dvd x 1).
static const uint PSX_CD_READSPEED = 153600; // Bytes per second, rough values from outer CD (CAV).
static const uint PSX_DVD_READSPEED = 1382400; // Bytes per second, rough values from outer DVD (CAV).
// Legacy Note: FullSeek timing causes many games to load very slow, but it likely not the real problem.
// Games breaking with it set to PSXCLK*40 : "wrath unleashed" and "Shijou Saikyou no Deshi Kenichi".

View File

@ -43,6 +43,11 @@ typedef struct _cdvdTN
u8 etrack; //number of the last track
} cdvdTN;
// SpindleCtrl Masks
#define CDVD_SPINDLE_SPEED 0x7 // Speed ranges from 0-3 (1, 2, 3, 4x for DVD) and 0-5 (1, 2, 4, 12, 24x for CD)
#define CDVD_SPINDLE_DVD16 0x40 // Forces DVD to 1.6x speed (Mentioned in open SDK not really anywhere else
#define CDVD_SPINDLE_CAV 0x80 // CAV/CLV selector
// CDVDreadTrack mode values:
#define CDVD_MODE_2352 0 // full 2352 bytes
#define CDVD_MODE_2340 1 // skip sync (12) bytes

View File

@ -320,6 +320,7 @@ enum IopEventId
IopEvt_CdvdRead,
IopEvt_DEV9,
IopEvt_USB,
IopEvt_CdvdDMA,
};
extern void PSX_INT( IopEventId n, s32 ecycle);

View File

@ -178,6 +178,7 @@ static __fi void _psxTestInterrupts()
IopTestEvent(IopEvt_SIF2, sif2Interrupt); // SIF2
// Originally controlled by a preprocessor define, now PSX dependent.
if (psxHu32(HW_ICFG) & (1 << 3)) IopTestEvent(IopEvt_SIO, sioInterruptR);
IopTestEvent(IopEvt_CdvdDMA, cdvdDMAInterrupt);
IopTestEvent(IopEvt_CdvdRead, cdvdReadInterrupt);
// Profile-guided Optimization (sorta)