1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/HW/DVD/DVDInterface.h"
6 
7 #include <algorithm>
8 #include <cinttypes>
9 #include <memory>
10 #include <optional>
11 #include <string>
12 #include <vector>
13 
14 #include "AudioCommon/AudioCommon.h"
15 
16 #include "Common/Align.h"
17 #include "Common/ChunkFile.h"
18 #include "Common/CommonTypes.h"
19 #include "Common/Config/Config.h"
20 #include "Common/Logging/Log.h"
21 
22 #include "Core/Analytics.h"
23 #include "Core/Config/MainSettings.h"
24 #include "Core/ConfigManager.h"
25 #include "Core/CoreTiming.h"
26 #include "Core/HW/AudioInterface.h"
27 #include "Core/HW/DVD/DVDMath.h"
28 #include "Core/HW/DVD/DVDThread.h"
29 #include "Core/HW/EXI/EXI_DeviceIPL.h"
30 #include "Core/HW/MMIO.h"
31 #include "Core/HW/Memmap.h"
32 #include "Core/HW/ProcessorInterface.h"
33 #include "Core/HW/StreamADPCM.h"
34 #include "Core/HW/SystemTimers.h"
35 #include "Core/IOS/DI/DI.h"
36 #include "Core/IOS/IOS.h"
37 #include "Core/Movie.h"
38 
39 #include "DiscIO/Blob.h"
40 #include "DiscIO/Enums.h"
41 #include "DiscIO/VolumeDisc.h"
42 #include "DiscIO/VolumeWii.h"
43 
44 #include "VideoCommon/OnScreenDisplay.h"
45 
46 // The minimum time it takes for the DVD drive to process a command (in microseconds)
47 constexpr u64 MINIMUM_COMMAND_LATENCY_US = 300;
48 
49 // The time it takes for a read command to start (in microseconds)
50 constexpr u64 READ_COMMAND_LATENCY_US = 600;
51 
52 // The size of the streaming buffer.
53 constexpr u64 STREAMING_BUFFER_SIZE = 1024 * 1024;
54 
55 // A single DVD disc sector
56 constexpr u64 DVD_SECTOR_SIZE = 0x800;
57 
58 // The minimum amount that a drive will read
59 constexpr u64 DVD_ECC_BLOCK_SIZE = 16 * DVD_SECTOR_SIZE;
60 
61 // Rate the drive can transfer data to main memory, given the data
62 // is already buffered. Measured in bytes per second.
63 constexpr u64 BUFFER_TRANSFER_RATE = 32 * 1024 * 1024;
64 
65 namespace DVDInterface
66 {
67 // internal hardware addresses
68 constexpr u32 DI_STATUS_REGISTER = 0x00;
69 constexpr u32 DI_COVER_REGISTER = 0x04;
70 constexpr u32 DI_COMMAND_0 = 0x08;
71 constexpr u32 DI_COMMAND_1 = 0x0C;
72 constexpr u32 DI_COMMAND_2 = 0x10;
73 constexpr u32 DI_DMA_ADDRESS_REGISTER = 0x14;
74 constexpr u32 DI_DMA_LENGTH_REGISTER = 0x18;
75 constexpr u32 DI_DMA_CONTROL_REGISTER = 0x1C;
76 constexpr u32 DI_IMMEDIATE_DATA_BUFFER = 0x20;
77 constexpr u32 DI_CONFIG_REGISTER = 0x24;
78 
79 // DI Status Register
80 union UDISR
81 {
82   u32 Hex;
83   struct
84   {
85     u32 BREAK : 1;      // Stop the Device + Interrupt
86     u32 DEINTMASK : 1;  // Access Device Error Int Mask
87     u32 DEINT : 1;      // Access Device Error Int
88     u32 TCINTMASK : 1;  // Transfer Complete Int Mask
89     u32 TCINT : 1;      // Transfer Complete Int
90     u32 BRKINTMASK : 1;
91     u32 BRKINT : 1;  // w 1: clear brkint
92     u32 : 25;
93   };
UDISR()94   UDISR() { Hex = 0; }
UDISR(u32 _hex)95   UDISR(u32 _hex) { Hex = _hex; }
96 };
97 
98 // DI Cover Register
99 union UDICVR
100 {
101   u32 Hex;
102   struct
103   {
104     u32 CVR : 1;         // 0: Cover closed  1: Cover open
105     u32 CVRINTMASK : 1;  // 1: Interrupt enabled
106     u32 CVRINT : 1;      // r 1: Interrupt requested w 1: Interrupt clear
107     u32 : 29;
108   };
UDICVR()109   UDICVR() { Hex = 0; }
UDICVR(u32 _hex)110   UDICVR(u32 _hex) { Hex = _hex; }
111 };
112 
113 // DI DMA Control Register
114 union UDICR
115 {
116   u32 Hex;
117   struct
118   {
119     u32 TSTART : 1;  // w:1 start   r:0 ready
120     u32 DMA : 1;     // 1: DMA Mode    0: Immediate Mode (can only do Access Register Command)
121     u32 RW : 1;      // 0: Read Command (DVD to Memory)  1: Write Command (Memory to DVD)
122     u32 : 29;
123   };
124 };
125 
126 // DI Config Register
127 union UDICFG
128 {
129   u32 Hex;
130   struct
131   {
132     u32 CONFIG : 8;
133     u32 : 24;
134   };
UDICFG()135   UDICFG() { Hex = 0; }
UDICFG(u32 _hex)136   UDICFG(u32 _hex) { Hex = _hex; }
137 };
138 
139 // STATE_TO_SAVE
140 
141 // Hardware registers
142 static UDISR s_DISR;
143 static UDICVR s_DICVR;
144 static u32 s_DICMDBUF[3];
145 static u32 s_DIMAR;
146 static u32 s_DILENGTH;
147 static UDICR s_DICR;
148 static u32 s_DIIMMBUF;
149 static UDICFG s_DICFG;
150 
151 static StreamADPCM::ADPCMDecoder s_adpcm_decoder;
152 
153 // DTK
154 static bool s_stream = false;
155 static bool s_stop_at_track_end = false;
156 static u64 s_audio_position;
157 static u64 s_current_start;
158 static u32 s_current_length;
159 static u64 s_next_start;
160 static u32 s_next_length;
161 static u32 s_pending_samples;
162 static bool s_enable_dtk = false;
163 static u8 s_dtk_buffer_length = 0;  // TODO: figure out how this affects the regular buffer
164 
165 // Disc drive state
166 static DriveState s_drive_state;
167 static DriveError s_error_code;
168 
169 // Disc drive timing
170 static u64 s_read_buffer_start_time;
171 static u64 s_read_buffer_end_time;
172 static u64 s_read_buffer_start_offset;
173 static u64 s_read_buffer_end_offset;
174 
175 // Disc changing
176 static std::string s_disc_path_to_insert;
177 static std::vector<std::string> s_auto_disc_change_paths;
178 static size_t s_auto_disc_change_index;
179 
180 // Events
181 static CoreTiming::EventType* s_finish_executing_command;
182 static CoreTiming::EventType* s_auto_change_disc;
183 static CoreTiming::EventType* s_eject_disc;
184 static CoreTiming::EventType* s_insert_disc;
185 
186 static void AutoChangeDiscCallback(u64 userdata, s64 cyclesLate);
187 static void EjectDiscCallback(u64 userdata, s64 cyclesLate);
188 static void InsertDiscCallback(u64 userdata, s64 cyclesLate);
189 static void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late);
190 
191 static void SetLidOpen();
192 
193 static void UpdateInterrupts();
194 static void GenerateDIInterrupt(DIInterruptType dvd_interrupt);
195 
196 static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length,
197                                u32 output_length, const DiscIO::Partition& partition,
198                                ReplyType reply_type, DIInterruptType* interrupt_type);
199 
200 static u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type);
201 
202 static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition,
203                           u32 output_address, ReplyType reply_type);
204 
DoState(PointerWrap & p)205 void DoState(PointerWrap& p)
206 {
207   p.DoPOD(s_DISR);
208   p.DoPOD(s_DICVR);
209   p.DoArray(s_DICMDBUF);
210   p.Do(s_DIMAR);
211   p.Do(s_DILENGTH);
212   p.Do(s_DICR);
213   p.Do(s_DIIMMBUF);
214   p.DoPOD(s_DICFG);
215 
216   p.Do(s_stream);
217   p.Do(s_stop_at_track_end);
218   p.Do(s_audio_position);
219   p.Do(s_current_start);
220   p.Do(s_current_length);
221   p.Do(s_next_start);
222   p.Do(s_next_length);
223   p.Do(s_pending_samples);
224   p.Do(s_enable_dtk);
225   p.Do(s_dtk_buffer_length);
226 
227   p.Do(s_drive_state);
228   p.Do(s_error_code);
229 
230   p.Do(s_read_buffer_start_time);
231   p.Do(s_read_buffer_end_time);
232   p.Do(s_read_buffer_start_offset);
233   p.Do(s_read_buffer_end_offset);
234 
235   p.Do(s_disc_path_to_insert);
236 
237   DVDThread::DoState(p);
238 
239   s_adpcm_decoder.DoState(p);
240 }
241 
ProcessDTKSamples(std::vector<s16> * temp_pcm,const std::vector<u8> & audio_data)242 static size_t ProcessDTKSamples(std::vector<s16>* temp_pcm, const std::vector<u8>& audio_data)
243 {
244   size_t samples_processed = 0;
245   size_t bytes_processed = 0;
246   while (samples_processed < temp_pcm->size() / 2 && bytes_processed < audio_data.size())
247   {
248     s_adpcm_decoder.DecodeBlock(&(*temp_pcm)[samples_processed * 2], &audio_data[bytes_processed]);
249     for (size_t i = 0; i < StreamADPCM::SAMPLES_PER_BLOCK * 2; ++i)
250     {
251       // TODO: Fix the mixer so it can accept non-byte-swapped samples.
252       s16* sample = &(*temp_pcm)[samples_processed * 2 + i];
253       *sample = Common::swap16(*sample);
254     }
255     samples_processed += StreamADPCM::SAMPLES_PER_BLOCK;
256     bytes_processed += StreamADPCM::ONE_BLOCK_SIZE;
257   }
258   return samples_processed;
259 }
260 
AdvanceDTK(u32 maximum_samples,u32 * samples_to_process)261 static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process)
262 {
263   u32 bytes_to_process = 0;
264   *samples_to_process = 0;
265   while (*samples_to_process < maximum_samples)
266   {
267     if (s_audio_position >= s_current_start + s_current_length)
268     {
269       DEBUG_LOG(DVDINTERFACE,
270                 "AdvanceDTK: NextStart=%08" PRIx64 ", NextLength=%08x, "
271                 "CurrentStart=%08" PRIx64 ", CurrentLength=%08x, AudioPos=%08" PRIx64,
272                 s_next_start, s_next_length, s_current_start, s_current_length, s_audio_position);
273 
274       s_audio_position = s_next_start;
275       s_current_start = s_next_start;
276       s_current_length = s_next_length;
277 
278       if (s_stop_at_track_end)
279       {
280         s_stop_at_track_end = false;
281         s_stream = false;
282         break;
283       }
284 
285       s_adpcm_decoder.ResetFilter();
286     }
287 
288     s_audio_position += StreamADPCM::ONE_BLOCK_SIZE;
289     bytes_to_process += StreamADPCM::ONE_BLOCK_SIZE;
290     *samples_to_process += StreamADPCM::SAMPLES_PER_BLOCK;
291   }
292 
293   return bytes_to_process;
294 }
295 
DTKStreamingCallback(DIInterruptType interrupt_type,const std::vector<u8> & audio_data,s64 cycles_late)296 static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
297                                  s64 cycles_late)
298 {
299   // TODO: Should we use GetAISSampleRate instead of a fixed 48 KHz? The audio mixer is using
300   // GetAISSampleRate. (This doesn't affect any actual games, since they all set it to 48 KHz.)
301   const u32 sample_rate = AudioInterface::Get48KHzSampleRate();
302 
303   // Determine which audio data to read next.
304   const u32 maximum_samples = sample_rate / 2000 * 7;  // 3.5 ms of samples
305   u64 read_offset = 0;
306   u32 read_length = 0;
307 
308   if (interrupt_type == DIInterruptType::TCINT)
309   {
310     // Send audio to the mixer.
311     std::vector<s16> temp_pcm(s_pending_samples * 2, 0);
312     ProcessDTKSamples(&temp_pcm, audio_data);
313     g_sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), s_pending_samples);
314 
315     if (s_stream && AudioInterface::IsPlaying())
316     {
317       read_offset = s_audio_position;
318       read_length = AdvanceDTK(maximum_samples, &s_pending_samples);
319     }
320     else
321     {
322       read_length = 0;
323       s_pending_samples = maximum_samples;
324     }
325   }
326   else
327   {
328     read_length = 0;
329     s_pending_samples = maximum_samples;
330   }
331 
332   // Read the next chunk of audio data asynchronously.
333   s64 ticks_to_dtk = SystemTimers::GetTicksPerSecond() * s64(s_pending_samples) / sample_rate;
334   ticks_to_dtk -= cycles_late;
335   if (read_length > 0)
336   {
337     DVDThread::StartRead(read_offset, read_length, DiscIO::PARTITION_NONE, ReplyType::DTK,
338                          ticks_to_dtk);
339   }
340   else
341   {
342     // There's nothing to read, so using DVDThread is unnecessary.
343     u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
344     CoreTiming::ScheduleEvent(ticks_to_dtk, s_finish_executing_command, userdata);
345   }
346 }
347 
Init()348 void Init()
349 {
350   ASSERT(!IsDiscInside());
351 
352   DVDThread::Start();
353 
354   s_DISR.Hex = 0;
355   s_DICVR.Hex = 1;  // Disc Channel relies on cover being open when no disc is inserted
356   s_DICMDBUF[0] = 0;
357   s_DICMDBUF[1] = 0;
358   s_DICMDBUF[2] = 0;
359   s_DIMAR = 0;
360   s_DILENGTH = 0;
361   s_DICR.Hex = 0;
362   s_DIIMMBUF = 0;
363   s_DICFG.Hex = 0;
364   s_DICFG.CONFIG = 1;  // Disable bootrom descrambler
365 
366   ResetDrive(false);
367 
368   s_auto_change_disc = CoreTiming::RegisterEvent("AutoChangeDisc", AutoChangeDiscCallback);
369   s_eject_disc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
370   s_insert_disc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
371 
372   s_finish_executing_command =
373       CoreTiming::RegisterEvent("FinishExecutingCommand", FinishExecutingCommandCallback);
374 
375   u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
376   CoreTiming::ScheduleEvent(0, s_finish_executing_command, userdata);
377 }
378 
379 // Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the
380 // emulated device, or any inserted disc.
ResetDrive(bool spinup)381 void ResetDrive(bool spinup)
382 {
383   s_stream = false;
384   s_stop_at_track_end = false;
385   s_audio_position = 0;
386   s_next_start = 0;
387   s_next_length = 0;
388   s_current_start = 0;
389   s_current_length = 0;
390   s_pending_samples = 0;
391   s_enable_dtk = false;
392   s_dtk_buffer_length = 0;
393 
394   if (!IsDiscInside())
395   {
396     // CoverOpened is used when the cover is open;
397     // NoMediumPresent is used when the cover is closed but there is no disc.
398     // On the Wii, this can only happen if something other than a DVD is inserted into the disc
399     // drive (for instance, an audio CD) and only after it attempts to read it.  Otherwise, it will
400     // report the cover as opened.
401     SetDriveState(DriveState::CoverOpened);
402   }
403   else if (!spinup)
404   {
405     // Wii hardware tests indicate that this is used when ejecting and inserting a new disc, or
406     // performing a reset without spinup.
407     SetDriveState(DriveState::DiscChangeDetected);
408   }
409   else
410   {
411     SetDriveState(DriveState::DiscIdNotRead);
412   }
413 
414   SetDriveError(DriveError::None);
415 
416   // The buffer is empty at start
417   s_read_buffer_start_offset = 0;
418   s_read_buffer_end_offset = 0;
419   s_read_buffer_start_time = 0;
420   s_read_buffer_end_time = 0;
421 }
422 
Shutdown()423 void Shutdown()
424 {
425   DVDThread::Stop();
426 }
427 
SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,std::optional<std::vector<std::string>> auto_disc_change_paths={})428 void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
429              std::optional<std::vector<std::string>> auto_disc_change_paths = {})
430 {
431   bool had_disc = IsDiscInside();
432   bool has_disc = static_cast<bool>(disc);
433 
434   if (has_disc)
435   {
436     const DiscIO::BlobReader& blob = disc->GetBlobReader();
437     if (!blob.HasFastRandomAccessInBlock() && blob.GetBlockSize() > 0x200000)
438     {
439       OSD::AddMessage("You are running a disc image with a very large block size.", 60000);
440       OSD::AddMessage("This will likely lead to performance problems.", 60000);
441       OSD::AddMessage("You can use Dolphin's convert feature to reduce the block size.", 60000);
442     }
443   }
444 
445   if (auto_disc_change_paths)
446   {
447     ASSERT_MSG(DISCIO, (*auto_disc_change_paths).size() != 1,
448                "Cannot automatically change between one disc");
449 
450     s_auto_disc_change_paths = *auto_disc_change_paths;
451     s_auto_disc_change_index = 0;
452   }
453 
454   // Assume that inserting a disc requires having an empty disc before
455   if (had_disc != has_disc)
456     ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::DiscChanged] = true;
457 
458   DVDThread::SetDisc(std::move(disc));
459   SetLidOpen();
460 
461   ResetDrive(false);
462 }
463 
IsDiscInside()464 bool IsDiscInside()
465 {
466   return DVDThread::HasDisc();
467 }
468 
AutoChangeDiscCallback(u64 userdata,s64 cyclesLate)469 static void AutoChangeDiscCallback(u64 userdata, s64 cyclesLate)
470 {
471   AutoChangeDisc();
472 }
473 
EjectDiscCallback(u64 userdata,s64 cyclesLate)474 static void EjectDiscCallback(u64 userdata, s64 cyclesLate)
475 {
476   SetDisc(nullptr, {});
477 }
478 
InsertDiscCallback(u64 userdata,s64 cyclesLate)479 static void InsertDiscCallback(u64 userdata, s64 cyclesLate)
480 {
481   std::unique_ptr<DiscIO::VolumeDisc> new_disc = DiscIO::CreateDisc(s_disc_path_to_insert);
482 
483   if (new_disc)
484     SetDisc(std::move(new_disc), {});
485   else
486     PanicAlertT("The disc that was about to be inserted couldn't be found.");
487 
488   s_disc_path_to_insert.clear();
489 }
490 
491 // Must only be called on the CPU thread
EjectDisc(EjectCause cause)492 void EjectDisc(EjectCause cause)
493 {
494   CoreTiming::ScheduleEvent(0, s_eject_disc);
495   if (cause == EjectCause::User)
496     ExpansionInterface::g_rtc_flags[ExpansionInterface::RTCFlag::EjectButton] = true;
497 }
498 
499 // Must only be called on the CPU thread
ChangeDisc(const std::vector<std::string> & paths)500 void ChangeDisc(const std::vector<std::string>& paths)
501 {
502   ASSERT_MSG(DISCIO, !paths.empty(), "Trying to insert an empty list of discs");
503 
504   if (paths.size() > 1)
505   {
506     s_auto_disc_change_paths = paths;
507     s_auto_disc_change_index = 0;
508   }
509 
510   ChangeDisc(paths[0]);
511 }
512 
513 // Must only be called on the CPU thread
ChangeDisc(const std::string & new_path)514 void ChangeDisc(const std::string& new_path)
515 {
516   if (!s_disc_path_to_insert.empty())
517   {
518     PanicAlertT("A disc is already about to be inserted.");
519     return;
520   }
521 
522   EjectDisc(EjectCause::User);
523 
524   s_disc_path_to_insert = new_path;
525   CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), s_insert_disc);
526   Movie::SignalDiscChange(new_path);
527 
528   for (size_t i = 0; i < s_auto_disc_change_paths.size(); ++i)
529   {
530     if (s_auto_disc_change_paths[i] == new_path)
531     {
532       s_auto_disc_change_index = i;
533       return;
534     }
535   }
536 
537   s_auto_disc_change_paths.clear();
538 }
539 
540 // Must only be called on the CPU thread
AutoChangeDisc()541 bool AutoChangeDisc()
542 {
543   if (s_auto_disc_change_paths.empty())
544     return false;
545 
546   s_auto_disc_change_index = (s_auto_disc_change_index + 1) % s_auto_disc_change_paths.size();
547   ChangeDisc(s_auto_disc_change_paths[s_auto_disc_change_index]);
548   return true;
549 }
550 
SetLidOpen()551 static void SetLidOpen()
552 {
553   u32 old_value = s_DICVR.CVR;
554   s_DICVR.CVR = IsDiscInside() ? 0 : 1;
555   if (s_DICVR.CVR != old_value)
556     GenerateDIInterrupt(DIInterruptType::CVRINT);
557 }
558 
UpdateRunningGameMetadata(std::optional<u64> title_id)559 bool UpdateRunningGameMetadata(std::optional<u64> title_id)
560 {
561   if (!DVDThread::HasDisc())
562     return false;
563 
564   return DVDThread::UpdateRunningGameMetadata(IOS::HLE::Device::DI::GetCurrentPartition(),
565                                               title_id);
566 }
567 
RegisterMMIO(MMIO::Mapping * mmio,u32 base)568 void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
569 {
570   mmio->Register(base | DI_STATUS_REGISTER, MMIO::DirectRead<u32>(&s_DISR.Hex),
571                  MMIO::ComplexWrite<u32>([](u32, u32 val) {
572                    UDISR tmpStatusReg(val);
573 
574                    s_DISR.DEINTMASK = tmpStatusReg.DEINTMASK;
575                    s_DISR.TCINTMASK = tmpStatusReg.TCINTMASK;
576                    s_DISR.BRKINTMASK = tmpStatusReg.BRKINTMASK;
577                    s_DISR.BREAK = tmpStatusReg.BREAK;
578 
579                    if (tmpStatusReg.DEINT)
580                      s_DISR.DEINT = 0;
581 
582                    if (tmpStatusReg.TCINT)
583                      s_DISR.TCINT = 0;
584 
585                    if (tmpStatusReg.BRKINT)
586                      s_DISR.BRKINT = 0;
587 
588                    if (s_DISR.BREAK)
589                    {
590                      DEBUG_ASSERT(0);
591                    }
592 
593                    UpdateInterrupts();
594                  }));
595 
596   mmio->Register(base | DI_COVER_REGISTER, MMIO::DirectRead<u32>(&s_DICVR.Hex),
597                  MMIO::ComplexWrite<u32>([](u32, u32 val) {
598                    UDICVR tmpCoverReg(val);
599 
600                    s_DICVR.CVRINTMASK = tmpCoverReg.CVRINTMASK;
601 
602                    if (tmpCoverReg.CVRINT)
603                      s_DICVR.CVRINT = 0;
604 
605                    UpdateInterrupts();
606                  }));
607 
608   // Command registers, which have no special logic
609   mmio->Register(base | DI_COMMAND_0, MMIO::DirectRead<u32>(&s_DICMDBUF[0]),
610                  MMIO::DirectWrite<u32>(&s_DICMDBUF[0]));
611   mmio->Register(base | DI_COMMAND_1, MMIO::DirectRead<u32>(&s_DICMDBUF[1]),
612                  MMIO::DirectWrite<u32>(&s_DICMDBUF[1]));
613   mmio->Register(base | DI_COMMAND_2, MMIO::DirectRead<u32>(&s_DICMDBUF[2]),
614                  MMIO::DirectWrite<u32>(&s_DICMDBUF[2]));
615 
616   // DMA related registers. Mostly direct accesses (+ masking for writes to
617   // handle things like address alignment) and complex write on the DMA
618   // control register that will trigger the DMA.
619   mmio->Register(base | DI_DMA_ADDRESS_REGISTER, MMIO::DirectRead<u32>(&s_DIMAR),
620                  MMIO::DirectWrite<u32>(&s_DIMAR, ~0x1F));
621   mmio->Register(base | DI_DMA_LENGTH_REGISTER, MMIO::DirectRead<u32>(&s_DILENGTH),
622                  MMIO::DirectWrite<u32>(&s_DILENGTH, ~0x1F));
623   mmio->Register(base | DI_DMA_CONTROL_REGISTER, MMIO::DirectRead<u32>(&s_DICR.Hex),
624                  MMIO::ComplexWrite<u32>([](u32, u32 val) {
625                    s_DICR.Hex = val & 7;
626                    if (s_DICR.TSTART)
627                    {
628                      ExecuteCommand(ReplyType::Interrupt);
629                    }
630                  }));
631 
632   mmio->Register(base | DI_IMMEDIATE_DATA_BUFFER, MMIO::DirectRead<u32>(&s_DIIMMBUF),
633                  MMIO::DirectWrite<u32>(&s_DIIMMBUF));
634 
635   // DI config register is read only.
636   mmio->Register(base | DI_CONFIG_REGISTER, MMIO::DirectRead<u32>(&s_DICFG.Hex),
637                  MMIO::InvalidWrite<u32>());
638 }
639 
UpdateInterrupts()640 static void UpdateInterrupts()
641 {
642   const bool set_mask = (s_DISR.DEINT & s_DISR.DEINTMASK) || (s_DISR.TCINT & s_DISR.TCINTMASK) ||
643                         (s_DISR.BRKINT & s_DISR.BRKINTMASK) ||
644                         (s_DICVR.CVRINT & s_DICVR.CVRINTMASK);
645 
646   ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DI, set_mask);
647 
648   // Required for Summoner: A Goddess Reborn
649   CoreTiming::ForceExceptionCheck(50);
650 }
651 
GenerateDIInterrupt(DIInterruptType dvd_interrupt)652 static void GenerateDIInterrupt(DIInterruptType dvd_interrupt)
653 {
654   switch (dvd_interrupt)
655   {
656   case DIInterruptType::DEINT:
657     s_DISR.DEINT = true;
658     break;
659   case DIInterruptType::TCINT:
660     s_DISR.TCINT = true;
661     break;
662   case DIInterruptType::BRKINT:
663     s_DISR.BRKINT = true;
664     break;
665   case DIInterruptType::CVRINT:
666     s_DICVR.CVRINT = true;
667     break;
668   }
669 
670   UpdateInterrupts();
671 }
672 
SetInterruptEnabled(DIInterruptType interrupt,bool enabled)673 void SetInterruptEnabled(DIInterruptType interrupt, bool enabled)
674 {
675   switch (interrupt)
676   {
677   case DIInterruptType::DEINT:
678     s_DISR.DEINTMASK = enabled;
679     break;
680   case DIInterruptType::TCINT:
681     s_DISR.TCINTMASK = enabled;
682     break;
683   case DIInterruptType::BRKINT:
684     s_DISR.BRKINTMASK = enabled;
685     break;
686   case DIInterruptType::CVRINT:
687     s_DICVR.CVRINTMASK = enabled;
688     break;
689   }
690 }
691 
ClearInterrupt(DIInterruptType interrupt)692 void ClearInterrupt(DIInterruptType interrupt)
693 {
694   switch (interrupt)
695   {
696   case DIInterruptType::DEINT:
697     s_DISR.DEINT = false;
698     break;
699   case DIInterruptType::TCINT:
700     s_DISR.TCINT = false;
701     break;
702   case DIInterruptType::BRKINT:
703     s_DISR.BRKINT = false;
704     break;
705   case DIInterruptType::CVRINT:
706     s_DICVR.CVRINT = false;
707     break;
708   }
709 }
710 
711 // Checks the drive state to make sure a read-like command can be performed.
712 // If false is returned, SetDriveError will have been called, and the caller
713 // should issue a DEINT interrupt.
CheckReadPreconditions()714 static bool CheckReadPreconditions()
715 {
716   if (!IsDiscInside())  // Implies CoverOpened or NoMediumPresent
717   {
718     ERROR_LOG(DVDINTERFACE, "No disc inside.");
719     SetDriveError(DriveError::MediumNotPresent);
720     return false;
721   }
722   if (s_drive_state == DriveState::DiscChangeDetected)
723   {
724     ERROR_LOG(DVDINTERFACE, "Disc changed (motor stopped).");
725     SetDriveError(DriveError::MediumChanged);
726     return false;
727   }
728   if (s_drive_state == DriveState::MotorStopped)
729   {
730     ERROR_LOG(DVDINTERFACE, "Motor stopped.");
731     SetDriveError(DriveError::MotorStopped);
732     return false;
733   }
734   if (s_drive_state == DriveState::DiscIdNotRead)
735   {
736     ERROR_LOG(DVDINTERFACE, "Disc id not read.");
737     SetDriveError(DriveError::NoDiscID);
738     return false;
739   }
740   return true;
741 }
742 
743 // Iff false is returned, ScheduleEvent must be used to finish executing the command
ExecuteReadCommand(u64 dvd_offset,u32 output_address,u32 dvd_length,u32 output_length,const DiscIO::Partition & partition,ReplyType reply_type,DIInterruptType * interrupt_type)744 static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length,
745                                u32 output_length, const DiscIO::Partition& partition,
746                                ReplyType reply_type, DIInterruptType* interrupt_type)
747 {
748   if (!CheckReadPreconditions())
749   {
750     // Disc read fails
751     *interrupt_type = DIInterruptType::DEINT;
752     return false;
753   }
754   else
755   {
756     // Disc read succeeds
757     *interrupt_type = DIInterruptType::TCINT;
758   }
759 
760   if (dvd_length > output_length)
761   {
762     WARN_LOG(DVDINTERFACE, "Detected an attempt to read more data from the DVD "
763                            "than what fits inside the out buffer. Clamping.");
764     dvd_length = output_length;
765   }
766 
767   // Many Wii games intentionally try to read from an offset which is just past the end of a regular
768   // DVD but just before the end of a DVD-R, displaying "Error #001" and failing to boot if the read
769   // succeeds (see https://wiibrew.org/wiki//dev/di#0x8D_DVDLowUnencryptedRead for more details).
770   // It would be nice if we simply could rely on DiscIO for letting us know whether a read is out
771   // of bounds, but this unfortunately doesn't work when using a disc image format that doesn't
772   // store the original size of the disc, most notably WBFS. Instead, we have a little hack here:
773   // reject all non-partition reads that come from IOS that go past the offset 0x50000. IOS only
774   // allows non-partition reads if they are before 0x50000 or if they are in one of the two small
775   // areas 0x118240000-0x118240020 and 0x1FB4E0000-0x1FB4E0020 (both of which only are used for
776   // Error #001 checks), so the only thing we disallow with this hack that actually should be
777   // allowed is non-partition reads in the 0x118240000-0x118240020 area on dual-layer discs.
778   // In practice, dual-layer games don't attempt to do non-partition reads in that area.
779   if (reply_type == ReplyType::IOS && partition == DiscIO::PARTITION_NONE &&
780       dvd_offset + dvd_length > 0x50000)
781   {
782     SetDriveError(DriveError::BlockOOB);
783     *interrupt_type = DIInterruptType::DEINT;
784     return false;
785   }
786 
787   ScheduleReads(dvd_offset, dvd_length, partition, output_address, reply_type);
788   return true;
789 }
790 
791 // When the command has finished executing, callback_event_type
792 // will be called using CoreTiming::ScheduleEvent,
793 // with the userdata set to the interrupt type.
ExecuteCommand(ReplyType reply_type)794 void ExecuteCommand(ReplyType reply_type)
795 {
796   DIInterruptType interrupt_type = DIInterruptType::TCINT;
797   bool command_handled_by_thread = false;
798 
799   // DVDLowRequestError needs access to the error code set by the previous command
800   if (static_cast<DICommand>(s_DICMDBUF[0] >> 24) != DICommand::RequestError)
801     SetDriveError(DriveError::None);
802 
803   switch (static_cast<DICommand>(s_DICMDBUF[0] >> 24))
804   {
805   // Used by both GC and Wii
806   case DICommand::Inquiry:
807     // (shuffle2) Taken from my Wii
808     Memory::Write_U32(0x00000002, s_DIMAR);      // Revision level, device code
809     Memory::Write_U32(0x20060526, s_DIMAR + 4);  // Release date
810     Memory::Write_U32(0x41000000, s_DIMAR + 8);  // Version
811 
812     INFO_LOG(DVDINTERFACE, "DVDLowInquiry (Buffer 0x%08x, 0x%x)", s_DIMAR, s_DILENGTH);
813     break;
814 
815   // GC-only patched drive firmware command, used by libogc
816   case DICommand::Unknown55:
817     INFO_LOG(DVDINTERFACE, "SetExtension");
818     SetDriveError(DriveError::InvalidCommand);
819     interrupt_type = DIInterruptType::DEINT;
820     break;
821 
822   // Wii-exclusive
823   case DICommand::ReportKey:
824     INFO_LOG(DVDINTERFACE, "DVDLowReportKey");
825     // Does not work on retail discs/drives
826     // Retail games send this command to see if they are running on real retail hw
827     SetDriveError(DriveError::InvalidCommand);
828     interrupt_type = DIInterruptType::DEINT;
829     break;
830 
831   // DMA Read from Disc. Only used through direct access on GC; direct use is prohibited by
832   // IOS (which uses it internally)
833   case DICommand::Read:
834     switch (s_DICMDBUF[0] & 0xFF)
835     {
836     case 0x00:  // Read Sector
837     {
838       u64 iDVDOffset = static_cast<u64>(s_DICMDBUF[1]) << 2;
839 
840       INFO_LOG(DVDINTERFACE,
841                "Read: DVDOffset=%08" PRIx64
842                ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x",
843                iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH);
844 
845       if (s_drive_state == DriveState::ReadyNoReadsMade)
846         SetDriveState(DriveState::Ready);
847 
848       command_handled_by_thread =
849           ExecuteReadCommand(iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH, DiscIO::PARTITION_NONE,
850                              reply_type, &interrupt_type);
851     }
852     break;
853 
854     case 0x40:  // Read DiscID
855       INFO_LOG(DVDINTERFACE, "Read DiscID: buffer %08x", s_DIMAR);
856       if (s_drive_state == DriveState::DiscIdNotRead)
857       {
858         SetDriveState(DriveState::ReadyNoReadsMade);
859       }
860       else if (s_drive_state == DriveState::ReadyNoReadsMade)
861       {
862         // The first disc ID reading is required before DTK can be configured.
863         // If the disc ID is read again (or any other read occurs), it no longer can
864         // be configured.
865         SetDriveState(DriveState::Ready);
866       }
867 
868       command_handled_by_thread = ExecuteReadCommand(
869           0, s_DIMAR, 0x20, s_DILENGTH, DiscIO::PARTITION_NONE, reply_type, &interrupt_type);
870       break;
871 
872     default:
873       ERROR_LOG(DVDINTERFACE, "Unknown read subcommand: %08x", s_DICMDBUF[0]);
874       break;
875     }
876     break;
877 
878   // Used by both GC and Wii
879   case DICommand::Seek:
880     // Currently unimplemented
881     INFO_LOG(DVDINTERFACE, "Seek: offset=%09" PRIx64 " (ignoring)",
882              static_cast<u64>(s_DICMDBUF[1]) << 2);
883     break;
884 
885   // Wii-exclusive
886   case DICommand::ReadDVDMetadata:
887     switch ((s_DICMDBUF[0] >> 16) & 0xFF)
888     {
889     case 0:
890       ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdPhysical");
891       break;
892     case 1:
893       ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdCopyright");
894       break;
895     case 2:
896       ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdDiscKey");
897       break;
898     default:
899       ERROR_LOG(DVDINTERFACE, "Unknown 0xAD subcommand in %08x", s_DICMDBUF[0]);
900       break;
901     }
902     SetDriveError(DriveError::InvalidCommand);
903     interrupt_type = DIInterruptType::DEINT;
904     break;
905   // Wii-exclusive
906   case DICommand::ReadDVD:
907     ERROR_LOG(DVDINTERFACE, "DVDLowReadDvd");
908     SetDriveError(DriveError::InvalidCommand);
909     interrupt_type = DIInterruptType::DEINT;
910     break;
911   // Wii-exclusive
912   case DICommand::ReadDVDConfig:
913     ERROR_LOG(DVDINTERFACE, "DVDLowReadDvdConfig");
914     SetDriveError(DriveError::InvalidCommand);
915     interrupt_type = DIInterruptType::DEINT;
916     break;
917   // Wii-exclusive
918   case DICommand::StopLaser:
919     ERROR_LOG(DVDINTERFACE, "DVDLowStopLaser");
920     DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_STOP_LASER);
921     SetDriveError(DriveError::InvalidCommand);
922     interrupt_type = DIInterruptType::DEINT;
923     break;
924   // Wii-exclusive
925   case DICommand::Offset:
926     ERROR_LOG(DVDINTERFACE, "DVDLowOffset");
927     DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_OFFSET);
928     SetDriveError(DriveError::InvalidCommand);
929     interrupt_type = DIInterruptType::DEINT;
930     break;
931   // Wii-exclusive
932   case DICommand::ReadBCA:
933     WARN_LOG(DVDINTERFACE, "DVDLowReadDiskBca - supplying dummy data to appease NSMBW");
934     DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_READ_DISK_BCA);
935     // NSMBW checks that the first 0x33 bytes of the BCA are 0, then it expects a 1.
936     // Most (all?) other games have 0x34 0's at the start of the BCA, but don't actually
937     // read it.  NSMBW doesn't care about the other 12 bytes (which contain manufacturing data?)
938 
939     // TODO: Read the .bca file that cleanrip generates, if it exists
940     // Memory::CopyToEmu(output_address, bca_data, 0x40);
941     Memory::Memset(s_DIMAR, 0, 0x40);
942     Memory::Write_U8(1, s_DIMAR + 0x33);
943     break;
944   // Wii-exclusive
945   case DICommand::RequestDiscStatus:
946     ERROR_LOG(DVDINTERFACE, "DVDLowRequestDiscStatus");
947     DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_DISC_STATUS);
948     SetDriveError(DriveError::InvalidCommand);
949     interrupt_type = DIInterruptType::DEINT;
950     break;
951   // Wii-exclusive
952   case DICommand::RequestRetryNumber:
953     ERROR_LOG(DVDINTERFACE, "DVDLowRequestRetryNumber");
954     DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_REQUEST_RETRY_NUMBER);
955     SetDriveError(DriveError::InvalidCommand);
956     interrupt_type = DIInterruptType::DEINT;
957     break;
958   // Wii-exclusive
959   case DICommand::SetMaximumRotation:
960     ERROR_LOG(DVDINTERFACE, "DVDLowSetMaximumRotation");
961     SetDriveError(DriveError::InvalidCommand);
962     interrupt_type = DIInterruptType::DEINT;
963     break;
964   // Wii-exclusive
965   case DICommand::SerMeasControl:
966     ERROR_LOG(DVDINTERFACE, "DVDLowSerMeasControl");
967     DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DVD_LOW_SER_MEAS_CONTROL);
968     SetDriveError(DriveError::InvalidCommand);
969     interrupt_type = DIInterruptType::DEINT;
970     break;
971 
972   // Used by both GC and Wii
973   case DICommand::RequestError:
974   {
975     u32 drive_state;
976     if (s_drive_state == DriveState::Ready)
977       drive_state = 0;
978     else
979       drive_state = static_cast<u32>(s_drive_state) - 1;
980 
981     const u32 result = (drive_state << 24) | static_cast<u32>(s_error_code);
982     INFO_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", result);
983     s_DIIMMBUF = result;
984     SetDriveError(DriveError::None);
985     break;
986   }
987 
988   // Audio Stream (Immediate). Only used by some GC games, but does exist on the Wii
989   // (command_0 >> 16) & 0xFF = Subcommand
990   // command_1 << 2           = Offset on disc
991   // command_2                = Length of the stream
992   case DICommand::AudioStream:
993   {
994     if (!CheckReadPreconditions())
995     {
996       ERROR_LOG(DVDINTERFACE, "Cannot play audio (command %08x)", s_DICMDBUF[0]);
997       interrupt_type = DIInterruptType::DEINT;
998       break;
999     }
1000     if (!s_enable_dtk)
1001     {
1002       ERROR_LOG(DVDINTERFACE,
1003                 "Attempted to change playing audio while audio is disabled!  (%08x %08x %08x)",
1004                 s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]);
1005       SetDriveError(DriveError::NoAudioBuf);
1006       interrupt_type = DIInterruptType::DEINT;
1007       break;
1008     }
1009 
1010     if (s_drive_state == DriveState::ReadyNoReadsMade)
1011       SetDriveState(DriveState::Ready);
1012 
1013     switch ((s_DICMDBUF[0] >> 16) & 0xFF)
1014     {
1015     case 0x00:
1016     {
1017       u64 offset = static_cast<u64>(s_DICMDBUF[1]) << 2;
1018       u32 length = s_DICMDBUF[2];
1019       INFO_LOG(DVDINTERFACE, "(Audio) Start stream: offset: %08" PRIx64 " length: %08x", offset,
1020                length);
1021 
1022       if ((offset == 0) && (length == 0))
1023       {
1024         s_stop_at_track_end = true;
1025       }
1026       else if (!s_stop_at_track_end)
1027       {
1028         s_next_start = offset;
1029         s_next_length = length;
1030         if (!s_stream)
1031         {
1032           s_current_start = s_next_start;
1033           s_current_length = s_next_length;
1034           s_audio_position = s_current_start;
1035           s_adpcm_decoder.ResetFilter();
1036           s_stream = true;
1037         }
1038       }
1039       break;
1040     }
1041     case 0x01:
1042       INFO_LOG(DVDINTERFACE, "(Audio) Stop stream");
1043       s_stop_at_track_end = false;
1044       s_stream = false;
1045       break;
1046     default:
1047       ERROR_LOG(DVDINTERFACE, "Invalid audio command!  (%08x %08x %08x)", s_DICMDBUF[0],
1048                 s_DICMDBUF[1], s_DICMDBUF[2]);
1049       SetDriveError(DriveError::InvalidAudioCommand);
1050       interrupt_type = DIInterruptType::DEINT;
1051       break;
1052     }
1053   }
1054   break;
1055 
1056   // Request Audio Status (Immediate). Only used by some GC games, but does exist on the Wii
1057   case DICommand::RequestAudioStatus:
1058   {
1059     if (!CheckReadPreconditions())
1060     {
1061       ERROR_LOG(DVDINTERFACE, "Attempted to request audio status in an invalid state!");
1062       interrupt_type = DIInterruptType::DEINT;
1063       break;
1064     }
1065 
1066     if (!s_enable_dtk)
1067     {
1068       ERROR_LOG(DVDINTERFACE, "Attempted to request audio status while audio is disabled!");
1069       SetDriveError(DriveError::NoAudioBuf);
1070       interrupt_type = DIInterruptType::DEINT;
1071       break;
1072     }
1073 
1074     switch (s_DICMDBUF[0] >> 16 & 0xFF)
1075     {
1076     case 0x00:  // Returns streaming status
1077       INFO_LOG(DVDINTERFACE,
1078                "(Audio): Stream Status: Request Audio status "
1079                "AudioPos:%08" PRIx64 "/%08" PRIx64 " "
1080                "CurrentStart:%08" PRIx64 " CurrentLength:%08x",
1081                s_audio_position, s_current_start + s_current_length, s_current_start,
1082                s_current_length);
1083       s_DIIMMBUF = (s_stream ? 1 : 0);
1084       break;
1085     case 0x01:  // Returns the current offset
1086       INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status AudioPos:%08" PRIx64,
1087                s_audio_position);
1088       s_DIIMMBUF = static_cast<u32>((s_audio_position & 0xffffffffffff8000ull) >> 2);
1089       break;
1090     case 0x02:  // Returns the start offset
1091       INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentStart:%08" PRIx64,
1092                s_current_start);
1093       s_DIIMMBUF = static_cast<u32>(s_current_start >> 2);
1094       break;
1095     case 0x03:  // Returns the total length
1096       INFO_LOG(DVDINTERFACE, "(Audio): Stream Status: Request Audio status CurrentLength:%08x",
1097                s_current_length);
1098       s_DIIMMBUF = s_current_length;
1099       break;
1100     default:
1101       ERROR_LOG(DVDINTERFACE, "Invalid audio status command!  (%08x %08x %08x)", s_DICMDBUF[0],
1102                 s_DICMDBUF[1], s_DICMDBUF[2]);
1103       SetDriveError(DriveError::InvalidAudioCommand);
1104       interrupt_type = DIInterruptType::DEINT;
1105       break;
1106     }
1107   }
1108   break;
1109 
1110   // Used by both GC and Wii
1111   case DICommand::StopMotor:
1112   {
1113     const bool eject = (s_DICMDBUF[0] & (1 << 17));
1114     const bool kill = (s_DICMDBUF[0] & (1 << 20));
1115     INFO_LOG(DVDINTERFACE, "DVDLowStopMotor%s%s", eject ? " eject" : "", kill ? " kill!" : "");
1116 
1117     if (s_drive_state == DriveState::Ready || s_drive_state == DriveState::ReadyNoReadsMade ||
1118         s_drive_state == DriveState::DiscIdNotRead)
1119     {
1120       SetDriveState(DriveState::MotorStopped);
1121     }
1122 
1123     const bool force_eject = eject && !kill;
1124 
1125     if (Config::Get(Config::MAIN_AUTO_DISC_CHANGE) && !Movie::IsPlayingInput() &&
1126         DVDThread::IsInsertedDiscRunning() && !s_auto_disc_change_paths.empty())
1127     {
1128       CoreTiming::ScheduleEvent(force_eject ? 0 : SystemTimers::GetTicksPerSecond() / 2,
1129                                 s_auto_change_disc);
1130       OSD::AddMessage("Changing discs automatically...", OSD::Duration::NORMAL);
1131     }
1132     else if (force_eject)
1133     {
1134       EjectDisc(EjectCause::Software);
1135     }
1136     break;
1137   }
1138 
1139   // DVD Audio Enable/Disable (Immediate). GC uses this, and the Wii can use it to configure GC
1140   // games.
1141   case DICommand::AudioBufferConfig:
1142     // The IPL uses this command to enable or disable DTK audio depending on the value of byte 0x8
1143     // in the disc header. See http://www.crazynation.org/GC/GC_DD_TECH/GCTech.htm for more info.
1144     // The link is dead, but you can access the page using the Wayback Machine at archive.org.
1145 
1146     // This command can only be used immediately after reading the disc ID, before any other
1147     // reads. Too early, and you get NoDiscID.  Too late, and you get InvalidPeriod.
1148     if (!CheckReadPreconditions())
1149     {
1150       ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration in an invalid state!");
1151       interrupt_type = DIInterruptType::DEINT;
1152       break;
1153     }
1154 
1155     if (s_drive_state == DriveState::Ready)
1156     {
1157       ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration after a read has been made!");
1158       SetDriveError(DriveError::InvalidPeriod);
1159       interrupt_type = DIInterruptType::DEINT;
1160       break;
1161     }
1162 
1163     // Note that this can be called multiple times, as long as the drive is in the ReadyNoReadsMade
1164     // state. Calling it does not exit that state.
1165     AudioBufferConfig((s_DICMDBUF[0] >> 16) & 1, s_DICMDBUF[0] & 0xf);
1166     break;
1167 
1168   // GC-only patched drive firmware command, used by libogc
1169   case DICommand::UnknownEE:
1170     INFO_LOG(DVDINTERFACE, "SetStatus");
1171     SetDriveError(DriveError::InvalidCommand);
1172     interrupt_type = DIInterruptType::DEINT;
1173     break;
1174 
1175   // Debug commands; see yagcd. We don't really care
1176   // NOTE: commands to stream data will send...a raw data stream
1177   // This will appear as unknown commands, unless the check is re-instated to catch such data.
1178   // Can only be used through direct access and only after unlocked.
1179   case DICommand::Debug:
1180     ERROR_LOG(DVDINTERFACE, "Unsupported DVD Drive debug command 0x%08x", s_DICMDBUF[0]);
1181     SetDriveError(DriveError::InvalidCommand);
1182     interrupt_type = DIInterruptType::DEINT;
1183     break;
1184 
1185   // Unlock Commands. 1: "MATSHITA" 2: "DVD-GAME"
1186   // Just for fun
1187   // Can only be used through direct access.  The unlock command doesn't seem to work on the Wii.
1188   case DICommand::DebugUnlock:
1189   {
1190     if (s_DICMDBUF[0] == 0xFF014D41 && s_DICMDBUF[1] == 0x54534849 && s_DICMDBUF[2] == 0x54410200)
1191     {
1192       INFO_LOG(DVDINTERFACE, "Unlock test 1 passed");
1193     }
1194     else if (s_DICMDBUF[0] == 0xFF004456 && s_DICMDBUF[1] == 0x442D4741 &&
1195              s_DICMDBUF[2] == 0x4D450300)
1196     {
1197       INFO_LOG(DVDINTERFACE, "Unlock test 2 passed");
1198     }
1199     else
1200     {
1201       INFO_LOG(DVDINTERFACE, "Unlock test failed");
1202     }
1203   }
1204   break;
1205 
1206   default:
1207     ERROR_LOG(DVDINTERFACE, "Unknown command 0x%08x (Buffer 0x%08x, 0x%x)", s_DICMDBUF[0], s_DIMAR,
1208               s_DILENGTH);
1209     PanicAlertT("Unknown DVD command %08x - fatal error", s_DICMDBUF[0]);
1210     SetDriveError(DriveError::InvalidCommand);
1211     interrupt_type = DIInterruptType::DEINT;
1212     break;
1213   }
1214 
1215   if (!command_handled_by_thread)
1216   {
1217     // TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
1218     CoreTiming::ScheduleEvent(
1219         MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000),
1220         s_finish_executing_command, PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
1221   }
1222 }
1223 
PerformDecryptingRead(u32 position,u32 length,u32 output_address,const DiscIO::Partition & partition,ReplyType reply_type)1224 void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
1225                            const DiscIO::Partition& partition, ReplyType reply_type)
1226 {
1227   DIInterruptType interrupt_type = DIInterruptType::TCINT;
1228   SetDriveState(DriveState::Ready);
1229 
1230   const bool command_handled_by_thread =
1231       ExecuteReadCommand(static_cast<u64>(position) << 2, output_address, length, length, partition,
1232                          reply_type, &interrupt_type);
1233 
1234   if (!command_handled_by_thread)
1235   {
1236     // TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
1237     CoreTiming::ScheduleEvent(
1238         MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000),
1239         s_finish_executing_command, PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
1240   }
1241 }
1242 
AudioBufferConfig(bool enable_dtk,u8 dtk_buffer_length)1243 void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length)
1244 {
1245   s_enable_dtk = enable_dtk;
1246   s_dtk_buffer_length = dtk_buffer_length;
1247   if (s_enable_dtk)
1248     INFO_LOG(DVDINTERFACE, "DTK enabled: buffer size %d", s_dtk_buffer_length);
1249   else
1250     INFO_LOG(DVDINTERFACE, "DTK disabled");
1251 }
1252 
PackFinishExecutingCommandUserdata(ReplyType reply_type,DIInterruptType interrupt_type)1253 static u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type)
1254 {
1255   return (static_cast<u64>(reply_type) << 32) + static_cast<u32>(interrupt_type);
1256 }
1257 
FinishExecutingCommandCallback(u64 userdata,s64 cycles_late)1258 void FinishExecutingCommandCallback(u64 userdata, s64 cycles_late)
1259 {
1260   ReplyType reply_type = static_cast<ReplyType>(userdata >> 32);
1261   DIInterruptType interrupt_type = static_cast<DIInterruptType>(userdata & 0xFFFFFFFF);
1262   FinishExecutingCommand(reply_type, interrupt_type, cycles_late);
1263 }
1264 
SetDriveState(DriveState state)1265 void SetDriveState(DriveState state)
1266 {
1267   s_drive_state = state;
1268 }
1269 
SetDriveError(DriveError error)1270 void SetDriveError(DriveError error)
1271 {
1272   s_error_code = error;
1273 }
1274 
FinishExecutingCommand(ReplyType reply_type,DIInterruptType interrupt_type,s64 cycles_late,const std::vector<u8> & data)1275 void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
1276                             const std::vector<u8>& data)
1277 {
1278   // The data parameter contains the requested data iff this was called from DVDThread, and is
1279   // empty otherwise. DVDThread is the only source of ReplyType::NoReply and ReplyType::DTK.
1280 
1281   u32 transfer_size = 0;
1282   if (reply_type == ReplyType::NoReply)
1283     transfer_size = static_cast<u32>(data.size());
1284   else if (reply_type == ReplyType::Interrupt || reply_type == ReplyType::IOS)
1285     transfer_size = s_DILENGTH;
1286 
1287   if (interrupt_type == DIInterruptType::TCINT)
1288   {
1289     s_DIMAR += transfer_size;
1290     s_DILENGTH -= transfer_size;
1291   }
1292 
1293   switch (reply_type)
1294   {
1295   case ReplyType::NoReply:
1296   {
1297     break;
1298   }
1299 
1300   case ReplyType::Interrupt:
1301   {
1302     if (s_DICR.TSTART)
1303     {
1304       s_DICR.TSTART = 0;
1305       GenerateDIInterrupt(interrupt_type);
1306     }
1307     break;
1308   }
1309 
1310   case ReplyType::IOS:
1311   {
1312     IOS::HLE::Device::DI::InterruptFromDVDInterface(interrupt_type);
1313     break;
1314   }
1315 
1316   case ReplyType::DTK:
1317   {
1318     DTKStreamingCallback(interrupt_type, data, cycles_late);
1319     break;
1320   }
1321   }
1322 }
1323 
1324 // Determines from a given read request how much of the request is buffered,
1325 // and how much is required to be read from disc.
ScheduleReads(u64 offset,u32 length,const DiscIO::Partition & partition,u32 output_address,ReplyType reply_type)1326 static void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition,
1327                           u32 output_address, ReplyType reply_type)
1328 {
1329   // The drive continues to read 1 MiB beyond the last read position when idle.
1330   // If a future read falls within this window, part of the read may be returned
1331   // from the buffer. Data can be transferred from the buffer at up to 32 MiB/s.
1332 
1333   // Metroid Prime is a good example of a game that's sensitive to disc timing
1334   // details; if there isn't enough latency in the right places, doors can open
1335   // faster than on real hardware, and if there's too much latency in the wrong
1336   // places, the video before the save-file select screen lags.
1337 
1338   const u64 current_time = CoreTiming::GetTicks();
1339   const u32 ticks_per_second = SystemTimers::GetTicksPerSecond();
1340   const bool wii_disc = DVDThread::GetDiscType() == DiscIO::Platform::WiiDisc;
1341 
1342   // Where the DVD read head is (usually parked at the end of the buffer,
1343   // unless we've interrupted it mid-buffer-read).
1344   u64 head_position;
1345 
1346   // Compute the start (inclusive) and end (exclusive) of the buffer.
1347   // If we fall within its bounds, we get DMA-speed reads.
1348   u64 buffer_start, buffer_end;
1349 
1350   // The variable offset uses the same addressing as games do.
1351   // The variable dvd_offset tracks the actual offset on the DVD
1352   // that the disc drive starts reading at, which differs in two ways:
1353   // It's rounded to a whole ECC block and never uses Wii partition addressing.
1354   u64 dvd_offset = DVDThread::PartitionOffsetToRawOffset(offset, partition);
1355   dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE);
1356 
1357   if (SConfig::GetInstance().bFastDiscSpeed)
1358   {
1359     // The SUDTR setting makes us act as if all reads are buffered
1360     buffer_start = std::numeric_limits<u64>::min();
1361     buffer_end = std::numeric_limits<u64>::max();
1362     head_position = 0;
1363   }
1364   else
1365   {
1366     if (s_read_buffer_start_time == s_read_buffer_end_time)
1367     {
1368       // No buffer
1369       buffer_start = buffer_end = head_position = 0;
1370     }
1371     else
1372     {
1373       buffer_start = s_read_buffer_end_offset > STREAMING_BUFFER_SIZE ?
1374                          s_read_buffer_end_offset - STREAMING_BUFFER_SIZE :
1375                          0;
1376 
1377       DEBUG_LOG(DVDINTERFACE,
1378                 "Buffer: now=0x%" PRIx64 " start time=0x%" PRIx64 " end time=0x%" PRIx64,
1379                 current_time, s_read_buffer_start_time, s_read_buffer_end_time);
1380 
1381       if (current_time >= s_read_buffer_end_time)
1382       {
1383         // Buffer is fully read
1384         buffer_end = s_read_buffer_end_offset;
1385       }
1386       else
1387       {
1388         // The amount of data the buffer contains *right now*, rounded to a DVD ECC block.
1389         buffer_end = s_read_buffer_start_offset +
1390                      Common::AlignDown((current_time - s_read_buffer_start_time) *
1391                                            (s_read_buffer_end_offset - s_read_buffer_start_offset) /
1392                                            (s_read_buffer_end_time - s_read_buffer_start_time),
1393                                        DVD_ECC_BLOCK_SIZE);
1394       }
1395       head_position = buffer_end;
1396 
1397       // Reading before the buffer is not only unbuffered,
1398       // but also destroys the old buffer for future reads.
1399       if (dvd_offset < buffer_start)
1400       {
1401         // Kill the buffer, but maintain the head position for seeks.
1402         buffer_start = buffer_end = 0;
1403       }
1404     }
1405   }
1406 
1407   DEBUG_LOG(DVDINTERFACE, "Buffer: start=0x%" PRIx64 " end=0x%" PRIx64 " avail=0x%" PRIx64,
1408             buffer_start, buffer_end, buffer_end - buffer_start);
1409 
1410   DEBUG_LOG(DVDINTERFACE,
1411             "Schedule reads: offset=0x%" PRIx64 " length=0x%" PRIx32 " address=0x%" PRIx32, offset,
1412             length, output_address);
1413 
1414   s64 ticks_until_completion =
1415       READ_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000);
1416 
1417   u32 buffered_blocks = 0;
1418   u32 unbuffered_blocks = 0;
1419 
1420   const u32 bytes_per_chunk =
1421       partition != DiscIO::PARTITION_NONE && DVDThread::IsEncryptedAndHashed() ?
1422           DiscIO::VolumeWii::BLOCK_DATA_SIZE :
1423           DVD_ECC_BLOCK_SIZE;
1424 
1425   do
1426   {
1427     // The length of this read - "+1" so that if this read is already
1428     // aligned to a block we'll read the entire block.
1429     u32 chunk_length = static_cast<u32>(Common::AlignUp(offset + 1, bytes_per_chunk) - offset);
1430 
1431     // The last chunk may be short
1432     chunk_length = std::min(chunk_length, length);
1433 
1434     // TODO: If the emulated software requests 0 bytes of data, should we seek or not?
1435 
1436     if (dvd_offset >= buffer_start && dvd_offset < buffer_end)
1437     {
1438       // Number of ticks it takes to transfer the data from the buffer to memory.
1439       // TODO: This calculation is slightly wrong when decrypt is true - it uses the size of
1440       // the copy from IOS to PPC but is supposed to model the copy from the disc drive to IOS.
1441       ticks_until_completion +=
1442           static_cast<u64>(chunk_length) * ticks_per_second / BUFFER_TRANSFER_RATE;
1443       buffered_blocks++;
1444     }
1445     else
1446     {
1447       // In practice we'll only ever seek if this is the first time
1448       // through this loop.
1449       if (dvd_offset != head_position)
1450       {
1451         // Unbuffered seek+read
1452         ticks_until_completion += static_cast<u64>(
1453             ticks_per_second * DVDMath::CalculateSeekTime(head_position, dvd_offset));
1454 
1455         // TODO: The above emulates seeking and then reading one ECC block of data,
1456         // and then the below emulates the rotational latency. The rotational latency
1457         // should actually happen before reading data from the disc.
1458 
1459         const double time_after_seek =
1460             (CoreTiming::GetTicks() + ticks_until_completion) / ticks_per_second;
1461         ticks_until_completion += ticks_per_second * DVDMath::CalculateRotationalLatency(
1462                                                          dvd_offset, time_after_seek, wii_disc);
1463 
1464         DEBUG_LOG(DVDINTERFACE, "Seek+read 0x%" PRIx32 " bytes @ 0x%" PRIx64 " ticks=%" PRId64,
1465                   chunk_length, offset, ticks_until_completion);
1466       }
1467       else
1468       {
1469         // Unbuffered read
1470         ticks_until_completion +=
1471             static_cast<u64>(ticks_per_second * DVDMath::CalculateRawDiscReadTime(
1472                                                     dvd_offset, DVD_ECC_BLOCK_SIZE, wii_disc));
1473       }
1474 
1475       unbuffered_blocks++;
1476       head_position = dvd_offset + DVD_ECC_BLOCK_SIZE;
1477     }
1478 
1479     // Schedule this read to complete at the appropriate time
1480     const ReplyType chunk_reply_type = chunk_length == length ? reply_type : ReplyType::NoReply;
1481     DVDThread::StartReadToEmulatedRAM(output_address, offset, chunk_length, partition,
1482                                       chunk_reply_type, ticks_until_completion);
1483 
1484     // Advance the read window
1485     output_address += chunk_length;
1486     offset += chunk_length;
1487     length -= chunk_length;
1488     dvd_offset += DVD_ECC_BLOCK_SIZE;
1489   } while (length > 0);
1490 
1491   // Update the buffer based on this read. Based on experimental testing,
1492   // we will only reuse the old buffer while reading forward. Note that the
1493   // buffer start we calculate here is not the actual start of the buffer -
1494   // it is just the start of the portion we need to read.
1495   const u64 last_block = dvd_offset;
1496   if (last_block == buffer_start + DVD_ECC_BLOCK_SIZE && buffer_start != buffer_end)
1497   {
1498     // Special case: reading less than one block at the start of the
1499     // buffer won't change the buffer state
1500   }
1501   else
1502   {
1503     if (last_block >= buffer_end)
1504       // Full buffer read
1505       s_read_buffer_start_offset = last_block;
1506     else
1507       // Partial buffer read
1508       s_read_buffer_start_offset = buffer_end;
1509 
1510     s_read_buffer_end_offset = last_block + STREAMING_BUFFER_SIZE - DVD_ECC_BLOCK_SIZE;
1511     // Assume the buffer starts reading right after the end of the last operation
1512     s_read_buffer_start_time = current_time + ticks_until_completion;
1513     s_read_buffer_end_time =
1514         s_read_buffer_start_time +
1515         static_cast<u64>(ticks_per_second *
1516                          DVDMath::CalculateRawDiscReadTime(
1517                              s_read_buffer_start_offset,
1518                              s_read_buffer_end_offset - s_read_buffer_start_offset, wii_disc));
1519   }
1520 
1521   DEBUG_LOG(DVDINTERFACE,
1522             "Schedule reads: ECC blocks unbuffered=%d, buffered=%d, "
1523             "ticks=%" PRId64 ", time=%" PRId64 " us",
1524             unbuffered_blocks, buffered_blocks, ticks_until_completion,
1525             ticks_until_completion * 1000000 / SystemTimers::GetTicksPerSecond());
1526 }
1527 
1528 }  // namespace DVDInterface
1529