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( m_command_event = TimingEvents::CreateTimingEvent(
"CDROM Command Event", 1, 1, "CDROM Command Event", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteCommand(); }, this, [](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteCommand(ticks_late); },
false); 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( m_drive_event = TimingEvents::CreateTimingEvent(
"CDROM Drive Event", 1, 1, "CDROM Drive Event", 1, 1,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteDrive(ticks_late); }, [](void* param, TickCount ticks, TickCount ticks_late) { static_cast<CDROM*>(param)->ExecuteDrive(ticks_late); },
@ -105,32 +111,26 @@ void CDROM::Reset()
{ {
SoftReset(); SoftReset();
// this should be reading sector 0 SetHoldPosition(0, true);
m_reader.WaitForReadToComplete();
if (m_reader.GetSectorSubQ().IsCRCValid())
m_last_subq = m_reader.GetSectorSubQ();
} }
void CDROM::SoftReset() void CDROM::SoftReset()
{ {
m_command = Command::None; m_command = Command::None;
m_command_event->Deactivate(); m_command_event->Deactivate();
m_drive_state = DriveState::Idle; ClearCommandSecondResponse();
m_drive_event->Deactivate(); ClearDriveState();
m_status.bits = 0; m_status.bits = 0;
m_secondary_status.bits = 0; m_secondary_status.bits = 0;
m_secondary_status.motor_on = CanReadMedia(); m_secondary_status.motor_on = CanReadMedia();
m_secondary_status.shell_open = !CanReadMedia(); m_secondary_status.shell_open = !CanReadMedia();
m_mode.bits = 0; m_mode.bits = 0;
m_mode.read_raw_sector = true;
m_current_double_speed = false; m_current_double_speed = false;
m_interrupt_enable_register = INTERRUPT_REGISTER_MASK; m_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
m_interrupt_flag_register = 0; m_interrupt_flag_register = 0;
m_pending_async_interrupt = 0; m_pending_async_interrupt = 0;
m_setloc_position = {}; 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_start_lba = 0;
m_seek_end_lba = 0; m_seek_end_lba = 0;
m_setloc_pending = false; m_setloc_pending = false;
@ -175,6 +175,7 @@ void CDROM::SoftReset()
bool CDROM::DoState(StateWrapper& sw) bool CDROM::DoState(StateWrapper& sw)
{ {
sw.Do(&m_command); sw.Do(&m_command);
sw.DoEx(&m_command_second_response, 53, Command::None);
sw.Do(&m_drive_state); sw.Do(&m_drive_state);
sw.Do(&m_status.bits); sw.Do(&m_status.bits);
sw.Do(&m_secondary_status.bits); sw.Do(&m_secondary_status.bits);
@ -276,19 +277,10 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
// motor automatically spins up // motor automatically spins up
if (m_drive_state != DriveState::ShellOpening) if (m_drive_state != DriveState::ShellOpening)
{ StartMotor();
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;
m_reader.SetMedia(std::move(media)); m_reader.SetMedia(std::move(media));
m_current_lba = 0; SetHoldPosition(0, true);
ResetPhysicalPosition();
} }
std::unique_ptr<CDImage> CDROM::RemoveMedia(bool force /* = false */) 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; m_disc_region = DiscRegion::Other;
// If the drive was doing anything, we need to abort the command. // 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 = Command::None;
m_command_event->Deactivate(); 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. // The console sends an interrupt when the shell is opened regardless of whether a command was executing.
if (HasPendingAsyncInterrupt()) if (HasPendingAsyncInterrupt())
@ -675,6 +667,21 @@ TickCount CDROM::GetAckDelayForCommand(Command command)
return CanReadMedia() ? default_ack_delay_with_disc : default_ack_delay_no_disc; 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() TickCount CDROM::GetTicksForRead()
{ {
const TickCount tps = System::GetTicksPerSecond(); const TickCount tps = System::GetTicksPerSecond();
@ -705,12 +712,10 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba)
// Motor spin-up time. // Motor spin-up time.
if (!m_secondary_status.motor_on) 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) if (m_drive_state == DriveState::ShellOpening || m_drive_state == DriveState::SpinningUp)
{ ClearDriveState();
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
}
} }
if (lba_diff >= 2550) if (lba_diff >= 2550)
@ -810,10 +815,11 @@ void CDROM::AbortCommand()
UpdateStatusRegister(); UpdateStatusRegister();
} }
void CDROM::ExecuteCommand() void CDROM::ExecuteCommand(TickCount ticks_late)
{ {
const CommandInfo& ci = s_command_info[static_cast<u8>(m_command)]; 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) 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), 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 else
{ {
SendACKAndStat(); SendACKAndStat();
QueueCommandSecondResponse(Command::GetID, GetTicksForIDRead());
m_drive_state = DriveState::ReadingID;
m_drive_event->Schedule(GetTicksForSeek(0) + GetTicksForRead());
} }
EndCommand(); EndCommand();
@ -883,9 +887,8 @@ void CDROM::ExecuteCommand()
else else
{ {
SendACKAndStat(); SendACKAndStat();
SetHoldPosition(0, true);
m_drive_state = DriveState::ReadingTOC; QueueCommandSecondResponse(Command::ReadTOC, System::GetTicksPerSecond() / 2); // half a second
m_drive_event->Schedule(System::GetTicksPerSecond() / 2); // half a second
} }
EndCommand(); EndCommand();
@ -1090,6 +1093,8 @@ void CDROM::ExecuteCommand()
case Command::Pause: case Command::Pause:
{ {
SendACKAndStat();
const bool was_reading = (m_drive_state == DriveState::Reading || m_drive_state == DriveState::Playing); 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; const TickCount pause_time = was_reading ? (m_mode.double_speed ? 2000000 : 1000000) : 7000;
@ -1099,20 +1104,22 @@ void CDROM::ExecuteCommand()
// hardware tests. // hardware tests.
Log_WarningPrintf("CDROM Pause command while seeking from %u to %u - jumping to seek target", m_seek_start_lba, Log_WarningPrintf("CDROM Pause command while seeking from %u to %u - jumping to seek target", m_seek_start_lba,
m_seek_end_lba); m_seek_end_lba);
m_drive_event->Deactivate();
m_read_after_seek = false; m_read_after_seek = false;
m_play_after_seek = false; m_play_after_seek = false;
CompleteSeek(); CompleteSeek();
} }
else
Log_DebugPrintf("CDROM pause command"); {
SendACKAndStat(); // 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. // Reset audio buffer here - control room cutscene audio repeats in Dino Crisis otherwise.
ResetAudioDecoder(); ResetAudioDecoder();
m_drive_state = DriveState::Pausing; QueueCommandSecondResponse(Command::Pause, pause_time);
m_drive_event->Schedule(pause_time);
EndCommand(); EndCommand();
return; return;
@ -1121,11 +1128,10 @@ void CDROM::ExecuteCommand()
case Command::Stop: case Command::Stop:
{ {
const TickCount stop_time = GetTicksForStop(m_secondary_status.motor_on); const TickCount stop_time = GetTicksForStop(m_secondary_status.motor_on);
Log_DebugPrintf("CDROM stop command");
SendACKAndStat(); SendACKAndStat();
m_drive_state = DriveState::Stopping; StopMotor();
m_drive_event->Schedule(stop_time); QueueCommandSecondResponse(Command::Stop, stop_time);
EndCommand(); EndCommand();
return; return;
@ -1136,15 +1142,27 @@ void CDROM::ExecuteCommand()
Log_DebugPrintf("CDROM reset command"); Log_DebugPrintf("CDROM reset command");
SendACKAndStat(); SendACKAndStat();
if (m_command_second_response == Command::Reset)
{
// still pending
EndCommand();
return;
}
if (IsSeeking()) if (IsSeeking())
UpdatePositionWhileSeeking(); UpdatePositionWhileSeeking();
m_drive_state = DriveState::Resetting; SoftReset();
m_drive_event->SetIntervalAndSchedule(BASE_RESET_TICKS + ((m_current_lba != 0) ? GetTicksForSeek(0) : 0));
m_seek_start_lba = m_current_lba;
m_seek_end_lba = 0;
EndCommand(); 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;
}
QueueCommandSecondResponse(Command::Reset, RESET_TICKS);
return; return;
} }
break; break;
@ -1160,8 +1178,14 @@ void CDROM::ExecuteCommand()
{ {
SendACKAndStat(); SendACKAndStat();
m_drive_state = DriveState::Resetting; // still pending?
m_drive_event->Schedule(System::GetTicksPerSecond()); if (m_command_second_response != Command::MotorOn)
{
if (CanReadMedia())
StartMotor();
QueueCommandSecondResponse(Command::MotorOn, MOTOR_ON_RESPONSE_TICKS);
}
} }
EndCommand(); 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() void CDROM::UpdateCommandEvent()
{ {
// if there's a pending interrupt, we can't execute the command yet // 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) switch (m_drive_state)
{ {
case DriveState::Resetting:
DoResetComplete(ticks_late);
break;
case DriveState::ShellOpening: case DriveState::ShellOpening:
DoShellOpenComplete(ticks_late); DoShellOpenComplete(ticks_late);
break; break;
@ -1459,20 +1513,8 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
DoSeekComplete(ticks_late); DoSeekComplete(ticks_late);
break; break;
case DriveState::Pausing: case DriveState::SeekingImplicit:
DoPauseComplete(); CompleteSeek();
break;
case DriveState::Stopping:
DoStopComplete();
break;
case DriveState::ReadingID:
DoIDRead();
break;
case DriveState::ReadingTOC:
DoTOCRead();
break; break;
case DriveState::Reading: case DriveState::Reading:
@ -1488,12 +1530,50 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
DoSpinUpComplete(); DoSpinUpComplete();
break; 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: case DriveState::Idle:
default: default:
break; break;
} }
} }
void CDROM::ClearDriveState()
{
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
}
void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = false */) void CDROM::BeginReading(TickCount ticks_late /* = 0 */, bool after_seek /* = false */)
{ {
ClearSectorBuffers(); ClearSectorBuffers();
@ -1642,19 +1722,14 @@ void CDROM::UpdatePositionWhileSeeking()
// access the image directly since we want to preserve the cached data for the seek complete // access the image directly since we want to preserve the cached data for the seek complete
CDImage::SubChannelQ subq; 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_last_subq = subq;
m_current_lba = current_lba;
ResetPhysicalPosition();
}
}
void CDROM::ResetPhysicalPosition() m_current_lba = current_lba;
{ m_physical_lba = current_lba;
const u32 ticks = TimingEvents::GetGlobalTickCounter(); m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
m_physical_lba = m_current_lba;
m_physical_lba_update_tick = ticks;
} }
void CDROM::UpdatePhysicalPosition() 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) void CDROM::DoShellOpenComplete(TickCount ticks_late)
{ {
// media is now readable (if any) // media is now readable (if any)
m_drive_state = DriveState::Idle; ClearDriveState();
m_drive_event->Deactivate();
if (m_reader.HasMedia()) if (CanReadMedia())
{ StartMotor();
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;
}
} }
bool CDROM::CompleteSeek() bool CDROM::CompleteSeek()
{ {
const bool logical = (m_drive_state == DriveState::SeekingLogical); const bool logical = (m_drive_state == DriveState::SeekingLogical);
m_drive_state = DriveState::Idle; ClearDriveState();
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits(); m_secondary_status.ClearActiveBits();
bool seek_okay = m_reader.WaitForReadToComplete(); bool seek_okay = m_reader.WaitForReadToComplete();
@ -1803,7 +1855,8 @@ bool CDROM::CompleteSeek()
} }
m_current_lba = m_reader.GetLastReadSector(); m_current_lba = m_reader.GetLastReadSector();
ResetPhysicalPosition(); m_physical_lba = m_current_lba;
m_physical_lba_update_tick = TimingEvents::GetGlobalTickCounter();
return seek_okay; return seek_okay;
} }
@ -1844,25 +1897,14 @@ void CDROM::DoSeekComplete(TickCount ticks_late)
UpdateStatusRegister(); UpdateStatusRegister();
} }
void CDROM::DoPauseComplete() void CDROM::DoStatSecondResponse()
{ {
Log_DebugPrintf("Pause complete"); // Mainly for Reset/MotorOn.
m_drive_state = DriveState::Idle; if (!CanReadMedia())
m_drive_event->Deactivate(); {
m_secondary_status.ClearActiveBits(); SendAsyncErrorResponse(STAT_ERROR, 0x08);
return;
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;
m_async_response_fifo.Clear(); m_async_response_fifo.Clear();
m_async_response_fifo.Push(m_secondary_status.bits); m_async_response_fifo.Push(m_secondary_status.bits);
@ -1872,8 +1914,7 @@ void CDROM::DoStopComplete()
void CDROM::DoChangeSessionComplete() void CDROM::DoChangeSessionComplete()
{ {
Log_DebugPrintf("Changing session complete"); Log_DebugPrintf("Changing session complete");
m_drive_state = DriveState::Idle; ClearDriveState();
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits(); m_secondary_status.ClearActiveBits();
m_secondary_status.motor_on = true; m_secondary_status.motor_on = true;
@ -1902,8 +1943,6 @@ void CDROM::DoSpinUpComplete()
void CDROM::DoIDRead() void CDROM::DoIDRead()
{ {
Log_DebugPrintf("ID read complete"); Log_DebugPrintf("ID read complete");
m_drive_state = DriveState::Idle;
m_drive_event->Deactivate();
m_secondary_status.ClearActiveBits(); m_secondary_status.ClearActiveBits();
m_secondary_status.motor_on = CanReadMedia(); m_secondary_status.motor_on = CanReadMedia();
@ -1917,11 +1956,6 @@ void CDROM::DoIDRead()
} }
else 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()) if (!IsMediaPS1Disc())
{ {
stat_byte |= STAT_ID_ERROR; stat_byte |= STAT_ID_ERROR;
@ -1948,17 +1982,6 @@ void CDROM::DoIDRead()
SetAsyncInterrupt((flags_byte != 0) ? Interrupt::Error : Interrupt::Complete); 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() void CDROM::StopReadingWithDataEnd()
{ {
ClearAsyncInterrupt(); ClearAsyncInterrupt();
@ -1966,8 +1989,29 @@ void CDROM::StopReadingWithDataEnd()
SetAsyncInterrupt(Interrupt::DataEnd); SetAsyncInterrupt(Interrupt::DataEnd);
m_secondary_status.ClearActiveBits(); m_secondary_status.ClearActiveBits();
m_drive_state = DriveState::Idle; ClearDriveState();
m_drive_event->Deactivate(); }
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() void CDROM::DoSectorRead()
@ -1978,7 +2022,8 @@ void CDROM::DoSectorRead()
Panic("Sector read failed"); Panic("Sector read failed");
m_current_lba = m_reader.GetLastReadSector(); 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 CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ();
const bool subq_valid = subq.IsCRCValid(); 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()); Log_DevPrintf("Read reached lead-out area of disc at LBA %u, stopping", m_reader.GetLastReadSector());
StopReadingWithDataEnd(); StopReadingWithDataEnd();
m_secondary_status.motor_on = false; StopMotor();
return; return;
} }
@ -2013,7 +2058,7 @@ void CDROM::DoSectorRead()
else if (m_mode.auto_pause && subq.track_number_bcd != m_play_track_number_bcd) 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. // 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(); StopReadingWithDataEnd();
return; return;
} }
@ -2506,9 +2551,9 @@ void CDROM::DrawDebugWindow()
if (ImGui::CollapsingHeader("Status/Mode", ImGuiTreeNodeFlags_DefaultOpen)) 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", {"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); ImGui::Columns(3);
@ -2598,7 +2643,8 @@ void CDROM::DrawDebugWindow()
if (HasPendingCommand()) 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); m_command_event->IsActive() ? m_command_event->GetTicksUntilNextExecution() : 0);
} }
else else

View file

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