2019-09-17 09:22:39 +00:00
|
|
|
#pragma once
|
2020-03-12 03:51:53 +00:00
|
|
|
#include "cdrom_async_reader.h"
|
2019-09-17 09:22:39 +00:00
|
|
|
#include "common/bitfield.h"
|
2019-10-04 09:05:19 +00:00
|
|
|
#include "common/cd_image.h"
|
2019-10-15 07:27:35 +00:00
|
|
|
#include "common/cd_xa.h"
|
2019-10-05 06:07:15 +00:00
|
|
|
#include "common/fifo_queue.h"
|
2019-10-27 03:44:23 +00:00
|
|
|
#include "common/heap_array.h"
|
2019-09-21 15:12:16 +00:00
|
|
|
#include "types.h"
|
2019-10-15 07:27:35 +00:00
|
|
|
#include <array>
|
2019-09-24 13:55:22 +00:00
|
|
|
#include <string>
|
2019-09-24 11:39:38 +00:00
|
|
|
#include <vector>
|
2019-09-17 09:22:39 +00:00
|
|
|
|
|
|
|
class StateWrapper;
|
|
|
|
|
2019-09-21 15:12:16 +00:00
|
|
|
class System;
|
2020-01-24 04:53:40 +00:00
|
|
|
class TimingEvent;
|
2019-09-17 09:22:39 +00:00
|
|
|
class DMA;
|
|
|
|
class InterruptController;
|
2019-10-15 07:27:35 +00:00
|
|
|
class SPU;
|
2019-09-17 09:22:39 +00:00
|
|
|
|
|
|
|
class CDROM
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
CDROM();
|
|
|
|
~CDROM();
|
|
|
|
|
2019-11-11 08:19:57 +00:00
|
|
|
void Initialize(System* system, DMA* dma, InterruptController* interrupt_controller, SPU* spu);
|
2019-09-17 09:22:39 +00:00
|
|
|
void Reset();
|
|
|
|
bool DoState(StateWrapper& sw);
|
|
|
|
|
2020-02-21 15:19:10 +00:00
|
|
|
bool HasMedia() const;
|
2020-01-24 04:50:48 +00:00
|
|
|
std::string GetMediaFileName() const;
|
2019-11-16 05:27:57 +00:00
|
|
|
void InsertMedia(std::unique_ptr<CDImage> media);
|
2019-09-20 10:14:00 +00:00
|
|
|
void RemoveMedia();
|
|
|
|
|
2019-09-17 09:22:39 +00:00
|
|
|
// I/O
|
|
|
|
u8 ReadRegister(u32 offset);
|
|
|
|
void WriteRegister(u32 offset, u8 value);
|
2019-10-13 06:48:11 +00:00
|
|
|
void DMARead(u32* words, u32 word_count);
|
2019-09-17 09:22:39 +00:00
|
|
|
|
2019-10-17 13:54:51 +00:00
|
|
|
// Render statistics debug window.
|
|
|
|
void DrawDebugWindow();
|
|
|
|
|
2020-02-21 15:19:10 +00:00
|
|
|
void SetUseReadThread(bool enabled);
|
|
|
|
|
2019-09-17 09:22:39 +00:00
|
|
|
private:
|
2019-10-27 03:44:23 +00:00
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
RAW_SECTOR_OUTPUT_SIZE = CDImage::RAW_SECTOR_SIZE - CDImage::SECTOR_SYNC_SIZE,
|
|
|
|
DATA_SECTOR_OUTPUT_SIZE = CDImage::DATA_SECTOR_SIZE,
|
|
|
|
SECTOR_SYNC_SIZE = CDImage::SECTOR_SYNC_SIZE,
|
|
|
|
SECTOR_HEADER_SIZE = CDImage::SECTOR_HEADER_SIZE,
|
|
|
|
XA_RESAMPLE_RING_BUFFER_SIZE = 32,
|
|
|
|
XA_RESAMPLE_ZIGZAG_TABLE_SIZE = 29,
|
|
|
|
XA_RESAMPLE_NUM_ZIGZAG_TABLES = 7,
|
|
|
|
|
|
|
|
PARAM_FIFO_SIZE = 16,
|
|
|
|
RESPONSE_FIFO_SIZE = 16,
|
|
|
|
DATA_FIFO_SIZE = RAW_SECTOR_OUTPUT_SIZE,
|
2020-03-12 05:32:41 +00:00
|
|
|
NUM_SECTOR_BUFFERS = 2,
|
2019-10-27 03:44:23 +00:00
|
|
|
};
|
|
|
|
|
2019-09-17 09:22:39 +00:00
|
|
|
static constexpr u8 INTERRUPT_REGISTER_MASK = 0x1F;
|
|
|
|
|
2019-09-17 12:18:58 +00:00
|
|
|
enum class Interrupt : u8
|
|
|
|
{
|
2020-03-07 05:10:19 +00:00
|
|
|
DataReady = 0x01,
|
|
|
|
Complete = 0x02,
|
2019-09-24 14:41:09 +00:00
|
|
|
ACK = 0x03,
|
2020-03-07 05:10:19 +00:00
|
|
|
DataEnd = 0x04,
|
|
|
|
Error = 0x05
|
2019-09-17 12:18:58 +00:00
|
|
|
};
|
|
|
|
|
2019-10-27 09:42:37 +00:00
|
|
|
enum class Command : u16
|
2019-09-17 12:18:58 +00:00
|
|
|
{
|
|
|
|
Sync = 0x00,
|
|
|
|
Getstat = 0x01,
|
|
|
|
Setloc = 0x02,
|
|
|
|
Play = 0x03,
|
|
|
|
Forward = 0x04,
|
|
|
|
Backward = 0x05,
|
|
|
|
ReadN = 0x06,
|
|
|
|
MotorOn = 0x07,
|
|
|
|
Stop = 0x08,
|
|
|
|
Pause = 0x09,
|
|
|
|
Init = 0x0A,
|
|
|
|
Mute = 0x0B,
|
|
|
|
Demute = 0x0C,
|
|
|
|
Setfilter = 0x0D,
|
|
|
|
Setmode = 0x0E,
|
|
|
|
Getparam = 0x0F,
|
|
|
|
GetlocL = 0x10,
|
|
|
|
GetlocP = 0x11,
|
|
|
|
SetSession = 0x12,
|
|
|
|
GetTN = 0x13,
|
|
|
|
GetTD = 0x14,
|
|
|
|
SeekL = 0x15,
|
|
|
|
SeekP = 0x16,
|
|
|
|
SetClock = 0x17,
|
|
|
|
GetClock = 0x18,
|
|
|
|
Test = 0x19,
|
|
|
|
GetID = 0x1A,
|
|
|
|
ReadS = 0x1B,
|
|
|
|
Reset = 0x1C,
|
|
|
|
GetQ = 0x1D,
|
|
|
|
ReadTOC = 0x1E,
|
2019-10-27 09:42:37 +00:00
|
|
|
VideoCD = 0x1F,
|
|
|
|
|
|
|
|
None = 0xFFFF
|
2019-09-17 12:18:58 +00:00
|
|
|
};
|
|
|
|
|
2019-10-27 09:42:37 +00:00
|
|
|
enum class DriveState : u8
|
2019-09-17 09:22:39 +00:00
|
|
|
{
|
2019-09-21 15:12:16 +00:00
|
|
|
Idle,
|
2019-10-28 07:25:04 +00:00
|
|
|
SpinningUp,
|
2019-11-16 02:54:41 +00:00
|
|
|
SeekingPhysical,
|
|
|
|
SeekingLogical,
|
2019-10-27 09:42:37 +00:00
|
|
|
ReadingID,
|
2019-11-08 14:21:11 +00:00
|
|
|
ReadingTOC,
|
2019-10-27 09:42:37 +00:00
|
|
|
Reading,
|
|
|
|
Playing,
|
2019-10-28 07:19:29 +00:00
|
|
|
Pausing,
|
2020-03-07 05:10:19 +00:00
|
|
|
Stopping,
|
|
|
|
ChangingSession
|
2019-09-17 09:22:39 +00:00
|
|
|
};
|
|
|
|
|
2019-09-21 15:12:16 +00:00
|
|
|
union StatusRegister
|
2019-09-17 09:22:39 +00:00
|
|
|
{
|
|
|
|
u8 bits;
|
|
|
|
BitField<u8, u8, 0, 2> index;
|
|
|
|
BitField<u8, bool, 2, 1> ADPBUSY;
|
|
|
|
BitField<u8, bool, 3, 1> PRMEMPTY;
|
|
|
|
BitField<u8, bool, 4, 1> PRMWRDY;
|
|
|
|
BitField<u8, bool, 5, 1> RSLRRDY;
|
|
|
|
BitField<u8, bool, 6, 1> DRQSTS;
|
|
|
|
BitField<u8, bool, 7, 1> BUSYSTS;
|
2019-09-21 15:12:16 +00:00
|
|
|
};
|
2019-09-17 09:22:39 +00:00
|
|
|
|
2020-03-08 05:53:53 +00:00
|
|
|
enum StatBits : u8
|
|
|
|
{
|
|
|
|
STAT_ERROR = (1 << 0),
|
|
|
|
STAT_MOTOR_ON = (1 << 1),
|
|
|
|
STAT_SEEK_ERROR = (1 << 2),
|
|
|
|
STAT_ID_ERROR = (1 << 3),
|
|
|
|
STAT_SHELL_OPEN = (1 << 4),
|
|
|
|
STAT_HEADER_VALID = (1 << 5),
|
|
|
|
STAT_SEEKING = (1 << 6),
|
|
|
|
STAT_PLAYING_CDDA = (1 << 7)
|
|
|
|
};
|
|
|
|
|
2019-09-21 15:12:16 +00:00
|
|
|
union SecondaryStatusRegister
|
2019-09-17 12:18:58 +00:00
|
|
|
{
|
|
|
|
u8 bits;
|
|
|
|
BitField<u8, bool, 0, 1> error;
|
|
|
|
BitField<u8, bool, 1, 1> motor_on;
|
|
|
|
BitField<u8, bool, 2, 1> seek_error;
|
|
|
|
BitField<u8, bool, 3, 1> id_error;
|
|
|
|
BitField<u8, bool, 4, 1> shell_open;
|
2019-10-27 09:42:37 +00:00
|
|
|
BitField<u8, bool, 5, 1> header_valid;
|
2019-09-17 12:18:58 +00:00
|
|
|
BitField<u8, bool, 6, 1> seeking;
|
|
|
|
BitField<u8, bool, 7, 1> playing_cdda;
|
2019-10-20 05:55:23 +00:00
|
|
|
|
2019-10-27 09:42:37 +00:00
|
|
|
/// Clears the CDDA/seeking/header valid bits.
|
|
|
|
ALWAYS_INLINE void ClearActiveBits()
|
2019-10-26 11:07:28 +00:00
|
|
|
{
|
2020-03-08 05:53:53 +00:00
|
|
|
bits &= ~(STAT_HEADER_VALID | STAT_SEEKING | STAT_PLAYING_CDDA);
|
2019-10-26 11:07:28 +00:00
|
|
|
}
|
2019-09-21 15:12:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
union ModeRegister
|
|
|
|
{
|
|
|
|
u8 bits;
|
|
|
|
BitField<u8, bool, 0, 1> cdda;
|
|
|
|
BitField<u8, bool, 1, 1> auto_pause;
|
|
|
|
BitField<u8, bool, 2, 1> report_audio;
|
|
|
|
BitField<u8, bool, 3, 1> xa_filter;
|
|
|
|
BitField<u8, bool, 4, 1> ignore_bit;
|
|
|
|
BitField<u8, bool, 5, 1> read_raw_sector;
|
2019-09-30 10:01:41 +00:00
|
|
|
BitField<u8, bool, 6, 1> xa_enable;
|
2019-09-21 15:12:16 +00:00
|
|
|
BitField<u8, bool, 7, 1> double_speed;
|
|
|
|
};
|
|
|
|
|
|
|
|
union RequestRegister
|
|
|
|
{
|
|
|
|
u8 bits;
|
|
|
|
BitField<u8, bool, 5, 1> SMEN;
|
|
|
|
BitField<u8, bool, 6, 1> BFWR;
|
|
|
|
BitField<u8, bool, 7, 1> BFRD;
|
|
|
|
};
|
|
|
|
|
2019-10-05 06:07:15 +00:00
|
|
|
void SoftReset();
|
|
|
|
|
2019-10-27 09:42:37 +00:00
|
|
|
bool IsDriveIdle() const { return m_drive_state == DriveState::Idle; }
|
|
|
|
bool HasPendingCommand() const { return m_command != Command::None; }
|
2019-09-21 15:12:16 +00:00
|
|
|
bool HasPendingInterrupt() const { return m_interrupt_flag_register != 0; }
|
2019-10-26 07:41:37 +00:00
|
|
|
bool HasPendingAsyncInterrupt() const { return m_pending_async_interrupt != 0; }
|
2019-09-21 15:12:16 +00:00
|
|
|
void SetInterrupt(Interrupt interrupt);
|
2019-10-26 07:41:37 +00:00
|
|
|
void SetAsyncInterrupt(Interrupt interrupt);
|
2019-11-12 10:31:59 +00:00
|
|
|
void ClearAsyncInterrupt();
|
2019-10-26 07:41:37 +00:00
|
|
|
void DeliverAsyncInterrupt();
|
2019-10-26 06:12:37 +00:00
|
|
|
void SendACKAndStat();
|
2020-03-08 05:53:53 +00:00
|
|
|
void SendErrorResponse(u8 stat_bits = STAT_ERROR, u8 reason = 0x80);
|
|
|
|
void SendAsyncErrorResponse(u8 stat_bits = STAT_ERROR, u8 reason = 0x80);
|
2019-09-21 15:12:16 +00:00
|
|
|
void UpdateStatusRegister();
|
2019-11-08 13:48:09 +00:00
|
|
|
void UpdateInterruptRequest();
|
2019-09-21 15:12:16 +00:00
|
|
|
|
2019-10-26 11:07:28 +00:00
|
|
|
TickCount GetAckDelayForCommand() const;
|
|
|
|
TickCount GetTicksForRead() const;
|
|
|
|
TickCount GetTicksForSeek() const;
|
2019-09-21 15:12:16 +00:00
|
|
|
void BeginCommand(Command command); // also update status register
|
2019-11-10 13:03:52 +00:00
|
|
|
void EndCommand(); // also updates status register
|
2019-09-21 15:12:16 +00:00
|
|
|
void ExecuteCommand();
|
|
|
|
void ExecuteTestCommand(u8 subcommand);
|
2020-01-24 04:53:40 +00:00
|
|
|
void UpdateCommandEvent();
|
|
|
|
void ExecuteDrive(TickCount ticks_late);
|
|
|
|
void BeginReading(TickCount ticks_late = 0);
|
|
|
|
void BeginPlaying(u8 track_bcd, TickCount ticks_late = 0);
|
2019-10-28 07:25:04 +00:00
|
|
|
void DoSpinUpComplete();
|
2020-01-24 04:53:40 +00:00
|
|
|
void DoSeekComplete(TickCount ticks_late);
|
2019-10-27 09:42:37 +00:00
|
|
|
void DoPauseComplete();
|
2019-10-28 07:19:29 +00:00
|
|
|
void DoStopComplete();
|
2020-03-07 05:10:19 +00:00
|
|
|
void DoChangeSessionComplete();
|
2019-10-27 09:42:37 +00:00
|
|
|
void DoIDRead();
|
2019-11-08 14:21:11 +00:00
|
|
|
void DoTOCRead();
|
2019-09-21 15:12:16 +00:00
|
|
|
void DoSectorRead();
|
2019-11-23 09:13:48 +00:00
|
|
|
void ProcessDataSectorHeader(const u8* raw_sector, bool set_valid);
|
2019-11-12 10:31:59 +00:00
|
|
|
void ProcessDataSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
|
|
|
|
void ProcessXAADPCMSector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
|
|
|
|
void ProcessCDDASector(const u8* raw_sector, const CDImage::SubChannelQ& subq);
|
2019-11-16 02:54:41 +00:00
|
|
|
void BeginSeeking(bool logical, bool read_after_seek, bool play_after_seek);
|
2019-09-30 10:01:41 +00:00
|
|
|
void LoadDataFIFO();
|
2020-03-12 05:32:41 +00:00
|
|
|
void ClearSectorBuffers();
|
2019-09-21 15:12:16 +00:00
|
|
|
|
|
|
|
System* m_system = nullptr;
|
|
|
|
DMA* m_dma = nullptr;
|
|
|
|
InterruptController* m_interrupt_controller = nullptr;
|
2019-10-15 07:27:35 +00:00
|
|
|
SPU* m_spu = nullptr;
|
2020-01-24 04:53:40 +00:00
|
|
|
std::unique_ptr<TimingEvent> m_command_event;
|
|
|
|
std::unique_ptr<TimingEvent> m_drive_event;
|
2019-09-21 15:12:16 +00:00
|
|
|
|
2019-10-27 09:42:37 +00:00
|
|
|
Command m_command = Command::None;
|
|
|
|
DriveState m_drive_state = DriveState::Idle;
|
2020-03-12 03:51:53 +00:00
|
|
|
DiscRegion m_disc_region = DiscRegion::Other;
|
2019-09-21 15:12:16 +00:00
|
|
|
|
|
|
|
StatusRegister m_status = {};
|
|
|
|
SecondaryStatusRegister m_secondary_status = {};
|
|
|
|
ModeRegister m_mode = {};
|
2019-09-17 12:18:58 +00:00
|
|
|
|
2019-10-15 07:27:35 +00:00
|
|
|
u8 m_interrupt_enable_register = INTERRUPT_REGISTER_MASK;
|
|
|
|
u8 m_interrupt_flag_register = 0;
|
2019-10-26 07:41:37 +00:00
|
|
|
u8 m_pending_async_interrupt = 0;
|
2019-10-15 07:27:35 +00:00
|
|
|
|
2019-10-26 11:07:28 +00:00
|
|
|
CDImage::Position m_setloc_position = {};
|
2020-02-21 15:19:10 +00:00
|
|
|
CDImage::LBA m_last_requested_sector{};
|
2019-10-26 11:07:28 +00:00
|
|
|
bool m_setloc_pending = false;
|
|
|
|
bool m_read_after_seek = false;
|
|
|
|
bool m_play_after_seek = false;
|
|
|
|
|
|
|
|
bool m_muted = false;
|
|
|
|
bool m_adpcm_muted = false;
|
2019-10-15 07:27:35 +00:00
|
|
|
|
|
|
|
u8 m_filter_file_number = 0;
|
|
|
|
u8 m_filter_channel_number = 0;
|
|
|
|
|
2019-11-10 12:45:48 +00:00
|
|
|
CDImage::SectorHeader m_last_sector_header{};
|
|
|
|
CDXA::XASubHeader m_last_sector_subheader{};
|
|
|
|
CDImage::SubChannelQ m_last_subq{};
|
2019-11-10 13:03:52 +00:00
|
|
|
u8 m_last_cdda_report_frame_nibble = 0xFF;
|
2020-03-08 14:01:28 +00:00
|
|
|
u8 m_cdda_report_delay = 0x00;
|
2019-11-13 06:33:49 +00:00
|
|
|
u8 m_play_track_number_bcd = 0xFF;
|
2020-03-07 05:10:19 +00:00
|
|
|
u8 m_async_command_parameter = 0x00;
|
2019-10-04 09:05:19 +00:00
|
|
|
|
2019-10-15 07:27:35 +00:00
|
|
|
std::array<std::array<u8, 2>, 2> m_cd_audio_volume_matrix{};
|
|
|
|
std::array<std::array<u8, 2>, 2> m_next_cd_audio_volume_matrix{};
|
|
|
|
|
|
|
|
std::array<s32, 4> m_xa_last_samples{};
|
|
|
|
std::array<std::array<s16, XA_RESAMPLE_RING_BUFFER_SIZE>, 2> m_xa_resample_ring_buffer{};
|
|
|
|
u8 m_xa_resample_p = 0;
|
|
|
|
u8 m_xa_resample_sixstep = 6;
|
2019-09-17 09:22:39 +00:00
|
|
|
|
|
|
|
InlineFIFOQueue<u8, PARAM_FIFO_SIZE> m_param_fifo;
|
|
|
|
InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> m_response_fifo;
|
2019-10-26 07:41:37 +00:00
|
|
|
InlineFIFOQueue<u8, RESPONSE_FIFO_SIZE> m_async_response_fifo;
|
2019-09-17 09:22:39 +00:00
|
|
|
HeapFIFOQueue<u8, DATA_FIFO_SIZE> m_data_fifo;
|
2020-03-12 05:32:41 +00:00
|
|
|
|
|
|
|
struct SectorBuffer
|
|
|
|
{
|
|
|
|
HeapArray<u8, RAW_SECTOR_OUTPUT_SIZE> data;
|
|
|
|
u32 size;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::array<SectorBuffer, NUM_SECTOR_BUFFERS> m_sector_buffers;
|
2020-02-21 15:19:10 +00:00
|
|
|
|
|
|
|
CDROMAsyncReader m_reader;
|
2019-09-21 15:12:16 +00:00
|
|
|
};
|