CDROM: Implement asynchronous disc reading
This commit is contained in:
parent
7ece901d57
commit
959a555274
|
@ -8,6 +8,8 @@ add_library(core
|
||||||
bus.inl
|
bus.inl
|
||||||
cdrom.cpp
|
cdrom.cpp
|
||||||
cdrom.h
|
cdrom.h
|
||||||
|
cdrom_async_reader.cpp
|
||||||
|
cdrom_async_reader.h
|
||||||
controller.cpp
|
controller.cpp
|
||||||
controller.h
|
controller.h
|
||||||
cpu_code_cache.cpp
|
cpu_code_cache.cpp
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "interrupt_controller.h"
|
#include "interrupt_controller.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "spu.h"
|
#include "spu.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
Log_SetChannel(CDROM);
|
Log_SetChannel(CDROM);
|
||||||
|
@ -26,13 +27,13 @@ void CDROM::Initialize(System* system, DMA* dma, InterruptController* interrupt_
|
||||||
m_system->CreateTimingEvent("CDROM Command Event", 1, 1, std::bind(&CDROM::ExecuteCommand, this), false);
|
m_system->CreateTimingEvent("CDROM Command Event", 1, 1, std::bind(&CDROM::ExecuteCommand, this), false);
|
||||||
m_drive_event = m_system->CreateTimingEvent("CDROM Drive Event", 1, 1,
|
m_drive_event = m_system->CreateTimingEvent("CDROM Drive Event", 1, 1,
|
||||||
std::bind(&CDROM::ExecuteDrive, this, std::placeholders::_2), false);
|
std::bind(&CDROM::ExecuteDrive, this, std::placeholders::_2), false);
|
||||||
|
|
||||||
|
if (m_system->GetSettings().cdrom_read_thread)
|
||||||
|
m_reader.StartThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::Reset()
|
void CDROM::Reset()
|
||||||
{
|
{
|
||||||
if (m_media)
|
|
||||||
m_media->Seek(0);
|
|
||||||
|
|
||||||
SoftReset();
|
SoftReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +50,9 @@ void CDROM::SoftReset()
|
||||||
m_interrupt_flag_register = 0;
|
m_interrupt_flag_register = 0;
|
||||||
m_pending_async_interrupt = 0;
|
m_pending_async_interrupt = 0;
|
||||||
m_setloc_position = {};
|
m_setloc_position = {};
|
||||||
m_seek_position = {};
|
m_last_requested_sector = 0;
|
||||||
|
if (m_reader.HasMedia())
|
||||||
|
m_reader.QueueReadSector(m_last_requested_sector);
|
||||||
m_setloc_pending = false;
|
m_setloc_pending = false;
|
||||||
m_read_after_seek = false;
|
m_read_after_seek = false;
|
||||||
m_play_after_seek = false;
|
m_play_after_seek = false;
|
||||||
|
@ -96,7 +99,7 @@ bool CDROM::DoState(StateWrapper& sw)
|
||||||
sw.Do(&m_interrupt_flag_register);
|
sw.Do(&m_interrupt_flag_register);
|
||||||
sw.Do(&m_pending_async_interrupt);
|
sw.Do(&m_pending_async_interrupt);
|
||||||
sw.DoPOD(&m_setloc_position);
|
sw.DoPOD(&m_setloc_position);
|
||||||
sw.DoPOD(&m_seek_position);
|
sw.DoPOD(&m_last_requested_sector);
|
||||||
sw.Do(&m_setloc_pending);
|
sw.Do(&m_setloc_pending);
|
||||||
sw.Do(&m_read_after_seek);
|
sw.Do(&m_read_after_seek);
|
||||||
sw.Do(&m_play_after_seek);
|
sw.Do(&m_play_after_seek);
|
||||||
|
@ -121,31 +124,25 @@ bool CDROM::DoState(StateWrapper& sw)
|
||||||
sw.Do(&m_data_fifo);
|
sw.Do(&m_data_fifo);
|
||||||
sw.Do(&m_sector_buffer);
|
sw.Do(&m_sector_buffer);
|
||||||
|
|
||||||
u32 media_lba = m_media ? m_media->GetPositionOnDisc() : 0;
|
|
||||||
sw.Do(&media_lba);
|
|
||||||
|
|
||||||
if (sw.IsReading())
|
if (sw.IsReading())
|
||||||
{
|
{
|
||||||
|
if (m_reader.HasMedia())
|
||||||
|
m_reader.QueueReadSector(m_last_requested_sector);
|
||||||
UpdateCommandEvent();
|
UpdateCommandEvent();
|
||||||
m_drive_event->SetState(!IsDriveIdle());
|
m_drive_event->SetState(!IsDriveIdle());
|
||||||
|
|
||||||
// load up media if we had something in there before
|
|
||||||
if (m_media && !m_media->Seek(media_lba))
|
|
||||||
{
|
|
||||||
Log_ErrorPrint("Failed to seek CD media from save state. Ejecting.");
|
|
||||||
RemoveMedia();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !sw.HasError();
|
return !sw.HasError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CDROM::HasMedia() const
|
||||||
|
{
|
||||||
|
return m_reader.HasMedia();
|
||||||
|
}
|
||||||
|
|
||||||
std::string CDROM::GetMediaFileName() const
|
std::string CDROM::GetMediaFileName() const
|
||||||
{
|
{
|
||||||
if (!m_media)
|
return m_reader.GetMediaFileName();
|
||||||
return std::string();
|
|
||||||
|
|
||||||
return m_media->GetFileName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
||||||
|
@ -153,16 +150,16 @@ void CDROM::InsertMedia(std::unique_ptr<CDImage> media)
|
||||||
if (HasMedia())
|
if (HasMedia())
|
||||||
RemoveMedia();
|
RemoveMedia();
|
||||||
|
|
||||||
m_media = std::move(media);
|
m_reader.SetMedia(std::move(media));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::RemoveMedia()
|
void CDROM::RemoveMedia()
|
||||||
{
|
{
|
||||||
if (!m_media)
|
if (!m_reader.HasMedia())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log_InfoPrintf("Removing CD...");
|
Log_InfoPrintf("Removing CD...");
|
||||||
m_media.reset();
|
m_reader.RemoveMedia();
|
||||||
|
|
||||||
m_secondary_status.shell_open = true;
|
m_secondary_status.shell_open = true;
|
||||||
|
|
||||||
|
@ -176,6 +173,17 @@ void CDROM::RemoveMedia()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CDROM::SetUseReadThread(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled == m_reader.IsUsingThread())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
m_reader.StartThread();
|
||||||
|
else
|
||||||
|
m_reader.StopThread();
|
||||||
|
}
|
||||||
|
|
||||||
u8 CDROM::ReadRegister(u32 offset)
|
u8 CDROM::ReadRegister(u32 offset)
|
||||||
{
|
{
|
||||||
switch (offset)
|
switch (offset)
|
||||||
|
@ -502,7 +510,7 @@ TickCount CDROM::GetTicksForRead() const
|
||||||
|
|
||||||
TickCount CDROM::GetTicksForSeek() const
|
TickCount CDROM::GetTicksForSeek() const
|
||||||
{
|
{
|
||||||
const CDImage::LBA current_lba = m_secondary_status.motor_on ? m_media->GetPositionOnDisc() : 0;
|
const CDImage::LBA current_lba = m_secondary_status.motor_on ? m_reader.GetLastReadSector() : 0;
|
||||||
const CDImage::LBA new_lba = m_setloc_position.ToLBA();
|
const CDImage::LBA new_lba = m_setloc_position.ToLBA();
|
||||||
const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba));
|
const u32 lba_diff = static_cast<u32>((new_lba > current_lba) ? (new_lba - current_lba) : (current_lba - new_lba));
|
||||||
|
|
||||||
|
@ -558,7 +566,7 @@ void CDROM::ExecuteCommand()
|
||||||
SendACKAndStat();
|
SendACKAndStat();
|
||||||
|
|
||||||
// shell open bit is cleared after sending the status
|
// shell open bit is cleared after sending the status
|
||||||
if (m_media)
|
if (HasMedia())
|
||||||
m_secondary_status.shell_open = false;
|
m_secondary_status.shell_open = false;
|
||||||
|
|
||||||
EndCommand();
|
EndCommand();
|
||||||
|
@ -652,7 +660,7 @@ void CDROM::ExecuteCommand()
|
||||||
{
|
{
|
||||||
const bool logical = (m_command == Command::SeekL);
|
const bool logical = (m_command == Command::SeekL);
|
||||||
Log_DebugPrintf("CDROM %s command", logical ? "SeekL" : "SeekP");
|
Log_DebugPrintf("CDROM %s command", logical ? "SeekL" : "SeekP");
|
||||||
if (!m_media)
|
if (!HasMedia())
|
||||||
{
|
{
|
||||||
SendErrorResponse(0x80);
|
SendErrorResponse(0x80);
|
||||||
}
|
}
|
||||||
|
@ -670,7 +678,7 @@ void CDROM::ExecuteCommand()
|
||||||
case Command::ReadS:
|
case Command::ReadS:
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("CDROM read command");
|
Log_DebugPrintf("CDROM read command");
|
||||||
if (!m_media)
|
if (!HasMedia())
|
||||||
{
|
{
|
||||||
SendErrorResponse(0x80);
|
SendErrorResponse(0x80);
|
||||||
}
|
}
|
||||||
|
@ -689,7 +697,7 @@ void CDROM::ExecuteCommand()
|
||||||
u8 track = m_param_fifo.IsEmpty() ? 0 : m_param_fifo.Peek(0);
|
u8 track = m_param_fifo.IsEmpty() ? 0 : m_param_fifo.Peek(0);
|
||||||
Log_DebugPrintf("CDROM play command, track=%u", track);
|
Log_DebugPrintf("CDROM play command, track=%u", track);
|
||||||
|
|
||||||
if (!m_media)
|
if (!HasMedia())
|
||||||
{
|
{
|
||||||
SendErrorResponse(0x80);
|
SendErrorResponse(0x80);
|
||||||
}
|
}
|
||||||
|
@ -825,11 +833,13 @@ void CDROM::ExecuteCommand()
|
||||||
case Command::GetTN:
|
case Command::GetTN:
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("CDROM GetTN command");
|
Log_DebugPrintf("CDROM GetTN command");
|
||||||
if (m_media)
|
if (HasMedia())
|
||||||
{
|
{
|
||||||
|
m_reader.WaitForReadToComplete();
|
||||||
|
|
||||||
m_response_fifo.Push(m_secondary_status.bits);
|
m_response_fifo.Push(m_secondary_status.bits);
|
||||||
m_response_fifo.Push(BinaryToBCD(Truncate8(m_media->GetTrackNumber())));
|
m_response_fifo.Push(BinaryToBCD(Truncate8(m_reader.GetMedia()->GetTrackNumber())));
|
||||||
m_response_fifo.Push(BinaryToBCD(Truncate8(m_media->GetTrackCount())));
|
m_response_fifo.Push(BinaryToBCD(Truncate8(m_reader.GetMedia()->GetTrackCount())));
|
||||||
SetInterrupt(Interrupt::ACK);
|
SetInterrupt(Interrupt::ACK);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -847,11 +857,11 @@ void CDROM::ExecuteCommand()
|
||||||
Assert(m_param_fifo.GetSize() >= 1);
|
Assert(m_param_fifo.GetSize() >= 1);
|
||||||
const u8 track = PackedBCDToBinary(m_param_fifo.Peek());
|
const u8 track = PackedBCDToBinary(m_param_fifo.Peek());
|
||||||
|
|
||||||
if (!m_media)
|
if (!HasMedia())
|
||||||
{
|
{
|
||||||
SendErrorResponse(0x80);
|
SendErrorResponse(0x80);
|
||||||
}
|
}
|
||||||
else if (track > m_media->GetTrackCount())
|
else if (track > m_reader.GetMedia()->GetTrackCount())
|
||||||
{
|
{
|
||||||
SendErrorResponse(0x10);
|
SendErrorResponse(0x10);
|
||||||
}
|
}
|
||||||
|
@ -859,9 +869,9 @@ void CDROM::ExecuteCommand()
|
||||||
{
|
{
|
||||||
CDImage::Position pos;
|
CDImage::Position pos;
|
||||||
if (track == 0)
|
if (track == 0)
|
||||||
pos = CDImage::Position::FromLBA(m_media->GetLBACount());
|
pos = CDImage::Position::FromLBA(m_reader.GetMedia()->GetLBACount());
|
||||||
else
|
else
|
||||||
pos = m_media->GetTrackStartMSFPosition(track);
|
pos = m_reader.GetMedia()->GetTrackStartMSFPosition(track);
|
||||||
|
|
||||||
m_response_fifo.Push(m_secondary_status.bits);
|
m_response_fifo.Push(m_secondary_status.bits);
|
||||||
m_response_fifo.Push(BinaryToBCD(Truncate8(pos.minute)));
|
m_response_fifo.Push(BinaryToBCD(Truncate8(pos.minute)));
|
||||||
|
@ -1005,7 +1015,7 @@ void CDROM::ExecuteDrive(TickCount ticks_late)
|
||||||
|
|
||||||
void CDROM::BeginReading(TickCount ticks_late)
|
void CDROM::BeginReading(TickCount ticks_late)
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("Starting reading");
|
Log_DebugPrintf("Starting reading @ LBA %u", m_last_requested_sector);
|
||||||
if (m_setloc_pending)
|
if (m_setloc_pending)
|
||||||
{
|
{
|
||||||
BeginSeeking(true, true, false);
|
BeginSeeking(true, true, false);
|
||||||
|
@ -1022,6 +1032,8 @@ void CDROM::BeginReading(TickCount ticks_late)
|
||||||
m_drive_state = DriveState::Reading;
|
m_drive_state = DriveState::Reading;
|
||||||
m_drive_event->SetInterval(ticks);
|
m_drive_event->SetInterval(ticks);
|
||||||
m_drive_event->Schedule(ticks - ticks_late);
|
m_drive_event->Schedule(ticks - ticks_late);
|
||||||
|
|
||||||
|
m_reader.QueueReadSector(m_last_requested_sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::BeginPlaying(u8 track_bcd, TickCount ticks_late)
|
void CDROM::BeginPlaying(u8 track_bcd, TickCount ticks_late)
|
||||||
|
@ -1034,13 +1046,13 @@ void CDROM::BeginPlaying(u8 track_bcd, TickCount ticks_late)
|
||||||
if (track_bcd != 0)
|
if (track_bcd != 0)
|
||||||
{
|
{
|
||||||
// play specific track?
|
// play specific track?
|
||||||
if (track_bcd > m_media->GetTrackCount())
|
if (track_bcd > m_reader.GetMedia()->GetTrackCount())
|
||||||
{
|
{
|
||||||
// restart current track
|
// restart current track
|
||||||
track_bcd = BinaryToBCD(Truncate8(m_media->GetTrackNumber()));
|
track_bcd = BinaryToBCD(Truncate8(m_reader.GetMedia()->GetTrackNumber()));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_setloc_position = m_media->GetTrackStartMSFPosition(PackedBCDToBinary(track_bcd));
|
m_setloc_position = m_reader.GetMedia()->GetTrackStartMSFPosition(PackedBCDToBinary(track_bcd));
|
||||||
m_setloc_pending = true;
|
m_setloc_pending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1060,6 +1072,8 @@ void CDROM::BeginPlaying(u8 track_bcd, TickCount ticks_late)
|
||||||
m_drive_state = DriveState::Playing;
|
m_drive_state = DriveState::Playing;
|
||||||
m_drive_event->SetInterval(ticks);
|
m_drive_event->SetInterval(ticks);
|
||||||
m_drive_event->Schedule(ticks - ticks_late);
|
m_drive_event->Schedule(ticks - ticks_late);
|
||||||
|
|
||||||
|
m_reader.QueueReadSector(m_last_requested_sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek)
|
void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek)
|
||||||
|
@ -1067,13 +1081,12 @@ void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_see
|
||||||
if (!m_setloc_pending)
|
if (!m_setloc_pending)
|
||||||
Log_WarningPrintf("Seeking without setloc set");
|
Log_WarningPrintf("Seeking without setloc set");
|
||||||
|
|
||||||
m_seek_position = m_setloc_position;
|
|
||||||
m_read_after_seek = read_after_seek;
|
m_read_after_seek = read_after_seek;
|
||||||
m_play_after_seek = play_after_seek;
|
m_play_after_seek = play_after_seek;
|
||||||
m_setloc_pending = false;
|
m_setloc_pending = false;
|
||||||
|
|
||||||
Log_DebugPrintf("Seeking to [%02u:%02u:%02u] (%s)", m_seek_position.minute, m_seek_position.second,
|
Log_DebugPrintf("Seeking to [%02u:%02u:%02u] (LBA %u) (%s)", m_setloc_position.minute, m_setloc_position.second,
|
||||||
m_seek_position.frame, logical ? "logical" : "physical");
|
m_setloc_position.frame, m_setloc_position.ToLBA(), logical ? "logical" : "physical");
|
||||||
|
|
||||||
const TickCount seek_time = GetTicksForSeek();
|
const TickCount seek_time = GetTicksForSeek();
|
||||||
|
|
||||||
|
@ -1084,11 +1097,8 @@ void CDROM::BeginSeeking(bool logical, bool read_after_seek, bool play_after_see
|
||||||
m_drive_state = logical ? DriveState::SeekingLogical : DriveState::SeekingPhysical;
|
m_drive_state = logical ? DriveState::SeekingLogical : DriveState::SeekingPhysical;
|
||||||
m_drive_event->SetIntervalAndSchedule(seek_time);
|
m_drive_event->SetIntervalAndSchedule(seek_time);
|
||||||
|
|
||||||
// Read sub-q early.. this is because we're not reading sectors while seeking.
|
m_last_requested_sector = m_setloc_position.ToLBA();
|
||||||
// Fixes music looping in Spyro.
|
m_reader.QueueReadSector(m_last_requested_sector);
|
||||||
CDImage::SubChannelQ subq;
|
|
||||||
if (m_media->Seek(m_seek_position) && m_media->ReadSubChannelQ(&subq) && subq.IsCRCValid())
|
|
||||||
m_last_subq = subq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::DoSpinUpComplete()
|
void CDROM::DoSpinUpComplete()
|
||||||
|
@ -1111,21 +1121,22 @@ void CDROM::DoSeekComplete(TickCount ticks_late)
|
||||||
m_secondary_status.ClearActiveBits();
|
m_secondary_status.ClearActiveBits();
|
||||||
m_sector_buffer.clear();
|
m_sector_buffer.clear();
|
||||||
|
|
||||||
|
bool seek_okay = m_reader.WaitForReadToComplete();
|
||||||
|
if (seek_okay)
|
||||||
|
{
|
||||||
|
m_last_subq = m_reader.GetSectorSubQ();
|
||||||
|
|
||||||
// seek and update sub-q for ReadP command
|
// seek and update sub-q for ReadP command
|
||||||
const auto [seek_mm, seek_ss, seek_ff] = m_seek_position.ToBCD();
|
DebugAssert(m_last_requested_sector == m_reader.GetLastReadSector());
|
||||||
bool seek_okay = (m_last_subq.absolute_minute_bcd == seek_mm && m_last_subq.absolute_second_bcd == seek_ss &&
|
const auto [seek_mm, seek_ss, seek_ff] = CDImage::Position::FromLBA(m_last_requested_sector).ToBCD();
|
||||||
m_last_subq.absolute_frame_bcd == seek_ff);
|
seek_okay = (m_last_subq.IsCRCValid() && m_last_subq.absolute_minute_bcd == seek_mm &&
|
||||||
|
m_last_subq.absolute_second_bcd == seek_ss && m_last_subq.absolute_frame_bcd == seek_ff);
|
||||||
if (seek_okay)
|
if (seek_okay)
|
||||||
{
|
{
|
||||||
// check for data header for logical seeks
|
// check for data header for logical seeks
|
||||||
if (logical)
|
if (logical)
|
||||||
{
|
{
|
||||||
u8 raw_sector[CDImage::RAW_SECTOR_SIZE];
|
ProcessDataSectorHeader(m_reader.GetSectorBuffer().data(), false);
|
||||||
seek_okay &= m_media->ReadRawSector(raw_sector);
|
|
||||||
seek_okay &= m_media->Seek(m_media->GetPositionOnDisc() - 1);
|
|
||||||
if (seek_okay)
|
|
||||||
{
|
|
||||||
ProcessDataSectorHeader(raw_sector, false);
|
|
||||||
|
|
||||||
// ensure the location matches up (it should)
|
// ensure the location matches up (it should)
|
||||||
seek_okay = (m_last_sector_header.minute == seek_mm && m_last_sector_header.second == seek_ss &&
|
seek_okay = (m_last_sector_header.minute == seek_mm && m_last_sector_header.second == seek_ss &&
|
||||||
|
@ -1154,8 +1165,9 @@ void CDROM::DoSeekComplete(TickCount ticks_late)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("%s seek to [%02u:%02u:%02u] failed", logical ? "Logical" : "Physical", m_seek_position.minute,
|
CDImage::Position pos(CDImage::Position::FromLBA(m_last_requested_sector));
|
||||||
m_seek_position.second, m_seek_position.frame);
|
Log_WarningPrintf("%s seek to [%02u:%02u:%02u] failed", logical ? "Logical" : "Physical", pos.minute, pos.second,
|
||||||
|
pos.frame);
|
||||||
m_secondary_status.seek_error = true;
|
m_secondary_status.seek_error = true;
|
||||||
SendAsyncErrorResponse(0x80);
|
SendAsyncErrorResponse(0x80);
|
||||||
}
|
}
|
||||||
|
@ -1188,7 +1200,7 @@ void CDROM::DoStopComplete()
|
||||||
m_secondary_status.motor_on = false;
|
m_secondary_status.motor_on = false;
|
||||||
m_sector_buffer.clear();
|
m_sector_buffer.clear();
|
||||||
|
|
||||||
m_media->Seek(0);
|
m_reader.QueueReadSector(0);
|
||||||
|
|
||||||
m_async_response_fifo.Clear();
|
m_async_response_fifo.Clear();
|
||||||
m_async_response_fifo.Push(m_secondary_status.bits);
|
m_async_response_fifo.Push(m_secondary_status.bits);
|
||||||
|
@ -1225,12 +1237,12 @@ void CDROM::DoTOCRead()
|
||||||
|
|
||||||
void CDROM::DoSectorRead()
|
void CDROM::DoSectorRead()
|
||||||
{
|
{
|
||||||
|
if (!m_reader.WaitForReadToComplete())
|
||||||
|
Panic("Sector read failed");
|
||||||
|
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
// TODO: Check SubQ checksum.
|
// TODO: Check SubQ checksum.
|
||||||
CDImage::SubChannelQ subq;
|
const CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ();
|
||||||
if (!m_media->ReadSubChannelQ(&subq))
|
|
||||||
Panic("SubChannel Q read failed");
|
|
||||||
|
|
||||||
const bool is_data_sector = subq.control.data;
|
const bool is_data_sector = subq.control.data;
|
||||||
m_secondary_status.playing_cdda = !is_data_sector;
|
m_secondary_status.playing_cdda = !is_data_sector;
|
||||||
if (!is_data_sector)
|
if (!is_data_sector)
|
||||||
|
@ -1245,7 +1257,7 @@ void CDROM::DoSectorRead()
|
||||||
{
|
{
|
||||||
// we don't want to update the position if the track changes, so we check it before reading the actual sector.
|
// we don't want to update the position if the track changes, so we check it before reading the actual sector.
|
||||||
Log_DevPrintf("Auto pause at the end of track %u (LBA %u)", m_play_track_number_bcd,
|
Log_DevPrintf("Auto pause at the end of track %u (LBA %u)", m_play_track_number_bcd,
|
||||||
m_media->GetPositionOnDisc());
|
m_reader.GetLastReadSector());
|
||||||
|
|
||||||
ClearAsyncInterrupt();
|
ClearAsyncInterrupt();
|
||||||
m_async_response_fifo.Push(m_secondary_status.bits);
|
m_async_response_fifo.Push(m_secondary_status.bits);
|
||||||
|
@ -1258,21 +1270,17 @@ void CDROM::DoSectorRead()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 raw_sector[CDImage::RAW_SECTOR_SIZE];
|
|
||||||
if (!m_media->ReadRawSector(raw_sector))
|
|
||||||
Panic("Sector read failed");
|
|
||||||
|
|
||||||
if (subq.IsCRCValid())
|
if (subq.IsCRCValid())
|
||||||
{
|
{
|
||||||
m_last_subq = subq;
|
m_last_subq = subq;
|
||||||
|
|
||||||
if (is_data_sector && m_drive_state == DriveState::Reading)
|
if (is_data_sector && m_drive_state == DriveState::Reading)
|
||||||
{
|
{
|
||||||
ProcessDataSector(raw_sector, subq);
|
ProcessDataSector(m_reader.GetSectorBuffer().data(), subq);
|
||||||
}
|
}
|
||||||
else if (!is_data_sector && m_drive_state == DriveState::Playing)
|
else if (!is_data_sector && m_drive_state == DriveState::Playing)
|
||||||
{
|
{
|
||||||
ProcessCDDASector(raw_sector, subq);
|
ProcessCDDASector(m_reader.GetSectorBuffer().data(), subq);
|
||||||
}
|
}
|
||||||
else if (m_drive_state != DriveState::Reading && m_drive_state != DriveState::Playing)
|
else if (m_drive_state != DriveState::Reading && m_drive_state != DriveState::Playing)
|
||||||
{
|
{
|
||||||
|
@ -1280,16 +1288,19 @@ void CDROM::DoSectorRead()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log_WarningPrintf("Skipping sector %u as it is a %s sector and we're not %s", m_media->GetPositionOnDisc() - 1,
|
Log_WarningPrintf("Skipping sector %u as it is a %s sector and we're not %s", m_reader.GetLastReadSector(),
|
||||||
is_data_sector ? "data" : "audio", is_data_sector ? "reading" : "playing");
|
is_data_sector ? "data" : "audio", is_data_sector ? "reading" : "playing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const CDImage::Position pos(CDImage::Position::FromLBA(m_media->GetPositionOnDisc() - 1));
|
const CDImage::Position pos(CDImage::Position::FromLBA(m_reader.GetLastReadSector()));
|
||||||
Log_DevPrintf("Skipping sector %u [%02u:%02u:%02u] due to invalid subchannel Q", m_media->GetPositionOnDisc() - 1,
|
Log_DevPrintf("Skipping sector %u [%02u:%02u:%02u] due to invalid subchannel Q", m_reader.GetLastReadSector(),
|
||||||
pos.minute, pos.second, pos.frame);
|
pos.minute, pos.second, pos.frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_last_requested_sector++;
|
||||||
|
m_reader.QueueReadSector(m_last_requested_sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDROM::ProcessDataSectorHeader(const u8* raw_sector, bool set_valid)
|
void CDROM::ProcessDataSectorHeader(const u8* raw_sector, bool set_valid)
|
||||||
|
@ -1304,7 +1315,7 @@ void CDROM::ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ&
|
||||||
{
|
{
|
||||||
ProcessDataSectorHeader(raw_sector, true);
|
ProcessDataSectorHeader(raw_sector, true);
|
||||||
|
|
||||||
Log_DevPrintf("Read sector %u: mode %u submode 0x%02X", m_media->GetPositionOnDisc() - 1,
|
Log_DevPrintf("Read sector %u: mode %u submode 0x%02X", m_last_requested_sector,
|
||||||
ZeroExtend32(m_last_sector_header.sector_mode), ZeroExtend32(m_last_sector_subheader.submode.bits));
|
ZeroExtend32(m_last_sector_header.sector_mode), ZeroExtend32(m_last_sector_subheader.submode.bits));
|
||||||
|
|
||||||
if (m_mode.xa_enable && m_last_sector_header.sector_mode == 2)
|
if (m_mode.xa_enable && m_last_sector_header.sector_mode == 2)
|
||||||
|
@ -1503,7 +1514,7 @@ void CDROM::ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannel
|
||||||
void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
|
void CDROM::ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq)
|
||||||
{
|
{
|
||||||
// For CDDA sectors, the whole sector contains the audio data.
|
// For CDDA sectors, the whole sector contains the audio data.
|
||||||
Log_DevPrintf("Read sector %u as CDDA", m_media->GetPositionOnDisc());
|
Log_DevPrintf("Read sector %u as CDDA", m_last_requested_sector);
|
||||||
|
|
||||||
if (m_mode.report_audio)
|
if (m_mode.report_audio)
|
||||||
{
|
{
|
||||||
|
@ -1594,16 +1605,17 @@ void CDROM::DrawDebugWindow()
|
||||||
// draw voice states
|
// draw voice states
|
||||||
if (ImGui::CollapsingHeader("Media", ImGuiTreeNodeFlags_DefaultOpen))
|
if (ImGui::CollapsingHeader("Media", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
{
|
{
|
||||||
if (m_media)
|
if (HasMedia())
|
||||||
{
|
{
|
||||||
const auto [disc_minute, disc_second, disc_frame] = m_media->GetMSFPositionOnDisc();
|
const CDImage* media = m_reader.GetMedia();
|
||||||
const auto [track_minute, track_second, track_frame] = m_media->GetMSFPositionInTrack();
|
const auto [disc_minute, disc_second, disc_frame] = media->GetMSFPositionOnDisc();
|
||||||
|
const auto [track_minute, track_second, track_frame] = media->GetMSFPositionInTrack();
|
||||||
|
|
||||||
ImGui::Text("Filename: %s", m_media->GetFileName().c_str());
|
ImGui::Text("Filename: %s", media->GetFileName().c_str());
|
||||||
ImGui::Text("Disc Position: MSF[%02u:%02u:%02u] LBA[%u]", disc_minute, disc_second, disc_frame,
|
ImGui::Text("Disc Position: MSF[%02u:%02u:%02u] LBA[%u]", disc_minute, disc_second, disc_frame,
|
||||||
m_media->GetPositionOnDisc());
|
media->GetPositionOnDisc());
|
||||||
ImGui::Text("Track Position: Number[%u] MSF[%02u:%02u:%02u] LBA[%u]", m_media->GetTrackNumber(), track_minute,
|
ImGui::Text("Track Position: Number[%u] MSF[%02u:%02u:%02u] LBA[%u]", media->GetTrackNumber(), track_minute,
|
||||||
track_second, track_frame, m_media->GetPositionInTrack());
|
track_second, track_frame, media->GetPositionInTrack());
|
||||||
ImGui::Text("Last Sector: %02X:%02X:%02X (Mode %u)", m_last_sector_header.minute, m_last_sector_header.second,
|
ImGui::Text("Last Sector: %02X:%02X:%02X (Mode %u)", m_last_sector_header.minute, m_last_sector_header.second,
|
||||||
m_last_sector_header.frame, m_last_sector_header.sector_mode);
|
m_last_sector_header.frame, m_last_sector_header.sector_mode);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "common/cd_xa.h"
|
#include "common/cd_xa.h"
|
||||||
#include "common/fifo_queue.h"
|
#include "common/fifo_queue.h"
|
||||||
#include "common/heap_array.h"
|
#include "common/heap_array.h"
|
||||||
|
#include "cdrom_async_reader.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -27,7 +28,7 @@ public:
|
||||||
void Reset();
|
void Reset();
|
||||||
bool DoState(StateWrapper& sw);
|
bool DoState(StateWrapper& sw);
|
||||||
|
|
||||||
bool HasMedia() const { return static_cast<bool>(m_media); }
|
bool HasMedia() const;
|
||||||
std::string GetMediaFileName() const;
|
std::string GetMediaFileName() const;
|
||||||
void InsertMedia(std::unique_ptr<CDImage> media);
|
void InsertMedia(std::unique_ptr<CDImage> media);
|
||||||
void RemoveMedia();
|
void RemoveMedia();
|
||||||
|
@ -40,6 +41,8 @@ public:
|
||||||
// Render statistics debug window.
|
// Render statistics debug window.
|
||||||
void DrawDebugWindow();
|
void DrawDebugWindow();
|
||||||
|
|
||||||
|
void SetUseReadThread(bool enabled);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
|
@ -219,7 +222,6 @@ private:
|
||||||
DMA* m_dma = nullptr;
|
DMA* m_dma = nullptr;
|
||||||
InterruptController* m_interrupt_controller = nullptr;
|
InterruptController* m_interrupt_controller = nullptr;
|
||||||
SPU* m_spu = nullptr;
|
SPU* m_spu = nullptr;
|
||||||
std::unique_ptr<CDImage> m_media;
|
|
||||||
std::unique_ptr<TimingEvent> m_command_event;
|
std::unique_ptr<TimingEvent> m_command_event;
|
||||||
std::unique_ptr<TimingEvent> m_drive_event;
|
std::unique_ptr<TimingEvent> m_drive_event;
|
||||||
|
|
||||||
|
@ -235,7 +237,7 @@ private:
|
||||||
u8 m_pending_async_interrupt = 0;
|
u8 m_pending_async_interrupt = 0;
|
||||||
|
|
||||||
CDImage::Position m_setloc_position = {};
|
CDImage::Position m_setloc_position = {};
|
||||||
CDImage::Position m_seek_position = {};
|
CDImage::LBA m_last_requested_sector{};
|
||||||
bool m_setloc_pending = false;
|
bool m_setloc_pending = false;
|
||||||
bool m_read_after_seek = false;
|
bool m_read_after_seek = false;
|
||||||
bool m_play_after_seek = false;
|
bool m_play_after_seek = false;
|
||||||
|
@ -265,4 +267,6 @@ private:
|
||||||
InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> m_async_response_fifo;
|
InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> m_async_response_fifo;
|
||||||
HeapFIFOQueue<u8, DATA_FIFO_SIZE> m_data_fifo;
|
HeapFIFOQueue<u8, DATA_FIFO_SIZE> m_data_fifo;
|
||||||
std::vector<u8> m_sector_buffer;
|
std::vector<u8> m_sector_buffer;
|
||||||
|
|
||||||
|
CDROMAsyncReader m_reader;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
#include "cdrom_async_reader.h"
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/timer.h"
|
||||||
|
Log_SetChannel(CDROMAsyncReader);
|
||||||
|
|
||||||
|
CDROMAsyncReader::CDROMAsyncReader() = default;
|
||||||
|
|
||||||
|
CDROMAsyncReader::~CDROMAsyncReader()
|
||||||
|
{
|
||||||
|
StopThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::StartThread()
|
||||||
|
{
|
||||||
|
if (IsUsingThread())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_shutdown_flag.store(false);
|
||||||
|
m_read_thread = std::thread(&CDROMAsyncReader::WorkerThreadEntryPoint, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::StopThread()
|
||||||
|
{
|
||||||
|
if (!IsUsingThread())
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
if (m_sector_read_pending.load())
|
||||||
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
||||||
|
|
||||||
|
m_shutdown_flag.store(true);
|
||||||
|
m_do_read_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_read_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::SetMedia(std::unique_ptr<CDImage> media)
|
||||||
|
{
|
||||||
|
WaitForReadToComplete();
|
||||||
|
m_media = std::move(media);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::RemoveMedia()
|
||||||
|
{
|
||||||
|
WaitForReadToComplete();
|
||||||
|
m_media.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::QueueReadSector(CDImage::LBA lba)
|
||||||
|
{
|
||||||
|
if (!IsUsingThread())
|
||||||
|
{
|
||||||
|
m_sector_read_pending.store(true);
|
||||||
|
m_next_position_set.store(true);
|
||||||
|
m_next_position = lba;
|
||||||
|
DoSectorRead();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
if (m_sector_read_pending.load())
|
||||||
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
||||||
|
|
||||||
|
// don't re-read the same sector if it was the last one we read
|
||||||
|
// the CDC code does this when seeking->reading
|
||||||
|
if (m_last_read_sector == lba && m_sector_read_result.load())
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Skipping re-reading same sector %u", lba);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sector_read_pending.store(true);
|
||||||
|
m_next_position_set.store(true);
|
||||||
|
m_next_position = lba;
|
||||||
|
m_do_read_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::QueueReadNextSector()
|
||||||
|
{
|
||||||
|
if (!IsUsingThread())
|
||||||
|
{
|
||||||
|
m_sector_read_pending.store(true);
|
||||||
|
DoSectorRead();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
if (m_sector_read_pending.load())
|
||||||
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
||||||
|
|
||||||
|
m_sector_read_pending.store(true);
|
||||||
|
m_do_read_cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDROMAsyncReader::WaitForReadToComplete()
|
||||||
|
{
|
||||||
|
if (!IsUsingThread())
|
||||||
|
return m_sector_read_result.load();
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
if (m_sector_read_pending.load())
|
||||||
|
{
|
||||||
|
Log_DebugPrintf("Sector read pending, waiting");
|
||||||
|
m_notify_read_complete_cv.wait(lock, [this]() { return !m_sector_read_pending.load(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_sector_read_result.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::DoSectorRead()
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
Common::Timer timer;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m_next_position_set.load())
|
||||||
|
{
|
||||||
|
if (m_media->GetPositionOnDisc() != m_next_position && !m_media->Seek(m_next_position))
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("Seek to LBA %u failed", m_next_position);
|
||||||
|
m_sector_read_result.store(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CDImage::LBA pos = m_media->GetPositionOnDisc();
|
||||||
|
if (!m_media->ReadSubChannelQ(&m_subq) || !m_media->ReadRawSector(m_sector_buffer.data()))
|
||||||
|
{
|
||||||
|
m_sector_read_result.store(false);
|
||||||
|
Log_WarningPrintf("Read of LBA %u failed", pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_last_read_sector = pos;
|
||||||
|
m_sector_read_result.store(true);
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
if (timer.GetTimeMilliseconds() > 1.0f)
|
||||||
|
Log_WarningPrintf("Read LBA %u took %.2f msec", pos, timer.GetTimeMilliseconds());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDROMAsyncReader::WorkerThreadEntryPoint()
|
||||||
|
{
|
||||||
|
std::unique_lock lock(m_mutex);
|
||||||
|
|
||||||
|
while (!m_shutdown_flag.load())
|
||||||
|
{
|
||||||
|
m_do_read_cv.wait(lock, [this]() { return (m_shutdown_flag.load() || m_sector_read_pending.load()); });
|
||||||
|
if (m_sector_read_pending.load())
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
DoSectorRead();
|
||||||
|
lock.lock();
|
||||||
|
m_sector_read_pending.store(false);
|
||||||
|
m_notify_read_complete_cv.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
#include "common/cd_image.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
class CDROMAsyncReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using SectorBuffer = std::array<u8, CDImage::RAW_SECTOR_SIZE>;
|
||||||
|
|
||||||
|
CDROMAsyncReader();
|
||||||
|
~CDROMAsyncReader();
|
||||||
|
|
||||||
|
const CDImage::LBA GetLastReadSector() const { return m_last_read_sector; }
|
||||||
|
const SectorBuffer& GetSectorBuffer() const { return m_sector_buffer; }
|
||||||
|
const CDImage::SubChannelQ& GetSectorSubQ() const { return m_subq; }
|
||||||
|
const bool HasMedia() const { return static_cast<bool>(m_media); }
|
||||||
|
const CDImage* GetMedia() const { return m_media.get(); }
|
||||||
|
const std::string GetMediaFileName() const { return m_media ? m_media->GetFileName() : std::string(); }
|
||||||
|
|
||||||
|
bool IsUsingThread() const { return m_read_thread.joinable(); }
|
||||||
|
void StartThread();
|
||||||
|
void StopThread();
|
||||||
|
|
||||||
|
void SetMedia(std::unique_ptr<CDImage> media);
|
||||||
|
void RemoveMedia();
|
||||||
|
|
||||||
|
void QueueReadSector(CDImage::LBA lba);
|
||||||
|
void QueueReadNextSector();
|
||||||
|
|
||||||
|
bool WaitForReadToComplete();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DoSectorRead();
|
||||||
|
void WorkerThreadEntryPoint();
|
||||||
|
|
||||||
|
std::unique_ptr<CDImage> m_media;
|
||||||
|
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::thread m_read_thread;
|
||||||
|
std::condition_variable m_do_read_cv;
|
||||||
|
std::condition_variable m_notify_read_complete_cv;
|
||||||
|
|
||||||
|
CDImage::LBA m_next_position{};
|
||||||
|
std::atomic_bool m_next_position_set{false};
|
||||||
|
std::atomic_bool m_sector_read_pending{false};
|
||||||
|
std::atomic_bool m_shutdown_flag{true};
|
||||||
|
|
||||||
|
CDImage::LBA m_last_read_sector{};
|
||||||
|
CDImage::SubChannelQ m_subq{};
|
||||||
|
SectorBuffer m_sector_buffer{};
|
||||||
|
std::atomic_bool m_sector_read_result{false};
|
||||||
|
};
|
|
@ -39,6 +39,7 @@
|
||||||
<ClCompile Include="bios.cpp" />
|
<ClCompile Include="bios.cpp" />
|
||||||
<ClCompile Include="bus.cpp" />
|
<ClCompile Include="bus.cpp" />
|
||||||
<ClCompile Include="cdrom.cpp" />
|
<ClCompile Include="cdrom.cpp" />
|
||||||
|
<ClCompile Include="cdrom_async_reader.cpp" />
|
||||||
<ClCompile Include="cpu_core.cpp" />
|
<ClCompile Include="cpu_core.cpp" />
|
||||||
<ClCompile Include="cpu_disasm.cpp" />
|
<ClCompile Include="cpu_disasm.cpp" />
|
||||||
<ClCompile Include="cpu_code_cache.cpp" />
|
<ClCompile Include="cpu_code_cache.cpp" />
|
||||||
|
@ -89,6 +90,7 @@
|
||||||
<ClInclude Include="bios.h" />
|
<ClInclude Include="bios.h" />
|
||||||
<ClInclude Include="bus.h" />
|
<ClInclude Include="bus.h" />
|
||||||
<ClInclude Include="cdrom.h" />
|
<ClInclude Include="cdrom.h" />
|
||||||
|
<ClInclude Include="cdrom_async_reader.h" />
|
||||||
<ClInclude Include="cpu_core.h" />
|
<ClInclude Include="cpu_core.h" />
|
||||||
<ClInclude Include="cpu_disasm.h" />
|
<ClInclude Include="cpu_disasm.h" />
|
||||||
<ClInclude Include="cpu_code_cache.h" />
|
<ClInclude Include="cpu_code_cache.h" />
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
<ClCompile Include="analog_controller.cpp" />
|
<ClCompile Include="analog_controller.cpp" />
|
||||||
<ClCompile Include="host_display.cpp" />
|
<ClCompile Include="host_display.cpp" />
|
||||||
<ClCompile Include="timing_event.cpp" />
|
<ClCompile Include="timing_event.cpp" />
|
||||||
|
<ClCompile Include="cdrom_async_reader.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="types.h" />
|
<ClInclude Include="types.h" />
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
<ClInclude Include="controller.h" />
|
<ClInclude Include="controller.h" />
|
||||||
<ClInclude Include="analog_controller.h" />
|
<ClInclude Include="analog_controller.h" />
|
||||||
<ClInclude Include="timing_event.h" />
|
<ClInclude Include="timing_event.h" />
|
||||||
|
<ClInclude Include="cdrom_async_reader.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="cpu_core.inl" />
|
<None Include="cpu_core.inl" />
|
||||||
|
|
|
@ -771,6 +771,8 @@ void HostInterface::SetDefaultSettings()
|
||||||
m_settings.display_fullscreen = false;
|
m_settings.display_fullscreen = false;
|
||||||
m_settings.video_sync_enabled = true;
|
m_settings.video_sync_enabled = true;
|
||||||
|
|
||||||
|
m_settings.cdrom_read_thread = true;
|
||||||
|
|
||||||
m_settings.audio_backend = AudioBackend::Cubeb;
|
m_settings.audio_backend = AudioBackend::Cubeb;
|
||||||
m_settings.audio_sync_enabled = true;
|
m_settings.audio_sync_enabled = true;
|
||||||
|
|
||||||
|
@ -800,6 +802,7 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
|
||||||
const bool old_audio_sync_enabled = m_settings.audio_sync_enabled;
|
const bool old_audio_sync_enabled = m_settings.audio_sync_enabled;
|
||||||
const bool old_speed_limiter_enabled = m_settings.speed_limiter_enabled;
|
const bool old_speed_limiter_enabled = m_settings.speed_limiter_enabled;
|
||||||
const bool old_display_linear_filtering = m_settings.display_linear_filtering;
|
const bool old_display_linear_filtering = m_settings.display_linear_filtering;
|
||||||
|
const bool old_cdrom_read_thread = m_settings.cdrom_read_thread;
|
||||||
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types;
|
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types;
|
||||||
|
|
||||||
apply_callback();
|
apply_callback();
|
||||||
|
@ -847,6 +850,9 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
|
||||||
{
|
{
|
||||||
m_system->UpdateGPUSettings();
|
m_system->UpdateGPUSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_settings.cdrom_read_thread != old_cdrom_read_thread)
|
||||||
|
m_system->GetCDROM()->SetUseReadThread(m_settings.cdrom_read_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||||
|
|
|
@ -30,6 +30,8 @@ void Settings::Load(SettingsInterface& si)
|
||||||
display_fullscreen = si.GetBoolValue("Display", "Fullscreen", false);
|
display_fullscreen = si.GetBoolValue("Display", "Fullscreen", false);
|
||||||
video_sync_enabled = si.GetBoolValue("Display", "VSync", true);
|
video_sync_enabled = si.GetBoolValue("Display", "VSync", true);
|
||||||
|
|
||||||
|
cdrom_read_thread = si.GetBoolValue("CDROM", "ReadThread", true);
|
||||||
|
|
||||||
audio_backend =
|
audio_backend =
|
||||||
ParseAudioBackend(si.GetStringValue("Audio", "Backend", "Cubeb").c_str()).value_or(AudioBackend::Cubeb);
|
ParseAudioBackend(si.GetStringValue("Audio", "Backend", "Cubeb").c_str()).value_or(AudioBackend::Cubeb);
|
||||||
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
|
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
|
||||||
|
@ -79,6 +81,8 @@ void Settings::Save(SettingsInterface& si) const
|
||||||
si.SetBoolValue("Display", "Fullscreen", display_fullscreen);
|
si.SetBoolValue("Display", "Fullscreen", display_fullscreen);
|
||||||
si.SetBoolValue("Display", "VSync", video_sync_enabled);
|
si.SetBoolValue("Display", "VSync", video_sync_enabled);
|
||||||
|
|
||||||
|
si.SetBoolValue("CDROM", "ReadThread", cdrom_read_thread);
|
||||||
|
|
||||||
si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
|
si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
|
||||||
si.SetBoolValue("Audio", "Sync", audio_sync_enabled);
|
si.SetBoolValue("Audio", "Sync", audio_sync_enabled);
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,8 @@ struct Settings
|
||||||
bool display_fullscreen = false;
|
bool display_fullscreen = false;
|
||||||
bool video_sync_enabled = true;
|
bool video_sync_enabled = true;
|
||||||
|
|
||||||
|
bool cdrom_read_thread = true;
|
||||||
|
|
||||||
AudioBackend audio_backend = AudioBackend::Cubeb;
|
AudioBackend audio_backend = AudioBackend::Cubeb;
|
||||||
bool audio_sync_enabled = true;
|
bool audio_sync_enabled = true;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.saveStateOnExit, "General/SaveStateOnExit");
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.saveStateOnExit, "General/SaveStateOnExit");
|
||||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU/ExecutionMode",
|
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.cpuExecutionMode, "CPU/ExecutionMode",
|
||||||
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName);
|
&Settings::ParseCPUExecutionMode, &Settings::GetCPUExecutionModeName);
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.cdromReadThread, "CDROM/ReadThread");
|
||||||
|
|
||||||
connect(m_ui.biosPathBrowse, &QPushButton::pressed, this, &ConsoleSettingsWidget::onBrowseBIOSPathButtonClicked);
|
connect(m_ui.biosPathBrowse, &QPushButton::pressed, this, &ConsoleSettingsWidget::onBrowseBIOSPathButtonClicked);
|
||||||
|
|
||||||
|
|
|
@ -182,6 +182,22 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_4">
|
||||||
|
<property name="title">
|
||||||
|
<string>CDROM Emulation</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout_4">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="cdromReadThread">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use Read Thread (Asynchronous)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
|
@ -971,6 +971,12 @@ void SDLHostInterface::DrawSettingsWindow()
|
||||||
settings_changed |= ImGui::Checkbox("Save State On Exit", &m_settings.save_state_on_exit);
|
settings_changed |= ImGui::Checkbox("Save State On Exit", &m_settings.save_state_on_exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::NewLine();
|
||||||
|
if (DrawSettingsSectionHeader("CDROM Emulation"))
|
||||||
|
{
|
||||||
|
settings_changed |= ImGui::Checkbox("Use Read Thread (Asynchronous)", &m_settings.cdrom_read_thread);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
if (DrawSettingsSectionHeader("Audio"))
|
if (DrawSettingsSectionHeader("Audio"))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue