mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-29 04:08:55 +00:00
Merge pull request #8571 from Pokechu22/di-interrupts
Fix gamecube games not noticing disc changes
This commit is contained in:
commit
07a0d44b36
8 changed files with 187 additions and 140 deletions
|
@ -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;
|
||||
|
@ -186,19 +186,19 @@ static void EjectDiscCallback(u64 userdata, s64 cyclesLate);
|
|||
static void InsertDiscCallback(u64 userdata, s64 cyclesLate);
|
||||
static void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late);
|
||||
|
||||
void SetLidOpen();
|
||||
static void SetLidOpen();
|
||||
|
||||
void UpdateInterrupts();
|
||||
void GenerateDIInterrupt(DIInterruptType _DVDInterrupt);
|
||||
static void UpdateInterrupts();
|
||||
static void GenerateDIInterrupt(DIInterruptType dvd_interrupt);
|
||||
|
||||
bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length,
|
||||
const DiscIO::Partition& partition, ReplyType reply_type,
|
||||
DIInterruptType* interrupt_type);
|
||||
static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length,
|
||||
u32 output_length, const DiscIO::Partition& partition,
|
||||
ReplyType reply_type, DIInterruptType* interrupt_type);
|
||||
|
||||
u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type);
|
||||
static u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type);
|
||||
|
||||
void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u32 output_address,
|
||||
ReplyType reply_type);
|
||||
static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition,
|
||||
u32 output_address, ReplyType reply_type);
|
||||
|
||||
void DoState(PointerWrap& p)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -345,8 +345,19 @@ void Init()
|
|||
|
||||
DVDThread::Start();
|
||||
|
||||
Reset();
|
||||
s_DISR.Hex = 0;
|
||||
s_DICVR.Hex = 1; // Disc Channel relies on cover being open when no disc is inserted
|
||||
s_DICMDBUF[0] = 0;
|
||||
s_DICMDBUF[1] = 0;
|
||||
s_DICMDBUF[2] = 0;
|
||||
s_DIMAR = 0;
|
||||
s_DILENGTH = 0;
|
||||
s_DICR.Hex = 0;
|
||||
s_DIIMMBUF = 0;
|
||||
s_DICFG.Hex = 0;
|
||||
s_DICFG.CONFIG = 1; // Disable bootrom descrambler
|
||||
|
||||
ResetDrive(false);
|
||||
|
||||
s_auto_change_disc = CoreTiming::RegisterEvent("AutoChangeDisc", AutoChangeDiscCallback);
|
||||
s_eject_disc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
|
||||
|
@ -359,22 +370,10 @@ void Init()
|
|||
CoreTiming::ScheduleEvent(0, s_finish_executing_command, userdata);
|
||||
}
|
||||
|
||||
// This doesn't reset any inserted disc or the cover state.
|
||||
void Reset(bool spinup)
|
||||
// Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the
|
||||
// emulated device, or any inserted disc.
|
||||
void ResetDrive(bool spinup)
|
||||
{
|
||||
INFO_LOG(DVDINTERFACE, "Reset %s spinup", spinup ? "with" : "without");
|
||||
|
||||
s_DISR.Hex = 0;
|
||||
s_DICMDBUF[0] = 0;
|
||||
s_DICMDBUF[1] = 0;
|
||||
s_DICMDBUF[2] = 0;
|
||||
s_DIMAR = 0;
|
||||
s_DILENGTH = 0;
|
||||
s_DICR.Hex = 0;
|
||||
s_DIIMMBUF = 0;
|
||||
s_DICFG.Hex = 0;
|
||||
s_DICFG.CONFIG = 1; // Disable bootrom descrambler
|
||||
|
||||
s_stream = false;
|
||||
s_stop_at_track_end = false;
|
||||
s_audio_position = 0;
|
||||
|
@ -383,31 +382,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;
|
||||
|
@ -454,7 +452,7 @@ void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
|||
DVDThread::SetDisc(std::move(disc));
|
||||
SetLidOpen();
|
||||
|
||||
Reset(false);
|
||||
ResetDrive(false);
|
||||
}
|
||||
|
||||
bool IsDiscInside()
|
||||
|
@ -544,7 +542,7 @@ bool AutoChangeDisc()
|
|||
return true;
|
||||
}
|
||||
|
||||
void SetLidOpen()
|
||||
static void SetLidOpen()
|
||||
{
|
||||
u32 old_value = s_DICVR.CVR;
|
||||
s_DICVR.CVR = IsDiscInside() ? 0 : 1;
|
||||
|
@ -633,7 +631,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
MMIO::InvalidWrite<u32>());
|
||||
}
|
||||
|
||||
void UpdateInterrupts()
|
||||
static void UpdateInterrupts()
|
||||
{
|
||||
const bool set_mask = (s_DISR.DEINT & s_DISR.DEINTMASK) || (s_DISR.TCINT & s_DISR.TCINTMASK) ||
|
||||
(s_DISR.BRKINT & s_DISR.BRKINTMASK) ||
|
||||
|
@ -645,7 +643,7 @@ void UpdateInterrupts()
|
|||
CoreTiming::ForceExceptionCheck(50);
|
||||
}
|
||||
|
||||
void GenerateDIInterrupt(DIInterruptType dvd_interrupt)
|
||||
static void GenerateDIInterrupt(DIInterruptType dvd_interrupt)
|
||||
{
|
||||
switch (dvd_interrupt)
|
||||
{
|
||||
|
@ -705,41 +703,41 @@ 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;
|
||||
}
|
||||
|
||||
// Iff false is returned, ScheduleEvent must be used to finish executing the command
|
||||
bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length,
|
||||
const DiscIO::Partition& partition, ReplyType reply_type,
|
||||
DIInterruptType* interrupt_type)
|
||||
static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length,
|
||||
u32 output_length, const DiscIO::Partition& partition,
|
||||
ReplyType reply_type, DIInterruptType* interrupt_type)
|
||||
{
|
||||
if (!CheckReadPreconditions())
|
||||
{
|
||||
|
@ -775,7 +773,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 +792,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 +809,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 +818,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 +836,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 +847,16 @@ 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)
|
||||
if (s_drive_state == DriveState::DiscIdNotRead)
|
||||
{
|
||||
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 +893,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 +939,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 +988,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 +996,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 +1040,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 +1050,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 +1094,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 +1108,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 +1138,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 +1172,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 +1201,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 +1219,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,
|
||||
|
@ -1218,7 +1244,7 @@ void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length)
|
|||
INFO_LOG(DVDINTERFACE, "DTK disabled");
|
||||
}
|
||||
|
||||
u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type)
|
||||
static u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type)
|
||||
{
|
||||
return (static_cast<u64>(reply_type) << 32) + static_cast<u32>(interrupt_type);
|
||||
}
|
||||
|
@ -1230,16 +1256,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,
|
||||
|
@ -1254,8 +1278,11 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type
|
|||
else if (reply_type == ReplyType::Interrupt || reply_type == ReplyType::IOS)
|
||||
transfer_size = s_DILENGTH;
|
||||
|
||||
s_DIMAR += transfer_size;
|
||||
s_DILENGTH -= transfer_size;
|
||||
if (interrupt_type == DIInterruptType::TCINT)
|
||||
{
|
||||
s_DIMAR += transfer_size;
|
||||
s_DILENGTH -= transfer_size;
|
||||
}
|
||||
|
||||
switch (reply_type)
|
||||
{
|
||||
|
@ -1290,8 +1317,8 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type
|
|||
|
||||
// Determines from a given read request how much of the request is buffered,
|
||||
// and how much is required to be read from disc.
|
||||
void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u32 output_address,
|
||||
ReplyType reply_type)
|
||||
static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition,
|
||||
u32 output_address, ReplyType reply_type)
|
||||
{
|
||||
// The drive continues to read 1 MiB beyond the last read position when idle.
|
||||
// If a future read falls within this window, part of the read may be returned
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -102,7 +110,7 @@ enum class EjectCause
|
|||
};
|
||||
|
||||
void Init();
|
||||
void Reset(bool spinup = true);
|
||||
void ResetDrive(bool spinup);
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Common/CommonTypes.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HW/DVD/DVDInterface.h"
|
||||
#include "Core/HW/MMIO.h"
|
||||
#include "Core/HW/SystemTimers.h"
|
||||
#include "Core/IOS/IOS.h"
|
||||
|
@ -111,8 +112,18 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
MMIO::ComplexWrite<u32>(
|
||||
[](u32, u32 val) { WARN_LOG(PROCESSORINTERFACE, "Fifo reset (%08x)", val); }));
|
||||
|
||||
mmio->Register(base | PI_RESET_CODE, MMIO::DirectRead<u32>(&m_ResetCode),
|
||||
MMIO::DirectWrite<u32>(&m_ResetCode));
|
||||
mmio->Register(base | PI_RESET_CODE, MMIO::ComplexRead<u32>([](u32) {
|
||||
DEBUG_LOG(PROCESSORINTERFACE, "Read PI_RESET_CODE: %08x", m_ResetCode);
|
||||
return m_ResetCode;
|
||||
}),
|
||||
MMIO::ComplexWrite<u32>([](u32, u32 val) {
|
||||
m_ResetCode = val;
|
||||
INFO_LOG(PROCESSORINTERFACE, "Wrote PI_RESET_CODE: %08x", m_ResetCode);
|
||||
if (~m_ResetCode & 0x4)
|
||||
{
|
||||
DVDInterface::ResetDrive(true);
|
||||
}
|
||||
}));
|
||||
|
||||
mmio->Register(base | PI_FLIPPER_REV, MMIO::DirectRead<u32>(&m_FlipperRev),
|
||||
MMIO::InvalidWrite<u32>());
|
||||
|
|
|
@ -266,9 +266,9 @@ std::optional<DI::DIResult> DI::StartIOCtl(const IOCtlRequest& request)
|
|||
return DIResult::Success;
|
||||
case DIIoctl::DVDLowReset:
|
||||
{
|
||||
const bool spinup = Memory::Read_U32(request.address + 4);
|
||||
const bool spinup = Memory::Read_U32(request.buffer_in + 4);
|
||||
INFO_LOG(IOS_DI, "DVDLowReset %s spinup", spinup ? "with" : "without");
|
||||
DVDInterface::Reset(spinup);
|
||||
DVDInterface::ResetDrive(spinup);
|
||||
ResetDIRegisters();
|
||||
return DIResult::Success;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ static void ReinitHardware()
|
|||
// HACK However, resetting DI will reset the DTK config, which is set by the system menu
|
||||
// (and not by MIOS), causing games that use DTK to break. Perhaps MIOS doesn't actually
|
||||
// reset DI fully, in such a way that the DTK config isn't cleared?
|
||||
// DVDInterface::Reset();
|
||||
// DVDInterface::ResetDrive(true);
|
||||
PowerPC::Reset();
|
||||
Wiimote::ResetAllWiimotes();
|
||||
// Note: this is specific to Dolphin and is required because we initialised it in Wii mode.
|
||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||
static std::thread g_save_thread;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
constexpr u32 STATE_VERSION = 121; // Last changed in PR 8988
|
||||
constexpr u32 STATE_VERSION = 122; // Last changed in PR 8571
|
||||
|
||||
// Maps savestate versions to Dolphin versions.
|
||||
// Versions after 42 don't need to be added to this list,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue