#include "pcfx.h"
#include "v810_cpu.h"
#include "scsicd.h"
#include "../cdromif.h"
#include "soundbox.h"
#include "../video.h"
#include "king.h"

#define INT_TO_BCD(A)  (((A) / 10) * 16 + ((A) % 10))              // convert INT --> BCD


#define BCD_TO_INT(B)  (((B) / 16) * 10 + ((B) % 16))              // convert BCD --> INT


#define CD_DATA_TRANSFER_RATE	(153600 * 2)

static int32 CDDADivAcc = (double)1789772.727272 * 4 * 3 * 65536 / 44100;

typedef struct
{
 uint8 DB; // Data bus
 bool BSY, kingBSY, kingACK, kingRST, MSG, kingMSG, kingSEL, CD, kingCD, REQ, IO, kingIO, kingATN;
 bool last_RST_signal;

 uint8 status_pending;
 uint8 message_pending;
 bool status_sent, message_sent;

 uint8 key_pending, asc_pending, ascq_pending, fru_pending;

 bool need_message_after_status;

 bool device_selected;

 uint8 command_buffer[256];
 uint8 command_buffer_pos;
 uint8 command_size_left;

 // To initiator(the king chip)
 uint8 data_in[8192];
 uint32 data_in_pos;
 uint32 data_in_size;
 bool data_transfer_done; // FALSE if not all pending data is in data_in, TRUE if it is(used for multiple sector CD reads)


 // To target(the cd unit), for "MODE SELECT" mainly
 uint8 data_out[8192];
 uint32 data_out_pos;
 uint32 data_out_size;

 uint32 lastts;
} scsicd_t;

void MakeSense(uint8 *target, uint8 key, uint8 asc, uint8 ascq, uint8 fru)
{
 memset(target, 0, 18);

 target[0] = 0x70; // Current errors and sense data is not SCSI compliant
 target[2] = key;
 target[12] = asc; // Additional Sense Code
 target[13] = ascq; // Additional Sense Code Qualifier
 target[14] = fru;  // Field Replaceable Unit code
}

scsicd_t cd;

typedef Blip_Synth<blip_good_quality, 65536> CDSynth;
extern Blip_Buffer FXsbuf[2];

static uint8 PlayMode;
static CDSynth CDDASynth[2];
static int16 last_sample[2];
static int16 CDDASectorBuffer[1176];
static uint32 CDDAReadPos;
static int8 CDDAStatus; // 1 = playing, 0 = stopped, -1 = paused
static int32 CDDADiv;

static uint32 read_sec_start;
static uint32 read_sec;
static uint32 read_sec_end = ~0;


static int32 CDReadTimer;
static uint32 SectorAddr;
static uint32 SectorCount;

static bool PhaseChange = 0;
static uint32 next_time;

#define BSY_signal (cd.BSY | cd.kingBSY)
#define ACK_signal (cd.kingACK)
#define RST_signal (cd.kingRST)
#define MSG_signal (cd.MSG | cd.kingMSG)
#define SEL_signal (cd.kingSEL)
#define REQ_signal (cd.REQ)
#define IO_signal (cd.IO | cd.kingIO)
#define CD_signal (cd.CD | cd.kingCD)

#define ATN_signal (cd.kingATN)

// Cycles // nanoseconds
static const int Arbitration_Delay = 47; //2200;
static const int Assertion_Period = 1; //90;
static const int Bus_Clear_Delay = 10; //500;
static const int Bus_Free_Delay = 10; //500;
static const int Bus_Set_Delay = 38; //1800;
static const int Bus_Settle_Delay = 8; //400;
static const int Cable_Skew_Delay = 0; //10;
static const int Data_Release_Delay = 8; //400;
static const int Deskew_Delay = 0; //45;
static const int Hold_Time = 0; //45;
static const int Negation_Period = 1; //90;
static const int Reset_Hold_Time = 536; //25000;
static const int Selection_Abort_Time = 4295; //200000;
static const int Selection_Timeout_Delay = 5369318; //250000000;
//static const int Transfer_Period = 

void SCSICD_Power(void)
{
 memset(&cd, 0, sizeof(scsicd_t));
}

bool SCSICD_GetkingBSY(void)
{
 return(cd.kingBSY);
}

bool SCSICD_GetkingCD(void)
{
 return(cd.kingCD);
}

bool SCSICD_GetkingIO(void)
{
 return(cd.kingIO);
}

bool SCSICD_GetkingMSG(void)
{
 return(cd.kingMSG);
}



void SCSICD_SetDB(uint8 data)
{
 cd.DB = data;
}

uint8 SCSICD_GetDB(void)
{
 return(cd.DB);
}

void SCSICD_SetACK(bool set)
{
 cd.kingACK = set;
}

void SCSICD_SetMSG(bool set)
{
 cd.MSG = set;
}

void SCSICD_SetREQ(bool set)
{
 cd.REQ = set;
}

void SCSICD_SetSEL(bool set)
{
 cd.kingSEL = set;
}

void SCSICD_SetRST(bool set)
{
 cd.kingRST = set;
}

void SCSICD_SetBSY(bool set)
{
 cd.kingBSY = set;
}

void SCSICD_SetCD(bool set)
{
 cd.CD = set;
}

void SCSICD_SetIO(bool set)
{
 cd.IO = set;
}

void SCSICD_SetATN(bool set)
{
 cd.kingATN = set;
}

bool SCSICD_GetBSY(void)
{
 return(BSY_signal);
}

bool SCSICD_GetACK(void)
{
 return(ACK_signal);
}

bool SCSICD_GetRST(void)
{
 return(RST_signal);
}

bool SCSICD_GetMSG(void)
{
 return(MSG_signal);
}

bool SCSICD_GetSEL(void)
{
 return(SEL_signal);
}

bool SCSICD_GetCD(void)
{
 return(CD_signal);
}

bool SCSICD_GetREQ(void)
{
 return(REQ_signal);
}

bool SCSICD_GetIO(void)
{
 return(IO_signal);
}

bool SCSICD_GetATN(void)
{
 return(ATN_signal);
}


static const uint8 InqData[] = 
{
 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, 0x45, 0x0E, 0x0E, 0x04
};


#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		0
#define SENSEKEY_RECOVERED_ERROR	1
#define SENSEKEY_NOT_READY		2
#define SENSEKEY_MEDIUM_ERROR		3
#define SENSEKEY_HARDWARE_ERROR		4
#define SENSEKEY_ILLEGAL_REQUEST	5
#define SENSEKEY_UNIT_ATTENTION		6
#define SENSEKEY_DATA_PROTECT		7

#define ASC_MEDIUM_NOT_PRESENT		0x3A
#define ASC_INVALID_command_OPERATION_CODE   0x20
#define ASC_INVALID_FIELD_IN_CDB	0x24

static uint8 CDIFFormat_To_DMF(CDIF_Track_Format format)
{
 switch(format)
 {
  default: return(0xFF);
  case CDIF_FORMAT_AUDIO:  return(0x00);
  case CDIF_FORMAT_MODE1:  return(0x01);
  case CDIF_FORMAT_MODE2:  return(0x02);
 }
}

static uint8 CDIFFORMAT_To_QSCF(CDIF_Track_Format format) // Q sub-channel control field
{
 switch(format)
 {
  case CDIF_FORMAT_AUDIO: return(0x00);
  default: return(0x04);
 }
}

static void SendStatus(uint8 status)
{
 cd.CD = TRUE;
 cd.MSG = FALSE;
 cd.IO = TRUE;
 cd.REQ = TRUE;

 cd.status_pending = status;
 cd.need_message_after_status = FALSE;
 cd.status_sent = FALSE;
 cd.DB = status;

 PhaseChange = TRUE;
}

static void SendMessage(uint8 message)
{
 cd.CD = TRUE;
 cd.MSG = TRUE;
 cd.IO = TRUE;
 cd.REQ = TRUE;
 cd.message_pending = message;
 cd.message_sent = FALSE;
 cd.DB = message;
 PhaseChange = TRUE;
}

static void SendStatusAndMessage(uint8 status, uint8 message)
{
 cd.CD = TRUE;
 cd.MSG = FALSE;
 cd.IO = TRUE;
 cd.REQ = TRUE;

 cd.status_pending = status;
 cd.message_pending = message;
 cd.need_message_after_status = TRUE;

 cd.status_sent = FALSE;
 cd.message_sent = FALSE;

 cd.DB = status;
 PhaseChange = TRUE;
}

static void lba2msf(uint32 lba, uint8* m, uint8* s, uint8* f)
{
        *m = lba / 75 / 60;
        *s = (lba - *m * 75 * 60) / 75;
        *f = lba - (*m * 75 * 60) - (*s * 75);
}

static void DoMODESELECT6(void)
{
                 if(cd.command_buffer[4])
                 {
                  cd.data_out_pos = 0;
                  cd.data_out_size = cd.command_buffer[4];
                  //printf("Switch to DATA OUT phase, len: %d\n", cd.data_out_size);
                  cd.CD = FALSE;
                  cd.IO = FALSE;
                  cd.MSG = FALSE;
		  cd.REQ = TRUE;
		  PhaseChange = TRUE;
                 }
                 else
                  SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
}

static void DoMODESENSE6(void)
{
	unsigned int PC = (cd.command_buffer[2] >> 6) & 0x3;
	unsigned int PageCode = cd.command_buffer[2] & 0x3F;
	bool DBD = cd.command_buffer[1] & 0x08;
	int AllocSize = cd.command_buffer[4];

        printf("Mode sense 6: %02x %d %d %d\n", PageCode, PC, DBD, AllocSize);
	
	switch(PageCode)
	{
	 default: exit(1);
	 case 0x0E: // Audio control page
	   	   if(AllocSize > 16) AllocSize = 16;
		   memset(cd.data_in, 0, 16);

		   cd.data_in[0] = 0x0E; // Page code
		   cd.data_in[1] = 0x0E; // Page length(kinky coincidence!)
		   cd.data_in[2] = 0x00;

		   //cd.data_in[5] = 0x80
		   cd.data_in[8] = 0x01; // Output port 0 channel selection
		   cd.data_in[9] = 0xFF; // Outport port 0 volume
		   cd.data_in[10] = 0x02; // Output port 1 channel selection
		   cd.data_in[11] = 0xFF; // Outport port 1 volume
		   break;
	}
	cd.data_in_size = AllocSize;
        cd.data_in_pos = 0;
	cd.data_transfer_done = TRUE;
	cd.IO = TRUE;
	cd.CD = FALSE;
	PhaseChange = TRUE;

	//exit(1);
}

static void DoSTARTSTOPUNIT6(void)
{
 bool Immed = cd.command_buffer[1] & 0x01;
 bool LoEj = cd.command_buffer[4] & 0x02;
 bool Start = cd.command_buffer[4] & 0x01;

 //printf("Do start stop unit 6: %d %d %d\n", Immed, LoEj, Start);
 SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
}

static void DoINQUIRY(void)
{
                  int AllocSize = cd.command_buffer[4];

                  if(!AllocSize)
                  {
                   cd.key_pending = SENSEKEY_ILLEGAL_REQUEST;
                   cd.asc_pending = 0x20;
                   cd.ascq_pending = 0x00;
                   cd.fru_pending = 0x00;
		   SendStatusAndMessage(STATUS_CHECK_CONDITION << 1, 0x00);
                   return;
                  }

                  memcpy(cd.data_in, InqData, sizeof(InqData));
                  cd.data_in_pos = 0;
                  cd.data_in_size = (AllocSize > sizeof(InqData)) ? sizeof(InqData) : AllocSize;
                  cd.IO = TRUE;
                  cd.CD = FALSE;
                  cd.data_transfer_done = TRUE;
		  PhaseChange = TRUE;

		  KING_CDIRQ();
}

static void DoREQUESTSENSE(void)
{
                 MakeSense(cd.data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending);
                 cd.data_in_pos = 0;
                 cd.data_in_size = 18;
                 cd.key_pending = 0;
                 cd.asc_pending = 0;
                 cd.ascq_pending = 0;
                 cd.fru_pending = 0;

                 cd.IO = TRUE;
                 cd.CD = FALSE;
                 cd.data_transfer_done = TRUE;
		 PhaseChange = TRUE;
}

static void DoGETDIRINFO(void)
{
                //printf("GETDIR: %02x\n", cd.command_buffer[1]);
	switch(cd.command_buffer[1])
        {
         default: printf("DOHDOH: %02x", cd.command_buffer[1]);
         case 0x0:
                  cd.data_in[0] = INT_TO_BCD(CDIF_GetFirstTrack());
                  cd.data_in[1] = INT_TO_BCD(CDIF_GetLastTrack());
                  cd.data_in_size = 2;
                  break;
         case 0x1:
                  {
                   uint8 m, s, f;
                   lba2msf(CDIF_GetSectorCountLBA(), &m, &s, &f);
                   cd.data_in[0] = INT_TO_BCD(m);
                   cd.data_in[1] = INT_TO_BCD(s);
                   cd.data_in[2] = INT_TO_BCD(f);
                   cd.data_in_size = 3;
                  }
                  break;
         case 0x2:
                  {
                   int m, s, f;
                   int track = BCD_TO_INT(cd.command_buffer[2]);

		   if(!track)
		    track = 1;
		   else if(cd.command_buffer[2] == 0xAA)
		   {
		    track = CDIF_GetLastTrack() + 1;
		   }
 		   else if(track > 99)
		   {
                    cd.key_pending = SENSEKEY_ILLEGAL_REQUEST;
                    cd.asc_pending = ASC_INVALID_FIELD_IN_CDB;
                    cd.ascq_pending = 0x00;
                    cd.fru_pending = 0x00;
		    SendStatusAndMessage(STATUS_CHECK_CONDITION << 1, 0x00);
		    return;
		   }

                   CDIF_Track_Format       format;
                   CDIF_GetTrackStartPositionMSF(track, m, s, f);
                   CDIF_GetTrackFormat(track, format);

                   //printf(" Track: %d %02x %02x %02x\n", track, m, s, f);

                   cd.data_in[0] = INT_TO_BCD(m);
                   cd.data_in[1] = INT_TO_BCD(s);
                   cd.data_in[2] = INT_TO_BCD(f);
                   cd.data_in[3] = (format == CDIF_FORMAT_AUDIO) ? 0x00 : 0x04;
                   cd.data_in_size = 4;
                  }
                  break;
                 
	}
        cd.data_transfer_done = TRUE;
        cd.data_in_pos = 0;
        cd.IO = TRUE;
        cd.CD = FALSE;
	PhaseChange = TRUE;
}

static void DoREADTOC(void)
{
                   int FirstTrack = CDIF_GetFirstTrack();
                   int LastTrack = CDIF_GetLastTrack();
                   int StartingTrack = BCD_TO_INT(cd.command_buffer[6]);
                   int AllocSize = (cd.command_buffer[7] << 8) | cd.command_buffer[8];
                   int RealSize = 0;
                   bool WantInMSF = cd.command_buffer[1] & 0x2;

                   //printf("READ TOC: %d %02x %d %d\n", StartingTrack, cd.command_buffer[6], AllocSize, WantInMSF);

                   if(!StartingTrack)
                    StartingTrack = 1;
		   else if(cd.command_buffer[6] == 0xAA)
		   {
                    StartingTrack = LastTrack + 1;
		   }
		   else if(StartingTrack > 99)
		   {
			puts("ERROR");
                    cd.key_pending = SENSEKEY_ILLEGAL_REQUEST;
                    cd.asc_pending = ASC_INVALID_FIELD_IN_CDB;
                    cd.ascq_pending = 0x00;
                    cd.fru_pending = 0x00;
                    SendStatusAndMessage(STATUS_CHECK_CONDITION << 1, 0x00);
                    return;
		   }

                   cd.data_in[2] = INT_TO_BCD(FirstTrack);
                   cd.data_in[3] = INT_TO_BCD(LastTrack);

                   RealSize += 4;

		   // Read leadout track too LastTrack + 1 ???
                   for(int track = StartingTrack; track <= (LastTrack + 1) && (RealSize + 8) <= AllocSize; track++)
                   {
                    uint8 *subptr = &cd.data_in[RealSize];

                    int minutes;
                    int seconds;
                    int frames;
                    CDIF_Track_Format format;
                    CDIF_GetTrackStartPositionMSF(track, minutes, seconds, frames);
                    CDIF_GetTrackFormat(track, format);

                    uint32 lba = frames + 75 * seconds + 75 * 60 * minutes;

                    //printf("%d, %d, %02x\n", track, WantInMSF, CDIFFORMAT_To_QSCF(format) | 0x10);
                    subptr[0] = 0;
                    subptr[1] = CDIFFORMAT_To_QSCF(format) | 0x10; // | 0x10 = Q sub channel encodes current position info
		    if(track == (LastTrack + 1))
		     subptr[2] = 0xAA;
		    else
                     subptr[2] = INT_TO_BCD(track);
                    subptr[3] = 0;

                    //printf("LA: %d\n", lba);
                    if(WantInMSF)
                    {
                     subptr[4] = 0;
                     subptr[5] = minutes; // Min
                     subptr[6] = seconds; // Sec
                     subptr[7] = frames;  // Frames
                    }
                    else
                    {
                     lba -= 150;
                     subptr[4] = lba >> 24;
                     subptr[5] = lba >> 16;
                     subptr[6] = lba >> 8;
                     subptr[7] = lba >> 0;
                    }
                    RealSize += 8;
                   }

                   if(RealSize > AllocSize) RealSize = AllocSize;

		   // Record the length of the response, minus the size of the TOC data length field(2 bytes)
                   cd.data_in[0] = (RealSize - 2) >> 8;
                   cd.data_in[1] = (RealSize - 2) >> 0;

                   //printf("%d %d %d %d\n", cd.data_in_size, StartingTrack, LastTrack, CDIFFORMAT_To_QSCF(format) | 0x10);
                   cd.data_in_size = RealSize;
                   cd.data_in_pos = 0;
                   cd.IO = TRUE;
                   cd.CD = FALSE;
                   cd.data_transfer_done = TRUE;
		   PhaseChange = TRUE;
}

static void DoREADHEADER(void)
{
                   bool WantInMSF = cd.command_buffer[1] & 0x2;
                   uint32 HeaderLBA = (cd.command_buffer[2] << 24) | (cd.command_buffer[3] << 16) | (cd.command_buffer[4] << 8) | cd.command_buffer[5];
                   int AllocSize = (cd.command_buffer[7] << 8) | cd.command_buffer[8];
                   int Track = CDIF_FindTrackByLBA(HeaderLBA);

                   int minutes;
                   int seconds;
                   int frames;
                   CDIF_Track_Format format;

                   CDIF_GetTrackStartPositionMSF(Track, minutes, seconds, frames);
                   CDIF_GetTrackFormat(Track, format);

                   uint32 lba = frames + 75 * seconds + 75 * 60 * minutes;

                   //printf("LBA: %d, Track: %d\n", HeaderLBA, Track);

                   cd.data_in[0] = CDIFFormat_To_DMF(format);
                   cd.data_in[1] = 0;
                   cd.data_in[2] = 0;
                   cd.data_in[3] = 0;

                   if(WantInMSF)
                   {
                    cd.data_in[4] = 0;
                    cd.data_in[5] = minutes; // Min
                    cd.data_in[6] = seconds; // Sec
                    cd.data_in[7] = frames;  // Frames
                   }
                   else
                   {
                    lba -= 150;
                    cd.data_in[4] = lba >> 24;
                    cd.data_in[5] = lba >> 16;
                    cd.data_in[6] = lba >> 8;
                    cd.data_in[7] = lba >> 0;
                   }
                   cd.data_in_size = 8;
                   cd.data_in_pos = 0;
                   cd.IO = TRUE;
                   cd.CD = FALSE;
                   cd.data_transfer_done = TRUE;

		   CDDAStatus = 0;
		   PhaseChange = TRUE;
}

static void DoSAPSP(void)
{
     //            printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cd.command_buffer[9], cd.command_buffer[1], cd.command_buffer[2], cd.command_buffer[3], cd.command_buffer[4], cd.command_buffer[5], cd.command_buffer[6]);
                  switch(cd.command_buffer[9] & 0xc0)
                  {
                   case 0x00: read_sec_start = (cd.command_buffer[3] << 16) | (cd.command_buffer[4] << 8) | cd.command_buffer[5];
                              read_sec_start -= 150;
                              break;
                   case 0x40: read_sec_start = BCD_TO_INT(cd.command_buffer[4]) + 75 * (BCD_TO_INT(cd.command_buffer[3]) + 60 * BCD_TO_INT(cd.command_buffer[2]));
                              read_sec_start -= 150;
                              break;
                   case 0x80: read_sec_start = CDIF_GetTrackStartPositionLBA(BCD_TO_INT(cd.command_buffer[2]));
                              break;
                  }
                  read_sec = read_sec_start;
                  CDDAReadPos = 588;
                  CDDAStatus = -1;

                  if(cd.command_buffer[1])
                  {
                   CDDAStatus = 1;
                  }
                  SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
		  KING_CDIRQ();
}

static void DoSAPEP(void)
{
 //                printf("Set audio end: %02x %02x %02x %02x %02x\n", cd.command_buffer[9], cd.command_buffer[1], cd.command_buffer[2], cd.command_buffer[3], cd.command_buffer[4]);
                 switch(cd.command_buffer[9] & 0xc0)
                  {
                   case 0x00: read_sec_end = (cd.command_buffer[3] << 16) | (cd.command_buffer[4] << 8) | cd.command_buffer[5];
                              read_sec_end -= 150;
                              break;
                   case 0x40: read_sec_end = BCD_TO_INT(cd.command_buffer[4]) + 75 * (BCD_TO_INT(cd.command_buffer[3]) + 60 * BCD_TO_INT(cd.command_buffer[2]));
                              read_sec_end -= 150;
                              break;
                   case 0x80: read_sec_end = CDIF_GetTrackStartPositionLBA(BCD_TO_INT(cd.command_buffer[2]));
                              break;
                   }
                   CDDAStatus = -1;
                   PlayMode = cd.command_buffer[1];

                   if(PlayMode)
                   {
                    CDDAStatus = 1;
                    CDDAReadPos = 588;
                   }
                  SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
}

static void DoPATI(void)
{
 int StartTrack = BCD_TO_INT(cd.command_buffer[4]);
 int EndTrack = BCD_TO_INT(cd.command_buffer[7]);
 int StartIndex = BCD_TO_INT(cd.command_buffer[5]);
 int EndIndex = BCD_TO_INT(cd.command_buffer[8]);
 
 CDIF_Track_Format start_format;
 
 if(!CDIF_GetTrackFormat(StartTrack, start_format) || start_format != CDIF_FORMAT_AUDIO)
 {
  cd.key_pending = SENSEKEY_ILLEGAL_REQUEST;
  cd.asc_pending = 0x00;
  cd.ascq_pending = 0x00;
  cd.fru_pending = 0x00;

  SendStatusAndMessage(STATUS_CHECK_CONDITION << 1, 0x00);
  return;
 }

 read_sec = read_sec_start = CDIF_GetTrackStartPositionLBA(StartTrack);
 read_sec_end = CDIF_GetTrackStartPositionLBA(EndTrack);
 CDDAStatus = 1;
 PlayMode = 3; // No repeat

 //printf("PATI: %d %d %d  SI: %d, EI: %d\n", StartTrack, EndTrack, CDIF_GetTrackStartPositionLBA(StartTrack), StartIndex, EndIndex);
 SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
}

static void DoPAUSERESUME(void)
{
 // Pause/resume
 // "It shall not be considered an error to request a pause when a pause is already in effect, 
 // or to request a resume when a play operation is in progress."
 if(!CDDAStatus) // If CDDA isn't playing/paused, make an error!
 {
  cd.asc_pending = 0x2c;
  cd.ascq_pending = 0x00;
  cd.fru_pending = 0x00;
  SendStatusAndMessage(STATUS_CHECK_CONDITION << 1, 0x00);
  return;
 }
 if(cd.command_buffer[8] & 1) // Resume
 {
  CDDAStatus = 1;
 }
 else
  CDDAStatus = -1;
 SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
}

static void DoREAD10(void)
{
	//printf("Read 10: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", cd.command_buffer[0], cd.command_buffer[1],cd.command_buffer[2],cd.command_buffer[3],
	//	cd.command_buffer[4],cd.command_buffer[5],cd.command_buffer[6],cd.command_buffer[7],cd.command_buffer[8],cd.command_buffer[9]);
        SectorAddr = (cd.command_buffer[2] << 24) | (cd.command_buffer[3] << 16) | (cd.command_buffer[4] << 8) | cd.command_buffer[5];
        SectorCount = (cd.command_buffer[7] << 8) | (cd.command_buffer[8]);
        if(SectorCount)
	{
         CDReadTimer = (uint64)1 * 2048 * 1789772.727272 * 12 / CD_DATA_TRANSFER_RATE;
	 PhaseChange = TRUE;
	}
        else
        {
         CDReadTimer = 0;
         SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
        }
	CDDAStatus = 0;
}

static void DoREAD12(void)
{
                   SectorAddr = (cd.command_buffer[2] << 24) | (cd.command_buffer[3] << 16) | (cd.command_buffer[4] << 8) | cd.command_buffer[5];
                   SectorCount = (cd.command_buffer[6] << 24) | (cd.command_buffer[7] << 16) | (cd.command_buffer[8] << 8) | cd.command_buffer[9];

                   if(SectorCount)
		   {
		    PhaseChange = TRUE;
                    CDReadTimer = (uint64)1 * 2048 * 1789772.727272 * 12 / CD_DATA_TRANSFER_RATE;
		   }
                   else
                   {
                    CDReadTimer = 0;
                    SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
                   }
		   CDDAStatus = 0;
}

static void DoPREFETCH(void)
{
 uint32 lba = (cd.command_buffer[2] << 24) | (cd.command_buffer[3] << 16) | (cd.command_buffer[4] << 8) | cd.command_buffer[5];
 uint32 len = (cd.command_buffer[7] << 8) | cd.command_buffer[8];
 bool link = cd.command_buffer[9] & 0x1;
 bool flag = cd.command_buffer[9] & 0x2;
 bool reladdr = cd.command_buffer[1] & 0x1;
 bool immed = cd.command_buffer[1] & 0x2;

 printf("Prefetch: %08x %08x %d %d %d %d\n", lba, len, link, flag, reladdr, immed);
 //SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
 SendStatusAndMessage(STATUS_CONDITION_MET << 1, 0x00);
 //exit(1);
}

static void DoREADSUBCHANNEL(void)
{
 int DataFormat = cd.command_buffer[3];
 int TrackNum = cd.command_buffer[6];
 int AllocSize = (cd.command_buffer[7] << 8) | cd.command_buffer[8];
 bool WantQ = cd.command_buffer[2] & 0x40;
 bool WantMSF = cd.command_buffer[1] & 0x02;

 if(!WantQ)
  SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
 else
 {
  switch(DataFormat)
  {
   default: exit(1);

   case 0x01: // CD-ROM current position
	{
	 uint32 lba = CDDAStatus ? read_sec : SectorAddr;
	 uint32 track_for_sector = CDIF_FindTrackByLBA(lba);
	 CDIF_Track_Format format;

	 CDIF_GetTrackFormat(track_for_sector, format);

	 cd.data_in[0] = 0;
	 if(CDDAStatus == 1)
	  cd.data_in[1] = 0x11;
	 else if(CDDAStatus == -1)
	  cd.data_in[1] = 0x12;
	 else
	  cd.data_in[1] = 0x13; // 0x13 or 0x15? :(

	 // Sub-channel data length
	 cd.data_in[2] = 0x00;
	 cd.data_in[3] = 0x0C;

	 // Sub-channel format code
	 cd.data_in[4] = 0x01;

	 cd.data_in[5] = CDIFFORMAT_To_QSCF(format) | 0x10; // | 0x10 = Q sub channel encodes current position info
	 cd.data_in[6] = INT_TO_BCD(track_for_sector);
	 cd.data_in[7] = 0x01; // Uhm, index number?
         uint32 ma,sa,fa;
         uint32 m, s, f;
	 uint32 lba_relative = lba - CDIF_GetTrackStartPositionLBA(track_for_sector);

         f = lba_relative % 75;
         s = (lba_relative / 75) % 60;
         m = (lba_relative / 75 / 60);

         fa = (lba + 150) % 75;
         sa = ((lba + 150) / 75) % 60;
         ma = ((lba + 150) / 75 / 60);

	 // Absolute CD-ROM address
	 if(WantMSF)
	 {
	  cd.data_in[8] = 0;
	  cd.data_in[9] = INT_TO_BCD(ma);
	  cd.data_in[10] = INT_TO_BCD(sa);
	  cd.data_in[11] = INT_TO_BCD(fa);
	 }
	 else
	 {
	  cd.data_in[8] = lba >> 24;
	  cd.data_in[9] = lba >> 16;
	  cd.data_in[10] = lba >> 8;
	  cd.data_in[11] = lba >> 0;
	 }

	 // Relative CD-ROM address
	 if(WantMSF)
	 {
	  cd.data_in[12] = 0;
	  cd.data_in[13] = INT_TO_BCD(m);
	  cd.data_in[14] = INT_TO_BCD(s);
	  cd.data_in[15] = INT_TO_BCD(f);
	 }
	 else
	 {
	  cd.data_in[12] = lba_relative >> 24;
	  cd.data_in[13] = lba_relative >> 16;
	  cd.data_in[14] = lba_relative >> 8;
	  cd.data_in[15] = lba_relative >> 0;
	 }
	 cd.data_in_size = (AllocSize > 16) ? 16 : AllocSize;
         cd.data_transfer_done = TRUE;
         cd.data_in_pos = 0;
         cd.IO = TRUE;
         cd.CD = FALSE;
	 PhaseChange = TRUE;
	}
	break;
  }
 }
 //printf("%02x %02x %04x %d\n", DataFormat, TrackNum, AllocSize, WantQ);
 //exit(1);
}

static void DoREADSUBQ(void)
{
                   uint32 lba = read_sec;
                   uint32 lba_relative;
                   uint32 track;
                   uint32 ma,sa,fa;
                   uint32 m, s, f;

                   fa = (lba + 150) % 75;
                   sa = ((lba + 150) / 75) % 60;
                   ma = ((lba + 150) / 75 / 60);

                   track = CDIF_FindTrackByLBA(lba);
                   lba_relative = lba - CDIF_GetTrackStartPositionLBA(track);

                   f = lba_relative % 75;
                   s = (lba_relative / 75) % 60;
                   m = (lba_relative / 75 / 60);

                   //printf("%d %d\n", lba, track);
                   memset(cd.data_in, 0x00, 10);
                   cd.data_in[2] = INT_TO_BCD(track);
                   cd.data_in[4] = INT_TO_BCD(m);
                   cd.data_in[5] = INT_TO_BCD(s);
                   cd.data_in[6] = INT_TO_BCD(f);
                   cd.data_in[7] = INT_TO_BCD(ma);
                   cd.data_in[8] = INT_TO_BCD(sa);
                   cd.data_in[9] = INT_TO_BCD(fa);

                   if (CDDAStatus == -1)
                    cd.data_in[0] = 2; // Pause
                   else if (CDDAStatus == 1)
                    cd.data_in[0] = 0; // Playing
                   else
                    cd.data_in[0] = 3; // Stopped

                   cd.data_in_size = 10;
                   cd.data_transfer_done = TRUE;
                   cd.data_in_pos = 0;
                   cd.IO = TRUE;
                   cd.CD = FALSE;
		   PhaseChange = TRUE;
}

static int32 lastts;
void SCSICD_ResetTS(void)
{
 lastts = 0;
}
void SCSICD_Run(void)
{
 int32 run_time = v810_timestamp - lastts;

 lastts = v810_timestamp;

 if(CDReadTimer > 0)
 {
  CDReadTimer -= run_time;
  if(CDReadTimer <= 0)
  {
   if(cd.data_in_pos != cd.data_in_size) 
   { 
    CDReadTimer += (uint64)1 * 2048 * 1789772.727272 * 12 / CD_DATA_TRANSFER_RATE;
    //printf("Crap: %d %d\n", SectorCount, CDReadTimer);
   }
   else
   {
    //printf("READSECTOR: %d\n", SectorAddr);
    KING_CDIRQ();
    CDIF_ReadSector(cd.data_in, SectorAddr, 1);
    SectorAddr++;
    SectorCount--;

    cd.IO = TRUE;
    cd.CD = FALSE;

    if(SectorCount)
    {
     cd.data_transfer_done = FALSE;
     CDReadTimer += (uint64)1 * 2048 * 1789772.727272 * 12 / CD_DATA_TRANSFER_RATE;
    }
    else
    {
     cd.data_transfer_done = TRUE;
    }
    cd.data_in_pos = 0;
    cd.data_in_size = 2048;
   }
  }
 }


 if(CDDAStatus == 1)
 {
  int16 sample[2];

  CDDADiv -= run_time << 16;

  while(CDDADiv <= 0)
  {
   CDDADiv += CDDADivAcc;

   if(CDDAReadPos == 588)
   {
    if(read_sec == read_sec_end)
    {
     switch(PlayMode)
     {
      default:
      case 0x03: // No repeat
		CDDAStatus = 0;
		break;
      case 0x02: // Interrupt when finished?
		CDDAStatus = 0;
		KING_CDIRQ();
		break;
      case 0x04:
      case 0x01: // Repeat play!
		read_sec = read_sec_start;
		break;
     }
     if(CDDAStatus != 1) break;
    }
    else
     read_sec++;
    CDDAReadPos = 0;
    CDIF_ReadAudioSector(CDDASectorBuffer, read_sec);
   }

   sample[0] = CDDASectorBuffer[CDDAReadPos * 2];
   sample[1] = CDDASectorBuffer[CDDAReadPos * 2 + 1];

   uint32 synthtime = ((v810_timestamp + (CDDADiv >> 16))) / 3;
   if(FSettings.SndRate)
   {
    CDDASynth[0].offset(synthtime, sample[0] - last_sample[0], &FXsbuf[0]);
    CDDASynth[1].offset(synthtime, sample[1] - last_sample[1], &FXsbuf[1]);

    last_sample[0] = sample[0];
    last_sample[1] = sample[1];
   }
   CDDAReadPos++;
  }
 }

 if(!SEL_signal && !BSY_signal) // BUS-free mode, release our signals
 {
  //puts("Bus free mode!");
  cd.CD = FALSE;
  cd.MSG = FALSE;
  cd.IO = FALSE;
  cd.REQ = FALSE;
 }

 if(RST_signal)
 {
  if(!cd.last_RST_signal)
  {
   cd.BSY = FALSE;
   cd.CD = FALSE;
   cd.MSG = FALSE;
   cd.IO = FALSE;
   cd.REQ = FALSE;
   PhaseChange = TRUE;
  }
  cd.last_RST_signal = RST_signal;
  return;
 }
 cd.last_RST_signal = RST_signal;

 if(SEL_signal)
 {
  if(BSY_signal) // Selection phase?
  {
   cd.device_selected = TRUE;

   cd.CD = TRUE; // Ask for message!
   cd.BSY = TRUE;
   cd.MSG = FALSE;
   cd.IO = FALSE;
   cd.REQ = TRUE;
   //puts("Yay");
  }
 }
 else if(ATN_signal)
 {
  //puts("Attention!");

  // If any bits are changing, set PhaseChange
  if(!cd.BSY || !cd.MSG || !cd.REQ || !cd.CD || cd.IO)
   PhaseChange = TRUE;

  cd.BSY = TRUE;
  cd.MSG = TRUE;
  cd.REQ = TRUE;
  cd.CD = TRUE;
  cd.IO = FALSE;
 }
 else if(!MSG_signal && BSY_signal) // Data phaseseses
 {
  if(!IO_signal) 
  {
   if(CD_signal) // COMMAND phase
   //if(1)
   {
    if(REQ_signal && ACK_signal) // Data bus is valid nowww
    {
     cd.command_buffer[cd.command_buffer_pos++] = cd.DB;    
     cd.REQ = FALSE;
    }

    if(!REQ_signal && !ACK_signal && cd.command_buffer_pos) // Received at least one byte, what should we do?
    {
     int totallength;

     switch(cd.command_buffer[0])
     {
      default:
      case 0x00: totallength = 1 + 0; break;
      case 0x12: totallength = 1 + 5; break;
      case 0x1A: totallength = 6; break; // MODE SENSE(6)
      case 0x1B: totallength = 6; break; // Start stop unit
      case 0x1E: totallength = 1 + 15; break;

      case 0x28: totallength = 10; break; // READ 10
      case 0xA8: totallength = 12; break; // Read 12
      case 0x34: totallength = 10; break; // Prefetch
      case 0x42: totallength = 10; break; // Read SUBCHANNEL
      case 0x43: totallength = 10; break; // Read TOC
      case 0x44: totallength = 10; break;

      case 0x4B: totallength = 10; break;
      case 0x15: totallength = 6; break; // MODE SELECT(6)

      case 0x48: totallength = 10; break;
      case 0xD8: totallength = 10; break; // Set audio playback start position
      case 0xD9: totallength = 10; break; // Set audio playback end position
      case 0xDA: totallength = 10; break; // pause
      case 0xDB: totallength = 10; break; // WTFCOMMANDO
      case 0xDD: totallength = 10; break; // Read subchannel Q
      case 0xDE: totallength = 10; break; // GET DIRECTORY??
     }

     if(cd.command_buffer_pos == totallength)  // We've received the command, and it's done, sooooo...
     {
      //printf("Command: %02x\n", cd.command_buffer[0]);
      switch(cd.command_buffer[0])
      {
       default: cd.key_pending = SENSEKEY_ILLEGAL_REQUEST;
 	        cd.asc_pending = 0x20;
 	        cd.ascq_pending = 0x00;
	        cd.fru_pending = 0x00;
	        SendStatusAndMessage(STATUS_CHECK_CONDITION << 1, 0x00);
	        printf("Command: %02x\n", cd.command_buffer[0]);
	 	exit(1);
	        break;

       case 0x00: SendStatusAndMessage(STATUS_GOOD << 1, 0x00); break;

       case 0x03: DoREQUESTSENSE(); break;
        case 0x12: DoINQUIRY(); break;// INQUIRY...?
        case 0x15: DoMODESELECT6(); break; // MODE Select
        case 0x1A: DoMODESENSE6(); break;
	case 0x1B: DoSTARTSTOPUNIT6(); break;
        case 0x1E: // PREVENT/ALLOW MEDIA REMOVAL
		  SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
		  break;

        case 0xA8: DoREAD12(); break; // READ 12
        case 0x28: DoREAD10(); break; // M READ 10
	case 0x34: DoPREFETCH(); break;
	case 0x42: DoREADSUBCHANNEL(); break;
        case 0x44: DoREADHEADER(); break; // O READ HEADER
        case 0x48: DoPATI(); break; // Play audio track index!
        case 0xd8: DoSAPSP(); break; // Set audio playback start position
        case 0xd9: DoSAPEP(); break; // Set audio playback end position
        case 0xDB: printf("Huh: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", cd.command_buffer[0], cd.command_buffer[1],cd.command_buffer[2],cd.command_buffer[3],cd.command_buffer[4], cd.command_buffer[5], cd.command_buffer[6], cd.command_buffer[7], cd.command_buffer[8], cd.command_buffer[9]); SendStatusAndMessage(STATUS_GOOD << 1, 0x00); break;
        case 0xDD: DoREADSUBQ(); break; // Read subchannel Q
        case 0x4B: DoPAUSERESUME(); break; 

        case 0xDA: // Pause
		  CDDAStatus = -1;
                  SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
		  break;
        case 0xDE: DoGETDIRINFO(); break;
        case 0x43: DoREADTOC(); break; // O READ TOC
      }
      cd.command_buffer_pos = 0;
     }    
     else		// Otherwise, get more data for the command!
      cd.REQ = TRUE;
    }
   }
   else // DATA OUT Phase, neato!?
   {
    if(REQ_signal && ACK_signal) // Data bus is valid nowww
    {
     //printf("DATAOUT-SCSIIN: %d %02x\n", cd.data_out_pos, cd.DB);
     cd.data_out[cd.data_out_pos++] = cd.DB;
     cd.REQ = FALSE;
    }
    else if(!REQ_signal && !ACK_signal && cd.data_out_pos)
    {
     if(cd.data_out_pos == cd.data_out_size)
     {
      //printf("Datumama: %02x %02x %02x %02x %02x %02x %02x   %d\n", cd.data_out[0], cd.data_out[1], cd.data_out[2], cd.data_out[3], cd.data_out[4], cd.data_out[5], cd.data_out[6], cd.data_out_size);
      if(cd.command_buffer[0] == 0x15)
      {
       switch(cd.data_out[4])
       {
	case 0x29: break; // ??? Miraculum tries to set this mode page
        case 0x2B: 
		   {
		    int8 speed = cd.data_out[6];
		    double rate = 44100 + 44100 * speed / 138;
		    printf("Speed: %d %f\n", speed, rate);
		    CDDADivAcc = (double)1789772.727272 * 4 * 3 * 65536 / rate;
		   }
		   break;
       }
      }
      cd.data_out_pos = 0;
      SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
     }
     else
      cd.REQ = TRUE;
    }
   }
  } 
  else  // to if(!IO_signal)
  {
   if(CD_signal) // STATUS
   {
    // cd.status_pending = status;
    // cd.message_pending = message;
    // cd.need_message_after_status = TRUE;
    if(REQ_signal && ACK_signal)
    {
     cd.REQ = FALSE;
     cd.status_sent = TRUE;
    }

    if(!REQ_signal && !ACK_signal && cd.status_sent)
    {
     cd.status_sent = FALSE;
     if(cd.need_message_after_status)
     {
      cd.need_message_after_status = FALSE;
      cd.MSG = TRUE;
      cd.REQ = TRUE;
      cd.DB = cd.message_pending;
     }
    //puts("Status");
    }
   } // END STATUS
   else
   { // BEGIN "DATA IN"(us to them!)
    //puts("Choo");

    if(!REQ_signal && !ACK_signal)
    {
     if(cd.data_in_pos == cd.data_in_size) // aaand we're done!
     {
      if(cd.data_transfer_done)
      {
       //puts("Data transfer done");
       SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
       //SendMessage(0x00);
       cd.data_transfer_done = FALSE;
      }
      else
      {
       //SendStatus(STATUS_GOOD << 1);
      }
     }
     else
     {
      //printf("DB Set: %d, %02x\n", cd.data_in_pos, cd.DB);
      cd.DB = cd.data_in[cd.data_in_pos++];
      cd.REQ = TRUE;
     }
    }
    if(REQ_signal && ACK_signal)
    {
     cd.REQ = FALSE;
    }
   }

  }
 }
 else if(MSG_signal && BSY_signal) // Message phases, todo
 {
  if(!IO_signal) // MESSAGE OUT
  {
   if(REQ_signal && ACK_signal)
   {
    cd.REQ = FALSE;
    if(cd.DB == 0x6) // ABORT message!
    {
      cd.data_in_pos = cd.data_in_size = 0;
      cd.BSY = FALSE;
      cd.CD = FALSE;
      cd.MSG = FALSE;
      cd.IO = FALSE;
      CDReadTimer = 0;
      CDDAStatus = 0;
      PhaseChange = TRUE;
    }
    else printf("Message to target: %02x\n", cd.DB);

   }
  }
  else		// MESSAGE IN
  {
   if(REQ_signal && ACK_signal)
   {
    cd.REQ = FALSE;
    cd.message_sent = TRUE;
   }

   if(!REQ_signal && !ACK_signal && cd.message_sent)
   {
    cd.message_sent = FALSE;
    cd.BSY = FALSE;
    //puts("Message");
   }

//   puts("Message in!");

  }
 }

 next_time = V810_EVENT_NONONO; //0x7FFFFFFF;

 if(CDReadTimer > 0 && CDReadTimer < next_time)
  next_time = CDReadTimer;

 if(CDDAStatus == 1)
 {
  int32 cdda_div_sexytime = (CDDADiv + 0xFFFF) >> 16;
  if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time)
   next_time = cdda_div_sexytime;
 }

 if(PhaseChange)
 {
  PhaseChange = FALSE;
  next_time = 1; 
 }

 v810_setevent(V810_EVENT_SCSI, next_time);
}

bool SCSICD_StealByte(uint8 &stolen)
{
 bool ret = 0;

 if(REQ_signal)
 {
  stolen = cd.DB;
  cd.REQ = FALSE;
  ret = 1;
 }
 else if(cd.data_in_pos < cd.data_in_size)
 {
  stolen = cd.data_in[cd.data_in_pos];
  //printf("Stolen: %02x %d %d\n", stolen, cd.data_in_pos, cd.data_in_size);
  ret = 1;
  cd.data_in_pos++;
  if(cd.data_in_pos == cd.data_in_size)
  {
   if(cd.data_transfer_done)
   {
    cd.data_transfer_done = FALSE;
    SendStatusAndMessage(STATUS_GOOD << 1, 0x00);
   }
  }
 }

 return(ret);
}


void SCSICD_Init(void)
{
        CDDASynth[0].volume(0.50f);
	CDDASynth[1].volume(0.50f);
}

void SCSICD_SetCDDAVolume(int left, int right)
{
	CDDASynth[0].volume(0.50f * left / 63);
	CDDASynth[1].volume(0.50f * right / 63);	
}

int SCSICD_StateAction(StateMem *sm, int load, int data_only)
{
 SFORMAT StateRegs[] =
 {
  SFVARN(cd.DB, "DB"),
  SFVARN(cd.BSY, "BSY"),
  SFVARN(cd.MSG, "MSG"),
  SFVARN(cd.CD, "CD"),
  SFVARN(cd.REQ, "REQ"),
  SFVARN(cd.IO, "IO"),

  SFVARN(cd.kingBSY, "kingBSY"),
  SFVARN(cd.kingACK, "kingACK"),
  SFVARN(cd.kingRST, "kingRST"),
  SFVARN(cd.kingMSG, "kingMSG"),
  SFVARN(cd.kingSEL, "kingSEL"),
  SFVARN(cd.kingCD, "kingCD"),
  SFVARN(cd.kingIO, "kingIO"),
  SFVARN(cd.kingATN, "kingATN"),

  SFVARN(cd.last_RST_signal, "last_RST"),
  SFVARN(cd.status_pending, "status_pending"),
  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"),
  SFVARN(cd.need_message_after_status, "need_message_after_status"),
  SFVARN(cd.device_selected, "device_selected"),

  SFARRAYN(cd.command_buffer, 256, "command_buffer"),
  SFVARN(cd.command_buffer_pos, "command_buffer_pos"),
  SFVARN(cd.command_size_left, "command_size_left"),

  SFARRAYN(cd.data_in, 8192, "data_in"),
  SFVARN(cd.data_in_pos, "data_in_pos"),
  SFVARN(cd.data_in_size, "data_in_size"),
  SFVARN(cd.data_transfer_done, "data_transfer_done"),

  SFARRAYN(cd.data_out, 8192, "data_out"),
  SFVARN(cd.data_out_pos, "data_out_pos"),
  SFVARN(cd.data_out_size, "data_out_size"),

  SFVAR(PlayMode),
  SFARRAY16(CDDASectorBuffer, 1176),
  SFVAR(CDDAReadPos),
  SFVAR(CDDAStatus),
  SFVAR(CDDADiv),
  SFVAR(read_sec_start),
  SFVAR(read_sec),
  SFVAR(read_sec_end),
  
  SFVAR(CDReadTimer),
  SFVAR(SectorAddr),
  SFVAR(SectorCount),

  SFEND
 };

 int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "CDRM");

 if(load)
 {
  v810_setevent(V810_EVENT_SCSI, next_time);
 }

 return(ret);
}

