From c25c0067af448dcea02985e2031da19da7806416 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 14 Dec 2024 13:36:12 +1000 Subject: [PATCH] CDROM: Handle repeated SeekL to same target Fixes more lockups in Resident Evil 3. --- src/core/cdrom.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index b4c70b6c4..3cc6f3ffb 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -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(ticks / g_settings.cdrom_seek_speedup, MIN_TICKS); + ticks = std::max(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(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();