summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorPierre Bourdon <delroth@gmail.com>2014-12-20 16:41:52 +0100
committerPierre Bourdon <delroth@gmail.com>2014-12-20 16:41:52 +0100
commite7eb4c06e2ae6bc14f5c509f4ebcbb1364da7f5e (patch)
treecca79a727300cbaebb7bec34750eaed3b0c38563 /Source/Core
parent9fb7091f369dfe7ff462807b55d896554c0f35bb (diff)
parentd1c8a8bd9f878bade3f0a235199b2d064507d117 (diff)
Merge pull request #1634 from JosJuice/disc-drive-unification-lite
Make WII_IPC_HLE_Device_DI call DVDInterface
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/HW/DVDInterface.cpp667
-rw-r--r--Source/Core/Core/HW/DVDInterface.h123
-rw-r--r--Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp371
-rw-r--r--Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h9
4 files changed, 502 insertions, 668 deletions
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)
+{
+ if (write_to_DIIMMBUF)
+ m_DIIMMBUF.Hex = value;
+ else
+ Memory::Write_U32(value, output_address);
+}
+
+DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address,
+ u32 DVD_length, u32 output_length, bool raw)
{
- // 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 (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);
- // _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;
+ // 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;
-
- // Just for fun
- INFO_LOG(DVDINTERFACE, "Drive Info: %02x %02x%02x/%02x (%02x)",
- driveInfo[6], driveInfo[4], driveInfo[5], driveInfo[7], driveInfo[8]);
- }
+ // (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);
+
+ 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;
+ // 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.
+
u64 current_time = CoreTiming::GetTicks();
- u64 ticks_until_TC;
+ 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 (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
+ 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.
-
- // 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;
-
- if (DVD_offset + m_DILENGTH.Length - g_last_read_offset > 1024 * 1024)
+ // 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,
+ 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 (current_time > buffer_fill_time)
+ {
+ 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)
{
- // 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, "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,35 +13,13 @@ namespace MMIO { class Mapping; }
namespace DVDInterface
{
-void Init();
-void Shutdown();
-void DoState(PointerWrap &p);
-
-void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
-
-// Disc detection and swapping
-void SetDiscInside(bool _DiscInside);
-bool IsDiscInside();
-void ChangeDisc(const std::string& fileName);
-
-// Lid Functions
-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);
-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_NO_DISK = 0x03000000, // No disk.
ERROR_MOTOR_STOP_L = 0x04000000, // Motor stop.
ERROR_NO_DISKID_L = 0x05000000 // Disk ID not read.
};
@@ -51,13 +29,13 @@ enum DIErrorHigh
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_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_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.
@@ -67,36 +45,71 @@ enum DIErrorHigh
enum DICommand
{
- DVDLowInquiry = 0x12,
- DVDLowReadDiskID = 0x70,
- DVDLowRead = 0x71,
- DVDLowWaitForCoverClose = 0x79,
- DVDLowGetCoverReg = 0x7a, // DVDLowPrepareCoverRegister?
- DVDLowNotifyReset = 0x7e,
- DVDLowReadDvdPhysical = 0x80,
- DVDLowReadDvdCopyright = 0x81,
- DVDLowReadDvdDiscKey = 0x82,
+ 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
+ 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);
+
+void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+
+// Disc detection and swapping
+void SetDiscInside(bool _DiscInside);
+bool IsDiscInside();
+void ChangeDisc(const std::string& fileName);
+
+// Lid Functions
+void SetLidOpen(bool _bOpen = true);
+bool IsLidOpen();
+
+// DVD Access Functions
+bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool raw = false);
+extern bool g_bStream;
+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;
};