CDROM: Refactor start/stop/second response

This commit is contained in:
Connor McLaughlin 2021-06-12 13:16:49 +10:00
parent 9d36ce757d
commit 36bfc461f9
3 changed files with 247 additions and 190 deletions

View file

@ -80,8 +80,14 @@ void CDROM::Initialize()
{
m_command_event = TimingEvents::CreateTimingEvent(
"CDROM Command Event", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteCommand(); }, this,
false);
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteCommand(ticks_late); },
this, false);
m_command_second_response_event = TimingEvents::CreateTimingEvent(
"CDROM Command Second Response Event", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) {
static_cast<CDROM*>(param)->ExecuteCommandSecondResponse(ticks_late);
},
this, false);
m_drive_event = TimingEvents::CreateTimingEvent(
"CDROM Drive Event", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteDrive(ticks_late); },
@ -105,32 +111,26 @@ void CDROM::Reset()
{
SoftReset();
// this should be reading sector 0
m_reader.WaitForReadToComplete();
if (m_reader.GetSectorSubQ().IsCRCValid())
m_last_subq = m_reader.GetSectorSubQ();
SetHoldPosition(0, true);
}
void CDROM::SoftReset()
{
m_command = Command::None;
m_command_event->Deactivate();
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
ClearCommandSecondResponse();
ClearDriveState();
m_status.bits = 0;
m_secondary_status.bits = 0;
m_secondary_status.motor_on = CanReadMedia();
m_secondary_status.shell_open = !CanReadMedia();
m_mode.bits = 0;
m_mode.read_raw_sector = true;
m_current_double_speed = false;
m_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
m_interrupt_flag_register = 0;
m_pending_async_interrupt = 0;
m_setloc_position = {};
m_current_lba = 0;
ResetPhysicalPosition();
if (m_reader.HasMedia())
m_reader.QueueReadSector(m_current_lba);
m_seek_start_lba = 0;
m_seek_end_lba = 0;
m_setloc_pending = false;
@ -175,6 +175,7 @@ void CDROM::SoftReset()
bool CDROM::DoState(StateWrapper& sw)
{
sw.Do(&m_command);
sw.DoEx(&m_command_second_response, 53, Command::None);
sw.Do(&m_drive_state);
sw.Do(&m_status.bits);
sw.Do(&m_secondary_status.bits);
@ -276,19 +277,10 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
// motor automatically spins up
if (m_drive_state != DriveState::ShellOpening)
{
m_drive_state = DriveState::SpinningUp;
m_drive_event->Schedule(System::GetTicksPerSecond());
}
// reading TOC? interestingly this doesn't work for GetlocL though...
CDImage::SubChannelQ subq;
if (media->Seek(0) && media->ReadRawSector(nullptr, &subq) && subq.IsCRCValid())
m_last_subq = subq;
StartMotor();
m_reader.SetMedia(std::move(media));
m_current_lba = 0;
ResetPhysicalPosition();
SetHoldPosition(0, true);
}
std::unique_ptr<CDImage> CDROM::RemoveMedia(bool force /* = false */)
@ -309,10 +301,10 @@ std::unique_ptr<CDImage> CDROM::RemoveMedia(bool force /* = false */)
m_disc_region = DiscRegion::Other;
// If the drive was doing anything, we need to abort the command.
m_drive_state = DriveState::Idle;
ClearDriveState();
ClearCommandSecondResponse();
m_command = Command::None;
m_command_event->Deactivate();
m_drive_event->Deactivate();
// The console sends an interrupt when the shell is opened regardless of whether a command was executing.
if (HasPendingAsyncInterrupt())
@ -675,6 +667,21 @@ TickCount CDROM::GetAckDelayForCommand(Command command)
return CanReadMedia() ? default_ack_delay_with_disc : default_ack_delay_no_disc;
}
TickCount CDROM::GetTicksForSpinUp()
{
// 1 second
return System::GetTicksPerSecond();
}
TickCount CDROM::GetTicksForIDRead()
{
TickCount ticks = ID_READ_TICKS;
if (m_drive_state == DriveState::SpinningUp)
ticks += m_drive_event->GetTicksUntilNextExecution();
return ticks;
}
TickCount CDROM::GetTicksForRead()
{
const TickCount tps = System::GetTicksPerSecond();
@ -705,12 +712,10 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba)
// Motor spin-up time.
if (!m_secondary_status.motor_on)
{
ticks += (m_drive_state == DriveState::SpinningUp) ? m_drive_event->GetTicksUntilNextExecution() : tps;
ticks +=
(m_drive_state == DriveState::SpinningUp) ? m_drive_event->GetTicksUntilNextExecution() : GetTicksForSpinUp();
if (m_drive_state == DriveState::ShellOpening || m_drive_state == DriveState::SpinningUp)
{
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
}
ClearDriveState();
}
if (lba_diff >= 2550)
@ -810,10 +815,11 @@ void CDROM::AbortCommand()
UpdateStatusRegister();
}
void CDROM::ExecuteCommand()
void CDROM::ExecuteCommand(TickCount ticks_late)
{
const CommandInfo& ci = s_command_info[static_cast<u8>(m_command)];
Log_DevPrintf("CDROM executing command 0x%02X (%s)", static_cast<u8>(m_command), ci.name);
Log_DevPrintf("CDROM executing command 0x%02X (%s), stat = 0x%02X", static_cast<u8>(m_command), ci.name,
m_secondary_status.bits);
if (m_param_fifo.GetSize() < ci.expected_parameters)
{
Log_WarningPrintf("Too few parameters for command 0x%02X (%s), expecting %u got %u", static_cast<u8>(m_command),
@ -864,9 +870,7 @@ void CDROM::ExecuteCommand()
else
{
SendACKAndStat();
m_drive_state = DriveState::ReadingID;
m_drive_event->Schedule(GetTicksForSeek(0) + GetTicksForRead());
QueueCommandSecondResponse(Command::GetID, GetTicksForIDRead());
}
EndCommand();
@ -883,9 +887,8 @@ void CDROM::ExecuteCommand()
else
{
SendACKAndStat();
m_drive_state = DriveState::ReadingTOC;
m_drive_event->Schedule(System::GetTicksPerSecond() / 2); // half a second
SetHoldPosition(0, true);
QueueCommandSecondResponse(Command::ReadTOC, System::GetTicksPerSecond() / 2); // half a second
}
EndCommand();
@ -1090,6 +1093,8 @@ void CDROM::ExecuteCommand()
case Command::Pause:
{
SendACKAndStat();
const bool was_reading = (m_drive_state == DriveState::Reading || m_drive_state == DriveState::Playing);
const TickCount pause_time = was_reading ? (m_mode.double_speed ? 2000000 : 1000000) : 7000;
@ -1099,20 +1104,22 @@ void CDROM::ExecuteCommand()
// hardware tests.
Log_WarningPrintf("CDROM Pause command while seeking from %u to %u - jumping to seek target", m_seek_start_lba,
m_seek_end_lba);
m_drive_event->Deactivate();
m_read_after_seek = false;
m_play_after_seek = false;
CompleteSeek();
}
Log_DebugPrintf("CDROM pause command");
SendACKAndStat();
else
{
// Stop reading.
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits();
}
// Reset audio buffer here - control room cutscene audio repeats in Dino Crisis otherwise.
ResetAudioDecoder();
m_drive_state = DriveState::Pausing;
m_drive_event->Schedule(pause_time);
QueueCommandSecondResponse(Command::Pause, pause_time);
EndCommand();
return;
@ -1121,11 +1128,10 @@ void CDROM::ExecuteCommand()
case Command::Stop:
{
const TickCount stop_time = GetTicksForStop(m_secondary_status.motor_on);
Log_DebugPrintf("CDROM stop command");
SendACKAndStat();
m_drive_state = DriveState::Stopping;
m_drive_event->Schedule(stop_time);
StopMotor();
QueueCommandSecondResponse(Command::Stop, stop_time);
EndCommand();
return;
@ -1136,15 +1142,27 @@ void CDROM::ExecuteCommand()
Log_DebugPrintf("CDROM reset command");
SendACKAndStat();
if (m_command_second_response == Command::Reset)
{
// still pending
EndCommand();
return;
}
if (IsSeeking())
UpdatePositionWhileSeeking();
m_drive_state = DriveState::Resetting;
m_drive_event->SetIntervalAndSchedule(BASE_RESET_TICKS + ((m_current_lba != 0) ? GetTicksForSeek(0) : 0));
SoftReset();
if (m_current_lba != 0)
{
m_drive_state = DriveState::SeekingImplicit;
m_drive_event->SetIntervalAndSchedule(((m_current_lba != 0) ? GetTicksForSeek(0) : 0));
m_seek_start_lba = m_current_lba;
m_seek_end_lba = 0;
}
EndCommand();
QueueCommandSecondResponse(Command::Reset, RESET_TICKS);
return;
}
break;
@ -1160,8 +1178,14 @@ void CDROM::ExecuteCommand()
{
SendACKAndStat();
m_drive_state = DriveState::Resetting;
m_drive_event->Schedule(System::GetTicksPerSecond());
// still pending?
if (m_command_second_response != Command::MotorOn)
{
if (CanReadMedia())
StartMotor();
QueueCommandSecondResponse(Command::MotorOn, MOTOR_ON_RESPONSE_TICKS);
}
}
EndCommand();
@ -1427,6 +1451,40 @@ void CDROM::ExecuteTestCommand(u8 subcommand)
}
}
void CDROM::ExecuteCommandSecondResponse(TickCount ticks_late)
{
switch (m_command_second_response)
{
case Command::GetID:
DoIDRead();
break;
case Command::ReadTOC:
case Command::Pause:
case Command::Reset:
case Command::MotorOn:
case Command::Stop:
DoStatSecondResponse();
break;
}
m_command_second_response = Command::None;
m_command_second_response_event->Deactivate();
}
void CDROM::QueueCommandSecondResponse(Command command, TickCount ticks)
{
ClearCommandSecondResponse();
m_command_second_response = command;
m_command_second_response_event->Schedule(ticks);
}
void CDROM::ClearCommandSecondResponse()
{
m_command_second_response_event->Deactivate();
m_command_second_response = Command::None;
}
void CDROM::UpdateCommandEvent()
{
// if there's a pending interrupt, we can't execute the command yet
@ -1446,10 +1504,6 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
{
switch (m_drive_state)
{
case DriveState::Resetting:
DoResetComplete(ticks_late);
break;
case DriveState::ShellOpening:
DoShellOpenComplete(ticks_late);
break;
@ -1459,20 +1513,8 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
DoSeekComplete(ticks_late);
break;
case DriveState::Pausing:
DoPauseComplete();
break;
case DriveState::Stopping:
DoStopComplete();
break;
case DriveState::ReadingID:
DoIDRead();
break;
case DriveState::ReadingTOC:
DoTOCRead();
case DriveState::SeekingImplicit:
CompleteSeek();
break;
case DriveState::Reading:
@ -1488,12 +1530,50 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
DoSpinUpComplete();
break;
// old states, no longer used, but kept for save state compatibility
case DriveState::UNUSED_ReadingID:
{
ClearDriveState();
DoIDRead();
}
break;
case DriveState::UNUSED_Resetting:
case DriveState::UNUSED_ReadingTOC:
{
ClearDriveState();
DoStatSecondResponse();
}
break;
case DriveState::UNUSED_Pausing:
{
ClearDriveState();
m_secondary_status.ClearActiveBits();
DoStatSecondResponse();
}
break;
case DriveState::UNUSED_Stopping:
{
ClearDriveState();
StopMotor();
DoStatSecondResponse();
}
break;
case DriveState::Idle:
default:
break;
}
}
void CDROM::ClearDriveState()
{
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
}
void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
{
ClearSectorBuffers();
@ -1642,19 +1722,14 @@ void CDROM::UpdatePositionWhileSeeking()
// access the image directly since we want to preserve the cached data for the seek complete
CDImage::SubChannelQ subq;
if (m_reader.ReadSectorUncached(current_lba, &subq, nullptr) && subq.IsCRCValid())
{
if (!m_reader.ReadSectorUncached(current_lba, &subq, nullptr))
Log_ErrorPrintf("Failed to read subq for sector %u for physical position", current_lba);
else if (subq.IsCRCValid())
m_last_subq = subq;
m_current_lba = current_lba;
ResetPhysicalPosition();
}
}
void CDROM::ResetPhysicalPosition()
{
const u32 ticks = TimingEvents::GetGlobalTickCounter();
m_physical_lba = m_current_lba;
m_physical_lba_update_tick = ticks;
m_current_lba = current_lba;
m_physical_lba = current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
}
void CDROM::UpdatePhysicalPosition()
@ -1701,58 +1776,35 @@ void CDROM::UpdatePhysicalPosition()
}
}
void CDROM::SetHoldPosition(CDImage::LBA lba, bool update_subq)
{
if (update_subq && m_physical_lba != lba && CanReadMedia())
{
CDImage::SubChannelQ subq;
if (!m_reader.ReadSectorUncached(lba, &subq, nullptr))
Log_ErrorPrintf("Failed to read subq for sector %u for physical position", lba);
else if (subq.IsCRCValid())
m_last_subq = subq;
}
m_current_lba = lba;
m_physical_lba = lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
}
void CDROM::DoShellOpenComplete(TickCount ticks_late)
{
// media is now readable (if any)
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
ClearDriveState();
if (m_reader.HasMedia())
{
m_drive_state = DriveState::SpinningUp;
m_drive_event->Schedule(System::GetTicksPerSecond());
}
}
void CDROM::DoResetComplete(TickCount ticks_late)
{
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_secondary_status.bits = 0;
m_secondary_status.motor_on = CanReadMedia();
m_mode.bits = 0;
m_mode.read_raw_sector = true;
m_data_fifo.Clear();
if (!CanReadMedia())
{
Log_DevPrintf("CDROM reset - no disc");
m_secondary_status.shell_open = true;
SendAsyncErrorResponse(STAT_ERROR, 0x08);
return;
}
m_current_lba = 0;
ResetPhysicalPosition();
m_reader.QueueReadSector(0);
m_async_response_fifo.Clear();
m_async_response_fifo.Push(m_secondary_status.bits);
SetAsyncInterrupt(Interrupt::Complete);
if (!CanReadMedia())
{
m_secondary_status.motor_on = false;
m_secondary_status.shell_open = true;
}
if (CanReadMedia())
StartMotor();
}
bool CDROM::CompleteSeek()
{
const bool logical = (m_drive_state == DriveState::SeekingLogical);
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
ClearDriveState();
m_secondary_status.ClearActiveBits();
bool seek_okay = m_reader.WaitForReadToComplete();
@ -1803,7 +1855,8 @@ bool CDROM::CompleteSeek()
}
m_current_lba = m_reader.GetLastReadSector();
ResetPhysicalPosition();
m_physical_lba = m_current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
return seek_okay;
}
@ -1844,25 +1897,14 @@ void CDROM::DoSeekComplete(TickCount ticks_late)
UpdateStatusRegister();
}
void CDROM::DoPauseComplete()
void CDROM::DoStatSecondResponse()
{
Log_DebugPrintf("Pause complete");
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits();
m_async_response_fifo.Clear();
m_async_response_fifo.Push(m_secondary_status.bits);
SetAsyncInterrupt(Interrupt::Complete);
}
void CDROM::DoStopComplete()
{
Log_DebugPrintf("Stop complete");
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits();
m_secondary_status.motor_on = false;
// Mainly for Reset/MotorOn.
if (!CanReadMedia())
{
SendAsyncErrorResponse(STAT_ERROR, 0x08);
return;
}
m_async_response_fifo.Clear();
m_async_response_fifo.Push(m_secondary_status.bits);
@ -1872,8 +1914,7 @@ void CDROM::DoStopComplete()
void CDROM::DoChangeSessionComplete()
{
Log_DebugPrintf("Changing session complete");
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
ClearDriveState();
m_secondary_status.ClearActiveBits();
m_secondary_status.motor_on = true;
@ -1902,8 +1943,6 @@ void CDROM::DoSpinUpComplete()
void CDROM::DoIDRead()
{
Log_DebugPrintf("ID read complete");
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits();
m_secondary_status.motor_on = CanReadMedia();
@ -1917,11 +1956,6 @@ void CDROM::DoIDRead()
}
else
{
// this is where it would get read from the start of the disc?
m_current_lba = 0;
ResetPhysicalPosition();
m_reader.QueueReadSector(0);
if (!IsMediaPS1Disc())
{
stat_byte |= STAT_ID_ERROR;
@ -1948,17 +1982,6 @@ void CDROM::DoIDRead()
SetAsyncInterrupt((flags_byte != 0) ? Interrupt::Error : Interrupt::Complete);
}
void CDROM::DoTOCRead()
{
Log_DebugPrintf("TOC read complete");
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_async_response_fifo.Clear();
m_async_response_fifo.Push(m_secondary_status.bits);
SetAsyncInterrupt(Interrupt::Complete);
}
void CDROM::StopReadingWithDataEnd()
{
ClearAsyncInterrupt();
@ -1966,8 +1989,29 @@ void CDROM::StopReadingWithDataEnd()
SetAsyncInterrupt(Interrupt::DataEnd);
m_secondary_status.ClearActiveBits();
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
ClearDriveState();
}
void CDROM::StartMotor()
{
if (m_drive_state == DriveState::SpinningUp)
{
Log_DevPrintf("Starting motor - already spinning up");
return;
}
Log_DevPrintf("Starting motor");
m_drive_state = DriveState::SpinningUp;
m_drive_event->Schedule(GetTicksForSpinUp());
}
void CDROM::StopMotor()
{
m_secondary_status.ClearActiveBits();
m_secondary_status.motor_on = false;
ClearDriveState();
SetHoldPosition(0, false);
m_last_sector_header_valid = false; // TODO: correct?
}
void CDROM::DoSectorRead()
@ -1978,7 +2022,8 @@ void CDROM::DoSectorRead()
Panic("Sector read failed");
m_current_lba = m_reader.GetLastReadSector();
ResetPhysicalPosition();
m_physical_lba = m_current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
const CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ();
const bool subq_valid = subq.IsCRCValid();
@ -1997,7 +2042,7 @@ void CDROM::DoSectorRead()
{
Log_DevPrintf("Read reached lead-out area of disc at LBA %u, stopping", m_reader.GetLastReadSector());
StopReadingWithDataEnd();
m_secondary_status.motor_on = false;
StopMotor();
return;
}
@ -2013,7 +2058,7 @@ void CDROM::DoSectorRead()
else if (m_mode.auto_pause && subq.track_number_bcd != m_play_track_number_bcd)
{
// we don't want to update the position if the track changes, so we check it before reading the actual sector.
Log_DevPrintf("Auto pause at the end of track %02x (LBA %u)", m_last_subq.track_number_bcd, m_current_lba);
Log_DevPrintf("Auto pause at the start of track %02x (LBA %u)", m_last_subq.track_number_bcd, m_current_lba);
StopReadingWithDataEnd();
return;
}
@ -2506,9 +2551,9 @@ void CDROM::DrawDebugWindow()
if (ImGui::CollapsingHeader("Status/Mode", ImGuiTreeNodeFlags_DefaultOpen))
{
static constexpr std::array<const char*, 13> drive_state_names = {
static constexpr std::array<const char*, 14> drive_state_names = {
{"Idle", "Opening Shell", "Resetting", "Seeking (Physical)", "Seeking (Logical)", "Reading ID", "Reading TOC",
"Reading", "Playing", "Pausing", "Stopping", "Changing Session", "Spinning Up"}};
"Reading", "Playing", "Pausing", "Stopping", "Changing Session", "Spinning Up", "Seeking (Implicit)"}};
ImGui::Columns(3);
@ -2598,7 +2643,8 @@ void CDROM::DrawDebugWindow()
if (HasPendingCommand())
{
ImGui::TextColored(active_color, "Command: 0x%02X (%d ticks remaining)", static_cast<u8>(m_command),
ImGui::TextColored(active_color, "Command: %s (0x%02X) (%d ticks remaining)",
s_command_info[static_cast<u8>(m_command)].name, static_cast<u8>(m_command),
m_command_event->IsActive() ? m_command_event->GetTicksUntilNextExecution() : 0);
}
else

View file

@ -78,7 +78,9 @@ private:
AUDIO_FIFO_SIZE = 44100 * 2,
AUDIO_FIFO_LOW_WATERMARK = 10,
BASE_RESET_TICKS = 400000,
RESET_TICKS = 400000,
ID_READ_TICKS = 33868,
MOTOR_ON_RESPONSE_TICKS = 400000,
MAX_FAST_FORWARD_RATE = 12,
FAST_FORWARD_RATE_STEP = 4
@ -137,17 +139,18 @@ private:
{
Idle,
ShellOpening,
Resetting,
UNUSED_Resetting,
SeekingPhysical,
SeekingLogical,
ReadingID,
ReadingTOC,
UNUSED_ReadingID,
UNUSED_ReadingTOC,
Reading,
Playing,
Pausing,
Stopping,
UNUSED_Pausing,
UNUSED_Stopping,
ChangingSession,
SpinningUp
SpinningUp,
SeekingImplicit
};
union StatusRegister
@ -222,10 +225,11 @@ private:
void SoftReset();
ALWAYS_INLINE bool IsDriveIdle() const { return m_drive_state == DriveState::Idle; }
ALWAYS_INLINE bool IsMotorOn() const { return m_secondary_status.motor_on; }
ALWAYS_INLINE bool IsSeeking() const
{
return (m_drive_state == DriveState::SeekingLogical || m_drive_state == DriveState::SeekingPhysical ||
m_drive_state == DriveState::Resetting);
m_drive_state == DriveState::SeekingImplicit);
}
ALWAYS_INLINE bool IsReadingOrPlaying() const
{
@ -261,6 +265,8 @@ private:
bool HasPendingDiscEvent() const;
TickCount GetAckDelayForCommand(Command command);
TickCount GetTicksForSpinUp();
TickCount GetTicksForIDRead();
TickCount GetTicksForRead();
TickCount GetTicksForSeek(CDImage::LBA new_lba);
TickCount GetTicksForStop(bool motor_was_on);
@ -270,31 +276,34 @@ private:
void BeginCommand(Command command); // also update status register
void EndCommand(); // also updates status register
void AbortCommand();
void ExecuteCommand();
void ExecuteCommand(TickCount ticks_late);
void ExecuteTestCommand(u8 subcommand);
void ExecuteCommandSecondResponse(TickCount ticks_late);
void QueueCommandSecondResponse(Command command, TickCount ticks);
void ClearCommandSecondResponse();
void UpdateCommandEvent();
void ExecuteDrive(TickCount ticks_late);
void ClearDriveState();
void BeginReading(TickCount ticks_late = 0, bool after_seek = false);
void BeginPlaying(u8 track, TickCount ticks_late = 0, bool after_seek = false);
void DoShellOpenComplete(TickCount ticks_late);
void DoResetComplete(TickCount ticks_late);
void DoSeekComplete(TickCount ticks_late);
void DoPauseComplete();
void DoStopComplete();
void DoStatSecondResponse();
void DoChangeSessionComplete();
void DoSpinUpComplete();
void DoIDRead();
void DoTOCRead();
void DoSectorRead();
void ProcessDataSectorHeader(const u8* raw_sector);
void ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
void ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
void ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
void StopReadingWithDataEnd();
void StartMotor();
void StopMotor();
void BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek);
void UpdatePositionWhileSeeking();
void ResetPhysicalPosition();
void UpdatePhysicalPosition();
void SetHoldPosition(CDImage::LBA lba, bool update_subq);
void ResetCurrentXAFile();
void ResetAudioDecoder();
void LoadDataFIFO();
@ -304,9 +313,11 @@ private:
void ResampleXAADPCM(const s16* frames_in, u32 num_frames_in);
std::unique_ptr<TimingEvent> m_command_event;
std::unique_ptr<TimingEvent> m_command_second_response_event;
std::unique_ptr<TimingEvent> m_drive_event;
Command m_command = Command::None;
Command m_command_second_response = Command::None;
DriveState m_drive_state = DriveState::Idle;
DiscRegion m_disc_region = DiscRegion::Other;
@ -320,10 +331,10 @@ private:
u8 m_pending_async_interrupt = 0;
CDImage::Position m_setloc_position = {};
CDImage::LBA m_current_lba{};
CDImage::LBA m_current_lba{}; // this is the hold position
CDImage::LBA m_seek_start_lba{};
CDImage::LBA m_seek_end_lba{};
CDImage::LBA m_physical_lba{};
CDImage::LBA m_physical_lba{}; // current position of the disc with respect to time
u32 m_physical_lba_update_tick = 0;
bool m_setloc_pending = false;
bool m_read_after_seek = false;

View file

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