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