#include <mednafen/mednafen.h>
#include <math.h>
#include <algorithm>
#include <trio/trio.h>
#include "scsicd.h"
#include "cdromif.h"
#include "SimpleFIFO.h"
#if defined(__SSE2__)
#include <xmmintrin.h>
#include <emmintrin.h>
#endif
using namespace CDUtility;
static uint32 CD_DATA_TRANSFER_RATE;
static uint32 System_Clock;
static void (*CDIRQCallback)(int);
static void (*CDStuffSubchannels)(uint8, int);
static int32* HRBufs[2];
static int WhichSystem;
static CDIF *Cur_CDIF;
static bool TrayOpen;
#define SetIOP(mask, set) { cd_bus.signals &= ~mask; if(set) cd_bus.signals |= mask; }
#define SetBSY(set) SetIOP(SCSICD_BSY_mask, set)
#define SetIO(set) SetIOP(SCSICD_IO_mask, set)
#define SetCD(set) SetIOP(SCSICD_CD_mask, set)
#define SetMSG(set) SetIOP(SCSICD_MSG_mask, set)
static INLINE void SetREQ(bool set)
{
if(set && !REQ_signal)
CDIRQCallback(SCSICD_IRQ_MAGICAL_REQ);
SetIOP(SCSICD_REQ_mask, set);
}
#define SetkingACK(set) SetIOP(SCSICD_kingACK_mask, set)
#define SetkingRST(set) SetIOP(SCSICD_kingRST_mask, set)
#define SetkingSEL(set) SetIOP(SCSICD_kingSEL_mask, set)
#define SetkingATN(set) SetIOP(SCSICD_kingATN_mask, set)
enum
{
QMode_Zero = 0,
QMode_Time = 1,
QMode_MCN = 2,
QMode_ISRC = 3
};
typedef struct
{
bool last_RST_signal;
uint8 message_pending;
bool status_sent, message_sent;
uint8 key_pending, asc_pending, ascq_pending, fru_pending;
uint8 command_buffer[256];
uint8 command_buffer_pos;
uint8 command_size_left;
bool data_transfer_done;
uint8 data_out[256];
uint8 data_out_pos;
uint8 data_out_want;
bool DiscChanged;
uint8 SubQBuf[4][0xC];
uint8 SubQBuf_Last[0xC];
uint8 SubPWBuf[96];
} scsicd_t;
enum
{
CDDASTATUS_PAUSED = -1,
CDDASTATUS_STOPPED = 0,
CDDASTATUS_PLAYING = 1,
CDDASTATUS_SCANNING = 2,
};
enum
{
PLAYMODE_SILENT = 0x00,
PLAYMODE_NORMAL,
PLAYMODE_INTERRUPT,
PLAYMODE_LOOP,
};
typedef struct
{
uint32 CDDADivAcc;
uint8 CDDADivAccVolFudge;
uint32 scan_sec_end;
uint8 PlayMode;
int32 CDDAVolume[2];
int16 CDDASectorBuffer[1176];
uint32 CDDAReadPos;
int8 CDDAStatus;
uint8 ScanMode;
int64 CDDADiv;
int CDDATimeDiv;
int16 OversampleBuffer[2][0x10 * 2];
unsigned OversamplePos;
int16 sr[2];
uint8 OutPortChSelect[2];
uint32 OutPortChSelectCache[2];
int32 OutPortVolumeCache[2];
float DeemphState[2][2];
} cdda_t;
void MakeSense(uint8 * target, uint8 key, uint8 asc, uint8 ascq, uint8 fru)
{
memset(target, 0, 18);
target[0] = 0x70;
target[2] = key;
target[7] = 0x0A;
target[12] = asc;
target[13] = ascq;
target[14] = fru;
}
static void (*SCSILog)(const char *, const char *format, ...);
static void InitModePages(void);
static scsicd_timestamp_t lastts;
static int64 monotonic_timestamp;
static int64 pce_lastsapsp_timestamp;
scsicd_t cd;
scsicd_bus_t cd_bus;
static cdda_t cdda;
static SimpleFIFO<uint8> *din = NULL;
static CDUtility::TOC toc;
static uint32 read_sec_start;
static uint32 read_sec;
static uint32 read_sec_end;
static int32 CDReadTimer;
static uint32 SectorAddr;
static uint32 SectorCount;
enum
{
PHASE_BUS_FREE = 0,
PHASE_COMMAND,
PHASE_DATA_IN,
PHASE_DATA_OUT,
PHASE_STATUS,
PHASE_MESSAGE_IN,
PHASE_MESSAGE_OUT
};
static unsigned int CurrentPhase;
static void ChangePhase(const unsigned int new_phase);
static void FixOPV(void)
{
for(int port = 0; port < 2; port++)
{
int32 tmpvol = cdda.CDDAVolume[port] * 100 / (2 * cdda.CDDADivAccVolFudge);
cdda.OutPortVolumeCache[port] = tmpvol;
if(cdda.OutPortChSelect[port] & 0x01)
cdda.OutPortChSelectCache[port] = 0;
else if(cdda.OutPortChSelect[port] & 0x02)
cdda.OutPortChSelectCache[port] = 1;
else
{
cdda.OutPortChSelectCache[port] = 0;
cdda.OutPortVolumeCache[port] = 0;
}
}
}
static void VirtualReset(void)
{
InitModePages();
din->Flush();
CDReadTimer = 0;
pce_lastsapsp_timestamp = monotonic_timestamp;
SectorAddr = SectorCount = 0;
read_sec_start = read_sec = 0;
read_sec_end = ~0;
cdda.PlayMode = PLAYMODE_SILENT;
cdda.CDDAReadPos = 0;
cdda.CDDAStatus = CDDASTATUS_STOPPED;
cdda.CDDADiv = 0;
cdda.ScanMode = 0;
cdda.scan_sec_end = 0;
cdda.OversamplePos = 0;
memset(cdda.sr, 0, sizeof(cdda.sr));
memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer));
memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
memset(cd.data_out, 0, sizeof(cd.data_out));
cd.data_out_pos = 0;
cd.data_out_want = 0;
FixOPV();
ChangePhase(PHASE_BUS_FREE);
}
void SCSICD_Power(scsicd_timestamp_t system_timestamp)
{
memset(&cd, 0, sizeof(scsicd_t));
memset(&cd_bus, 0, sizeof(scsicd_bus_t));
monotonic_timestamp = system_timestamp;
cd.DiscChanged = false;
if(Cur_CDIF && !TrayOpen)
Cur_CDIF->ReadTOC(&toc);
CurrentPhase = PHASE_BUS_FREE;
VirtualReset();
}
void SCSICD_SetDB(uint8 data)
{
cd_bus.DB = data;
}
void SCSICD_SetACK(bool set)
{
SetkingACK(set);
}
void SCSICD_SetSEL(bool set)
{
SetkingSEL(set);
}
void SCSICD_SetRST(bool set)
{
SetkingRST(set);
}
void SCSICD_SetATN(bool set)
{
SetkingATN(set);
}
static void GenSubQFromSubPW(void)
{
uint8 SubQBuf[0xC];
memset(SubQBuf, 0, 0xC);
for(int i = 0; i < 96; i++)
SubQBuf[i >> 3] |= ((cd.SubPWBuf[i] & 0x40) >> 6) << (7 - (i & 7));
if(!subq_check_checksum(SubQBuf))
{
}
else
{
memcpy(cd.SubQBuf_Last, SubQBuf, 0xC);
uint8 adr = SubQBuf[0] & 0xF;
if(adr <= 0x3)
memcpy(cd.SubQBuf[adr], SubQBuf, 0xC);
}
}
#define STATUS_GOOD 0
#define STATUS_CHECK_CONDITION 1
#define STATUS_CONDITION_MET 2
#define STATUS_BUSY 4
#define STATUS_INTERMEDIATE 8
#define SENSEKEY_NO_SENSE 0x0
#define SENSEKEY_NOT_READY 0x2
#define SENSEKEY_MEDIUM_ERROR 0x3
#define SENSEKEY_HARDWARE_ERROR 0x4
#define SENSEKEY_ILLEGAL_REQUEST 0x5
#define SENSEKEY_UNIT_ATTENTION 0x6
#define SENSEKEY_ABORTED_COMMAND 0xB
#define ASC_MEDIUM_NOT_PRESENT 0x3A
#define NSE_NO_DISC 0x0B
#define NSE_TRAY_OPEN 0x0D
#define NSE_SEEK_ERROR 0x15
#define NSE_HEADER_READ_ERROR 0x16
#define NSE_NOT_AUDIO_TRACK 0x1C
#define NSE_NOT_DATA_TRACK 0x1D
#define NSE_INVALID_COMMAND 0x20
#define NSE_INVALID_ADDRESS 0x21
#define NSE_INVALID_PARAMETER 0x22
#define NSE_END_OF_VOLUME 0x25
#define NSE_INVALID_REQUEST_IN_CDB 0x27
#define NSE_DISC_CHANGED 0x28
#define NSE_AUDIO_NOT_PLAYING 0x2C
#define AP_UNRECOVERED_READ_ERROR 0x11, 0x00
#define AP_LEC_UNCORRECTABLE_ERROR 0x11, 0x05
#define AP_CIRC_UNRECOVERED_ERROR 0x11, 0x06
#define AP_UNKNOWN_MEDIUM_FORMAT 0x30, 0x01
#define AP_INCOMPAT_MEDIUM_FORMAT 0x30, 0x02
static void ChangePhase(const unsigned int new_phase)
{
switch(new_phase)
{
case PHASE_BUS_FREE:
SetBSY(false);
SetMSG(false);
SetCD(false);
SetIO(false);
SetREQ(false);
CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_DONE);
break;
case PHASE_DATA_IN:
SetBSY(true);
SetMSG(false);
SetCD(false);
SetIO(true);
SetREQ(false);
break;
case PHASE_STATUS:
SetBSY(true);
SetMSG(false);
SetCD(true);
SetIO(true);
SetREQ(true);
break;
case PHASE_MESSAGE_IN:
SetBSY(true);
SetMSG(true);
SetCD(true);
SetIO(true);
SetREQ(true);
break;
case PHASE_DATA_OUT:
SetBSY(true);
SetMSG(false);
SetCD(false);
SetIO(false);
SetREQ(true);
break;
case PHASE_COMMAND:
SetBSY(true);
SetMSG(false);
SetCD(true);
SetIO(false);
SetREQ(true);
break;
case PHASE_MESSAGE_OUT:
SetBSY(true);
SetMSG(true);
SetCD(true);
SetIO(false);
SetREQ(true);
break;
}
CurrentPhase = new_phase;
}
static void SendStatusAndMessage(uint8 status, uint8 message)
{
if(din->CanRead())
{
din->Flush();
}
cd.message_pending = message;
cd.status_sent = FALSE;
cd.message_sent = FALSE;
if(WhichSystem == SCSICD_PCE)
{
if(status == STATUS_GOOD || status == STATUS_CONDITION_MET)
cd_bus.DB = 0x00;
else
cd_bus.DB = 0x01;
}
else
cd_bus.DB = status << 1;
ChangePhase(PHASE_STATUS);
}
static void DoSimpleDataIn(const uint8 *data_in, uint32 len)
{
din->Write(data_in, len);
cd.data_transfer_done = true;
ChangePhase(PHASE_DATA_IN);
}
void SCSICD_SetDisc(bool new_tray_open, CDIF *cdif, bool no_emu_side_effects)
{
Cur_CDIF = cdif;
if(TrayOpen && !new_tray_open)
{
TrayOpen = false;
if(cdif)
{
cdif->ReadTOC(&toc);
if(!no_emu_side_effects)
{
memset(cd.SubQBuf, 0, sizeof(cd.SubQBuf));
memset(cd.SubQBuf_Last, 0, sizeof(cd.SubQBuf_Last));
cd.DiscChanged = true;
}
}
}
else if(!TrayOpen && new_tray_open)
{
TrayOpen = true;
}
}
static void CommandCCError(int key, int asc = 0, int ascq = 0)
{
cd.key_pending = key;
cd.asc_pending = asc;
cd.ascq_pending = ascq;
cd.fru_pending = 0x00;
SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
}
static bool ValidateRawDataSector(uint8 *data, const uint32 lba)
{
if(!Cur_CDIF->ValidateRawSector(data))
{
MDFN_DispMessage(_("Uncorrectable data at sector %d"), lba);
MDFN_PrintError(_("Uncorrectable data at sector %d"), lba);
din->Flush();
cd.data_transfer_done = false;
CommandCCError(SENSEKEY_MEDIUM_ERROR, AP_LEC_UNCORRECTABLE_ERROR);
return(false);
}
return(true);
}
static void DoMODESELECT6(const uint8 *cdb)
{
if(cdb[4])
{
cd.data_out_pos = 0;
cd.data_out_want = cdb[4];
ChangePhase(PHASE_DATA_OUT);
}
else
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
struct ModePageParam
{
uint8 default_value;
uint8 alterable_mask;
uint8 real_mask;
};
struct ModePage
{
const uint8 code;
const uint8 param_length;
const ModePageParam params[64];
uint8 current_value[64];
};
static const int NumModePages = 5;
static ModePage ModePages[NumModePages] =
{
{ 0x28,
0x04,
{
{ 0x00, 0x00, 0xFF },
{ 0x00, 0x00, 0xFF },
{ 0x00, 0x00, 0xFF },
{ 0x00, 0x00, 0xFF },
}
},
{ 0x29,
0x01,
{
{ 0x00, 0x00, 0xFF },
}
},
{ 0x2a,
0x02,
{
{ 0x00, 0x00, 0xFF },
{ 0x11, 0x00, 0xFF },
}
},
{ 0x2B,
0x01,
{
{ 0x00, 0x00, 0xFF },
}
},
{ 0x0E,
0x0E,
{
{ 0x04, 0x04, 0x04 },
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x01, 0x01 },
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0x01, 0x01, 0x03 },
{ 0xFF, 0x00, 0x00 },
{ 0x02, 0x02, 0x03 },
{ 0xFF, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00 },
}
},
};
static void UpdateMPCacheP(const ModePage* mp)
{
switch(mp->code)
{
case 0x0E:
{
const uint8 *pd = &mp->current_value[0];
for(int i = 0; i < 2; i++)
cdda.OutPortChSelect[i] = pd[6 + i * 2];
FixOPV();
}
break;
case 0x28:
break;
case 0x29:
break;
case 0x2A:
break;
case 0x2B:
{
int speed;
int rate;
speed = std::max<int>(-32, std::min<int>(32, (int8)mp->current_value[0]));
rate = 44100 + 441 * speed;
cdda.CDDADivAcc = ((int64)System_Clock * (1024 * 1024) / (2 * rate));
cdda.CDDADivAccVolFudge = 100 + speed;
FixOPV();
}
break;
}
}
static void UpdateMPCache(uint8 code)
{
for(int pi = 0; pi < NumModePages; pi++)
{
const ModePage* mp = &ModePages[pi];
if(mp->code == code)
{
UpdateMPCacheP(mp);
break;
}
}
}
static void InitModePages(void)
{
for(int pi = 0; pi < NumModePages; pi++)
{
ModePage *mp = &ModePages[pi];
const ModePageParam *params = &ModePages[pi].params[0];
for(int parami = 0; parami < mp->param_length; parami++)
mp->current_value[parami] = params[parami].default_value;
UpdateMPCacheP(mp);
}
}
static void FinishMODESELECT6(const uint8 *data, const uint8 data_len)
{
uint8 mode_data_length, medium_type, device_specific, block_descriptor_length;
uint32 offset = 0;
if(data_len < 4)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
mode_data_length = data[offset++];
medium_type = data[offset++];
device_specific = data[offset++];
block_descriptor_length = data[offset++];
(void)mode_data_length;
(void)medium_type;
(void)device_specific;
if(block_descriptor_length & 0x7)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if((offset + block_descriptor_length) > data_len)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
offset += block_descriptor_length;
while(offset < data_len)
{
const uint8 code = data[offset++];
uint8 param_len = 0;
bool page_found = false;
if(code == 0x00)
{
if((offset + 0x5) > data_len)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
UpdateMPCache(0x00);
offset += 0x5;
continue;
}
if(offset >= data_len)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
param_len = data[offset++];
for(int pi = 0; pi < NumModePages; pi++)
{
ModePage *mp = &ModePages[pi];
if(code == mp->code)
{
page_found = true;
if(param_len != mp->param_length)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if((param_len + offset) > data_len)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
for(int parami = 0; parami < mp->param_length; parami++)
{
mp->current_value[parami] &= ~mp->params[parami].real_mask;
mp->current_value[parami] |= (data[offset++]) & mp->params[parami].real_mask;
}
UpdateMPCacheP(mp);
break;
}
}
if(!page_found)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
}
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoMODESENSE6(const uint8 *cdb)
{
unsigned int PC = (cdb[2] >> 6) & 0x3;
unsigned int PageCode = cdb[2] & 0x3F;
bool DBD = cdb[1] & 0x08;
int AllocSize = cdb[4];
int index = 0;
uint8 data_in[8192];
uint8 PageMatchOR = 0x00;
bool AnyPageMatch = false;
if(!AllocSize)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
if(PC == 3)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(PageCode == 0x00)
{
if(DBD || PC)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
memset(data_in, 0, 0xA);
data_in[0] = 0x09;
data_in[2] = 0x80;
data_in[9] = 0x0F;
if(AllocSize > 0xA)
AllocSize = 0xA;
DoSimpleDataIn(data_in, AllocSize);
return;
}
data_in[0] = 0x00;
data_in[1] = 0x00;
data_in[2] = 0x00;
data_in[3] = DBD ? 0x00 : 0x08;
index += 4;
if(!DBD)
{
data_in[index++] = 0x00;
MDFN_en24msb(&data_in[index], 0x6E);
index += 3;
data_in[index++] = 0x00;
MDFN_en24msb(&data_in[index], 0x800);
index += 3;
}
PageMatchOR = 0x00;
if(PageCode == 0x3F)
PageMatchOR = 0x3F;
for(int pi = 0; pi < NumModePages; pi++)
{
const ModePage *mp = &ModePages[pi];
const ModePageParam *params = &ModePages[pi].params[0];
if((mp->code | PageMatchOR) != PageCode)
continue;
AnyPageMatch = true;
data_in[index++] = mp->code;
data_in[index++] = mp->param_length;
for(int parami = 0; parami < mp->param_length; parami++)
{
uint8 data;
if(PC == 0x02)
data = params[parami].default_value;
else if(PC == 0x01)
data = params[parami].alterable_mask;
else
data = mp->current_value[parami];
data_in[index++] = data;
}
}
if(!AnyPageMatch)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(AllocSize > index)
AllocSize = index;
data_in[0] = AllocSize - 1;
DoSimpleDataIn(data_in, AllocSize);
}
static void DoSTARTSTOPUNIT6(const uint8 *cdb)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoREZEROUNIT(const uint8 *cdb)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
#if 0
static const uint8 InqData[0x24] =
{
0x05, 0x80, 0x02, 0x00,
0x1F,
0x00, 0x00, 0x00, 0x4E, 0x45, 0x43, 0x20, 0x20,
0x20, 0x20, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F,
0x4D, 0x20, 0x44, 0x52, 0x49, 0x56, 0x45, 0x3A,
0x46, 0x58, 0x20, 0x31, 0x2E, 0x30, 0x20
};
#endif
static const uint8 InqData[0x24] =
{
0x05,
0x80,
0x02,
0x00,
0x1F,
0x00, 0x00,
0x00,
0x4E, 0x45, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20,
0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x44, 0x52, 0x49, 0x56, 0x45, 0x3A, 0x46, 0x58, 0x20,
0x31, 0x2E, 0x30, 0x20
};
static void DoINQUIRY(const uint8 *cdb)
{
unsigned int AllocSize = (cdb[4] < sizeof(InqData)) ? cdb[4] : sizeof(InqData);
if(AllocSize)
DoSimpleDataIn(InqData, AllocSize);
else
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoNEC_NOP(const uint8 *cdb)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoNEC_EJECT(const uint8 *cdb)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_REQUEST_IN_CDB);
}
static void DoREQUESTSENSE(const uint8 *cdb)
{
uint8 data_in[8192];
MakeSense(data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending);
DoSimpleDataIn(data_in, 18);
cd.key_pending = 0;
cd.asc_pending = 0;
cd.ascq_pending = 0;
cd.fru_pending = 0;
}
static void EncodeM3TOC(uint8 *buf, uint8 POINTER_RAW, int32 LBA, uint32 PLBA, uint8 control)
{
uint8 MIN, SEC, FRAC;
uint8 PMIN, PSEC, PFRAC;
LBA_to_AMSF(LBA, &MIN, &SEC, &FRAC);
LBA_to_AMSF(PLBA, &PMIN, &PSEC, &PFRAC);
buf[0x0] = control << 4;
buf[0x1] = 0x00;
buf[0x2] = POINTER_RAW;
buf[0x3] = U8_to_BCD(MIN);
buf[0x4] = U8_to_BCD(SEC);
buf[0x5] = U8_to_BCD(FRAC);
buf[0x6] = 0x00;
buf[0x7] = U8_to_BCD(PMIN);
buf[0x8] = U8_to_BCD(PSEC);
buf[0x9] = U8_to_BCD(PFRAC);
}
static void DoNEC_GETDIRINFO(const uint8 *cdb)
{
uint8 data_in[2048];
uint32 data_in_size = 0;
memset(data_in, 0, sizeof(data_in));
switch(cdb[1] & 0x03)
{
case 0x3:
{
int offset = 0;
int32 lilba = -150;
uint8 match = cdb[2];
if(match != 0x00 && match != 0xA0 && match != 0xA1 && match != 0xA2 && match != 0xB0)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS);
return;
}
memset(data_in, 0, sizeof(data_in));
data_in[0] = 0x00;
data_in[1] = 0x00;
offset = 2;
if(!match || match == 0xA0)
{
EncodeM3TOC(&data_in[offset], 0xA0, lilba, toc.first_track * 75 * 60 - 150, toc.tracks[toc.first_track].control);
lilba++;
offset += 0xA;
}
if(!match || match == 0xA1)
{
EncodeM3TOC(&data_in[offset], 0xA1, lilba, toc.last_track * 75 * 60 - 150, toc.tracks[toc.last_track].control);
lilba++;
offset += 0xA;
}
if(!match || match == 0xA2)
{
EncodeM3TOC(&data_in[offset], 0xA2, lilba, toc.tracks[100].lba, toc.tracks[100].control);
lilba++;
offset += 0xA;
}
if(!match)
for(int track = toc.first_track; track <= toc.last_track; track++)
{
EncodeM3TOC(&data_in[offset], U8_to_BCD(track), lilba, toc.tracks[track].lba, toc.tracks[track].control);
lilba++;
offset += 0xA;
}
if(match == 0xB0)
{
memset(&data_in[offset], 0, 0x14);
offset += 0x14;
}
assert((unsigned int)offset <= sizeof(data_in));
data_in_size = offset;
MDFN_en16msb(&data_in[0], offset - 2);
}
break;
case 0x0:
data_in[0] = U8_to_BCD(toc.first_track);
data_in[1] = U8_to_BCD(toc.last_track);
data_in_size = 4;
break;
case 0x1:
{
uint8 m, s, f;
LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
data_in[0] = U8_to_BCD(m);
data_in[1] = U8_to_BCD(s);
data_in[2] = U8_to_BCD(f);
data_in_size = 4;
}
break;
case 0x2:
{
uint8 m, s, f;
int track = BCD_to_U8(cdb[2]);
if(track < toc.first_track || track > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS);
return;
}
LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
data_in[0] = U8_to_BCD(m);
data_in[1] = U8_to_BCD(s);
data_in[2] = U8_to_BCD(f);
data_in[3] = toc.tracks[track].control;
data_in_size = 4;
}
break;
}
DoSimpleDataIn(data_in, data_in_size);
}
static void DoREADTOC(const uint8 *cdb)
{
uint8 data_in[8192];
int FirstTrack = toc.first_track;
int LastTrack = toc.last_track;
int StartingTrack = cdb[6];
unsigned int AllocSize = (cdb[7] << 8) | cdb[8];
unsigned int RealSize = 0;
const bool WantInMSF = cdb[1] & 0x2;
if(!AllocSize)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
if((cdb[1] & ~0x2) || cdb[2] || cdb[3] || cdb[4] || cdb[5] || cdb[9])
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(!StartingTrack)
StartingTrack = 1;
else if(StartingTrack == 0xAA)
{
StartingTrack = LastTrack + 1;
}
else if(StartingTrack > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
data_in[2] = FirstTrack;
data_in[3] = LastTrack;
RealSize += 4;
for(int track = StartingTrack; track <= (LastTrack + 1); track++)
{
uint8 *subptr = &data_in[RealSize];
uint32 lba;
uint8 m, s, f;
uint32 eff_track;
if(track == (LastTrack + 1))
eff_track = 100;
else
eff_track = track;
lba = toc.tracks[eff_track].lba;
LBA_to_AMSF(lba, &m, &s, &f);
subptr[0] = 0;
subptr[1] = toc.tracks[eff_track].control | (toc.tracks[eff_track].adr << 4);
if(eff_track == 100)
subptr[2] = 0xAA;
else
subptr[2] = track;
subptr[3] = 0;
if(WantInMSF)
{
subptr[4] = 0;
subptr[5] = m;
subptr[6] = s;
subptr[7] = f;
}
else
{
subptr[4] = lba >> 24;
subptr[5] = lba >> 16;
subptr[6] = lba >> 8;
subptr[7] = lba >> 0;
}
RealSize += 8;
}
data_in[0] = (RealSize - 2) >> 8;
data_in[1] = (RealSize - 2) >> 0;
DoSimpleDataIn(data_in, (AllocSize < RealSize) ? AllocSize : RealSize);
}
static void DoREADCDCAP10(const uint8 *cdb)
{
bool pmi = cdb[8] & 0x1;
uint32 lba = MDFN_de32msb(cdb + 0x2);
uint32 ret_lba;
uint32 ret_bl;
uint8 data_in[8];
memset(data_in, 0, sizeof(data_in));
if(lba > 0x05FF69)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
ret_lba = toc.tracks[100].lba - 1;
if(pmi)
{
if(lba >= toc.tracks[100].lba)
ret_lba = toc.tracks[100].lba - 1;
else if(lba < toc.tracks[toc.first_track].lba)
ret_lba = toc.tracks[toc.first_track].lba - 1;
else
{
const int track = toc.FindTrackByLBA(lba);
for(int st = track + 1; st <= toc.last_track; st++)
{
if((toc.tracks[st].control ^ toc.tracks[track].control) & 0x4)
{
ret_lba = toc.tracks[st].lba - 1;
break;
}
}
}
}
ret_bl = 2048;
MDFN_en32msb(&data_in[0], ret_lba);
MDFN_en32msb(&data_in[4], ret_bl);
cdda.CDDAStatus = CDDASTATUS_STOPPED;
DoSimpleDataIn(data_in, 8);
}
static void DoREADHEADER10(const uint8 *cdb)
{
uint8 data_in[8192];
bool WantInMSF = cdb[1] & 0x2;
uint32 HeaderLBA = MDFN_de32msb(cdb + 0x2);
int AllocSize = MDFN_de16msb(cdb + 0x7);
uint8 raw_buf[2352 + 96];
uint8 mode;
int m, s, f;
uint32 lba;
if(!AllocSize)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
if(HeaderLBA >= toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(HeaderLBA < toc.tracks[toc.first_track].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
Cur_CDIF->ReadRawSector(raw_buf, HeaderLBA);
if(!ValidateRawDataSector(raw_buf, HeaderLBA))
return;
m = BCD_to_U8(raw_buf[12 + 0]);
s = BCD_to_U8(raw_buf[12 + 1]);
f = BCD_to_U8(raw_buf[12 + 2]);
mode = raw_buf[12 + 3];
lba = AMSF_to_LBA(m, s, f);
data_in[0] = mode;
data_in[1] = 0;
data_in[2] = 0;
data_in[3] = 0;
if(WantInMSF)
{
data_in[4] = 0;
data_in[5] = m;
data_in[6] = s;
data_in[7] = f;
}
else
{
data_in[4] = lba >> 24;
data_in[5] = lba >> 16;
data_in[6] = lba >> 8;
data_in[7] = lba >> 0;
}
cdda.CDDAStatus = CDDASTATUS_STOPPED;
DoSimpleDataIn(data_in, 8);
}
static void DoNEC_SST(const uint8 *cdb)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoPABase(const uint32 lba, const uint32 length, unsigned int status = CDDASTATUS_PLAYING, unsigned int mode = PLAYMODE_NORMAL)
{
if(lba > toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(lba < toc.tracks[toc.first_track].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(!length)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
else
{
if(toc.tracks[toc.FindTrackByLBA(lba)].control & 0x04)
{
CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_AUDIO_TRACK);
return;
}
cdda.CDDAReadPos = 588;
read_sec = read_sec_start = lba;
read_sec_end = read_sec_start + length;
cdda.CDDAStatus = status;
cdda.PlayMode = mode;
if(read_sec < toc.tracks[100].lba)
{
Cur_CDIF->HintReadSector(read_sec);
}
}
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoNEC_SAPSP(const uint8 *cdb)
{
uint32 lba;
switch (cdb[9] & 0xc0)
{
default:
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
break;
case 0x00:
lba = MDFN_de24msb(&cdb[3]);
break;
case 0x40:
{
uint8 m, s, f;
if(!BCD_to_U8_check(cdb[2], &m) || !BCD_to_U8_check(cdb[3], &s) || !BCD_to_U8_check(cdb[4], &f))
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
lba = AMSF_to_LBA(m, s, f);
}
break;
case 0x80:
{
uint8 track;
if(!cdb[2] || !BCD_to_U8_check(cdb[2], &track))
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(track == toc.last_track + 1)
track = 100;
else if(track > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
lba = toc.tracks[track].lba;
}
break;
}
if(cdb[1] & 0x01)
DoPABase(lba, toc.tracks[100].lba - lba, CDDASTATUS_PLAYING, PLAYMODE_NORMAL);
else
DoPABase(lba, toc.tracks[100].lba - lba, CDDASTATUS_PAUSED, PLAYMODE_SILENT);
}
static void DoNEC_SAPEP(const uint8 *cdb)
{
uint32 lba;
if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
return;
}
switch (cdb[9] & 0xc0)
{
default:
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
break;
case 0x00:
lba = MDFN_de24msb(&cdb[3]);
break;
case 0x40:
{
uint8 m, s, f;
if(!BCD_to_U8_check(cdb[2], &m) || !BCD_to_U8_check(cdb[3], &s) || !BCD_to_U8_check(cdb[4], &f))
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
lba = AMSF_to_LBA(m, s, f);
}
break;
case 0x80:
{
uint8 track;
if(!cdb[2] || !BCD_to_U8_check(cdb[2], &track))
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(track == toc.last_track + 1)
track = 100;
else if(track > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
lba = toc.tracks[track].lba;
}
break;
}
switch(cdb[1] & 0x7)
{
case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
break;
case 0x04: cdda.PlayMode = PLAYMODE_LOOP;
break;
default: cdda.PlayMode = PLAYMODE_NORMAL;
break;
}
cdda.CDDAStatus = CDDASTATUS_PLAYING;
read_sec_end = lba;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoPA10(const uint8 *cdb)
{
const uint32 lba = MDFN_de32msb(cdb + 0x2);
const uint16 length = MDFN_de16msb(cdb + 0x7);
DoPABase(lba, length);
}
static void DoPA12(const uint8 *cdb)
{
const uint32 lba = MDFN_de32msb(cdb + 0x2);
const uint32 length = MDFN_de32msb(cdb + 0x6);
DoPABase(lba, length);
}
static void DoPAMSF(const uint8 *cdb)
{
int32 lba_start, lba_end;
lba_start = AMSF_to_LBA(cdb[3], cdb[4], cdb[5]);
lba_end = AMSF_to_LBA(cdb[6], cdb[7], cdb[8]);
if(lba_start < 0 || lba_end < 0 || lba_start >= (int32)toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
if(lba_start == lba_end)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
else if(lba_start > lba_end)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS);
return;
}
cdda.CDDAReadPos = 588;
read_sec = read_sec_start = lba_start;
read_sec_end = lba_end;
cdda.CDDAStatus = CDDASTATUS_PLAYING;
cdda.PlayMode = PLAYMODE_NORMAL;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoPATI(const uint8 *cdb)
{
int StartTrack = cdb[4];
int EndTrack = cdb[7];
if(!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
DoPABase(toc.tracks[StartTrack].lba, toc.tracks[EndTrack].lba - toc.tracks[StartTrack].lba);
}
static void DoPATRBase(const uint32 lba, const uint32 length)
{
if(lba >= toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(lba < toc.tracks[toc.first_track].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(!length)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
else
{
if(toc.tracks[toc.FindTrackByLBA(lba)].control & 0x04)
{
CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_AUDIO_TRACK);
return;
}
cdda.CDDAReadPos = 588;
read_sec = read_sec_start = lba;
read_sec_end = read_sec_start + length;
cdda.CDDAStatus = CDDASTATUS_PLAYING;
cdda.PlayMode = PLAYMODE_NORMAL;
}
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoPATR10(const uint8 *cdb)
{
const int32 rel_lba = MDFN_de32msb(cdb + 0x2);
const int StartTrack = cdb[6];
const uint16 length = MDFN_de16msb(cdb + 0x7);
if(!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
DoPATRBase(toc.tracks[StartTrack].lba + rel_lba, length);
}
static void DoPATR12(const uint8 *cdb)
{
const int32 rel_lba = MDFN_de32msb(cdb + 0x2);
const int StartTrack = cdb[10];
const uint32 length = MDFN_de32msb(cdb + 0x6);
if(!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
DoPATRBase(toc.tracks[StartTrack].lba + rel_lba, length);
}
static void DoPAUSERESUME(const uint8 *cdb)
{
if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
return;
}
if(cdb[8] & 1)
cdda.CDDAStatus = CDDASTATUS_PLAYING;
else
cdda.CDDAStatus = CDDASTATUS_PAUSED;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoREADBase(uint32 sa, uint32 sc)
{
int track;
if(sa > toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
if((track = toc.FindTrackByLBA(sa)) == 0)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
if(!(toc.tracks[track].control) & 0x4)
{
CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_DATA_TRACK);
return;
}
if(!sc && sa == toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_HEADER_READ_ERROR);
return;
}
if(SCSILog)
{
int Track = toc.FindTrackByLBA(sa);
uint32 Offset = sa - toc.tracks[Track].lba;
SCSILog("SCSI", "Read: start=0x%08x(track=%d, offs=0x%08x), cnt=0x%08x", sa, Track, Offset, sc);
}
SectorAddr = sa;
SectorCount = sc;
if(SectorCount)
{
Cur_CDIF->HintReadSector(sa);
CDReadTimer = (uint64)1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
}
else
{
CDReadTimer = 0;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
cdda.CDDAStatus = CDDASTATUS_STOPPED;
}
static void DoREAD6(const uint8 *cdb)
{
uint32 sa = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | (cdb[3] << 0);
uint32 sc = cdb[4];
if(!sc)
{
sc = 256;
}
DoREADBase(sa, sc);
}
static void DoREAD10(const uint8 *cdb)
{
uint32 sa = MDFN_de32msb(cdb + 0x2);
uint32 sc = MDFN_de16msb(cdb + 0x7);
DoREADBase(sa, sc);
}
static void DoREAD12(const uint8 *cdb)
{
uint32 sa = MDFN_de32msb(cdb + 0x2);
uint32 sc = MDFN_de32msb(cdb + 0x6);
DoREADBase(sa, sc);
}
static void DoPREFETCH(const uint8 *cdb)
{
uint32 lba = MDFN_de32msb(cdb + 0x2);
if(lba >= toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
SendStatusAndMessage(STATUS_CONDITION_MET, 0x00);
}
static void DoSEEKBase(uint32 lba)
{
if(lba >= toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
return;
}
cdda.CDDAStatus = CDDASTATUS_STOPPED;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoSEEK6(const uint8 *cdb)
{
uint32 lba = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | cdb[3];
DoSEEKBase(lba);
}
static void DoSEEK10(const uint8 *cdb)
{
uint32 lba = MDFN_de32msb(cdb + 0x2);
DoSEEKBase(lba);
}
static void DoREADSUBCHANNEL(const uint8 *cdb)
{
uint8 data_in[8192];
int DataFormat = cdb[3];
int TrackNum = cdb[6];
unsigned AllocSize = (cdb[7] << 8) | cdb[8];
bool WantQ = cdb[2] & 0x40;
bool WantMSF = cdb[1] & 0x02;
uint32 offset = 0;
if(!AllocSize)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
return;
}
if(DataFormat > 0x3)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
if(DataFormat == 0x3 && (TrackNum < toc.first_track || TrackNum > toc.last_track))
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
return;
}
data_in[offset++] = 0;
if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
data_in[offset++] = 0x11;
else if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
data_in[offset++] = 0x12;
else
data_in[offset++] = 0x13;
data_in[offset++] = 0x00;
data_in[offset++] = 0x00;
if(WantQ)
{
data_in[offset++] = DataFormat;
if(!DataFormat || DataFormat == 0x01)
{
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
data_in[offset++] = ((SubQBuf[0] & 0x0F) << 4) | ((SubQBuf[0] & 0xF0) >> 4);
data_in[offset++] = SubQBuf[1];
data_in[offset++] = SubQBuf[2];
if(WantMSF)
{
data_in[offset++] = 0;
data_in[offset++] = BCD_to_U8(SubQBuf[7]);
data_in[offset++] = BCD_to_U8(SubQBuf[8]);
data_in[offset++] = BCD_to_U8(SubQBuf[9]);
}
else
{
uint32 tmp_lba = BCD_to_U8(SubQBuf[7]) * 60 * 75 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150;
data_in[offset++] = tmp_lba >> 24;
data_in[offset++] = tmp_lba >> 16;
data_in[offset++] = tmp_lba >> 8;
data_in[offset++] = tmp_lba >> 0;
}
if(WantMSF)
{
data_in[offset++] = 0;
data_in[offset++] = BCD_to_U8(SubQBuf[3]);
data_in[offset++] = BCD_to_U8(SubQBuf[4]);
data_in[offset++] = BCD_to_U8(SubQBuf[5]);
}
else
{
uint32 tmp_lba = BCD_to_U8(SubQBuf[3]) * 60 * 75 + BCD_to_U8(SubQBuf[4]) * 75 + BCD_to_U8(SubQBuf[5]);
data_in[offset++] = tmp_lba >> 24;
data_in[offset++] = tmp_lba >> 16;
data_in[offset++] = tmp_lba >> 8;
data_in[offset++] = tmp_lba >> 0;
}
}
if(!DataFormat || DataFormat == 0x02)
{
if(DataFormat == 0x02)
{
data_in[offset++] = 0x00;
data_in[offset++] = 0x00;
data_in[offset++] = 0x00;
}
data_in[offset++] = 0x00;
for(int i = 0; i < 15; i++)
data_in[offset++] = 0x00;
}
if(!DataFormat || DataFormat == 0x03)
{
if(DataFormat == 0x03)
{
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
data_in[offset++] = ((SubQBuf[0] & 0x0F) << 4) | ((SubQBuf[0] & 0xF0) >> 4);
data_in[offset++] = TrackNum;
data_in[offset++] = 0x00;
}
data_in[offset++] = 0x00;
for(int i = 0; i < 15; i++)
data_in[offset++] = 0x00;
}
}
MDFN_en16msb(&data_in[0x2], offset - 0x4);
DoSimpleDataIn(data_in, (AllocSize > offset) ? offset : AllocSize);
}
static void DoNEC_READSUBQ(const uint8 *cdb)
{
uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
uint8 data_in[10];
const uint8 alloc_size = (cdb[1] < 10) ? cdb[1] : 10;
memset(data_in, 0x00, 10);
if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
data_in[0] = 2;
else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
data_in[0] = 0;
else
data_in[0] = 3;
data_in[1] = SubQBuf[0];
data_in[2] = SubQBuf[1];
data_in[3] = SubQBuf[2];
data_in[4] = SubQBuf[3];
data_in[5] = SubQBuf[4];
data_in[6] = SubQBuf[5];
data_in[7] = SubQBuf[7];
data_in[8] = SubQBuf[8];
data_in[9] = SubQBuf[9];
DoSimpleDataIn(data_in, alloc_size);
}
static void DoTESTUNITREADY(const uint8 *cdb)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoNEC_PAUSE(const uint8 *cdb)
{
if(cdda.CDDAStatus != CDDASTATUS_STOPPED)
{
cdda.CDDAStatus = CDDASTATUS_PAUSED;
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
else
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
}
}
static void DoNEC_SCAN(const uint8 *cdb)
{
uint32 sector_tmp = 0;
switch (cdb[9] & 0xc0)
{
default:
break;
case 0x00:
sector_tmp = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
break;
case 0x40:
sector_tmp = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
break;
case 0x80:
sector_tmp = toc.tracks[BCD_to_U8(cdb[2])].lba;
break;
}
cdda.ScanMode = cdb[1] & 0x3;
cdda.scan_sec_end = sector_tmp;
if(cdda.CDDAStatus != CDDASTATUS_STOPPED)
{
if(cdda.ScanMode)
{
cdda.CDDAStatus = CDDASTATUS_SCANNING;
}
}
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
static void DoPREVENTALLOWREMOVAL(const uint8 *cdb)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_REQUEST_IN_CDB);
}
#include "scsicd-pce-commands.inc"
#define SCF_REQUIRES_MEDIUM 0x0001
#define SCF_INCOMPLETE 0x4000
#define SCF_UNTESTED 0x8000
typedef struct
{
uint8 cmd;
uint32 flags;
void (*func)(const uint8 *cdb);
const char *pretty_name;
const char *format_string;
} SCSICH;
static const int32 RequiredCDBLen[16] =
{
6,
6,
10,
10,
10,
10,
10,
10,
10,
10,
12,
12,
10,
10,
10,
10,
};
static SCSICH PCFXCommandDefs[] =
{
{ 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" },
{ 0x01, 0, DoREZEROUNIT, "Rezero Unit" },
{ 0x03, 0, DoREQUESTSENSE, "Request Sense" },
{ 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" },
{ 0x0B, SCF_REQUIRES_MEDIUM, DoSEEK6, "Seek(6)" },
{ 0x0D, 0, DoNEC_NOP, "No Operation" },
{ 0x12, 0, DoINQUIRY, "Inquiry" },
{ 0x15, 0, DoMODESELECT6, "Mode Select(6)" },
{ 0x1A, 0, DoMODESENSE6, "Mode Sense(6)" },
{ 0x1B, SCF_REQUIRES_MEDIUM, DoSTARTSTOPUNIT6, "Start/Stop Unit" },
{ 0x1E, 0, DoPREVENTALLOWREMOVAL, "Prevent/Allow Media Removal" },
{ 0x25, SCF_REQUIRES_MEDIUM, DoREADCDCAP10, "Read CD-ROM Capacity" },
{ 0x28, SCF_REQUIRES_MEDIUM, DoREAD10, "Read(10)" },
{ 0x2B, SCF_REQUIRES_MEDIUM, DoSEEK10, "Seek(10)" },
{ 0x34, SCF_REQUIRES_MEDIUM, DoPREFETCH, "Prefetch" },
{ 0x42, SCF_REQUIRES_MEDIUM, DoREADSUBCHANNEL, "Read Subchannel" },
{ 0x43, SCF_REQUIRES_MEDIUM, DoREADTOC, "Read TOC" },
{ 0x44, SCF_REQUIRES_MEDIUM, DoREADHEADER10, "Read Header" },
{ 0x45, SCF_REQUIRES_MEDIUM, DoPA10, "Play Audio(10)" },
{ 0x47, SCF_REQUIRES_MEDIUM, DoPAMSF, "Play Audio MSF" },
{ 0x48, SCF_REQUIRES_MEDIUM, DoPATI, "Play Audio Track Index" },
{ 0x49, SCF_REQUIRES_MEDIUM, DoPATR10, "Play Audio Track Relative(10)" },
{ 0x4B, SCF_REQUIRES_MEDIUM, DoPAUSERESUME, "Pause/Resume" },
{ 0xA5, SCF_REQUIRES_MEDIUM, DoPA12, "Play Audio(12)" },
{ 0xA8, SCF_REQUIRES_MEDIUM, DoREAD12, "Read(12)" },
{ 0xA9, SCF_REQUIRES_MEDIUM, DoPATR12, "Play Audio Track Relative(12)" },
{ 0xD2, SCF_REQUIRES_MEDIUM, DoNEC_SCAN, "Scan" },
{ 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_SAPSP, "Set Audio Playback Start Position" },
{ 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_SAPEP, "Set Audio Playback End Position" },
{ 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PAUSE, "Pause" },
{ 0xDB, SCF_REQUIRES_MEDIUM | SCF_UNTESTED, DoNEC_SST, "Set Stop Time" },
{ 0xDC, SCF_REQUIRES_MEDIUM, DoNEC_EJECT, "Eject" },
{ 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_READSUBQ, "Read Subchannel Q" },
{ 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_GETDIRINFO, "Get Dir Info" },
{ 0xFF, 0, 0, NULL, NULL },
};
static SCSICH PCECommandDefs[] =
{
{ 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" },
{ 0x03, 0, DoREQUESTSENSE, "Request Sense" },
{ 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" },
{ 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPSP, "Set Audio Playback Start Position" },
{ 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPEP, "Set Audio Playback End Position" },
{ 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PCE_PAUSE, "Pause" },
{ 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_PCE_READSUBQ, "Read Subchannel Q" },
{ 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_PCE_GETDIRINFO, "Get Dir Info" },
{ 0xFF, 0, 0, NULL, NULL },
};
void SCSICD_ResetTS(uint32 ts_base)
{
lastts = ts_base;
}
void SCSICD_GetCDDAValues(int16 &left, int16 &right)
{
if(cdda.CDDAStatus)
{
left = cdda.sr[0];
right = cdda.sr[1];
}
else
left = right = 0;
}
#define CDDA_FILTER_NUMCONVOLUTIONS 7
#define CDDA_FILTER_NUMCONVOLUTIONS_PADDED 8
#define CDDA_FILTER_NUMPHASES_SHIFT 6
#define CDDA_FILTER_NUMPHASES (1 << CDDA_FILTER_NUMPHASES_SHIFT)
alignas(16) static const int16 CDDA_Filter[1 + CDDA_FILTER_NUMPHASES + 1][CDDA_FILTER_NUMCONVOLUTIONS_PADDED] =
{
#include "scsicd_cdda_filter.inc"
};
alignas(16) static const int16 OversampleFilter[2][0x10] =
{
{ -82, 217, -463, 877, -1562, 2783, -5661, 29464, 9724, -3844, 2074, -1176, 645, -323, 138, -43, },
{ -43, 138, -323, 645, -1176, 2074, -3844, 9724, 29464, -5661, 2783, -1562, 877, -463, 217, -82, },
};
static INLINE void RunCDDA(uint32 system_timestamp, int32 run_time)
{
if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
{
cdda.CDDADiv -= (int64)run_time << 20;
while(cdda.CDDADiv <= 0)
{
const uint32 synthtime_ex = (((uint64)system_timestamp << 20) + (int64)cdda.CDDADiv) / cdda.CDDATimeDiv;
const int synthtime = (synthtime_ex >> 16) & 0xFFFF;
const int synthtime_phase = (int)(synthtime_ex & 0xFFFF) - 0x80;
const int synthtime_phase_int = synthtime_phase >> (16 - CDDA_FILTER_NUMPHASES_SHIFT);
const int synthtime_phase_fract = synthtime_phase & ((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - 1);
int32 sample_va[2];
cdda.CDDADiv += cdda.CDDADivAcc;
if(!(cdda.OversamplePos & 1))
{
if(cdda.CDDAReadPos == 588)
{
if(read_sec >= read_sec_end || (cdda.CDDAStatus == CDDASTATUS_SCANNING && read_sec == cdda.scan_sec_end))
{
switch(cdda.PlayMode)
{
case PLAYMODE_SILENT:
case PLAYMODE_NORMAL:
cdda.CDDAStatus = CDDASTATUS_STOPPED;
break;
case PLAYMODE_INTERRUPT:
cdda.CDDAStatus = CDDASTATUS_STOPPED;
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
break;
case PLAYMODE_LOOP:
read_sec = read_sec_start;
break;
}
if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
break;
}
if(read_sec >= toc.tracks[100].lba)
{
cdda.CDDAStatus = CDDASTATUS_STOPPED;
break;
}
if(TrayOpen || !Cur_CDIF)
{
cdda.CDDAStatus = CDDASTATUS_STOPPED;
#if 0
cd.data_transfer_done = FALSE;
cd.key_pending = SENSEKEY_NOT_READY;
cd.asc_pending = ASC_MEDIUM_NOT_PRESENT;
cd.ascq_pending = 0x00;
cd.fru_pending = 0x00;
SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
#endif
break;
}
cdda.CDDAReadPos = 0;
{
uint8 tmpbuf[2352 + 96];
Cur_CDIF->ReadRawSector(tmpbuf, read_sec);
for(int i = 0; i < 588 * 2; i++)
cdda.CDDASectorBuffer[i] = MDFN_de16lsb(&tmpbuf[i * 2]);
memcpy(cd.SubPWBuf, tmpbuf + 2352, 96);
}
GenSubQFromSubPW();
if(!(cd.SubQBuf_Last[0] & 0x10))
{
memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
}
if(cdda.CDDAStatus == CDDASTATUS_SCANNING)
{
int64 tmp_read_sec = read_sec;
if(cdda.ScanMode & 1)
{
tmp_read_sec -= 24;
if(tmp_read_sec < cdda.scan_sec_end)
tmp_read_sec = cdda.scan_sec_end;
}
else
{
tmp_read_sec += 24;
if(tmp_read_sec > cdda.scan_sec_end)
tmp_read_sec = cdda.scan_sec_end;
}
read_sec = tmp_read_sec;
}
else
read_sec++;
}
if(!(cdda.CDDAReadPos % 6))
{
int subindex = cdda.CDDAReadPos / 6 - 2;
if(subindex >= 0)
CDStuffSubchannels(cd.SubPWBuf[subindex], subindex);
else
CDStuffSubchannels(0x00, subindex);
}
if(!(cd.SubQBuf_Last[0] & 0x40) && cdda.PlayMode != PLAYMODE_SILENT)
{
cdda.sr[0] = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + cdda.OutPortChSelectCache[0]];
cdda.sr[1] = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + cdda.OutPortChSelectCache[1]];
}
#if 0
{
static int16 wv = 0x7FFF;
static unsigned counter = 0;
static double phase = 0;
static double phase_inc = 0;
static const double phase_inc_inc = 0.000003 / 2;
cdda.sr[0] = 32767 * sin(phase);
cdda.sr[1] = 32767 * sin(phase);
if(counter == 0)
wv = -wv;
counter = (counter + 1) & 1;
phase += phase_inc;
phase_inc += phase_inc_inc;
}
#endif
{
const unsigned obwp = cdda.OversamplePos >> 1;
cdda.OversampleBuffer[0][obwp] = cdda.OversampleBuffer[0][0x10 + obwp] = cdda.sr[0];
cdda.OversampleBuffer[1][obwp] = cdda.OversampleBuffer[1][0x10 + obwp] = cdda.sr[1];
}
cdda.CDDAReadPos++;
}
{
const int16* f = OversampleFilter[cdda.OversamplePos & 1];
#if defined(__SSE2__)
__m128i f0 = _mm_load_si128((__m128i *)&f[0]);
__m128i f1 = _mm_load_si128((__m128i *)&f[8]);
#endif
for(unsigned lr = 0; lr < 2; lr++)
{
const int16* b = &cdda.OversampleBuffer[lr][((cdda.OversamplePos >> 1) + 1) & 0xF];
#if defined(__SSE2__)
union
{
int32 accum;
float accum_f;
};
{
__m128i b0;
__m128i b1;
__m128i sum;
b0 = _mm_loadu_si128((__m128i *)&b[0]);
b1 = _mm_loadu_si128((__m128i *)&b[8]);
sum = _mm_add_epi32(_mm_madd_epi16(f0, b0), _mm_madd_epi16(f1, b1));
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2) | (3 << 4) | (2 << 6)));
_mm_store_ss(&accum_f, (__m128)sum);
}
#else
int32 accum = 0;
for(unsigned i = 0; i < 0x10; i++)
accum += f[i] * b[i];
#endif
sample_va[lr] = ((int64)accum * cdda.OutPortVolumeCache[lr]) >> 16;
}
}
if(MDFN_UNLIKELY(cd.SubQBuf_Last[0] & 0x10))
{
for(unsigned lr = 0; lr < 2; lr++)
{
float inv = sample_va[lr] * 0.35971507338824012f;
cdda.DeemphState[lr][1] = (cdda.DeemphState[lr][0] - 0.4316395666f * inv) + (0.7955522347f * cdda.DeemphState[lr][1]);
cdda.DeemphState[lr][0] = inv;
sample_va[lr] = std::max<float>(-2147483648.0, std::min<float>(2147483647.0, cdda.DeemphState[lr][1]));
}
}
if(HRBufs[0] && HRBufs[1])
{
#define FINAL_OUT_SHIFT 32
#define MULT_SHIFT_ADJ (32 - (26 + (8 - CDDA_FILTER_NUMPHASES_SHIFT)))
#if (((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - 0) << MULT_SHIFT_ADJ) > 32767
#error "COEFF MULT OVERFLOW"
#endif
const int16 mult_a = ((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - synthtime_phase_fract) << MULT_SHIFT_ADJ;
const int16 mult_b = synthtime_phase_fract << MULT_SHIFT_ADJ;
int32 coeff[CDDA_FILTER_NUMCONVOLUTIONS];
for(unsigned c = 0; c < CDDA_FILTER_NUMCONVOLUTIONS; c++)
{
coeff[c] = (CDDA_Filter[1 + synthtime_phase_int + 0][c] * mult_a +
CDDA_Filter[1 + synthtime_phase_int + 1][c] * mult_b);
}
int32* tb0 = &HRBufs[0][synthtime];
int32* tb1 = &HRBufs[1][synthtime];
for(unsigned c = 0; c < CDDA_FILTER_NUMCONVOLUTIONS; c++)
{
tb0[c] += ((int64)coeff[c] * sample_va[0]) >> FINAL_OUT_SHIFT;
tb1[c] += ((int64)coeff[c] * sample_va[1]) >> FINAL_OUT_SHIFT;
}
#undef FINAL_OUT_SHIFT
#undef MULT_SHIFT_ADJ
}
cdda.OversamplePos = (cdda.OversamplePos + 1) & 0x1F;
}
}
}
static INLINE void RunCDRead(uint32 system_timestamp, int32 run_time)
{
if(CDReadTimer > 0)
{
CDReadTimer -= run_time;
if(CDReadTimer <= 0)
{
if(din->CanWrite() < ((WhichSystem == SCSICD_PCFX) ? 2352 : 2048))
{
CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
}
else
{
uint8 tmp_read_buf[2352 + 96];
if(TrayOpen)
{
din->Flush();
cd.data_transfer_done = FALSE;
CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
}
else if(!Cur_CDIF)
{
CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
}
else if(SectorAddr >= toc.tracks[100].lba)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
}
else if(!Cur_CDIF->ReadRawSector(tmp_read_buf, SectorAddr))
{
cd.data_transfer_done = FALSE;
CommandCCError(SENSEKEY_ILLEGAL_REQUEST);
}
else if(ValidateRawDataSector(tmp_read_buf, SectorAddr))
{
memcpy(cd.SubPWBuf, tmp_read_buf + 2352, 96);
if(tmp_read_buf[12 + 3] == 0x2)
din->Write(tmp_read_buf + 24, 2048);
else
din->Write(tmp_read_buf + 16, 2048);
GenSubQFromSubPW();
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_READY);
SectorAddr++;
SectorCount--;
if(CurrentPhase != PHASE_DATA_IN)
ChangePhase(PHASE_DATA_IN);
if(SectorCount)
{
cd.data_transfer_done = FALSE;
CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
}
else
{
cd.data_transfer_done = TRUE;
}
}
}
}
}
}
uint32 SCSICD_Run(scsicd_timestamp_t system_timestamp)
{
int32 run_time = system_timestamp - lastts;
if(system_timestamp < lastts)
{
fprintf(stderr, "Meow: %d %d\n", system_timestamp, lastts);
assert(system_timestamp >= lastts);
}
monotonic_timestamp += run_time;
lastts = system_timestamp;
RunCDRead(system_timestamp, run_time);
RunCDDA(system_timestamp, run_time);
bool ResetNeeded = false;
if(RST_signal && !cd.last_RST_signal)
ResetNeeded = true;
cd.last_RST_signal = RST_signal;
if(ResetNeeded)
{
VirtualReset();
}
else if(CurrentPhase == PHASE_BUS_FREE)
{
if(SEL_signal)
{
if(WhichSystem == SCSICD_PCFX)
{
{
ChangePhase(PHASE_COMMAND);
}
}
else
{
ChangePhase(PHASE_COMMAND);
}
}
}
else if(ATN_signal && !REQ_signal && !ACK_signal)
{
ChangePhase(PHASE_MESSAGE_OUT);
}
else switch(CurrentPhase)
{
case PHASE_COMMAND:
if(REQ_signal && ACK_signal)
{
cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB;
SetREQ(FALSE);
}
if(!REQ_signal && !ACK_signal && cd.command_buffer_pos)
{
if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
{
const SCSICH *cmd_info_ptr;
if(WhichSystem == SCSICD_PCFX)
cmd_info_ptr = PCFXCommandDefs;
else
cmd_info_ptr = PCECommandDefs;
while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0])
cmd_info_ptr++;
if(SCSILog)
{
char log_buffer[1024];
int lb_pos;
log_buffer[0] = 0;
lb_pos = trio_snprintf(log_buffer, 1024, "Command: %02x, %s%s ", cd.command_buffer[0], cmd_info_ptr->pretty_name ? cmd_info_ptr->pretty_name : "!!BAD COMMAND!!",
(cmd_info_ptr->flags & SCF_UNTESTED) ? "(UNTESTED)" : "");
for(int i = 0; i < RequiredCDBLen[cd.command_buffer[0] >> 4]; i++)
lb_pos += trio_snprintf(log_buffer + lb_pos, 1024 - lb_pos, "%02x ", cd.command_buffer[i]);
SCSILog("SCSI", "%s", log_buffer);
}
if(cmd_info_ptr->pretty_name == NULL)
{
CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND);
if(SCSILog)
SCSILog("SCSI", "Bad Command: %02x", cd.command_buffer[0]);
cd.command_buffer_pos = 0;
}
else
{
if(cmd_info_ptr->flags & SCF_UNTESTED)
{
}
if(TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
{
CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
}
else if(!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
{
CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
}
else if(cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
{
CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED);
cd.DiscChanged = false;
}
else
{
bool prev_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING);
cmd_info_ptr->func(cd.command_buffer);
bool new_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING);
if(!prev_ps && new_ps)
{
memset(cdda.sr, 0, sizeof(cdda.sr));
memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer));
memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
}
}
cd.command_buffer_pos = 0;
}
}
else
SetREQ(TRUE);
}
break;
case PHASE_DATA_OUT:
if(REQ_signal && ACK_signal)
{
cd.data_out[cd.data_out_pos++] = cd_bus.DB;
SetREQ(FALSE);
}
else if(!REQ_signal && !ACK_signal && cd.data_out_pos)
{
if(cd.data_out_pos == cd.data_out_want)
{
cd.data_out_pos = 0;
if(cd.command_buffer[0] == 0x15)
FinishMODESELECT6(cd.data_out, cd.data_out_want);
else
SendStatusAndMessage(STATUS_GOOD, 0x00);
}
else
SetREQ(TRUE);
}
break;
case PHASE_MESSAGE_OUT:
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
if(1)
{
din->Flush();
cd.data_out_pos = cd.data_out_want = 0;
CDReadTimer = 0;
cdda.CDDAStatus = CDDASTATUS_STOPPED;
ChangePhase(PHASE_BUS_FREE);
}
}
break;
case PHASE_STATUS:
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
cd.status_sent = TRUE;
}
if(!REQ_signal && !ACK_signal && cd.status_sent)
{
cd.status_sent = FALSE;
cd_bus.DB = cd.message_pending;
ChangePhase(PHASE_MESSAGE_IN);
}
break;
case PHASE_DATA_IN:
if(!REQ_signal && !ACK_signal)
{
if(din->CanRead() == 0)
{
CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_READY);
if(cd.data_transfer_done)
{
SendStatusAndMessage(STATUS_GOOD, 0x00);
cd.data_transfer_done = FALSE;
CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
}
}
else
{
cd_bus.DB = din->ReadByte();
SetREQ(TRUE);
}
}
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
}
break;
case PHASE_MESSAGE_IN:
if(REQ_signal && ACK_signal)
{
SetREQ(FALSE);
cd.message_sent = TRUE;
}
if(!REQ_signal && !ACK_signal && cd.message_sent)
{
cd.message_sent = FALSE;
ChangePhase(PHASE_BUS_FREE);
}
break;
}
int32 next_time = 0x7fffffff;
if(CDReadTimer > 0 && CDReadTimer < next_time)
next_time = CDReadTimer;
if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
{
int32 cdda_div_sexytime = (cdda.CDDADiv + (cdda.CDDADivAcc * (cdda.OversamplePos & 1)) + ((1 << 20) - 1)) >> 20;
if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time)
next_time = cdda_div_sexytime;
}
assert(next_time >= 0);
return(next_time);
}
void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...))
{
SCSILog = logfunc;
}
void SCSICD_SetTransferRate(uint32 TransferRate)
{
CD_DATA_TRANSFER_RATE = TransferRate;
}
void SCSICD_Close(void)
{
if(din)
{
delete din;
din = NULL;
}
}
void SCSICD_Init(int type, int cdda_time_div, int32* left_hrbuf, int32* right_hrbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int))
{
Cur_CDIF = NULL;
TrayOpen = true;
assert(SystemClock < 30000000);
monotonic_timestamp = 0;
lastts = 0;
SCSILog = NULL;
if(type == SCSICD_PCFX)
din = new SimpleFIFO<uint8>(65536);
else
din = new SimpleFIFO<uint8>(2048);
WhichSystem = type;
cdda.CDDADivAcc = (int64)System_Clock * (1024 * 1024) / 88200;
cdda.CDDADivAccVolFudge = 100;
cdda.CDDATimeDiv = cdda_time_div * (1 << (4 + 2));
cdda.CDDAVolume[0] = 65536;
cdda.CDDAVolume[1] = 65536;
FixOPV();
HRBufs[0] = left_hrbuf;
HRBufs[1] = right_hrbuf;
CD_DATA_TRANSFER_RATE = TransferRate;
System_Clock = SystemClock;
CDIRQCallback = IRQFunc;
CDStuffSubchannels = SSCFunc;
}
void SCSICD_SetCDDAVolume(double left, double right)
{
cdda.CDDAVolume[0] = 65536 * left;
cdda.CDDAVolume[1] = 65536 * right;
for(int i = 0; i < 2; i++)
{
if(cdda.CDDAVolume[i] > 65536)
{
printf("[SCSICD] Debug Warning: CD-DA volume %d too large: %d\n", i, cdda.CDDAVolume[i]);
cdda.CDDAVolume[i] = 65536;
}
}
FixOPV();
}
void SCSICD_StateAction(StateMem* sm, const unsigned load, const bool data_only, const char *sname)
{
SFORMAT StateRegs[] =
{
SFVARN(cd_bus.DB, "DB"),
SFVARN(cd_bus.signals, "Signals"),
SFVAR(CurrentPhase),
SFVARN(cd.last_RST_signal, "last_RST"),
SFVARN(cd.message_pending, "message_pending"),
SFVARN(cd.status_sent, "status_sent"),
SFVARN(cd.message_sent, "message_sent"),
SFVARN(cd.key_pending, "key_pending"),
SFVARN(cd.asc_pending, "asc_pending"),
SFVARN(cd.ascq_pending, "ascq_pending"),
SFVARN(cd.fru_pending, "fru_pending"),
SFARRAYN(cd.command_buffer, 256, "command_buffer"),
SFVARN(cd.command_buffer_pos, "command_buffer_pos"),
SFVARN(cd.command_size_left, "command_size_left"),
SFARRAYN(&din->data[0], din->data.size(), "din_fifo"),
SFVARN(din->read_pos, "din_read_pos"),
SFVARN(din->in_count, "din_in_count"),
SFVARN(cd.data_transfer_done, "data_transfer_done"),
SFARRAYN(cd.data_out, sizeof(cd.data_out), "data_out"),
SFVARN(cd.data_out_pos, "data_out_pos"),
SFVARN(cd.data_out_want, "data_out_want"),
SFVARN(cd.DiscChanged, "DiscChanged"),
SFVAR(cdda.PlayMode),
SFARRAY16(cdda.CDDASectorBuffer, 1176),
SFVAR(cdda.CDDAReadPos),
SFVAR(cdda.CDDAStatus),
SFVAR(cdda.CDDADiv),
SFVAR(read_sec_start),
SFVAR(read_sec),
SFVAR(read_sec_end),
SFVAR(CDReadTimer),
SFVAR(SectorAddr),
SFVAR(SectorCount),
SFVAR(cdda.ScanMode),
SFVAR(cdda.scan_sec_end),
SFVAR(cdda.OversamplePos),
SFARRAY16(&cdda.sr[0], sizeof(cdda.sr) / sizeof(cdda.sr[0])),
SFARRAY16(&cdda.OversampleBuffer[0][0], sizeof(cdda.OversampleBuffer) / sizeof(cdda.OversampleBuffer[0][0])),
SFVAR(cdda.DeemphState[0][0]),
SFVAR(cdda.DeemphState[0][1]),
SFVAR(cdda.DeemphState[1][0]),
SFVAR(cdda.DeemphState[1][1]),
SFARRAYN(&cd.SubQBuf[0][0], sizeof(cd.SubQBuf), "SubQBufs"),
SFARRAYN(cd.SubQBuf_Last, sizeof(cd.SubQBuf_Last), "SubQBufLast"),
SFARRAYN(cd.SubPWBuf, sizeof(cd.SubPWBuf), "SubPWBuf"),
SFVAR(monotonic_timestamp),
SFVAR(pce_lastsapsp_timestamp),
SFARRAY(ModePages[0].current_value, ModePages[0].param_length),
SFARRAY(ModePages[1].current_value, ModePages[1].param_length),
SFARRAY(ModePages[2].current_value, ModePages[2].param_length),
SFARRAY(ModePages[3].current_value, ModePages[3].param_length),
SFARRAY(ModePages[4].current_value, ModePages[4].param_length),
SFEND
};
MDFNSS_StateAction(sm, load, data_only, StateRegs, sname);
if(load)
{
din->in_count &= din->size - 1;
din->read_pos &= din->size - 1;
din->write_pos = (din->read_pos + din->in_count) & (din->size - 1);
if(load < 0x0935)
cdda.CDDADiv /= 2;
if(cdda.CDDADiv <= 0)
cdda.CDDADiv = 1;
cdda.OversamplePos &= 0x1F;
for(int i = 0; i < NumModePages; i++)
UpdateMPCacheP(&ModePages[i]);
}
}