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.h"
19 #include "../mednafen-endian.h"
20 #include <math.h>
21 #include "pcecd_drive.h"
22 #include "../cdrom/cdromif.h"
23 #include "../cdrom/SimpleFIFO.h"
24 #include "../state_helpers.h"
25
26 static uint32 CD_DATA_TRANSFER_RATE;
27 static uint32 System_Clock;
28 static void (*CDIRQCallback)(int);
29 static void (*CDStuffSubchannels)(uint8, int);
30 static Blip_Buffer *sbuf[2];
31 static CDIF *Cur_CDIF;
32
33 // Internal operation to the SCSI CD unit. Only pass 1 or 0 to these macros!
34 #define SetIOP(mask, set) { cd_bus.signals &= ~mask; if(set) cd_bus.signals |= mask; }
35
36 #define SetBSY(set) SetIOP(PCECD_Drive_BSY_mask, set)
37 #define SetIO(set) SetIOP(PCECD_Drive_IO_mask, set)
38 #define SetCD(set) SetIOP(PCECD_Drive_CD_mask, set)
39 #define SetMSG(set) SetIOP(PCECD_Drive_MSG_mask, set)
40
SetREQ(bool set)41 static INLINE void SetREQ(bool set)
42 {
43 if(set && !REQ_signal)
44 CDIRQCallback(PCECD_Drive_IRQ_MAGICAL_REQ);
45
46 SetIOP(PCECD_Drive_REQ_mask, set);
47 }
48
49 #define SetkingACK(set) SetIOP(PCECD_Drive_kingACK_mask, set)
50 #define SetkingRST(set) SetIOP(PCECD_Drive_kingRST_mask, set)
51 #define SetkingSEL(set) SetIOP(PCECD_Drive_kingSEL_mask, set)
52
53
54 enum
55 {
56 QMode_Zero = 0,
57 QMode_Time = 1,
58 QMode_MCN = 2, // Media Catalog Number
59 QMode_ISRC = 3 // International Standard Recording Code
60 };
61
62 typedef struct
63 {
64 bool last_RST_signal;
65
66 // The pending message to send(in the message phase)
67 uint8 message_pending;
68
69 bool status_sent, message_sent;
70
71 // Pending error codes
72 uint8 key_pending, asc_pending, ascq_pending, fru_pending;
73
74 uint8 command_buffer[256];
75 uint8 command_buffer_pos;
76 uint8 command_size_left;
77
78 // FALSE if not all pending data is in the FIFO, TRUE if it is.
79 // Used for multiple sector CD reads.
80 bool data_transfer_done;
81
82 bool TrayOpen;
83 bool DiscChanged;
84
85 uint8 SubQBuf[4][0xC]; // One for each of the 4 most recent q-Modes.
86 uint8 SubQBuf_Last[0xC]; // The most recent q subchannel data, regardless of q-mode.
87
88 uint8 SubPWBuf[96];
89
90 } pcecd_drive_t;
91
92 typedef Blip_Synth CDSynth;
93
94 enum
95 {
96 CDDASTATUS_PAUSED = -1,
97 CDDASTATUS_STOPPED = 0,
98 CDDASTATUS_PLAYING = 1,
99 };
100
101 enum
102 {
103 PLAYMODE_SILENT = 0x00,
104 PLAYMODE_NORMAL,
105 PLAYMODE_INTERRUPT,
106 PLAYMODE_LOOP,
107 };
108
109 typedef struct
110 {
111 int32 CDDADivAcc;
112 uint32 scan_sec_end;
113
114 uint8 PlayMode;
115 CDSynth CDDASynth;
116 int32 CDDAVolume;
117 int16 last_sample[2];
118 int16 CDDASectorBuffer[1176];
119 uint32 CDDAReadPos;
120
121 int8 CDDAStatus;
122 uint8 ScanMode;
123 int32 CDDADiv;
124 int CDDATimeDiv;
125 } cdda_t;
126
MakeSense(uint8 target[18],uint8 key,uint8 asc,uint8 ascq,uint8 fru)127 static INLINE void MakeSense(uint8 target[18], uint8 key, uint8 asc, uint8 ascq, uint8 fru)
128 {
129 memset(target, 0, 18);
130
131 target[0] = 0x70; // Current errors and sense data is not SCSI compliant
132 target[2] = key;
133 target[7] = 0x0A;
134 target[12] = asc; // Additional Sense Code
135 target[13] = ascq; // Additional Sense Code Qualifier
136 target[14] = fru; // Field Replaceable Unit code
137 }
138
139 static pcecd_drive_timestamp_t lastts;
140 static int64 monotonic_timestamp;
141 static int64 pce_lastsapsp_timestamp;
142
143 static pcecd_drive_t cd;
144 pcecd_drive_bus_t cd_bus;
145 static cdda_t cdda;
146
147 static SimpleFIFO din(2048);
148
149 static TOC toc;
150
151 static uint32 read_sec_start;
152 static uint32 read_sec;
153 static uint32 read_sec_end;
154
155 static int32 CDReadTimer;
156 static uint32 SectorAddr;
157 static uint32 SectorCount;
158
159
160 enum
161 {
162 PHASE_BUS_FREE = 0,
163 PHASE_COMMAND,
164 PHASE_DATA_IN,
165 PHASE_STATUS,
166 PHASE_MESSAGE_IN,
167 };
168
169 static unsigned int CurrentPhase;
170 static void ChangePhase(const unsigned int new_phase);
171
VirtualReset(void)172 static void VirtualReset(void)
173 {
174 din.Flush();
175
176 cdda.CDDADivAcc = (int64)System_Clock * 65536 / 44100;
177 CDReadTimer = 0;
178
179 pce_lastsapsp_timestamp = monotonic_timestamp;
180
181 SectorAddr = SectorCount = 0;
182 read_sec_start = read_sec = 0;
183 read_sec_end = ~0;
184
185 cdda.PlayMode = PLAYMODE_SILENT;
186 cdda.CDDAReadPos = 0;
187 cdda.CDDAStatus = CDDASTATUS_STOPPED;
188 cdda.CDDADiv = 0;
189
190 cdda.ScanMode = 0;
191 cdda.scan_sec_end = 0;
192
193 ChangePhase(PHASE_BUS_FREE);
194 }
195
PCECD_Drive_Power(pcecd_drive_timestamp_t system_timestamp)196 void PCECD_Drive_Power(pcecd_drive_timestamp_t system_timestamp)
197 {
198 memset(&cd, 0, sizeof(pcecd_drive_t));
199 memset(&cd_bus, 0, sizeof(pcecd_drive_bus_t));
200
201 monotonic_timestamp = system_timestamp;
202
203 cd.DiscChanged = false;
204
205 if(Cur_CDIF && !cd.TrayOpen)
206 Cur_CDIF->ReadTOC(&toc);
207
208 CurrentPhase = PHASE_BUS_FREE;
209
210 VirtualReset();
211 }
212
213
PCECD_Drive_SetDB(uint8 data)214 void PCECD_Drive_SetDB(uint8 data)
215 {
216 cd_bus.DB = data;
217 //printf("Set DB: %02x\n", data);
218 }
219
PCECD_Drive_SetACK(bool set)220 void PCECD_Drive_SetACK(bool set)
221 {
222 SetkingACK(set);
223 //printf("Set ACK: %d\n", set);
224 }
225
PCECD_Drive_SetSEL(bool set)226 void PCECD_Drive_SetSEL(bool set)
227 {
228 SetkingSEL(set);
229 //printf("Set SEL: %d\n", set);
230 }
231
PCECD_Drive_SetRST(bool set)232 void PCECD_Drive_SetRST(bool set)
233 {
234 SetkingRST(set);
235 //printf("Set RST: %d\n", set);
236 }
237
GenSubQFromSubPW(void)238 static void GenSubQFromSubPW(void)
239 {
240 uint8 SubQBuf[0xC];
241
242 subq_deinterleave(cd.SubPWBuf, SubQBuf);
243
244 //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);
245 // Debug code, remove me.
246 //for(int i = 0; i < 0xC; i++)
247 // printf("%02x ", SubQBuf[i]);
248 //printf("\n");
249
250 if(subq_check_checksum(SubQBuf))
251 {
252 memcpy(cd.SubQBuf_Last, SubQBuf, 0xC);
253
254 uint8 adr = SubQBuf[0] & 0xF;
255
256 if(adr <= 0x3)
257 memcpy(cd.SubQBuf[adr], SubQBuf, 0xC);
258
259 //if(adr == 0x02)
260 //for(int i = 0; i < 12; i++)
261 // printf("%02x\n", cd.SubQBuf[0x2][i]);
262 }
263 }
264
265
266 #define STATUS_GOOD 0
267 #define STATUS_CHECK_CONDITION 1
268
269 #define SENSEKEY_NO_SENSE 0x0
270 #define SENSEKEY_NOT_READY 0x2
271 #define SENSEKEY_MEDIUM_ERROR 0x3
272 #define SENSEKEY_HARDWARE_ERROR 0x4
273 #define SENSEKEY_ILLEGAL_REQUEST 0x5
274 #define SENSEKEY_UNIT_ATTENTION 0x6
275 #define SENSEKEY_ABORTED_COMMAND 0xB
276
277 #define ASC_MEDIUM_NOT_PRESENT 0x3A
278
279
280 // NEC sub-errors(ASC), no ASCQ.
281 #define NSE_NO_DISC 0x0B // Used with SENSEKEY_NOT_READY - This condition occurs when tray is closed with no disc present.
282 #define NSE_TRAY_OPEN 0x0D // Used with SENSEKEY_NOT_READY
283 #define NSE_SEEK_ERROR 0x15
284 #define NSE_HEADER_READ_ERROR 0x16 // Used with SENSEKEY_MEDIUM_ERROR
285 #define NSE_NOT_AUDIO_TRACK 0x1C // Used with SENSEKEY_MEDIUM_ERROR
286 #define NSE_NOT_DATA_TRACK 0x1D // Used with SENSEKEY_MEDIUM_ERROR
287 #define NSE_INVALID_COMMAND 0x20
288 #define NSE_INVALID_ADDRESS 0x21
289 #define NSE_INVALID_PARAMETER 0x22
290 #define NSE_END_OF_VOLUME 0x25
291 #define NSE_INVALID_REQUEST_IN_CDB 0x27
292 #define NSE_DISC_CHANGED 0x28 // Used with SENSEKEY_UNIT_ATTENTION
293 #define NSE_AUDIO_NOT_PLAYING 0x2C
294
295 // ASC, ASCQ pair
296 #define AP_UNRECOVERED_READ_ERROR 0x11, 0x00
297 #define AP_LEC_UNCORRECTABLE_ERROR 0x11, 0x05
298 #define AP_CIRC_UNRECOVERED_ERROR 0x11, 0x06
299
300 #define AP_UNKNOWN_MEDIUM_FORMAT 0x30, 0x01
301 #define AP_INCOMPAT_MEDIUM_FORMAT 0x30, 0x02
302
ChangePhase(const unsigned int new_phase)303 static void ChangePhase(const unsigned int new_phase)
304 {
305 //printf("New phase: %d %lld\n", new_phase, monotonic_timestamp);
306 switch(new_phase)
307 {
308 case PHASE_BUS_FREE:
309 SetBSY(false);
310 SetMSG(false);
311 SetCD(false);
312 SetIO(false);
313 SetREQ(false);
314
315 CDIRQCallback(0x8000 | PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
316 break;
317
318 case PHASE_DATA_IN: // Us to them
319 SetBSY(true);
320 SetMSG(false);
321 SetCD(false);
322 SetIO(true);
323 //SetREQ(true);
324 SetREQ(false);
325 break;
326
327 case PHASE_STATUS: // Us to them
328 SetBSY(true);
329 SetMSG(false);
330 SetCD(true);
331 SetIO(true);
332 SetREQ(true);
333 break;
334
335 case PHASE_MESSAGE_IN: // Us to them
336 SetBSY(true);
337 SetMSG(true);
338 SetCD(true);
339 SetIO(true);
340 SetREQ(true);
341 break;
342
343 case PHASE_COMMAND: // Them to us
344 SetBSY(true);
345 SetMSG(false);
346 SetCD(true);
347 SetIO(false);
348 SetREQ(true);
349 break;
350 }
351 CurrentPhase = new_phase;
352 }
353
SendStatusAndMessage(uint8 status,uint8 message)354 static void SendStatusAndMessage(uint8 status, uint8 message)
355 {
356 // This should never ever happen, but that doesn't mean it won't. ;)
357 if(din.in_count)
358 {
359 /* BUG: x bytes still in SCSI CD FIFO */
360 din.Flush();
361 }
362
363 cd.message_pending = message;
364
365 cd.status_sent = FALSE;
366 cd.message_sent = FALSE;
367
368
369 if(status == STATUS_GOOD)
370 cd_bus.DB = 0x00;
371 else
372 cd_bus.DB = 0x01;
373
374
375 ChangePhase(PHASE_STATUS);
376 }
377
DoSimpleDataIn(const uint8 * data_in,uint32 len)378 static void DoSimpleDataIn(const uint8 *data_in, uint32 len)
379 {
380 din.Write(data_in, len);
381
382 cd.data_transfer_done = true;
383
384 ChangePhase(PHASE_DATA_IN);
385 }
386
PCECD_Drive_SetDisc(bool tray_open,CDIF * cdif,bool no_emu_side_effects)387 void PCECD_Drive_SetDisc(bool tray_open, CDIF *cdif, bool no_emu_side_effects)
388 {
389 Cur_CDIF = cdif;
390
391 // Closing the tray.
392 if(cd.TrayOpen && !tray_open)
393 {
394 cd.TrayOpen = false;
395
396 if(cdif)
397 {
398 cdif->ReadTOC(&toc);
399
400 if(!no_emu_side_effects)
401 {
402 memset(cd.SubQBuf, 0, sizeof(cd.SubQBuf));
403 memset(cd.SubQBuf_Last, 0, sizeof(cd.SubQBuf_Last));
404 cd.DiscChanged = true;
405 }
406 }
407 }
408 else if(!cd.TrayOpen && tray_open) // Opening the tray
409 {
410 cd.TrayOpen = true;
411 }
412 }
413
CommandCCError(int key,int asc=0,int ascq=0)414 static void CommandCCError(int key, int asc = 0, int ascq = 0)
415 {
416 /* CC error */
417
418 cd.key_pending = key;
419 cd.asc_pending = asc;
420 cd.ascq_pending = ascq;
421 cd.fru_pending = 0x00;
422
423 SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
424 }
425
ValidateRawDataSector(uint8 * data,const uint32 lba)426 static bool ValidateRawDataSector(uint8 *data, const uint32 lba)
427 {
428 if(!edc_lec_check_and_correct(data, false))
429 {
430 MDFN_DispMessage("Uncorrectable data at sector %u", lba);
431
432 din.Flush();
433 cd.data_transfer_done = false;
434
435 CommandCCError(SENSEKEY_MEDIUM_ERROR, AP_LEC_UNCORRECTABLE_ERROR);
436 return(false);
437 }
438
439 return(true);
440 }
441
DoREADBase(uint32 sa,uint32 sc)442 static void DoREADBase(uint32 sa, uint32 sc)
443 {
444 if(sa > toc.tracks[100].lba) // Another one of those off-by-one PC-FX CD bugs.
445 {
446 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
447 return;
448 }
449
450 // Case for READ(10) and READ(12) where sc == 0, and sa == toc.tracks[100].lba
451 if(!sc && sa == toc.tracks[100].lba)
452 {
453 CommandCCError(SENSEKEY_MEDIUM_ERROR, NSE_HEADER_READ_ERROR);
454 return;
455 }
456
457 SectorAddr = sa;
458 SectorCount = sc;
459 if(SectorCount)
460 {
461 Cur_CDIF->HintReadSector(sa); //, sa + sc);
462
463 CDReadTimer = (uint64)3 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
464 }
465 else
466 {
467 CDReadTimer = 0;
468 SendStatusAndMessage(STATUS_GOOD, 0x00);
469 }
470 cdda.CDDAStatus = CDDASTATUS_STOPPED;
471 }
472
473 //
474 //
475 //
DoTESTUNITREADY(const uint8 * cdb)476 static void DoTESTUNITREADY(const uint8 *cdb)
477 {
478 SendStatusAndMessage(STATUS_GOOD, 0x00);
479 }
480
DoREQUESTSENSE(const uint8 * cdb)481 static void DoREQUESTSENSE(const uint8 *cdb)
482 {
483 uint8 data_in[18];
484
485 MakeSense(data_in, cd.key_pending, cd.asc_pending, cd.ascq_pending, cd.fru_pending);
486
487 DoSimpleDataIn(data_in, 18);
488
489 cd.key_pending = 0;
490 cd.asc_pending = 0;
491 cd.ascq_pending = 0;
492 cd.fru_pending = 0;
493 }
494
495 /********************************************************
496 * *
497 * SCSI Command 0x08 - READ(6) *
498 * *
499 ********************************************************/
DoREAD6(const uint8 * cdb)500 static void DoREAD6(const uint8 *cdb)
501 {
502 uint32 sa = ((cdb[1] & 0x1F) << 16) | (cdb[2] << 8) | (cdb[3] << 0);
503 uint32 sc = cdb[4];
504
505 // TODO: confirm real PCE does this(PC-FX does at least).
506 if(!sc)
507 {
508 /* READ(6) with count == 0 */
509 sc = 256;
510 }
511
512 DoREADBase(sa, sc);
513 }
514
515 /********************************************************
516 * *
517 * PC Engine CD Command 0xD8 - SAPSP *
518 * *
519 ********************************************************/
DoNEC_PCE_SAPSP(const uint8 * cdb)520 static void DoNEC_PCE_SAPSP(const uint8 *cdb)
521 {
522 uint32 new_read_sec_start;
523
524 //printf("Set audio start: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
525 switch (cdb[9] & 0xc0)
526 {
527 default:
528 /* Unknown SAPSP 9 */
529 case 0x00:
530 new_read_sec_start = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
531 break;
532
533 case 0x40:
534 new_read_sec_start = AMSF_to_LBA(BCD_to_U8(cdb[2]), BCD_to_U8(cdb[3]), BCD_to_U8(cdb[4]));
535 break;
536
537 case 0x80:
538 {
539 int track = BCD_to_U8(cdb[2]);
540
541 if(!track)
542 track = 1;
543 else if(track >= toc.last_track + 1)
544 track = 100;
545 new_read_sec_start = toc.tracks[track].lba;
546 }
547 break;
548 }
549
550 //printf("%lld\n", (long long)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock);
551 if(cdda.CDDAStatus == CDDASTATUS_PLAYING && new_read_sec_start == read_sec_start && ((int64)(monotonic_timestamp - pce_lastsapsp_timestamp) * 1000 / System_Clock) < 190)
552 {
553 pce_lastsapsp_timestamp = monotonic_timestamp;
554
555 SendStatusAndMessage(STATUS_GOOD, 0x00);
556 CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
557 return;
558 }
559
560 pce_lastsapsp_timestamp = monotonic_timestamp;
561
562 read_sec = read_sec_start = new_read_sec_start;
563 read_sec_end = toc.tracks[100].lba;
564
565
566 cdda.CDDAReadPos = 588;
567
568 cdda.CDDAStatus = CDDASTATUS_PAUSED;
569 cdda.PlayMode = PLAYMODE_SILENT;
570
571 if(cdb[1])
572 {
573 cdda.PlayMode = PLAYMODE_NORMAL;
574 cdda.CDDAStatus = CDDASTATUS_PLAYING;
575 }
576
577 if(read_sec < toc.tracks[100].lba)
578 Cur_CDIF->HintReadSector(read_sec);
579
580 SendStatusAndMessage(STATUS_GOOD, 0x00);
581 CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
582 }
583
584
585
586 /********************************************************
587 * *
588 * PC Engine CD Command 0xD9 - SAPEP *
589 * *
590 ********************************************************/
DoNEC_PCE_SAPEP(const uint8 * cdb)591 static void DoNEC_PCE_SAPEP(const uint8 *cdb)
592 {
593 uint32 new_read_sec_end;
594
595 //printf("Set audio end: %02x %02x %02x %02x %02x %02x %02x\n", cdb[9], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6]);
596
597 switch (cdb[9] & 0xc0)
598 {
599 default:
600 /* Unknown SAPEP 9 */
601 case 0x00:
602 new_read_sec_end = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
603 break;
604
605 case 0x40:
606 new_read_sec_end = BCD_to_U8(cdb[4]) + 75 * (BCD_to_U8(cdb[3]) + 60 * BCD_to_U8(cdb[2]));
607 new_read_sec_end -= 150;
608 break;
609
610 case 0x80:
611 {
612 int track = BCD_to_U8(cdb[2]);
613
614 if(!track)
615 track = 1;
616 else if(track >= toc.last_track + 1)
617 track = 100;
618 new_read_sec_end = toc.tracks[track].lba;
619 }
620 break;
621 }
622
623 read_sec_end = new_read_sec_end;
624
625 switch(cdb[1]) // PCE CD(TODO: Confirm these, and check the mode mask):
626 {
627 default:
628 case 0x03: cdda.PlayMode = PLAYMODE_NORMAL;
629 cdda.CDDAStatus = CDDASTATUS_PLAYING;
630 break;
631
632 case 0x02: cdda.PlayMode = PLAYMODE_INTERRUPT;
633 cdda.CDDAStatus = CDDASTATUS_PLAYING;
634 break;
635
636 case 0x01: cdda.PlayMode = PLAYMODE_LOOP;
637 cdda.CDDAStatus = CDDASTATUS_PLAYING;
638 break;
639
640 case 0x00: cdda.PlayMode = PLAYMODE_SILENT;
641 cdda.CDDAStatus = CDDASTATUS_STOPPED;
642 break;
643 }
644
645 SendStatusAndMessage(STATUS_GOOD, 0x00);
646 }
647
648
649
650 /********************************************************
651 * *
652 * PC Engine CD Command 0xDA - Pause *
653 * *
654 ********************************************************/
DoNEC_PCE_PAUSE(const uint8 * cdb)655 static void DoNEC_PCE_PAUSE(const uint8 *cdb)
656 {
657 if(cdda.CDDAStatus != CDDASTATUS_STOPPED) // Hmm, should we give an error if it tries to pause and it's already paused?
658 {
659 cdda.CDDAStatus = CDDASTATUS_PAUSED;
660 SendStatusAndMessage(STATUS_GOOD, 0x00);
661 }
662 else // Definitely give an error if it tries to pause when no track is playing!
663 {
664 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_AUDIO_NOT_PLAYING);
665 }
666 }
667
668
669
670 /********************************************************
671 * *
672 * PC Engine CD Command 0xDD - Read Subchannel Q *
673 * *
674 ********************************************************/
DoNEC_PCE_READSUBQ(const uint8 * cdb)675 static void DoNEC_PCE_READSUBQ(const uint8 *cdb)
676 {
677 uint8 *SubQBuf = cd.SubQBuf[QMode_Time];
678 uint8 data_in[8192];
679
680 memset(data_in, 0x00, 10);
681
682 data_in[2] = SubQBuf[1]; // Track
683 data_in[3] = SubQBuf[2]; // Index
684 data_in[4] = SubQBuf[3]; // M(rel)
685 data_in[5] = SubQBuf[4]; // S(rel)
686 data_in[6] = SubQBuf[5]; // F(rel)
687 data_in[7] = SubQBuf[7]; // M(abs)
688 data_in[8] = SubQBuf[8]; // S(abs)
689 data_in[9] = SubQBuf[9]; // F(abs)
690
691 if(cdda.CDDAStatus == CDDASTATUS_PAUSED)
692 data_in[0] = 2; // Pause
693 else if(cdda.CDDAStatus == CDDASTATUS_PLAYING)
694 data_in[0] = 0; // Playing
695 else
696 data_in[0] = 3; // Stopped
697
698 DoSimpleDataIn(data_in, 10);
699 }
700
701
702
703 /********************************************************
704 * *
705 * PC Engine CD Command 0xDE - Get Directory Info *
706 * *
707 ********************************************************/
DoNEC_PCE_GETDIRINFO(const uint8 * cdb)708 static void DoNEC_PCE_GETDIRINFO(const uint8 *cdb)
709 {
710 // Problems:
711 // Returned data lengths on real PCE are not confirmed.
712 // Mode 0x03 behavior not tested on real PCE
713
714 uint8 data_in[2048];
715 uint32 data_in_size = 0;
716
717 memset(data_in, 0, sizeof(data_in));
718
719 switch(cdb[1])
720 {
721 default:
722 MDFN_DispMessage("Unknown GETDIRINFO Mode: %02x", cdb[1]);
723 case 0x0:
724 data_in[0] = U8_to_BCD(toc.first_track);
725 data_in[1] = U8_to_BCD(toc.last_track);
726
727 data_in_size = 2;
728 break;
729
730 case 0x1:
731 {
732 uint8 m, s, f;
733
734 LBA_to_AMSF(toc.tracks[100].lba, &m, &s, &f);
735
736 data_in[0] = U8_to_BCD(m);
737 data_in[1] = U8_to_BCD(s);
738 data_in[2] = U8_to_BCD(f);
739
740 data_in_size = 3;
741 }
742 break;
743
744 case 0x2:
745 {
746 uint8 m, s, f;
747 int track = BCD_to_U8(cdb[2]);
748
749 if(!track)
750 track = 1;
751 else if(cdb[2] == 0xAA)
752 {
753 track = 100;
754 }
755 else if(track > 99)
756 {
757 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_PARAMETER);
758 return;
759 }
760
761 LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f);
762
763 data_in[0] = U8_to_BCD(m);
764 data_in[1] = U8_to_BCD(s);
765 data_in[2] = U8_to_BCD(f);
766 data_in[3] = toc.tracks[track].control;
767 data_in_size = 4;
768 }
769 break;
770 }
771
772 DoSimpleDataIn(data_in, data_in_size);
773 }
774
775 #define SCF_REQUIRES_MEDIUM 0x01
776
777 typedef struct
778 {
779 uint8 cmd;
780 uint8 flags;
781 void (*func)(const uint8 *cdb);
782 const char *pretty_name;
783 const char *format_string;
784 } SCSICH;
785
786 static const uint8 RequiredCDBLen[16] =
787 {
788 6, // 0x0n
789 6, // 0x1n
790 10, // 0x2n
791 10, // 0x3n
792 10, // 0x4n
793 10, // 0x5n
794 10, // 0x6n
795 10, // 0x7n
796 10, // 0x8n
797 10, // 0x9n
798 12, // 0xAn
799 12, // 0xBn
800 10, // 0xCn
801 10, // 0xDn
802 10, // 0xEn
803 10, // 0xFn
804 };
805
806 static SCSICH PCECommandDefs[] =
807 {
808 { 0x00, SCF_REQUIRES_MEDIUM, DoTESTUNITREADY, "Test Unit Ready" },
809 { 0x03, 0, DoREQUESTSENSE, "Request Sense" },
810 { 0x08, SCF_REQUIRES_MEDIUM, DoREAD6, "Read(6)" },
811
812 { 0xD8, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPSP, "Set Audio Playback Start Position" },
813 { 0xD9, SCF_REQUIRES_MEDIUM, DoNEC_PCE_SAPEP, "Set Audio Playback End Position" },
814 { 0xDA, SCF_REQUIRES_MEDIUM, DoNEC_PCE_PAUSE, "Pause" },
815 { 0xDD, SCF_REQUIRES_MEDIUM, DoNEC_PCE_READSUBQ, "Read Subchannel Q" },
816 { 0xDE, SCF_REQUIRES_MEDIUM, DoNEC_PCE_GETDIRINFO, "Get Dir Info" },
817
818 { 0xFF, 0, 0, NULL, NULL },
819 };
820
PCECD_Drive_ResetTS(void)821 void PCECD_Drive_ResetTS(void)
822 {
823 lastts = 0;
824 }
825
PCECD_Drive_GetCDDAValues(int16 * left,int16 * right)826 void PCECD_Drive_GetCDDAValues(int16 *left, int16 *right)
827 {
828 if(cdda.CDDAStatus)
829 {
830 *left = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2];
831 *right = cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 1];
832 }
833 else
834 *left = *right = 0;
835 }
836
RunCDDA(uint32 system_timestamp,int32 run_time)837 static INLINE void RunCDDA(uint32 system_timestamp, int32 run_time)
838 {
839 if(cdda.CDDAStatus == CDDASTATUS_PLAYING)
840 {
841 int32 sample[2];
842
843 cdda.CDDADiv -= run_time << 16;
844
845 while(cdda.CDDADiv <= 0)
846 {
847 const uint32 synthtime = ((system_timestamp + (cdda.CDDADiv >> 16))) / cdda.CDDATimeDiv;
848
849 cdda.CDDADiv += cdda.CDDADivAcc;
850
851 //MDFN_DispMessage("%d %d %d\n", read_sec_start, read_sec, read_sec_end);
852
853 if(cdda.CDDAReadPos == 588)
854 {
855 if(read_sec >= read_sec_end)
856 {
857 switch(cdda.PlayMode)
858 {
859 case PLAYMODE_SILENT:
860 case PLAYMODE_NORMAL:
861 cdda.CDDAStatus = CDDASTATUS_STOPPED;
862 break;
863
864 case PLAYMODE_INTERRUPT:
865 cdda.CDDAStatus = CDDASTATUS_STOPPED;
866 CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
867 break;
868
869 case PLAYMODE_LOOP:
870 read_sec = read_sec_start;
871 break;
872 }
873
874 // If CDDA playback is stopped, break out of our while(CDDADiv ...) loop and don't play any more sound!
875 if(cdda.CDDAStatus == CDDASTATUS_STOPPED)
876 break;
877 }
878
879 // Don't play past the user area of the disc.
880 if(read_sec >= toc.tracks[100].lba)
881 {
882 cdda.CDDAStatus = CDDASTATUS_STOPPED;
883 break;
884 }
885
886 if(cd.TrayOpen)
887 {
888 cdda.CDDAStatus = CDDASTATUS_STOPPED;
889
890 #if 0
891 cd.data_transfer_done = FALSE;
892 cd.key_pending = SENSEKEY_NOT_READY;
893 cd.asc_pending = ASC_MEDIUM_NOT_PRESENT;
894 cd.ascq_pending = 0x00;
895 cd.fru_pending = 0x00;
896 SendStatusAndMessage(STATUS_CHECK_CONDITION, 0x00);
897 #endif
898
899 break;
900 }
901
902
903 cdda.CDDAReadPos = 0;
904
905 {
906 uint8 tmpbuf[2352 + 96];
907
908 Cur_CDIF->ReadRawSector(tmpbuf, read_sec); //, read_sec_end, read_sec_start);
909
910 for(int i = 0; i < 588 * 2; i++)
911 cdda.CDDASectorBuffer[i] = MDFN_de16lsb(&tmpbuf[i * 2]);
912
913 memcpy(cd.SubPWBuf, tmpbuf + 2352, 96);
914 }
915 GenSubQFromSubPW();
916 read_sec++;
917 } // End if(CDDAReadPos == 588)
918
919 // If the last valid sub-Q data decoded indicate that the corresponding sector is a data sector, don't output the
920 // current sector as audio.
921 sample[0] = sample[1] = 0;
922
923 if(!(cd.SubQBuf_Last[0] & 0x40) && cdda.PlayMode != PLAYMODE_SILENT)
924 {
925 sample[0] = (cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 0] * cdda.CDDAVolume) >> 16;
926 sample[1] = (cdda.CDDASectorBuffer[cdda.CDDAReadPos * 2 + 1] * cdda.CDDAVolume) >> 16;
927 }
928
929 if(!(cdda.CDDAReadPos % 6))
930 {
931 int subindex = cdda.CDDAReadPos / 6 - 2;
932
933 if(subindex >= 0)
934 CDStuffSubchannels(cd.SubPWBuf[subindex], subindex);
935 else // The system-specific emulation code should handle what value the sync bytes are.
936 CDStuffSubchannels(0x00, subindex);
937 }
938
939 if(sbuf[0] && sbuf[1])
940 {
941 Blip_Synth_offset(&cdda.CDDASynth, synthtime, sample[0] - cdda.last_sample[0], sbuf[0]);
942 Blip_Synth_offset(&cdda.CDDASynth, synthtime, sample[1] - cdda.last_sample[1], sbuf[1]);
943 }
944
945 cdda.last_sample[0] = sample[0];
946 cdda.last_sample[1] = sample[1];
947
948 cdda.CDDAReadPos++;
949 }
950 }
951 }
952
RunCDRead(uint32 system_timestamp,int32 run_time)953 static INLINE void RunCDRead(uint32 system_timestamp, int32 run_time)
954 {
955 if(CDReadTimer > 0)
956 {
957 CDReadTimer -= run_time;
958
959 if(CDReadTimer <= 0)
960 {
961 if(din.CanWrite() < 2048)
962 {
963 //printf("Carp: %d %d %d\n", din.CanWrite(), SectorCount, CDReadTimer);
964 //CDReadTimer = (cd.data_in_size - cd.data_in_pos) * 10;
965
966 CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
967
968 //CDReadTimer += (uint64) 1 * 128 * System_Clock / CD_DATA_TRANSFER_RATE;
969 }
970 else
971 {
972 uint8 tmp_read_buf[2352 + 96];
973
974 if(cd.TrayOpen)
975 {
976 din.Flush();
977 cd.data_transfer_done = FALSE;
978
979 CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
980 }
981 else if(SectorAddr >= toc.tracks[100].lba)
982 {
983 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_END_OF_VOLUME);
984 }
985 else if(!Cur_CDIF->ReadRawSector(tmp_read_buf, SectorAddr)) //, SectorAddr + SectorCount))
986 {
987 cd.data_transfer_done = FALSE;
988
989 CommandCCError(SENSEKEY_ILLEGAL_REQUEST);
990 }
991 else if(ValidateRawDataSector(tmp_read_buf, SectorAddr))
992 {
993 memcpy(cd.SubPWBuf, tmp_read_buf + 2352, 96);
994
995 if(tmp_read_buf[12 + 3] == 0x2)
996 din.Write(tmp_read_buf + 24, 2048);
997 else
998 din.Write(tmp_read_buf + 16, 2048);
999
1000 GenSubQFromSubPW();
1001
1002 CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_READY);
1003
1004 SectorAddr++;
1005 SectorCount--;
1006
1007 if(CurrentPhase != PHASE_DATA_IN)
1008 ChangePhase(PHASE_DATA_IN);
1009
1010 if(SectorCount)
1011 {
1012 cd.data_transfer_done = FALSE;
1013 CDReadTimer += (uint64) 1 * 2048 * System_Clock / CD_DATA_TRANSFER_RATE;
1014 }
1015 else
1016 {
1017 cd.data_transfer_done = TRUE;
1018 }
1019 }
1020 } // end else to if(!Cur_CDIF->ReadSector
1021
1022 }
1023 }
1024 }
1025
PCECD_Drive_Run(pcecd_drive_timestamp_t system_timestamp)1026 uint32 PCECD_Drive_Run(pcecd_drive_timestamp_t system_timestamp)
1027 {
1028 int32 run_time = system_timestamp - lastts;
1029
1030 monotonic_timestamp += run_time;
1031
1032 lastts = system_timestamp;
1033
1034 RunCDRead(system_timestamp, run_time);
1035 RunCDDA(system_timestamp, run_time);
1036
1037 bool ResetNeeded = false;
1038
1039 if(RST_signal && !cd.last_RST_signal)
1040 ResetNeeded = true;
1041
1042 cd.last_RST_signal = RST_signal;
1043
1044 if(ResetNeeded)
1045 {
1046 //puts("RST");
1047 VirtualReset();
1048 }
1049 else switch(CurrentPhase)
1050 {
1051 case PHASE_BUS_FREE:
1052 if(SEL_signal)
1053 {
1054 ChangePhase(PHASE_COMMAND);
1055 }
1056 break;
1057
1058 case PHASE_COMMAND:
1059 if(REQ_signal && ACK_signal) // Data bus is valid nowww
1060 {
1061 //printf("Command Phase Byte I->T: %02x, %d\n", cd_bus.DB, cd.command_buffer_pos);
1062 cd.command_buffer[cd.command_buffer_pos++] = cd_bus.DB;
1063 SetREQ(FALSE);
1064 }
1065
1066 if(!REQ_signal && !ACK_signal && cd.command_buffer_pos) // Received at least one byte, what should we do?
1067 {
1068 if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
1069 {
1070 const SCSICH* cmd_info_ptr = PCECommandDefs;
1071
1072 while(cmd_info_ptr->pretty_name && cmd_info_ptr->cmd != cd.command_buffer[0])
1073 cmd_info_ptr++;
1074
1075 if(cmd_info_ptr->pretty_name == NULL) // Command not found!
1076 {
1077 CommandCCError(SENSEKEY_ILLEGAL_REQUEST, NSE_INVALID_COMMAND);
1078
1079 /* Bad Command */
1080
1081 cd.command_buffer_pos = 0;
1082 }
1083 else
1084 {
1085 if(cd.TrayOpen && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
1086 {
1087 CommandCCError(SENSEKEY_NOT_READY, NSE_TRAY_OPEN);
1088 }
1089 else if(!Cur_CDIF && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
1090 {
1091 CommandCCError(SENSEKEY_NOT_READY, NSE_NO_DISC);
1092 }
1093 else if(cd.DiscChanged && (cmd_info_ptr->flags & SCF_REQUIRES_MEDIUM))
1094 {
1095 CommandCCError(SENSEKEY_UNIT_ATTENTION, NSE_DISC_CHANGED);
1096 cd.DiscChanged = false;
1097 }
1098 else
1099 {
1100 cmd_info_ptr->func(cd.command_buffer);
1101 }
1102
1103 cd.command_buffer_pos = 0;
1104 }
1105 } // end if(cd.command_buffer_pos == RequiredCDBLen[cd.command_buffer[0] >> 4])
1106 else // Otherwise, get more data for the command!
1107 SetREQ(TRUE);
1108 }
1109 break;
1110
1111 case PHASE_STATUS:
1112 if(REQ_signal && ACK_signal)
1113 {
1114 SetREQ(FALSE);
1115 cd.status_sent = TRUE;
1116 }
1117
1118 if(!REQ_signal && !ACK_signal && cd.status_sent)
1119 {
1120 // Status sent, so get ready to send the message!
1121 cd.status_sent = FALSE;
1122 cd_bus.DB = cd.message_pending;
1123
1124 ChangePhase(PHASE_MESSAGE_IN);
1125 }
1126 break;
1127
1128 case PHASE_DATA_IN:
1129 if(!REQ_signal && !ACK_signal)
1130 {
1131 //puts("REQ and ACK false");
1132 if(din.in_count == 0) // aaand we're done!
1133 {
1134 CDIRQCallback(0x8000 | PCECD_Drive_IRQ_DATA_TRANSFER_READY);
1135
1136 if(cd.data_transfer_done)
1137 {
1138 SendStatusAndMessage(STATUS_GOOD, 0x00);
1139 cd.data_transfer_done = FALSE;
1140 CDIRQCallback(PCECD_Drive_IRQ_DATA_TRANSFER_DONE);
1141 }
1142 }
1143 else
1144 {
1145 cd_bus.DB = din.ReadUnit();
1146 SetREQ(TRUE);
1147 }
1148 }
1149 if(REQ_signal && ACK_signal)
1150 {
1151 //puts("REQ and ACK true");
1152 SetREQ(FALSE);
1153 }
1154 break;
1155
1156 case PHASE_MESSAGE_IN:
1157 if(REQ_signal && ACK_signal)
1158 {
1159 SetREQ(FALSE);
1160 cd.message_sent = TRUE;
1161 }
1162
1163 if(!REQ_signal && !ACK_signal && cd.message_sent)
1164 {
1165 cd.message_sent = FALSE;
1166 ChangePhase(PHASE_BUS_FREE);
1167 }
1168 break;
1169 }
1170
1171 int32 next_time = 0x7fffffff;
1172
1173 if(CDReadTimer > 0 && CDReadTimer < next_time)
1174 next_time = CDReadTimer;
1175
1176 if(cdda.CDDAStatus == CDDASTATUS_PLAYING)
1177 {
1178 int32 cdda_div_sexytime = (cdda.CDDADiv + 0xFFFF) >> 16;
1179 if(cdda_div_sexytime > 0 && cdda_div_sexytime < next_time)
1180 next_time = cdda_div_sexytime;
1181 }
1182
1183 assert(next_time >= 0);
1184
1185 return(next_time);
1186 }
1187
PCECD_Drive_SetLog(void (* logfunc)(const char *,const char *,...))1188 void PCECD_Drive_SetLog(void (*logfunc)(const char *, const char *, ...))
1189 {
1190 }
1191
PCECD_Drive_SetTransferRate(uint32 TransferRate)1192 void PCECD_Drive_SetTransferRate(uint32 TransferRate)
1193 {
1194 CD_DATA_TRANSFER_RATE = TransferRate;
1195 }
1196
PCECD_Drive_Close(void)1197 void PCECD_Drive_Close(void)
1198 {
1199
1200 }
1201
PCECD_Drive_Init(int cdda_time_div,Blip_Buffer * leftbuf,Blip_Buffer * rightbuf,uint32 TransferRate,uint32 SystemClock,void (* IRQFunc)(int),void (* SSCFunc)(uint8,int))1202 void PCECD_Drive_Init(int cdda_time_div, Blip_Buffer *leftbuf, Blip_Buffer *rightbuf, uint32 TransferRate, uint32 SystemClock, void (*IRQFunc)(int), void (*SSCFunc)(uint8, int))
1203 {
1204 Cur_CDIF = NULL;
1205 cd.TrayOpen = false;
1206
1207 monotonic_timestamp = 0;
1208 lastts = 0;
1209
1210 //din = new SimpleFIFO(2048);
1211
1212 cdda.CDDATimeDiv = cdda_time_div;
1213
1214 cdda.CDDAVolume = 65536;
1215 Blip_Synth_set_volume(&cdda.CDDASynth, 1.0f / 65536, 1);
1216 sbuf[0] = leftbuf;
1217 sbuf[1] = rightbuf;
1218
1219 CD_DATA_TRANSFER_RATE = TransferRate;
1220 System_Clock = SystemClock;
1221 CDIRQCallback = IRQFunc;
1222 CDStuffSubchannels = SSCFunc;
1223 }
1224
PCECD_Drive_SetCDDAVolume(unsigned vol)1225 void PCECD_Drive_SetCDDAVolume(unsigned vol)
1226 {
1227 cdda.CDDAVolume = vol;
1228 }
1229
PCECD_Drive_StateAction(StateMem * sm,int load,int data_only,const char * sname)1230 int PCECD_Drive_StateAction(StateMem * sm, int load, int data_only, const char *sname)
1231 {
1232 SFORMAT StateRegs[] =
1233 {
1234 SFVARN(cd_bus.DB, "DB"),
1235 SFVARN(cd_bus.signals, "Signals"),
1236 SFVARN(CurrentPhase, "CurrentPhase"),
1237
1238 SFVAR_BOOL(cd.last_RST_signal),
1239 SFVARN(cd.message_pending, "message_pending"),
1240 SFVAR_BOOL(cd.status_sent),
1241 SFVAR_BOOL(cd.message_sent),
1242 SFVARN(cd.key_pending, "key_pending"),
1243 SFVARN(cd.asc_pending, "asc_pending"),
1244 SFVARN(cd.ascq_pending, "ascq_pending"),
1245 SFVARN(cd.fru_pending, "fru_pending"),
1246
1247 SFARRAYN(cd.command_buffer, 256, "command_buffer"),
1248 SFVARN(cd.command_buffer_pos, "command_buffer_pos"),
1249 SFVARN(cd.command_size_left, "command_size_left"),
1250
1251 // Don't save the FIFO's write position, it will be reconstructed from read_pos and in_count
1252 SFARRAYN(&din.data[0], din.size, "din_fifo"),
1253 SFVARN(din.read_pos, "din_read_pos"),
1254 SFVARN(din.in_count, "din_in_count"),
1255 SFVAR_BOOL(cd.data_transfer_done),
1256
1257 SFVAR_BOOL(cd.TrayOpen),
1258 SFVAR_BOOL(cd.DiscChanged),
1259
1260 SFVAR(cdda.PlayMode),
1261 SFARRAY16(cdda.CDDASectorBuffer, 1176),
1262 SFVAR(cdda.CDDAReadPos),
1263 SFVAR(cdda.CDDAStatus),
1264 SFVAR(cdda.CDDADiv),
1265 SFVAR(read_sec_start),
1266 SFVAR(read_sec),
1267 SFVAR(read_sec_end),
1268
1269 SFVAR(CDReadTimer),
1270 SFVAR(SectorAddr),
1271 SFVAR(SectorCount),
1272
1273 SFVAR(cdda.ScanMode),
1274 SFVAR(cdda.scan_sec_end),
1275
1276 SFARRAYN(&cd.SubQBuf[0][0], sizeof(cd.SubQBuf), "SubQBufs"),
1277 SFARRAYN(cd.SubQBuf_Last, sizeof(cd.SubQBuf_Last), "SubQBufLast"),
1278 SFARRAYN(cd.SubPWBuf, sizeof(cd.SubPWBuf), "SubPWBuf"),
1279
1280 SFVARN(monotonic_timestamp, "monotonic_timestamp"),
1281 SFVARN(pce_lastsapsp_timestamp, "pce_lastsapsp_timestamp"),
1282
1283 SFEND
1284 };
1285
1286 int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, sname, false);
1287
1288 if(load)
1289 {
1290 din.in_count &= din.size - 1;
1291 din.read_pos &= din.size - 1;
1292 din.write_pos = (din.read_pos + din.in_count) & (din.size - 1);
1293
1294 if(cdda.CDDADiv <= 0)
1295 cdda.CDDADiv = 1;
1296 //printf("%d %d %d\n", din.in_count, din.read_pos, din.write_pos);
1297 }
1298
1299 return (ret);
1300 }
1301