Split drive state and drive error into 2 fields, and fix some inaccuracies
In particular: - Trying to play audio in a non-ready state returns the state-specific error, not an audio buf error - Audio status cannot be requested in non-ready states - The audio buffer cannot be configured in states other than ReadyNoReadsMade - Using the stop motor command while the motor is already stopped doesn't change states Additionally, the internal state IDs are used (which distinguish ReadyNoReadsMade and Ready), instead of the state IDs exposed in request error. This makes some of the weird behavior a bit more obvious. State and error behavior of the seek command was not implemented in this commit.
This commit is contained in:
parent
3d680ff2f6
commit
fba51b2956
|
@ -250,8 +250,9 @@ bool CBoot::DVDReadDiscID(const DiscIO::VolumeDisc& disc, u32 output_address)
|
||||||
if (!disc.Read(0, buffer.size(), buffer.data(), DiscIO::PARTITION_NONE))
|
if (!disc.Read(0, buffer.size(), buffer.data(), DiscIO::PARTITION_NONE))
|
||||||
return false;
|
return false;
|
||||||
Memory::CopyToEmu(output_address, buffer.data(), buffer.size());
|
Memory::CopyToEmu(output_address, buffer.data(), buffer.size());
|
||||||
// Clear ERROR_NO_DISKID_L, probably should check if that's currently set
|
// Transition out of the DiscIdNotRead state (which the drive should be in at this point,
|
||||||
DVDInterface::SetLowError(DVDInterface::ERROR_READY);
|
// on the assumption that this is only used for the first read)
|
||||||
|
DVDInterface::SetDriveState(DVDInterface::DriveState::ReadyNoReadsMade);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,12 +157,12 @@ static u32 s_current_length;
|
||||||
static u64 s_next_start;
|
static u64 s_next_start;
|
||||||
static u32 s_next_length;
|
static u32 s_next_length;
|
||||||
static u32 s_pending_samples;
|
static u32 s_pending_samples;
|
||||||
static bool s_can_configure_dtk = true;
|
|
||||||
static bool s_enable_dtk = false;
|
static bool s_enable_dtk = false;
|
||||||
static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer
|
static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer
|
||||||
|
|
||||||
// Disc drive state
|
// Disc drive state
|
||||||
static u32 s_error_code = 0;
|
static DriveState s_drive_state;
|
||||||
|
static DriveError s_error_code;
|
||||||
|
|
||||||
// Disc drive timing
|
// Disc drive timing
|
||||||
static u64 s_read_buffer_start_time;
|
static u64 s_read_buffer_start_time;
|
||||||
|
@ -219,10 +219,10 @@ void DoState(PointerWrap& p)
|
||||||
p.Do(s_next_start);
|
p.Do(s_next_start);
|
||||||
p.Do(s_next_length);
|
p.Do(s_next_length);
|
||||||
p.Do(s_pending_samples);
|
p.Do(s_pending_samples);
|
||||||
p.Do(s_can_configure_dtk);
|
|
||||||
p.Do(s_enable_dtk);
|
p.Do(s_enable_dtk);
|
||||||
p.Do(s_dtk_buffer_length);
|
p.Do(s_dtk_buffer_length);
|
||||||
|
|
||||||
|
p.Do(s_drive_state);
|
||||||
p.Do(s_error_code);
|
p.Do(s_error_code);
|
||||||
|
|
||||||
p.Do(s_read_buffer_start_time);
|
p.Do(s_read_buffer_start_time);
|
||||||
|
@ -383,31 +383,30 @@ void Reset(bool spinup)
|
||||||
s_current_start = 0;
|
s_current_start = 0;
|
||||||
s_current_length = 0;
|
s_current_length = 0;
|
||||||
s_pending_samples = 0;
|
s_pending_samples = 0;
|
||||||
s_can_configure_dtk = true;
|
|
||||||
s_enable_dtk = false;
|
s_enable_dtk = false;
|
||||||
s_dtk_buffer_length = 0;
|
s_dtk_buffer_length = 0;
|
||||||
|
|
||||||
if (!IsDiscInside())
|
if (!IsDiscInside())
|
||||||
{
|
{
|
||||||
// ERROR_COVER is used when the cover is open;
|
// CoverOpened is used when the cover is open;
|
||||||
// ERROR_NO_DISK_L is used when the cover is closed but there is no disc.
|
// NoMediumPresent is used when the cover is closed but there is no disc.
|
||||||
// On the Wii, this can only happen if something other than a DVD is inserted into the disc
|
// On the Wii, this can only happen if something other than a DVD is inserted into the disc
|
||||||
// drive (for instance, an audio CD) and only after it attempts to read it. Otherwise, it will
|
// drive (for instance, an audio CD) and only after it attempts to read it. Otherwise, it will
|
||||||
// report the cover as opened.
|
// report the cover as opened.
|
||||||
SetLowError(ERROR_COVER);
|
SetDriveState(DriveState::CoverOpened);
|
||||||
}
|
}
|
||||||
else if (!spinup)
|
else if (!spinup)
|
||||||
{
|
{
|
||||||
// Wii hardware tests indicate that this is used when ejecting and inserting a new disc, or
|
// Wii hardware tests indicate that this is used when ejecting and inserting a new disc, or
|
||||||
// performing a reset without spinup.
|
// performing a reset without spinup.
|
||||||
SetLowError(ERROR_CHANGE_DISK);
|
SetDriveState(DriveState::DiscChangeDetected);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetLowError(ERROR_NO_DISKID_L);
|
SetDriveState(DriveState::DiscIdNotRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetHighError(ERROR_NONE);
|
SetDriveError(DriveError::None);
|
||||||
|
|
||||||
// The buffer is empty at start
|
// The buffer is empty at start
|
||||||
s_read_buffer_start_offset = 0;
|
s_read_buffer_start_offset = 0;
|
||||||
|
@ -705,32 +704,32 @@ void ClearInterrupt(DIInterruptType interrupt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks the drive state to make sure a read-like command can be performed.
|
// Checks the drive state to make sure a read-like command can be performed.
|
||||||
// If false is returned, SetHighError will have been called, and the caller
|
// If false is returned, SetDriveError will have been called, and the caller
|
||||||
// should issue a DEINT interrupt.
|
// should issue a DEINT interrupt.
|
||||||
static bool CheckReadPreconditions()
|
static bool CheckReadPreconditions()
|
||||||
{
|
{
|
||||||
if (!IsDiscInside()) // Implies ERROR_COVER or ERROR_NO_DISK
|
if (!IsDiscInside()) // Implies CoverOpened or NoMediumPresent
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "No disc inside.");
|
ERROR_LOG(DVDINTERFACE, "No disc inside.");
|
||||||
SetHighError(ERROR_NO_DISK_H);
|
SetDriveError(DriveError::MediumNotPresent);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((s_error_code & LOW_ERROR_MASK) == ERROR_CHANGE_DISK)
|
if (s_drive_state == DriveState::DiscChangeDetected)
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "Disc changed (motor stopped).");
|
ERROR_LOG(DVDINTERFACE, "Disc changed (motor stopped).");
|
||||||
SetHighError(ERROR_MEDIUM);
|
SetDriveError(DriveError::MediumChanged);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((s_error_code & LOW_ERROR_MASK) == ERROR_MOTOR_STOP_L)
|
if (s_drive_state == DriveState::MotorStopped)
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "Motor stopped.");
|
ERROR_LOG(DVDINTERFACE, "Motor stopped.");
|
||||||
SetHighError(ERROR_MOTOR_STOP_H);
|
SetDriveError(DriveError::MotorStopped);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((s_error_code & LOW_ERROR_MASK) == ERROR_NO_DISKID_L)
|
if (s_drive_state == DriveState::DiscIdNotRead)
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "Disc id not read.");
|
ERROR_LOG(DVDINTERFACE, "Disc id not read.");
|
||||||
SetHighError(ERROR_NO_DISKID_H);
|
SetDriveError(DriveError::NoDiscID);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -775,7 +774,7 @@ bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32
|
||||||
if (reply_type == ReplyType::IOS && partition == DiscIO::PARTITION_NONE &&
|
if (reply_type == ReplyType::IOS && partition == DiscIO::PARTITION_NONE &&
|
||||||
dvd_offset + dvd_length > 0x50000)
|
dvd_offset + dvd_length > 0x50000)
|
||||||
{
|
{
|
||||||
SetHighError(DVDInterface::ERROR_BLOCK_OOB);
|
SetDriveError(DriveError::BlockOOB);
|
||||||
*interrupt_type = DIInterruptType::DEINT;
|
*interrupt_type = DIInterruptType::DEINT;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -794,7 +793,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
|
|
||||||
// DVDLowRequestError needs access to the error code set by the previous command
|
// DVDLowRequestError needs access to the error code set by the previous command
|
||||||
if (static_cast<DICommand>(s_DICMDBUF[0] >> 24) != DICommand::RequestError)
|
if (static_cast<DICommand>(s_DICMDBUF[0] >> 24) != DICommand::RequestError)
|
||||||
SetHighError(0);
|
SetDriveError(DriveError::None);
|
||||||
|
|
||||||
switch (static_cast<DICommand>(s_DICMDBUF[0] >> 24))
|
switch (static_cast<DICommand>(s_DICMDBUF[0] >> 24))
|
||||||
{
|
{
|
||||||
|
@ -811,7 +810,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
// GC-only patched drive firmware command, used by libogc
|
// GC-only patched drive firmware command, used by libogc
|
||||||
case DICommand::Unknown55:
|
case DICommand::Unknown55:
|
||||||
INFO_LOG(DVDINTERFACE, "SetExtension");
|
INFO_LOG(DVDINTERFACE, "SetExtension");
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -820,7 +819,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
INFO_LOG(DVDINTERFACE, "DVDLowReportKey");
|
INFO_LOG(DVDINTERFACE, "DVDLowReportKey");
|
||||||
// Does not work on retail discs/drives
|
// Does not work on retail discs/drives
|
||||||
// Retail games send this command to see if they are running on real retail hw
|
// Retail games send this command to see if they are running on real retail hw
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -838,7 +837,9 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x",
|
", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x",
|
||||||
iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH);
|
iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH);
|
||||||
|
|
||||||
s_can_configure_dtk = false;
|
if (s_drive_state == DriveState::ReadyNoReadsMade)
|
||||||
|
SetDriveState(DriveState::Ready);
|
||||||
|
|
||||||
command_handled_by_thread =
|
command_handled_by_thread =
|
||||||
ExecuteReadCommand(iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH, DiscIO::PARTITION_NONE,
|
ExecuteReadCommand(iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH, DiscIO::PARTITION_NONE,
|
||||||
reply_type, &interrupt_type);
|
reply_type, &interrupt_type);
|
||||||
|
@ -847,20 +848,20 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
|
|
||||||
case 0x40: // Read DiscID
|
case 0x40: // Read DiscID
|
||||||
INFO_LOG(DVDINTERFACE, "Read DiscID: buffer %08x", s_DIMAR);
|
INFO_LOG(DVDINTERFACE, "Read DiscID: buffer %08x", s_DIMAR);
|
||||||
// TODO: It doesn't make sense to include ERROR_CHANGE_DISK here, as it implies that the drive
|
// TODO: It doesn't make sense to include DiscChangeDetected here, as it implies that the
|
||||||
// is not spinning and reading the disc ID shouldn't change it. However, the Wii Menu breaks
|
// drive is not spinning and reading the disc ID shouldn't change it. However, the Wii Menu
|
||||||
// without it.
|
// breaks without it.
|
||||||
if ((s_error_code & LOW_ERROR_MASK) == ERROR_NO_DISKID_L ||
|
if (s_drive_state == DriveState::DiscIdNotRead ||
|
||||||
(s_error_code & LOW_ERROR_MASK) == ERROR_CHANGE_DISK)
|
s_drive_state == DriveState::DiscChangeDetected)
|
||||||
{
|
{
|
||||||
SetLowError(ERROR_READY);
|
SetDriveState(DriveState::ReadyNoReadsMade);
|
||||||
}
|
}
|
||||||
else
|
else if (s_drive_state == DriveState::ReadyNoReadsMade)
|
||||||
{
|
{
|
||||||
// The first disc ID reading is required before DTK can be configured.
|
// The first disc ID reading is required before DTK can be configured.
|
||||||
// If the disc ID is read again (or any other read occurs), it no longer can
|
// If the disc ID is read again (or any other read occurs), it no longer can
|
||||||
// be configured.
|
// be configured.
|
||||||
s_can_configure_dtk = false;
|
SetDriveState(DriveState::Ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
command_handled_by_thread = ExecuteReadCommand(
|
command_handled_by_thread = ExecuteReadCommand(
|
||||||
|
@ -897,33 +898,33 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
ERROR_LOG(DVDINTERFACE, "Unknown 0xAD subcommand in %08x", s_DICMDBUF[0]);
|
ERROR_LOG(DVDINTERFACE, "Unknown 0xAD subcommand in %08x", s_DICMDBUF[0]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::ReadDVD:
|
case DICommand::ReadDVD:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowReadDvd");
|
ERROR_LOG(DVDINTERFACE, "DVDLowReadDvd");
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::ReadDVDConfig:
|
case DICommand::ReadDVDConfig:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdConfig");
|
ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdConfig");
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::StopLaser:
|
case DICommand::StopLaser:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowStopLaser");
|
ERROR_LOG(DVDINTERFACE, "DVDLowStopLaser");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_STOP_LASER);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_STOP_LASER);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::Offset:
|
case DICommand::Offset:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowOffset");
|
ERROR_LOG(DVDINTERFACE, "DVDLowOffset");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_OFFSET);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_OFFSET);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
|
@ -943,36 +944,45 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
case DICommand::RequestDiscStatus:
|
case DICommand::RequestDiscStatus:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowRequestDiscStatus");
|
ERROR_LOG(DVDINTERFACE, "DVDLowRequestDiscStatus");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_DISC_STATUS);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_DISC_STATUS);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::RequestRetryNumber:
|
case DICommand::RequestRetryNumber:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowRequestRetryNumber");
|
ERROR_LOG(DVDINTERFACE, "DVDLowRequestRetryNumber");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_RETRY_NUMBER);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_RETRY_NUMBER);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::SetMaximumRotation:
|
case DICommand::SetMaximumRotation:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowSetMaximumRotation");
|
ERROR_LOG(DVDINTERFACE, "DVDLowSetMaximumRotation");
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
// Wii-exclusive
|
// Wii-exclusive
|
||||||
case DICommand::SerMeasControl:
|
case DICommand::SerMeasControl:
|
||||||
ERROR_LOG(DVDINTERFACE, "DVDLowSerMeasControl");
|
ERROR_LOG(DVDINTERFACE, "DVDLowSerMeasControl");
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_SER_MEAS_CONTROL);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_SER_MEAS_CONTROL);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Used by both GC and Wii
|
// Used by both GC and Wii
|
||||||
case DICommand::RequestError:
|
case DICommand::RequestError:
|
||||||
INFO_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", s_error_code);
|
{
|
||||||
s_DIIMMBUF = s_error_code;
|
u32 drive_state;
|
||||||
SetHighError(0);
|
if (s_drive_state == DriveState::Ready)
|
||||||
|
drive_state = 0;
|
||||||
|
else
|
||||||
|
drive_state = static_cast<u32>(s_drive_state) - 1;
|
||||||
|
|
||||||
|
const u32 result = (drive_state << 24) | static_cast<u32>(s_error_code);
|
||||||
|
INFO_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", result);
|
||||||
|
s_DIIMMBUF = result;
|
||||||
|
SetDriveError(DriveError::None);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Audio Stream (Immediate). Only used by some GC games, but does exist on the Wii
|
// Audio Stream (Immediate). Only used by some GC games, but does exist on the Wii
|
||||||
// (command_0 >> 16) & 0xFF = Subcommand
|
// (command_0 >> 16) & 0xFF = Subcommand
|
||||||
|
@ -983,7 +993,6 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
if (!CheckReadPreconditions())
|
if (!CheckReadPreconditions())
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "Cannot play audio (command %08x)", s_DICMDBUF[0]);
|
ERROR_LOG(DVDINTERFACE, "Cannot play audio (command %08x)", s_DICMDBUF[0]);
|
||||||
SetHighError(ERROR_AUDIO_BUF);
|
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -992,12 +1001,13 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
ERROR_LOG(DVDINTERFACE,
|
ERROR_LOG(DVDINTERFACE,
|
||||||
"Attempted to change playing audio while audio is disabled! (%08x %08x %08x)",
|
"Attempted to change playing audio while audio is disabled! (%08x %08x %08x)",
|
||||||
s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]);
|
s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]);
|
||||||
SetHighError(ERROR_AUDIO_BUF);
|
SetDriveError(DriveError::NoAudioBuf);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_can_configure_dtk = false;
|
if (s_drive_state == DriveState::ReadyNoReadsMade)
|
||||||
|
SetDriveState(DriveState::Ready);
|
||||||
|
|
||||||
switch ((s_DICMDBUF[0] >> 16) & 0xFF)
|
switch ((s_DICMDBUF[0] >> 16) & 0xFF)
|
||||||
{
|
{
|
||||||
|
@ -1035,7 +1045,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(DVDINTERFACE, "Invalid audio command! (%08x %08x %08x)", s_DICMDBUF[0],
|
ERROR_LOG(DVDINTERFACE, "Invalid audio command! (%08x %08x %08x)", s_DICMDBUF[0],
|
||||||
s_DICMDBUF[1], s_DICMDBUF[2]);
|
s_DICMDBUF[1], s_DICMDBUF[2]);
|
||||||
SetHighError(ERROR_INV_AUDIO);
|
SetDriveError(DriveError::InvalidAudioCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1045,10 +1055,17 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
// Request Audio Status (Immediate). Only used by some GC games, but does exist on the Wii
|
// Request Audio Status (Immediate). Only used by some GC games, but does exist on the Wii
|
||||||
case DICommand::RequestAudioStatus:
|
case DICommand::RequestAudioStatus:
|
||||||
{
|
{
|
||||||
|
if (!CheckReadPreconditions())
|
||||||
|
{
|
||||||
|
ERROR_LOG(DVDINTERFACE, "Attempted to request audio status in an invalid state!");
|
||||||
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!s_enable_dtk)
|
if (!s_enable_dtk)
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "Attempted to request audio status while audio is disabled!");
|
ERROR_LOG(DVDINTERFACE, "Attempted to request audio status while audio is disabled!");
|
||||||
SetHighError(ERROR_AUDIO_BUF);
|
SetDriveError(DriveError::NoAudioBuf);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1082,7 +1099,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(DVDINTERFACE, "Invalid audio status command! (%08x %08x %08x)", s_DICMDBUF[0],
|
ERROR_LOG(DVDINTERFACE, "Invalid audio status command! (%08x %08x %08x)", s_DICMDBUF[0],
|
||||||
s_DICMDBUF[1], s_DICMDBUF[2]);
|
s_DICMDBUF[1], s_DICMDBUF[2]);
|
||||||
SetHighError(ERROR_INV_AUDIO);
|
SetDriveError(DriveError::InvalidAudioCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1096,7 +1113,12 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
const bool kill = (s_DICMDBUF[0] & (1 << 20));
|
const bool kill = (s_DICMDBUF[0] & (1 << 20));
|
||||||
INFO_LOG(DVDINTERFACE, "DVDLowStopMotor%s%s", eject ? " eject" : "", kill ? " kill!" : "");
|
INFO_LOG(DVDINTERFACE, "DVDLowStopMotor%s%s", eject ? " eject" : "", kill ? " kill!" : "");
|
||||||
|
|
||||||
SetLowError(ERROR_MOTOR_STOP_L);
|
if (s_drive_state == DriveState::Ready || s_drive_state == DriveState::ReadyNoReadsMade ||
|
||||||
|
s_drive_state == DriveState::DiscIdNotRead)
|
||||||
|
{
|
||||||
|
SetDriveState(DriveState::MotorStopped);
|
||||||
|
}
|
||||||
|
|
||||||
const bool force_eject = eject && !kill;
|
const bool force_eject = eject && !kill;
|
||||||
|
|
||||||
if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() &&
|
if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() &&
|
||||||
|
@ -1121,22 +1143,31 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
// The link is dead, but you can access the page using the Wayback Machine at archive.org.
|
// The link is dead, but you can access the page using the Wayback Machine at archive.org.
|
||||||
|
|
||||||
// This command can only be used immediately after reading the disc ID, before any other
|
// This command can only be used immediately after reading the disc ID, before any other
|
||||||
// reads. Too early, and you get ERROR_NO_DISKID. Too late, and you get ERROR_INV_PERIOD.
|
// reads. Too early, and you get NoDiscID. Too late, and you get InvalidPeriod.
|
||||||
if (!s_can_configure_dtk)
|
if (!CheckReadPreconditions())
|
||||||
{
|
{
|
||||||
ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration after a read has been made!");
|
ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration in an invalid state!");
|
||||||
SetHighError(ERROR_INV_PERIOD);
|
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s_drive_state == DriveState::Ready)
|
||||||
|
{
|
||||||
|
ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration after a read has been made!");
|
||||||
|
SetDriveError(DriveError::InvalidPeriod);
|
||||||
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that this can be called multiple times, as long as the drive is in the ReadyNoReadsMade
|
||||||
|
// state. Calling it does not exit that state.
|
||||||
AudioBufferConfig((s_DICMDBUF[0] >> 16) & 1, s_DICMDBUF[0] & 0xf);
|
AudioBufferConfig((s_DICMDBUF[0] >> 16) & 1, s_DICMDBUF[0] & 0xf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// GC-only patched drive firmware command, used by libogc
|
// GC-only patched drive firmware command, used by libogc
|
||||||
case DICommand::UnknownEE:
|
case DICommand::UnknownEE:
|
||||||
INFO_LOG(DVDINTERFACE, "SetStatus");
|
INFO_LOG(DVDINTERFACE, "SetStatus");
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1146,7 +1177,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
// Can only be used through direct access and only after unlocked.
|
// Can only be used through direct access and only after unlocked.
|
||||||
case DICommand::Debug:
|
case DICommand::Debug:
|
||||||
ERROR_LOG(DVDINTERFACE, "Unsupported DVD Drive debug command 0x%08x", s_DICMDBUF[0]);
|
ERROR_LOG(DVDINTERFACE, "Unsupported DVD Drive debug command 0x%08x", s_DICMDBUF[0]);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1175,7 +1206,7 @@ void ExecuteCommand(ReplyType reply_type)
|
||||||
ERROR_LOG(DVDINTERFACE, "Unknown command 0x%08x (Buffer 0x%08x, 0x%x)", s_DICMDBUF[0], s_DIMAR,
|
ERROR_LOG(DVDINTERFACE, "Unknown command 0x%08x (Buffer 0x%08x, 0x%x)", s_DICMDBUF[0], s_DIMAR,
|
||||||
s_DILENGTH);
|
s_DILENGTH);
|
||||||
PanicAlertT("Unknown DVD command %08x - fatal error", s_DICMDBUF[0]);
|
PanicAlertT("Unknown DVD command %08x - fatal error", s_DICMDBUF[0]);
|
||||||
SetHighError(ERROR_INV_CMD);
|
SetDriveError(DriveError::InvalidCommand);
|
||||||
interrupt_type = DIInterruptType::DEINT;
|
interrupt_type = DIInterruptType::DEINT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1193,7 +1224,7 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
const DiscIO::Partition& partition, ReplyType reply_type)
|
const DiscIO::Partition& partition, ReplyType reply_type)
|
||||||
{
|
{
|
||||||
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
||||||
s_can_configure_dtk = false;
|
SetDriveState(DriveState::Ready);
|
||||||
|
|
||||||
const bool command_handled_by_thread =
|
const bool command_handled_by_thread =
|
||||||
ExecuteReadCommand(static_cast<u64>(position) << 2, output_address, length, length, partition,
|
ExecuteReadCommand(static_cast<u64>(position) << 2, output_address, length, length, partition,
|
||||||
|
@ -1230,16 +1261,14 @@ void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late)
|
||||||
FinishExecutingCommand(reply_type, interrupt_type, cycles_late);
|
FinishExecutingCommand(reply_type, interrupt_type, cycles_late);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetLowError(u32 low_error)
|
void SetDriveState(DriveState state)
|
||||||
{
|
{
|
||||||
DEBUG_ASSERT((low_error & HIGH_ERROR_MASK) == 0);
|
s_drive_state = state;
|
||||||
s_error_code = (s_error_code & HIGH_ERROR_MASK) | (low_error & LOW_ERROR_MASK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetHighError(u32 high_error)
|
void SetDriveError(DriveError error)
|
||||||
{
|
{
|
||||||
DEBUG_ASSERT((high_error & LOW_ERROR_MASK) == 0);
|
s_error_code = error;
|
||||||
s_error_code = (s_error_code & LOW_ERROR_MASK) | (high_error & HIGH_ERROR_MASK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
||||||
|
|
|
@ -51,33 +51,41 @@ enum class DICommand : u8
|
||||||
UnknownEE = 0xee,
|
UnknownEE = 0xee,
|
||||||
};
|
};
|
||||||
|
|
||||||
// "low" error codes
|
// Disc drive state.
|
||||||
constexpr u32 ERROR_READY = 0x0000000; // Ready.
|
// Reported in error codes as 0 for Ready, and value-1 for the rest
|
||||||
constexpr u32 ERROR_COVER = 0x01000000; // Cover is opened.
|
// (i.e. Ready and ReadyNoReadsMade are both reported as 0)
|
||||||
constexpr u32 ERROR_CHANGE_DISK = 0x02000000; // Disk change.
|
enum class DriveState : u8
|
||||||
constexpr u32 ERROR_NO_DISK_L = 0x03000000; // No disk.
|
{
|
||||||
constexpr u32 ERROR_MOTOR_STOP_L = 0x04000000; // Motor stop.
|
Ready = 0,
|
||||||
constexpr u32 ERROR_NO_DISKID_L = 0x05000000; // Disk ID not read.
|
ReadyNoReadsMade = 1,
|
||||||
constexpr u32 LOW_ERROR_MASK = 0xff000000;
|
CoverOpened = 2,
|
||||||
|
DiscChangeDetected = 3,
|
||||||
|
NoMediumPresent = 4,
|
||||||
|
MotorStopped = 5,
|
||||||
|
DiscIdNotRead = 6
|
||||||
|
};
|
||||||
|
|
||||||
// "high" error codes
|
// Actual drive error codes, which fill the remaining 3 bytes
|
||||||
constexpr u32 ERROR_NONE = 0x000000; // No error.
|
// Numbers more or less match a SCSI sense key (1 nybble) followed by SCSI ASC/ASCQ (2 bytes).
|
||||||
constexpr u32 ERROR_MOTOR_STOP_H = 0x020400; // Motor stopped.
|
enum class DriveError : u32
|
||||||
constexpr u32 ERROR_NO_DISKID_H = 0x020401; // Disk ID not read.
|
{
|
||||||
constexpr u32 ERROR_NO_DISK_H = 0x023a00; // Medium not present / Cover opened.
|
None = 0x00000, // No error.
|
||||||
constexpr u32 ERROR_SEEK_NDONE = 0x030200; // No seek complete.
|
MotorStopped = 0x20400, // Motor stopped.
|
||||||
constexpr u32 ERROR_READ = 0x031100; // Unrecovered read error.
|
NoDiscID = 0x20401, // Disk ID not read.
|
||||||
constexpr u32 ERROR_PROTOCOL = 0x040800; // Transfer protocol error.
|
MediumNotPresent = 0x23a00, // Medium not present / Cover opened.
|
||||||
constexpr u32 ERROR_INV_CMD = 0x052000; // Invalid command operation code.
|
SeekNotDone = 0x30200, // No seek complete.
|
||||||
constexpr u32 ERROR_AUDIO_BUF = 0x052001; // Audio Buffer not set.
|
ReadError = 0x31100, // Unrecovered read error.
|
||||||
constexpr u32 ERROR_BLOCK_OOB = 0x052100; // Logical block address out of bounds.
|
ProtocolError = 0x40800, // Transfer protocol error.
|
||||||
constexpr u32 ERROR_INV_FIELD = 0x052400; // Invalid field in command packet.
|
InvalidCommand = 0x52000, // Invalid command operation code.
|
||||||
constexpr u32 ERROR_INV_AUDIO = 0x052401; // Invalid audio command.
|
NoAudioBuf = 0x52001, // Audio Buffer not set.
|
||||||
constexpr u32 ERROR_INV_PERIOD = 0x052402; // Configuration out of permitted period.
|
BlockOOB = 0x52100, // Logical block address out of bounds.
|
||||||
constexpr u32 ERROR_END_USR_AREA = 0x056300; // End of user area encountered on this track.
|
InvalidField = 0x52400, // Invalid field in command packet.
|
||||||
constexpr u32 ERROR_MEDIUM = 0x062800; // Medium may have changed.
|
InvalidAudioCommand = 0x52401, // Invalid audio command.
|
||||||
constexpr u32 ERROR_MEDIUM_REQ = 0x0b5a01; // Operator medium removal request.
|
InvalidPeriod = 0x52402, // Configuration out of permitted period.
|
||||||
constexpr u32 HIGH_ERROR_MASK = 0x00ffffff;
|
EndOfUserArea = 0x56300, // End of user area encountered on this track.
|
||||||
|
MediumChanged = 0x62800, // Medium may have changed.
|
||||||
|
MediumRemovalRequest = 0xb5a01, // Operator medium removal request.
|
||||||
|
};
|
||||||
|
|
||||||
enum class DIInterruptType : int
|
enum class DIInterruptType : int
|
||||||
{
|
{
|
||||||
|
@ -130,8 +138,8 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
// Exposed for use by emulated BS2; does not perform any checks on drive state
|
// Exposed for use by emulated BS2; does not perform any checks on drive state
|
||||||
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
||||||
|
|
||||||
void SetLowError(u32 low_error);
|
void SetDriveState(DriveState state);
|
||||||
void SetHighError(u32 high_error);
|
void SetDriveError(DriveError error);
|
||||||
|
|
||||||
// Used by DVDThread
|
// Used by DVDThread
|
||||||
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
||||||
|
|
|
@ -348,7 +348,7 @@ static void FinishRead(u64 id, s64 cycles_late)
|
||||||
PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").",
|
PanicAlertT("The disc could not be read (at 0x%" PRIx64 " - 0x%" PRIx64 ").",
|
||||||
request.dvd_offset, request.dvd_offset + request.length);
|
request.dvd_offset, request.dvd_offset + request.length);
|
||||||
|
|
||||||
DVDInterface::SetHighError(DVDInterface::ERROR_BLOCK_OOB);
|
DVDInterface::SetDriveError(DVDInterface::DriveError::BlockOOB);
|
||||||
interrupt = DVDInterface::DIInterruptType::DEINT;
|
interrupt = DVDInterface::DIInterruptType::DEINT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue