1 /* Mednafen - Multi-system Emulator
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include <mednafen/mednafen.h>
19 #include <math.h>
20 #include <algorithm>
21 #include "scsicd.h"
22 #include "cdromif.h"
23 #include "SimpleFIFO.h"
24 #include <compat/msvc.h>
25 
26 #if defined(__SSE2__)
27 #include <xmmintrin.h>
28 #include <emmintrin.h>
29 #endif
30 
31 static uint32_t CD_DATA_TRANSFER_RATE;
32 static uint32_t System_Clock;
33 static void (*CDIRQCallback)(int);
34 static void (*CDStuffSubchannels)(uint8_t, int);
35 static int32_t* HRBufs[2];
36 static int WhichSystem;
37 
38 static CDIF *Cur_CDIF;
39 static bool TrayOpen;
40 
41 // Internal operation to the SCSI CD unit.  Only pass 1 or 0 to these macros!
42 #define SetIOP(mask, set)	{ cd_bus.signals &= ~mask; if(set) cd_bus.signals |= mask; }
43 
44 #define SetBSY(set)		SetIOP(SCSICD_BSY_mask, set)
45 #define SetIO(set)              SetIOP(SCSICD_IO_mask, set)
46 #define SetCD(set)              SetIOP(SCSICD_CD_mask, set)
47 #define SetMSG(set)             SetIOP(SCSICD_MSG_mask, set)
48 
SetREQ(bool set)49 static INLINE void SetREQ(bool set)
50 {
51  if(set && !REQ_signal)
52   CDIRQCallback(SCSICD_IRQ_MAGICAL_REQ);
53 
54  SetIOP(SCSICD_REQ_mask, set);
55 }
56 
57 #define SetkingACK(set)		SetIOP(SCSICD_kingACK_mask, set)
58 #define SetkingRST(set)         SetIOP(SCSICD_kingRST_mask, set)
59 #define SetkingSEL(set)         SetIOP(SCSICD_kingSEL_mask, set)
60 #define SetkingATN(set)         SetIOP(SCSICD_kingATN_mask, set)
61 
62 
63 enum
64 {
65  QMode_Zero = 0,
66  QMode_Time = 1,
67  QMode_MCN = 2, // Media Catalog Number
68  QMode_ISRC = 3 // International Standard Recording Code
69 };
70 
71 typedef struct
72 {
73  bool last_RST_signal;
74 
75  // The pending message to send(in the message phase)
76  uint8_t message_pending;
77 
78  bool status_sent, message_sent;
79 
80  // Pending error codes
81  uint8_t key_pending, asc_pending, ascq_pending, fru_pending;
82 
83  uint8_t command_buffer[256];
84  uint8_t command_buffer_pos;
85  uint8_t command_size_left;
86 
87  // FALSE if not all pending data is in the FIFO, TRUE if it is.
88  // Used for multiple sector CD reads.
89  bool data_transfer_done;
90 
91  // To target(the cd unit); for "MODE SELECT".
92  uint8_t data_out[256];	// Technically it only needs to be 255, but powers of 2 are better than those degenerate powers of 2 minus one goons.
93  uint8_t data_out_pos;	// Current index for writing into data_out.
94  uint8_t data_out_want;	// Total number of bytes to buffer into data_out.
95 
96  bool DiscChanged;
97 
98  uint8_t SubQBuf[4][0xC];		// One for each of the 4 most recent q-Modes.
99  uint8_t SubQBuf_Last[0xC];	// The most recent q subchannel data, regardless of q-mode.
100 
101  uint8_t SubPWBuf[96];
102 
103 } scsicd_t;
104 
105 enum
106 {
107  CDDASTATUS_PAUSED = -1,
108  CDDASTATUS_STOPPED = 0,
109  CDDASTATUS_PLAYING = 1,
110  CDDASTATUS_SCANNING = 2
111 };
112 
113 enum
114 {
115  PLAYMODE_SILENT = 0x00,
116  PLAYMODE_NORMAL,
117  PLAYMODE_INTERRUPT,
118  PLAYMODE_LOOP
119 };
120 
121 typedef struct
122 {
123  uint32_t CDDADivAcc;
124  uint8_t CDDADivAccVolFudge;	// For PC-FX CD-DA rate control RE impulses and resampling; 100 = 1.0.
125  uint32_t scan_sec_end;
126 
127  uint8_t PlayMode;
128  int32_t CDDAVolume[2];		// 65536 = 1.0, the maximum.
129  int16 CDDASectorBuffer[1176];
130  uint32_t CDDAReadPos;
131 
132  int8_t CDDAStatus;
133  uint8_t ScanMode;
134  int64_t CDDADiv;
135  int CDDATimeDiv;
136 
137  int16 OversampleBuffer[2][0x10 * 2];	// *2 so our MAC loop can blast through without masking the index.
138  unsigned OversamplePos;
139 
140  int16 sr[2];
141 
142  uint8_t OutPortChSelect[2];
143  uint32_t OutPortChSelectCache[2];
144  int32_t OutPortVolumeCache[2];
145 
146  float DeemphState[2][2];
147 } cdda_t;
148 
MakeSense(uint8_t * target,uint8_t key,uint8_t asc,uint8_t ascq,uint8_t fru)149 void MakeSense(uint8_t * target, uint8_t key, uint8_t asc, uint8_t ascq, uint8_t fru)
150 {
151  memset(target, 0, 18);
152 
153  target[0] = 0x70;		// Current errors and sense data is not SCSI compliant
154  target[2] = key;
155  target[7] = 0x0A;
156  target[12] = asc;		// Additional Sense Code
157  target[13] = ascq;		// Additional Sense Code Qualifier
158  target[14] = fru;		// Field Replaceable Unit code
159 }
160 
161 static void (*SCSILog)(const char *, const char *format, ...);
162 static void InitModePages(void);
163 
164 static scsicd_timestamp_t lastts;
165 static int64_t monotonic_timestamp;
166 static int64_t pce_lastsapsp_timestamp;
167 
168 scsicd_t cd;
169 scsicd_bus_t cd_bus;
170 static cdda_t cdda;
171 
172 static SimpleFIFO<uint8_t> *din = NULL;
173 
174 static TOC toc;
175 
176 static uint32_t read_sec_start;
177 static uint32_t read_sec;
178 static uint32_t read_sec_end;
179 
180 static int32_t CDReadTimer;
181 static uint32_t SectorAddr;
182 static uint32_t SectorCount;
183 
184 
185 enum
186 {
187  PHASE_BUS_FREE = 0,
188  PHASE_COMMAND,
189  PHASE_DATA_IN,
190  PHASE_DATA_OUT,
191  PHASE_STATUS,
192  PHASE_MESSAGE_IN,
193  PHASE_MESSAGE_OUT
194 };
195 static unsigned int CurrentPhase;
196 static void ChangePhase(const unsigned int new_phase);
197 
198 
FixOPV(void)199 static void FixOPV(void)
200 {
201  if(!cdda.CDDADivAccVolFudge)
202   cdda.CDDADivAccVolFudge = 100;
203 
204  for(int port = 0; port < 2; port++)
205  {
206   int32_t tmpvol = cdda.CDDAVolume[port] * 100 / (2 * cdda.CDDADivAccVolFudge);
207 
208   //printf("TV: %d\n", tmpvol);
209 
210   cdda.OutPortVolumeCache[port] = tmpvol;
211 
212   if(cdda.OutPortChSelect[port] & 0x01)
213    cdda.OutPortChSelectCache[port] = 0;
214   else if(cdda.OutPortChSelect[port] & 0x02)
215    cdda.OutPortChSelectCache[port] = 1;
216   else
217   {
218    cdda.OutPortChSelectCache[port] = 0;
219    cdda.OutPortVolumeCache[port] = 0;
220   }
221  }
222 }
223 
VirtualReset(void)224 static void VirtualReset(void)
225 {
226  InitModePages();
227 
228  din->Flush();
229 
230  CDReadTimer = 0;
231 
232  pce_lastsapsp_timestamp = monotonic_timestamp;
233 
234  SectorAddr = SectorCount = 0;
235  read_sec_start = read_sec = 0;
236  read_sec_end = ~0;
237 
238  cdda.PlayMode = PLAYMODE_SILENT;
239  cdda.CDDAReadPos = 0;
240  cdda.CDDAStatus = CDDASTATUS_STOPPED;
241  cdda.CDDADiv = 0;
242 
243  cdda.ScanMode = 0;
244  cdda.scan_sec_end = 0;
245 
246  cdda.OversamplePos = 0;
247  memset(cdda.sr, 0, sizeof(cdda.sr));
248  memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer));
249  memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
250 
251  memset(cd.data_out, 0, sizeof(cd.data_out));
252  cd.data_out_pos = 0;
253  cd.data_out_want = 0;
254 
255 
256  FixOPV();
257 
258  ChangePhase(PHASE_BUS_FREE);
259 }
260 
SCSICD_Power(scsicd_timestamp_t system_timestamp)261 void SCSICD_Power(scsicd_timestamp_t system_timestamp)
262 {
263  memset(&cd, 0, sizeof(scsicd_t));
264  memset(&cd_bus, 0, sizeof(scsicd_bus_t));
265 
266  monotonic_timestamp = system_timestamp;
267 
268  cd.DiscChanged = false;
269 
270  if(Cur_CDIF && !TrayOpen)
271   Cur_CDIF->ReadTOC(&toc);
272 
273  CurrentPhase = PHASE_BUS_FREE;
274 
275  VirtualReset();
276 }
277 
278 
SCSICD_SetDB(uint8_t data)279 void SCSICD_SetDB(uint8_t data)
280 {
281  cd_bus.DB = data;
282  //printf("Set DB: %02x\n", data);
283 }
284 
SCSICD_SetACK(bool set)285 void SCSICD_SetACK(bool set)
286 {
287  SetkingACK(set);
288  //printf("Set ACK: %d\n", set);
289 }
290 
SCSICD_SetSEL(bool set)291 void SCSICD_SetSEL(bool set)
292 {
293  SetkingSEL(set);
294  //printf("Set SEL: %d\n", set);
295 }
296 
SCSICD_SetRST(bool set)297 void SCSICD_SetRST(bool set)
298 {
299  SetkingRST(set);
300  //printf("Set RST: %d\n", set);
301 }
302 
SCSICD_SetATN(bool set)303 void SCSICD_SetATN(bool set)
304 {
305  SetkingATN(set);
306  //printf("Set ATN: %d\n", set);
307 }
308 
GenSubQFromSubPW(void)309 static void GenSubQFromSubPW(void)
310 {
311  uint8_t SubQBuf[0xC];
312 
313  memset(SubQBuf, 0, 0xC);
314 
315  for(int i = 0; i < 96; i++)
316   SubQBuf[i >> 3] |= ((cd.SubPWBuf[i] & 0x40) >> 6) << (7 - (i & 7));
317 
318  //printf("Real %d/ SubQ %d - ", read_sec, BCD_to_U8(SubQBuf[7]) * 75 * 60 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150);
319  // Debug code, remove me.
320  //for(int i = 0; i < 0xC; i++)
321  // printf("%02x ", SubQBuf[i]);
322  //printf("\n");
323 
324  if(!subq_check_checksum(SubQBuf))
325  {
326   //SCSIDBG("SubQ checksum error!");
327  }
328  else
329  {
330   memcpy(cd.SubQBuf_Last, SubQBuf, 0xC);
331 
332   uint8_t adr = SubQBuf[0] & 0xF;
333 
334   if(adr <= 0x3)
335    memcpy(cd.SubQBuf[adr], SubQBuf, 0xC);
336 
337   //if(adr == 0x02)
338   //for(int i = 0; i < 12; i++)
339   // printf("%02x\n", cd.SubQBuf[0x2][i]);
340  }
341 }
342 
343 
344 #define STATUS_GOOD		0
345 #define STATUS_CHECK_CONDITION	1
346 #define STATUS_CONDITION_MET	2
347 #define STATUS_BUSY		4
348 #define STATUS_INTERMEDIATE	8
349 
350 #define SENSEKEY_NO_SENSE		0x0
351 #define SENSEKEY_NOT_READY		0x2
352 #define SENSEKEY_MEDIUM_ERROR		0x3
353 #define SENSEKEY_HARDWARE_ERROR		0x4
354 #define SENSEKEY_ILLEGAL_REQUEST	0x5
355 #define SENSEKEY_UNIT_ATTENTION		0x6
356 #define SENSEKEY_ABORTED_COMMAND	0xB
357 
358 #define ASC_MEDIUM_NOT_PRESENT		0x3A
359 
360 
361 // NEC sub-errors(ASC), no ASCQ.
362 #define NSE_NO_DISC			0x0B		// Used with SENSEKEY_NOT_READY	- This condition occurs when tray is closed with no disc present.
363 #define NSE_TRAY_OPEN			0x0D		// Used with SENSEKEY_NOT_READY
364 #define NSE_SEEK_ERROR			0x15
365 #define NSE_HEADER_READ_ERROR		0x16		// Used with SENSEKEY_MEDIUM_ERROR
366 #define NSE_NOT_AUDIO_TRACK		0x1C		// Used with SENSEKEY_MEDIUM_ERROR
367 #define NSE_NOT_DATA_TRACK		0x1D		// Used with SENSEKEY_MEDIUM_ERROR
368 #define NSE_INVALID_COMMAND		0x20
369 #define NSE_INVALID_ADDRESS		0x21
370 #define NSE_INVALID_PARAMETER		0x22
371 #define NSE_END_OF_VOLUME		0x25
372 #define NSE_INVALID_REQUEST_IN_CDB	0x27
373 #define NSE_DISC_CHANGED		0x28		// Used with SENSEKEY_UNIT_ATTENTION
374 #define NSE_AUDIO_NOT_PLAYING		0x2C
375 
376 // ASC, ASCQ pair
377 #define AP_UNRECOVERED_READ_ERROR	0x11, 0x00
378 #define AP_LEC_UNCORRECTABLE_ERROR	0x11, 0x05
379 #define AP_CIRC_UNRECOVERED_ERROR	0x11, 0x06
380 
381 #define AP_UNKNOWN_MEDIUM_FORMAT	0x30, 0x01
382 #define AP_INCOMPAT_MEDIUM_FORMAT	0x30, 0x02
383 
ChangePhase(const unsigned int new_phase)384 static void ChangePhase(const unsigned int new_phase)
385 {
386  //printf("New phase: %d %lld\n", new_phase, monotonic_timestamp);
387  switch(new_phase)
388  {
389   case PHASE_BUS_FREE:
390 		SetBSY(false);
391 		SetMSG(false);
392 		SetCD(false);
393 		SetIO(false);
394 		SetREQ(false);
395 
396 	        CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_DONE);
397 		break;
398 
399   case PHASE_DATA_IN:		// Us to them
400 		SetBSY(true);
401 	        SetMSG(false);
402 	        SetCD(false);
403 	        SetIO(true);
404 	        //SetREQ(true);
405 		SetREQ(false);
406 		break;
407 
408   case PHASE_STATUS:		// Us to them
409 		SetBSY(true);
410 		SetMSG(false);
411 		SetCD(true);
412 		SetIO(true);
413 		SetREQ(true);
414 		break;
415 
416   case PHASE_MESSAGE_IN:	// Us to them
417 		SetBSY(true);
418 		SetMSG(true);
419 		SetCD(true);
420 		SetIO(true);
421 		SetREQ(true);
422 		break;
423 
424 
425   case PHASE_DATA_OUT:		// Them to us
426 		SetBSY(true);
427 	        SetMSG(false);
428 	        SetCD(false);
429 	        SetIO(false);
430 	        SetREQ(true);
431 		break;
432 
433   case PHASE_COMMAND:		// Them to us
434 		SetBSY(true);
435 	        SetMSG(false);
436 	        SetCD(true);
437 	        SetIO(false);
438 	        SetREQ(true);
439 		break;
440 
441   case PHASE_MESSAGE_OUT:	// Them to us
442 		SetBSY(true);
443   		SetMSG(true);
444 		SetCD(true);
445 		SetIO(false);
446 		SetREQ(true);
447 		break;
448  }
449  CurrentPhase = new_phase;
450 }
451 
SendStatusAndMessage(uint8_t status,uint8_t message)452 static void SendStatusAndMessage(uint8_t status, uint8_t message)
453 {
454  // This should never ever happen, but that doesn't mean it won't. ;)
455  if(din->in_count)
456  {
457   //printf("[SCSICD] BUG: %d bytes still in SCSI CD FIFO\n", din->in_count);
458   din->Flush();
459  }
460 
461  cd.message_pending = message;
462 
463  cd.status_sent = FALSE;
464  cd.message_sent = FALSE;
465 
466  if(WhichSystem == SCSICD_PCE)
467  {
468   if(status == STATUS_GOOD || status == STATUS_CONDITION_MET)
469    cd_bus.DB = 0x00;
470   else
471    cd_bus.DB = 0x01;
472  }
473  else
474   cd_bus.DB = status << 1;
475 
476  ChangePhase(PHASE_STATUS);
477 }
478 
DoSimpleDataIn(const uint8_t * data_in,uint32_t len)479 static void DoSimpleDataIn(const uint8_t *data_in, uint32_t len)
480 {
481  din->Write(data_in, len);
482 
483  cd.data_transfer_done = true;
484 
485  ChangePhase(PHASE_DATA_IN);
486 }
487 
SCSICD_SetDisc(bool new_tray_open,CDIF * cdif,bool no_emu_side_effects)488 void SCSICD_SetDisc(bool new_tray_open, CDIF *cdif, bool no_emu_side_effects)
489 {
490  Cur_CDIF = cdif;
491 
492  // Closing the tray.
493  if(TrayOpen && !new_tray_open)
494  {
495   TrayOpen = false;
496 
497   if(cdif)
498   {
499    cdif->ReadTOC(&toc);
500 
501    if(!no_emu_side_effects)
502    {
503     memset(cd.SubQBuf, 0, sizeof(cd.SubQBuf));
504     memset(cd.SubQBuf_Last, 0, sizeof(cd.SubQBuf_Last));
505     cd.DiscChanged = true;
506    }
507   }
508  }
509  else if(!TrayOpen && new_tray_open)	// Opening the tray
510  {
511   TrayOpen = true;
512  }
513 }
514 
CommandCCError(int key,int asc=0,int ascq=0)515 static void CommandCCError(int key, int asc = 0, int ascq = 0)
516 {
517  //printf("[SCSICD] CC Error: %02x %02x %02x\n", key, asc, ascq);
518 
519  cd.key_pending = key;
520  cd.asc_pending = asc;
521  cd.ascq_pending = ascq;
522  cd.fru_pending = 0x00;
523 
524  SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
525 }
526 
ValidateRawDataSector(uint8_t * data,const uint32_t lba)527 static bool ValidateRawDataSector(uint8_t *data, const uint32_t lba)
528 {
529  if(!Cur_CDIF->ValidateRawSector(data))
530  {
531   MDFN_DispMessage(_("Uncorrectable data at sector %d"), lba);
532   MDFN_PrintError(_("Uncorrectable data at sector %d"), lba);
533 
534   din->Flush();
535   cd.data_transfer_done = false;
536 
537   CommandCCError(SENSEKEY_MEDIUM_ERROR, AP_LEC_UNCORRECTABLE_ERROR);
538   return(false);
539  }
540 
541  return(true);
542 }
543 
DoMODESELECT6(const uint8_t * cdb)544 static void DoMODESELECT6(const uint8_t *cdb)
545 {
546  if(cdb[4])
547  {
548   cd.data_out_pos = 0;
549   cd.data_out_want = cdb[4];
550   //printf("Switch to DATA OUT phase, len: %d\n", cd.data_out_want);
551 
552   ChangePhase(PHASE_DATA_OUT);
553  }
554  else
555   SendStatusAndMessage(STATUS_GOOD, 0x00);
556 }
557 
558 /*
559  All Japan Female Pro Wrestle:
560 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0a
561 
562  Kokuu Hyouryuu Nirgends:
563 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0f
564 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0f
565 
566  Last Imperial Prince:
567 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0f
568 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0f
569 
570  Megami Paradise II:
571 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0a
572 
573  Miraculum:
574 	Datumama: 7, 00 00 00 00 29 01 00
575 	Datumama: 10, 00 00 00 00 00 00 00 00 00 0f
576 	Datumama: 7, 00 00 00 00 29 01 00
577 	Datumama: 10, 00 00 00 00 00 00 00 00 00 00
578 	Datumama: 7, 00 00 00 00 29 01 00
579 
580  Pachio Kun FX:
581 	Datumama: 10, 00 00 00 00 00 00 00 00 00 14
582 
583  Return to Zork:
584 	Datumama: 10, 00 00 00 00 00 00 00 00 00 00
585 
586  Sotsugyou II:
587 	Datumama: 10, 00 00 00 00 01 00 00 00 00 01
588 
589  Tokimeki Card Paradise:
590 	Datumama: 10, 00 00 00 00 00 00 00 00 00 14
591 	Datumama: 10, 00 00 00 00 00 00 00 00 00 07
592 
593  Tonari no Princess Rolfee:
594 	Datumama: 10, 00 00 00 00 00 00 00 00 00 00
595 
596  Zoku Hakutoi Monogatari:
597 	Datumama: 10, 00 00 00 00 00 00 00 00 00 14
598 */
599 
600       // Page 151: MODE SENSE(6)
601 	// PC = 0 current
602 	// PC = 1 Changeable
603 	// PC = 2 Default
604 	// PC = 3 Saved
605       // Page 183: Mode parameter header.
606       // Page 363: CD-ROM density codes.
607       // Page 364: CD-ROM mode page codes.
608       // Page 469: ASC and ASCQ table
609 
610 
611 struct ModePageParam
612 {
613  uint8_t default_value;
614  uint8_t alterable_mask;	// Alterable mask reported when PC == 1
615  uint8_t real_mask;		// Real alterable mask.
616 };
617 
618 struct ModePage
619 {
620  const uint8_t code;
621  const uint8_t param_length;
622  const ModePageParam params[64];	// 64 should be more than enough
623  uint8_t current_value[64];
624 };
625 
626 /*
627  Mode pages present:
628 	0x00:
629 	0x0E:
630 	0x28:
631 	0x29:
632 	0x2A:
633 	0x2B:
634 	0x3F(Yes, not really a mode page but a fetch method)
635 */
636 // Remember to update the code in StateAction() if we change the number or layout of modepages here.
637 static const int NumModePages = 5;
638 static ModePage ModePages[NumModePages] =
639 {
640  // Unknown
641  { 0x28,
642    0x04,
643    {
644         { 0x00, 0x00, 0xFF },
645         { 0x00, 0x00, 0xFF },
646         { 0x00, 0x00, 0xFF },
647         { 0x00, 0x00, 0xFF },
648    }
649  },
650 
651  // Unknown
652  { 0x29,
653    0x01,
654    {
655 	{ 0x00, 0x00, 0xFF },
656    }
657  },
658 
659  // Unknown
660  { 0x2a,
661    0x02,
662    {
663         { 0x00, 0x00, 0xFF },
664         { 0x11, 0x00, 0xFF },
665    }
666  },
667 
668  // CD-DA playback speed modifier
669  { 0x2B,
670    0x01,
671    {
672 	{ 0x00, 0x00, 0xFF },
673    }
674  },
675 
676  // 0x0E goes last, for correct order of return data when page code == 0x3F
677  // Real mask values are probably not right; some functionality not emulated yet.
678  // CD-ROM audio control parameters
679  { 0x0E,
680    0x0E,
681    {
682         { 0x04, 0x04, 0x04 },   // Immed
683         { 0x00, 0x00, 0x00 },   // Reserved
684         { 0x00, 0x00, 0x00 }, // Reserved
685         { 0x00, 0x01, 0x01 }, // Reserved?
686         { 0x00, 0x00, 0x00 },   // MSB of LBA per second.
687         { 0x00, 0x00, 0x00 }, // LSB of LBA per second.
688         { 0x01, 0x01, 0x03 }, // Outport port 0 channel selection.
689         { 0xFF, 0x00, 0x00 }, // Outport port 0 volume.
690         { 0x02, 0x02, 0x03 }, // Outport port 1 channel selection.
691         { 0xFF, 0x00, 0x00 }, // Outport port 1 volume.
692         { 0x00, 0x00, 0x00 }, // Outport port 2 channel selection.
693         { 0x00, 0x00, 0x00 }, // Outport port 2 volume.
694         { 0x00, 0x00, 0x00 }, // Outport port 3 channel selection.
695         { 0x00, 0x00, 0x00 }, // Outport port 3 volume.
696    }
697  },
698 };
699 
UpdateMPCacheP(const ModePage * mp)700 static void UpdateMPCacheP(const ModePage* mp)
701 {
702   switch(mp->code)
703   {
704    case 0x0E:
705 	     {
706               const uint8_t *pd = &mp->current_value[0];
707 
708               for(int i = 0; i < 2; i++)
709                cdda.OutPortChSelect[i] = pd[6 + i * 2];
710               FixOPV();
711 	     }
712 	     break;
713 
714    case 0x28:
715 	     break;
716 
717    case 0x29:
718 	     break;
719 
720    case 0x2A:
721 	     break;
722 
723    case 0x2B:
724 	    {
725              int speed;
726              int rate;
727 
728 	     //
729 	     // Not sure what the actual limits are, or what happens when exceeding them, but these will at least keep the
730 	     // CD-DA playback system from imploding in on itself.
731 	     //
732 	     // The range of speed values accessible via the BIOS CD-DA player is apparently -10 to 10.
733 	     //
734 	     // No game is known to use the CD-DA playback speed control.  It may be useful in homebrew to lower the rate for fitting more CD-DA onto the disc,
735 	     // is implemented on the PC-FX in such a way that it degrades audio quality, so it wouldn't really make sense to increase the rate in homebrew.
736 	     //
737 	     // Due to performance considerations, we only partially emulate the CD-DA oversampling filters used on the PC Engine and PC-FX, and instead
738 	     // blast impulses into the 1.78MHz buffer, relying on the final sound resampler to kill spectrum mirrors.  This is less than ideal, but generally
739 	     // works well in practice, except when lowering CD-DA playback rate...which causes the spectrum mirrors to enter the non-murder zone, causing
740 	     // the sound output amplitude to approach overflow levels.
741 	     // But, until there's a killer PC-FX homebrew game that necessitates more computationally-expensive CD-DA handling,
742 	     // I don't see a good reason to change how CD-DA resampling is currently implemented.
743 	     //
744 	     speed = std::max<int>(-32, std::min<int>(32, (int8_t)mp->current_value[0]));
745 	     rate = 44100 + 441 * speed;
746 
747              //printf("[SCSICD] Speed: %d(pre-clamped=%d) %d\n", speed, (int8_t)mp->current_value[0], rate);
748              cdda.CDDADivAcc = ((int64_t)System_Clock * (1024 * 1024) / (2 * rate));
749 	     cdda.CDDADivAccVolFudge = 100 + speed;
750 	     FixOPV();	// Resampler impulse amplitude volume adjustment(call after setting cdda.CDDADivAccVolFudge)
751 	    }
752 	    break;
753   }
754 }
755 
UpdateMPCache(uint8_t code)756 static void UpdateMPCache(uint8_t code)
757 {
758  for(int pi = 0; pi < NumModePages; pi++)
759  {
760   const ModePage* mp = &ModePages[pi];
761 
762   if(mp->code == code)
763   {
764    UpdateMPCacheP(mp);
765    break;
766   }
767  }
768 }
769 
InitModePages(void)770 static void InitModePages(void)
771 {
772  for(int pi = 0; pi < NumModePages; pi++)
773  {
774   ModePage *mp = &ModePages[pi];
775   const ModePageParam *params = &ModePages[pi].params[0];
776 
777   for(int parami = 0; parami < mp->param_length; parami++)
778    mp->current_value[parami] = params[parami].default_value;
779 
780   UpdateMPCacheP(mp);
781  }
782 }
783 
FinishMODESELECT6(const uint8_t * data,const uint8_t data_len)784 static void FinishMODESELECT6(const uint8_t *data, const uint8_t data_len)
785 {
786 	uint8_t mode_data_length, medium_type, device_specific, block_descriptor_length;
787 	uint32_t offset = 0;
788 
789         //printf("[SCSICD] Mode Select (6) Data: Length=0x%02x, ", data_len);
790         //for(uint32_t i = 0; i < data_len; i++)
791         // printf("0x%02x ", data[i]);
792         //printf("\n");
793 
794         if(data_len < 4)
795         {
796          CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
797          return;
798         }
799 
800 	mode_data_length = data[offset++];
801 	medium_type = data[offset++];
802 	device_specific = data[offset++];
803 	block_descriptor_length = data[offset++];
804 
805 	// For now, shut up gcc.
806 	(void)mode_data_length;
807 	(void)medium_type;
808 	(void)device_specific;
809 
810 	if(block_descriptor_length & 0x7)
811 	{
812 	 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
813 	 return;
814 	}
815 
816 	if((offset + block_descriptor_length) > data_len)
817 	{
818 	 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
819 	 return;
820 	}
821 
822 	// TODO: block descriptors.
823 	offset += block_descriptor_length;
824 
825 	// Now handle mode pages
826 	while(offset < data_len)
827 	{
828 	 const uint8_t code = data[offset++];
829 	 uint8_t param_len = 0;
830 	 bool page_found = false;
831 
832 	 if(code == 0x00)
833 	 {
834 	  if((offset + 0x5) > data_len)
835 	  {
836            CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
837 	   return;
838 	  }
839 
840 	  UpdateMPCache(0x00);
841 
842 	  offset += 0x5;
843  	  continue;
844 	 }
845 
846 	 if(offset >= data_len)
847 	 {
848           CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
849 	  return;
850 	 }
851 
852          param_len = data[offset++];
853 
854 	 for(int pi = 0; pi < NumModePages; pi++)
855 	 {
856 	  ModePage *mp = &ModePages[pi];
857 
858 	  if(code == mp->code)
859 	  {
860 	   page_found = true;
861 
862 	   if(param_len != mp->param_length)
863 	   {
864             CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
865             return;
866 	   }
867 
868 	   if((param_len + offset) > data_len)
869 	   {
870             CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
871             return;
872 	   }
873 
874 	   for(int parami = 0; parami < mp->param_length; parami++)
875 	   {
876 	    mp->current_value[parami] &= ~mp->params[parami].real_mask;
877 	    mp->current_value[parami] |= (data[offset++]) & mp->params[parami].real_mask;
878 	   }
879 
880 	   UpdateMPCacheP(mp);
881 	   break;
882 	  }
883 	 }
884 
885 	 if(!page_found)
886 	 {
887 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
888 	  return;
889 	 }
890 	}
891 
892 	SendStatusAndMessage(STATUS_GOOD, 0x00);
893 }
894 
DoMODESENSE6(const uint8_t * cdb)895 static void DoMODESENSE6(const uint8_t *cdb)
896 {
897  unsigned int PC = (cdb[2] >> 6) & 0x3;
898  unsigned int PageCode = cdb[2] & 0x3F;
899  bool DBD = cdb[1] & 0x08;
900  int AllocSize = cdb[4];
901  int index = 0;
902  uint8_t data_in[8192];
903  uint8_t PageMatchOR = 0x00;
904  bool AnyPageMatch = false;
905 
906  //SCSIDBG("Mode sense 6: %02x %d %d %d", PageCode, PC, DBD, AllocSize);
907 
908  if(!AllocSize)
909  {
910   SendStatusAndMessage(STATUS_GOOD, 0x00);
911   return;
912  }
913 
914  if(PC == 3)
915  {
916   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
917   return;
918  }
919 
920  if(PageCode == 0x00)	// Special weird case.
921  {
922   if(DBD || PC)
923   {
924    CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
925    return;
926   }
927 
928   memset(data_in, 0, 0xA);
929   data_in[0] = 0x09;
930   data_in[2] = 0x80;
931   data_in[9] = 0x0F;
932 
933   if(AllocSize > 0xA)
934    AllocSize = 0xA;
935 
936   DoSimpleDataIn(data_in, AllocSize);
937   return;
938  }
939 
940  data_in[0] = 0x00;	// Fill this in later.
941  data_in[1] = 0x00;	// Medium type
942  data_in[2] = 0x00;	// Device-specific parameter.
943  data_in[3] = DBD ? 0x00 : 0x08;	// Block descriptor length.
944  index += 4;
945 
946  if(!DBD)
947  {
948   data_in[index++] = 0x00;	// Density code.
949   MDFN_en24msb(&data_in[index], 0x6E); // FIXME: Number of blocks?
950   index += 3;
951 
952   data_in[index++] = 0x00;	// Reserved
953   MDFN_en24msb(&data_in[index], 0x800); // Block length;
954   index += 3;
955  }
956 
957  PageMatchOR = 0x00;
958  if(PageCode == 0x3F)
959   PageMatchOR = 0x3F;
960 
961  for(int pi = 0; pi < NumModePages; pi++)
962  {
963   const ModePage *mp = &ModePages[pi];
964   const ModePageParam *params = &ModePages[pi].params[0];
965 
966   if((mp->code | PageMatchOR) != PageCode)
967    continue;
968 
969   AnyPageMatch = true;
970 
971   data_in[index++] = mp->code;
972   data_in[index++] = mp->param_length;
973 
974   for(int parami = 0; parami < mp->param_length; parami++)
975   {
976    uint8_t data;
977 
978    if(PC == 0x02)
979     data = params[parami].default_value;
980    else if(PC == 0x01)
981     data = params[parami].alterable_mask;
982    else
983     data = mp->current_value[parami];
984 
985    data_in[index++] = data;
986   }
987  }
988 
989  if(!AnyPageMatch)
990  {
991   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
992   return;
993  }
994 
995  if(AllocSize > index)
996   AllocSize = index;
997 
998  data_in[0] = AllocSize - 1;
999 
1000  DoSimpleDataIn(data_in, AllocSize);
1001 }
1002 
DoSTARTSTOPUNIT6(const uint8_t * cdb)1003 static void DoSTARTSTOPUNIT6(const uint8_t *cdb)
1004 {
1005  //bool Immed = cdb[1] & 0x01;
1006  //bool LoEj = cdb[4] & 0x02;
1007  //bool Start = cdb[4] & 0x01;
1008 
1009  //SCSIDBG("Do start stop unit 6: %d %d %d\n", Immed, LoEj, Start);
1010 
1011  SendStatusAndMessage(STATUS_GOOD, 0x00);
1012 }
1013 
DoREZEROUNIT(const uint8_t * cdb)1014 static void DoREZEROUNIT(const uint8_t *cdb)
1015 {
1016  //SCSIDBG("Rezero Unit: %02x\n", cdb[5]);
1017  SendStatusAndMessage(STATUS_GOOD, 0x00);
1018 }
1019 
1020 // This data was originally taken from a PC-FXGA software loader, but
1021 // while it was mostly correct(maybe it is correct for the FXGA, but not for the PC-FX?),
1022 // it was 3 bytes too long, and the last real byte was 0x45 instead of 0x20.
1023 // TODO:  Investigate this discrepancy by testing an FXGA with the official loader software.
1024 #if 0
1025 static const uint8_t InqData[0x24] =
1026 {
1027  // Standard
1028  0x05, 0x80, 0x02, 0x00,
1029 
1030  // Additional Length
1031  0x1F,
1032 
1033  // Vendor Specific
1034  0x00, 0x00, 0x00, 0x4E, 0x45, 0x43, 0x20, 0x20,
1035  0x20, 0x20, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F,
1036  0x4D, 0x20, 0x44, 0x52, 0x49, 0x56, 0x45, 0x3A,
1037  0x46, 0x58, 0x20, 0x31, 0x2E, 0x30, 0x20
1038 };
1039 #endif
1040 
1041 // Miraculum behaves differently if the last byte(offset 0x23) of the inquiry data is 0x45(ASCII character 'E').  Relavent code is at PC=0x3E382
1042 // If it's = 0x45, it will run MODE SELECT, and transfer this data to the CD unit: 00 00 00 00 29 01 00
1043 static const uint8_t InqData[0x24] =
1044 {
1045  // Peripheral device-type: CD-ROM/read-only direct access device
1046  0x05,
1047 
1048  // Removable media: yes
1049  // Device-type qualifier: 0
1050  0x80,
1051 
1052  // ISO version: 0
1053  // ECMA version: 0
1054  // ANSI version: 2	(SCSI-2? ORLY?)
1055  0x02,
1056 
1057  // Supports asynchronous event notification: no
1058  // Supports the terminate I/O process message: no
1059  // Response data format: 0 (not exactly correct, not exactly incorrect, meh. :b)
1060  0x00,
1061 
1062  // Additional Length
1063  0x1F,
1064 
1065  // Reserved
1066  0x00, 0x00,
1067 
1068  // Yay, no special funky features.
1069  0x00,
1070 
1071  // 8-15, vendor ID
1072  // NEC
1073  0x4E, 0x45, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20,
1074 
1075  // 16-31, product ID
1076  // CD-ROM DRIVE:FX
1077  0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x44, 0x52, 0x49, 0x56, 0x45, 0x3A, 0x46, 0x58, 0x20,
1078 
1079  // 32-35, product revision level
1080  // 1.0
1081  0x31, 0x2E, 0x30, 0x20
1082 };
1083 
DoINQUIRY(const uint8_t * cdb)1084 static void DoINQUIRY(const uint8_t *cdb)
1085 {
1086  unsigned int AllocSize = (cdb[4] < sizeof(InqData)) ? cdb[4] : sizeof(InqData);
1087 
1088  if(AllocSize)
1089   DoSimpleDataIn(InqData, AllocSize);
1090  else
1091   SendStatusAndMessage(STATUS_GOOD, 0x00);
1092 }
1093 
DoNEC_NOP(const uint8_t * cdb)1094 static void DoNEC_NOP(const uint8_t *cdb)
1095 {
1096  SendStatusAndMessage(STATUS_GOOD, 0x00);
1097 }
1098 
1099 
1100 
1101 /********************************************************
1102 *							*
1103 *	PC-FX CD Command 0xDC - EJECT			*
1104 *				  			*
1105 ********************************************************/
DoNEC_EJECT(const uint8_t * cdb)1106 static void DoNEC_EJECT(const uint8_t *cdb)
1107 {
1108  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_REQUEST_IN_CDB);
1109 }
1110 
DoREQUESTSENSE(const uint8_t * cdb)1111 static void DoREQUESTSENSE(const uint8_t *cdb)
1112 {
1113  uint8_t data_in[8192];
1114 
1115  MakeSense(data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending);
1116 
1117  DoSimpleDataIn(data_in, 18);
1118 
1119  cd.key_pending = 0;
1120  cd.asc_pending = 0;
1121  cd.ascq_pending = 0;
1122  cd.fru_pending = 0;
1123 }
1124 
EncodeM3TOC(uint8_t * buf,uint8_t POINTER_RAW,int32_t LBA,uint32_t PLBA,uint8_t control)1125 static void EncodeM3TOC(uint8_t *buf, uint8_t POINTER_RAW, int32_t LBA, uint32_t PLBA, uint8_t control)
1126 {
1127  uint8_t MIN, SEC, FRAC;
1128  uint8_t PMIN, PSEC, PFRAC;
1129 
1130  LBA_to_AMSF(LBA, &MIN, &SEC, &FRAC);
1131  LBA_to_AMSF(PLBA, &PMIN, &PSEC, &PFRAC);
1132 
1133  buf[0x0] = control << 4;
1134  buf[0x1] = 0x00;	// TNO
1135  buf[0x2] = POINTER_RAW;
1136  buf[0x3] = U8_to_BCD(MIN);
1137  buf[0x4] = U8_to_BCD(SEC);
1138  buf[0x5] = U8_to_BCD(FRAC);
1139  buf[0x6] = 0x00;	// Zero
1140  buf[0x7] = U8_to_BCD(PMIN);
1141  buf[0x8] = U8_to_BCD(PSEC);
1142  buf[0x9] = U8_to_BCD(PFRAC);
1143 }
1144 
1145 /********************************************************
1146 *							*
1147 *	PC-FX CD Command 0xDE - Get Directory Info	*
1148 *							*
1149 ********************************************************/
DoNEC_GETDIRINFO(const uint8_t * cdb)1150 static void DoNEC_GETDIRINFO(const uint8_t *cdb)
1151 {
1152  // Problems:
1153  //	Mode 0x03 has a few semi-indeterminate(but within a range, and they only change when the disc is reloaded) fields on a real PC-FX, that correspond to where in the lead-in area the data
1154  //	was read, that we don't bother to handle here.
1155  //	Mode 0x03 returns weird/wrong control field data for the "last track" and "leadout" entries in the "Blue Breaker" TOC.
1156  //		A bug in the PC-FX CD firmware, or an oddity of the disc(maybe other PC-FX discs are similar)?  Or maybe it's an undefined field in that context?
1157  //	"Match" value of 0xB0 is probably not handled properly.  Is it to return the catalog number, or something else?
1158 
1159  uint8_t data_in[2048];
1160  uint32_t data_in_size = 0;
1161 
1162  memset(data_in, 0, sizeof(data_in));
1163 
1164  switch(cdb[1] & 0x03)
1165  {
1166   // This commands returns relevant raw TOC data as encoded in the Q subchannel(sans the CRC bytes).
1167   case 0x3:
1168    {
1169     int offset = 0;
1170     int32_t lilba = -150;
1171     uint8_t match = cdb[2];
1172 
1173     if(match != 0x00 && match != 0xA0 && match != 0xA1 && match != 0xA2 && match != 0xB0)
1174     {
1175      CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS);
1176      return;
1177     }
1178     memset(data_in, 0, sizeof(data_in));
1179 
1180     data_in[0] = 0x00;	// Size MSB???
1181     data_in[1] = 0x00;	// Total Size - 2(we'll fill it in later).
1182     offset = 2;
1183 
1184     if(!match || match == 0xA0)
1185     {
1186      EncodeM3TOC(&data_in[offset], 0xA0, lilba, toc.first_track * 75 * 60 - 150, toc.tracks[toc.first_track].control);
1187      lilba++;
1188      offset += 0xA;
1189     }
1190 
1191     if(!match || match == 0xA1)
1192     {
1193      EncodeM3TOC(&data_in[offset], 0xA1, lilba, toc.last_track * 75 * 60 - 150, toc.tracks[toc.last_track].control);
1194      lilba++;
1195      offset += 0xA;
1196     }
1197 
1198     if(!match || match == 0xA2)
1199     {
1200      EncodeM3TOC(&data_in[offset], 0xA2, lilba, toc.tracks[100].lba, toc.tracks[100].control);
1201      lilba++;
1202      offset += 0xA;
1203     }
1204 
1205     if(!match)
1206      for(int track = toc.first_track; track <= toc.last_track; track++)
1207      {
1208       EncodeM3TOC(&data_in[offset], U8_to_BCD(track), lilba, toc.tracks[track].lba, toc.tracks[track].control);
1209       lilba++;
1210       offset += 0xA;
1211      }
1212 
1213     if(match == 0xB0)
1214     {
1215      memset(&data_in[offset], 0, 0x14);
1216      offset += 0x14;
1217     }
1218 
1219     assert((unsigned int)offset <= sizeof(data_in));
1220     data_in_size = offset;
1221     MDFN_en16msb(&data_in[0], offset - 2);
1222    }
1223    break;
1224 
1225   case 0x0:
1226    data_in[0] = U8_to_BCD(toc.first_track);
1227    data_in[1] = U8_to_BCD(toc.last_track);
1228 
1229    data_in_size = 4;
1230    break;
1231 
1232   case 0x1:
1233    {
1234     uint8_t m, s, f;
1235 
1236     LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
1237 
1238     data_in[0] = U8_to_BCD(m);
1239     data_in[1] = U8_to_BCD(s);
1240     data_in[2] = U8_to_BCD(f);
1241 
1242     data_in_size = 4;
1243    }
1244    break;
1245 
1246   case 0x2:
1247    {
1248     uint8_t m, s, f;
1249     int track = BCD_to_U8(cdb[2]);
1250 
1251     if(track < toc.first_track || track > toc.last_track)
1252     {
1253      CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS);
1254      return;
1255     }
1256 
1257     LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
1258 
1259     data_in[0] = U8_to_BCD(m);
1260     data_in[1] = U8_to_BCD(s);
1261     data_in[2] = U8_to_BCD(f);
1262     data_in[3] = toc.tracks[track].control;
1263     data_in_size = 4;
1264    }
1265    break;
1266  }
1267 
1268  DoSimpleDataIn(data_in, data_in_size);
1269 }
1270 
DoREADTOC(const uint8_t * cdb)1271 static void DoREADTOC(const uint8_t *cdb)
1272 {
1273  uint8_t data_in[8192];
1274  int FirstTrack = toc.first_track;
1275  int LastTrack = toc.last_track;
1276  int StartingTrack = cdb[6];
1277  unsigned int AllocSize = (cdb[7] << 8) | cdb[8];
1278  unsigned int RealSize = 0;
1279  const bool WantInMSF = cdb[1] & 0x2;
1280 
1281  if(!AllocSize)
1282  {
1283   SendStatusAndMessage(STATUS_GOOD, 0x00);
1284   return;
1285  }
1286 
1287  if((cdb[1] & ~0x2) || cdb[2] || cdb[3] || cdb[4] || cdb[5] || cdb[9])
1288  {
1289   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1290   return;
1291  }
1292 
1293  if(!StartingTrack)
1294   StartingTrack = 1;
1295  else if(StartingTrack == 0xAA)
1296  {
1297   StartingTrack = LastTrack + 1;
1298  }
1299  else if(StartingTrack > toc.last_track)
1300  {
1301   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1302   return;
1303  }
1304 
1305  data_in[2] = FirstTrack;
1306  data_in[3] = LastTrack;
1307 
1308  RealSize += 4;
1309 
1310  // Read leadout track too LastTrack + 1 ???
1311  for(int track = StartingTrack; track <= (LastTrack + 1); track++)
1312  {
1313   uint8_t *subptr = &data_in[RealSize];
1314   uint32_t lba;
1315   uint8_t m, s, f;
1316   uint32_t eff_track;
1317 
1318   if(track == (LastTrack + 1))
1319    eff_track = 100;
1320   else
1321    eff_track = track;
1322 
1323   lba = toc.tracks[eff_track].lba;
1324   LBA_to_AMSF(lba, &m, &s, &f);
1325 
1326   subptr[0] = 0;
1327   subptr[1] = toc.tracks[eff_track].control | (toc.tracks[eff_track].adr << 4);
1328 
1329   if(eff_track == 100)
1330    subptr[2] = 0xAA;
1331   else
1332    subptr[2] = track;
1333 
1334   subptr[3] = 0;
1335 
1336   if(WantInMSF)
1337   {
1338    subptr[4] = 0;
1339    subptr[5] = m;		// Min
1340    subptr[6] = s;		// Sec
1341    subptr[7] = f;		// Frames
1342   }
1343   else
1344   {
1345    subptr[4] = lba >> 24;
1346    subptr[5] = lba >> 16;
1347    subptr[6] = lba >> 8;
1348    subptr[7] = lba >> 0;
1349   }
1350   RealSize += 8;
1351  }
1352 
1353  // PC-FX: AllocSize too small doesn't reflect in this.
1354  data_in[0] = (RealSize - 2) >> 8;
1355  data_in[1] = (RealSize - 2) >> 0;
1356 
1357  DoSimpleDataIn(data_in, (AllocSize < RealSize) ? AllocSize : RealSize);
1358 }
1359 
1360 
1361 
1362 /********************************************************
1363 *							*
1364 *	SCSI-2 CD Command 0x25 - READ CD-ROM CAPACITY	*
1365 *							*
1366 ********************************************************/
DoREADCDCAP10(const uint8_t * cdb)1367 static void DoREADCDCAP10(const uint8_t *cdb)
1368 {
1369  bool pmi = cdb[8] & 0x1;
1370  uint32_t lba = MDFN_de32msb(cdb + 0x2);
1371  uint32_t ret_lba;
1372  uint32_t ret_bl;
1373  uint8_t data_in[8];
1374 
1375  memset(data_in, 0, sizeof(data_in));
1376 
1377  if(lba > 0x05FF69)
1378  {
1379   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
1380   return;
1381  }
1382 
1383  ret_lba = toc.tracks[100].lba - 1;
1384 
1385  if(pmi)
1386  {
1387   // Look for the track containing the LBA specified, then search for the first track afterwards that has a different track type(audio, data),
1388   // and set the returned LBA to the sector preceding that track.
1389   //
1390   // If the specified LBA is >= leadout track, return the LBA of the sector immediately before the leadout track.
1391   //
1392   // If the specified LBA is < than the LBA of the first track, then return the LBA of sector preceding the first track.  (I don't know if PC-FX can even handle discs like this, though)
1393   if(lba >= toc.tracks[100].lba)
1394    ret_lba = toc.tracks[100].lba - 1;
1395   else if(lba < toc.tracks[toc.first_track].lba)
1396    ret_lba = toc.tracks[toc.first_track].lba - 1;
1397   else
1398   {
1399    const int track = toc.FindTrackByLBA(lba);
1400 
1401    for(int st = track + 1; st <= toc.last_track; st++)
1402    {
1403     if((toc.tracks[st].control ^ toc.tracks[track].control) & 0x4)
1404     {
1405      ret_lba = toc.tracks[st].lba - 1;
1406      break;
1407     }
1408    }
1409   }
1410  }
1411 
1412  ret_bl = 2048;
1413 
1414  MDFN_en32msb(&data_in[0], ret_lba);
1415  MDFN_en32msb(&data_in[4], ret_bl);
1416 
1417  cdda.CDDAStatus = CDDASTATUS_STOPPED;
1418 
1419  DoSimpleDataIn(data_in, 8);
1420 }
1421 
DoREADHEADER10(const uint8_t * cdb)1422 static void DoREADHEADER10(const uint8_t *cdb)
1423 {
1424  uint8_t data_in[8192];
1425  bool WantInMSF = cdb[1] & 0x2;
1426  uint32_t HeaderLBA = MDFN_de32msb(cdb + 0x2);
1427  int AllocSize = MDFN_de16msb(cdb + 0x7);
1428  uint8_t raw_buf[2352 + 96];
1429  uint8_t mode;
1430  int m, s, f;
1431  uint32_t lba;
1432 
1433  // Don't run command at all if AllocSize == 0(FIXME: On a real PC-FX, this command will return success
1434  // if there's no CD when AllocSize == 0, implement this here, might require refactoring).
1435  if(!AllocSize)
1436  {
1437   SendStatusAndMessage(STATUS_GOOD, 0x00);
1438   return;
1439  }
1440 
1441  if(HeaderLBA >= toc.tracks[100].lba)
1442  {
1443   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1444   return;
1445  }
1446 
1447  if(HeaderLBA < toc.tracks[toc.first_track].lba)
1448  {
1449   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1450   return;
1451  }
1452 
1453  Cur_CDIF->ReadRawSector(raw_buf, HeaderLBA);	//, HeaderLBA + 1);
1454  if(!ValidateRawDataSector(raw_buf, HeaderLBA))
1455   return;
1456 
1457  m = BCD_to_U8(raw_buf[12 + 0]);
1458  s = BCD_to_U8(raw_buf[12 + 1]);
1459  f = BCD_to_U8(raw_buf[12 + 2]);
1460  mode = raw_buf[12 + 3];
1461  lba = AMSF_to_LBA(m, s, f);
1462 
1463  //printf("%d:%d:%d(LBA=%08x) %02x\n", m, s, f, lba, mode);
1464 
1465  data_in[0] = mode;
1466  data_in[1] = 0;
1467  data_in[2] = 0;
1468  data_in[3] = 0;
1469 
1470  if(WantInMSF)
1471  {
1472   data_in[4] = 0;
1473   data_in[5] = m;	// Min
1474   data_in[6] = s;	// Sec
1475   data_in[7] = f;	// Frames
1476  }
1477  else
1478  {
1479   data_in[4] = lba >> 24;
1480   data_in[5] = lba >> 16;
1481   data_in[6] = lba >> 8;
1482   data_in[7] = lba >> 0;
1483  }
1484 
1485  cdda.CDDAStatus = CDDASTATUS_STOPPED;
1486 
1487  DoSimpleDataIn(data_in, 8);
1488 }
1489 
DoNEC_SST(const uint8_t * cdb)1490 static void DoNEC_SST(const uint8_t *cdb)		// Command 0xDB, Set Stop Time
1491 {
1492  SendStatusAndMessage(STATUS_GOOD, 0x00);
1493 }
1494 
DoPABase(const uint32_t lba,const uint32_t length,unsigned int status=CDDASTATUS_PLAYING,unsigned int mode=PLAYMODE_NORMAL)1495 static void DoPABase(const uint32_t lba, const uint32_t length, unsigned int status = CDDASTATUS_PLAYING, unsigned int mode = PLAYMODE_NORMAL)
1496 {
1497  if(lba > toc.tracks[100].lba) // > is not a typo, it's a PC-FX bug apparently.
1498  {
1499   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1500   return;
1501  }
1502 
1503  if(lba < toc.tracks[toc.first_track].lba)
1504  {
1505   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1506   return;
1507  }
1508 
1509  if(!length)	// FIXME to return good status in this case even if no CD is present
1510  {
1511   SendStatusAndMessage(STATUS_GOOD, 0x00);
1512   return;
1513  }
1514  else
1515  {
1516   if(toc.tracks[toc.FindTrackByLBA(lba)].control & 0x04)
1517   {
1518    CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_AUDIO_TRACK);
1519    return;
1520   }
1521 
1522   cdda.CDDAReadPos = 588;
1523   read_sec = read_sec_start = lba;
1524   read_sec_end = read_sec_start + length;
1525 
1526   cdda.CDDAStatus = status;
1527   cdda.PlayMode = mode;
1528 
1529   if(read_sec < toc.tracks[100].lba)
1530   {
1531    Cur_CDIF->HintReadSector(read_sec);	//, read_sec_end, read_sec_start);
1532   }
1533  }
1534 
1535  SendStatusAndMessage(STATUS_GOOD, 0x00);
1536 }
1537 
1538 
1539 
1540 /********************************************************
1541 *							*
1542 *	PC-FX CD Command 0xD8 - SAPSP			*
1543 *							*
1544 ********************************************************/
DoNEC_SAPSP(const uint8_t * cdb)1545 static void DoNEC_SAPSP(const uint8_t *cdb)
1546 {
1547  uint32_t lba;
1548 
1549  switch (cdb[9] & 0xc0)
1550  {
1551   default:
1552 	CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1553 	return;
1554 	break;
1555 
1556   case 0x00:
1557 	lba = MDFN_de24msb(&cdb[3]);
1558 	break;
1559 
1560   case 0x40:
1561 	{
1562 	 uint8_t m, s, f;
1563 
1564 	 if(!BCD_to_U8_check(cdb[2], &m) || !BCD_to_U8_check(cdb[3], &s) || !BCD_to_U8_check(cdb[4], &f))
1565 	 {
1566 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1567 	  return;
1568 	 }
1569 
1570 	 lba = AMSF_to_LBA(m, s, f);
1571 	}
1572 	break;
1573 
1574   case 0x80:
1575 	{
1576 	 uint8_t track;
1577 
1578 	 if(!cdb[2] || !BCD_to_U8_check(cdb[2], &track))
1579 	 {
1580 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1581 	  return;
1582 	 }
1583 
1584 	 if(track == toc.last_track + 1)
1585 	  track = 100;
1586 	 else if(track > toc.last_track)
1587 	 {
1588 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
1589 	  return;
1590 	 }
1591 	 lba = toc.tracks[track].lba;
1592 	}
1593 	break;
1594  }
1595 
1596  if(cdb[1] & 0x01)
1597   DoPABase(lba, toc.tracks[100].lba - lba, CDDASTATUS_PLAYING, PLAYMODE_NORMAL);
1598  else
1599   DoPABase(lba, toc.tracks[100].lba - lba, CDDASTATUS_PAUSED, PLAYMODE_SILENT);
1600 }
1601 
1602 
1603 
1604 /********************************************************
1605 *							*
1606 *	PC-FX CD Command 0xD9 - SAPEP			*
1607 *							*
1608 ********************************************************/
DoNEC_SAPEP(const uint8_t * cdb)1609 static void DoNEC_SAPEP(const uint8_t *cdb)
1610 {
1611  uint32_t lba;
1612 
1613  if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
1614  {
1615   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
1616   return;
1617  }
1618 
1619  switch (cdb[9] & 0xc0)
1620  {
1621   default:
1622 	CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1623 	return;
1624 	break;
1625 
1626   case 0x00:
1627 	lba = MDFN_de24msb(&cdb[3]);
1628 	break;
1629 
1630   case 0x40:
1631 	{
1632 	 uint8_t m, s, f;
1633 
1634 	 if(!BCD_to_U8_check(cdb[2], &m) || !BCD_to_U8_check(cdb[3], &s) || !BCD_to_U8_check(cdb[4], &f))
1635 	 {
1636 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1637 	  return;
1638 	 }
1639 
1640 	 lba = AMSF_to_LBA(m, s, f);
1641 	}
1642 	break;
1643 
1644   case 0x80:
1645 	{
1646 	 uint8_t track;
1647 
1648 	 if(!cdb[2] || !BCD_to_U8_check(cdb[2], &track))
1649 	 {
1650 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1651 	  return;
1652 	 }
1653 
1654 	 if(track == toc.last_track + 1)
1655 	  track = 100;
1656 	 else if(track > toc.last_track)
1657 	 {
1658 	  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
1659 	  return;
1660 	 }
1661 	 lba = toc.tracks[track].lba;
1662 	}
1663 	break;
1664  }
1665 
1666  switch(cdb[1] & 0x7)
1667  {
1668    case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
1669 	      break;
1670 
1671    case 0x04: cdda.PlayMode = PLAYMODE_LOOP;
1672 	      break;
1673 
1674    default:   cdda.PlayMode = PLAYMODE_NORMAL;
1675 	      break;
1676  }
1677  cdda.CDDAStatus = CDDASTATUS_PLAYING;
1678 
1679  read_sec_end = lba;
1680 
1681  SendStatusAndMessage(STATUS_GOOD, 0x00);
1682 }
1683 
1684 
1685 
1686 /********************************************************
1687 *							*
1688 *	SCSI-2 CD Command 0x45 - PLAY AUDIO(10) 	*
1689 *				  			*
1690 ********************************************************/
DoPA10(const uint8_t * cdb)1691 static void DoPA10(const uint8_t *cdb)
1692 {
1693  // Real PC-FX Bug: Error out on LBA >(not >=) leadout sector number
1694  const uint32_t lba = MDFN_de32msb(cdb + 0x2);
1695  const uint16 length = MDFN_de16msb(cdb + 0x7);
1696 
1697  DoPABase(lba, length);
1698 }
1699 
1700 
1701 
1702 /********************************************************
1703 *							*
1704 *	SCSI-2 CD Command 0xA5 - PLAY AUDIO(12) 	*
1705 *				  			*
1706 ********************************************************/
DoPA12(const uint8_t * cdb)1707 static void DoPA12(const uint8_t *cdb)
1708 {
1709  // Real PC-FX Bug: Error out on LBA >(not >=) leadout sector number
1710  const uint32_t lba = MDFN_de32msb(cdb + 0x2);
1711  const uint32_t length = MDFN_de32msb(cdb + 0x6);
1712 
1713  DoPABase(lba, length);
1714 }
1715 
1716 
1717 
1718 /********************************************************
1719 *							*
1720 *	SCSI-2 CD Command 0x47 - PLAY AUDIO MSF 	*
1721 *				  			*
1722 ********************************************************/
DoPAMSF(const uint8_t * cdb)1723 static void DoPAMSF(const uint8_t *cdb)
1724 {
1725  int32_t lba_start, lba_end;
1726 
1727  lba_start = AMSF_to_LBA(cdb[3], cdb[4], cdb[5]);
1728  lba_end = AMSF_to_LBA(cdb[6], cdb[7], cdb[8]);
1729 
1730  if(lba_start < 0 || lba_end < 0 || lba_start >= (int32_t)toc.tracks[100].lba)
1731  {
1732   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
1733   return;
1734  }
1735 
1736  if(lba_start == lba_end)
1737  {
1738   SendStatusAndMessage(STATUS_GOOD, 0x00);
1739   return;
1740  }
1741  else if(lba_start > lba_end)
1742  {
1743   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_ADDRESS);
1744   return;
1745  }
1746 
1747  cdda.CDDAReadPos = 588;
1748  read_sec = read_sec_start = lba_start;
1749  read_sec_end = lba_end;
1750 
1751  cdda.CDDAStatus = CDDASTATUS_PLAYING;
1752  cdda.PlayMode = PLAYMODE_NORMAL;
1753 
1754  SendStatusAndMessage(STATUS_GOOD, 0x00);
1755 }
1756 
1757 
1758 
DoPATI(const uint8_t * cdb)1759 static void DoPATI(const uint8_t *cdb)
1760 {
1761  // "Boundary Gate" uses this command.
1762  // Problems:
1763  //  The index fields aren't handled.  The ending index wouldn't be too bad, but the starting index would require a bit of work and code uglyfying(to scan for the index), and may be highly
1764  //  problematic when Mednafen is used with a physical CD.
1765  int StartTrack = cdb[4];
1766  int EndTrack = cdb[7];
1767  //int StartIndex = cdb[5];
1768  //int EndIndex = cdb[8];
1769 
1770  if(!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track)
1771  {
1772   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1773   return;
1774  }
1775 
1776  //printf("PATI: %d %d %d  SI: %d, EI: %d\n", StartTrack, EndTrack, Cur_CDIF->GetTrackStartPositionLBA(StartTrack), StartIndex, EndIndex);
1777 
1778  DoPABase(toc.tracks[StartTrack].lba, toc.tracks[EndTrack].lba - toc.tracks[StartTrack].lba);
1779 }
1780 
1781 
DoPATRBase(const uint32_t lba,const uint32_t length)1782 static void DoPATRBase(const uint32_t lba, const uint32_t length)
1783 {
1784  if(lba >= toc.tracks[100].lba)
1785  {
1786   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1787   return;
1788  }
1789 
1790  if(lba < toc.tracks[toc.first_track].lba)
1791  {
1792   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1793   return;
1794  }
1795 
1796  if(!length)	// FIXME to return good status in this case even if no CD is present
1797  {
1798   SendStatusAndMessage(STATUS_GOOD, 0x00);
1799   return;
1800  }
1801  else
1802  {
1803   if(toc.tracks[toc.FindTrackByLBA(lba)].control & 0x04)
1804   {
1805    CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_AUDIO_TRACK);
1806    return;
1807   }
1808 
1809   cdda.CDDAReadPos = 588;
1810   read_sec = read_sec_start = lba;
1811   read_sec_end = read_sec_start + length;
1812 
1813   cdda.CDDAStatus = CDDASTATUS_PLAYING;
1814   cdda.PlayMode = PLAYMODE_NORMAL;
1815  }
1816 
1817  SendStatusAndMessage(STATUS_GOOD, 0x00);
1818 }
1819 
1820 
1821 /********************************************************
1822 *							*
1823 *	SCSI-2 CD Command 0x49 - PLAY AUDIO TRACK 	*
1824 *				  RELATIVE(10)		*
1825 ********************************************************/
DoPATR10(const uint8_t * cdb)1826 static void DoPATR10(const uint8_t *cdb)
1827 {
1828  const int32_t rel_lba = MDFN_de32msb(cdb + 0x2);
1829  const int StartTrack = cdb[6];
1830  const uint16 length = MDFN_de16msb(cdb + 0x7);
1831 
1832  if(!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track)
1833  {
1834   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1835   return;
1836  }
1837 
1838  DoPATRBase(toc.tracks[StartTrack].lba + rel_lba, length);
1839 }
1840 
1841 
1842 
1843 /********************************************************
1844 *							*
1845 *	SCSI-2 CD Command 0xA9 - PLAY AUDIO TRACK 	*
1846 *				  RELATIVE(12)		*
1847 ********************************************************/
DoPATR12(const uint8_t * cdb)1848 static void DoPATR12(const uint8_t *cdb)
1849 {
1850  const int32_t rel_lba = MDFN_de32msb(cdb + 0x2);
1851  const int StartTrack = cdb[10];
1852  const uint32_t length = MDFN_de32msb(cdb + 0x6);
1853 
1854  if(!StartTrack || StartTrack < toc.first_track || StartTrack > toc.last_track)
1855  {
1856   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
1857   return;
1858  }
1859 
1860  DoPATRBase(toc.tracks[StartTrack].lba + rel_lba, length);
1861 }
1862 
DoPAUSERESUME(const uint8_t * cdb)1863 static void DoPAUSERESUME(const uint8_t *cdb)
1864 {
1865  // Pause/resume
1866  // "It shall not be considered an error to request a pause when a pause is already in effect,
1867  // or to request a resume when a play operation is in progress."
1868 
1869  if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
1870  {
1871   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
1872   return;
1873  }
1874 
1875  if(cdb[8] & 1)	// Resume
1876   cdda.CDDAStatus = CDDASTATUS_PLAYING;
1877  else
1878   cdda.CDDAStatus = CDDASTATUS_PAUSED;
1879 
1880  SendStatusAndMessage(STATUS_GOOD, 0x00);
1881 }
1882 
1883 
1884 
1885 
1886 
DoREADBase(uint32_t sa,uint32_t sc)1887 static void DoREADBase(uint32_t sa, uint32_t sc)
1888 {
1889  int track;
1890 
1891  if(sa > toc.tracks[100].lba) // Another one of those off-by-one PC-FX CD bugs.
1892  {
1893   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
1894   return;
1895  }
1896 
1897  if((track = toc.FindTrackByLBA(sa)) == 0)
1898  {
1899   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
1900   return;
1901  }
1902 
1903  if(!(toc.tracks[track].control) & 0x4)
1904  {
1905   CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_NOT_DATA_TRACK);
1906   return;
1907  }
1908 
1909  // Case for READ(10) and READ(12) where sc == 0, and sa == toc.tracks[100].lba
1910  if(!sc && sa == toc.tracks[100].lba)
1911  {
1912   CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_HEADER_READ_ERROR);
1913   return;
1914  }
1915 
1916  if(SCSILog)
1917  {
1918   int Track = toc.FindTrackByLBA(sa);
1919   uint32_t Offset = sa - toc.tracks[Track].lba; //Cur_CDIF->GetTrackStartPositionLBA(Track);
1920   SCSILog("SCSI", "Read: start=0x%08x(track=%d, offs=0x%08x), cnt=0x%08x", sa, Track, Offset, sc);
1921  }
1922 
1923  SectorAddr = sa;
1924  SectorCount = sc;
1925  if(SectorCount)
1926  {
1927   Cur_CDIF->HintReadSector(sa);	//, sa + sc);
1928 
1929   CDReadTimer = (uint64_t)((WhichSystem == SCSICD_PCE) ? 3 : 1) * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
1930  }
1931  else
1932  {
1933   CDReadTimer = 0;
1934   SendStatusAndMessage(STATUS_GOOD, 0x00);
1935  }
1936  cdda.CDDAStatus = CDDASTATUS_STOPPED;
1937 }
1938 
1939 
1940 
1941 /********************************************************
1942 *							*
1943 *	SCSI-2 CD Command 0x08 - READ(6)		*
1944 *							*
1945 ********************************************************/
DoREAD6(const uint8_t * cdb)1946 static void DoREAD6(const uint8_t *cdb)
1947 {
1948  uint32_t sa = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | (cdb[3] << 0);
1949  uint32_t sc = cdb[4];
1950 
1951  // TODO: confirm real PCE does this(PC-FX does at least).
1952  if(!sc)
1953  {
1954   //SCSIDBG("READ(6) with count == 0.\n");
1955   sc = 256;
1956  }
1957 
1958  DoREADBase(sa, sc);
1959 }
1960 
1961 
1962 
1963 /********************************************************
1964 *							*
1965 *	SCSI-2 CD Command 0x28 - READ(10)		*
1966 *							*
1967 ********************************************************/
DoREAD10(const uint8_t * cdb)1968 static void DoREAD10(const uint8_t *cdb)
1969 {
1970  uint32_t sa = MDFN_de32msb(cdb + 0x2);
1971  uint32_t sc = MDFN_de16msb(cdb + 0x7);
1972 
1973  DoREADBase(sa, sc);
1974 }
1975 
1976 
1977 
1978 /********************************************************
1979 *							*
1980 *	SCSI-2 CD Command 0xA8 - READ(12)		*
1981 *							*
1982 ********************************************************/
DoREAD12(const uint8_t * cdb)1983 static void DoREAD12(const uint8_t *cdb)
1984 {
1985  uint32_t sa = MDFN_de32msb(cdb + 0x2);
1986  uint32_t sc = MDFN_de32msb(cdb + 0x6);
1987 
1988  DoREADBase(sa, sc);
1989 }
1990 
1991 
1992 
1993 /********************************************************
1994 *							*
1995 *	SCSI-2 CD Command 0x34 - PREFETCH(10)		*
1996 *							*
1997 ********************************************************/
DoPREFETCH(const uint8_t * cdb)1998 static void DoPREFETCH(const uint8_t *cdb)
1999 {
2000  uint32_t lba = MDFN_de32msb(cdb + 0x2);
2001  //uint32_t len = MDFN_de16msb(cdb + 0x7);
2002  //bool reladdr = cdb[1] & 0x1;
2003  //bool immed = cdb[1] & 0x2;
2004 
2005  // Note: This command appears to lock up the CD unit to some degree on a real PC-FX if the (lba + len) >= leadout_track_lba,
2006  // more testing is needed if we ever try to fully emulate this command.
2007  if(lba >= toc.tracks[100].lba)
2008  {
2009   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
2010   return;
2011  }
2012 
2013  //printf("Prefetch: %08x %08x %d %d %d %d\n", lba, len, link, flag, reladdr, immed);
2014  //SendStatusAndMessage(STATUS_GOOD, 0x00);
2015  SendStatusAndMessage(STATUS_CONDITION_MET, 0x00);
2016 }
2017 
2018 
2019 
2020 
2021 // SEEK functions are mostly just stubs for now, until(if) we emulate seek delays.
DoSEEKBase(uint32_t lba)2022 static void DoSEEKBase(uint32_t lba)
2023 {
2024  if(lba >= toc.tracks[100].lba)
2025  {
2026   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
2027   return;
2028  }
2029 
2030  cdda.CDDAStatus = CDDASTATUS_STOPPED;
2031  SendStatusAndMessage(STATUS_GOOD, 0x00);
2032 }
2033 
2034 
2035 
2036 /********************************************************
2037 *							*
2038 *	SCSI-2 CD Command 0x0B - SEEK(6)		*
2039 *							*
2040 ********************************************************/
DoSEEK6(const uint8_t * cdb)2041 static void DoSEEK6(const uint8_t *cdb)
2042 {
2043  uint32_t lba = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | cdb[3];
2044 
2045  DoSEEKBase(lba);
2046 }
2047 
2048 
2049 
2050 /********************************************************
2051 *							*
2052 *	SCSI-2 CD Command 0x2B - SEEK(10)		*
2053 *							*
2054 ********************************************************/
DoSEEK10(const uint8_t * cdb)2055 static void DoSEEK10(const uint8_t *cdb)
2056 {
2057  uint32_t lba = MDFN_de32msb(cdb + 0x2);
2058 
2059  DoSEEKBase(lba);
2060 }
2061 
2062 // 353
2063 /********************************************************
2064 *							*
2065 *	SCSI-2 CD Command 0x42 - READ SUB-CHANNEL(10)	*
2066 *							*
2067 ********************************************************/
DoREADSUBCHANNEL(const uint8_t * cdb)2068 static void DoREADSUBCHANNEL(const uint8_t *cdb)
2069 {
2070  uint8_t data_in[8192];
2071  int DataFormat = cdb[3];
2072  int TrackNum = cdb[6];
2073  unsigned AllocSize = (cdb[7] << 8) | cdb[8];
2074  bool WantQ = cdb[2] & 0x40;
2075  bool WantMSF = cdb[1] & 0x02;
2076  uint32_t offset = 0;
2077 
2078  if(!AllocSize)
2079  {
2080   SendStatusAndMessage(STATUS_GOOD, 0x00);
2081   return;
2082  }
2083 
2084  if(DataFormat > 0x3)
2085  {
2086   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
2087   return;
2088  }
2089 
2090  if(DataFormat == 0x3 && (TrackNum < toc.first_track || TrackNum > toc.last_track))
2091  {
2092   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
2093   return;
2094  }
2095 
2096  data_in[offset++] = 0;
2097 
2098  // FIXME:  Is this audio status code correct for scanning playback??
2099  if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
2100   data_in[offset++] = 0x11;	// Audio play operation in progress
2101  else if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
2102   data_in[offset++] = 0x12;	// Audio play operation paused
2103  else
2104   data_in[offset++] = 0x13;	// 0x13(audio play operation completed successfully) or 0x15(no current audio status to return)? :(
2105 
2106 
2107  // Subchannel data length(at data_in[0x2], filled out at the end of the function)
2108  data_in[offset++] = 0x00;
2109  data_in[offset++] = 0x00;
2110 
2111  //printf("42Read SubChannel: %02x %02x %d %d %d\n", DataFormat, TrackNum, AllocSize, WantQ, WantMSF);
2112  if(WantQ)
2113  {
2114   // Sub-channel format code
2115   data_in[offset++] = DataFormat;
2116   if(!DataFormat || DataFormat == 0x01)
2117   {
2118    uint8_t *SubQBuf = cd.SubQBuf[QMode_Time];
2119 
2120    data_in[offset++] = ((SubQBuf[0] & 0x0F) << 4) | ((SubQBuf[0] & 0xF0) >> 4); // Control/adr
2121    data_in[offset++] = SubQBuf[1]; // Track
2122    data_in[offset++] = SubQBuf[2]; // Index
2123 
2124    // Absolute CD-ROM address
2125    if(WantMSF)
2126    {
2127     data_in[offset++] = 0;
2128     data_in[offset++] = BCD_to_U8(SubQBuf[7]); // M
2129     data_in[offset++] = BCD_to_U8(SubQBuf[8]); // S
2130     data_in[offset++] = BCD_to_U8(SubQBuf[9]); // F
2131    }
2132    else
2133    {
2134     uint32_t tmp_lba = BCD_to_U8(SubQBuf[7]) * 60 * 75 + BCD_to_U8(SubQBuf[8]) * 75 + BCD_to_U8(SubQBuf[9]) - 150;
2135 
2136     data_in[offset++] = tmp_lba >> 24;
2137     data_in[offset++] = tmp_lba >> 16;
2138     data_in[offset++] = tmp_lba >> 8;
2139     data_in[offset++] = tmp_lba >> 0;
2140    }
2141 
2142    // Relative CD-ROM address
2143    if(WantMSF)
2144    {
2145     data_in[offset++] = 0;
2146     data_in[offset++] = BCD_to_U8(SubQBuf[3]); // M
2147     data_in[offset++] = BCD_to_U8(SubQBuf[4]); // S
2148     data_in[offset++] = BCD_to_U8(SubQBuf[5]); // F
2149    }
2150    else
2151    {
2152     uint32_t tmp_lba = BCD_to_U8(SubQBuf[3]) * 60 * 75 + BCD_to_U8(SubQBuf[4]) * 75 + BCD_to_U8(SubQBuf[5]);	// Don't subtract 150 in the conversion!
2153 
2154     data_in[offset++] = tmp_lba >> 24;
2155     data_in[offset++] = tmp_lba >> 16;
2156     data_in[offset++] = tmp_lba >> 8;
2157     data_in[offset++] = tmp_lba >> 0;
2158    }
2159   }
2160 
2161   if(!DataFormat || DataFormat == 0x02)
2162   {
2163    if(DataFormat == 0x02)
2164    {
2165     data_in[offset++] = 0x00;
2166     data_in[offset++] = 0x00;
2167     data_in[offset++] = 0x00;
2168    }
2169    data_in[offset++] = 0x00;	// MCVal and reserved.
2170    for(int i = 0; i < 15; i++)
2171     data_in[offset++] = 0x00;
2172   }
2173 
2174   // Track ISRC
2175   if(!DataFormat || DataFormat == 0x03)
2176   {
2177    if(DataFormat == 0x03)
2178    {
2179     uint8_t *SubQBuf = cd.SubQBuf[QMode_Time];	// FIXME
2180     data_in[offset++] = ((SubQBuf[0] & 0x0F) << 4) | ((SubQBuf[0] & 0xF0) >> 4); // Control/adr
2181     data_in[offset++] = TrackNum;	// From sub Q or from parameter?
2182     data_in[offset++] = 0x00;		// Reserved.
2183    }
2184    data_in[offset++] = 0x00; // TCVal and reserved
2185    for(int i = 0; i < 15; i++)
2186     data_in[offset++] = 0x00;
2187   }
2188  }
2189 
2190  MDFN_en16msb(&data_in[0x2], offset - 0x4);
2191 
2192  DoSimpleDataIn(data_in, (AllocSize > offset) ? offset : AllocSize);
2193 }
2194 
2195 
2196 
2197 /********************************************************
2198 *							*
2199 *	PC-FX CD Command 0xDD - READ SUB Q		*
2200 *				  			*
2201 ********************************************************/
DoNEC_READSUBQ(const uint8_t * cdb)2202 static void DoNEC_READSUBQ(const uint8_t *cdb)
2203 {
2204  uint8_t *SubQBuf = cd.SubQBuf[QMode_Time];
2205  uint8_t data_in[10];
2206  const uint8_t alloc_size = (cdb[1] < 10) ? cdb[1] : 10;
2207 
2208  memset(data_in, 0x00, 10);
2209 
2210  if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
2211   data_in[0] = 2;		// Pause
2212  else if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING) // FIXME:  Is this the correct status code for scanning playback?
2213   data_in[0] = 0;		// Playing
2214  else
2215   data_in[0] = 3;		// Stopped
2216 
2217  data_in[1] = SubQBuf[0];	// Control/adr
2218  data_in[2] = SubQBuf[1];	// Track
2219  data_in[3] = SubQBuf[2];	// Index
2220  data_in[4] = SubQBuf[3];	// M(rel)
2221  data_in[5] = SubQBuf[4];	// S(rel)
2222  data_in[6] = SubQBuf[5];	// F(rel)
2223  data_in[7] = SubQBuf[7];	// M(abs)
2224  data_in[8] = SubQBuf[8];	// S(abs)
2225  data_in[9] = SubQBuf[9];	// F(abs)
2226 
2227  DoSimpleDataIn(data_in, alloc_size);
2228 }
2229 
DoTESTUNITREADY(const uint8_t * cdb)2230 static void DoTESTUNITREADY(const uint8_t *cdb)
2231 {
2232  SendStatusAndMessage(STATUS_GOOD, 0x00);
2233 }
2234 
DoNEC_PAUSE(const uint8_t * cdb)2235 static void DoNEC_PAUSE(const uint8_t *cdb)
2236 {
2237  if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
2238  {
2239   cdda.CDDAStatus = CDDASTATUS_PAUSED;
2240   SendStatusAndMessage(STATUS_GOOD, 0x00);
2241  }
2242  else // Definitely give an error if it tries to pause when no track is playing!
2243  {
2244   CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
2245  }
2246 }
2247 
DoNEC_SCAN(const uint8_t * cdb)2248 static void DoNEC_SCAN(const uint8_t *cdb)
2249 {
2250  uint32_t sector_tmp = 0;
2251 
2252  // 0: 0xD2
2253  // 1: 0x03 = reverse scan, 0x02 = forward scan
2254  // 2: End M
2255  // 3: End S
2256  // 4: End F
2257 
2258  switch (cdb[9] & 0xc0)
2259  {
2260   default:
2261    //SCSIDBG("Unknown NECSCAN format");
2262    break;
2263 
2264   case 0x00:
2265    sector_tmp = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
2266    break;
2267 
2268   case 0x40:
2269    sector_tmp = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
2270    break;
2271 
2272   case 0x80:	// FIXME: error on invalid track number???
2273    sector_tmp = toc.tracks[BCD_to_U8(cdb[2])].lba;
2274    break;
2275  }
2276 
2277  cdda.ScanMode = cdb[1] & 0x3;
2278  cdda.scan_sec_end = sector_tmp;
2279 
2280  if(cdda.CDDAStatus != CDDASTATUS_STOPPED)
2281  {
2282   if(cdda.ScanMode)
2283   {
2284    cdda.CDDAStatus = CDDASTATUS_SCANNING;
2285   }
2286  }
2287  SendStatusAndMessage(STATUS_GOOD, 0x00);
2288 }
2289 
2290 
2291 
2292 /********************************************************
2293 *							*
2294 *	SCSI-2 CD Command 0x1E - PREVENT/ALLOW MEDIUM 	*
2295 *				  REMOVAL		*
2296 ********************************************************/
DoPREVENTALLOWREMOVAL(const uint8_t * cdb)2297 static void DoPREVENTALLOWREMOVAL(const uint8_t *cdb)
2298 {
2299  //bool prevent = cdb[4] & 0x01;
2300  //const int logical_unit = cdb[1] >> 5;
2301  //SCSIDBG("PREVENT ALLOW MEDIUM REMOVAL: %d for %d\n", cdb[4] & 0x1, logical_unit);
2302  //SendStatusAndMessage(STATUS_GOOD, 0x00);
2303 
2304  CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_REQUEST_IN_CDB);
2305 }
2306 
2307 //
2308 //
2309 //
2310 #include "scsicd-pce-commands.inc"
2311 
2312 
2313 #define SCF_REQUIRES_MEDIUM	0x0001
2314 #define SCF_INCOMPLETE		0x4000
2315 #define SCF_UNTESTED		0x8000
2316 
2317 typedef struct
2318 {
2319  uint8_t cmd;
2320  uint32_t flags;
2321  void (*func)(const uint8_t *cdb);
2322  const char *pretty_name;
2323  const char *format_string;
2324 } SCSICH;
2325 
2326 static const int32_t RequiredCDBLen[16] =
2327 {
2328  6, // 0x0n
2329  6, // 0x1n
2330  10, // 0x2n
2331  10, // 0x3n
2332  10, // 0x4n
2333  10, // 0x5n
2334  10, // 0x6n
2335  10, // 0x7n
2336  10, // 0x8n
2337  10, // 0x9n
2338  12, // 0xAn
2339  12, // 0xBn
2340  10, // 0xCn
2341  10, // 0xDn
2342  10, // 0xEn
2343  10, // 0xFn
2344 };
2345 
2346 static SCSICH PCFXCommandDefs[] =
2347 {
2348  { 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" },
2349  { 0x01, 0/* ? */, DoREZEROUNIT, "Rezero Unit" },
2350  { 0x03, 0, DoREQUESTSENSE, "Request Sense" },
2351  { 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" },
2352  { 0x0B, SCF_REQUIRES_MEDIUM, DoSEEK6, "Seek(6)" },
2353  { 0x0D, 0, DoNEC_NOP, "No Operation" },
2354  { 0x12, 0, DoINQUIRY, "Inquiry" },
2355  { 0x15, 0, DoMODESELECT6, "Mode Select(6)" },
2356  // TODO: { 0x16, 0 /* ? */, DoRESERVE, "Reserve" },			// 9.2.12
2357  // TODO: { 0x17, 0 /* ? */, DoRELEASE, "Release" },			// 9.2.11
2358  { 0x1A, 0, DoMODESENSE6, "Mode Sense(6)" },
2359  { 0x1B, SCF_REQUIRES_MEDIUM, DoSTARTSTOPUNIT6, "Start/Stop Unit" },		// 9.2.17
2360  // TODO: { 0x1D, , DoSENDDIAG, "Send Diagnostic" },		// 8.2.15
2361  { 0x1E, 0, DoPREVENTALLOWREMOVAL, "Prevent/Allow Media Removal" },
2362 
2363  { 0x25, SCF_REQUIRES_MEDIUM, DoREADCDCAP10, "Read CD-ROM Capacity" },	// 14.2.8
2364  { 0x28, SCF_REQUIRES_MEDIUM, DoREAD10, "Read(10)" },
2365  { 0x2B, SCF_REQUIRES_MEDIUM, DoSEEK10, "Seek(10)" },
2366 
2367  // TODO: { 0x2F, SCF_REQUIRES_MEDIUM, DoVERIFY10, "Verify(10)" },		// 16.2.11
2368 
2369  { 0x34, SCF_REQUIRES_MEDIUM, DoPREFETCH, "Prefetch" },
2370  // TODO: { 0x3B, 0, 10, DoWRITEBUFFER, "Write Buffer" },		// 8.2.17
2371  // TODO: { 0x3C, 0, 10, DoREADBUFFER, "Read Buffer" },		// 8.2.12
2372 
2373  { 0x42, SCF_REQUIRES_MEDIUM, DoREADSUBCHANNEL, "Read Subchannel" },
2374  { 0x43, SCF_REQUIRES_MEDIUM, DoREADTOC, "Read TOC" },
2375  { 0x44, SCF_REQUIRES_MEDIUM, DoREADHEADER10, "Read Header" },
2376 
2377  { 0x45, SCF_REQUIRES_MEDIUM, DoPA10, "Play Audio(10)" },
2378  { 0x47, SCF_REQUIRES_MEDIUM, DoPAMSF, "Play Audio MSF" },
2379  { 0x48, SCF_REQUIRES_MEDIUM, DoPATI, "Play Audio Track Index" },
2380  { 0x49, SCF_REQUIRES_MEDIUM, DoPATR10, "Play Audio Track Relative(10)" },
2381  { 0x4B, SCF_REQUIRES_MEDIUM, DoPAUSERESUME, "Pause/Resume" },
2382 
2383  { 0xA5, SCF_REQUIRES_MEDIUM, DoPA12, "Play Audio(12)" },
2384  { 0xA8, SCF_REQUIRES_MEDIUM, DoREAD12, "Read(12)" },
2385  { 0xA9, SCF_REQUIRES_MEDIUM, DoPATR12, "Play Audio Track Relative(12)" },
2386 
2387  // TODO: { 0xAF, SCF_REQUIRES_MEDIUM, DoVERIFY12, "Verify(12)" },		// 16.2.12
2388 
2389  { 0xD2, SCF_REQUIRES_MEDIUM, DoNEC_SCAN, "Scan" },
2390  { 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_SAPSP, "Set Audio Playback Start Position" }, // "Audio track search"
2391  { 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_SAPEP, "Set Audio Playback End Position" },   // "Play"
2392  { 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PAUSE, "Pause" },			     // "Still"
2393  { 0xDB, SCF_REQUIRES_MEDIUM | SCF_UNTESTED, DoNEC_SST, "Set Stop Time" },
2394  { 0xDC, SCF_REQUIRES_MEDIUM, DoNEC_EJECT, "Eject" },
2395  { 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_READSUBQ, "Read Subchannel Q" },
2396  { 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_GETDIRINFO, "Get Dir Info" },
2397 
2398  { 0xFF, 0, 0, NULL, NULL },
2399 };
2400 
2401 static SCSICH PCECommandDefs[] =
2402 {
2403  { 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" },
2404  { 0x03, 0, DoREQUESTSENSE, "Request Sense" },
2405  { 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" },
2406  //{ 0x15, DoMODESELECT6, "Mode Select(6)" },
2407  { 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPSP, "Set Audio Playback Start Position" },
2408  { 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPEP, "Set Audio Playback End Position" },
2409  { 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PCE_PAUSE, "Pause" },
2410  { 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_PCE_READSUBQ, "Read Subchannel Q" },
2411  { 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_PCE_GETDIRINFO, "Get Dir Info" },
2412 
2413  { 0xFF, 0, 0, NULL, NULL },
2414 };
2415 
SCSICD_ResetTS(uint32_t ts_base)2416 void SCSICD_ResetTS(uint32_t ts_base)
2417 {
2418  lastts = ts_base;
2419 }
2420 
SCSICD_GetCDDAValues(int16 & left,int16 & right)2421 void SCSICD_GetCDDAValues(int16 &left, int16 &right)
2422 {
2423  if(cdda.CDDAStatus)
2424  {
2425   left = cdda.sr[0];
2426   right = cdda.sr[1];
2427  }
2428  else
2429   left = right = 0;
2430 }
2431 
2432 #define CDDA_FILTER_NUMCONVOLUTIONS		7
2433 #define CDDA_FILTER_NUMCONVOLUTIONS_PADDED	8
2434 
2435 #define CDDA_FILTER_NUMPHASES_SHIFT		6
2436 #define CDDA_FILTER_NUMPHASES	       		(1 << CDDA_FILTER_NUMPHASES_SHIFT)
2437 
2438 static const int16 CDDA_Filter[1 + CDDA_FILTER_NUMPHASES + 1][CDDA_FILTER_NUMCONVOLUTIONS_PADDED] =
2439 {
2440  #include "scsicd_cdda_filter.inc"
2441 };
2442 
2443 static const int16 OversampleFilter[2][0x10] =
2444 {
2445  {    -82,    217,   -463,    877,  -1562,   2783,  -5661,  29464,   9724,  -3844,   2074,  -1176,    645,   -323,    138,    -43,  }, /* sum=32768, sum_abs=59076 */
2446  {    -43,    138,   -323,    645,  -1176,   2074,  -3844,   9724,  29464,  -5661,   2783,  -1562,    877,   -463,    217,    -82,  }, /* sum=32768, sum_abs=59076 */
2447 };
2448 
RunCDDA(uint32_t system_timestamp,int32_t run_time)2449 static INLINE void RunCDDA(uint32_t system_timestamp, int32_t run_time)
2450 {
2451  if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
2452  {
2453   cdda.CDDADiv -= (int64_t)run_time << 20;
2454 
2455   while(cdda.CDDADiv <= 0)
2456   {
2457    const uint32_t synthtime_ex = (((uint64_t)system_timestamp << 20) + (int64_t)cdda.CDDADiv) / cdda.CDDATimeDiv;
2458    const int synthtime = (synthtime_ex >> 16) & 0xFFFF;	// & 0xFFFF(or equivalent) to prevent overflowing HRBufs[]
2459    const int synthtime_phase = (int)(synthtime_ex & 0xFFFF) - 0x80;
2460    const int synthtime_phase_int = synthtime_phase >> (16 - CDDA_FILTER_NUMPHASES_SHIFT);
2461    const int synthtime_phase_fract = synthtime_phase & ((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - 1);
2462    int32_t sample_va[2];
2463 
2464    cdda.CDDADiv += cdda.CDDADivAcc;
2465 
2466    if(!(cdda.OversamplePos & 1))
2467    {
2468     if(cdda.CDDAReadPos == 588)
2469     {
2470      if(read_sec >= read_sec_end || (cdda.CDDAStatus == CDDASTATUS_SCANNING && read_sec == cdda.scan_sec_end))
2471      {
2472       switch(cdda.PlayMode)
2473       {
2474        case PLAYMODE_SILENT:
2475        case PLAYMODE_NORMAL:
2476         cdda.CDDAStatus = CDDASTATUS_STOPPED;
2477         break;
2478 
2479        case PLAYMODE_INTERRUPT:
2480         cdda.CDDAStatus = CDDASTATUS_STOPPED;
2481         CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
2482         break;
2483 
2484        case PLAYMODE_LOOP:
2485         read_sec = read_sec_start;
2486         break;
2487       }
2488 
2489       // If CDDA playback is stopped, break out of our while(CDDADiv ...) loop and don't play any more sound!
2490       if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
2491        break;
2492      }
2493 
2494      // Don't play past the user area of the disc.
2495      if(read_sec >= toc.tracks[100].lba)
2496      {
2497       cdda.CDDAStatus = CDDASTATUS_STOPPED;
2498       break;
2499      }
2500 
2501      if(TrayOpen || !Cur_CDIF)
2502      {
2503       cdda.CDDAStatus = CDDASTATUS_STOPPED;
2504 
2505       #if 0
2506       cd.data_transfer_done = FALSE;
2507       cd.key_pending = SENSEKEY_NOT_READY;
2508       cd.asc_pending = ASC_MEDIUM_NOT_PRESENT;
2509       cd.ascq_pending = 0x00;
2510       cd.fru_pending = 0x00;
2511       SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
2512       #endif
2513 
2514       break;
2515      }
2516 
2517 
2518      cdda.CDDAReadPos = 0;
2519 
2520      {
2521       uint8_t tmpbuf[2352 + 96];
2522 
2523       Cur_CDIF->ReadRawSector(tmpbuf, read_sec);	//, read_sec_end, read_sec_start);
2524 
2525       for(int i = 0; i < 588 * 2; i++)
2526        cdda.CDDASectorBuffer[i] = MDFN_de16lsb(&tmpbuf[i * 2]);
2527 
2528       memcpy(cd.SubPWBuf, tmpbuf + 2352, 96);
2529      }
2530      GenSubQFromSubPW();
2531 
2532      if(!(cd.SubQBuf_Last[0] & 0x10))
2533      {
2534       // Not using de-emphasis, so clear the de-emphasis filter state.
2535       memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
2536      }
2537 
2538      if(cdda.CDDAStatus == CDDASTATUS_SCANNING)
2539      {
2540       int64_t tmp_read_sec = read_sec;
2541 
2542       if(cdda.ScanMode & 1)
2543       {
2544        tmp_read_sec -= 24;
2545        if(tmp_read_sec < cdda.scan_sec_end)
2546         tmp_read_sec = cdda.scan_sec_end;
2547       }
2548       else
2549       {
2550        tmp_read_sec += 24;
2551        if(tmp_read_sec > cdda.scan_sec_end)
2552         tmp_read_sec = cdda.scan_sec_end;
2553       }
2554       read_sec = tmp_read_sec;
2555      }
2556      else
2557       read_sec++;
2558     } // End    if(CDDAReadPos == 588)
2559 
2560     if(!(cdda.CDDAReadPos % 6))
2561     {
2562      int subindex = cdda.CDDAReadPos / 6 - 2;
2563 
2564      if(subindex >= 0)
2565       CDStuffSubchannels(cd.SubPWBuf[subindex], subindex);
2566      else // The system-specific emulation code should handle what value the sync bytes are.
2567       CDStuffSubchannels(0x00, subindex);
2568     }
2569 
2570     // If the last valid sub-Q data decoded indicate that the corresponding sector is a data sector, don't output the
2571     // current sector as audio.
2572     if(!(cd.SubQBuf_Last[0] & 0x40) && cdda.PlayMode != PLAYMODE_SILENT)
2573     {
2574      cdda.sr[0] = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + cdda.OutPortChSelectCache[0]];
2575      cdda.sr[1] = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + cdda.OutPortChSelectCache[1]];
2576     }
2577 
2578 #if 0
2579     {
2580      static int16 wv = 0x7FFF; //0x5000;
2581      static unsigned counter = 0;
2582      static double phase = 0;
2583      static double phase_inc = 0;
2584      static const double phase_inc_inc = 0.000003 / 2;
2585 
2586      cdda.sr[0] = 32767 * sin(phase);
2587      cdda.sr[1] = 32767 * sin(phase);
2588 
2589      //cdda.sr[0] = wv;
2590      //cdda.sr[1] = wv;
2591 
2592      if(counter == 0)
2593       wv = -wv;
2594      counter = (counter + 1) & 1;
2595      phase += phase_inc;
2596      phase_inc += phase_inc_inc;
2597     }
2598 #endif
2599 
2600     {
2601      const unsigned obwp = cdda.OversamplePos >> 1;
2602      cdda.OversampleBuffer[0][obwp] = cdda.OversampleBuffer[0][0x10 + obwp] = cdda.sr[0];
2603      cdda.OversampleBuffer[1][obwp] = cdda.OversampleBuffer[1][0x10 + obwp] = cdda.sr[1];
2604     }
2605 
2606     cdda.CDDAReadPos++;
2607    } // End if(!(cdda.OversamplePos & 1))
2608 
2609    {
2610     const int16* f = OversampleFilter[cdda.OversamplePos & 1];
2611 #if defined(__SSE2__)
2612     __m128i f0 = _mm_load_si128((__m128i *)&f[0]);
2613     __m128i f1 = _mm_load_si128((__m128i *)&f[8]);
2614 #endif
2615 
2616     for(unsigned lr = 0; lr < 2; lr++)
2617     {
2618      const int16* b = &cdda.OversampleBuffer[lr][((cdda.OversamplePos >> 1) + 1) & 0xF];
2619 #if defined(__SSE2__)
2620      union
2621      {
2622       int32_t accum;
2623       float accum_f;
2624       //__m128i accum_m128;
2625      };
2626 
2627      {
2628       __m128i b0;
2629       __m128i b1;
2630       __m128i sum;
2631 
2632       b0 = _mm_loadu_si128((__m128i *)&b[0]);
2633       b1 = _mm_loadu_si128((__m128i *)&b[8]);
2634 
2635       sum = _mm_add_epi32(_mm_madd_epi16(f0, b0), _mm_madd_epi16(f1, b1));
2636       sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6)));
2637       sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, (1 << 0) | (0 << 2) | (3 << 4) | (2 << 6)));
2638       _mm_store_ss(&accum_f, (__m128)sum);
2639       //_mm_store_si128(&accum_m128, sum);
2640      }
2641 #else
2642      int32_t accum = 0;
2643 
2644      for(unsigned i = 0; i < 0x10; i++)
2645       accum += f[i] * b[i];
2646 #endif
2647      // sum_abs * cdda_min =
2648      // 59076 * -32768 = -1935802368
2649      // OPVC can have a maximum value of 65536.
2650      // -1935802368 * 65536 = -126864743989248
2651      //
2652      // -126864743989248 / 65536 = -1935802368
2653      sample_va[lr] = ((int64_t)accum * cdda.OutPortVolumeCache[lr]) >> 16;
2654      // Output of this stage will be (approximate max ranges) -2147450880 through 2147385345.
2655     }
2656    }
2657 
2658    //
2659    // This de-emphasis filter's frequency response isn't totally correct, but it's much better than nothing(and it's not like any known PCE CD/TG16 CD/PC-FX games
2660    // utilize pre-emphasis anyway).
2661    //
2662    if(MDFN_UNLIKELY(cd.SubQBuf_Last[0] & 0x10))
2663    {
2664     //puts("Deemph");
2665     for(unsigned lr = 0; lr < 2; lr++)
2666     {
2667      float inv = sample_va[lr] * 0.35971507338824012f;
2668 
2669      cdda.DeemphState[lr][1] = (cdda.DeemphState[lr][0] - 0.4316395666f * inv) + (0.7955522347f * cdda.DeemphState[lr][1]);
2670      cdda.DeemphState[lr][0] = inv;
2671 
2672      sample_va[lr] = std::max<float>(-2147483648.0, std::min<float>(2147483647.0, cdda.DeemphState[lr][1]));
2673      //printf("%u: %f, %d\n", lr, cdda.DeemphState[lr][1], sample_va[lr]);
2674     }
2675    }
2676 
2677 
2678    if(HRBufs[0] && HRBufs[1])
2679    {
2680     //
2681     // FINAL_OUT_SHIFT should be 32 so we can take advantage of 32x32->64 multipliers on 32-bit CPUs.
2682     //
2683     #define FINAL_OUT_SHIFT 32
2684     #define MULT_SHIFT_ADJ (32 - (26 + (8 - CDDA_FILTER_NUMPHASES_SHIFT)))
2685 
2686     #if (((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - 0) << MULT_SHIFT_ADJ) > 32767
2687      #error "COEFF MULT OVERFLOW"
2688     #endif
2689 
2690     const int16 mult_a = ((1 << (16 - CDDA_FILTER_NUMPHASES_SHIFT)) - synthtime_phase_fract) << MULT_SHIFT_ADJ;
2691     const int16 mult_b = synthtime_phase_fract << MULT_SHIFT_ADJ;
2692     int32_t coeff[CDDA_FILTER_NUMCONVOLUTIONS];
2693 
2694     //if(synthtime_phase_fract == 0)
2695     // printf("%5d: %d %d\n", synthtime_phase_fract, mult_a, mult_b);
2696 
2697     for(unsigned c = 0; c < CDDA_FILTER_NUMCONVOLUTIONS; c++)
2698     {
2699      coeff[c] = (CDDA_Filter[1 + synthtime_phase_int + 0][c] * mult_a +
2700 		 CDDA_Filter[1 + synthtime_phase_int + 1][c] * mult_b);
2701     }
2702 
2703     int32_t* tb0 = &HRBufs[0][synthtime];
2704     int32_t* tb1 = &HRBufs[1][synthtime];
2705 
2706     for(unsigned c = 0; c < CDDA_FILTER_NUMCONVOLUTIONS; c++)
2707     {
2708      tb0[c] += ((int64_t)coeff[c] * sample_va[0]) >> FINAL_OUT_SHIFT;
2709      tb1[c] += ((int64_t)coeff[c] * sample_va[1]) >> FINAL_OUT_SHIFT;
2710     }
2711     #undef FINAL_OUT_SHIFT
2712     #undef MULT_SHIFT_ADJ
2713    }
2714 
2715    cdda.OversamplePos = (cdda.OversamplePos + 1) & 0x1F;
2716   } // end while(cdda.CDDADiv <= 0)
2717  }
2718 }
2719 
RunCDRead(uint32_t system_timestamp,int32_t run_time)2720 static INLINE void RunCDRead(uint32_t system_timestamp, int32_t run_time)
2721 {
2722  if(CDReadTimer > 0)
2723  {
2724   CDReadTimer -= run_time;
2725 
2726   if(CDReadTimer <= 0)
2727   {
2728    if(din->CanWrite() < ((WhichSystem == SCSICD_PCFX) ? 2352 : 2048))	// +96 if we find out the PC-FX can read subchannel data along with raw data too. ;)
2729    {
2730     //printf("Carp: %d %d %d\n", din->CanWrite(), SectorCount, CDReadTimer);
2731     //CDReadTimer = (cd.data_in_size - cd.data_in_pos) * 10;
2732 
2733     CDReadTimer += (uint64_t) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
2734 
2735     //CDReadTimer += (uint64_t) 1 * 128 * System_Clock / CD_DATA_TRANSFER_RATE;
2736    }
2737    else
2738    {
2739     uint8_t tmp_read_buf[2352 + 96];
2740 
2741     if(TrayOpen)
2742     {
2743      din->Flush();
2744      cd.data_transfer_done = FALSE;
2745 
2746      CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
2747     }
2748     else if(!Cur_CDIF)
2749     {
2750      CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
2751     }
2752     else if(SectorAddr >= toc.tracks[100].lba)
2753     {
2754      CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
2755     }
2756     else if(!Cur_CDIF->ReadRawSector(tmp_read_buf, SectorAddr))	//, SectorAddr + SectorCount))
2757     {
2758      cd.data_transfer_done = FALSE;
2759 
2760      CommandCCError(SENSEKEY_ILLEGAL_REQUEST);
2761     }
2762     else if(ValidateRawDataSector(tmp_read_buf, SectorAddr))
2763     {
2764      memcpy(cd.SubPWBuf, tmp_read_buf + 2352, 96);
2765 
2766      if(tmp_read_buf[12 + 3] == 0x2)
2767       din->Write(tmp_read_buf + 24, 2048);
2768      else
2769       din->Write(tmp_read_buf + 16, 2048);
2770 
2771      GenSubQFromSubPW();
2772 
2773      CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_READY);
2774 
2775      SectorAddr++;
2776      SectorCount--;
2777 
2778      if(CurrentPhase != PHASE_DATA_IN)
2779       ChangePhase(PHASE_DATA_IN);
2780 
2781      if(SectorCount)
2782      {
2783       cd.data_transfer_done = FALSE;
2784       CDReadTimer += (uint64_t) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
2785      }
2786      else
2787      {
2788       cd.data_transfer_done = TRUE;
2789      }
2790     }
2791    }				// end else to if(!Cur_CDIF->ReadSector
2792 
2793   }
2794  }
2795 }
2796 
2797 
SCSICD_Run(scsicd_timestamp_t system_timestamp)2798 uint32_t SCSICD_Run(scsicd_timestamp_t system_timestamp)
2799 {
2800  int32_t run_time = system_timestamp - lastts;
2801 
2802  if(system_timestamp < lastts)
2803  {
2804   fprintf(stderr, "Meow: %d %d\n", system_timestamp, lastts);
2805   assert(system_timestamp >= lastts);
2806  }
2807 
2808  monotonic_timestamp += run_time;
2809 
2810  lastts = system_timestamp;
2811 
2812  RunCDRead(system_timestamp, run_time);
2813  RunCDDA(system_timestamp, run_time);
2814 
2815  bool ResetNeeded = false;
2816 
2817  if(RST_signal && !cd.last_RST_signal)
2818   ResetNeeded = true;
2819 
2820  cd.last_RST_signal = RST_signal;
2821 
2822  if(ResetNeeded)
2823  {
2824   //puts("RST");
2825   VirtualReset();
2826  }
2827  else if(CurrentPhase == PHASE_BUS_FREE)
2828  {
2829   if(SEL_signal)
2830   {
2831    if(WhichSystem == SCSICD_PCFX)
2832    {
2833     //if(cd_bus.DB == 0x84)
2834     {
2835      ChangePhase(PHASE_COMMAND);
2836     }
2837    }
2838    else // PCE
2839    {
2840     ChangePhase(PHASE_COMMAND);
2841    }
2842   }
2843  }
2844  else if(ATN_signal && !REQ_signal && !ACK_signal)
2845  {
2846   //printf("Yay: %d %d\n", REQ_signal, ACK_signal);
2847   ChangePhase(PHASE_MESSAGE_OUT);
2848  }
2849  else switch(CurrentPhase)
2850  {
2851   case PHASE_COMMAND:
2852     if(REQ_signal && ACK_signal)	// Data bus is valid nowww
2853     {
2854      //printf("Command Phase Byte I->T: %02x, %d\n", cd_bus.DB, cd.command_buffer_pos);
2855      cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB;
2856      SetREQ(FALSE);
2857     }
2858 
2859     if(!REQ_signal && !ACK_signal && cd.command_buffer_pos)	// Received at least one byte, what should we do?
2860     {
2861      if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
2862      {
2863       const SCSICH *cmd_info_ptr;
2864 
2865       if(WhichSystem == SCSICD_PCFX)
2866        cmd_info_ptr = PCFXCommandDefs;
2867       else
2868        cmd_info_ptr = PCECommandDefs;
2869 
2870       while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0])
2871        cmd_info_ptr++;
2872 
2873       if(SCSILog)
2874       {
2875        char log_buffer[1024];
2876        int lb_pos;
2877 
2878        log_buffer[0] = 0;
2879 
2880        lb_pos = snprintf(log_buffer, 1024, "Command: %02x, %s%s  ", cd.command_buffer[0], cmd_info_ptr->pretty_name ? cmd_info_ptr->pretty_name : "!!BAD COMMAND!!",
2881 			(cmd_info_ptr->flags & SCF_UNTESTED) ? "(UNTESTED)" : "");
2882 
2883        for(int i = 0; i < RequiredCDBLen[cd.command_buffer[0] >> 4]; i++)
2884         lb_pos += snprintf(log_buffer + lb_pos, 1024 - lb_pos, "%02x ", cd.command_buffer[i]);
2885 
2886        SCSILog("SCSI", "%s", log_buffer);
2887        //puts(log_buffer);
2888       }
2889 
2890 
2891       if(cmd_info_ptr->pretty_name == NULL)	// Command not found!
2892       {
2893        CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND);
2894 
2895        //SCSIDBG("Bad Command: %02x\n", cd.command_buffer[0]);
2896 
2897        if(SCSILog)
2898         SCSILog("SCSI", "Bad Command: %02x", cd.command_buffer[0]);
2899 
2900        cd.command_buffer_pos = 0;
2901       }
2902       else
2903       {
2904        if(cmd_info_ptr->flags & SCF_UNTESTED)
2905        {
2906         //SCSIDBG("Untested SCSI command: %02x, %s", cd.command_buffer[0], cmd_info_ptr->pretty_name);
2907        }
2908 
2909        if(TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
2910        {
2911 	CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
2912        }
2913        else if(!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
2914        {
2915 	CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
2916        }
2917        else if(cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
2918        {
2919 	CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED);
2920 	cd.DiscChanged = false;
2921        }
2922        else
2923        {
2924 	bool prev_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING);
2925 
2926         cmd_info_ptr->func(cd.command_buffer);
2927 
2928 	bool new_ps = (cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING);
2929 
2930 	// A bit kludgey, but ehhhh.
2931 	if(!prev_ps && new_ps)
2932 	{
2933 	 memset(cdda.sr, 0, sizeof(cdda.sr));
2934 	 memset(cdda.OversampleBuffer, 0, sizeof(cdda.OversampleBuffer));
2935 	 memset(cdda.DeemphState, 0, sizeof(cdda.DeemphState));
2936 	 //printf("CLEAR BUFFERS LALALA\n");
2937 	}
2938        }
2939 
2940        cd.command_buffer_pos = 0;
2941       }
2942      } // end if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
2943      else			// Otherwise, get more data for the command!
2944       SetREQ(TRUE);
2945     }
2946     break;
2947 
2948   case PHASE_DATA_OUT:
2949     if(REQ_signal && ACK_signal)	// Data bus is valid nowww
2950     {
2951      //printf("DATAOUT-SCSIIN: %d %02x\n", cd.data_out_pos, cd_bus.DB);
2952      cd.data_out[cd.data_out_pos++] = cd_bus.DB;
2953      SetREQ(FALSE);
2954     }
2955     else if(!REQ_signal && !ACK_signal && cd.data_out_pos)
2956     {
2957      if(cd.data_out_pos == cd.data_out_want)
2958      {
2959       cd.data_out_pos = 0;
2960 
2961       if(cd.command_buffer[0] == 0x15)
2962 	FinishMODESELECT6(cd.data_out, cd.data_out_want);
2963       else	// Error out here?  It shouldn't be reached:
2964        SendStatusAndMessage(STATUS_GOOD, 0x00);
2965      }
2966      else
2967       SetREQ(TRUE);
2968     }
2969     break;
2970 
2971 
2972   case PHASE_MESSAGE_OUT:
2973    //printf("%d %d, %02x\n", REQ_signal, ACK_signal, cd_bus.DB);
2974    if(REQ_signal && ACK_signal)
2975    {
2976     SetREQ(FALSE);
2977 
2978     // ABORT message is 0x06, but the code isn't set up to be able to recover from a MESSAGE OUT phase back to the previous phase, so we treat any message as an ABORT.
2979     // Real tests are needed on the PC-FX to determine its behavior.
2980     //  (Previously, ATN emulation was a bit broken, which resulted in the wrong data on the data bus in this code path in at least "Battle Heat", but it's fixed now and 0x06 is on the data bus).
2981     //if(cd_bus.DB == 0x6)		// ABORT message!
2982     if(1)
2983     {
2984      //printf("[SCSICD] Abort Received(DB=0x%02x)\n", cd_bus.DB);
2985      din->Flush();
2986      cd.data_out_pos = cd.data_out_want = 0;
2987 
2988      CDReadTimer = 0;
2989      cdda.CDDAStatus = CDDASTATUS_STOPPED;
2990      ChangePhase(PHASE_BUS_FREE);
2991     }
2992     //else
2993     // printf("[SCSICD] Message to target: 0x%02x\n", cd_bus.DB);
2994    }
2995    break;
2996 
2997 
2998   case PHASE_STATUS:
2999     if(REQ_signal && ACK_signal)
3000     {
3001      SetREQ(FALSE);
3002      cd.status_sent = TRUE;
3003     }
3004 
3005     if(!REQ_signal && !ACK_signal && cd.status_sent)
3006     {
3007      // Status sent, so get ready to send the message!
3008      cd.status_sent = FALSE;
3009      cd_bus.DB = cd.message_pending;
3010 
3011      ChangePhase(PHASE_MESSAGE_IN);
3012     }
3013     break;
3014 
3015   case PHASE_DATA_IN:
3016     if(!REQ_signal && !ACK_signal)
3017     {
3018      //puts("REQ and ACK false");
3019      if(din->in_count == 0)	// aaand we're done!
3020      {
3021       CDIRQCallback(0x8000 | SCSICD_IRQ_DATA_TRANSFER_READY);
3022 
3023       if(cd.data_transfer_done)
3024       {
3025        SendStatusAndMessage(STATUS_GOOD, 0x00);
3026        cd.data_transfer_done = FALSE;
3027        CDIRQCallback(SCSICD_IRQ_DATA_TRANSFER_DONE);
3028       }
3029      }
3030      else
3031      {
3032       cd_bus.DB = din->ReadByte();
3033       SetREQ(TRUE);
3034      }
3035     }
3036     if(REQ_signal && ACK_signal)
3037     {
3038      //puts("REQ and ACK true");
3039      SetREQ(FALSE);
3040     }
3041     break;
3042 
3043   case PHASE_MESSAGE_IN:
3044    if(REQ_signal && ACK_signal)
3045    {
3046     SetREQ(FALSE);
3047     cd.message_sent = TRUE;
3048    }
3049 
3050    if(!REQ_signal && !ACK_signal && cd.message_sent)
3051    {
3052     cd.message_sent = FALSE;
3053     ChangePhase(PHASE_BUS_FREE);
3054    }
3055    break;
3056  }
3057 
3058  int32_t next_time = 0x7fffffff;
3059 
3060  if(CDReadTimer > 0 && CDReadTimer < next_time)
3061   next_time = CDReadTimer;
3062 
3063  if(cdda.CDDAStatus == CDDASTATUS_PLAYING || cdda.CDDAStatus == CDDASTATUS_SCANNING)
3064  {
3065   int32_t cdda_div_sexytime = (cdda.CDDADiv + (cdda.CDDADivAcc * (cdda.OversamplePos & 1)) + ((1 << 20) - 1)) >> 20;
3066   if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time)
3067    next_time = cdda_div_sexytime;
3068  }
3069 
3070  assert(next_time >= 0);
3071 
3072  return(next_time);
3073 }
3074 
SCSICD_SetLog(void (* logfunc)(const char *,const char *,...))3075 void SCSICD_SetLog(void (*logfunc)(const char *, const char *, ...))
3076 {
3077  SCSILog = logfunc;
3078 }
3079 
SCSICD_SetTransferRate(uint32_t TransferRate)3080 void SCSICD_SetTransferRate(uint32_t TransferRate)
3081 {
3082  CD_DATA_TRANSFER_RATE = TransferRate;
3083 }
3084 
SCSICD_Close(void)3085 void SCSICD_Close(void)
3086 {
3087  if(din)
3088  {
3089   delete din;
3090   din = NULL;
3091  }
3092 }
3093 
SCSICD_Init(int type,int cdda_time_div,int32_t * left_hrbuf,int32_t * right_hrbuf,uint32_t TransferRate,uint32_t SystemClock,void (* IRQFunc)(int),void (* SSCFunc)(uint8_t,int))3094 void SCSICD_Init(int type, int cdda_time_div, int32_t* left_hrbuf, int32_t* right_hrbuf, uint32_t TransferRate, uint32_t SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8_t, int))
3095 {
3096  Cur_CDIF = NULL;
3097  TrayOpen = true;
3098 
3099  assert(SystemClock < 30000000);	// 30 million, sanity check.
3100 
3101  monotonic_timestamp = 0;
3102  lastts = 0;
3103 
3104  SCSILog = NULL;
3105 
3106  if(type == SCSICD_PCFX)
3107   din = new SimpleFIFO<uint8_t>(65536);	//4096);
3108  else
3109   din = new SimpleFIFO<uint8_t>(2048); //8192); //1024); /2048);
3110 
3111  WhichSystem = type;
3112 
3113  cdda.CDDADivAcc = (int64_t)System_Clock * (1024 * 1024) / 88200;
3114  cdda.CDDADivAccVolFudge = 100;
3115  cdda.CDDATimeDiv = cdda_time_div * (1 << (4 + 2));
3116 
3117  cdda.CDDAVolume[0] = 65536;
3118  cdda.CDDAVolume[1] = 65536;
3119 
3120  FixOPV();
3121 
3122  HRBufs[0] = left_hrbuf;
3123  HRBufs[1] = right_hrbuf;
3124 
3125  CD_DATA_TRANSFER_RATE = TransferRate;
3126  System_Clock = SystemClock;
3127  CDIRQCallback = IRQFunc;
3128  CDStuffSubchannels = SSCFunc;
3129 }
3130 
SCSICD_SetCDDAVolume(double left,double right)3131 void SCSICD_SetCDDAVolume(double left, double right)
3132 {
3133  cdda.CDDAVolume[0] = 65536 * left;
3134  cdda.CDDAVolume[1] = 65536 * right;
3135 
3136  for(int i = 0; i < 2; i++)
3137  {
3138   if(cdda.CDDAVolume[i] > 65536)
3139   {
3140    printf("[SCSICD] Debug Warning: CD-DA volume %d too large: %d\n", i, cdda.CDDAVolume[i]);
3141    cdda.CDDAVolume[i] = 65536;
3142   }
3143  }
3144 
3145  FixOPV();
3146 }
3147 
SCSICD_StateAction(StateMem * sm,const unsigned load,const bool data_only,const char * sname)3148 int SCSICD_StateAction(StateMem* sm, const unsigned load, const bool data_only, const char *sname)
3149 {
3150  SFORMAT StateRegs[] =
3151  {
3152   SFVARN(cd_bus.DB, "DB"),
3153   SFVARN(cd_bus.signals, "Signals"),
3154   SFVAR(CurrentPhase),
3155 
3156   SFVARN(cd.last_RST_signal, "last_RST"),
3157   SFVARN(cd.message_pending, "message_pending"),
3158   SFVARN(cd.status_sent, "status_sent"),
3159   SFVARN(cd.message_sent, "message_sent"),
3160   SFVARN(cd.key_pending, "key_pending"),
3161   SFVARN(cd.asc_pending, "asc_pending"),
3162   SFVARN(cd.ascq_pending, "ascq_pending"),
3163   SFVARN(cd.fru_pending, "fru_pending"),
3164 
3165   SFARRAYN(cd.command_buffer, 256, "command_buffer"),
3166   SFVARN(cd.command_buffer_pos, "command_buffer_pos"),
3167   SFVARN(cd.command_size_left, "command_size_left"),
3168 
3169   // Don't save the FIFO's write position, it will be reconstructed from read_pos and in_count
3170   SFARRAYN(&din->data[0], din->data.size(), "din_fifo"),
3171   SFVARN(din->read_pos, "din_read_pos"),
3172   SFVARN(din->in_count, "din_in_count"),
3173   SFVARN(cd.data_transfer_done, "data_transfer_done"),
3174 
3175   SFARRAYN(cd.data_out, sizeof(cd.data_out), "data_out"),
3176   SFVARN(cd.data_out_pos, "data_out_pos"),
3177   SFVARN(cd.data_out_want, "data_out_want"),
3178 
3179   SFVARN(cd.DiscChanged, "DiscChanged"),
3180 
3181   SFVAR(cdda.PlayMode),
3182   SFARRAY16(cdda.CDDASectorBuffer, 1176),
3183   SFVAR(cdda.CDDAReadPos),
3184   SFVAR(cdda.CDDAStatus),
3185   SFVAR(cdda.CDDADiv),
3186   SFVAR(read_sec_start),
3187   SFVAR(read_sec),
3188   SFVAR(read_sec_end),
3189 
3190   SFVAR(CDReadTimer),
3191   SFVAR(SectorAddr),
3192   SFVAR(SectorCount),
3193 
3194   SFVAR(cdda.ScanMode),
3195   SFVAR(cdda.scan_sec_end),
3196 
3197   SFVAR(cdda.OversamplePos),
3198   SFARRAY16(&cdda.sr[0], sizeof(cdda.sr) / sizeof(cdda.sr[0])),
3199   SFARRAY16(&cdda.OversampleBuffer[0][0], sizeof(cdda.OversampleBuffer) / sizeof(cdda.OversampleBuffer[0][0])),
3200 
3201   SFVAR(cdda.DeemphState[0][0]),
3202   SFVAR(cdda.DeemphState[0][1]),
3203   SFVAR(cdda.DeemphState[1][0]),
3204   SFVAR(cdda.DeemphState[1][1]),
3205 
3206   SFARRAYN(&cd.SubQBuf[0][0], sizeof(cd.SubQBuf), "SubQBufs"),
3207   SFARRAYN(cd.SubQBuf_Last, sizeof(cd.SubQBuf_Last), "SubQBufLast"),
3208   SFARRAYN(cd.SubPWBuf, sizeof(cd.SubPWBuf), "SubPWBuf"),
3209 
3210   SFVAR(monotonic_timestamp),
3211   SFVAR(pce_lastsapsp_timestamp),
3212 
3213   //
3214   //
3215   //
3216   SFARRAY(ModePages[0].current_value, ModePages[0].param_length),
3217   SFARRAY(ModePages[1].current_value, ModePages[1].param_length),
3218   SFARRAY(ModePages[2].current_value, ModePages[2].param_length),
3219   SFARRAY(ModePages[3].current_value, ModePages[3].param_length),
3220   SFARRAY(ModePages[4].current_value, ModePages[4].param_length),
3221   SFEND
3222  };
3223 
3224  int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, sname, false);
3225 
3226  if(load)
3227  {
3228   din->in_count &= din->size - 1;
3229   din->read_pos &= din->size - 1;
3230   din->write_pos = (din->read_pos + din->in_count) & (din->size - 1);
3231   //printf("%d %d %d\n", din->in_count, din->read_pos, din->write_pos);
3232 
3233   if(load < 0x0935)
3234    cdda.CDDADiv /= 2;
3235 
3236   if(cdda.CDDADiv <= 0)
3237    cdda.CDDADiv = 1;
3238 
3239   cdda.OversamplePos &= 0x1F;
3240 
3241   for(int i = 0; i < NumModePages; i++)
3242    UpdateMPCacheP(&ModePages[i]);
3243  }
3244 
3245  return ret;
3246 }
3247