rework input code
[pcsx_rearmed.git] / libpcsxcore / cdrom.c
... / ...
CommitLineData
1/***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
18 ***************************************************************************/
19
20/*
21* Handles all CD-ROM registers and functions.
22*/
23
24#include <stdalign.h>
25#include <assert.h>
26#include "cdrom.h"
27#include "cdrom-async.h"
28#include "misc.h"
29#include "ppf.h"
30#include "psxdma.h"
31#include "psxevents.h"
32#include "arm_features.h"
33
34/* logging */
35#if 0
36#define CDR_LOG SysPrintf
37#else
38#define CDR_LOG(...)
39#endif
40#if 0
41#define CDR_LOG_I SysPrintf
42#else
43#define CDR_LOG_I(fmt, ...) \
44 log_unhandled("%u cdrom: " fmt, psxRegs.cycle, ##__VA_ARGS__)
45#endif
46#if 0
47#define CDR_LOG_IO SysPrintf
48#else
49#define CDR_LOG_IO(...)
50#endif
51//#define CDR_LOG_CMD
52//#define CDR_LOG_CMD_ACK
53
54static struct {
55 // unused members maintain savesate compatibility
56 unsigned char unused0;
57 unsigned char unused1;
58 unsigned char IrqMask;
59 unsigned char unused2;
60 unsigned char Ctrl;
61 unsigned char IrqStat;
62
63 unsigned char StatP;
64
65 unsigned char Transfer[DATA_SIZE];
66 struct {
67 unsigned char Track;
68 unsigned char Index;
69 unsigned char Relative[3];
70 unsigned char Absolute[3];
71 } subq;
72 unsigned char TrackChanged;
73 unsigned char ReportDelay;
74 unsigned char PhysCdPropagations;
75 unsigned short sectorsRead;
76 unsigned int freeze_ver;
77
78 unsigned char Prev[4];
79 unsigned char Param[8];
80 unsigned char Result[16];
81
82 unsigned char ParamC;
83 unsigned char ParamP;
84 unsigned char ResultC;
85 unsigned char ResultP;
86 unsigned char ResultReady;
87 unsigned char Cmd;
88 unsigned char SubqForwardSectors;
89 unsigned char SetlocPending;
90 u32 Reading;
91
92 unsigned char ResultTN[6];
93 unsigned char ResultTD[4];
94 unsigned char SetSectorPlay[4];
95 unsigned char SetSectorEnd[4];
96 unsigned char SetSector[4];
97 unsigned char Track;
98 boolean Play, Muted;
99 int CurTrack;
100 unsigned char Mode;
101 unsigned char FileChannelSelected;
102 unsigned char CurFile, CurChannel;
103 int FilterFile, FilterChannel;
104 unsigned char LocL[8];
105 int unused4;
106
107 xa_decode_t Xa;
108
109 u16 FifoOffset;
110 u16 FifoSize;
111
112 u16 CmdInProgress;
113 u8 Irq1Pending;
114 u8 AdpcmActive;
115 u32 LastReadSeekCycles;
116
117 u8 RetryDetected;
118
119 u8 DriveState; // enum drive_state
120 u8 FastForward;
121 u8 FastBackward;
122 u8 errorRetryhack;
123
124 u8 AttenuatorLeftToLeft, AttenuatorLeftToRight;
125 u8 AttenuatorRightToRight, AttenuatorRightToLeft;
126 u8 AttenuatorLeftToLeftT, AttenuatorLeftToRightT;
127 u8 AttenuatorRightToRightT, AttenuatorRightToLeftT;
128} cdr;
129alignas(64) static s16 read_buf[CD_FRAMESIZE_RAW_ALIGNED / 2];
130
131struct SubQ {
132 char res0[12];
133 unsigned char ControlAndADR;
134 unsigned char TrackNumber;
135 unsigned char IndexNumber;
136 unsigned char TrackRelativeAddress[3];
137 unsigned char Filler;
138 unsigned char AbsoluteAddress[3];
139 unsigned char CRC[2];
140 char res1[72];
141};
142
143/* CD-ROM magic numbers */
144#define CdlSync 0 /* nocash documentation : "Uh, actually, returns error code 40h = Invalid Command...?" */
145#define CdlNop 1
146#define CdlSetloc 2
147#define CdlPlay 3
148#define CdlForward 4
149#define CdlBackward 5
150#define CdlReadN 6
151#define CdlStandby 7
152#define CdlStop 8
153#define CdlPause 9
154#define CdlReset 10
155#define CdlMute 11
156#define CdlDemute 12
157#define CdlSetfilter 13
158#define CdlSetmode 14
159#define CdlGetparam 15
160#define CdlGetlocL 16
161#define CdlGetlocP 17
162#define CdlReadT 18
163#define CdlGetTN 19
164#define CdlGetTD 20
165#define CdlSeekL 21
166#define CdlSeekP 22
167#define CdlSetclock 23
168#define CdlGetclock 24
169#define CdlTest 25
170#define CdlID 26
171#define CdlReadS 27
172#define CdlInit 28
173#define CdlGetQ 29
174#define CdlReadToc 30
175
176#ifdef CDR_LOG_CMD
177static const char * const CmdName[0x100] = {
178 "CdlSync", "CdlNop", "CdlSetloc", "CdlPlay",
179 "CdlForward", "CdlBackward", "CdlReadN", "CdlStandby",
180 "CdlStop", "CdlPause", "CdlReset", "CdlMute",
181 "CdlDemute", "CdlSetfilter", "CdlSetmode", "CdlGetparam",
182 "CdlGetlocL", "CdlGetlocP", "CdlReadT", "CdlGetTN",
183 "CdlGetTD", "CdlSeekL", "CdlSeekP", "CdlSetclock",
184 "CdlGetclock", "CdlTest", "CdlID", "CdlReadS",
185 "CdlInit", NULL, "CDlReadToc", NULL
186};
187#endif
188
189unsigned char Test04[] = { 0 };
190unsigned char Test05[] = { 0 };
191unsigned char Test20[] = { 0x98, 0x06, 0x10, 0xC3 };
192unsigned char Test22[] = { 0x66, 0x6F, 0x72, 0x20, 0x45, 0x75, 0x72, 0x6F };
193unsigned char Test23[] = { 0x43, 0x58, 0x44, 0x32, 0x39 ,0x34, 0x30, 0x51 };
194
195// cdr.IrqStat:
196#define NoIntr 0
197#define DataReady 1
198#define Complete 2
199#define Acknowledge 3
200#define DataEnd 4
201#define DiskError 5
202
203/* Modes flags */
204#define MODE_SPEED (1<<7) // 0x80
205#define MODE_STRSND (1<<6) // 0x40 ADPCM on/off
206#define MODE_SIZE_2340 (1<<5) // 0x20
207#define MODE_SIZE_2328 (1<<4) // 0x10 (likely wrong)
208#define MODE_SIZE_2048 (0<<4) // 0x00
209#define MODE_BIT4 (1<<4) // 0x10
210#define MODE_SF (1<<3) // 0x08 channel on/off
211#define MODE_REPORT (1<<2) // 0x04
212#define MODE_AUTOPAUSE (1<<1) // 0x02
213#define MODE_CDDA (1<<0) // 0x01
214
215/* Status flags */
216#define STATUS_PLAY (1<<7) // 0x80
217#define STATUS_SEEK (1<<6) // 0x40
218#define STATUS_READ (1<<5) // 0x20
219#define STATUS_SHELLOPEN (1<<4) // 0x10
220#define STATUS_UNKNOWN3 (1<<3) // 0x08
221#define STATUS_SEEKERROR (1<<2) // 0x04
222#define STATUS_ROTATING (1<<1) // 0x02
223#define STATUS_ERROR (1<<0) // 0x01
224
225/* Errors */
226#define ERROR_NOTREADY (1<<7) // 0x80
227#define ERROR_INVALIDCMD (1<<6) // 0x40
228#define ERROR_BAD_ARGNUM (1<<5) // 0x20
229#define ERROR_BAD_ARGVAL (1<<4) // 0x10
230#define ERROR_SHELLOPEN (1<<3) // 0x08
231
232// 1x = 75 sectors per second
233// PSXCLK = 1 sec in the ps
234// so (PSXCLK / 75) = cdr read time (linuzappz)
235#define cdReadTime (PSXCLK / 75)
236
237#define LOCL_INVALID 0xff
238#define SUBQ_FORWARD_SECTORS 2u
239
240enum drive_state {
241 DRIVESTATE_STANDBY = 0, // different from paused
242 DRIVESTATE_LID_OPEN,
243 DRIVESTATE_RESCAN_CD,
244 DRIVESTATE_PREPARE_CD,
245 DRIVESTATE_STOPPED,
246 DRIVESTATE_PAUSED,
247 DRIVESTATE_PLAY_READ,
248 DRIVESTATE_SEEK,
249};
250
251static struct CdrStat cdr_stat;
252
253static unsigned int msf2sec(const u8 *msf) {
254 return ((msf[0] * 60 + msf[1]) * 75) + msf[2];
255}
256
257// cdrPlayReadInterrupt
258#define CDRPLAYREAD_INT(eCycle, isFirst) { \
259 u32 e_ = eCycle; \
260 psxRegs.interrupt |= (1 << PSXINT_CDREAD); \
261 if (isFirst) \
262 psxRegs.intCycle[PSXINT_CDREAD].sCycle = psxRegs.cycle; \
263 else \
264 psxRegs.intCycle[PSXINT_CDREAD].sCycle += psxRegs.intCycle[PSXINT_CDREAD].cycle; \
265 psxRegs.intCycle[PSXINT_CDREAD].cycle = e_; \
266 set_event_raw_abs(PSXINT_CDREAD, psxRegs.intCycle[PSXINT_CDREAD].sCycle + e_); \
267}
268
269#define StopReading() { \
270 cdr.Reading = 0; \
271 psxRegs.interrupt &= ~(1 << PSXINT_CDREAD); \
272}
273
274#define StopCdda() { \
275 cdr.Play = FALSE; \
276 cdr.FastForward = 0; \
277 cdr.FastBackward = 0; \
278}
279
280#define SetPlaySeekRead(x, f) { \
281 x &= ~(STATUS_PLAY | STATUS_SEEK | STATUS_READ); \
282 x |= f; \
283}
284
285#define SetResultSize_(size) { \
286 cdr.ResultP = 0; \
287 cdr.ResultC = size; \
288 cdr.ResultReady = 1; \
289}
290
291#define SetResultSize(size) { \
292 if (cdr.ResultP < cdr.ResultC) \
293 CDR_LOG_I("overwriting result, len=%u\n", cdr.ResultC); \
294 SetResultSize_(size); \
295}
296
297static void setIrq(u8 irq, int log_cmd)
298{
299 u8 old = cdr.IrqStat & cdr.IrqMask ? 1 : 0;
300 u8 new_ = irq & cdr.IrqMask ? 1 : 0;
301
302 cdr.IrqStat = irq;
303 if ((old ^ new_) & new_)
304 psxHu32ref(0x1070) |= SWAP32((u32)0x4);
305
306#ifdef CDR_LOG_CMD
307 if (cdr.IrqStat)
308 {
309 int i;
310 CDR_LOG_I("CDR IRQ=%d cmd %02x irqstat %02x: ",
311 !!(cdr.IrqStat & cdr.IrqMask), log_cmd, cdr.IrqStat);
312 for (i = 0; i < cdr.ResultC; i++)
313 SysPrintf("%02x ", cdr.Result[i]);
314 SysPrintf("\n");
315 }
316#endif
317}
318
319// timing used in this function was taken from tests on real hardware
320// (yes it's slow, but you probably don't want to modify it)
321void cdrLidSeekInterrupt(void)
322{
323 CDR_LOG_I("%s cdr.DriveState=%d\n", __func__, cdr.DriveState);
324
325 switch (cdr.DriveState) {
326 default:
327 case DRIVESTATE_STANDBY:
328 StopCdda();
329 //StopReading();
330 SetPlaySeekRead(cdr.StatP, 0);
331
332 if (cdra_getStatus(&cdr_stat) == -1)
333 return;
334
335 if (cdr_stat.Status & STATUS_SHELLOPEN)
336 {
337 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
338 cdr.DriveState = DRIVESTATE_LID_OPEN;
339 set_event(PSXINT_CDRLID, 0x800);
340 }
341 break;
342
343 case DRIVESTATE_LID_OPEN:
344 if (cdra_getStatus(&cdr_stat) != 0)
345 cdr_stat.Status &= ~STATUS_SHELLOPEN;
346
347 // 02, 12, 10
348 if (!(cdr.StatP & STATUS_SHELLOPEN)) {
349 int was_reading = cdr.Reading;
350 StopReading();
351 SetPlaySeekRead(cdr.StatP, 0);
352 cdr.StatP |= STATUS_SHELLOPEN;
353 memset(cdr.Prev, 0xff, sizeof(cdr.Prev));
354
355 // IIRC this sometimes doesn't happen on real hw
356 // (when lots of commands are sent?)
357 SetResultSize(2);
358 cdr.Result[0] = cdr.StatP | STATUS_SEEKERROR;
359 cdr.Result[1] = ERROR_SHELLOPEN;
360 if (cdr.CmdInProgress || was_reading) {
361 psxRegs.interrupt &= ~(1 << PSXINT_CDR);
362 cdr.CmdInProgress = 0;
363 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
364 cdr.Result[1] = ERROR_NOTREADY;
365 }
366 setIrq(DiskError, 0x1006);
367
368 set_event(PSXINT_CDRLID, cdReadTime * 30);
369 break;
370 }
371 else if (cdr.StatP & STATUS_ROTATING) {
372 cdr.StatP &= ~STATUS_ROTATING;
373 }
374 else if (!(cdr_stat.Status & STATUS_SHELLOPEN)) {
375 // closed now
376 CheckCdrom();
377
378 // cdr.StatP STATUS_SHELLOPEN is "sticky"
379 // and is only cleared by CdlNop
380
381 cdr.DriveState = DRIVESTATE_RESCAN_CD;
382 set_event(PSXINT_CDRLID, cdReadTime * 105);
383 break;
384 }
385
386 // recheck for close
387 set_event(PSXINT_CDRLID, cdReadTime * 3);
388 break;
389
390 case DRIVESTATE_RESCAN_CD:
391 cdr.StatP |= STATUS_ROTATING;
392 cdr.DriveState = DRIVESTATE_PREPARE_CD;
393
394 // this is very long on real hardware, over 6 seconds
395 // make it a bit faster here...
396 set_event(PSXINT_CDRLID, cdReadTime * 150);
397 break;
398
399 case DRIVESTATE_PREPARE_CD:
400 if (cdr.StatP & STATUS_SEEK) {
401 SetPlaySeekRead(cdr.StatP, 0);
402 cdr.DriveState = DRIVESTATE_STANDBY;
403 }
404 else {
405 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
406 set_event(PSXINT_CDRLID, cdReadTime * 26);
407 }
408 break;
409 }
410}
411
412static void Find_CurTrack(const u8 *time)
413{
414 int current, sect;
415
416 current = msf2sec(time);
417
418 for (cdr.CurTrack = 1; cdr.CurTrack < cdr.ResultTN[1]; cdr.CurTrack++) {
419 cdra_getTD(cdr.CurTrack + 1, cdr.ResultTD);
420 sect = msf2sec(cdr.ResultTD);
421 if (sect - current >= 150)
422 break;
423 }
424}
425
426static void generate_subq(const u8 *time)
427{
428 unsigned char start[3], next[3];
429 int this_s, start_s, next_s, pregap;
430 int relative_s;
431
432 if (cdr.CurTrack <= cdr.ResultTN[1])
433 cdra_getTD(cdr.CurTrack, start);
434 else
435 memcpy(start, cdr.SetSectorEnd, 3);
436 if (cdr.CurTrack + 1 <= cdr.ResultTN[1]) {
437 pregap = 150;
438 cdra_getTD(cdr.CurTrack + 1, next);
439 }
440 else {
441 // last track - cd size
442 pregap = 0;
443 memcpy(next, cdr.SetSectorEnd, 3);
444 }
445
446 this_s = msf2sec(time);
447 start_s = msf2sec(start);
448 next_s = msf2sec(next);
449
450 cdr.TrackChanged = FALSE;
451
452 if (next_s - this_s < pregap && cdr.CurTrack <= cdr.ResultTN[1]) {
453 cdr.TrackChanged = TRUE;
454 cdr.CurTrack++;
455 start_s = next_s;
456 }
457
458 cdr.subq.Index = 1;
459
460 relative_s = this_s - start_s;
461 if (relative_s < 0) {
462 cdr.subq.Index = 0;
463 relative_s = -relative_s;
464 }
465 lba2msf(relative_s, &cdr.subq.Relative[0],
466 &cdr.subq.Relative[1], &cdr.subq.Relative[2]);
467
468 cdr.subq.Track = itob(cdr.CurTrack);
469 if (cdr.CurTrack > cdr.ResultTN[1]) // lead-out
470 cdr.subq.Track = 0xaa;
471 cdr.subq.Relative[0] = itob(cdr.subq.Relative[0]);
472 cdr.subq.Relative[1] = itob(cdr.subq.Relative[1]);
473 cdr.subq.Relative[2] = itob(cdr.subq.Relative[2]);
474 cdr.subq.Absolute[0] = itob(time[0]);
475 cdr.subq.Absolute[1] = itob(time[1]);
476 cdr.subq.Absolute[2] = itob(time[2]);
477}
478
479static int ReadTrack(const u8 *time)
480{
481 int ret;
482
483 CDR_LOG("ReadTrack *** %02d:%02d:%02d\n", time[0], time[1], time[2]);
484
485 if (memcmp(cdr.Prev, time, 3) == 0)
486 return 1;
487
488 ret = cdra_readTrack(time);
489 if (ret == 0)
490 memcpy(cdr.Prev, time, 3);
491 else
492 log_unhandled("ReadTrack %02d:%02d:%02d ret %d\n",
493 time[0], time[1], time[2], ret);
494 return ret == 0;
495}
496
497static void UpdateSubq(const u8 *time)
498{
499 int ret = -1, s = MSF2SECT(time[0], time[1], time[2]);
500 struct SubQ subq;
501 u16 crc;
502
503 if (CheckSBI(s))
504 return;
505
506 if (cdr.CurTrack == 1)
507 ret = cdra_readSub(time, &subq);
508 if (ret == 0) {
509 crc = calcCrc((u8 *)&subq + 12, 10);
510 if (crc == (((u16)subq.CRC[0] << 8) | subq.CRC[1])) {
511 cdr.subq.Track = subq.TrackNumber;
512 cdr.subq.Index = subq.IndexNumber;
513 memcpy(cdr.subq.Relative, subq.TrackRelativeAddress, 3);
514 memcpy(cdr.subq.Absolute, subq.AbsoluteAddress, 3);
515 }
516 else {
517 CDR_LOG_I("subq bad crc @%02d:%02d:%02d\n",
518 time[0], time[1], time[2]);
519 }
520 }
521 else {
522 generate_subq(time);
523 }
524
525 CDR_LOG(" -> %02x,%02x %02x:%02x:%02x %02x:%02x:%02x\n",
526 cdr.subq.Track, cdr.subq.Index,
527 cdr.subq.Relative[0], cdr.subq.Relative[1], cdr.subq.Relative[2],
528 cdr.subq.Absolute[0], cdr.subq.Absolute[1], cdr.subq.Absolute[2]);
529}
530
531static void cdrPlayInterrupt_Autopause()
532{
533 u32 abs_lev_max = 0;
534 boolean abs_lev_chselect;
535 u32 i;
536
537 if ((cdr.Mode & MODE_AUTOPAUSE) && cdr.TrackChanged) {
538 CDR_LOG_I("autopause\n");
539
540 SetResultSize(1);
541 cdr.Result[0] = cdr.StatP;
542 setIrq(DataEnd, 0x1000); // 0x1000 just for logging purposes
543
544 StopCdda();
545 SetPlaySeekRead(cdr.StatP, 0);
546 cdr.DriveState = DRIVESTATE_PAUSED;
547 }
548 else if ((cdr.Mode & MODE_REPORT) && !cdr.ReportDelay &&
549 ((cdr.subq.Absolute[2] & 0x0f) == 0 || cdr.FastForward || cdr.FastBackward))
550 {
551 SetResultSize(8);
552 cdr.Result[0] = cdr.StatP;
553 cdr.Result[1] = cdr.subq.Track;
554 cdr.Result[2] = cdr.subq.Index;
555
556 abs_lev_chselect = cdr.subq.Absolute[1] & 0x01;
557
558 /* 8 is a hack. For accuracy, it should be 588. */
559 for (i = 0; i < 8; i++)
560 {
561 abs_lev_max = MAX_VALUE(abs_lev_max, abs(read_buf[i * 2 + abs_lev_chselect]));
562 }
563 abs_lev_max = MIN_VALUE(abs_lev_max, 32767);
564 abs_lev_max |= abs_lev_chselect << 15;
565
566 if (cdr.subq.Absolute[2] & 0x10) {
567 cdr.Result[3] = cdr.subq.Relative[0];
568 cdr.Result[4] = cdr.subq.Relative[1] | 0x80;
569 cdr.Result[5] = cdr.subq.Relative[2];
570 }
571 else {
572 cdr.Result[3] = cdr.subq.Absolute[0];
573 cdr.Result[4] = cdr.subq.Absolute[1];
574 cdr.Result[5] = cdr.subq.Absolute[2];
575 }
576 cdr.Result[6] = abs_lev_max >> 0;
577 cdr.Result[7] = abs_lev_max >> 8;
578
579 setIrq(DataReady, 0x1001);
580 }
581
582 if (cdr.ReportDelay)
583 cdr.ReportDelay--;
584}
585
586static boolean canDoTurbo(void)
587{
588 u32 c = psxRegs.cycle;
589 return Config.TurboCD && !cdr.RetryDetected && !cdr.AdpcmActive
590 //&& c - psxRegs.intCycle[PSXINT_SPUDMA].sCycle > (u32)cdReadTime * 2
591 && c - psxRegs.intCycle[PSXINT_MDECOUTDMA].sCycle > (u32)cdReadTime * 16;
592}
593
594static int cdrSeekTime(unsigned char *target)
595{
596 int diff = abs((int)msf2sec(cdr.SetSectorPlay) - (int)msf2sec(target));
597 int seekTime = diff * (cdReadTime / 2000);
598 int cyclesSinceRS = psxRegs.cycle - cdr.LastReadSeekCycles;
599 seekTime = MAX_VALUE(seekTime, 20000);
600
601 // sled seek?
602 if (diff >= 7200)
603 seekTime = PSXCLK / 7 + diff * 64;
604 // add *something* as rotation time until the target sector
605 if (cyclesSinceRS >= cdReadTime)
606 seekTime += (8 - ((cyclesSinceRS >> 18) & 7)) * (cdReadTime / 2);
607
608 // Transformers Beast Wars Transmetals does Setloc(x),SeekL,Setloc(x),ReadN
609 // and then wants some slack time
610 if (cdr.DriveState == DRIVESTATE_PAUSED || cyclesSinceRS < cdReadTime *3/2)
611 seekTime += cdReadTime;
612
613 //seekTime = MIN_VALUE(seekTime, PSXCLK * 2 / 3);
614 CDR_LOG("seek: %02d:%02d:%02d %.2f %.2f (%.2f) st %d di %d\n",
615 target[0], target[1], target[2], (float)seekTime / PSXCLK,
616 (float)seekTime / cdReadTime, (float)cyclesSinceRS / cdReadTime,
617 cdr.DriveState, diff);
618 return seekTime;
619}
620
621static u32 cdrAlignTimingHack(u32 cycles)
622{
623 /*
624 * timing hack for T'ai Fu - Wrath of the Tiger:
625 * The game has a bug where it issues some cdc commands from a low priority
626 * vint handler, however there is a higher priority default bios handler
627 * that acks the vint irq and returns, so game's handler is not reached
628 * (see bios irq handler chains at e004 and the game's irq handling func
629 * at 80036810). For the game to work, vint has to arrive after the bios
630 * vint handler rejects some other irq (of which only cd and rcnt2 are
631 * active), but before the game's handler loop reads I_STAT. The time
632 * window for this is quite small (~1k cycles of so). Apparently this
633 * somehow happens naturally on the real hardware.
634 *
635 * Note: always enforcing this breaks other games like Crash PAL version
636 * (inputs get dropped because bios handler doesn't see interrupts).
637 */
638 u32 vint_rel = rcnts[3].cycleStart + 63000 - psxRegs.cycle;
639 while ((s32)(vint_rel - cycles) < 0)
640 vint_rel += PSXCLK / 60;
641 return vint_rel;
642}
643
644static void cdrUpdateTransferBuf(const u8 *buf);
645static void cdrReadInterrupt(void);
646static void cdrPrepCdda(s16 *buf, int samples);
647
648static void msfiAdd(u8 *msfi, u32 count)
649{
650 assert(count < 75);
651 msfi[2] += count;
652 if (msfi[2] >= 75) {
653 msfi[2] -= 75;
654 msfi[1]++;
655 if (msfi[1] == 60) {
656 msfi[1] = 0;
657 msfi[0]++;
658 }
659 }
660}
661
662static void msfiSub(u8 *msfi, u32 count)
663{
664 assert(count < 75);
665 msfi[2] -= count;
666 if ((s8)msfi[2] < 0) {
667 msfi[2] += 75;
668 msfi[1]--;
669 if ((s8)msfi[1] < 0) {
670 msfi[1] = 60;
671 msfi[0]--;
672 }
673 }
674}
675
676static int msfiEq(const u8 *a, const u8 *b)
677{
678 return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
679}
680
681void cdrPlayReadInterrupt(void)
682{
683 // this works but causes instability for timing sensitive games
684#if 0
685 int hit = cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
686 if (!hit && cdr.PhysCdPropagations < 75/2) {
687 // this propagates the real cdrom delays to the emulated game
688 CDRPLAYREAD_INT(cdReadTime / 2, 0);
689 cdr.PhysCdPropagations++;
690 return;
691 }
692#endif
693 cdr.LastReadSeekCycles = psxRegs.cycle;
694
695 if (cdr.Reading) {
696 cdrReadInterrupt();
697 return;
698 }
699
700 if (!cdr.Play) return;
701
702 CDR_LOG("CDDA - %02d:%02d:%02d m %02x\n",
703 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], cdr.Mode);
704
705 cdr.DriveState = DRIVESTATE_PLAY_READ;
706 SetPlaySeekRead(cdr.StatP, STATUS_PLAY);
707 if (memcmp(cdr.SetSectorPlay, cdr.SetSectorEnd, 3) == 0) {
708 CDR_LOG_I("end stop\n");
709 StopCdda();
710 SetPlaySeekRead(cdr.StatP, 0);
711 cdr.TrackChanged = TRUE;
712 cdr.DriveState = DRIVESTATE_PAUSED;
713 }
714 else {
715 cdra_readCDDA(cdr.SetSectorPlay, read_buf);
716 }
717
718 if (!cdr.IrqStat && (cdr.Mode & (MODE_AUTOPAUSE|MODE_REPORT)))
719 cdrPlayInterrupt_Autopause();
720
721 if (cdr.Play && !Config.Cdda) {
722 cdrPrepCdda(read_buf, CD_FRAMESIZE_RAW / 4);
723 SPU_playCDDAchannel(read_buf, CD_FRAMESIZE_RAW, psxRegs.cycle, 0);
724 }
725
726 msfiAdd(cdr.SetSectorPlay, 1);
727 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 1);
728
729 // update for CdlGetlocP/autopause
730 generate_subq(cdr.SetSectorPlay);
731
732 CDRPLAYREAD_INT(cdReadTime, 0);
733
734 // stop propagation since it breaks streaming
735 cdr.PhysCdPropagations = 0xff;
736}
737
738static void softReset(void)
739{
740 cdra_getStatus(&cdr_stat);
741 if (cdr_stat.Status & STATUS_SHELLOPEN) {
742 cdr.DriveState = DRIVESTATE_LID_OPEN;
743 cdr.StatP = STATUS_SHELLOPEN;
744 }
745 else if (CdromId[0] == '\0') {
746 cdr.DriveState = DRIVESTATE_STOPPED;
747 cdr.StatP = 0;
748 }
749 else {
750 cdr.DriveState = DRIVESTATE_STANDBY;
751 cdr.StatP = STATUS_ROTATING;
752 }
753
754 cdr.FifoOffset = DATA_SIZE; // fifo empty
755 cdr.LocL[0] = LOCL_INVALID;
756 cdr.Mode = MODE_SIZE_2340;
757 cdr.Muted = FALSE;
758 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
759 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
760}
761
762#define CMD_PART2 0x100
763#define CMD_WHILE_NOT_READY 0x200
764
765void cdrInterrupt(void) {
766 int start_rotating = 0;
767 int error = 0;
768 u32 cycles, seekTime = 0;
769 u32 second_resp_time = 0;
770 const void *buf;
771 u8 ParamC;
772 int read_ok;
773 u16 not_ready = 0;
774 u8 IrqStat = Acknowledge;
775 u8 DriveStateOld;
776 u16 Cmd;
777 int i;
778
779 if (cdr.IrqStat) {
780 CDR_LOG_I("cmd %02x with irqstat %x\n",
781 cdr.CmdInProgress, cdr.IrqStat);
782 return;
783 }
784 if (cdr.Irq1Pending) {
785 // hand out the "newest" sector, according to nocash
786 cdrUpdateTransferBuf(cdra_getBuffer());
787 CDR_LOG_I("%x:%02x:%02x loaded on ack, cmd=%02x res=%02x\n",
788 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2],
789 cdr.CmdInProgress, cdr.Irq1Pending);
790 SetResultSize(1);
791 cdr.Result[0] = cdr.Irq1Pending;
792 cdr.Irq1Pending = 0;
793 setIrq((cdr.Irq1Pending & STATUS_ERROR) ? DiskError : DataReady, 0x1003);
794 return;
795 }
796
797 // default response
798 SetResultSize(1);
799 cdr.Result[0] = cdr.StatP;
800
801 Cmd = cdr.CmdInProgress;
802 cdr.CmdInProgress = 0;
803 ParamC = cdr.ParamC;
804
805 if (Cmd < 0x100) {
806 cdr.Ctrl &= ~0x80;
807 cdr.ParamC = 0;
808 cdr.Cmd = 0;
809 }
810
811 switch (cdr.DriveState) {
812 case DRIVESTATE_PREPARE_CD:
813 if (Cmd > 2) {
814 // Syphon filter 2 expects commands to work shortly after it sees
815 // STATUS_ROTATING, so give up trying to emulate the startup seq
816 cdr.DriveState = DRIVESTATE_STANDBY;
817 cdr.StatP &= ~STATUS_SEEK;
818 psxRegs.interrupt &= ~(1 << PSXINT_CDRLID);
819 break;
820 }
821 // fallthrough
822 case DRIVESTATE_LID_OPEN:
823 case DRIVESTATE_RESCAN_CD:
824 // no disk or busy with the initial scan, allowed cmds are limited
825 not_ready = CMD_WHILE_NOT_READY;
826 break;
827 }
828
829 switch (Cmd | not_ready) {
830 case CdlNop:
831 case CdlNop + CMD_WHILE_NOT_READY:
832 if (cdr.DriveState != DRIVESTATE_LID_OPEN)
833 cdr.StatP &= ~STATUS_SHELLOPEN;
834 break;
835
836 case CdlSetloc:
837 case CdlSetloc + CMD_WHILE_NOT_READY: // apparently?
838 if (cdr.StatP & STATUS_SHELLOPEN)
839 // wrong? Driver2 vs Amerzone
840 goto set_error;
841
842 // MM must be BCD, SS must be BCD and <0x60, FF must be BCD and <0x75
843 if (((cdr.Param[0] & 0x0F) > 0x09) || (cdr.Param[0] > 0x99) || ((cdr.Param[1] & 0x0F) > 0x09) || (cdr.Param[1] >= 0x60) || ((cdr.Param[2] & 0x0F) > 0x09) || (cdr.Param[2] >= 0x75))
844 {
845 CDR_LOG_I("Invalid/out of range seek to %02X:%02X:%02X\n", cdr.Param[0], cdr.Param[1], cdr.Param[2]);
846 if (++cdr.errorRetryhack > 100)
847 break;
848 error = ERROR_BAD_ARGNUM;
849 goto set_error;
850 }
851 else
852 {
853 u8 set_loc[3];
854 for (i = 0; i < 3; i++)
855 set_loc[i] = btoi(cdr.Param[i]);
856 if ((msfiEq(cdr.SetSector, set_loc)) //|| msfiEq(cdr.Param, cdr.Transfer))
857 && !cdr.SetlocPending)
858 cdr.RetryDetected++;
859 else
860 cdr.RetryDetected = 0;
861 memcpy(cdr.SetSector, set_loc, 3);
862 cdr.SetSector[3] = 0;
863 cdr.SetlocPending = 1;
864 cdr.errorRetryhack = 0;
865 }
866 break;
867
868 case CdlPlay:
869 StopCdda();
870 StopReading();
871
872 cdr.FastBackward = 0;
873 cdr.FastForward = 0;
874
875 // BIOS CD Player
876 // - Pause player, hit Track 01/02/../xx (Setloc issued!!)
877
878 if (ParamC != 0 && cdr.Param[0] != 0) {
879 int track = btoi( cdr.Param[0] );
880
881 if (track <= cdr.ResultTN[1])
882 cdr.CurTrack = track;
883
884 CDR_LOG("PLAY track %d\n", cdr.CurTrack);
885
886 if (cdra_getTD(cdr.CurTrack, cdr.ResultTD) != -1) {
887 seekTime = cdrSeekTime(cdr.ResultTD);
888 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
889 }
890 }
891 else if (cdr.SetlocPending) {
892 seekTime = cdrSeekTime(cdr.SetSector);
893 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
894 }
895 else {
896 CDR_LOG("PLAY Resume @ %d:%d:%d\n",
897 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2]);
898 }
899 cdr.SetlocPending = 0;
900
901 /*
902 Rayman: detect track changes
903 - fixes logo freeze
904
905 Twisted Metal 2: skip PREGAP + starting accurate SubQ
906 - plays tracks without retry play
907
908 Wild 9: skip PREGAP + starting accurate SubQ
909 - plays tracks without retry play
910 */
911 Find_CurTrack(cdr.SetSectorPlay);
912 generate_subq(cdr.SetSectorPlay);
913 cdr.LocL[0] = LOCL_INVALID;
914 cdr.SubqForwardSectors = 1;
915 cdr.TrackChanged = FALSE;
916 cdr.FileChannelSelected = 0;
917 cdr.AdpcmActive = 0;
918 cdr.ReportDelay = 60;
919 cdr.sectorsRead = 0;
920
921 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
922
923 // BIOS player - set flag again
924 cdr.Play = TRUE;
925 cdr.DriveState = DRIVESTATE_PLAY_READ;
926 cdr.PhysCdPropagations = 0;
927
928 CDRPLAYREAD_INT(cdReadTime + seekTime, 1);
929 start_rotating = 1;
930 break;
931
932 case CdlForward:
933 // TODO: error 80 if stopped
934 IrqStat = Complete;
935
936 // GameShark CD Player: Calls 2x + Play 2x
937 cdr.FastForward = 1;
938 cdr.FastBackward = 0;
939 break;
940
941 case CdlBackward:
942 IrqStat = Complete;
943
944 // GameShark CD Player: Calls 2x + Play 2x
945 cdr.FastBackward = 1;
946 cdr.FastForward = 0;
947 break;
948
949 case CdlStandby:
950 if (cdr.DriveState != DRIVESTATE_STOPPED) {
951 error = ERROR_BAD_ARGNUM;
952 goto set_error;
953 }
954 second_resp_time = cdReadTime * 125 / 2;
955 start_rotating = 1;
956 break;
957
958 case CdlStandby + CMD_PART2:
959 IrqStat = Complete;
960 break;
961
962 case CdlStop:
963 if (cdr.Play) {
964 // grab time for current track
965 cdra_getTD(cdr.CurTrack, cdr.ResultTD);
966 memcpy(cdr.SetSectorPlay, cdr.ResultTD, 3);
967 }
968
969 StopCdda();
970 StopReading();
971 SetPlaySeekRead(cdr.StatP, 0);
972 cdr.StatP &= ~STATUS_ROTATING;
973 cdr.LocL[0] = LOCL_INVALID;
974
975 second_resp_time = 0x800;
976 if (cdr.DriveState != DRIVESTATE_STOPPED)
977 second_resp_time = cdReadTime * 30 / 2;
978
979 cdr.DriveState = DRIVESTATE_STOPPED;
980 break;
981
982 case CdlStop + CMD_PART2:
983 IrqStat = Complete;
984 break;
985
986 case CdlPause:
987 if (cdr.AdpcmActive) {
988 cdr.AdpcmActive = 0;
989 cdr.Xa.nsamples = 0;
990 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, 1); // flush adpcm
991 }
992 StopCdda();
993 StopReading();
994
995 // how the drive maintains the position while paused is quite
996 // complicated, this is the minimum to make "Bedlam" happy
997 msfiSub(cdr.SetSectorPlay, MIN_VALUE(cdr.sectorsRead, 4));
998 cdr.sectorsRead = 0;
999
1000 /*
1001 Gundam Battle Assault 2
1002 Hokuto no Ken 2
1003 InuYasha - Feudal Fairy Tale
1004 Dance Dance Revolution Konamix
1005 Digimon Rumble Arena
1006 ...
1007 */
1008 if (!(cdr.StatP & (STATUS_PLAY | STATUS_READ)))
1009 {
1010 second_resp_time = 7000;
1011 }
1012 else
1013 {
1014 second_resp_time = 2100011;
1015 // a hack to try to avoid weird cmd vs irq1 races causing games to retry
1016 second_resp_time += (cdr.RetryDetected & 15) * 100001;
1017 }
1018 SetPlaySeekRead(cdr.StatP, 0);
1019 DriveStateOld = cdr.DriveState;
1020 cdr.DriveState = DRIVESTATE_PAUSED;
1021 if (DriveStateOld == DRIVESTATE_SEEK) {
1022 // According to Duckstation this fails, but the
1023 // exact conditions and effects are not clear.
1024 // Moto Racer World Tour seems to rely on this.
1025 // For now assume pause works anyway, just errors out.
1026 error = ERROR_NOTREADY;
1027 goto set_error;
1028 }
1029 break;
1030
1031 case CdlPause + CMD_PART2:
1032 IrqStat = Complete;
1033 break;
1034
1035 case CdlReset:
1036 case CdlReset + CMD_WHILE_NOT_READY:
1037 // note: nocash and Duckstation calls this 'Init', but
1038 // the official SDK calls it 'Reset', and so do we
1039 StopCdda();
1040 StopReading();
1041 softReset();
1042 second_resp_time = not_ready ? 70000 : 4100000;
1043 start_rotating = 1;
1044 break;
1045
1046 case CdlReset + CMD_PART2:
1047 case CdlReset + CMD_PART2 + CMD_WHILE_NOT_READY:
1048 IrqStat = Complete;
1049 break;
1050
1051 case CdlMute:
1052 cdr.Muted = TRUE;
1053 SPU_setCDvol(0, 0, 0, 0, psxRegs.cycle);
1054 break;
1055
1056 case CdlDemute:
1057 cdr.Muted = FALSE;
1058 SPU_setCDvol(cdr.AttenuatorLeftToLeft, cdr.AttenuatorLeftToRight,
1059 cdr.AttenuatorRightToLeft, cdr.AttenuatorRightToRight, psxRegs.cycle);
1060 break;
1061
1062 case CdlSetfilter:
1063 cdr.FilterFile = cdr.Param[0];
1064 cdr.FilterChannel = cdr.Param[1];
1065 cdr.FileChannelSelected = 0;
1066 break;
1067
1068 case CdlSetmode:
1069 case CdlSetmode + CMD_WHILE_NOT_READY:
1070 if ((cdr.Mode ^ cdr.Param[0]) & MODE_BIT4)
1071 log_unhandled("cdrom: mode4 changed: %02x\n", cdr.Param[0]);
1072 cdr.Mode = cdr.Param[0];
1073 break;
1074
1075 case CdlGetparam:
1076 case CdlGetparam + CMD_WHILE_NOT_READY:
1077 /* Gameblabla : According to mednafen, Result size should be 5 and done this way. */
1078 SetResultSize_(5);
1079 cdr.Result[1] = cdr.Mode;
1080 cdr.Result[2] = 0;
1081 cdr.Result[3] = cdr.FilterFile;
1082 cdr.Result[4] = cdr.FilterChannel;
1083 break;
1084
1085 case CdlGetlocL:
1086 if (cdr.LocL[0] == LOCL_INVALID) {
1087 error = 0x80;
1088 goto set_error;
1089 }
1090 SetResultSize_(8);
1091 memcpy(cdr.Result, cdr.LocL, 8);
1092 break;
1093
1094 case CdlGetlocP:
1095 SetResultSize_(8);
1096 memcpy(&cdr.Result, &cdr.subq, 8);
1097 break;
1098
1099 case CdlReadT: // SetSession?
1100 // really long
1101 second_resp_time = cdReadTime * 290 / 4;
1102 start_rotating = 1;
1103 break;
1104
1105 case CdlReadT + CMD_PART2:
1106 IrqStat = Complete;
1107 break;
1108
1109 case CdlGetTN:
1110 if (cdra_getTN(cdr.ResultTN) != 0) {
1111 assert(0);
1112 }
1113 SetResultSize_(3);
1114 cdr.Result[1] = itob(cdr.ResultTN[0]);
1115 cdr.Result[2] = itob(cdr.ResultTN[1]);
1116 break;
1117
1118 case CdlGetTD:
1119 cdr.Track = btoi(cdr.Param[0]);
1120 if (cdra_getTD(cdr.Track, cdr.ResultTD) != 0) {
1121 error = ERROR_BAD_ARGVAL;
1122 goto set_error;
1123 }
1124 SetResultSize_(3);
1125 cdr.Result[1] = itob(cdr.ResultTD[0]);
1126 cdr.Result[2] = itob(cdr.ResultTD[1]);
1127 // no sector number
1128 //cdr.Result[3] = itob(cdr.ResultTD[2]);
1129 break;
1130
1131 case CdlSeekL:
1132 case CdlSeekP:
1133 StopCdda();
1134 StopReading();
1135 SetPlaySeekRead(cdr.StatP, STATUS_SEEK | STATUS_ROTATING);
1136
1137 if (!canDoTurbo())
1138 seekTime = cdrSeekTime(cdr.SetSector);
1139 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1140 cdr.DriveState = DRIVESTATE_SEEK;
1141 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1142 cdr.SetSectorPlay[2], 0);
1143 /*
1144 Crusaders of Might and Magic = 0.5x-4x
1145 - fix cutscene speech start
1146
1147 Eggs of Steel = 2x-?
1148 - fix new game
1149
1150 Medievil = ?-4x
1151 - fix cutscene speech
1152
1153 Rockman X5 = 0.5-4x
1154 - fix capcom logo
1155 */
1156 second_resp_time = cdReadTime + seekTime;
1157 start_rotating = 1;
1158 break;
1159
1160 case CdlSeekL + CMD_PART2:
1161 case CdlSeekP + CMD_PART2:
1162 SetPlaySeekRead(cdr.StatP, 0);
1163 cdr.Result[0] = cdr.StatP;
1164 IrqStat = Complete;
1165
1166 Find_CurTrack(cdr.SetSectorPlay);
1167 read_ok = ReadTrack(cdr.SetSectorPlay);
1168 if (read_ok && (buf = cdra_getBuffer()))
1169 memcpy(cdr.LocL, buf, 8);
1170 UpdateSubq(cdr.SetSectorPlay);
1171 cdr.DriveState = DRIVESTATE_STANDBY;
1172 cdr.TrackChanged = FALSE;
1173 cdr.LastReadSeekCycles = psxRegs.cycle;
1174 break;
1175
1176 case CdlTest:
1177 case CdlTest + CMD_WHILE_NOT_READY:
1178 switch (cdr.Param[0]) {
1179 case 0x20: // System Controller ROM Version
1180 SetResultSize_(4);
1181 memcpy(cdr.Result, Test20, 4);
1182 break;
1183 case 0x22:
1184 SetResultSize_(8);
1185 memcpy(cdr.Result, Test22, 4);
1186 break;
1187 case 0x23: case 0x24:
1188 SetResultSize_(8);
1189 memcpy(cdr.Result, Test23, 4);
1190 break;
1191 }
1192 break;
1193
1194 case CdlID:
1195 second_resp_time = 20480;
1196 break;
1197
1198 case CdlID + CMD_PART2:
1199 SetResultSize_(8);
1200 cdr.Result[0] = cdr.StatP;
1201 cdr.Result[1] = 0;
1202 cdr.Result[2] = 0;
1203 cdr.Result[3] = 0;
1204
1205 // 0x10 - audio | 0x40 - disk missing | 0x80 - unlicensed
1206 if (cdra_getStatus(&cdr_stat) != 0 ||
1207 cdr_stat.Type == CDRT_UNKNOWN || cdr_stat.Type == 0xff) {
1208 cdr.Result[1] = 0xc0;
1209 }
1210 else {
1211 if (cdr_stat.Type == CDRT_CDDA)
1212 cdr.Result[1] |= 0x10;
1213 if (CdromId[0] == '\0')
1214 cdr.Result[1] |= 0x80;
1215 }
1216 cdr.Result[0] |= (cdr.Result[1] >> 4) & 0x08;
1217 CDR_LOG_I("CdlID: %02x %02x %02x %02x\n", cdr.Result[0],
1218 cdr.Result[1], cdr.Result[2], cdr.Result[3]);
1219
1220 /* 4-char string in Playstation bios boot screen */
1221 if (Config.SlowBoot == 1)
1222 memcpy(&cdr.Result[4], "PCSX", 4);
1223 else {
1224 cdr.Result[4] = 'S';
1225 cdr.Result[5] = 'C';
1226 cdr.Result[6] = 'E';
1227 if (Config.PsxType == PSX_TYPE_PAL)
1228 cdr.Result[7] = 'E';
1229 else if (CdromId[2] == 'P' || CdromId[2] == 'p')
1230 cdr.Result[7] = 'I';
1231 else
1232 cdr.Result[7] = 'A';
1233 }
1234 IrqStat = Complete;
1235 break;
1236
1237 case CdlInit:
1238 case CdlInit + CMD_WHILE_NOT_READY:
1239 StopCdda();
1240 StopReading();
1241 SetPlaySeekRead(cdr.StatP, 0);
1242 // yes, it really sets STATUS_SHELLOPEN
1243 cdr.StatP |= STATUS_SHELLOPEN;
1244 cdr.DriveState = DRIVESTATE_RESCAN_CD;
1245 set_event(PSXINT_CDRLID, 20480);
1246 start_rotating = 1;
1247 break;
1248
1249 case CdlGetQ:
1250 case CdlGetQ + CMD_WHILE_NOT_READY:
1251 break;
1252
1253 case CdlReadToc:
1254 case CdlReadToc + CMD_WHILE_NOT_READY:
1255 cdr.LocL[0] = LOCL_INVALID;
1256 second_resp_time = cdReadTime * 180 / 4;
1257 if (!Config.HLE && Config.SlowBoot) {
1258 // hack: compensate cdrom being emulated too fast
1259 // and bios finishing before the reverb decays
1260 second_resp_time += cdReadTime * 75*2;
1261 if ((psxRegs.pc >> 28) == 0x0b)
1262 second_resp_time += cdReadTime * 75*3;
1263 }
1264 start_rotating = 1;
1265 break;
1266
1267 case CdlReadToc + CMD_PART2:
1268 case CdlReadToc + CMD_PART2 + CMD_WHILE_NOT_READY:
1269 IrqStat = Complete;
1270 break;
1271
1272 case CdlReadN:
1273 case CdlReadS:
1274 if (cdr.Reading && !cdr.SetlocPending)
1275 break;
1276
1277 Find_CurTrack(cdr.SetlocPending ? cdr.SetSector : cdr.SetSectorPlay);
1278
1279 if (cdr_stat.Type != CDRT_DATA && !(cdr.Mode & MODE_CDDA)) {
1280 error = ERROR_INVALIDCMD;
1281 goto set_error;
1282 }
1283
1284 StopCdda();
1285 if (cdr.SetlocPending) {
1286 seekTime = cdrSeekTime(cdr.SetSector);
1287 memcpy(cdr.SetSectorPlay, cdr.SetSector, 4);
1288 cdr.SetlocPending = 0;
1289 }
1290 cdr.Reading = 1;
1291 cdr.FileChannelSelected = 0;
1292 cdr.AdpcmActive = 0;
1293
1294 // Fighting Force 2 - update subq time immediately
1295 // - fixes new game
1296 UpdateSubq(cdr.SetSectorPlay);
1297 cdr.LocL[0] = LOCL_INVALID;
1298 cdr.SubqForwardSectors = 1;
1299 cdr.sectorsRead = 0;
1300 cdr.DriveState = DRIVESTATE_SEEK;
1301 cdr.PhysCdPropagations = 0;
1302 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1],
1303 cdr.SetSectorPlay[2], 0);
1304
1305 cycles = (cdr.Mode & MODE_SPEED) ? cdReadTime : cdReadTime * 2;
1306 cycles += seekTime;
1307 if (Config.hacks.cdr_read_timing)
1308 cycles = cdrAlignTimingHack(cycles);
1309 else if (canDoTurbo())
1310 cycles = cdReadTime / 2;
1311 CDRPLAYREAD_INT(cycles, 1);
1312
1313 SetPlaySeekRead(cdr.StatP, STATUS_SEEK);
1314 start_rotating = 1;
1315 break;
1316
1317 case CdlSync:
1318 default:
1319 error = ERROR_INVALIDCMD;
1320 // FALLTHROUGH
1321
1322 set_error:
1323 SetResultSize_(2);
1324 cdr.Result[0] = cdr.StatP | STATUS_ERROR;
1325 cdr.Result[1] = not_ready ? ERROR_NOTREADY : error;
1326 IrqStat = DiskError;
1327 CDR_LOG_I("cmd %02x error %02x\n", Cmd, cdr.Result[1]);
1328 break;
1329 }
1330
1331 if (cdr.DriveState == DRIVESTATE_STOPPED && start_rotating) {
1332 cdr.DriveState = DRIVESTATE_STANDBY;
1333 cdr.StatP |= STATUS_ROTATING;
1334 }
1335
1336 if (second_resp_time) {
1337 cdr.CmdInProgress = Cmd | 0x100;
1338 set_event(PSXINT_CDR, second_resp_time);
1339 }
1340 else if (cdr.Cmd && cdr.Cmd != (Cmd & 0xff)) {
1341 cdr.CmdInProgress = cdr.Cmd;
1342 CDR_LOG_I("cmd %02x came before %02x finished\n", cdr.Cmd, Cmd);
1343 }
1344
1345 setIrq(IrqStat, Cmd);
1346}
1347
1348static void cdrPrepCdda(s16 *buf, int samples)
1349{
1350#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1351 int i;
1352 for (i = 0; i < samples; i++) {
1353 buf[i * 2 + 0] = SWAP16(buf[i * 2 + 0]);
1354 buf[i * 2 + 1] = SWAP16(buf[i * 2 + 1]);
1355 }
1356#endif
1357}
1358
1359static void cdrReadInterruptSetResult(unsigned char result)
1360{
1361 if (cdr.IrqStat) {
1362 CDR_LOG_I("%d:%02d:%02d irq miss, cmd=%02x irqstat=%02x\n",
1363 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1364 cdr.CmdInProgress, cdr.IrqStat);
1365 cdr.Irq1Pending = result;
1366 // F1 2000 timing hack :(
1367 // compensate for some csum func @80014380 taking too long
1368 if (!cdr.AdpcmActive)
1369 psxRegs.intCycle[PSXINT_CDREAD].sCycle += cdReadTime / 10;
1370 return;
1371 }
1372 SetResultSize(1);
1373 cdr.Result[0] = result;
1374 setIrq((result & STATUS_ERROR) ? DiskError : DataReady, 0x1004);
1375}
1376
1377static void cdrUpdateTransferBuf(const u8 *buf)
1378{
1379 if (!buf)
1380 return;
1381 memcpy(cdr.Transfer, buf, DATA_SIZE);
1382 CheckPPFCache(cdr.Transfer, cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]);
1383 CDR_LOG("cdr.Transfer %02x:%02x:%02x\n",
1384 cdr.Transfer[0], cdr.Transfer[1], cdr.Transfer[2]);
1385 if (cdr.FifoOffset < 2048 + 12)
1386 CDR_LOG("FifoOffset(1) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1387}
1388
1389static void cdrReadInterrupt(void)
1390{
1391 const struct { u8 file, chan, mode, coding; } *subhdr;
1392 const u8 *buf = NULL;
1393 int deliver_data = 1;
1394 u8 subqPos[3];
1395 int read_ok;
1396 int is_start;
1397
1398 memcpy(subqPos, cdr.SetSectorPlay, sizeof(subqPos));
1399 msfiAdd(subqPos, cdr.SubqForwardSectors);
1400 UpdateSubq(subqPos);
1401 if (cdr.SubqForwardSectors < SUBQ_FORWARD_SECTORS) {
1402 cdr.SubqForwardSectors++;
1403 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1404 return;
1405 }
1406
1407 // note: CdlGetlocL should work as soon as STATUS_READ is indicated
1408 SetPlaySeekRead(cdr.StatP, STATUS_READ | STATUS_ROTATING);
1409 cdr.DriveState = DRIVESTATE_PLAY_READ;
1410 cdr.sectorsRead++;
1411
1412 read_ok = ReadTrack(cdr.SetSectorPlay);
1413 if (read_ok)
1414 buf = cdra_getBuffer();
1415 if (buf == NULL)
1416 read_ok = 0;
1417
1418 if (!read_ok) {
1419 CDR_LOG_I("cdrReadInterrupt() Log: err\n");
1420 cdrReadInterruptSetResult(cdr.StatP | STATUS_ERROR);
1421 cdr.DriveState = DRIVESTATE_PAUSED; // ?
1422 return;
1423 }
1424 memcpy(cdr.LocL, buf, 8);
1425
1426 if (!cdr.IrqStat && !cdr.Irq1Pending)
1427 cdrUpdateTransferBuf(buf);
1428
1429 subhdr = (void *)(buf + 4);
1430 do {
1431 // try to process as adpcm
1432 if (!(cdr.Mode & MODE_STRSND))
1433 break;
1434 if (buf[3] != 2 || (subhdr->mode & 0x44) != 0x44) // or 0x64?
1435 break;
1436 CDR_LOG("f=%d m=%d %d,%3d | %d,%2d | %d,%2d\n", !!(cdr.Mode & MODE_SF), cdr.Muted,
1437 subhdr->file, subhdr->chan, cdr.CurFile, cdr.CurChannel, cdr.FilterFile, cdr.FilterChannel);
1438 if ((cdr.Mode & MODE_SF) && (subhdr->file != cdr.FilterFile || subhdr->chan != cdr.FilterChannel))
1439 break;
1440 if (subhdr->chan & 0x80) { // ?
1441 if (subhdr->chan != 0xff)
1442 log_unhandled("adpcm %d:%d\n", subhdr->file, subhdr->chan);
1443 break;
1444 }
1445 if (!cdr.FileChannelSelected) {
1446 cdr.CurFile = subhdr->file;
1447 cdr.CurChannel = subhdr->chan;
1448 cdr.FileChannelSelected = 1;
1449 }
1450 else if (subhdr->file != cdr.CurFile || subhdr->chan != cdr.CurChannel)
1451 break;
1452
1453 // accepted as adpcm
1454 deliver_data = 0;
1455
1456 if (Config.Xa)
1457 break;
1458 is_start = !cdr.AdpcmActive;
1459 cdr.AdpcmActive = !xa_decode_sector(&cdr.Xa, buf + 4, is_start);
1460 if (cdr.AdpcmActive)
1461 SPU_playADPCMchannel(&cdr.Xa, psxRegs.cycle, is_start);
1462 } while (0);
1463
1464 if ((cdr.Mode & MODE_SF) && (subhdr->mode & 0x44) == 0x44) // according to nocash
1465 deliver_data = 0;
1466 if (!(cdr.Mode & MODE_SIZE_2340) && buf[3] != 1 && buf[3] != 2) {
1467 deliver_data = 0; // according to duckstation
1468 CDR_LOG_I("%d:%02d:%02d msf %x:%02x:%02x mode %02x ignored\n",
1469 cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2],
1470 buf[0], buf[1], buf[2], buf[3]);
1471 }
1472
1473 /*
1474 Croc 2: $40 - only FORM1 (*)
1475 Judge Dredd: $C8 - only FORM1 (*)
1476 Sim Theme Park - no adpcm at all (zero)
1477 */
1478
1479 if (deliver_data)
1480 cdrReadInterruptSetResult(cdr.StatP);
1481
1482 msfiAdd(cdr.SetSectorPlay, 1);
1483 cdra_prefetch(cdr.SetSectorPlay[0], cdr.SetSectorPlay[1], cdr.SetSectorPlay[2], 0);
1484
1485 CDRPLAYREAD_INT((cdr.Mode & MODE_SPEED) ? (cdReadTime / 2) : cdReadTime, 0);
1486}
1487
1488/*
1489cdrRead0:
1490 bit 0,1 - reg index
1491 bit 2 - adpcm active
1492 bit 3 - 1 parameter fifo empty
1493 bit 4 - 1 parameter fifo not full
1494 bit 5 - 1 response fifo not empty
1495 bit 6 - 1 data fifo not empty
1496 bit 7 - 1 command being processed
1497*/
1498
1499unsigned char cdrRead0(void) {
1500 cdr.Ctrl &= ~0x64;
1501 cdr.Ctrl |= cdr.AdpcmActive << 2;
1502 cdr.Ctrl |= cdr.ResultReady << 5;
1503 cdr.Ctrl |= ((signed int)(cdr.FifoOffset - cdr.FifoSize) >> 31) & 0x40;
1504
1505 cdr.Ctrl |= 0x18;
1506
1507 CDR_LOG_IO("cdr r0.sta: %02x\n", cdr.Ctrl);
1508
1509 return psxHu8(0x1800) = cdr.Ctrl;
1510}
1511
1512void cdrWrite0(unsigned char rt) {
1513 CDR_LOG_IO("cdr w0.x.idx: %02x\n", rt);
1514
1515 cdr.Ctrl = (rt & 3) | (cdr.Ctrl & ~3);
1516}
1517
1518unsigned char cdrRead1(void) {
1519 if ((cdr.ResultP & 0xf) < cdr.ResultC)
1520 psxHu8(0x1801) = cdr.Result[cdr.ResultP & 0xf];
1521 else
1522 psxHu8(0x1801) = 0;
1523 cdr.ResultP++;
1524 if (cdr.ResultP == cdr.ResultC)
1525 cdr.ResultReady = 0;
1526
1527 CDR_LOG_IO("cdr r1.x.rsp: %02x #%u\n", psxHu8(0x1801), cdr.ResultP - 1);
1528
1529 return psxHu8(0x1801);
1530}
1531
1532void cdrWrite1(unsigned char rt) {
1533 const char *rnames[] = { "0.cmd", "1.smd", "2.smc", "3.arr" }; (void)rnames;
1534 CDR_LOG_IO("cdr w1.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1535
1536 switch (cdr.Ctrl & 3) {
1537 case 0:
1538 break;
1539 case 3:
1540 cdr.AttenuatorRightToRightT = rt;
1541 return;
1542 default:
1543 return;
1544 }
1545
1546#ifdef CDR_LOG_CMD
1547 CDR_LOG_I("CD1 write: %x (%s)", rt, CmdName[rt]);
1548 if (cdr.ParamC) {
1549 int i;
1550 SysPrintf(" Param[%d] = {", cdr.ParamC);
1551 for (i = 0; i < cdr.ParamC; i++)
1552 SysPrintf(" %x,", cdr.Param[i]);
1553 SysPrintf("}");
1554 }
1555 SysPrintf(" @%08x\n", psxRegs.pc);
1556#endif
1557
1558 cdr.ResultReady = 0;
1559 cdr.Ctrl |= 0x80;
1560
1561 if (!cdr.CmdInProgress) {
1562 cdr.CmdInProgress = rt;
1563 // should be something like 12k + controller delays
1564 set_event(PSXINT_CDR, 5000);
1565 }
1566 else {
1567 CDR_LOG_I("cmd while busy: %02x, prev %02x, busy %02x\n",
1568 rt, cdr.Cmd, cdr.CmdInProgress);
1569 if (cdr.CmdInProgress < 0x100) // no pending 2nd response
1570 cdr.CmdInProgress = rt;
1571 }
1572
1573 cdr.Cmd = rt;
1574}
1575
1576unsigned char cdrRead2(void) {
1577 unsigned char ret = cdr.Transfer[0x920];
1578
1579 if (cdr.FifoOffset < cdr.FifoSize)
1580 ret = cdr.Transfer[cdr.FifoOffset++];
1581 else
1582 CDR_LOG_I("read empty fifo (%d)\n", cdr.FifoSize);
1583
1584 CDR_LOG_IO("cdr r2.x.dat: %02x\n", ret);
1585 return ret;
1586}
1587
1588void cdrWrite2(unsigned char rt) {
1589 const char *rnames[] = { "0.prm", "1.ien", "2.all", "3.arl" }; (void)rnames;
1590 CDR_LOG_IO("cdr w2.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1591
1592 switch (cdr.Ctrl & 3) {
1593 case 0:
1594 if (cdr.ParamC < 8) // FIXME: size and wrapping
1595 cdr.Param[cdr.ParamC++] = rt;
1596 return;
1597 case 1:
1598 cdr.IrqMask = rt;
1599 setIrq(cdr.IrqStat, 0x1005);
1600 return;
1601 case 2:
1602 cdr.AttenuatorLeftToLeftT = rt;
1603 return;
1604 case 3:
1605 cdr.AttenuatorRightToLeftT = rt;
1606 return;
1607 }
1608}
1609
1610unsigned char cdrRead3(void) {
1611 if (cdr.Ctrl & 0x1)
1612 psxHu8(0x1803) = cdr.IrqStat | 0xE0;
1613 else
1614 psxHu8(0x1803) = cdr.IrqMask | 0xE0;
1615
1616 CDR_LOG_IO("cdr r3.%d.%s: %02x\n", cdr.Ctrl & 3,
1617 (cdr.Ctrl & 1) ? "ifl" : "ien", psxHu8(0x1803));
1618 return psxHu8(0x1803);
1619}
1620
1621void cdrWrite3(unsigned char rt) {
1622 const char *rnames[] = { "0.req", "1.ifl", "2.alr", "3.ava" }; (void)rnames;
1623 u8 ll, lr, rl, rr;
1624 CDR_LOG_IO("cdr w3.%s: %02x\n", rnames[cdr.Ctrl & 3], rt);
1625
1626 switch (cdr.Ctrl & 3) {
1627 case 0:
1628 break; // transfer
1629 case 1:
1630 if (cdr.IrqStat & rt) {
1631 u32 nextCycle = psxRegs.intCycle[PSXINT_CDR].sCycle
1632 + psxRegs.intCycle[PSXINT_CDR].cycle;
1633 int pending = psxRegs.interrupt & (1 << PSXINT_CDR);
1634#ifdef CDR_LOG_CMD_ACK
1635 CDR_LOG_I("ack %02x (w=%02x p=%d,%x,%x,%d)\n",
1636 cdr.IrqStat & rt, rt, !!pending, cdr.CmdInProgress,
1637 cdr.Irq1Pending, nextCycle - psxRegs.cycle);
1638#endif
1639 // note: Croc, Shadow Tower (more) vs Discworld Noir (<993)
1640 if (!pending && (cdr.CmdInProgress || cdr.Irq1Pending))
1641 {
1642 s32 c = 2048;
1643 if (cdr.CmdInProgress) {
1644 c = 2048 - (psxRegs.cycle - nextCycle);
1645 c = MAX_VALUE(c, 512);
1646 }
1647 set_event(PSXINT_CDR, c);
1648 }
1649 }
1650 cdr.IrqStat &= ~rt;
1651
1652 if (rt & 0x40)
1653 cdr.ParamC = 0;
1654 return;
1655 case 2:
1656 cdr.AttenuatorLeftToRightT = rt;
1657 return;
1658 case 3:
1659 if (rt & 0x01)
1660 log_unhandled("Mute ADPCM?\n");
1661 if (rt & 0x20) {
1662 ll = cdr.AttenuatorLeftToLeftT; lr = cdr.AttenuatorLeftToRightT;
1663 rl = cdr.AttenuatorRightToLeftT; rr = cdr.AttenuatorRightToRightT;
1664 if (ll == cdr.AttenuatorLeftToLeft &&
1665 lr == cdr.AttenuatorLeftToRight &&
1666 rl == cdr.AttenuatorRightToLeft &&
1667 rr == cdr.AttenuatorRightToRight)
1668 return;
1669 cdr.AttenuatorLeftToLeft = ll; cdr.AttenuatorLeftToRight = lr;
1670 cdr.AttenuatorRightToLeft = rl; cdr.AttenuatorRightToRight = rr;
1671 CDR_LOG_I("CD-XA Volume: %02x %02x | %02x %02x\n", ll, lr, rl, rr);
1672 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1673 }
1674 return;
1675 }
1676
1677 // test: Viewpoint
1678 if ((rt & 0x80) && cdr.FifoOffset < cdr.FifoSize) {
1679 CDR_LOG("cdrom: FifoOffset(2) %d/%d\n", cdr.FifoOffset, cdr.FifoSize);
1680 }
1681 else if (rt & 0x80) {
1682 switch (cdr.Mode & (MODE_SIZE_2328|MODE_SIZE_2340)) {
1683 case MODE_SIZE_2328:
1684 case 0x00:
1685 cdr.FifoOffset = 12;
1686 cdr.FifoSize = 2048 + 12;
1687 break;
1688
1689 case MODE_SIZE_2340:
1690 default:
1691 cdr.FifoOffset = 0;
1692 cdr.FifoSize = 2340;
1693 break;
1694 }
1695 }
1696 else if (!(rt & 0xc0))
1697 cdr.FifoOffset = DATA_SIZE; // fifo empty
1698}
1699
1700void psxDma3(u32 madr, u32 bcr, u32 chcr) {
1701 u32 cdsize, max_words, cycles;
1702 int size;
1703 u8 *ptr;
1704
1705#if 0
1706 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** %x addr = %x size = %x", chcr, madr, bcr);
1707 if (cdr.FifoOffset == 0) {
1708 ptr = cdr.Transfer;
1709 SysPrintf(" %02x:%02x:%02x", ptr[0], ptr[1], ptr[2]);
1710 }
1711 SysPrintf("\n");
1712#endif
1713
1714 switch (chcr & 0x71000000) {
1715 case 0x11000000:
1716 madr &= ~3;
1717 ptr = getDmaRam(madr, &max_words);
1718 if (ptr == INVALID_PTR) {
1719 CDR_LOG_I("psxDma3() Log: *** DMA 3 *** NULL Pointer!\n");
1720 break;
1721 }
1722
1723 cdsize = (((bcr - 1) & 0xffff) + 1) * 4;
1724
1725 /*
1726 GS CDX: Enhancement CD crash
1727 - Setloc 0:0:0
1728 - CdlPlay
1729 - Spams DMA3 and gets buffer overrun
1730 */
1731 size = DATA_SIZE - cdr.FifoOffset;
1732 if (size > cdsize)
1733 size = cdsize;
1734 if (size > max_words * 4)
1735 size = max_words * 4;
1736 if (size > 0)
1737 {
1738 memcpy(ptr, cdr.Transfer + cdr.FifoOffset, size);
1739 cdr.FifoOffset += size;
1740 }
1741 if (size < cdsize) {
1742 CDR_LOG_I("cdrom: dma3 %d/%d\n", size, cdsize);
1743 memset(ptr + size, cdr.Transfer[0x920], cdsize - size);
1744 }
1745 psxCpu->Clear(madr, cdsize / 4);
1746
1747 cycles = (cdsize / 4) * 24;
1748 set_event(PSXINT_CDRDMA, cycles);
1749
1750 HW_DMA3_CHCR &= SWAPu32(~0x10000000);
1751 if (chcr & 0x100) {
1752 HW_DMA3_MADR = SWAPu32(madr + cdsize);
1753 HW_DMA3_BCR &= SWAPu32(0xffff0000);
1754 }
1755 else {
1756 // halted
1757 psxRegs.cycle += cycles - 20;
1758 }
1759 if (canDoTurbo() && cdr.Reading && cdr.FifoOffset >= 2048)
1760 CDRPLAYREAD_INT(cycles + 4096, 1);
1761 return;
1762
1763 default:
1764 CDR_LOG_I("psxDma3() Log: Unknown cddma %x\n", chcr);
1765 break;
1766 }
1767
1768 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1769 DMA_INTERRUPT(3);
1770}
1771
1772void cdrDmaInterrupt(void)
1773{
1774 if (HW_DMA3_CHCR & SWAP32(0x01000000))
1775 {
1776 HW_DMA3_CHCR &= SWAP32(~0x01000000);
1777 DMA_INTERRUPT(3);
1778 }
1779}
1780
1781static void getCdInfo(void)
1782{
1783 cdra_getTN(cdr.ResultTN);
1784 cdra_getTD(0, cdr.SetSectorEnd);
1785 cdra_getStatus(&cdr_stat);
1786}
1787
1788void cdrReset() {
1789 memset(&cdr, 0, sizeof(cdr));
1790 cdr.CurTrack = 1;
1791 cdr.FilterFile = 0;
1792 cdr.FilterChannel = 0;
1793 cdr.IrqMask = 0x1f;
1794 cdr.IrqStat = NoIntr;
1795
1796 // BIOS player - default values
1797 cdr.AttenuatorLeftToLeft = 0x80;
1798 cdr.AttenuatorLeftToRight = 0x00;
1799 cdr.AttenuatorRightToLeft = 0x00;
1800 cdr.AttenuatorRightToRight = 0x80;
1801
1802 softReset();
1803 getCdInfo();
1804}
1805
1806int cdrFreeze(void *f, int Mode) {
1807 u32 tmp;
1808 u8 tmpp[3];
1809
1810 cdr.freeze_ver = 0x63647203;
1811 gzfreeze(&cdr, sizeof(cdr));
1812
1813 if (Mode == 1) {
1814 cdr.ParamP = cdr.ParamC;
1815 tmp = cdr.FifoOffset;
1816 }
1817
1818 gzfreeze(&tmp, sizeof(tmp));
1819
1820 if (Mode == 0) {
1821 u8 ll = 0, lr = 0, rl = 0, rr = 0;
1822 getCdInfo();
1823
1824 cdr.FifoOffset = tmp < DATA_SIZE ? tmp : DATA_SIZE;
1825 cdr.FifoSize = (cdr.Mode & MODE_SIZE_2340) ? 2340 : 2048 + 12;
1826 if (cdr.SubqForwardSectors > SUBQ_FORWARD_SECTORS)
1827 cdr.SubqForwardSectors = SUBQ_FORWARD_SECTORS;
1828
1829 // read right sub data
1830 memcpy(tmpp, cdr.Prev, sizeof(tmpp));
1831 if (cdr.freeze_ver < 0x63647203) {
1832 tmpp[0] = btoi(tmpp[0]);
1833 tmpp[1] = btoi(tmpp[1]);
1834 tmpp[2] = btoi(tmpp[2]);
1835 }
1836 cdr.Prev[0] = 0xff;
1837 if (tmpp[0] != 0xff)
1838 ReadTrack(tmpp);
1839
1840 if (cdr.Play) {
1841 if (cdr.freeze_ver < 0x63647202)
1842 memcpy(cdr.SetSectorPlay, cdr.SetSector, 3);
1843
1844 Find_CurTrack(cdr.SetSectorPlay);
1845 }
1846 if (!cdr.Muted)
1847 ll = cdr.AttenuatorLeftToLeft, lr = cdr.AttenuatorLeftToRight,
1848 rl = cdr.AttenuatorRightToLeft, rr = cdr.AttenuatorRightToRight;
1849 SPU_setCDvol(ll, lr, rl, rr, psxRegs.cycle);
1850 }
1851
1852 return 0;
1853}
1854
1855void LidInterrupt(void) {
1856 getCdInfo();
1857 cdrLidSeekInterrupt();
1858}