CDROM: Improve simulation of seek timing

This commit is contained in:
Connor McLaughlin 2021-06-19 15:15:54 +10:00
parent 2c19c7ce57
commit 67344070ea
3 changed files with 63 additions and 29 deletions

View file

@ -264,6 +264,7 @@ bool CDROM::DoState(StateWrapper& sw)
sw.Do(&m_seek_end_lba); sw.Do(&m_seek_end_lba);
sw.DoEx(&m_physical_lba, 49, m_current_lba); sw.DoEx(&m_physical_lba, 49, m_current_lba);
sw.DoEx(&m_physical_lba_update_tick, 49, static_cast<u32>(0)); sw.DoEx(&m_physical_lba_update_tick, 49, static_cast<u32>(0));
sw.DoEx(&m_physical_lba_update_carry, 54, static_cast<u32>(0));
sw.Do(&m_setloc_pending); sw.Do(&m_setloc_pending);
sw.Do(&m_read_after_seek); sw.Do(&m_read_after_seek);
sw.Do(&m_play_after_seek); sw.Do(&m_play_after_seek);
@ -779,7 +780,9 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
else else
UpdatePhysicalPosition(false); UpdatePhysicalPosition(false);
const TickCount tps = System::MASTER_CLOCK; const u32 ticks_per_sector =
m_mode.double_speed ? static_cast<u32>(System::MASTER_CLOCK / 150) : static_cast<u32>(System::MASTER_CLOCK / 75);
const u32 ticks_per_second = static_cast<u32>(System::MASTER_CLOCK);
const CDImage::LBA current_lba = m_secondary_status.motor_on ? (IsSeeking() ? m_seek_end_lba : m_physical_lba) : 0; const CDImage::LBA current_lba = m_secondary_status.motor_on ? (IsSeeking() ? m_seek_end_lba : m_physical_lba) : 0;
const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba)); const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba));
@ -794,22 +797,33 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
if (lba_diff < 32) if (lba_diff < 32)
{ {
ticks += static_cast<u32>(GetTicksForRead()) * std::min<u32>(BASE_SECTORS_PER_TRACK, lba_diff); ticks += ticks_per_sector * std::min<u32>(5u, lba_diff);
} }
else else
{ {
// This is a very inaccurate model. // This is a still not a very accurate model, but it's roughly in line with the behavior of hardware tests.
// TODO: Use the actual time for track jumps. const float disc_distance = 0.2323384936f * std::log(static_cast<float>((new_lba / 4500) + 1u));
// 1000ms for the whole disc float seconds;
ticks += std::max<u32>( if (lba_diff <= CDImage::FRAMES_PER_SECOND)
20000, static_cast<u32>(((static_cast<u64>(lba_diff) * static_cast<u64>(tps) * static_cast<u64>(1000)) / {
(72 * CDImage::FRAMES_PER_MINUTE)) / // 30ms + (diff * 30ms) + (disc distance * 30ms)
1000)); seconds = 0.03f + ((static_cast<float>(lba_diff) / static_cast<float>(CDImage::FRAMES_PER_SECOND)) * 0.03f) +
(disc_distance * 0.03f);
}
else if (lba_diff <= CDImage::FRAMES_PER_MINUTE)
{
// 150ms + (diff * 30ms) + (disc distance * 50ms)
seconds = 0.15f + ((static_cast<float>(lba_diff) / static_cast<float>(CDImage::FRAMES_PER_MINUTE)) * 0.03f) +
(disc_distance * 0.05f);
}
else
{
// 200ms + (diff * 500ms)
seconds = 0.2f + ((static_cast<float>(lba_diff) / static_cast<float>(72 * CDImage::FRAMES_PER_MINUTE)) * 0.4f);
}
// 300ms for non-short seeks (1 minute) ticks += static_cast<u32>(seconds * static_cast<float>(ticks_per_second));
if (lba_diff >= CDImage::FRAMES_PER_MINUTE)
ticks += static_cast<u32>((u64(tps) * 300) / 1000);
} }
if (m_drive_state == DriveState::ChangingSpeedOrTOCRead && !ignore_speed_change) if (m_drive_state == DriveState::ChangingSpeedOrTOCRead && !ignore_speed_change)
@ -818,12 +832,13 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba, bool ignore_speed_change)
const TickCount remaining_change_ticks = m_drive_event->GetTicksUntilNextExecution(); const TickCount remaining_change_ticks = m_drive_event->GetTicksUntilNextExecution();
ticks += remaining_change_ticks; ticks += remaining_change_ticks;
Log_DevPrintf("Seek time for %u LBAs: %d (%d for speed change/implicit TOC read)", lba_diff, ticks, Log_DevPrintf("Seek time for %u LBAs: %d (%.3f ms) (%d for speed change/implicit TOC read)", lba_diff, ticks,
remaining_change_ticks); (static_cast<float>(ticks) / static_cast<float>(ticks_per_second)) * 1000.0f, remaining_change_ticks);
} }
else else
{ {
Log_DevPrintf("Seek time for %u LBAs: %d", lba_diff, ticks); Log_DevPrintf("Seek time for %u LBAs: %d (%.3f ms)", lba_diff, ticks,
(static_cast<float>(ticks) / static_cast<float>(ticks_per_second)) * 1000.0f);
} }
if (g_settings.cdrom_seek_speedup > 1) if (g_settings.cdrom_seek_speedup > 1)
@ -849,7 +864,7 @@ TickCount CDROM::GetTicksForTOCRead()
if (!HasMedia()) if (!HasMedia())
return 0; return 0;
return System::GetTicksPerSecond(); return System::GetTicksPerSecond() / 2u;
} }
CDImage::LBA CDROM::GetNextSectorToBeRead() CDImage::LBA CDROM::GetNextSectorToBeRead()
@ -1109,7 +1124,7 @@ void CDROM::ExecuteCommand(TickCount ticks_late)
m_async_command_parameter = session; m_async_command_parameter = session;
m_drive_state = DriveState::ChangingSession; m_drive_state = DriveState::ChangingSession;
m_drive_event->Schedule(System::GetTicksPerSecond() / 2); // half a second m_drive_event->Schedule(GetTicksForTOCRead());
} }
EndCommand(); EndCommand();
@ -1863,6 +1878,7 @@ void CDROM::UpdatePositionWhileSeeking()
m_current_lba = current_lba; m_current_lba = current_lba;
m_physical_lba = current_lba; m_physical_lba = current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter(); m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
m_physical_lba_update_carry = 0;
} }
void CDROM::UpdatePhysicalPosition(bool update_logical) void CDROM::UpdatePhysicalPosition(bool update_logical)
@ -1874,15 +1890,31 @@ void CDROM::UpdatePhysicalPosition(bool update_logical)
return; return;
} }
const u32 diff = ticks - m_physical_lba_update_tick; const u32 ticks_per_read = GetTicksForRead();
const u32 sector_diff = diff / GetTicksForRead(); const u32 diff = ticks - m_physical_lba_update_tick + m_physical_lba_update_carry;
const u32 sector_diff = diff / ticks_per_read;
const u32 carry = diff % ticks_per_read;
if (sector_diff > 0) if (sector_diff > 0)
{ {
const CDImage::LBA hold_offset = m_last_sector_header_valid ? 2 : 0; CDImage::LBA hold_offset;
const CDImage::LBA sectors_per_track = BASE_SECTORS_PER_TRACK + hold_offset; CDImage::LBA sectors_per_track;
// hardware tests show that it holds much closer to the target sector in logical mode
if (m_last_sector_header_valid)
{
hold_offset = 2;
sectors_per_track = 4;
}
else
{
hold_offset = 0;
sectors_per_track =
static_cast<CDImage::LBA>(7.0f + 2.811844405f * std::log(static_cast<float>(m_current_lba / 4500u) + 1u));
}
const CDImage::LBA hold_position = m_current_lba + hold_offset; const CDImage::LBA hold_position = m_current_lba + hold_offset;
const CDImage::LBA base = const CDImage::LBA base =
(hold_position >= sectors_per_track) ? (hold_position - sectors_per_track) : hold_position; (hold_position >= (sectors_per_track - 1)) ? (hold_position - (sectors_per_track - 1)) : hold_position;
if (m_physical_lba < base) if (m_physical_lba < base)
m_physical_lba = base; m_physical_lba = base;
@ -1915,6 +1947,7 @@ void CDROM::UpdatePhysicalPosition(bool update_logical)
} }
m_physical_lba_update_tick = ticks; m_physical_lba_update_tick = ticks;
m_physical_lba_update_carry = carry;
} }
} }
} }
@ -1933,6 +1966,7 @@ void CDROM::SetHoldPosition(CDImage::LBA lba, bool update_subq)
m_current_lba = lba; m_current_lba = lba;
m_physical_lba = lba; m_physical_lba = lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter(); m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
m_physical_lba_update_carry = 0;
} }
void CDROM::DoShellOpenComplete(TickCount ticks_late) void CDROM::DoShellOpenComplete(TickCount ticks_late)
@ -2000,6 +2034,7 @@ bool CDROM::CompleteSeek()
m_current_lba = m_reader.GetLastReadSector(); m_current_lba = m_reader.GetLastReadSector();
m_physical_lba = m_current_lba; m_physical_lba = m_current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter(); m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
m_physical_lba_update_carry = 0;
return seek_okay; return seek_okay;
} }
@ -2174,6 +2209,7 @@ void CDROM::DoSectorRead()
m_current_lba = m_reader.GetLastReadSector(); m_current_lba = m_reader.GetLastReadSector();
m_physical_lba = m_current_lba; m_physical_lba = m_current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter(); m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
m_physical_lba_update_carry = 0;
const CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ(); const CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ();
const bool subq_valid = subq.IsCRCValid(); const bool subq_valid = subq.IsCRCValid();

View file

@ -83,10 +83,7 @@ private:
MOTOR_ON_RESPONSE_TICKS = 400000, MOTOR_ON_RESPONSE_TICKS = 400000,
MAX_FAST_FORWARD_RATE = 12, MAX_FAST_FORWARD_RATE = 12,
FAST_FORWARD_RATE_STEP = 4, FAST_FORWARD_RATE_STEP = 4
// Actually varies depending the distance into the disc.
BASE_SECTORS_PER_TRACK = 9,
}; };
static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F; static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F;
@ -342,6 +339,7 @@ private:
CDImage::LBA m_seek_end_lba{}; CDImage::LBA m_seek_end_lba{};
CDImage::LBA m_physical_lba{}; // current position of the disc with respect to time CDImage::LBA m_physical_lba{}; // current position of the disc with respect to time
u32 m_physical_lba_update_tick = 0; u32 m_physical_lba_update_tick = 0;
u32 m_physical_lba_update_carry = 0;
bool m_setloc_pending = false; bool m_setloc_pending = false;
bool m_read_after_seek = false; bool m_read_after_seek = false;
bool m_play_after_seek = false; bool m_play_after_seek = false;

View file

@ -2,7 +2,7 @@
#include "types.h" #include "types.h"
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544; static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
static constexpr u32 SAVE_STATE_VERSION = 53; static constexpr u32 SAVE_STATE_VERSION = 54;
static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42; static constexpr u32 SAVE_STATE_MINIMUM_VERSION = 42;
static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION); static_assert(SAVE_STATE_VERSION >= SAVE_STATE_MINIMUM_VERSION);