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