mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-25 15:15:40 +00:00
CDROM: Add delay when swapping discs
Fixes broken disc swap detection in Metal Gear Solid.
This commit is contained in:
parent
415880fc40
commit
340640821e
|
@ -48,7 +48,7 @@ void CDROM::SoftReset()
|
|||
m_drive_event->Deactivate();
|
||||
m_status.bits = 0;
|
||||
m_secondary_status.bits = 0;
|
||||
m_secondary_status.motor_on = HasMedia();
|
||||
m_secondary_status.motor_on = CanReadMedia();
|
||||
m_mode.bits = 0;
|
||||
m_current_double_speed = false;
|
||||
m_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
|
||||
|
@ -164,19 +164,9 @@ bool CDROM::DoState(StateWrapper& sw)
|
|||
return !sw.HasError();
|
||||
}
|
||||
|
||||
bool CDROM::HasMedia() const
|
||||
{
|
||||
return m_reader.HasMedia();
|
||||
}
|
||||
|
||||
std::string CDROM::GetMediaFileName() const
|
||||
{
|
||||
return m_reader.GetMediaFileName();
|
||||
}
|
||||
|
||||
void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
||||
{
|
||||
if (HasMedia())
|
||||
if (CanReadMedia())
|
||||
RemoveMedia();
|
||||
|
||||
// set the region from the system area of the disc
|
||||
|
@ -185,6 +175,7 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
|||
Settings::GetConsoleRegionName(m_system->GetRegion()));
|
||||
|
||||
// motor automatically spins up
|
||||
if (m_drive_state != DriveState::ShellOpening)
|
||||
m_secondary_status.motor_on = true;
|
||||
|
||||
// reading TOC? interestingly this doesn't work for GetlocL though...
|
||||
|
@ -197,9 +188,11 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
|||
|
||||
void CDROM::RemoveMedia(bool force /* = false */)
|
||||
{
|
||||
if (!m_reader.HasMedia() && !force)
|
||||
if (!HasMedia() && !force)
|
||||
return;
|
||||
|
||||
const TickCount stop_ticks = GetTicksForStop(true);
|
||||
|
||||
Log_InfoPrintf("Removing CD...");
|
||||
m_reader.RemoveMedia();
|
||||
|
||||
|
@ -218,6 +211,13 @@ void CDROM::RemoveMedia(bool force /* = false */)
|
|||
|
||||
// The console sends an interrupt when the shell is opened regardless of whether a command was executing.
|
||||
SendAsyncErrorResponse(STAT_ERROR, 0x08);
|
||||
|
||||
// Begin spin-down timer, we can't swap the new disc in immediately for some games (e.g. Metal Gear Solid).
|
||||
if (!force)
|
||||
{
|
||||
m_drive_state = DriveState::ShellOpening;
|
||||
m_drive_event->SetIntervalAndSchedule(stop_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
void CDROM::SetUseReadThread(bool enabled)
|
||||
|
@ -541,7 +541,7 @@ TickCount CDROM::GetAckDelayForCommand(Command command)
|
|||
// presumably because the controller is busy doing discy-things.
|
||||
constexpr u32 default_ack_delay_no_disc = 15000;
|
||||
constexpr u32 default_ack_delay_with_disc = 25000;
|
||||
return HasMedia() ? default_ack_delay_with_disc : default_ack_delay_no_disc;
|
||||
return CanReadMedia() ? default_ack_delay_with_disc : default_ack_delay_no_disc;
|
||||
}
|
||||
|
||||
TickCount CDROM::GetTicksForRead()
|
||||
|
@ -580,6 +580,11 @@ TickCount CDROM::GetTicksForSeek(CDImage::LBA new_lba)
|
|||
return ticks;
|
||||
}
|
||||
|
||||
TickCount CDROM::GetTicksForStop(bool motor_was_on)
|
||||
{
|
||||
return motor_was_on ? (m_mode.double_speed ? 25000000 : 13000000) : 7000;
|
||||
}
|
||||
|
||||
void CDROM::BeginCommand(Command command)
|
||||
{
|
||||
m_command = command;
|
||||
|
@ -624,7 +629,7 @@ void CDROM::ExecuteCommand()
|
|||
SendACKAndStat();
|
||||
|
||||
// shell open bit is cleared after sending the status
|
||||
if (HasMedia())
|
||||
if (CanReadMedia())
|
||||
m_secondary_status.shell_open = false;
|
||||
|
||||
EndCommand();
|
||||
|
@ -653,7 +658,7 @@ void CDROM::ExecuteCommand()
|
|||
case Command::ReadTOC:
|
||||
{
|
||||
Log_DebugPrintf("CDROM ReadTOC command");
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
SendErrorResponse(STAT_ERROR, 0x80);
|
||||
}
|
||||
|
@ -712,7 +717,7 @@ void CDROM::ExecuteCommand()
|
|||
{
|
||||
const bool logical = (m_command == Command::SeekL);
|
||||
Log_DebugPrintf("CDROM %s command", logical ? "SeekL" : "SeekP");
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
SendErrorResponse(STAT_ERROR, 0x80);
|
||||
}
|
||||
|
@ -731,7 +736,7 @@ void CDROM::ExecuteCommand()
|
|||
const u8 session = m_param_fifo.IsEmpty() ? 0 : m_param_fifo.Peek(0);
|
||||
Log_DebugPrintf("CDROM SetSession command, session=%u", session);
|
||||
|
||||
if (!HasMedia() || m_drive_state == DriveState::Reading || m_drive_state == DriveState::Playing)
|
||||
if (!CanReadMedia() || m_drive_state == DriveState::Reading || m_drive_state == DriveState::Playing)
|
||||
{
|
||||
SendErrorResponse(STAT_ERROR, 0x80);
|
||||
}
|
||||
|
@ -756,7 +761,7 @@ void CDROM::ExecuteCommand()
|
|||
case Command::ReadS:
|
||||
{
|
||||
Log_DebugPrintf("CDROM read command");
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
SendErrorResponse(STAT_ERROR, 0x80);
|
||||
}
|
||||
|
@ -786,7 +791,7 @@ void CDROM::ExecuteCommand()
|
|||
u8 track = m_param_fifo.IsEmpty() ? 0 : m_param_fifo.Peek(0);
|
||||
Log_DebugPrintf("CDROM play command, track=%u", track);
|
||||
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
SendErrorResponse(STAT_ERROR, 0x80);
|
||||
}
|
||||
|
@ -836,8 +841,7 @@ void CDROM::ExecuteCommand()
|
|||
|
||||
case Command::Stop:
|
||||
{
|
||||
const bool was_motor_on = m_secondary_status.motor_on;
|
||||
const TickCount stop_time = was_motor_on ? (m_mode.double_speed ? 25000000 : 13000000) : 7000;
|
||||
const TickCount stop_time = GetTicksForStop(m_secondary_status.motor_on);
|
||||
Log_DebugPrintf("CDROM stop command");
|
||||
SendACKAndStat();
|
||||
|
||||
|
@ -922,7 +926,7 @@ void CDROM::ExecuteCommand()
|
|||
|
||||
case Command::GetlocP:
|
||||
{
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
Log_DebugPrintf("CDROM GetlocP command - not ready");
|
||||
SendErrorResponse(STAT_ERROR, ERROR_NOT_READY);
|
||||
|
@ -956,7 +960,7 @@ void CDROM::ExecuteCommand()
|
|||
case Command::GetTN:
|
||||
{
|
||||
Log_DebugPrintf("CDROM GetTN command");
|
||||
if (HasMedia())
|
||||
if (CanReadMedia())
|
||||
{
|
||||
m_reader.WaitForReadToComplete();
|
||||
|
||||
|
@ -980,7 +984,7 @@ void CDROM::ExecuteCommand()
|
|||
Assert(m_param_fifo.GetSize() >= 1);
|
||||
const u8 track = PackedBCDToBinary(m_param_fifo.Peek());
|
||||
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
SendErrorResponse(STAT_ERROR, 0x80);
|
||||
}
|
||||
|
@ -1141,6 +1145,10 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
|
|||
DoResetComplete(ticks_late);
|
||||
break;
|
||||
|
||||
case DriveState::ShellOpening:
|
||||
DoShellOpenComplete(ticks_late);
|
||||
break;
|
||||
|
||||
case DriveState::SeekingPhysical:
|
||||
case DriveState::SeekingLogical:
|
||||
DoSeekComplete(ticks_late);
|
||||
|
@ -1303,17 +1311,27 @@ void CDROM::UpdatePositionWhileSeeking()
|
|||
}
|
||||
}
|
||||
|
||||
void CDROM::DoShellOpenComplete(TickCount ticks_late)
|
||||
{
|
||||
// media is now readable (if any)
|
||||
m_drive_state = DriveState::Idle;
|
||||
m_drive_event->Deactivate();
|
||||
|
||||
if (m_reader.HasMedia())
|
||||
m_secondary_status.motor_on = true;
|
||||
}
|
||||
|
||||
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 = HasMedia();
|
||||
m_secondary_status.motor_on = CanReadMedia();
|
||||
m_mode.bits = 0;
|
||||
m_mode.read_raw_sector = true;
|
||||
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
Log_DevPrintf("CDROM reset - no disc");
|
||||
m_secondary_status.shell_open = true;
|
||||
|
@ -1325,7 +1343,7 @@ void CDROM::DoResetComplete(TickCount ticks_late)
|
|||
m_async_response_fifo.Push(m_secondary_status.bits);
|
||||
SetAsyncInterrupt(Interrupt::Complete);
|
||||
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
m_secondary_status.motor_on = false;
|
||||
m_secondary_status.shell_open = true;
|
||||
|
@ -1464,12 +1482,12 @@ void CDROM::DoIDRead()
|
|||
m_drive_state = DriveState::Idle;
|
||||
m_drive_event->Deactivate();
|
||||
m_secondary_status.ClearActiveBits();
|
||||
m_secondary_status.motor_on = HasMedia();
|
||||
m_secondary_status.motor_on = CanReadMedia();
|
||||
|
||||
// TODO: Audio CD.
|
||||
u8 stat_byte = m_secondary_status.bits;
|
||||
u8 flags_byte = 0;
|
||||
if (!HasMedia())
|
||||
if (!CanReadMedia())
|
||||
{
|
||||
flags_byte |= (1 << 6); // Disc Missing
|
||||
}
|
||||
|
@ -1964,7 +1982,7 @@ void CDROM::DrawDebugWindow()
|
|||
// draw voice states
|
||||
if (ImGui::CollapsingHeader("Media", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
if (HasMedia())
|
||||
if (m_reader.HasMedia())
|
||||
{
|
||||
const CDImage* media = m_reader.GetMedia();
|
||||
const auto [disc_minute, disc_second, disc_frame] = media->GetMSFPositionOnDisc();
|
||||
|
@ -1986,9 +2004,9 @@ void CDROM::DrawDebugWindow()
|
|||
|
||||
if (ImGui::CollapsingHeader("Status/Mode", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
static constexpr std::array<const char*, 11> drive_state_names = {
|
||||
{"Idle", "Resetting", "Seeking (Physical)", "Seeking (Logical)", "Reading ID", "Reading TOC", "Reading",
|
||||
"Playing", "Pausing", "Stopping", "Changing Session"}};
|
||||
static constexpr std::array<const char*, 12> drive_state_names = {
|
||||
{"Idle", "Opening Shell", "Resetting", "Seeking (Physical)", "Seeking (Logical)", "Reading ID", "Reading TOC",
|
||||
"Reading", "Playing", "Pausing", "Stopping", "Changing Session"}};
|
||||
|
||||
ImGui::Columns(3);
|
||||
|
||||
|
|
|
@ -28,8 +28,9 @@ public:
|
|||
void Reset();
|
||||
bool DoState(StateWrapper& sw);
|
||||
|
||||
bool HasMedia() const;
|
||||
std::string GetMediaFileName() const;
|
||||
bool HasMedia() const { return m_reader.HasMedia(); }
|
||||
std::string GetMediaFileName() const { return m_reader.GetMediaFileName(); }
|
||||
|
||||
void InsertMedia(std::unique_ptr<CDImage> media);
|
||||
void RemoveMedia(bool force = false);
|
||||
|
||||
|
@ -112,6 +113,7 @@ private:
|
|||
enum class DriveState : u8
|
||||
{
|
||||
Idle,
|
||||
ShellOpening,
|
||||
Resetting,
|
||||
SeekingPhysical,
|
||||
SeekingLogical,
|
||||
|
@ -200,6 +202,7 @@ private:
|
|||
{
|
||||
return (m_drive_state == DriveState::SeekingLogical || m_drive_state == DriveState::SeekingPhysical);
|
||||
}
|
||||
bool CanReadMedia() const { return (m_drive_state != DriveState::ShellOpening && m_reader.HasMedia()); }
|
||||
bool HasPendingCommand() const { return m_command != Command::None; }
|
||||
bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; }
|
||||
bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; }
|
||||
|
@ -216,6 +219,7 @@ private:
|
|||
TickCount GetAckDelayForCommand(Command command);
|
||||
TickCount GetTicksForRead();
|
||||
TickCount GetTicksForSeek(CDImage::LBA new_lba);
|
||||
TickCount GetTicksForStop(bool motor_was_on);
|
||||
void BeginCommand(Command command); // also update status register
|
||||
void EndCommand(); // also updates status register
|
||||
void AbortCommand();
|
||||
|
@ -225,6 +229,7 @@ private:
|
|||
void ExecuteDrive(TickCount ticks_late);
|
||||
void BeginReading(TickCount ticks_late = 0, bool after_seek = false);
|
||||
void BeginPlaying(u8 track_bcd, 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();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "types.h"
|
||||
|
||||
static constexpr u32 SAVE_STATE_MAGIC = 0x43435544;
|
||||
static constexpr u32 SAVE_STATE_VERSION = 34;
|
||||
static constexpr u32 SAVE_STATE_VERSION = 35;
|
||||
|
||||
#pragma pack(push, 4)
|
||||
struct SAVE_STATE_HEADER
|
||||
|
|
Loading…
Reference in a new issue