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))
|
||||
return false;
|
||||
Memory::CopyToEmu(output_address, buffer.data(), buffer.size());
|
||||
// Clear ERROR_NO_DISKID_L, probably should check if that's currently set
|
||||
DVDInterface::SetLowError(DVDInterface::ERROR_READY);
|
||||
// Transition out of the DiscIdNotRead state (which the drive should be in at this point,
|
||||
// on the assumption that this is only used for the first read)
|
||||
DVDInterface::SetDriveState(DVDInterface::DriveState::ReadyNoReadsMade);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -157,12 +157,12 @@ static u32 s_current_length;
|
|||
static u64 s_next_start;
|
||||
static u32 s_next_length;
|
||||
static u32 s_pending_samples;
|
||||
static bool s_can_configure_dtk = true;
|
||||
static bool s_enable_dtk = false;
|
||||
static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer
|
||||
|
||||
// Disc drive state
|
||||
static u32 s_error_code = 0;
|
||||
static DriveState s_drive_state;
|
||||
static DriveError s_error_code;
|
||||
|
||||
// Disc drive timing
|
||||
static u64 s_read_buffer_start_time;
|
||||
|
@ -219,10 +219,10 @@ void DoState(PointerWrap& p)
|
|||
p.Do(s_next_start);
|
||||
p.Do(s_next_length);
|
||||
p.Do(s_pending_samples);
|
||||
p.Do(s_can_configure_dtk);
|
||||
p.Do(s_enable_dtk);
|
||||
p.Do(s_dtk_buffer_length);
|
||||
|
||||
p.Do(s_drive_state);
|
||||
p.Do(s_error_code);
|
||||
|
||||
p.Do(s_read_buffer_start_time);
|
||||
|
@ -383,31 +383,30 @@ void Reset(bool spinup)
|
|||
s_current_start = 0;
|
||||
s_current_length = 0;
|
||||
s_pending_samples = 0;
|
||||
s_can_configure_dtk = true;
|
||||
s_enable_dtk = false;
|
||||
s_dtk_buffer_length = 0;
|
||||
|
||||
if (!IsDiscInside())
|
||||
{
|
||||
// ERROR_COVER is used when the cover is open;
|
||||
// ERROR_NO_DISK_L is used when the cover is closed but there is no disc.
|
||||
// CoverOpened is used when the cover is open;
|
||||
// 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
|
||||
// drive (for instance, an audio CD) and only after it attempts to read it. Otherwise, it will
|
||||
// report the cover as opened.
|
||||
SetLowError(ERROR_COVER);
|
||||
SetDriveState(DriveState::CoverOpened);
|
||||
}
|
||||
else if (!spinup)
|
||||
{
|
||||
// Wii hardware tests indicate that this is used when ejecting and inserting a new disc, or
|
||||
// performing a reset without spinup.
|
||||
SetLowError(ERROR_CHANGE_DISK);
|
||||
SetDriveState(DriveState::DiscChangeDetected);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLowError(ERROR_NO_DISKID_L);
|
||||
SetDriveState(DriveState::DiscIdNotRead);
|
||||
}
|
||||
|
||||
SetHighError(ERROR_NONE);
|
||||
SetDriveError(DriveError::None);
|
||||
|
||||
// The buffer is empty at start
|
||||
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.
|
||||
// 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.
|
||||
static bool CheckReadPreconditions()
|
||||
{
|
||||
if (!IsDiscInside()) // Implies ERROR_COVER or ERROR_NO_DISK
|
||||
if (!IsDiscInside()) // Implies CoverOpened or NoMediumPresent
|
||||
{
|
||||
ERROR_LOG(DVDINTERFACE, "No disc inside.");
|
||||
SetHighError(ERROR_NO_DISK_H);
|
||||
SetDriveError(DriveError::MediumNotPresent);
|
||||
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).");
|
||||
SetHighError(ERROR_MEDIUM);
|
||||
SetDriveError(DriveError::MediumChanged);
|
||||
return false;
|
||||
}
|
||||
if ((s_error_code & LOW_ERROR_MASK) == ERROR_MOTOR_STOP_L)
|
||||
if (s_drive_state == DriveState::MotorStopped)
|
||||
{
|
||||
ERROR_LOG(DVDINTERFACE, "Motor stopped.");
|
||||
SetHighError(ERROR_MOTOR_STOP_H);
|
||||
SetDriveError(DriveError::MotorStopped);
|
||||
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.");
|
||||
SetHighError(ERROR_NO_DISKID_H);
|
||||
SetDriveError(DriveError::NoDiscID);
|
||||
return false;
|
||||
}
|
||||
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 &&
|
||||
dvd_offset + dvd_length > 0x50000)
|
||||
{
|
||||
SetHighError(DVDInterface::ERROR_BLOCK_OOB);
|
||||
SetDriveError(DriveError::BlockOOB);
|
||||
*interrupt_type = DIInterruptType::DEINT;
|
||||
return false;
|
||||
}
|
||||
|
@ -794,7 +793,7 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
|
||||
// DVDLowRequestError needs access to the error code set by the previous command
|
||||
if (static_cast<DICommand>(s_DICMDBUF[0] >> 24) != DICommand::RequestError)
|
||||
SetHighError(0);
|
||||
SetDriveError(DriveError::None);
|
||||
|
||||
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
|
||||
case DICommand::Unknown55:
|
||||
INFO_LOG(DVDINTERFACE, "SetExtension");
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
|
||||
|
@ -820,7 +819,7 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
INFO_LOG(DVDINTERFACE, "DVDLowReportKey");
|
||||
// Does not work on retail discs/drives
|
||||
// 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;
|
||||
break;
|
||||
|
||||
|
@ -838,7 +837,9 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x",
|
||||
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 =
|
||||
ExecuteReadCommand(iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH, DiscIO::PARTITION_NONE,
|
||||
reply_type, &interrupt_type);
|
||||
|
@ -847,20 +848,20 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
|
||||
case 0x40: // Read DiscID
|
||||
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
|
||||
// is not spinning and reading the disc ID shouldn't change it. However, the Wii Menu breaks
|
||||
// without it.
|
||||
if ((s_error_code & LOW_ERROR_MASK) == ERROR_NO_DISKID_L ||
|
||||
(s_error_code & LOW_ERROR_MASK) == ERROR_CHANGE_DISK)
|
||||
// TODO: It doesn't make sense to include DiscChangeDetected here, as it implies that the
|
||||
// drive is not spinning and reading the disc ID shouldn't change it. However, the Wii Menu
|
||||
// breaks without it.
|
||||
if (s_drive_state == DriveState::DiscIdNotRead ||
|
||||
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.
|
||||
// If the disc ID is read again (or any other read occurs), it no longer can
|
||||
// be configured.
|
||||
s_can_configure_dtk = false;
|
||||
SetDriveState(DriveState::Ready);
|
||||
}
|
||||
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::ReadDVD:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowReadDvd");
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::ReadDVDConfig:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdConfig");
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::StopLaser:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowStopLaser");
|
||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_STOP_LASER);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::Offset:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowOffset");
|
||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_OFFSET);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
|
@ -943,36 +944,45 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
case DICommand::RequestDiscStatus:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowRequestDiscStatus");
|
||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_DISC_STATUS);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::RequestRetryNumber:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowRequestRetryNumber");
|
||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_RETRY_NUMBER);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::SetMaximumRotation:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowSetMaximumRotation");
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
// Wii-exclusive
|
||||
case DICommand::SerMeasControl:
|
||||
ERROR_LOG(DVDINTERFACE, "DVDLowSerMeasControl");
|
||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_SER_MEAS_CONTROL);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
|
||||
// Used by both GC and Wii
|
||||
case DICommand::RequestError:
|
||||
INFO_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", s_error_code);
|
||||
s_DIIMMBUF = s_error_code;
|
||||
SetHighError(0);
|
||||
{
|
||||
u32 drive_state;
|
||||
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;
|
||||
}
|
||||
|
||||
// Audio Stream (Immediate). Only used by some GC games, but does exist on the Wii
|
||||
// (command_0 >> 16) & 0xFF = Subcommand
|
||||
|
@ -983,7 +993,6 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
if (!CheckReadPreconditions())
|
||||
{
|
||||
ERROR_LOG(DVDINTERFACE, "Cannot play audio (command %08x)", s_DICMDBUF[0]);
|
||||
SetHighError(ERROR_AUDIO_BUF);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
}
|
||||
|
@ -992,12 +1001,13 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
ERROR_LOG(DVDINTERFACE,
|
||||
"Attempted to change playing audio while audio is disabled! (%08x %08x %08x)",
|
||||
s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]);
|
||||
SetHighError(ERROR_AUDIO_BUF);
|
||||
SetDriveError(DriveError::NoAudioBuf);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
}
|
||||
|
||||
s_can_configure_dtk = false;
|
||||
if (s_drive_state == DriveState::ReadyNoReadsMade)
|
||||
SetDriveState(DriveState::Ready);
|
||||
|
||||
switch ((s_DICMDBUF[0] >> 16) & 0xFF)
|
||||
{
|
||||
|
@ -1035,7 +1045,7 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
default:
|
||||
ERROR_LOG(DVDINTERFACE, "Invalid audio command! (%08x %08x %08x)", s_DICMDBUF[0],
|
||||
s_DICMDBUF[1], s_DICMDBUF[2]);
|
||||
SetHighError(ERROR_INV_AUDIO);
|
||||
SetDriveError(DriveError::InvalidAudioCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
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
|
||||
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)
|
||||
{
|
||||
ERROR_LOG(DVDINTERFACE, "Attempted to request audio status while audio is disabled!");
|
||||
SetHighError(ERROR_AUDIO_BUF);
|
||||
SetDriveError(DriveError::NoAudioBuf);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
}
|
||||
|
@ -1082,7 +1099,7 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
default:
|
||||
ERROR_LOG(DVDINTERFACE, "Invalid audio status command! (%08x %08x %08x)", s_DICMDBUF[0],
|
||||
s_DICMDBUF[1], s_DICMDBUF[2]);
|
||||
SetHighError(ERROR_INV_AUDIO);
|
||||
SetDriveError(DriveError::InvalidAudioCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
}
|
||||
|
@ -1096,7 +1113,12 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
const bool kill = (s_DICMDBUF[0] & (1 << 20));
|
||||
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;
|
||||
|
||||
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.
|
||||
|
||||
// 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.
|
||||
if (!s_can_configure_dtk)
|
||||
// reads. Too early, and you get NoDiscID. Too late, and you get InvalidPeriod.
|
||||
if (!CheckReadPreconditions())
|
||||
{
|
||||
ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration after a read has been made!");
|
||||
SetHighError(ERROR_INV_PERIOD);
|
||||
ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration in an invalid state!");
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
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);
|
||||
break;
|
||||
|
||||
// GC-only patched drive firmware command, used by libogc
|
||||
case DICommand::UnknownEE:
|
||||
INFO_LOG(DVDINTERFACE, "SetStatus");
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
|
||||
|
@ -1146,7 +1177,7 @@ void ExecuteCommand(ReplyType reply_type)
|
|||
// Can only be used through direct access and only after unlocked.
|
||||
case DICommand::Debug:
|
||||
ERROR_LOG(DVDINTERFACE, "Unsupported DVD Drive debug command 0x%08x", s_DICMDBUF[0]);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
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,
|
||||
s_DILENGTH);
|
||||
PanicAlertT("Unknown DVD command %08x - fatal error", s_DICMDBUF[0]);
|
||||
SetHighError(ERROR_INV_CMD);
|
||||
SetDriveError(DriveError::InvalidCommand);
|
||||
interrupt_type = DIInterruptType::DEINT;
|
||||
break;
|
||||
}
|
||||
|
@ -1193,7 +1224,7 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
|||
const DiscIO::Partition& partition, ReplyType reply_type)
|
||||
{
|
||||
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
||||
s_can_configure_dtk = false;
|
||||
SetDriveState(DriveState::Ready);
|
||||
|
||||
const bool command_handled_by_thread =
|
||||
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);
|
||||
}
|
||||
|
||||
void SetLowError(u32 low_error)
|
||||
void SetDriveState(DriveState state)
|
||||
{
|
||||
DEBUG_ASSERT((low_error & HIGH_ERROR_MASK) == 0);
|
||||
s_error_code = (s_error_code & HIGH_ERROR_MASK) | (low_error & LOW_ERROR_MASK);
|
||||
s_drive_state = state;
|
||||
}
|
||||
|
||||
void SetHighError(u32 high_error)
|
||||
void SetDriveError(DriveError error)
|
||||
{
|
||||
DEBUG_ASSERT((high_error & LOW_ERROR_MASK) == 0);
|
||||
s_error_code = (s_error_code & LOW_ERROR_MASK) | (high_error & HIGH_ERROR_MASK);
|
||||
s_error_code = error;
|
||||
}
|
||||
|
||||
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
||||
|
|
|
@ -51,33 +51,41 @@ enum class DICommand : u8
|
|||
UnknownEE = 0xee,
|
||||
};
|
||||
|
||||
// "low" error codes
|
||||
constexpr u32 ERROR_READY = 0x0000000; // Ready.
|
||||
constexpr u32 ERROR_COVER = 0x01000000; // Cover is opened.
|
||||
constexpr u32 ERROR_CHANGE_DISK = 0x02000000; // Disk change.
|
||||
constexpr u32 ERROR_NO_DISK_L = 0x03000000; // No disk.
|
||||
constexpr u32 ERROR_MOTOR_STOP_L = 0x04000000; // Motor stop.
|
||||
constexpr u32 ERROR_NO_DISKID_L = 0x05000000; // Disk ID not read.
|
||||
constexpr u32 LOW_ERROR_MASK = 0xff000000;
|
||||
// Disc drive state.
|
||||
// Reported in error codes as 0 for Ready, and value-1 for the rest
|
||||
// (i.e. Ready and ReadyNoReadsMade are both reported as 0)
|
||||
enum class DriveState : u8
|
||||
{
|
||||
Ready = 0,
|
||||
ReadyNoReadsMade = 1,
|
||||
CoverOpened = 2,
|
||||
DiscChangeDetected = 3,
|
||||
NoMediumPresent = 4,
|
||||
MotorStopped = 5,
|
||||
DiscIdNotRead = 6
|
||||
};
|
||||
|
||||
// "high" error codes
|
||||
constexpr u32 ERROR_NONE = 0x000000; // No error.
|
||||
constexpr u32 ERROR_MOTOR_STOP_H = 0x020400; // Motor stopped.
|
||||
constexpr u32 ERROR_NO_DISKID_H = 0x020401; // Disk ID not read.
|
||||
constexpr u32 ERROR_NO_DISK_H = 0x023a00; // Medium not present / Cover opened.
|
||||
constexpr u32 ERROR_SEEK_NDONE = 0x030200; // No seek complete.
|
||||
constexpr u32 ERROR_READ = 0x031100; // Unrecovered read error.
|
||||
constexpr u32 ERROR_PROTOCOL = 0x040800; // Transfer protocol error.
|
||||
constexpr u32 ERROR_INV_CMD = 0x052000; // Invalid command operation code.
|
||||
constexpr u32 ERROR_AUDIO_BUF = 0x052001; // Audio Buffer not set.
|
||||
constexpr u32 ERROR_BLOCK_OOB = 0x052100; // Logical block address out of bounds.
|
||||
constexpr u32 ERROR_INV_FIELD = 0x052400; // Invalid field in command packet.
|
||||
constexpr u32 ERROR_INV_AUDIO = 0x052401; // Invalid audio command.
|
||||
constexpr u32 ERROR_INV_PERIOD = 0x052402; // Configuration out of permitted period.
|
||||
constexpr u32 ERROR_END_USR_AREA = 0x056300; // End of user area encountered on this track.
|
||||
constexpr u32 ERROR_MEDIUM = 0x062800; // Medium may have changed.
|
||||
constexpr u32 ERROR_MEDIUM_REQ = 0x0b5a01; // Operator medium removal request.
|
||||
constexpr u32 HIGH_ERROR_MASK = 0x00ffffff;
|
||||
// Actual drive error codes, which fill the remaining 3 bytes
|
||||
// Numbers more or less match a SCSI sense key (1 nybble) followed by SCSI ASC/ASCQ (2 bytes).
|
||||
enum class DriveError : u32
|
||||
{
|
||||
None = 0x00000, // No error.
|
||||
MotorStopped = 0x20400, // Motor stopped.
|
||||
NoDiscID = 0x20401, // Disk ID not read.
|
||||
MediumNotPresent = 0x23a00, // Medium not present / Cover opened.
|
||||
SeekNotDone = 0x30200, // No seek complete.
|
||||
ReadError = 0x31100, // Unrecovered read error.
|
||||
ProtocolError = 0x40800, // Transfer protocol error.
|
||||
InvalidCommand = 0x52000, // Invalid command operation code.
|
||||
NoAudioBuf = 0x52001, // Audio Buffer not set.
|
||||
BlockOOB = 0x52100, // Logical block address out of bounds.
|
||||
InvalidField = 0x52400, // Invalid field in command packet.
|
||||
InvalidAudioCommand = 0x52401, // Invalid audio command.
|
||||
InvalidPeriod = 0x52402, // Configuration out of permitted period.
|
||||
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
|
||||
{
|
||||
|
@ -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
|
||||
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
||||
|
||||
void SetLowError(u32 low_error);
|
||||
void SetHighError(u32 high_error);
|
||||
void SetDriveState(DriveState state);
|
||||
void SetDriveError(DriveError error);
|
||||
|
||||
// Used by DVDThread
|
||||
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 ").",
|
||||
request.dvd_offset, request.dvd_offset + request.length);
|
||||
|
||||
DVDInterface::SetHighError(DVDInterface::ERROR_BLOCK_OOB);
|
||||
DVDInterface::SetDriveError(DVDInterface::DriveError::BlockOOB);
|
||||
interrupt = DVDInterface::DIInterruptType::DEINT;
|
||||
}
|
||||
else
|
||||
|
|
Loading…
Reference in New Issue