From 114effd9a15975836659a158fc13f9c3d5f94c7d Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 25 Sep 2024 01:10:50 +1000 Subject: [PATCH] CDROM: Simulate backwards 1T jump on short seeks Fixes hangs in LMA Manager, Nightmare Creatures PAL with Interpreter. --- src/core/cdrom.cpp | 82 ++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 1dc08f458..50e197e84 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -312,6 +312,7 @@ static TickCount GetTicksForStop(bool motor_was_on); static TickCount GetTicksForSpeedChange(); static TickCount GetTicksForTOCRead(); static CDImage::LBA GetNextSectorToBeRead(); +static u32 GetSectorsPerTrack(CDImage::LBA lba); static bool CompleteSeek(); static void BeginCommand(Command command); // also update status register @@ -1440,6 +1441,12 @@ TickCount CDROM::GetTicksForRead() return s_mode.double_speed ? (tps / 150) : (tps / 75); } +u32 CDROM::GetSectorsPerTrack(CDImage::LBA lba) +{ + return static_cast(9.0f + + 2.5440497f * std::log(static_cast(lba / CDImage::FRAMES_PER_MINUTE) + 1u)); +} + TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) { static constexpr TickCount MIN_TICKS = 30000; @@ -1456,7 +1463,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) UpdatePhysicalPosition(false); const CDImage::LBA current_lba = IsMotorOn() ? (IsSeeking() ? s_seek_end_lba : s_physical_lba) : 0; - const u32 lba_diff = static_cast((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba)); + const CDImage::LBA lba_diff = ((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba)); // Motor spin-up time. if (!IsMotorOn()) @@ -1468,15 +1475,28 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) } float seconds; - if (current_lba < new_lba && lba_diff < 10) + std::string_view seek_type; + if (lba_diff < 10) { - // If we're behind the current sector, and within a small distance, the mech just waits for the sector to come up by - // reading normally (or apparently moves the lens according to some?). This timing is actually needed for - // Transformers - Beast Wars Transmetals, it gets very unstable during loading if seeks are too fast. - const u32 ticks_per_sector = - s_mode.double_speed ? static_cast(System::MASTER_CLOCK / 150) : static_cast(System::MASTER_CLOCK / 75); - ticks += ticks_per_sector * std::min(5u, lba_diff); seconds = 0.0f; + + const TickCount ticks_per_sector = s_mode.double_speed ? (System::MASTER_CLOCK / 150) : (System::MASTER_CLOCK / 75); + if (current_lba < new_lba) + { + // If we're behind the current sector, and within a small distance, the mech just waits for the sector to come up + // by reading normally (or apparently moves the lens according to some?). This timing is actually needed for + // Transformers - Beast Wars Transmetals, it gets very unstable during loading if seeks are too fast. + ticks += ticks_per_sector * std::min(8u, lba_diff); + seek_type = "forward"; + } + else + { + // Track jump back. We cap this at 8 sectors (~53ms), so it doesn't take longer than the medium seek below. + const CDImage::LBA sectors_per_track = GetSectorsPerTrack(current_lba); + const CDImage::LBA tjump_position = (current_lba >= sectors_per_track) ? (current_lba - sectors_per_track) : 0; + ticks += ticks_per_sector * std::clamp((new_lba - std::min(new_lba, tjump_position)), 1u, 8u); + seek_type = "1T back+forward"; + } } else if (lba_diff < 7200) { @@ -1487,6 +1507,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) (-63.1333f * std::log(std::clamp(static_cast(current_lba) / static_cast(CDImage::FRAMES_PER_MINUTE), 1.0f, 72.0f)))); seconds = (lba_diff < switch_point) ? 0.05f : 0.1f; + seek_type = (new_lba > current_lba) ? "NT forward" : "NT backward"; } else { @@ -1500,6 +1521,7 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) SLED_FIXED_COST + (((SLED_VARIABLE_COST * (std::log(static_cast(lba_diff)) / std::log(MAX_SLED_LBA)))) * LOG_WEIGHT) + ((SLED_VARIABLE_COST * (lba_diff / MAX_SLED_LBA)) * (1.0f - LOG_WEIGHT)); + seek_type = (new_lba > current_lba) ? "2N/sled forward" : "2N/sled backward"; } constexpr u32 ticks_per_second = static_cast(System::MASTER_CLOCK); @@ -1514,13 +1536,15 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change) const TickCount remaining_change_ticks = s_drive_event.GetTicksUntilNextExecution(); ticks += remaining_change_ticks; - DEV_LOG("Seek time for {} LBAs: {} ({:.3f} ms) ({} for speed change/implicit TOC read)", lba_diff, ticks, - (static_cast(ticks) / static_cast(ticks_per_second)) * 1000.0f, remaining_change_ticks); + DEV_LOG("Seek time for {}->{} ({} LBA): {} ({:.3f} ms) ({} for speed change/init) ({})", + LBAToMSFString(current_lba), LBAToMSFString(new_lba), lba_diff, ticks, + (static_cast(ticks) / static_cast(ticks_per_second)) * 1000.0f, remaining_change_ticks, + seek_type); } else { - DEV_LOG("Seek time for {} LBAs: {} ({:.3f} ms)", lba_diff, ticks, - (static_cast(ticks) / static_cast(ticks_per_second)) * 1000.0f); + DEV_LOG("Seek time for {}->{} ({} LBA): {} ({:.3f} ms) ({})", LBAToMSFString(current_lba), LBAToMSFString(new_lba), + lba_diff, ticks, (static_cast(ticks) / static_cast(ticks_per_second)) * 1000.0f, seek_type); } return System::ScaleTicksToOverclock(static_cast(ticks)); @@ -2703,33 +2727,17 @@ void CDROM::UpdatePhysicalPosition(bool update_logical) const u32 carry = diff % ticks_per_read; if (sector_diff > 0) { - CDImage::LBA hold_offset; - CDImage::LBA sectors_per_track; - // hardware tests show that it holds much closer to the target sector in logical mode - if (s_last_sector_header_valid) - { - hold_offset = 2; - sectors_per_track = 4; - } - else - { - hold_offset = 0; - sectors_per_track = - static_cast(7.0f + 2.811844405f * std::log(static_cast(s_current_lba / 4500u) + 1u)); - } - + const CDImage::LBA hold_offset = s_last_sector_header_valid ? 2 : 0; + const CDImage::LBA sectors_per_track = GetSectorsPerTrack(s_current_lba); const CDImage::LBA hold_position = s_current_lba + hold_offset; - const CDImage::LBA base = - (hold_position >= (sectors_per_track - 1)) ? (hold_position - (sectors_per_track - 1)) : hold_position; - if (s_physical_lba < base) - s_physical_lba = base; - - const CDImage::LBA old_offset = s_physical_lba - base; + const CDImage::LBA tjump_position = (hold_position >= sectors_per_track) ? (hold_position - sectors_per_track) : 0; + const CDImage::LBA old_offset = s_physical_lba - tjump_position; const CDImage::LBA new_offset = (old_offset + sector_diff) % sectors_per_track; - const CDImage::LBA new_physical_lba = base + new_offset; + const CDImage::LBA new_physical_lba = tjump_position + new_offset; #ifdef _DEBUG - DEV_LOG("Tick diff {}, sector diff {}, old pos {}, new pos {}", diff, sector_diff, LBAToMSFString(s_physical_lba), + DEV_LOG("{} sectors @ {} SPT, old pos {}, hold pos {}, tjump pos {}, new pos {}", sector_diff, sectors_per_track, + LBAToMSFString(s_physical_lba), LBAToMSFString(hold_position), LBAToMSFString(tjump_position), LBAToMSFString(new_physical_lba)); #endif if (s_physical_lba != new_physical_lba) @@ -2849,6 +2857,10 @@ void CDROM::DoSeekComplete(TickCount ticks_late) const bool seek_okay = CompleteSeek(); if (seek_okay) { + DEV_LOG("{} seek to [{}] complete{}", logical ? "Logical" : "Physical", + LBAToMSFString(s_reader.GetLastReadSector()), + s_read_after_seek ? ", now reading" : (s_play_after_seek ? ", now playing" : "")); + // seek complete, transition to play/read if requested // INT2 is not sent on play/read if (s_read_after_seek)