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