CDROM: Handle repeated SeekL to same target

Fixes more lockups in Resident Evil 3.
This commit is contained in:
Stenzek 2024-12-14 13:36:12 +10:00
parent e683c89770
commit c25c0067af
No known key found for this signature in database
1 changed files with 21 additions and 5 deletions

View File

@ -74,6 +74,8 @@ enum : u32
static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F;
static constexpr TickCount MIN_SEEK_TICKS = 30000;
enum class Interrupt : u8
{
DataReady = 0x01,
@ -1557,10 +1559,8 @@ u32 CDROM::GetSectorsPerTrack(CDImage::LBA lba)
TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
{
static constexpr TickCount MIN_TICKS = 30000;
if (g_settings.cdrom_seek_speedup == 0)
return MIN_TICKS;
return System::ScaleTicksToOverclock(MIN_SEEK_TICKS);
u32 ticks = 0;
@ -1630,7 +1630,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
}
if (g_settings.cdrom_seek_speedup > 1)
ticks = std::max<u32>(ticks / g_settings.cdrom_seek_speedup, MIN_TICKS);
ticks = std::max<u32>(ticks / g_settings.cdrom_seek_speedup, MIN_SEEK_TICKS);
if (s_state.drive_state == DriveState::ChangingSpeedOrTOCRead && !ignore_speed_change)
{
@ -2789,7 +2789,23 @@ void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_see
logical ? "logical" : "physical");
const CDImage::LBA seek_lba = s_state.setloc_position.ToLBA();
const TickCount seek_time = GetTicksForSeek(seek_lba, play_after_seek);
TickCount seek_time;
// Yay for edge cases. If we repeatedly send SeekL to the same target before a new sector is read, it should complete
// nearly instantly, because it's looking for a valid target of -2. See the note in CompleteSeek(). We gate this with
// the seek target in case another read happened in the interim. Test case: Resident Evil 3.
if (logical && !read_after_seek && s_state.current_subq_lba == (seek_lba - SUBQ_SECTOR_SKEW) &&
s_state.seek_end_lba == seek_lba &&
(System::GetGlobalTickCounter() - s_state.subq_lba_update_tick) < static_cast<GlobalTicks>(GetTicksForRead()))
{
DEV_COLOR_LOG(StrongCyan, "Completing seek instantly due to not passing target {}.",
LBAToMSFString(seek_lba - SUBQ_SECTOR_SKEW));
seek_time = MIN_SEEK_TICKS;
}
else
{
seek_time = GetTicksForSeek(seek_lba, play_after_seek);
}
ClearCommandSecondResponse();
ClearAsyncInterrupt();