diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index 95c26c5fb3..6e3b00aa40 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -7,9 +7,7 @@ #include "AudioCommon/AudioCommon.h" -#include "Common/ChunkFile.h" #include "Common/CommonTypes.h" -#include "Common/Thread.h" #include "Core/ConfigManager.h" #include "Core/CoreTiming.h" @@ -91,16 +89,6 @@ enum DI_CONFIG_REGISTER = 0x24 }; - -// DVD IntteruptTypes -enum DI_InterruptType -{ - INT_DEINT = 0, - INT_TCINT = 1, - INT_BRKINT = 2, - INT_CVRINT = 3, -}; - // debug commands which may be ORd enum { @@ -265,10 +253,13 @@ void EjectDiscCallback(u64 userdata, int cyclesLate); void InsertDiscCallback(u64 userdata, int cyclesLate); void UpdateInterrupts(); -void GenerateDIInterrupt(DI_InterruptType _DVDInterrupt); -void ExecuteCommand(); -void FinishExecuteRead(); -u64 SimulateDiscReadTime(); +void GenerateDIInterrupt(DIInterruptType _DVDInterrupt); + +void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF); +DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address, + u32 DVD_length, u32 output_length, bool raw = false); + +u64 SimulateDiscReadTime(u64 offset, u32 length); s64 CalculateRawDiscReadTime(u64 offset, s64 length); void DoState(PointerWrap &p) @@ -305,8 +296,7 @@ static void TransferComplete(u64 userdata, int cyclesLate) { m_DICR.TSTART = 0; m_DILENGTH.Length = 0; - GenerateDIInterrupt(INT_TCINT); - g_ErrorCode = 0; + GenerateDIInterrupt((DIInterruptType)userdata); } } @@ -318,8 +308,8 @@ static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples) if (AudioPos >= CurrentStart + CurrentLength) { DEBUG_LOG(DVDINTERFACE, - "ProcessDTKSamples: NextStart=%08x,NextLength=%08x,CurrentStart=%08x,CurrentLength=%08x,AudioPos=%08x", - NextStart, NextLength, CurrentStart, CurrentLength, AudioPos); + "ProcessDTKSamples: NextStart=%08x,NextLength=%08x,CurrentStart=%08x,CurrentLength=%08x,AudioPos=%08x", + NextStart, NextLength, CurrentStart, CurrentLength, AudioPos); AudioPos = NextStart; CurrentStart = NextStart; @@ -480,14 +470,12 @@ bool IsLidOpen() return (m_DICVR.CVR == 1); } -void ClearCoverInterrupt() +bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool raw) { - m_DICVR.CVRINT = 0; -} - -bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength) -{ - return VolumeHandler::ReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength); + if (raw) + return VolumeHandler::RAWReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength); + else + return VolumeHandler::ReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength); } void RegisterMMIO(MMIO::Mapping* mmio, u32 base) @@ -559,7 +547,21 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base) m_DICR.Hex = val & 7; if (m_DICR.TSTART) { - ExecuteCommand(); + DVDCommandResult result = ExecuteCommand( + m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex, + m_DIMAR.Hex, m_DILENGTH.Hex, true); + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) + { + // Make sure fast disc speed performs "instant" reads; in addition + // to being used to speed up games, fast disc speed is used as a + // workaround for crashes in Star Wars Rogue Leader. + TransferComplete(result.interrupt_type, 0); + } + else + { + // The transfer is finished after a delay + CoreTiming::ScheduleEvent((int)result.ticks_until_completion, tc, result.interrupt_type); + } } }) ); @@ -594,7 +596,7 @@ void UpdateInterrupts() CoreTiming::ForceExceptionCheck(50); } -void GenerateDIInterrupt(DI_InterruptType _DVDInterrupt) +void GenerateDIInterrupt(DIInterruptType _DVDInterrupt) { switch (_DVDInterrupt) { @@ -607,115 +609,264 @@ void GenerateDIInterrupt(DI_InterruptType _DVDInterrupt) UpdateInterrupts(); } -void ExecuteCommand() +void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF) { - // This variable is used to simulate the time is takes to execute a command. - // 1 / 15000 seconds is just some arbitrary default value. - // Commands that implement more precise timing are supposed to overwrite this. - u64 ticks_until_TC = SystemTimers::GetTicksPerSecond() / 15000; + if (write_to_DIIMMBUF) + m_DIIMMBUF.Hex = value; + else + Memory::Write_U32(value, output_address); +} - // _dbg_assert_(DVDINTERFACE, _DICR.RW == 0); // only DVD to Memory - int GCAM = ((SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && - (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD)) - ? 1 : 0; +DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address, + u32 DVD_length, u32 output_length, bool raw) +{ + if (DVD_length > output_length) + { + WARN_LOG(DVDINTERFACE, "Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); + DVD_length = output_length; + } + + DVDCommandResult result; + result.ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length); + + // Is this check needed? + if (output_address == 0) + { + PanicAlert("DVDLowRead : _BufferOut == 0"); + result.interrupt_type = INT_DEINT; + return result; + } + + if (raw) + { + // We must make sure it is in a valid area! (#001 check) + // * 0x00000000 - 0x00014000 (limit of older IOS versions) + // * 0x460a0000 - 0x460a0008 + // * 0x7ed40000 - 0x7ed40008 + u32 DVD_offset_32 = (u32)(DVD_offset >> 2); + // Are these checks correct? They seem to mix 32-bit offsets and 8-bit lengths + if (!((DVD_offset_32 > 0x00000000 && DVD_offset_32 < 0x00014000) || + (((DVD_offset_32 + DVD_length) > 0x00000000) && (DVD_offset_32 + DVD_length) < 0x00014000) || + (DVD_offset_32 > 0x460a0000 && DVD_offset_32 < 0x460a0008) || + (((DVD_offset_32 + DVD_length) > 0x460a0000) && (DVD_offset_32 + DVD_length) < 0x460a0008) || + (DVD_offset_32 > 0x7ed40000 && DVD_offset_32 < 0x7ed40008) || + (((DVD_offset_32 + DVD_length) > 0x7ed40000) && (DVD_offset_32 + DVD_length) < 0x7ed40008))) + { + WARN_LOG(DVDINTERFACE, "DVDLowUnencryptedRead: trying to read out of bounds @ %09" PRIx64, DVD_offset); + g_ErrorCode = ERROR_READY | ERROR_BLOCK_OOB; + // Should cause software to call DVDLowRequestError + result.interrupt_type = INT_BRKINT; + return result; + } + } + + if (!DVDRead(DVD_offset, output_address, DVD_length, raw)) + PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); + + result.interrupt_type = INT_TCINT; + return result; +} + +DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, + u32 output_address, u32 output_length, bool write_to_DIIMMBUF) +{ + DVDCommandResult result; + result.interrupt_type = INT_TCINT; + result.ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000; + + bool GCAM = (SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && + (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD); + + // DVDLowRequestError needs access to the error code set by the previous command + if (command_0 >> 24 != DVDLowRequestError) + g_ErrorCode = 0; if (GCAM) { - ERROR_LOG(DVDINTERFACE, - "DVD: %08x, %08x, %08x, DMA=addr:%08x,len:%08x,ctrl:%08x", - m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex, - m_DIMAR.Hex, m_DILENGTH.Hex, m_DICR.Hex); + ERROR_LOG(DVDINTERFACE, "DVD: %08x, %08x, %08x, DMA=addr:%08x,len:%08x,ctrl:%08x", + command_0, command_1, command_2, output_address, output_length, m_DICR.Hex); // decrypt command. But we have a zero key, that simplifies things a lot. // If you get crazy dvd command errors, make sure 0x80000000 - 0x8000000c is zero'd - m_DICMDBUF[0].Hex <<= 24; + command_0 <<= 24; } - - switch (m_DICMDBUF[0].CMDBYTE0) + switch (command_0 >> 24) { + // Seems to be used by both GC and Wii case DVDLowInquiry: if (GCAM) { // 0x29484100... // was 21 i'm not entirely sure about this, but it works well. - m_DIIMMBUF.Hex = 0x21000000; + WriteImmediate(0x21000000, output_address, write_to_DIIMMBUF); } else { - // small safety check, dunno if it's needed - if ((m_DICMDBUF[1].Hex == 0) && (m_DILENGTH.Length == 0x20)) - { - u8* driveInfo = Memory::GetPointer(m_DIMAR.Address); - // gives the correct output in GCOS - 06 2001/08 (61) - // there may be other stuff missing ? - driveInfo[4] = 0x20; - driveInfo[5] = 0x01; - driveInfo[6] = 0x06; - driveInfo[7] = 0x08; - driveInfo[8] = 0x61; + // (shuffle2) Taken from my Wii + Memory::Write_U32(0x00000002, output_address); + Memory::Write_U32(0x20060526, output_address + 4); + // This was in the oubuf even though this cmd is only supposed to reply with 64bits + // However, this and other tests strongly suggest that the buffer is static, and it's never - or rarely cleared. + Memory::Write_U32(0x41000000, output_address + 8); - // Just for fun - INFO_LOG(DVDINTERFACE, "Drive Info: %02x %02x%02x/%02x (%02x)", - driveInfo[6], driveInfo[4], driveInfo[5], driveInfo[7], driveInfo[8]); - } + INFO_LOG(DVDINTERFACE, "DVDLowInquiry (Buffer 0x%08x, 0x%x)", + output_address, output_length); } break; - // "Set Extension"...not sure what it does + // Only seems to be used from WII_IPC, not through direct access + case DVDLowReadDiskID: + INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID"); + result = ExecuteReadCommand(0, output_address, command_1, output_length, true); + break; + + // Only seems to be used from WII_IPC, not through direct access + case DVDLowRead: + INFO_LOG(DVDINTERFACE, "DVDLowRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2, command_1); + result = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length); + break; + + // Probably only used by Wii + case DVDLowWaitForCoverClose: + INFO_LOG(DVDINTERFACE, "DVDLowWaitForCoverClose"); + result.interrupt_type = (DIInterruptType)4; // ??? + break; + + // "Set Extension"...not sure what it does. GC only? case 0x55: INFO_LOG(DVDINTERFACE, "SetExtension"); break; - // DMA Read from Disc + // Probably only used though WII_IPC + case DVDLowGetCoverReg: + WriteImmediate(m_DICVR.Hex, output_address, write_to_DIIMMBUF); + INFO_LOG(DVDINTERFACE, "DVDLowGetCoverReg 0x%08x", m_DICVR.Hex); + break; + + // Probably only used by Wii + case DVDLowNotifyReset: + ERROR_LOG(DVDINTERFACE, "DVDLowNotifyReset"); + PanicAlert("DVDLowNotifyReset"); + break; + // Probably only used by Wii + case DVDLowReadDvdPhysical: + ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdPhysical"); + PanicAlert("DVDLowReadDvdPhysical"); + break; + // Probably only used by Wii + case DVDLowReadDvdCopyright: + ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdCopyright"); + PanicAlert("DVDLowReadDvdCopyright"); + break; + // Probably only used by Wii + case DVDLowReadDvdDiscKey: + ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdDiscKey"); + PanicAlert("DVDLowReadDvdDiscKey"); + break; + + // Probably only used by Wii + case DVDLowClearCoverInterrupt: + INFO_LOG(DVDINTERFACE, "DVDLowClearCoverInterrupt"); + m_DICVR.CVRINT = 0; + + // Less than ~1/155th of a second hangs Oregon Trail at "loading wheel". + // More than ~1/140th of a second hangs Resident Evil Archives: Resident Evil Zero. + result.ticks_until_completion = SystemTimers::GetTicksPerSecond() / 146; + break; + + // Probably only used by Wii + case DVDLowGetCoverStatus: + WriteImmediate(g_bDiscInside ? 2 : 1, output_address, write_to_DIIMMBUF); + INFO_LOG(DVDINTERFACE, "DVDLowGetCoverStatus: Disc %sInserted", g_bDiscInside ? "" : "Not "); + break; + + // Probably only used by Wii + case DVDLowReset: + INFO_LOG(DVDINTERFACE, "DVDLowReset"); + break; + + // Probably only used by Wii + case DVDLowClosePartition: + INFO_LOG(DVDINTERFACE, "DVDLowClosePartition"); + break; + + // Probably only used by Wii + case DVDLowUnencryptedRead: + INFO_LOG(DVDINTERFACE, "DVDLowUnencryptedRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2, command_1); + result = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true); + break; + + // Probably only used by Wii + case DVDLowEnableDvdVideo: + ERROR_LOG(DVDINTERFACE, "DVDLowEnableDvdVideo"); + break; + + // New Super Mario Bros. Wii sends these commands, + // but it seems we don't need to implement anything. + // Probably only used by Wii + case 0x95: + case 0x96: + ERROR_LOG(DVDINTERFACE, "Unimplemented BCA command 0x%08x (Buffer 0x%08x, 0x%x)", + command_0, output_address, output_length); + break; + + // Probably only used by Wii + case DVDLowReportKey: + 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 + g_ErrorCode = ERROR_READY | ERROR_INV_CMD; + result.interrupt_type = INT_BRKINT; + break; + + // DMA Read from Disc. Only seems to be used through direct access, not WII_IPC case 0xA8: if (g_bDiscInside) { - switch (m_DICMDBUF[0].CMDBYTE3) + switch (command_0 & 0xFF) { case 0x00: // Read Sector { - u32 iDVDOffset = m_DICMDBUF[1].Hex << 2; + u64 iDVDOffset = (u64)command_1 << 2; - DEBUG_LOG(DVDINTERFACE, "Read: DVDOffset=%08x, DMABuffer=%08x, SrcLength=%08x, DMALength=%08x", - iDVDOffset, m_DIMAR.Address, m_DICMDBUF[2].Hex, m_DILENGTH.Length); - _dbg_assert_(DVDINTERFACE, m_DICMDBUF[2].Hex == m_DILENGTH.Length); + INFO_LOG(DVDINTERFACE, "Read: DVDOffset=%08" PRIx64 ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", + iDVDOffset, output_address, command_2, output_length); if (GCAM) { if (iDVDOffset & 0x80000000) // read request to hardware buffer { - u32 len = m_DILENGTH.Length / 4; switch (iDVDOffset) { case 0x80000000: ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD STATUS (80000000)"); - for (u32 i = 0; i < len; i++) - Memory::Write_U32(0, m_DIMAR.Address + i * 4); + for (u32 i = 0; i < output_length; i += 4) + Memory::Write_U32(0, output_address + i); break; case 0x80000040: ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD STATUS (2) (80000040)"); - for (u32 i = 0; i < len; i++) - Memory::Write_U32(~0, m_DIMAR.Address + i * 4); - Memory::Write_U32(0x00000020, m_DIMAR.Address); // DIMM SIZE, LE - Memory::Write_U32(0x4743414D, m_DIMAR.Address + 4); // GCAM signature + for (u32 i = 0; i < output_length; i += 4) + Memory::Write_U32(~0, output_address + i); + Memory::Write_U32(0x00000020, output_address); // DIMM SIZE, LE + Memory::Write_U32(0x4743414D, output_address + 4); // GCAM signature break; case 0x80000120: ERROR_LOG(DVDINTERFACE, "GC-AM: READ FIRMWARE STATUS (80000120)"); - for (u32 i = 0; i < len; i++) - Memory::Write_U32(0x01010101, m_DIMAR.Address + i * 4); + for (u32 i = 0; i < output_length; i += 4) + Memory::Write_U32(0x01010101, output_address + i); break; case 0x80000140: ERROR_LOG(DVDINTERFACE, "GC-AM: READ FIRMWARE STATUS (80000140)"); - for (u32 i = 0; i < len; i++) - Memory::Write_U32(0x01010101, m_DIMAR.Address + i * 4); + for (u32 i = 0; i < output_length; i += 4) + Memory::Write_U32(0x01010101, output_address + i); break; case 0x84000020: ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD STATUS (1) (84000020)"); - for (u32 i = 0; i < len; i++) - Memory::Write_U32(0x00000000, m_DIMAR.Address + i * 4); + for (u32 i = 0; i < output_length; i += 4) + Memory::Write_U32(0x00000000, output_address + i); break; default: - ERROR_LOG(DVDINTERFACE, "GC-AM: UNKNOWN MEDIA BOARD LOCATION %x", iDVDOffset); + ERROR_LOG(DVDINTERFACE, "GC-AM: UNKNOWN MEDIA BOARD LOCATION %" PRIx64, iDVDOffset); break; } break; @@ -724,56 +875,43 @@ void ExecuteCommand() { ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD COMM AREA (1f900020)"); u8* source = media_buffer + iDVDOffset - 0x1f900000; - Memory::CopyToEmu(m_DIMAR.Address, source, m_DILENGTH.Length); - for (u32 i = 0; i < m_DILENGTH.Length; i += 4) - ERROR_LOG(DVDINTERFACE, "GC-AM: %08x", Memory::Read_U32(m_DIMAR.Address + i)); + Memory::CopyToEmu(output_address, source, output_length); + for (u32 i = 0; i < output_length; i += 4) + ERROR_LOG(DVDINTERFACE, "GC-AM: %08x", Memory::Read_U32(output_address + i)); break; } } - ticks_until_TC = SimulateDiscReadTime(); - - // Here is the actual disc reading - if (!DVDRead(iDVDOffset, m_DIMAR.Address, m_DILENGTH.Length)) - { - PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - } + result = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length); } break; case 0x40: // Read DiscID - _dbg_assert_(DVDINTERFACE, m_DICMDBUF[1].Hex == 0); - _dbg_assert_(DVDINTERFACE, m_DICMDBUF[2].Hex == m_DILENGTH.Length); - _dbg_assert_(DVDINTERFACE, m_DILENGTH.Length == 0x20); - if (!DVDRead(m_DICMDBUF[1].Hex, m_DIMAR.Address, m_DILENGTH.Length)) - PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error"); - WARN_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(m_DIMAR.Address)); + INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address)); + result = ExecuteReadCommand(0, output_address, command_2, output_length); break; default: - _dbg_assert_msg_(DVDINTERFACE, 0, "Unknown Read Subcommand"); + ERROR_LOG(DVDINTERFACE, "Unknown read subcommand: %08x", command_0); break; } } else { // there is no disc to read - m_DICR.TSTART = 0; - m_DILENGTH.Length = 0; g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; - GenerateDIInterrupt(INT_DEINT); - return; + result.interrupt_type = INT_DEINT; } break; - // GC-AM + // GC-AM only case 0xAA: if (GCAM) { - ERROR_LOG(DVDINTERFACE, "GC-AM: 0xAA, DMABuffer=%08x, DMALength=%08x", m_DIMAR.Address, m_DILENGTH.Length); - u32 iDVDOffset = m_DICMDBUF[1].Hex << 2; - unsigned int len = m_DILENGTH.Length; - int offset = iDVDOffset - 0x1F900000; + ERROR_LOG(DVDINTERFACE, "GC-AM: 0xAA, DMABuffer=%08x, DMALength=%08x", output_address, output_length); + u64 iDVDOffset = (u64)command_1 << 2; + u32 len = output_length; + s64 offset = iDVDOffset - 0x1F900000; /* if (iDVDOffset == 0x84800000) { @@ -782,7 +920,7 @@ void ExecuteCommand() else*/ if ((offset < 0) || ((offset + len) > 0x40) || len > 0x40) { - u32 addr = m_DIMAR.Address; + u32 addr = output_address; if (iDVDOffset == 0x84800000) { ERROR_LOG(DVDINTERFACE, "FIRMWARE UPLOAD"); @@ -794,7 +932,7 @@ void ExecuteCommand() while (len >= 4) { - ERROR_LOG(DVDINTERFACE, "GC-AM Media Board WRITE (0xAA): %08x: %08x", iDVDOffset, Memory::Read_U32(addr)); + ERROR_LOG(DVDINTERFACE, "GC-AM Media Board WRITE (0xAA): %08" PRIx64 ": %08x", iDVDOffset, Memory::Read_U32(addr)); addr += 4; len -= 4; iDVDOffset += 4; @@ -806,7 +944,7 @@ void ExecuteCommand() Memory::CopyFromEmu(media_buffer + offset, addr, len); while (len >= 4) { - ERROR_LOG(DVDINTERFACE, "GC-AM Media Board WRITE (0xAA): %08x: %08x", iDVDOffset, Memory::Read_U32(addr)); + ERROR_LOG(DVDINTERFACE, "GC-AM Media Board WRITE (0xAA): %08" PRIx64 ": %08x", iDVDOffset, Memory::Read_U32(addr)); addr += 4; len -= 4; iDVDOffset += 4; @@ -815,12 +953,12 @@ void ExecuteCommand() } break; - // Seek (immediate) + // Seems to be used by both GC and Wii case DVDLowSeek: if (!GCAM) { - // We don't care :) - DEBUG_LOG(DVDINTERFACE, "Seek: offset=%08x (ignoring)", m_DICMDBUF[1].Hex << 2); + // Currently unimplemented + INFO_LOG(DVDINTERFACE, "Seek: offset=%09" PRIx64 " (ignoring)", (u64)command_1 << 2); } else { @@ -893,27 +1031,62 @@ void ExecuteCommand() break; } memset(media_buffer + 0x20, 0, 0x20); - m_DIIMMBUF.Hex = 0x66556677; // just a random value that works. + WriteImmediate(0x66556677, output_address, write_to_DIIMMBUF); // just a random value that works. } break; + // Probably only used by Wii + case DVDLowReadDvd: + ERROR_LOG(DVDINTERFACE, "DVDLowReadDvd"); + break; + // Probably only used by Wii + case DVDLowReadDvdConfig: + ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdConfig"); + break; + // Probably only used by Wii + case DVDLowStopLaser: + ERROR_LOG(DVDINTERFACE, "DVDLowStopLaser"); + break; + // Probably only used by Wii case DVDLowOffset: - DEBUG_LOG(DVDINTERFACE, "DVDLowOffset: ignoring..."); + ERROR_LOG(DVDINTERFACE, "DVDLowOffset"); + break; + // Probably only used by Wii + case DVDLowReadDiskBca: + WARN_LOG(DVDINTERFACE, "DVDLowReadDiskBca"); + Memory::Write_U32(1, output_address + 0x30); + break; + // Probably only used by Wii + case DVDLowRequestDiscStatus: + ERROR_LOG(DVDINTERFACE, "DVDLowRequestDiscStatus"); + break; + // Probably only used by Wii + case DVDLowRequestRetryNumber: + ERROR_LOG(DVDINTERFACE, "DVDLowRequestRetryNumber"); + break; + // Probably only used by Wii + case DVDLowSetMaximumRotation: + ERROR_LOG(DVDINTERFACE, "DVDLowSetMaximumRotation"); + break; + // Probably only used by Wii + case DVDLowSerMeasControl: + ERROR_LOG(DVDINTERFACE, "DVDLowSerMeasControl"); break; - // Request Error Code + // Used by both GC and Wii case DVDLowRequestError: - ERROR_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", g_ErrorCode); - m_DIIMMBUF.Hex = g_ErrorCode; + INFO_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", g_ErrorCode); + WriteImmediate(g_ErrorCode, output_address, write_to_DIIMMBUF); + g_ErrorCode = 0; break; - // Audio Stream (Immediate) - // m_DICMDBUF[0].CMDBYTE1 = Subcommand - // m_DICMDBUF[1].Hex << 2 = Offset on disc - // m_DICMDBUF[2].Hex = Length of the stream + // Audio Stream (Immediate). Only seems to be used by some GC games + // (command_0 >> 16) & 0xFF = Subcommand + // command_1 << 2 = Offset on disc + // command_2 = Length of the stream case 0xE1: { - u8 cancel_stream = m_DICMDBUF[0].CMDBYTE1; + u8 cancel_stream = (command_0 >> 16) & 0xFF; if (cancel_stream) { g_bStopAtTrackEnd = false; @@ -926,17 +1099,16 @@ void ExecuteCommand() } else { - u32 pos = m_DICMDBUF[1].Hex << 2; - u32 length = m_DICMDBUF[2].Hex; - - if ((pos == 0) && (length == 0)) + if ((command_1 == 0) && (command_2 == 0)) { g_bStopAtTrackEnd = true; } else if (!g_bStopAtTrackEnd) { - NextStart = pos; - NextLength = length; + // Setting NextStart (a u32) like this discards two bits, + // but GC games can't be 4 GiB big, so it shouldn't matter + NextStart = command_1 << 2; + NextLength = command_2; if (!g_bStream) { CurrentStart = NextStart; @@ -948,50 +1120,59 @@ void ExecuteCommand() } } - - WARN_LOG(DVDINTERFACE, "(Audio) Stream subcmd = %08x offset = %08x length=%08x", - m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex << 2, m_DICMDBUF[2].Hex); + INFO_LOG(DVDINTERFACE, "(Audio) Stream cmd: %08x offset: %08" PRIx64 " length: %08x", + command_0, (u64)command_1 << 2, command_2); } break; - // Request Audio Status (Immediate) + // Request Audio Status (Immediate). Only seems to be used by some GC games case 0xE2: { - switch (m_DICMDBUF[0].CMDBYTE1) + switch (command_0 >> 16 & 0xFF) { case 0x00: // Returns streaming status - DEBUG_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x/%08x CurrentStart:%08x CurrentLength:%08x", AudioPos, CurrentStart + CurrentLength, CurrentStart, CurrentLength); - m_DIIMMBUF.REGVAL0 = 0; - m_DIIMMBUF.REGVAL1 = 0; - m_DIIMMBUF.REGVAL2 = 0; - m_DIIMMBUF.REGVAL3 = (g_bStream) ? 1 : 0; + INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x/%08x CurrentStart:%08x CurrentLength:%08x", AudioPos, CurrentStart + CurrentLength, CurrentStart, CurrentLength); + WriteImmediate((g_bStream) ? 1 : 0, output_address, write_to_DIIMMBUF); break; case 0x01: // Returns the current offset - DEBUG_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x", AudioPos); - m_DIIMMBUF.Hex = AudioPos >> 2; + INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08x", AudioPos); + WriteImmediate(AudioPos >> 2, output_address, write_to_DIIMMBUF); break; case 0x02: // Returns the start offset - DEBUG_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentStart:%08x", CurrentStart); - m_DIIMMBUF.Hex = CurrentStart >> 2; + INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentStart:%08x", CurrentStart); + WriteImmediate(CurrentStart >> 2, output_address, write_to_DIIMMBUF); break; case 0x03: // Returns the total length - DEBUG_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentLength:%08x", CurrentLength); - m_DIIMMBUF.Hex = CurrentLength; + INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentLength:%08x", CurrentLength); + WriteImmediate(CurrentLength >> 2, output_address, write_to_DIIMMBUF); break; default: - WARN_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", m_DICMDBUF[0].CMDBYTE1, g_bStream? "on":"off"); + WARN_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", command_0 >> 16 & 0xFF, g_bStream ? "on" : "off"); break; } } break; case DVDLowStopMotor: - DEBUG_LOG(DVDINTERFACE, "Stop motor"); + INFO_LOG(DVDINTERFACE, "DVDLowStopMotor %s %s", + command_1 ? "eject" : "", command_2 ? "kill!" : ""); + + if (command_1) + EjectDiscCallback(0, 0); break; - // DVD Audio Enable/Disable (Immediate) + // DVD Audio Enable/Disable (Immediate). GC uses this, and apparently Wii also does...? case DVDLowAudioBufferConfig: - if (m_DICMDBUF[0].CMDBYTE1 == 1) + // For more information: http://www.crazynation.org/GC/GC_DD_TECH/GCTech.htm (dead link?) + // + // Upon Power up or reset , 2 commands must be issued for proper use of audio streaming: + // DVDReadDiskID A8000040,00000000,00000020 + // DVDLowAudioBufferConfig E4xx00yy,00000000,00000020 + // + // xx=byte 8 [0 or 1] from the disk header retrieved from DVDReadDiskID + // yy=0 (if xx=0) or 0xA (if xx=1) + + if ((command_0 >> 16) & 0xFF) { // TODO: What is this actually supposed to do? g_bStream = true; @@ -1005,31 +1186,33 @@ void ExecuteCommand() } break; - // yet another command we prolly don't care about + // yet another (GC?) command we prolly don't care about case 0xEE: - DEBUG_LOG(DVDINTERFACE, "SetStatus - Unimplemented"); + INFO_LOG(DVDINTERFACE, "SetStatus"); break; // Debug commands; see yagcd. We don't really care // NOTE: commands to stream data will send...a raw data stream // This will appear as unknown commands, unless the check is re-instated to catch such data. + // Can probably only be used through direct access case 0xFE: - INFO_LOG(DVDINTERFACE, "Unsupported DVD Drive debug command 0x%08x", m_DICMDBUF[0].Hex); + ERROR_LOG(DVDINTERFACE, "Unsupported DVD Drive debug command 0x%08x", command_0); break; // Unlock Commands. 1: "MATSHITA" 2: "DVD-GAME" // Just for fun + // Can probably only be used through direct access case 0xFF: { - if (m_DICMDBUF[0].Hex == 0xFF014D41 && - m_DICMDBUF[1].Hex == 0x54534849 && - m_DICMDBUF[2].Hex == 0x54410200) + if (command_0 == 0xFF014D41 && + command_1 == 0x54534849 && + command_2 == 0x54410200) { INFO_LOG(DVDINTERFACE, "Unlock test 1 passed"); } - else if (m_DICMDBUF[0].Hex == 0xFF004456 && - m_DICMDBUF[1].Hex == 0x442D4741 && - m_DICMDBUF[2].Hex == 0x4D450300) + else if (command_0 == 0xFF004456 && + command_1 == 0x442D4741 && + command_2 == 0x4D450300) { INFO_LOG(DVDINTERFACE, "Unlock test 2 passed"); } @@ -1041,120 +1224,97 @@ void ExecuteCommand() break; default: - PanicAlertT("Unknown DVD command %08x - fatal error", m_DICMDBUF[0].Hex); - _dbg_assert_(DVDINTERFACE, 0); + ERROR_LOG(DVDINTERFACE, "Unknown command 0x%08x (Buffer 0x%08x, 0x%x)", + command_0, output_address, output_length); + PanicAlertT("Unknown DVD command %08x - fatal error", command_0); break; } - if (ticks_until_TC) - { - // The transfer is finished after a delay - CoreTiming::ScheduleEvent((int)ticks_until_TC, tc); - } - else - { - // transfer is done - m_DICR.TSTART = 0; - m_DILENGTH.Length = 0; - GenerateDIInterrupt(INT_TCINT); - g_ErrorCode = 0; - } + return result; } // Simulates the timing aspects of reading data from a disc. -// Sets g_last_read_offset and g_last_read_time, and returns ticks_until_TC. -u64 SimulateDiscReadTime() +// Returns the amount of ticks needed to finish executing the command, +// and sets some state that is used the next time this function runs. +u64 SimulateDiscReadTime(u64 offset, u32 length) { - u64 DVD_offset = (u64)m_DICMDBUF[1].Hex << 2; - u64 current_time = CoreTiming::GetTicks(); - u64 ticks_until_TC; + // The drive buffers 1 MiB (?) of data after every read request; + // if a read request is covered by this buffer (or if it's + // faster to wait for the data to be buffered), the drive + // doesn't seek; it returns buffered data. Data can be + // transferred from the buffer at up to 16 MiB/s. + // + // If the drive has to seek, the time this takes varies a lot. + // A short seek is around 50 ms; a long seek is around 150 ms. + // However, the time isn't purely dependent on the distance; the + // pattern of previous seeks seems to matter in a way I'm + // not sure how to explain. + // + // Metroid Prime is a good example of a game that's sensitive to + // all of these details; if there isn't enough latency in the + // right places, doors open too quickly, and if there's too + // much latency in the wrong places, the video before the + // save-file select screen lags. + // + // For now, just use a very rough approximation: 50 ms seek + // for reads outside 1 MiB, accelerated reads within 1 MiB. + // We can refine this if someone comes up with a more complete + // model for seek times. - if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) + u64 current_time = CoreTiming::GetTicks(); + u64 ticks_until_completion; + + // Number of ticks it takes to seek and read directly from the disk. + u64 disk_read_duration = CalculateRawDiscReadTime(offset, length) + + SystemTimers::GetTicksPerSecond() / 1000 * DISC_ACCESS_TIME_MS; + + if (offset + length - g_last_read_offset > 1024 * 1024) { - // Make sure fast disc speed performs "instant" reads; in addition - // to being used to speed up games, fast disc speed is used as a - // workaround for crashes in certain games, including Star Wars - // Rogue Leader. - ticks_until_TC = 0; - g_last_read_time = current_time; + // No buffer; just use the simple seek time + read time. + DEBUG_LOG(DVDINTERFACE, "Seeking %" PRId64 " bytes", + s64(g_last_read_offset) - s64(offset)); + ticks_until_completion = disk_read_duration; + g_last_read_time = current_time + ticks_until_completion; } else { - // The drive buffers 1 MiB (?) of data after every read request; - // if a read request is covered by this buffer (or if it's - // faster to wait for the data to be buffered), the drive - // doesn't seek; it returns buffered data. Data can be - // transferred from the buffer at up to 16 MiB/s. - // - // If the drive has to seek, the time this takes varies a lot. - // A short seek is around 50 ms; a long seek is around 150 ms. - // However, the time isn't purely dependent on the distance; the - // pattern of previous seeks seems to matter in a way I'm - // not sure how to explain. - // - // Metroid Prime is a good example of a game that's sensitive to - // all of these details; if there isn't enough latency in the - // right places, doors open too quickly, and if there's too - // much latency in the wrong places, the video before the - // save-file select screen lags. - // - // For now, just use a very rough approximation: 50 ms seek - // for reads outside 1 MiB, accelerated reads within 1 MiB. - // We can refine this if someone comes up with a more complete - // model for seek times. + // Possibly buffered; use the buffer if it saves time. + // It's not proven that the buffer actually behaves like this, but + // it appears to be a decent approximation. - // Number of ticks it takes to seek and read directly from the disk. - u64 disk_read_duration = CalculateRawDiscReadTime(DVD_offset, m_DILENGTH.Length) + - SystemTimers::GetTicksPerSecond() / 1000 * DISC_ACCESS_TIME_MS; + // Time at which the buffer will contain the data we need. + u64 buffer_fill_time = g_last_read_time + + CalculateRawDiscReadTime(g_last_read_offset, + offset + length - g_last_read_offset); + // Number of ticks it takes to transfer the data from the buffer to memory. + u64 buffer_read_duration = length * + (SystemTimers::GetTicksPerSecond() / BUFFER_TRANSFER_RATE); - if (DVD_offset + m_DILENGTH.Length - g_last_read_offset > 1024 * 1024) + if (current_time > buffer_fill_time) { - // No buffer; just use the simple seek time + read time. - DEBUG_LOG(DVDINTERFACE, "Seeking %" PRId64 " bytes", - s64(g_last_read_offset) - s64(DVD_offset)); - ticks_until_TC = disk_read_duration; - g_last_read_time = current_time + ticks_until_TC; + DEBUG_LOG(DVDINTERFACE, "Fast buffer read at %" PRIx64, offset); + ticks_until_completion = buffer_read_duration; + g_last_read_time = buffer_fill_time; + } + else if (current_time + disk_read_duration > buffer_fill_time) + { + DEBUG_LOG(DVDINTERFACE, "Slow buffer read at %" PRIx64, offset); + ticks_until_completion = std::max(buffer_fill_time - current_time, + buffer_read_duration); + g_last_read_time = buffer_fill_time; } else { - // Possibly buffered; use the buffer if it saves time. - // It's not proven that the buffer actually behaves like this, but - // it appears to be a decent approximation. - - // Time at which the buffer will contain the data we need. - u64 buffer_fill_time = g_last_read_time + - CalculateRawDiscReadTime(g_last_read_offset, - DVD_offset + m_DILENGTH.Length - g_last_read_offset); - // Number of ticks it takes to transfer the data from the buffer to memory. - u64 buffer_read_duration = m_DILENGTH.Length * - (SystemTimers::GetTicksPerSecond() / BUFFER_TRANSFER_RATE); - - if (current_time > buffer_fill_time) - { - DEBUG_LOG(DVDINTERFACE, "Fast buffer read at %" PRId64, s64(DVD_offset)); - ticks_until_TC = buffer_read_duration; - g_last_read_time = buffer_fill_time; - } - else if (current_time + disk_read_duration > buffer_fill_time) - { - DEBUG_LOG(DVDINTERFACE, "Slow buffer read at %" PRId64, s64(DVD_offset)); - ticks_until_TC = std::max(buffer_fill_time - current_time, - buffer_read_duration); - g_last_read_time = buffer_fill_time; - } - else - { - DEBUG_LOG(DVDINTERFACE, "Short seek %" PRId64 " bytes", - s64(g_last_read_offset) - s64(DVD_offset)); - ticks_until_TC = disk_read_duration; - g_last_read_time = current_time + ticks_until_TC; - } + DEBUG_LOG(DVDINTERFACE, "Short seek %" PRId64 " bytes", + s64(g_last_read_offset) - s64(offset)); + ticks_until_completion = disk_read_duration; + g_last_read_time = current_time + ticks_until_completion; } } - g_last_read_offset = (DVD_offset + m_DILENGTH.Length - 2048) & ~2047; + g_last_read_offset = (offset + length - 2048) & ~2047; - return ticks_until_TC; + return ticks_until_completion; } // Returns the number of ticks it takes to read an amount of @@ -1199,6 +1359,7 @@ s64 CalculateRawDiscReadTime(u64 offset, s64 length) speed = std::sqrt(((average_offset - GC_DISC_LOCATION_1_OFFSET) / GC_BYTES_PER_AREA_UNIT + GC_DISC_AREA_UP_TO_LOCATION_1) / PI); } + DEBUG_LOG(DVDINTERFACE, "Disc speed: %f MiB/s", speed / 1024 / 1024); return (s64)(SystemTimers::GetTicksPerSecond() / speed * length); } diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 06b05fd300..e96a0a4ce0 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -13,6 +13,84 @@ namespace MMIO { class Mapping; } namespace DVDInterface { +// Not sure about endianness here. I'll just name them like this... +enum DIErrorLow +{ + ERROR_READY = 0x00000000, // Ready. + ERROR_COVER_L = 0x01000000, // Cover is opened. + ERROR_CHANGE_DISK = 0x02000000, // Disk change. + ERROR_NO_DISK = 0x03000000, // No disk. + ERROR_MOTOR_STOP_L = 0x04000000, // Motor stop. + ERROR_NO_DISKID_L = 0x05000000 // Disk ID not read. +}; +enum DIErrorHigh +{ + ERROR_NONE = 0x000000, // No error. + ERROR_MOTOR_STOP_H = 0x020400, // Motor stopped. + ERROR_NO_DISKID_H = 0x020401, // Disk ID not read. + ERROR_COVER_H = 0x023a00, // Medium not present / Cover opened. + ERROR_SEEK_NDONE = 0x030200, // No seek complete. + ERROR_READ = 0x031100, // Unrecovered read error. + ERROR_PROTOCOL = 0x040800, // Transfer protocol error. + ERROR_INV_CMD = 0x052000, // Invalid command operation code. + ERROR_AUDIO_BUF = 0x052001, // Audio Buffer not set. + ERROR_BLOCK_OOB = 0x052100, // Logical block address out of bounds. + ERROR_INV_FIELD = 0x052400, // Invalid field in command packet. + ERROR_INV_AUDIO = 0x052401, // Invalid audio command. + ERROR_INV_PERIOD = 0x052402, // Configuration out of permitted period. + ERROR_END_USR_AREA = 0x056300, // End of user area encountered on this track. + ERROR_MEDIUM = 0x062800, // Medium may have changed. + ERROR_MEDIUM_REQ = 0x0b5a01 // Operator medium removal request. +}; + +enum DICommand +{ + DVDLowInquiry = 0x12, + DVDLowReadDiskID = 0x70, + DVDLowRead = 0x71, + DVDLowWaitForCoverClose = 0x79, + DVDLowGetCoverReg = 0x7a, // DVDLowPrepareCoverRegister? + DVDLowNotifyReset = 0x7e, + DVDLowReadDvdPhysical = 0x80, + DVDLowReadDvdCopyright = 0x81, + DVDLowReadDvdDiscKey = 0x82, + DVDLowClearCoverInterrupt = 0x86, + DVDLowGetCoverStatus = 0x88, + DVDLowReset = 0x8a, + DVDLowOpenPartition = 0x8b, + DVDLowClosePartition = 0x8c, + DVDLowUnencryptedRead = 0x8d, + DVDLowEnableDvdVideo = 0x8e, + DVDLowReportKey = 0xa4, + DVDLowSeek = 0xab, + DVDLowReadDvd = 0xd0, + DVDLowReadDvdConfig = 0xd1, + DVDLowStopLaser = 0xd2, + DVDLowOffset = 0xd9, + DVDLowReadDiskBca = 0xda, + DVDLowRequestDiscStatus = 0xdb, + DVDLowRequestRetryNumber = 0xdc, + DVDLowSetMaximumRotation = 0xdd, + DVDLowSerMeasControl = 0xdf, + DVDLowRequestError = 0xe0, + DVDLowStopMotor = 0xe3, + DVDLowAudioBufferConfig = 0xe4 +}; + +enum DIInterruptType +{ + INT_DEINT = 0, + INT_TCINT = 1, + INT_BRKINT = 2, + INT_CVRINT = 3, +}; + +struct DVDCommandResult +{ + DIInterruptType interrupt_type; + u64 ticks_until_completion; +}; + void Init(); void Shutdown(); void DoState(PointerWrap &p); @@ -28,75 +106,10 @@ void ChangeDisc(const std::string& fileName); void SetLidOpen(bool _bOpen = true); bool IsLidOpen(); -// Used as low level control by WII_IPC_HLE_Device_DI -void ClearCoverInterrupt(); - // DVD Access Functions -bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength); +bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool raw = false); extern bool g_bStream; - -// Not sure about endianness here. I'll just name them like this... -enum DIErrorLow -{ - ERROR_READY = 0x00000000, // Ready. - ERROR_COVER_L = 0x01000000, // Cover is opened. - ERROR_CHANGE_DISK = 0x02000000, // Disk change. - ERROR_NO_DISK = 0x03000000, // No Disk. - ERROR_MOTOR_STOP_L = 0x04000000, // Motor stop. - ERROR_NO_DISKID_L = 0x05000000 // Disk ID not read. -}; -enum DIErrorHigh -{ - ERROR_NONE = 0x000000, // No error. - ERROR_MOTOR_STOP_H = 0x020400, // Motor stopped. - ERROR_NO_DISKID_H = 0x020401, // Disk ID not read. - ERROR_COVER_H = 0x023a00, // Medium not present / Cover opened. - ERROR_SEEK_NDONE = 0x030200, // No Seek complete. - ERROR_READ = 0x031100, // UnRecoverd read error. - ERROR_PROTOCOL = 0x040800, // Transfer protocol error. - ERROR_INV_CMD = 0x052000, // Invalid command operation code. - ERROR_AUDIO_BUF = 0x052001, // Audio Buffer not set. - ERROR_BLOCK_OOB = 0x052100, // Logical block address out of bounds. - ERROR_INV_FIELD = 0x052400, // Invalid Field in command packet. - ERROR_INV_AUDIO = 0x052401, // Invalid audio command. - ERROR_INV_PERIOD = 0x052402, // Configuration out of permitted period. - ERROR_END_USR_AREA = 0x056300, // End of user area encountered on this track. - ERROR_MEDIUM = 0x062800, // Medium may have changed. - ERROR_MEDIUM_REQ = 0x0b5a01 // Operator medium removal request. -}; - -enum DICommand -{ - DVDLowInquiry = 0x12, - DVDLowReadDiskID = 0x70, - DVDLowRead = 0x71, - DVDLowWaitForCoverClose = 0x79, - DVDLowGetCoverReg = 0x7a, // DVDLowPrepareCoverRegister? - DVDLowNotifyReset = 0x7e, - DVDLowReadDvdPhysical = 0x80, - DVDLowReadDvdCopyright = 0x81, - DVDLowReadDvdDiscKey = 0x82, - DVDLowClearCoverInterrupt = 0x86, - DVDLowGetCoverStatus = 0x88, - DVDLowReset = 0x8a, - DVDLowOpenPartition = 0x8b, - DVDLowClosePartition = 0x8c, - DVDLowUnencryptedRead = 0x8d, - DVDLowEnableDvdVideo = 0x8e, - DVDLowReportKey = 0xa4, - DVDLowSeek = 0xab, - DVDLowReadDvd = 0xd0, - DVDLowReadDvdConfig = 0xd1, - DVDLowStopLaser = 0xd2, - DVDLowOffset = 0xd9, - DVDLowReadDiskBca = 0xda, - DVDLowRequestDiscStatus = 0xdb, - DVDLowRequestRetryNumber = 0xdc, - DVDLowSetMaximumRotation = 0xdd, - DVDLowSerMeasControl = 0xdf, - DVDLowRequestError = 0xe0, - DVDLowStopMotor = 0xe3, - DVDLowAudioBufferConfig = 0xe4 -}; +DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, + u32 output_address, u32 output_length, bool write_to_DIIMMBUF); } // end of namespace DVDInterface diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp index aef2190d1a..5ff80b44c5 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp @@ -8,9 +8,7 @@ #include "Common/CommonTypes.h" #include "Common/Logging/LogManager.h" -#include "Core/Core.h" #include "Core/VolumeHandler.h" -#include "Core/HW/CPU.h" #include "Core/HW/DVDInterface.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" @@ -18,40 +16,17 @@ #include "Core/IPC_HLE/WII_IPC_HLE.h" #include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h" -#include "DiscIO/FileMonitor.h" -#include "DiscIO/Filesystem.h" -#include "DiscIO/VolumeCreator.h" - using namespace DVDInterface; - -#define DI_COVER_REG_INITIALIZED 0 // Should be 4, but doesn't work correctly... -#define DI_COVER_REG_NO_DISC 1 - CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName ) : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) - , m_pFileSystem(nullptr) - , m_ErrorStatus(0) - , m_CoverStatus(DI_COVER_REG_NO_DISC) {} CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di() -{ - if (m_pFileSystem) - { - delete m_pFileSystem; - m_pFileSystem = nullptr; - } -} +{} bool CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode) { - if (VolumeHandler::IsValid()) - { - m_pFileSystem = DiscIO::CreateFileSystem(VolumeHandler::GetVolume()); - m_CoverStatus |= DI_COVER_REG_INITIALIZED; - m_CoverStatus &= ~DI_COVER_REG_NO_DISC; - } Memory::Write_U32(GetDeviceID(), _CommandAddress + 4); m_Active = true; return true; @@ -59,12 +34,6 @@ bool CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode) bool CWII_IPC_HLE_Device_di::Close(u32 _CommandAddress, bool _bForce) { - if (m_pFileSystem) - { - delete m_pFileSystem; - m_pFileSystem = nullptr; - } - m_ErrorStatus = 0; if (!_bForce) Memory::Write_U32(0, _CommandAddress + 4); m_Active = false; @@ -73,18 +42,30 @@ bool CWII_IPC_HLE_Device_di::Close(u32 _CommandAddress, bool _bForce) bool CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress) { - u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); - u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); - u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); + u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); + u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); + u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); - u32 Command = Memory::Read_U32(BufferIn) >> 24; + + u32 command_0 = Memory::Read_U32(BufferIn); + u32 command_1 = Memory::Read_U32(BufferIn + 4); + u32 command_2 = Memory::Read_U32(BufferIn + 8); DEBUG_LOG(WII_IPC_DVD, "IOCtl Command(0x%08x) BufferIn(0x%08x, 0x%x) BufferOut(0x%08x, 0x%x)", - Command, BufferIn, BufferInSize, BufferOut, BufferOutSize); + command_0, BufferIn, BufferInSize, BufferOut, BufferOutSize); - u32 ReturnValue = ExecuteCommand(BufferIn, BufferInSize, BufferOut, BufferOutSize); - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); + // TATSUNOKO VS CAPCOM: Gets here with BufferOut == 0!!! + if (BufferOut != 0) + { + // Set out buffer to zeroes as a safety precaution + // to avoid answering nonsense values + Memory::Memset(BufferOut, 0, BufferOutSize); + } + DVDCommandResult result = ExecuteCommand(command_0, command_1, command_2, + BufferOut, BufferOutSize, false); + Memory::Write_U32(result.interrupt_type, _CommandAddress + 0x4); + // TODO: Don't discard result.ticks_until_completion return true; } @@ -133,318 +114,6 @@ bool CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress) return true; } -u32 CWII_IPC_HLE_Device_di::ExecuteCommand(u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize) -{ - u32 Command = Memory::Read_U32(_BufferIn) >> 24; - - // TATSUNOKO VS CAPCOM: Gets here with _BufferOut == 0!!! - if (_BufferOut != 0) - { - // Set out buffer to zeroes as a safety precaution to avoid answering - // nonsense values - Memory::Memset(_BufferOut, 0, _BufferOutSize); - } - - // Initializing a filesystem if it was just loaded - if (!m_pFileSystem && VolumeHandler::IsValid()) - { - m_pFileSystem = DiscIO::CreateFileSystem(VolumeHandler::GetVolume()); - m_CoverStatus |= DI_COVER_REG_INITIALIZED; - m_CoverStatus &= ~DI_COVER_REG_NO_DISC; - } - - // De-initializing a filesystem if the volume was unmounted - if (m_pFileSystem && !VolumeHandler::IsValid()) - { - delete m_pFileSystem; - m_pFileSystem = nullptr; - m_CoverStatus |= DI_COVER_REG_NO_DISC; - } - - switch (Command) - { - case DVDLowInquiry: - { - // (shuffle2) Taken from my Wii - Memory::Write_U32(0x00000002, _BufferOut); - Memory::Write_U32(0x20060526, _BufferOut + 4); - // This was in the oubuf even though this cmd is only supposed to reply with 64bits - // However, this and other tests strongly suggest that the buffer is static, and it's never - or rarely cleared. - Memory::Write_U32(0x41000000, _BufferOut + 8); - - INFO_LOG(WII_IPC_DVD, "DVDLowInquiry (Buffer 0x%08x, 0x%x)", - _BufferOut, _BufferOutSize); - } - break; - - case DVDLowReadDiskID: - { - VolumeHandler::RAWReadToPtr(Memory::GetPointer(_BufferOut), 0, _BufferOutSize); - - INFO_LOG(WII_IPC_DVD, "DVDLowReadDiskID %s", - ArrayToString(Memory::GetPointer(_BufferOut), _BufferOutSize, _BufferOutSize).c_str()); - } - break; - - case DVDLowRead: - { - if (_BufferOut == 0) - { - PanicAlert("DVDLowRead : _BufferOut == 0"); - return 0; - } - u32 Size = Memory::Read_U32(_BufferIn + 0x04); - u64 DVDAddress = (u64)Memory::Read_U32(_BufferIn + 0x08) << 2; - - // Don't do anything if the log is unselected - if (LogManager::GetInstance()->IsEnabled(LogTypes::FILEMON)) - { - if (m_pFileSystem) - { - const std::string filename = m_pFileSystem->GetFileName(DVDAddress); - - INFO_LOG(WII_IPC_DVD, "DVDLowRead: %s (0x%" PRIx64 ") - (DVDAddr: 0x%" PRIx64 ", Size: 0x%x)", - filename.c_str(), m_pFileSystem->GetFileSize(filename), DVDAddress, Size); - FileMon::CheckFile(filename, (int)m_pFileSystem->GetFileSize(filename)); - } - else - { - ERROR_LOG(WII_IPC_DVD, "Filesystem is invalid."); - } - } - - if (Size > _BufferOutSize) - { - PanicAlertT("Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); - Size = _BufferOutSize; - } - - if (!VolumeHandler::ReadToPtr(Memory::GetPointer(_BufferOut), DVDAddress, Size)) - { - PanicAlertT("DVDLowRead - Fatal Error: failed to read from volume"); - } - } - break; - - case DVDLowWaitForCoverClose: - { - INFO_LOG(WII_IPC_DVD, "DVDLowWaitForCoverClose (Buffer 0x%08x, 0x%x)", - _BufferOut, _BufferOutSize); - return 4; // ??? - } - break; - - case DVDLowGetCoverReg: - Memory::Write_U32(m_CoverStatus, _BufferOut); - - INFO_LOG(WII_IPC_DVD, "DVDLowGetCoverReg 0x%08x", Memory::Read_U32(_BufferOut)); - break; - - case DVDLowNotifyReset: - PanicAlert("DVDLowNotifyReset"); - break; - - case DVDLowReadDvdPhysical: - PanicAlert("DVDLowReadDvdPhysical"); - break; - - case DVDLowReadDvdCopyright: - PanicAlert("DVDLowReadDvdCopyright"); - break; - - case DVDLowReadDvdDiscKey: - PanicAlert("DVDLowReadDvdDiscKey"); - break; - - case DVDLowClearCoverInterrupt: - // TODO: check (seems to work ok) - INFO_LOG(WII_IPC_DVD, "DVDLowClearCoverInterrupt"); - ClearCoverInterrupt(); - break; - - case DVDLowGetCoverStatus: - Memory::Write_U32(IsDiscInside() ? 2 : 1, _BufferOut); - INFO_LOG(WII_IPC_DVD, "DVDLowGetCoverStatus: Disc %sInserted", IsDiscInside() ? "" : "Not "); - break; - - case DVDLowReset: - INFO_LOG(WII_IPC_DVD, "DVDLowReset"); - break; - - case DVDLowClosePartition: - INFO_LOG(WII_IPC_DVD, "DVDLowClosePartition"); - break; - - case DVDLowUnencryptedRead: - { - if (_BufferOut == 0) - { - PanicAlert("DVDLowRead : _BufferOut == 0"); - return 0; - } - - u32 Size = Memory::Read_U32(_BufferIn + 0x04); - - // We must make sure it is in a valid area! (#001 check) - // * 0x00000000 - 0x00014000 (limit of older IOS versions) - // * 0x460a0000 - 0x460a0008 - // * 0x7ed40000 - 0x7ed40008 - u32 DVDAddress32 = Memory::Read_U32(_BufferIn + 0x08); - if (!((DVDAddress32 > 0x00000000 && DVDAddress32 < 0x00014000) || - (((DVDAddress32 + Size) > 0x00000000) && (DVDAddress32 + Size) < 0x00014000) || - (DVDAddress32 > 0x460a0000 && DVDAddress32 < 0x460a0008) || - (((DVDAddress32 + Size) > 0x460a0000) && (DVDAddress32 + Size) < 0x460a0008) || - (DVDAddress32 > 0x7ed40000 && DVDAddress32 < 0x7ed40008) || - (((DVDAddress32 + Size) > 0x7ed40000) && (DVDAddress32 + Size) < 0x7ed40008))) - { - WARN_LOG(WII_IPC_DVD, "DVDLowUnencryptedRead: trying to read out of bounds @ %x", DVDAddress32); - m_ErrorStatus = ERROR_READY | ERROR_BLOCK_OOB; - // Should cause software to call DVDLowRequestError - return 2; - } - - u64 DVDAddress = (u64)DVDAddress32 << 2; - - INFO_LOG(WII_IPC_DVD, "DVDLowUnencryptedRead: DVDAddr: 0x%08" PRIx64 ", Size: 0x%x", DVDAddress, Size); - - if (Size > _BufferOutSize) - { - PanicAlertT("Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); - Size = _BufferOutSize; - } - if (!VolumeHandler::RAWReadToPtr(Memory::GetPointer(_BufferOut), DVDAddress, Size)) - { - PanicAlertT("DVDLowUnencryptedRead - Fatal Error: failed to read from volume"); - } - } - break; - - case DVDLowEnableDvdVideo: - ERROR_LOG(WII_IPC_DVD, "DVDLowEnableDvdVideo"); - break; - - case DVDLowReportKey: - INFO_LOG(WII_IPC_DVD, "DVDLowReportKey"); - // Does not work on retail discs/drives - // Retail games send this command to see if they are running on real retail hw - m_ErrorStatus = ERROR_READY | ERROR_INV_CMD; - return 2; - break; - - case DVDLowSeek: - { - u64 DVDAddress = Memory::Read_U32(_BufferIn + 0x4) << 2; - - if (m_pFileSystem) - { - const std::string filename = m_pFileSystem->GetFileName(DVDAddress); - - INFO_LOG(WII_IPC_DVD, "DVDLowSeek: %s (0x%" PRIx64 ") - (DVDAddr: 0x%" PRIx64 ")", - filename.c_str(), m_pFileSystem->GetFileSize(filename), DVDAddress); - } - else - { - ERROR_LOG(WII_IPC_DVD, "Filesystem is invalid."); - } - } - break; - - case DVDLowReadDvd: - ERROR_LOG(WII_IPC_DVD, "DVDLowReadDvd"); - break; - - case DVDLowReadDvdConfig: - ERROR_LOG(WII_IPC_DVD, "DVDLowReadDvdConfig"); - break; - - case DVDLowStopLaser: - ERROR_LOG(WII_IPC_DVD, "DVDLowStopLaser"); - break; - - case DVDLowOffset: - ERROR_LOG(WII_IPC_DVD, "DVDLowOffset"); - break; - - case DVDLowReadDiskBca: - WARN_LOG(WII_IPC_DVD, "DVDLowReadDiskBca"); - Memory::Write_U32(1, _BufferOut + 0x30); - break; - - case DVDLowRequestDiscStatus: - ERROR_LOG(WII_IPC_DVD, "DVDLowRequestDiscStatus"); - break; - - case DVDLowRequestRetryNumber: - ERROR_LOG(WII_IPC_DVD, "DVDLowRequestRetryNumber"); - break; - - case DVDLowSetMaximumRotation: - ERROR_LOG(WII_IPC_DVD, "DVDLowSetMaximumRotation"); - break; - - case DVDLowSerMeasControl: - ERROR_LOG(WII_IPC_DVD, "DVDLowSerMeasControl"); - break; - - case DVDLowRequestError: - // Identical to the error codes found in yagcd section 5.7.3.5.1 (so far) - WARN_LOG(WII_IPC_DVD, "DVDLowRequestError status = 0x%08x", m_ErrorStatus); - Memory::Write_U32(m_ErrorStatus, _BufferOut); - // When does error status get reset? - break; - -// Ex commands are immediate and respond with 4 bytes - case DVDLowStopMotor: - { - u32 eject = Memory::Read_U32(_BufferIn + 4); - // Drive won't do anything till reset is issued. I think it replies like nothing is wrong though? - u32 kill = Memory::Read_U32(_BufferIn + 8); - - INFO_LOG(WII_IPC_DVD, "DVDLowStopMotor %s %s", - eject ? "eject" : "", kill ? "kill!" : ""); - - if (eject) - { - SetLidOpen(true); - SetDiscInside(false); - } - } - break; - - case DVDLowAudioBufferConfig: - /* - For more information: http://www.crazynation.org/GC/GC_DD_TECH/GCTech.htm - - Upon Power up or reset , 2 commands must be issued for proper use of audio streaming: - DVDReadDiskID A8000040,00000000,00000020 - DVDLowAudioBufferConfig E4xx00yy,00000000,00000020 - - xx=byte 8 [0 or 1] from the disk header retrieved from DVDReadDiskID - yy=0 (if xx=0) or 0xA (if xx=1) - */ - ERROR_LOG(WII_IPC_DVD, "DVDLowAudioBufferConfig"); - break; - - // New Super Mario Bros.Wii sends these cmds - // but it seems we don't need to implement anything - case 0x95: - case 0x96: - WARN_LOG(WII_IPC_DVD, "Unimplemented command 0x%08x (Buffer 0x%08x, 0x%x)", - Command, _BufferOut, _BufferOutSize); - break; - - default: - ERROR_LOG(WII_IPC_DVD, "Unknown command 0x%08x (Buffer 0x%08x, 0x%x)", - Command, _BufferOut, _BufferOutSize); - - PanicAlertT("Unknown command 0x%08x", Command); - break; - } - - // i dunno but prolly 1 is okay all the time :) - return 1; -} - int CWII_IPC_HLE_Device_di::GetCmdDelay(u32 _CommandAddress) { u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h index 7216d4fd7d..6f28ae498d 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h @@ -27,13 +27,4 @@ public: bool IOCtlV(u32 _CommandAddress) override; int GetCmdDelay(u32) override; - -private: - - u32 ExecuteCommand(u32 BufferIn, u32 BufferInSize, u32 _BufferOut, u32 BufferOutSize); - - DiscIO::IFileSystem* m_pFileSystem; - u32 m_ErrorStatus; - // This flag seems to only be reset with poweron/off, not sure - u32 m_CoverStatus; };